Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9bd59a2
Separate v19 EBS population from AWS orchestration
lirazsiri Sep 1, 2026
d179aca
Use the built root for TKLBAM profiles
lirazsiri Sep 1, 2026
e71bbdd
Run TKLBAM profiles inside the built root
lirazsiri Sep 1, 2026
0c04fc2
Expose the packaged PyPy library in chroot
lirazsiri Sep 1, 2026
721f55c
Run profile generation through fab-chroot
lirazsiri Sep 1, 2026
ee4406a
Authorize EC2 keys for the active login user
lirazsiri Sep 1, 2026
796396c
Normalize populated EBS root permissions
lirazsiri Sep 1, 2026
f749500
Enable automatic TLS for Hub hostnames
lirazsiri Sep 2, 2026
2d5ad56
Install the corrected HubDNS v19 candidate
lirazsiri Sep 2, 2026
154a972
Keep automatic TLS pending until ACME is ready
lirazsiri Sep 2, 2026
7399233
Use the persistent HubDNS package in EC2 images
lirazsiri Sep 2, 2026
afa945d
Preserve AWS session tokens during image conversion
lirazsiri Sep 2, 2026
4419b26
Allow credential-free EBS population
lirazsiri Sep 2, 2026
626f9e8
Verify v19 images with the Trixie signing key
lirazsiri Sep 2, 2026
3f9cc1f
Wait for HTTP challenges before ACME validation
lirazsiri Sep 2, 2026
9b068e3
Publish HubDNS before requesting TLS
lirazsiri Sep 2, 2026
beee200
Avoid stale DNS during first TLS setup
lirazsiri Sep 2, 2026
9480cb3
Verify ACME challenges on the public path
lirazsiri Sep 2, 2026
5443e98
Retry initial TLS every minute
lirazsiri Sep 2, 2026
797f206
Enforce key-only admin access on EC2 images
lirazsiri Sep 2, 2026
01bf376
Match purge help options exactly
lirazsiri Sep 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 40 additions & 31 deletions bin/ec2/ebs_bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@
import sys
import time

from botocore.exceptions import ClientError

import utils

log = utils.get_logger("ebs-bundle")
Expand Down Expand Up @@ -102,6 +100,8 @@ def create(self, size, zone=None):

def delete(self, max_attempts=10):
if self.vol:
from botocore.exceptions import ClientError

attempt = 0
while True:
attempt += 1
Expand Down Expand Up @@ -153,8 +153,8 @@ def __del__(self):


class Device:
def __init__(self):
self.real_path = self._get_freedevice()
def __init__(self, real_path=None):
self.real_path = real_path if real_path else self._get_freedevice()
if not self.real_path:
raise EbsBundleError("no free devices available...")

Expand Down Expand Up @@ -197,13 +197,47 @@ def mkpart(self):
subprocess.run(["partprobe", self.real_path], check=True)
time.sleep(5)
self.root_path = self.real_path
self.real_path = self.real_path + "2"
separator = "p" if os.path.basename(self.real_path).startswith("nvme") else ""
self.real_path = self.real_path + separator + "2"

def __del__(self):
if self.is_mounted():
self.umount()


def populate(rootfs, device, filesystem="ext4"):
"""Populate one pre-attached block device without making an AWS call."""
log.info("creating partitions")
device.mkpart()
device.mkfs(filesystem)
mount_path = rootfs + ".mount"
device.mount(mount_path)

submounts = []
try:
log.info("syncing rootfs to partition")
utils.rsync(rootfs, mount_path)
os.chmod(mount_path, 0o755)

log.info("installing GRUB on volume")
for submount in ("/sys", "/proc", "/dev"):
subprocess.run(
["mount", "--bind", "--make-rslave", submount, mount_path + submount],
check=True,
)
submounts.append(submount)
subprocess.run(["chroot", mount_path, "grub-install", device.root_path], check=True)
subprocess.run(["chroot", mount_path, "update-grub"], check=True)
subprocess.run(["chroot", mount_path, "update-initramfs", "-u"], check=True)
finally:
for submount in reversed(submounts):
subprocess.run(["umount", "-l", mount_path + submount], check=False)
if device.is_mounted():
device.umount()
if os.path.isdir(mount_path):
os.rmdir(mount_path)


def bundle(rootfs, snapshot_name, size=10, filesystem="ext4"):
log.info(f"target snapshot - {snapshot_name} ")

Expand All @@ -214,33 +248,8 @@ def bundle(rootfs, snapshot_name, size=10, filesystem="ext4"):
device = Device()
volume.attach(utils.get_instanceid(), device)

