Change to a non-root account with ssh key

This commit is contained in:
Audiobuzz
2026-06-16 11:12:23 +00:00
parent 450a989d4e
commit df27be40f1
3 changed files with 63 additions and 0 deletions
+3
View File
@@ -15,6 +15,8 @@
# Required Gitea repo secrets:
# REGISTRY_USERNAME - registry user for `oci_image` (oci_push=true)
# REGISTRY_TOKEN - registry password/token
# SSH_AUTHORIZED_KEY - public key for the sudo admin user; empty => no admin
# user + root left unlocked (lab). Set it to lock root.
# # SB_KEY, SB_CERT - (future) Secure Boot signing key + cert for signed UKIs
#
# Runner: needs KVM (qemu accelerator=kvm) and the build-host tooling installed
@@ -153,4 +155,5 @@ jobs:
-var oci_push=true \
-var disable_dot1x=${{ env.DISABLE_DOT1X }} \
-var nvmet_traddr="${{ env.NVMET_TRADDR }}" \
-var ssh_authorized_key="${{ secrets.SSH_AUTHORIZED_KEY }}" \
.
+52
View File
@@ -100,6 +100,18 @@ variable "nvmet_traddr" {
default = "nvme.fleet.lab"
}
variable "ssh_authorized_key" {
type = string
description = "Single SSH public key authorized for the sudo-enabled admin user. Fleet-generic (same on every host — not per-host state). Baked into the immutable rootfs, so rotating it needs a rebuild. EMPTY = no admin user is created and root is left UNLOCKED (lab convenience, avoids a no-way-in image)."
default = ""
}
variable "admin_user" {
type = string
description = "Username of the sudo-enabled, key-only admin account baked into the image (wheel group, NOPASSWD sudo since the account has no password). Created only when ssh_authorized_key is set."
default = "fleetadmin"
}
variable "sb_key" {
type = string
description = "Secure Boot signing key (PEM) for the UKI. Empty = unsigned lab build. Production: CI/HSM-held; never enters the build VM."
@@ -232,6 +244,46 @@ build {
]
}
# --- 2c. Admin access: sudo-enabled key user + locked root --------------
# Fleet-generic (same user + key on every host => NOT per-host state). The
# key comes from var.ssh_authorized_key (CI secret). If no key is supplied we
# skip the whole thing AND leave root unlocked, so a lab build can't end up
# with no way in. Runs LAST: locking root is safe because Packer drives
# shutdown over the already-authenticated SSH session (no re-auth as root).
provisioner "shell" {
environment_vars = [
"SSH_AUTHORIZED_KEY=${var.ssh_authorized_key}",
"ADMIN_USER=${var.admin_user}",
]
inline = [
"set -e",
"if [ -z \"$SSH_AUTHORIZED_KEY\" ]; then echo 'WARNING: ssh_authorized_key empty -> not creating admin user, leaving root UNLOCKED (lab build).'; exit 0; fi",
"id \"$ADMIN_USER\" >/dev/null 2>&1 || useradd -m -G wheel \"$ADMIN_USER\"",
"install -d -m0700 -o \"$ADMIN_USER\" -g \"$ADMIN_USER\" \"/home/$ADMIN_USER/.ssh\"",
"printf '%s\\n' \"$SSH_AUTHORIZED_KEY\" > \"/home/$ADMIN_USER/.ssh/authorized_keys\"",
"chmod 0600 \"/home/$ADMIN_USER/.ssh/authorized_keys\"",
"chown \"$ADMIN_USER:$ADMIN_USER\" \"/home/$ADMIN_USER/.ssh/authorized_keys\"",
"restorecon -R \"/home/$ADMIN_USER/.ssh\"",
# Key-only account (useradd leaves no password), so sudo must not prompt
# for one. NOPASSWD is a STIG finding (tracked in TODO #2) — acceptable for
# a key-only admin; reconcile when hardening lands.
"printf '%s ALL=(ALL) NOPASSWD:ALL\\n' \"$ADMIN_USER\" > \"/etc/sudoers.d/$ADMIN_USER\"",
"chmod 0440 \"/etc/sudoers.d/$ADMIN_USER\"",
"visudo -cf \"/etc/sudoers.d/$ADMIN_USER\"",
"restorecon \"/etc/sudoers.d/$ADMIN_USER\"",
# Image sshd: no root login, key-only. A drop-in (read via Include before
# the main config) overrides the ks's PermitRootLogin yes. Takes effect on
# the next boot only (sshd isn't restarted), so the live build session is
# unaffected.
"printf 'PermitRootLogin no\\nPasswordAuthentication no\\nKbdInteractiveAuthentication no\\n' > /etc/ssh/sshd_config.d/50-fleet-hardening.conf",
"chmod 0644 /etc/ssh/sshd_config.d/50-fleet-hardening.conf",
"restorecon /etc/ssh/sshd_config.d/50-fleet-hardening.conf",
# Lock root in the image. Done last; admin access is via the key user.
"passwd -l root",
"echo \"root locked; admin user $ADMIN_USER created (wheel + NOPASSWD sudo, key-only).\"",
]
}
# --- 3. Extract kernel, initramfs, rootfs from the built disk ------------
# --- 4. Assemble + sign the UKI; build the OCI release-artifact image ----
post-processor "shell-local" {
+8
View File
@@ -56,6 +56,14 @@ and cleanup.
report-only.
- Note: orthogonal to the `rd.dot1x=0` test toggle, but a full STIG production
posture wants 802.1X re-enabled.
- **Credentials (partly done):** root is now **locked** and access is via a
baked sudo-enabled key-only admin user (`var.ssh_authorized_key` /
`var.admin_user`, `wheel` + NOPASSWD sudo), with sshd drop-in
`PermitRootLogin no` / `PasswordAuthentication no`. Two items for STIG to
reconcile: **NOPASSWD sudo** is a finding (acceptable for a key-only account,
but document/deviate), and the admin key/user are **fleet-wide baked** — for
per-host/rotatable access without rebuilds, layer the signed HTTP/SMBIOS
config-agent or an SSH CA (`TrustedUserCAKeys`) discussed in session.
## [ ] 3. Roll the errata into design doc v3