nixosTests.openstack-image: fix hardcoded image filename

The test hardcoded "/nixos.qcow2" but the actual output filename
is derived from image.baseName and image.extension (e.g.
"nixos-image-...-x86_64-linux.qcow2"). Use image.fileName to get
the correct path, matching the pattern in ec2-image.nix.

Also make the IMDS server's token validation conditional: when no
token file exists in the metadata directory, requests are served
without authentication. This supports both EC2 (IMDSv2 with tokens)
and OpenStack (plain GET) metadata fetchers.
This commit is contained in:
Philip Taron
2026-03-24 15:14:23 -07:00
parent 4e9154173d
commit aa80ef70bb
3 changed files with 19 additions and 13 deletions

View File

@@ -23,12 +23,10 @@ in
name = "metadata";
buildCommand = ''
mkdir -p $out/1.0/meta-data
mkdir -p $out/latest/api
ln -s ${pkgs.writeText "userData" userData} $out/1.0/user-data
echo "${hostname}" > $out/1.0/meta-data/hostname
echo "(unknown)" > $out/1.0/meta-data/ami-manifest-path
echo "i-1234567890abcdef0" > $out/1.0/meta-data/instance-id
echo "test-imdsv2-token" > $out/latest/api/token
''
+ optionalString (sshPublicKey != null) ''
mkdir -p $out/1.0/meta-data/public-keys/0

View File

@@ -59,27 +59,34 @@ def respond(status, body):
def main():
base_dir = sys.argv[1] if len(sys.argv) > 1 else "."
# Load expected token from file
# Load expected token from file. If no token file exists, IMDSv2
# authentication is disabled — requests are served without tokens.
# This supports both EC2 (IMDSv2 with tokens) and OpenStack (plain GET)
# metadata fetchers.
token_path = os.path.join(base_dir, "latest", "api", "token")
if os.path.isfile(token_path):
with open(token_path) as f:
expected_token = f.read().strip()
else:
expected_token = "test-imdsv2-token"
expected_token = None
method, path, headers = read_request()
rel_path = path.lstrip("/")
# PUT /latest/api/token — IMDSv2 token acquisition
if method == "PUT" and rel_path == "latest/api/token":
respond("200 OK", expected_token)
if expected_token is not None:
respond("200 OK", expected_token)
else:
respond("404 Not Found", "IMDSv2 token endpoint not configured\n")
return
# All other requests require a valid token
request_token = headers.get("x-aws-ec2-metadata-token", "")
if request_token != expected_token:
respond("401 Unauthorized", "Invalid or missing IMDSv2 token\n")
return
# Token validation (only when a token file is present)
if expected_token is not None:
request_token = headers.get("x-aws-ec2-metadata-token", "")
if request_token != expected_token:
respond("401 Unauthorized", "Invalid or missing IMDSv2 token\n")
return
# Serve file from the metadata directory
file_path = os.path.join(base_dir, rel_path)

View File

@@ -10,7 +10,7 @@ with pkgs.lib;
with import common/ec2.nix { inherit makeTest pkgs; };
let
image =
imageCfg =
(import ../lib/eval-config.nix {
system = null;
modules = [
@@ -26,8 +26,8 @@ let
nixpkgs.pkgs = pkgs;
}
];
}).config.system.build.openstackImage
+ "/nixos.qcow2";
}).config;
image = "${imageCfg.system.build.openstackImage}/${imageCfg.image.fileName}";
sshKeys = import ./ssh-keys.nix pkgs;
snakeOilPrivateKey = sshKeys.snakeOilPrivateKey.text;
@@ -80,6 +80,7 @@ in
userdata = makeEc2Test {
name = "openstack-ec2-metadata";
meta.broken = true; # amazon-init wants to download from the internet while building the system
inherit image;
sshPublicKey = snakeOilPublicKey;
userData = ''