log.info("creating partitions")
device.mkpart()
device.mkfs(filesystem)
mount_path = rootfs + ".mount"
device.mount(mount_path)

log.info("syncing rootfs to partition")
utils.rsync(rootfs, mount_path)

log.info("installing GRUB on volume")
submounts = ["/sys", "/proc", "/dev"]
for s in submounts:
subprocess.run(["mount", "--bind", "--make-rslave", s, mount_path + s],
check=True)
subprocess.run(["chroot", mount_path, "grub-install", device.root_path],
check=True)
subprocess.run(["chroot", mount_path, "update-grub"], check=True)
subprocess.run(["chroot", mount_path, "update-initramfs", "-u"],
check=True)

submounts.reverse()
for s in submounts:
subprocess.run(["umount", "-l", mount_path + s], check=True)

device.umount()
populate(rootfs, device, filesystem)
volume.detach()
os.removedirs(mount_path)

log.info("creating snapshot from volume")
snapshot = Snapshot()
Expand Down
23 changes: 23 additions & 0 deletions bin/ec2/ebs_populate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/python3
"""Populate a pre-attached EBS device from a prepared root filesystem."""

import argparse
import os

from ebs_bundle import Device, populate


def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("rootfs")
parser.add_argument("device")
args = parser.parse_args()
if not os.path.isdir(args.rootfs):
parser.error("rootfs path does not exist")
if not os.path.exists(args.device):
parser.error("device path does not exist")
populate(args.rootfs, Device(args.device))


if __name__ == "__main__":
main()
14 changes: 10 additions & 4 deletions bin/ec2/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@
import subprocess
import sys

import boto3
import ec2metadata

import conf


def connect_boto3(region=None):
import boto3

region = region if region else get_region()
return boto3.client(
"ec2",
region_name=region,
aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID"),
aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY"))
aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY"),
aws_session_token=os.environ.get("AWS_SESSION_TOKEN"))


def get_turnkey_version(rootfs):
Expand All @@ -35,14 +35,20 @@ def get_turnkey_version(rootfs):


def get_instanceid():
import ec2metadata

return ec2metadata.get("instance-id")


def get_zone():
import ec2metadata

return ec2metadata.get("availability-zone")


def get_region():
import ec2metadata

return ec2metadata.get("availability-zone")[0:-1]


Expand Down
44 changes: 43 additions & 1 deletion bin/iso-release
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,49 @@ cp build/product.iso "$O/$name.iso"
if [[ -e $BT_PROFILES/$appname ]]; then
mkdir -p "$O/$name.tklbam"
export PROFILES_CONF=$BT_PROFILES
"$BT/bin/generate-tklbam-profile" "$O/$name.iso" "$O/$name.tklbam"
if [[ -x /usr/lib/tklbam-pypy2/bin/pypy ]]; then
"$BT/bin/generate-tklbam-profile" "$O/$name.iso" "$O/$name.tklbam"
else
(
rootfs=$(readlink -f "$rootfs")
[[ -x "$rootfs/usr/lib/tklbam-pypy2/bin/pypy" ]] || \
fatal "TKLBAM runtime not found in $rootfs"
staging=$(mktemp -d "$rootfs/var/tmp/buildtasks-tklbam-profile.XXXXXX")
guest_staging=${staging#"$rootfs"}
cleanup() {
status=$?
trap - EXIT
rm -rf -- "$staging"
exit "$status"
}
trap cleanup EXIT

install -d "$staging/profiles" "$staging/output"
install -m 0755 "$BT/bin/generate-tklbam-profile" \
"$staging/generate-tklbam-profile"
cat > "$staging/run-tklbam-profile" <<EOF
#!/bin/bash
exec /usr/lib/tklbam-pypy2/bin/pypy "$guest_staging/generate-tklbam-profile" / "$guest_staging/output"
EOF
chmod 0755 "$staging/run-tklbam-profile"
cp "$BT_PROFILES/core" "$staging/profiles/core"
if [[ "$appname" != "core" ]]; then
cp "$BT_PROFILES/$appname" "$staging/profiles/$appname"
fi
if [[ -d "$BT_PROFILES/hooks/$appname" ]]; then
install -d "$staging/profiles/hooks"
cp -a "$BT_PROFILES/hooks/$appname" "$staging/profiles/hooks/"
fi

PROFILES_CONF="$guest_staging/profiles" \
TKLBAM_LIB_PATH=/usr/lib/tklbam \
LD_LIBRARY_PATH=/usr/lib/tklbam-pypy2/bin \
fab-chroot \
-e PROFILES_CONF:TKLBAM_LIB_PATH:LD_LIBRARY_PATH \
"$rootfs" --script "$staging/run-tklbam-profile"
cp -a "$staging/output/." "$O/$name.tklbam/"
)
fi
fi
if [[ -z "$no_screens" ]]; then
mkdir -p "$O/$name.screens"
Expand Down
10 changes: 6 additions & 4 deletions bin/purge-pkgs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,12 @@ EOF
exit 1
}

