Refresh agent signing keys from repository (Refs #64)
This commit is contained in:
+19
-8
@@ -96,11 +96,6 @@ def trusted_json(path):
|
||||
|
||||
trusted_directories(root)
|
||||
trusted_directories(config.parent)
|
||||
keyring = config.with_name('release-signing.gpg')
|
||||
try:
|
||||
trusted_file(keyring)
|
||||
except FileNotFoundError:
|
||||
raise SystemExit('Provision the publisher public key at /etc/electric-cherry/release-signing.gpg through a trusted channel before installing. Never obtain the trust key from the controller.') from None
|
||||
mountinfo = pathlib.Path('/proc/1/mountinfo')
|
||||
if mountinfo.exists():
|
||||
for line in mountinfo.read_text().splitlines():
|
||||
@@ -147,7 +142,7 @@ PY
|
||||
if [ -f /etc/electric-cherry/agent.json ]; then existing_enrollment=true; fi
|
||||
if [ -d "$data_dir" ]; then existing_data_root=true; fi
|
||||
installation_preflight
|
||||
temporary_dir=$(mktemp -d)
|
||||
temporary_dir=$(mktemp -d /tmp/electric-cherry-install.XXXXXXXXXX)
|
||||
updater_timer_was_active=false
|
||||
update_lock_held=false
|
||||
activity_lock_held=false
|
||||
@@ -172,23 +167,39 @@ installation_cleanup() {
|
||||
exit "$ec_install_status"
|
||||
}
|
||||
trap installation_cleanup EXIT
|
||||
# INSTALLATION_TRUST: always fetch directly from the repository, never the controller.
|
||||
key_status=$(curl --proto '=https' --tlsv1.2 --fail --silent --show-error \
|
||||
--connect-timeout 25 --max-time 120 --max-filesize 262144 \
|
||||
--header 'Cache-Control: no-cache' --write-out '%{http_code}' \
|
||||
'https://gitea.thewrightserver.net/josh/electric-cherry/raw/branch/main/agent/release-signing.gpg' \
|
||||
-o "$temporary_dir/release-signing.gpg")
|
||||
[ "$key_status" = 200 ] && [ -s "$temporary_dir/release-signing.gpg" ] || {
|
||||
echo 'The release-signing repository did not return a public key. Installation stopped.' >&2
|
||||
exit 1
|
||||
}
|
||||
# INSTALLATION_TRUST_END
|
||||
controller="${controller%/}"
|
||||
curl --proto '=https' --tlsv1.2 --fail --silent --show-error "$controller/api/agent/release/manifest.json" -o "$temporary_dir/manifest.json"
|
||||
curl --proto '=https' --tlsv1.2 --fail --silent --show-error "$controller/api/agent/release/manifest.json.sig" -o "$temporary_dir/manifest.json.sig"
|
||||
curl --proto '=https' --tlsv1.2 --fail --silent --show-error "$controller/api/agent/release/ec-agent.py" -o "$temporary_dir/ec-agent.py"
|
||||
curl --proto '=https' --tlsv1.2 --fail --silent --show-error "$controller/api/agent/release/ec-agent-update.py" -o "$temporary_dir/ec-agent-update.py"
|
||||
python3 - "$temporary_dir/manifest.json" "$temporary_dir/ec-agent.py" "$temporary_dir/ec-agent-update.py" /etc/electric-cherry/release-signing.gpg <<'PY'
|
||||
python3 - "$temporary_dir/manifest.json" "$temporary_dir/ec-agent.py" "$temporary_dir/ec-agent-update.py" "$temporary_dir/release-signing.gpg" <<'PY'
|
||||
# INSTALLATION_RELEASE: check both downloads before installing or executing either.
|
||||
import ast, hashlib, json, pathlib, re, stat, subprocess, sys, tempfile
|
||||
manifest_path = pathlib.Path(sys.argv[1])
|
||||
manifest_bytes = manifest_path.read_bytes()
|
||||
signature = manifest_path.with_name('manifest.json.sig')
|
||||
keyring = pathlib.Path(sys.argv[4])
|
||||
for path in (*reversed(keyring.parents), keyring):
|
||||
# The key lives in our private root-owned mktemp directory under sticky /tmp.
|
||||
# /tmp itself is intentionally writable; require protection of the directory
|
||||
# and file we created, rather than accepting a pre-existing host keyring.
|
||||
for path in (keyring.parent, keyring):
|
||||
info = path.lstat()
|
||||
expected_type = stat.S_ISREG if path == keyring else stat.S_ISDIR
|
||||
if not expected_type(info.st_mode) or info.st_uid != 0 or info.st_mode & 0o022:
|
||||
raise SystemExit('Release signing key must be a regular root-owned file in protected directories.')
|
||||
if not 0 < keyring.stat().st_size <= 256 * 1024:
|
||||
raise SystemExit('Repository public key is empty or exceeds its size limit.')
|
||||
if len(manifest_bytes) > 16 * 1024 or signature.stat().st_size > 16 * 1024:
|
||||
raise SystemExit('Release signing metadata exceeds its size limit.')
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user