Release builds the tag's commissioning image, without SSH keys
build-image.sh copied the build user's authorized_keys into the image unless told otherwise, so the images published with v0.2.0 and v0.2.1 let three lab keys in as root on commissioning machines. Keys are now opt-in (AUTHKEYS=); the lab deploy passes the deploy user's keys. The release workflow gets an image job: it builds the tag's agent/ on the PXE server in a directory of its own (the lab image stays), refuses an image with authorized_keys, and uploads it as commissioning-* (token over stdin). Tried by hand on pxe-server: built, no keys, new agents. Closes #29 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
008fa3f66f
commit
554b7bffd8
4 files changed
+66
-3
No files matched your search
@@ -80,7 +80,8 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
HOST: ${{ vars.PXE_HOST }}
|
HOST: ${{ vars.PXE_HOST }}
|
||||||
USER: ${{ vars.PXE_USER }}
|
USER: ${{ vars.PXE_USER }}
|
||||||
run: ssh "$USER@$HOST" 'sudo /opt/rollcall/agent/build-image.sh 2>&1 | tail -20'
|
# the lab image lets the deploy user's keys in as root (debugging); release images do not
|
||||||
|
run: ssh "$USER@$HOST" 'sudo env AUTHKEYS=$HOME/.ssh/authorized_keys /opt/rollcall/agent/build-image.sh 2>&1 | tail -20'
|
||||||
|
|
||||||
site:
|
site:
|
||||||
needs: server
|
needs: server
|
||||||
|
|||||||
@@ -69,3 +69,56 @@ jobs:
|
|||||||
-F "attachment=@$f" "$API/releases/$id/assets?name=$f" >/dev/null
|
-F "attachment=@$f" "$API/releases/$id/assets?name=$f" >/dev/null
|
||||||
echo "uploaded $f"
|
echo "uploaded $f"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# The commissioning image of this tag: built with live-build on the PXE server
|
||||||
|
# (the runner cannot), in a directory of its own (the lab's live image and
|
||||||
|
# build stay as they are), without anyone's SSH keys, and uploaded to the
|
||||||
|
# release as commissioning-* for bootstrap.sh --image-url.
|
||||||
|
image:
|
||||||
|
needs: release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: SSH setup
|
||||||
|
env:
|
||||||
|
KEY: ${{ secrets.PXE_SSH_KEY }}
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.ssh && chmod 700 ~/.ssh
|
||||||
|
printf '%s\n' "$KEY" > ~/.ssh/id_ed25519 && chmod 600 ~/.ssh/id_ed25519
|
||||||
|
printf 'Host *\n StrictHostKeyChecking accept-new\n ServerAliveInterval 30\n' > ~/.ssh/config
|
||||||
|
- name: Build the image on the PXE server
|
||||||
|
env:
|
||||||
|
HOST: ${{ vars.PXE_HOST }}
|
||||||
|
USER: ${{ vars.PXE_USER }}
|
||||||
|
TAG: ${{ github.ref_name }}
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
W=/var/tmp/rollcall-release-$TAG
|
||||||
|
ssh "$USER@$HOST" "sudo rm -rf $W && mkdir -p $W/src"
|
||||||
|
scp -q -r agent deploy/publish-image.sh "$USER@$HOST:$W/src/"
|
||||||
|
ssh "$USER@$HOST" "sudo env AUTHKEYS= BUILD=$W/build OUT=$W/image bash $W/src/agent/build-image.sh > $W/build.log 2>&1 || { tail -40 $W/build.log; exit 1; }; tail -5 $W/build.log"
|
||||||
|
# refuse an image that lets someone in (a listing that fails refuses too)
|
||||||
|
ssh "$USER@$HOST" "sudo unsquashfs -l $W/image/filesystem.squashfs > $W/files" ||
|
||||||
|
{ echo "::error::cannot list the image" >&2; exit 1; }
|
||||||
|
if ssh "$USER@$HOST" "grep -E '/\.ssh/authorized_keys' $W/files"; then
|
||||||
|
echo "::error::the image carries SSH authorized_keys" >&2; exit 1
|
||||||
|
fi
|
||||||
|
- name: Upload to the release
|
||||||
|
env:
|
||||||
|
HOST: ${{ vars.PXE_HOST }}
|
||||||
|
USER: ${{ vars.PXE_USER }}
|
||||||
|
TAG: ${{ github.ref_name }}
|
||||||
|
API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
|
||||||
|
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
W=/var/tmp/rollcall-release-$TAG
|
||||||
|
# the token over stdin, not on a command line
|
||||||
|
printf '%s\n' "$TOKEN" | ssh "$USER@$HOST" "read -r GITEA_TOKEN && export GITEA_TOKEN && IMG=$W/image sh $W/src/publish-image.sh $TAG $API"
|
||||||
|
- name: Clean up
|
||||||
|
if: always()
|
||||||
|
env:
|
||||||
|
HOST: ${{ vars.PXE_HOST }}
|
||||||
|
USER: ${{ vars.PXE_USER }}
|
||||||
|
TAG: ${{ github.ref_name }}
|
||||||
|
run: ssh "$USER@$HOST" "sudo rm -rf /var/tmp/rollcall-release-$TAG" || true
|
||||||
@@ -22,6 +22,8 @@ publish a tag that has no section here.
|
|||||||
address (DNS-published in `dns_view`, or IPAM-only), ownership by the `rollcall_machine` extensible
|
address (DNS-published in `dns_view`, or IPAM-only), ownership by the `rollcall_machine` extensible
|
||||||
attribute (kept with `extattrs+`), leases, fixed addresses and ranges count as taken, delegated
|
attribute (kept with `extattrs+`), leases, fixed addresses and ranges count as taken, delegated
|
||||||
networks by the `rollcall` attribute. Not tested against a real grid yet.
|
networks by the `rollcall` attribute. Not tested against a real grid yet.
|
||||||
|
- The release workflow builds the tag's commissioning image on the PXE server and publishes it as
|
||||||
|
`commissioning-*` assets.
|
||||||
- Lab NetBox (`lab/netbox/install.sh`): a container on the lab segment for the NetBox drivers.
|
- Lab NetBox (`lab/netbox/install.sh`): a container on the lab segment for the NetBox drivers.
|
||||||
- Fixed leases on the site's DHCP server for proxy-mode VLANs (`dhcp_reservations`), for Kea with
|
- Fixed leases on the site's DHCP server for proxy-mode VLANs (`dhcp_reservations`), for Kea with
|
||||||
`host_cmds` (Kea 3.0+) and OpenWrt (rpcd). Rollcall's own leases are reconciled; foreign ones are
|
`host_cmds` (Kea 3.0+) and OpenWrt (rpcd). Rollcall's own leases are reconciled; foreign ones are
|
||||||
@@ -49,6 +51,11 @@ publish a tag that has no section here.
|
|||||||
### Security
|
### Security
|
||||||
A review of the whole code base; the fixes below. Upgrading: rerun `deploy/install.sh` on the
|
A review of the whole code base; the fixes below. Upgrading: rerun `deploy/install.sh` on the
|
||||||
region and `deploy/install-rack.sh` on rack hosts (new dnsmasq layout, signed rack updates).
|
region and `deploy/install-rack.sh` on rack hosts (new dnsmasq layout, signed rack updates).
|
||||||
|
- **Root SSH keys in published images.** `agent/build-image.sh` took
|
||||||
|
the build user's `authorized_keys` unless told otherwise, so the images published with v0.2.0 and
|
||||||
|
v0.2.1 let three lab keys (the maintainer's laptop, the CI deploy key, the lab hypervisor) log in as
|
||||||
|
root to machines while they commission. Keys are now opt-in (`AUTHKEYS=`, the lab deploy sets it),
|
||||||
|
and the release workflow builds the image of the tag itself and refuses one with `authorized_keys`.
|
||||||
- **Region file read (critical).** A rack controller could read any file on the region (CA key,
|
- **Region file read (critical).** A rack controller could read any file on the region (CA key,
|
||||||
config) through `/api/v1/rack/binary?arch=../..`. `arch` is now a known architecture.
|
config) through `/api/v1/rack/binary?arch=../..`. `arch` is now a known architecture.
|
||||||
- **Path traversal.** In `/api/v1/render/...` (admin) as well.
|
- **Path traversal.** In `/api/v1/render/...` (admin) as well.
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ SRC=$(cd "$(dirname "$0")" && pwd)
|
|||||||
BUILD=${BUILD:-/srv/rollcall/build}
|
BUILD=${BUILD:-/srv/rollcall/build}
|
||||||
OUT=${OUT:-/srv/rollcall/image}
|
OUT=${OUT:-/srv/rollcall/image}
|
||||||
MIRROR=${MIRROR:-http://deb.debian.org/debian}
|
MIRROR=${MIRROR:-http://deb.debian.org/debian}
|
||||||
AUTHKEYS=${AUTHKEYS:-/home/ilker/.ssh/authorized_keys}
|
# root's SSH keys in the commissioning environment: none unless asked for (a lab
|
||||||
|
# debugging aid; a published image must not carry anyone's keys)
|
||||||
|
AUTHKEYS=${AUTHKEYS:-}
|
||||||
|
|
||||||
mkdir -p "$BUILD" "$OUT"
|
mkdir -p "$BUILD" "$OUT"
|
||||||
cd "$BUILD"
|
cd "$BUILD"
|
||||||
@@ -98,7 +100,7 @@ install -m 0755 "$SRC/rollcall-agent.py" config/includes.chroot/usr/local/bin/ro
|
|||||||
install -m 0755 "$SRC/rollcall-deploy.py" config/includes.chroot/usr/local/bin/rollcall-deploy.py
|
install -m 0755 "$SRC/rollcall-deploy.py" config/includes.chroot/usr/local/bin/rollcall-deploy.py
|
||||||
install -m 0644 "$SRC/rollcall-agent.service" config/includes.chroot/etc/systemd/system/rollcall-agent.service
|
install -m 0644 "$SRC/rollcall-agent.service" config/includes.chroot/etc/systemd/system/rollcall-agent.service
|
||||||
printf 'ipmi_msghandler\nipmi_devintf\nipmi_si\nipmi_ssif\n' > config/includes.chroot/etc/modules-load.d/ipmi.conf
|
printf 'ipmi_msghandler\nipmi_devintf\nipmi_si\nipmi_ssif\n' > config/includes.chroot/etc/modules-load.d/ipmi.conf
|
||||||
if [ -r "$AUTHKEYS" ]; then
|
if [ -n "$AUTHKEYS" ] && [ -r "$AUTHKEYS" ]; then
|
||||||
install -m 0600 "$AUTHKEYS" config/includes.chroot/root/.ssh/authorized_keys
|
install -m 0600 "$AUTHKEYS" config/includes.chroot/root/.ssh/authorized_keys
|
||||||
chmod 0700 config/includes.chroot/root/.ssh
|
chmod 0700 config/includes.chroot/root/.ssh
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in new issue
Block a user