# catch --help first
if [[ " $* " == *"--help"* ]] || [[ " $* " == *"-h"* ]]; then
usage
fi
# Catch help options without treating a rootfs path containing "-h" as one.
for arg in "$@"; do
case "$arg" in
--help|-h) usage;;
esac
done
path_to_rootfs=$1
shift

Expand Down
11 changes: 9 additions & 2 deletions bt-ec2
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ Options::
(implies --secupdates)
--testing - allow use of current buildtasks branch (conflicts
with --publish &/or --marketplace &/or --copy)
--local-device= - populate this pre-attached block device and leave
AWS snapshot and image registration to the caller
--pvmshim - apply paravirtual shim so snapshot is pvm compat.
--pvmregister - register pvm-virtualized snapshot, too

Expand All @@ -49,7 +51,7 @@ EOF
}

ARGS=("$@")
unset force secupdates all_updates increment testing pvmshim appver
unset force secupdates all_updates increment testing local_device pvmshim appver
ebs_opts=()
while [[ $# -ne 0 ]]; do
case $1 in
Expand All @@ -62,6 +64,7 @@ while [[ $# -ne 0 ]]; do
--secupdates) secupdates="yes";;
--increment) increment="yes";;
--testing) testing="yes";;
--local-device=*) local_device=${1#*=};;
--pvmshim) pvmshim="yes";;
--pvmregister) ebs_opts+=("$1");;
*) if [[ -n "$appver" ]]; then
Expand Down Expand Up @@ -245,7 +248,11 @@ umount -l "$rootfs/proc" || true
"$BT/bin/rootfs-cleanup" "$rootfs"
"$BT/bin/aptconf-tag" "$rootfs" ec2
"$BT/bin/build-tag" "$rootfs" ec2
if [[ -f /usr/bin/python ]]; then
if [[ -n "$local_device" ]]; then
[[ -b "$local_device" ]] \
|| fatal "local device is not a block device: $local_device"
"$BT/bin/ec2/ebs_populate.py" "$rootfs" "$local_device"
elif [[ -f /usr/bin/python ]]; then
"$BT/bin/ec2/legacy/ebs.py" "${ebs_opts[@]}" "$rootfs"
else
"$BT/bin/ec2/ebs.py" "${ebs_opts[@]}" "$rootfs"
Expand Down
2 changes: 1 addition & 1 deletion config.example/common.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export BT_PUBLISH_IMGS="s3://${BUCKET}/images"
export BT_PUBLISH_META="s3://${BUCKET}/metadata"
export BT_PUBLISH_SCREENS="s3://${BUCKET}/screens"
export BT_PUBLISH_PROFILES="s3://${BUCKET}/profiles"
export BT_GPGKEY=26147592087C0EDE42143B637761DEBABBCFBA7C
export BT_GPGKEY=DEE90D9970AE35D55B7C6C2DC6D254B76A9D9430
export BT_ISOS=/mnt/isos
export BT_IMGS=/mnt/imgs
export BT_QEMU=/mnt/qemu
Expand Down
14 changes: 12 additions & 2 deletions patches/cloud/overlay/usr/lib/inithooks/firstboot.d/40ec2-sshkeys
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,17 @@ if '_TURNKEY_INIT' in os.environ:
import pwd
import ec2metadata

USERNAME = 'root'

def login_username(config='/etc/default/inithooks'):
username = 'root'
with open(config) as fob:
for line in fob:
key, separator, value = line.partition('=')
if separator and key.strip() == 'SUDOADMIN':
if value.strip().strip("'\"").lower() == 'true':
username = 'admin'
break
return username


def authorize_sshkeys(keys, username):
Expand All @@ -31,7 +41,7 @@ def authorize_sshkeys(keys, username):
def main():
keys = ec2metadata.get('public-keys')
if keys:
authorize_sshkeys(keys, USERNAME)
authorize_sshkeys(keys, login_username())


if __name__ == "__main__":
Expand Down
Loading