fix(enrollment): repair node enrollment path (#24)

* fix(enrollment): repair node enrollment path

Make trimmed Agent configuration fail closed, harden Control URL validation, add device-code and bootstrap delivery, and make alpha.7 Control updates migration- and recovery-safe.

* fix(enrollment): allow agent token cleanup

---------

Co-authored-by: Ochenstarik <ochenstarik@inbox.ru>
This commit is contained in:
ochenstarik-ui 2026-08-09 22:32:53 +07:00 committed by GitHub
parent 1b798e7ee8
commit 80b4797f2a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 1136 additions and 92 deletions

View file

@ -90,54 +90,11 @@ jobs:
tar -C smoke -czf smoke-release.tar.gz agent control provisioning-helper deploy bootstrap
sha256sum smoke-release.tar.gz > smoke-release.tar.gz.sha256
- name: Verify repeated systemd installation
run: |
set -Eeuo pipefail
smoke_log=/tmp/smm-systemd-smoke.log
report_failure() {
local title="$1" message main_pid
sudo systemctl status --no-pager ochenstarik-smm-control.service >>"$smoke_log" 2>&1 || true
sudo journalctl --no-pager -u ochenstarik-smm-control.service -n 40 >>"$smoke_log" 2>&1 || true
sudo ss -ltnp >>"$smoke_log" 2>&1 || true
main_pid="$(sudo systemctl show -p MainPID --value ochenstarik-smm-control.service 2>/dev/null || true)"
if [[ "$main_pid" =~ ^[1-9][0-9]*$ ]]; then
sudo sh -c "tr '\0' '\n' </proc/$main_pid/environ | grep '^ASPNETCORE_'" \
>>"$smoke_log" 2>&1 || true
sudo sh -c "printf 'wchan='; cat /proc/$main_pid/wchan" >>"$smoke_log" 2>&1 || true
fi
message="$(tail -c 6000 "$smoke_log" | sed ':a;N;$!ba;s/%/%25/g;s/\r/%0D/g;s/\n/%0A/g')"
printf '::error title=%s::%s\n' "$title" "$message"
exit 1
}
sudo deploy/ochenstarik-server-monitor-manager.sh install-control \
smoke-release.tar.gz 127.0.0.1 7443 >"$smoke_log" 2>&1 \
|| report_failure 'Initial Control installation failed'
sudo test -x /usr/local/sbin/ochenstarik-smm-emergency \
|| report_failure 'Emergency recovery command was not installed'
sudo /usr/local/sbin/ochenstarik-smm-emergency status >>"$smoke_log" 2>&1 \
|| report_failure 'Emergency recovery status failed'
for _ in {1..30}; do
sudo curl --fail --silent --cacert /etc/ochenstarik-server-monitor-manager/control-ca.crt \
https://127.0.0.1:7443/healthz && break
sleep 1
done
sudo curl --fail --silent --show-error \
--cacert /etc/ochenstarik-server-monitor-manager/control-ca.crt \
https://127.0.0.1:7443/healthz >>"$smoke_log" 2>&1 \
|| report_failure 'Initial Control healthcheck failed'
sudo deploy/ochenstarik-server-monitor-manager.sh install-control \
smoke-release.tar.gz 127.0.0.1 7443 >"$smoke_log" 2>&1 \
|| report_failure 'Repeated Control installation failed'
sudo systemctl restart ochenstarik-smm-control.service \
|| report_failure 'Control restart failed'
sudo systemctl is-active --quiet ochenstarik-smm-control.service \
|| report_failure 'Control is inactive after restart'
sudo curl --fail --silent --show-error \
--retry 10 --retry-all-errors --retry-delay 1 \
--cacert /etc/ochenstarik-server-monitor-manager/control-ca.crt \
https://127.0.0.1:7443/healthz >>"$smoke_log" 2>&1 \
|| report_failure 'Repeated Control healthcheck failed'
- name: Verify complete published enrollment with systemd
run: bash tests/bootstrap/run-native-systemd-smoke.sh smoke-release.tar.gz deploy/ochenstarik-server-monitor-manager.sh
- name: Clean up systemd smoke installation
if: always()
run: sudo deploy/ochenstarik-server-monitor-manager.sh uninstall-control --confirm-destroy-control || true
run: |
sudo /usr/local/sbin/ochenstarik-server-monitor-manager.sh uninstall-agent --purge || true
sudo /usr/local/sbin/ochenstarik-server-monitor-manager.sh uninstall-control --confirm-destroy-control || true

View file

@ -16,6 +16,7 @@ readonly AGENT_UNIT="ochenstarik-smm-agent.service"
readonly PROVISIONING_HELPER_UNIT="ochenstarik-smm-provisioning-helper.service"
readonly POLICY_HELPER="/usr/local/libexec/ochenstarik-smm-policy-apply"
readonly EMERGENCY_COMMAND="/usr/local/sbin/ochenstarik-smm-emergency"
readonly BOOTSTRAP_COMMAND="/usr/local/sbin/ochenstarik-server-monitor-manager.sh"
readonly SUDOERS_FILE="/etc/sudoers.d/ochenstarik-smm-control"
readonly MESH_DIR="${STATE_DIR}/mesh"
readonly WG_DIR="${ETC_DIR}/wireguard"
@ -27,11 +28,25 @@ TEMP_DIR=""
MESH_PEER_CODE=""
ENROLLMENT_TOKEN_FILE=""
ENROLLMENT_TOKEN_TEMP=""
CONTROL_UPDATE_BACKUP_ID=""
CONTROL_UPDATE_RECOVERY_REQUIRED=0
CONTROL_UPDATE_LEGACY_ITEMS=()
log() { printf '%s\n' "[$PROGRAM] $*"; }
fail() { printf '%s\n' "[$PROGRAM] ERROR: $*" >&2; exit 1; }
cleanup() {
local status=$?
trap - EXIT
if [[ "$CONTROL_UPDATE_RECOVERY_REQUIRED" == "1" ]]; then
log "Control update failed; restoring the pre-update state."
if recover_control_update "$CONTROL_UPDATE_BACKUP_ID"; then
log "Control recovery completed."
else
printf '%s\n' "[$PROGRAM] ERROR: Automatic Control recovery failed; manual recovery is required." >&2
status=1
fi
fi
if [[ -n "$ENROLLMENT_TOKEN_FILE" ]]; then
rm -f -- "$ENROLLMENT_TOKEN_FILE"
fi
@ -41,6 +56,7 @@ cleanup() {
if [[ -n "$TEMP_DIR" && -d "$TEMP_DIR" ]]; then
rm -rf -- "$TEMP_DIR"
fi
exit "$status"
}
trap cleanup EXIT
@ -61,6 +77,7 @@ Usage:
ochenstarik-server-monitor-manager.sh update-agent ARCHIVE
ochenstarik-server-monitor-manager.sh rollback control|agent [BACKUP_ID]
ochenstarik-server-monitor-manager.sh node-code NODE_ID
ochenstarik-server-monitor-manager.sh control-device-code DEVICE_ID
ochenstarik-server-monitor-manager.sh node-token NODE_ID
ochenstarik-server-monitor-manager.sh control-ca-fingerprint
ochenstarik-server-monitor-manager.sh status
@ -125,13 +142,99 @@ validate_node_id() {
}
validate_port() {
[[ "$1" =~ ^[0-9]+$ ]] && (( 10#$1 >= 1 && 10#$1 <= 65535 )) \
[[ "$1" =~ ^[0-9]{1,5}$ ]] && (( 10#$1 >= 1 && 10#$1 <= 65535 )) \
|| fail "Port must be in range 1-65535."
}
validate_ipv4_literal() {
local address="$1" octet
local -a octets
IFS=. read -r -a octets <<<"$address"
(( ${#octets[@]} == 4 )) || fail "Control URL contains an invalid IPv4 host."
for octet in "${octets[@]}"; do
[[ "$octet" =~ ^[0-9]{1,3}$ ]] && (( 10#$octet <= 255 )) \
|| fail "Control URL contains an invalid IPv4 host."
done
}
validate_control_url() {
[[ "$1" =~ ^https://[A-Za-z0-9._:\[\]-]+(:[0-9]{1,5})?/?$ ]] \
local authority host port="" label left right compressed=0 ipv4_groups=0 group_count last_group_index
local -a groups labels right_groups
[[ "$1" == https://* ]] || fail "Control URL must be an https URL without a path or credentials."
authority="${1#https://}"
authority="${authority%/}"
[[ -n "$authority" && "$authority" != *['/?#@']* ]] \
|| fail "Control URL must be an https URL without a path or credentials."
if [[ "$authority" == \[* ]]; then
[[ "$authority" =~ ^\[([0-9A-Fa-f:.]+)\](:([0-9]+))?$ ]] \
|| fail "Control URL contains an invalid bracketed IPv6 authority."
host="${BASH_REMATCH[1]}"
port="${BASH_REMATCH[3]:-}"
[[ "$host" == *:* ]] || fail "Control URL contains an invalid bracketed IPv6 authority."
[[ "$host" != *:::* ]] || fail "Control URL contains an invalid bracketed IPv6 authority."
[[ "$host" != :* || "$host" == ::* ]] \
|| fail "Control URL contains an invalid bracketed IPv6 authority."
[[ "$host" != *: || "$host" == *:: ]] \
|| fail "Control URL contains an invalid bracketed IPv6 authority."
groups=()
if [[ "$host" == *::* ]]; then
compressed=1
[[ "${host/::/}" != *::* ]] \
|| fail "Control URL contains an invalid bracketed IPv6 authority."
left="${host%%::*}"
right="${host#*::}"
if [[ -n "$left" ]]; then
IFS=: read -r -a groups <<<"$left"
fi
if [[ -n "$right" ]]; then
IFS=: read -r -a right_groups <<<"$right"
groups+=("${right_groups[@]}")
fi
else
IFS=: read -r -a groups <<<"$host"
fi
if (( ${#groups[@]} > 0 )) && [[ "${groups[${#groups[@]}-1]}" == *.* ]]; then
last_group_index=$(( ${#groups[@]} - 1 ))
validate_ipv4_literal "${groups[$last_group_index]}"
unset "groups[$last_group_index]"
ipv4_groups=2
fi
for label in "${groups[@]}"; do
[[ "$label" =~ ^[0-9A-Fa-f]{1,4}$ ]] \
|| fail "Control URL contains an invalid bracketed IPv6 authority."
done
group_count=$(( ${#groups[@]} + ipv4_groups ))
if (( compressed == 1 )); then
(( group_count < 8 )) || fail "Control URL contains an invalid bracketed IPv6 authority."
else
(( group_count == 8 )) || fail "Control URL contains an invalid bracketed IPv6 authority."
fi
else
[[ "$authority" != *:*:* ]] \
|| fail "Control URL IPv6 authorities must use balanced brackets."
if [[ "$authority" == *:* ]]; then
host="${authority%%:*}"
port="${authority#*:}"
[[ -n "$port" ]] || fail "Control URL contains an invalid port."
else
host="$authority"
fi
[[ "$host" =~ ^[A-Za-z0-9]([A-Za-z0-9.-]*[A-Za-z0-9])?$ ]] \
|| fail "Control URL contains an invalid DNS or IPv4 host."
[[ ${#host} -le 253 && "$host" != *..* ]] \
|| fail "Control URL contains an invalid DNS or IPv4 host."
IFS=. read -r -a labels <<<"$host"
for label in "${labels[@]}"; do
[[ ${#label} -le 63 && "$label" =~ ^[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?$ ]] \
|| fail "Control URL contains an invalid DNS or IPv4 host."
done
if [[ "$host" =~ ^[0-9.]+$ ]]; then
validate_ipv4_literal "$host"
fi
fi
[[ -n "$host" ]] || fail "Control URL host is empty."
[[ -z "$port" ]] || validate_port "$port"
}
verify_archive() {
@ -269,6 +372,137 @@ install_unit() {
systemctl daemon-reload
}
install_bootstrap_command() {
local source="$TEMP_DIR/bootstrap/ochenstarik-server-monitor-manager.sh" staging
[[ -x "$source" ]] || fail "Packaged bootstrap is missing."
install -d -m 0755 "$(dirname "$BOOTSTRAP_COMMAND")"
staging="$(mktemp "$(dirname "$BOOTSTRAP_COMMAND")/.ochenstarik-server-monitor-manager.XXXXXXXX")"
if ! install -m 0755 -o root -g root "$source" "$staging"; then
rm -f -- "$staging"
fail "Could not stage the system bootstrap command."
fi
if ! mv -fT -- "$staging" "$BOOTSTRAP_COMMAND"; then
rm -f -- "$staging"
fail "Could not publish the system bootstrap command."
fi
}
validate_control_state_migration() {
local name
for name in control.db control.db-wal control.db-shm; do
[[ ! -e "$STATE_DIR/$name" || ! -e "$STATE_DIR/control/$name" ]] \
|| fail "Both legacy and role-isolated Control state exist: $name"
done
[[ ! -e "$STATE_DIR/backups" || ! -e "$STATE_DIR/control/backups" ]] \
|| fail "Both legacy and role-isolated Control backup directories exist."
}
record_control_legacy_state() {
local name
CONTROL_UPDATE_LEGACY_ITEMS=()
for name in control.db control.db-wal control.db-shm; do
[[ ! -e "$STATE_DIR/$name" ]] || CONTROL_UPDATE_LEGACY_ITEMS+=("$name")
done
[[ ! -e "$STATE_DIR/backups" ]] || CONTROL_UPDATE_LEGACY_ITEMS+=(backups)
}
prepare_control_state() {
local name
install -d -m 0700 -o "$CONTROL_USER" -g "$CONTROL_USER" "$STATE_DIR/control"
for name in control.db control.db-wal control.db-shm; do
if [[ -e "$STATE_DIR/$name" ]]; then
mv -- "$STATE_DIR/$name" "$STATE_DIR/control/$name"
fi
done
if [[ -e "$STATE_DIR/backups" ]]; then
mv -- "$STATE_DIR/backups" "$STATE_DIR/control/backups"
fi
install -d -m 0700 -o "$CONTROL_USER" -g "$CONTROL_USER" "$STATE_DIR/control/backups"
chown -R "$CONTROL_USER:$CONTROL_USER" "$STATE_DIR/control"
find "$STATE_DIR/control" -type d -exec chmod 0700 {} +
find "$STATE_DIR/control" -type f -exec chmod 0600 {} +
}
reverse_control_state_migration() {
local name source destination
for name in "${CONTROL_UPDATE_LEGACY_ITEMS[@]}"; do
source="$STATE_DIR/control/$name"
destination="$STATE_DIR/$name"
if [[ -e "$destination" ]]; then
[[ ! -e "$source" ]] || return 1
continue
fi
[[ ! -e "$source" ]] || mv -- "$source" "$destination" || return 1
done
rmdir "$STATE_DIR/control/backups" 2>/dev/null || true
rmdir "$STATE_DIR/control" 2>/dev/null || true
}
restore_control_update_backup() {
local archive="$1" restore_root="${2:-/}"
[[ -f "$archive" ]] || return 1
tar -C "$restore_root" -xzf "$archive"
}
restore_control_binary_from_archive() {
local archive="$1" restore_root="${2:-/}"
[[ -f "$archive" ]] || return 1
tar -C "$restore_root" -xzf "$archive" \
usr/local/lib/ochenstarik-server-monitor-manager/control
}
recover_control_update() {
local backup_id="$1" restore_root="${2:-/}"
local archive="$BACKUP_DIR/${backup_id}.tar.gz"
systemctl stop "$CONTROL_UNIT" 2>/dev/null || true
reverse_control_state_migration || return 1
restore_control_update_backup "$archive" "$restore_root" || return 1
systemctl daemon-reload || return 1
systemctl start "$CONTROL_UNIT" || return 1
systemctl is-active --quiet "$CONTROL_UNIT"
}
validate_control_environment_migration() {
local env_file="$ETC_DIR/control.env" database_count backup_count database_value backup_value
[[ -f "$env_file" && ! -L "$env_file" ]] \
|| fail "Control environment is missing or unsafe."
database_count="$(grep -c '^Control__DatabasePath=' "$env_file" || true)"
backup_count="$(grep -c '^Control__BackupDirectory=' "$env_file" || true)"
[[ "$database_count" == 1 && "$backup_count" == 1 ]] \
|| fail "Control environment contains missing or conflicting state paths."
database_value="$(grep '^Control__DatabasePath=' "$env_file")"
backup_value="$(grep '^Control__BackupDirectory=' "$env_file")"
case "$database_value" in
"Control__DatabasePath=$STATE_DIR/control.db"|"Control__DatabasePath=$STATE_DIR/control/control.db") ;;
*) fail "Control environment contains an unsupported database path." ;;
esac
case "$backup_value" in
"Control__BackupDirectory=$STATE_DIR/backups"|"Control__BackupDirectory=$STATE_DIR/control/backups") ;;
*) fail "Control environment contains an unsupported backup path." ;;
esac
}
rewrite_control_environment() {
local env_file="$ETC_DIR/control.env" staging line
staging="$(mktemp "$ETC_DIR/.control.env.XXXXXXXX")"
if ! while IFS= read -r line || [[ -n "$line" ]]; do
case "$line" in
Control__DatabasePath=*) printf 'Control__DatabasePath=%s/control/control.db\n' "$STATE_DIR" ;;
Control__BackupDirectory=*) printf 'Control__BackupDirectory=%s/control/backups\n' "$STATE_DIR" ;;
*) printf '%s\n' "$line" ;;
esac
done <"$env_file" >"$staging"; then
rm -f -- "$staging"
fail "Could not rewrite the Control environment."
fi
chown root:"$CONTROL_USER" "$staging"
chmod 0640 "$staging"
if ! mv -fT -- "$staging" "$env_file"; then
rm -f -- "$staging"
fail "Could not publish the Control environment."
fi
}
write_mesh_firewall() {
cat >"$ETC_DIR/mesh.nft" <<'EOF'
table inet ochenstarik_smm {
@ -480,18 +714,21 @@ install_control() {
[[ -x "$TEMP_DIR/control/ochenstarik-smm-control" ]] || fail "Control binary is missing."
backup_id="$(create_backup control)"
ensure_system_user "$CONTROL_USER"
install -d -m 0750 -o root -g "$CONTROL_USER" "$ETC_DIR"
install -d -m 0750 -o "$CONTROL_USER" -g "$CONTROL_USER" "$STATE_DIR" "$STATE_DIR/backups"
validate_control_state_migration
systemctl stop "$CONTROL_UNIT" 2>/dev/null || true
install -d -m 0711 -o root -g root "$ETC_DIR" "$STATE_DIR"
prepare_control_state
install_tree_atomic "$TEMP_DIR/control" "$LIB_DIR/control" "root:root"
install_bootstrap_command
if [[ ! -f "$ETC_DIR/control-ca.pfx" || ! -f "$ETC_DIR/control-server.pfx" ]]; then
create_control_certificates "$public_host"
fi
cat >"$ETC_DIR/control.env" <<EOF
ASPNETCORE_URLS=https://0.0.0.0:$port
ASPNETCORE_Kestrel__Certificates__Default__Path=$ETC_DIR/control-server.pfx
Control__DatabasePath=$STATE_DIR/control.db
Control__DatabasePath=$STATE_DIR/control/control.db
Control__CertificateAuthorityPath=$ETC_DIR/control-ca.pfx
Control__BackupDirectory=$STATE_DIR/backups
Control__BackupDirectory=$STATE_DIR/control/backups
Control__HubHelperPath=$POLICY_HELPER
Control__PrivilegeEscalationPath=/usr/bin/sudo
Control__LinkReconciliationSeconds=300
@ -550,12 +787,13 @@ install_agent() {
[[ -x "$TEMP_DIR/provisioning-helper/ochenstarik-smm-provisioning-helper" ]] || fail "Provisioning helper binary is missing."
backup_id="$(create_backup agent)"
ensure_system_user "$AGENT_USER"
install -d -m 0750 -o root -g "$AGENT_USER" "$ETC_DIR"
install -d -m 0711 -o root -g root "$ETC_DIR" "$STATE_DIR"
install -d -m 0700 -o "$AGENT_USER" -g "$AGENT_USER" "$STATE_DIR/agent"
install -d -m 0710 -o root -g "$AGENT_USER" "$ENROLLMENT_DIR"
install -d -m 0700 -o "$AGENT_USER" -g "$AGENT_USER" "$ENROLLMENT_DIR"
install -d -m 0700 -o root -g root "$STATE_DIR/provisioning/rollback"
install_tree_atomic "$TEMP_DIR/agent" "$LIB_DIR/agent" "root:root"
install_tree_atomic "$TEMP_DIR/provisioning-helper" "$LIB_DIR/provisioning-helper" "root:root"
install_bootstrap_command
install -d -m 0755 "$(dirname "$EMERGENCY_COMMAND")"
install -m 0755 "$TEMP_DIR/deploy/ochenstarik-smm-emergency" "$EMERGENCY_COMMAND"
if [[ "$(realpath "$ca_cert")" != "$(realpath -m "$ETC_DIR/control-ca.crt")" ]]; then
@ -727,16 +965,32 @@ update_role() {
*) fail "Unknown role: $role" ;;
esac
[[ -x "$TEMP_DIR/$role/$binary" ]] || fail "$role binary is missing."
if [[ "$role" == "agent" ]]; then
if [[ "$role" == "control" ]]; then
validate_control_state_migration
validate_control_environment_migration
record_control_legacy_state
else
refresh_agent_uid
fi
backup_id="$(create_backup "$role")"
if [[ "$role" == "control" ]]; then
CONTROL_UPDATE_BACKUP_ID="$backup_id"
CONTROL_UPDATE_RECOVERY_REQUIRED=1
fi
systemctl stop "$unit"
if [[ "$role" == "agent" ]]; then
systemctl stop "$PROVISIONING_HELPER_UNIT" 2>/dev/null || true
fi
backup_id="$(create_backup "$role")"
install_tree_atomic "$TEMP_DIR/$role" "$LIB_DIR/$role" "$user"
if [[ "$role" == "agent" ]]; then
if [[ "$role" == "control" ]]; then
install -d -m 0711 -o root -g root "$ETC_DIR" "$STATE_DIR"
prepare_control_state
rewrite_control_environment
install_bootstrap_command
install_unit "$TEMP_DIR/deploy/$CONTROL_UNIT" "$CONTROL_UNIT"
systemctl enable "$CONTROL_UNIT"
install_tree_atomic "$TEMP_DIR/control" "$LIB_DIR/control" "$user"
else
install_tree_atomic "$TEMP_DIR/$role" "$LIB_DIR/$role" "$user"
[[ -x "$TEMP_DIR/provisioning-helper/ochenstarik-smm-provisioning-helper" ]] \
|| fail "Provisioning helper binary is missing."
install_tree_atomic "$TEMP_DIR/provisioning-helper" "$LIB_DIR/provisioning-helper" "root:root"
@ -744,12 +998,19 @@ update_role() {
install_unit "$TEMP_DIR/deploy/$PROVISIONING_HELPER_UNIT" "$PROVISIONING_HELPER_UNIT"
systemctl enable --now "$PROVISIONING_HELPER_UNIT"
fi
systemctl start "$unit"
systemctl restart "$unit"
if ! systemctl is-active --quiet "$unit"; then
log "Update failed; restoring backup $backup_id"
restore_backup "$role" "$backup_id"
if [[ "$role" == "agent" ]]; then
log "Update failed; restoring backup $backup_id"
restore_backup "$role" "$backup_id"
fi
fail "$role update was rolled back."
fi
if [[ "$role" == "control" ]]; then
CONTROL_UPDATE_RECOVERY_REQUIRED=0
CONTROL_UPDATE_BACKUP_ID=""
CONTROL_UPDATE_LEGACY_ITEMS=()
fi
log "$role updated. Backup: $backup_id"
}
@ -768,7 +1029,11 @@ restore_backup() {
if [[ "$role" == "agent" ]]; then
systemctl stop "$PROVISIONING_HELPER_UNIT" || true
fi
tar -C / -xzf "$archive"
if [[ "$role" == "control" ]]; then
restore_control_binary_from_archive "$archive"
else
tar -C / -xzf "$archive"
fi
systemctl daemon-reload
if [[ "$role" == "agent" ]]; then
systemctl start "$PROVISIONING_HELPER_UNIT"
@ -849,6 +1114,27 @@ create_node_code() {
token=""
}
create_device_code() {
local device_id="$1" token control_url ca_der
require_root
validate_node_id "$device_id"
[[ -r "$ETC_DIR/control-public-url" ]] || fail "Control public URL is missing; reinstall Control with PUBLIC_HOST."
[[ -r "$ETC_DIR/control-ca.crt" ]] || fail "Control CA certificate is missing."
require_command base64
require_command openssl
control_url="$(tr -d '\r\n' <"$ETC_DIR/control-public-url")"
validate_control_url "$control_url"
token="$(run_control_cli device-token-create "$device_id")"
[[ "$token" =~ ^[A-Za-z0-9_-]{43}$ ]] \
|| fail "Control returned an invalid device enrollment token."
ca_der="$(openssl x509 -in "$ETC_DIR/control-ca.crt" -outform DER | base64 -w 0)"
printf 'SMMDEV1-'
printf 'VERSION=1\nDEVICE=%s\nTOKEN=%s\nURL=%s\nCA=%s\n' \
"$device_id" "$token" "$control_url" "$ca_der" | base64url_encode
printf '\n'
token=""
}
add_mesh_peer() {
local code="$1" prefix node_part address_part key_part extra
local node_id address public_key current tmp
@ -908,6 +1194,22 @@ show_ca_fingerprint() {
openssl x509 -in "$ETC_DIR/control-ca.crt" -noout -fingerprint -sha256
}
role_is_installed() {
case "$1" in
control) [[ -d "$LIB_DIR/control" || -f "$ETC_DIR/control.env" \
|| -f "/etc/systemd/system/${CONTROL_UNIT:-ochenstarik-smm-control.service}" ]] ;;
agent) [[ -d "$LIB_DIR/agent" || -f "$ETC_DIR/agent.env" \
|| -f "/etc/systemd/system/${AGENT_UNIT:-ochenstarik-smm-agent.service}" ]] ;;
*) return 1 ;;
esac
}
remove_shared_ca_if_unused() {
if ! role_is_installed control && ! role_is_installed agent; then
rm -f -- "$ETC_DIR/control-ca.crt"
fi
}
uninstall_agent() {
local purge="${1:-}"
require_root
@ -915,8 +1217,10 @@ uninstall_agent() {
systemctl disable --now "$PROVISIONING_HELPER_UNIT" 2>/dev/null || true
rm -f -- "/etc/systemd/system/$AGENT_UNIT" "/etc/systemd/system/$PROVISIONING_HELPER_UNIT" "$ETC_DIR/agent.env"
rm -rf -- "$LIB_DIR/agent" "$LIB_DIR/provisioning-helper"
[[ "$purge" == "--purge" ]] && rm -rf -- \
"$STATE_DIR/agent" "$ENROLLMENT_DIR" "$ETC_DIR/control-ca.crt"
if [[ "$purge" == "--purge" ]]; then
rm -rf -- "$STATE_DIR/agent" "$ENROLLMENT_DIR"
remove_shared_ca_if_unused
fi
systemctl daemon-reload
log "Agent removed${purge:+ ($purge)}."
}
@ -926,10 +1230,12 @@ uninstall_control() {
require_root
systemctl disable --now "$CONTROL_UNIT" 2>/dev/null || true
rm -f -- "/etc/systemd/system/$CONTROL_UNIT" "$ETC_DIR/control.env" \
"$ETC_DIR/control-ca.pfx" "$ETC_DIR/control-server.pfx" "$ETC_DIR/control-ca.crt" \
"$ETC_DIR/control-ca.pfx" "$ETC_DIR/control-server.pfx" \
"$POLICY_HELPER" "$SUDOERS_FILE"
rm -rf -- "$LIB_DIR/control" "$STATE_DIR/control.db" "$STATE_DIR/control.db-wal" \
rm -rf -- "$LIB_DIR/control" "$STATE_DIR/control" \
"$STATE_DIR/control.db" "$STATE_DIR/control.db-wal" \
"$STATE_DIR/control.db-shm" "$STATE_DIR/backups"
remove_shared_ca_if_unused
systemctl daemon-reload
log "Control role and its state were removed."
}
@ -947,9 +1253,9 @@ main() {
local action="${1:-help}"
shift || true
case "$action" in
help|-h|--help) usage ;;
version|--version) printf '%s %s\n' "$PROGRAM" "$PROGRAM_VERSION" ;;
preflight) preflight ;;
help|-h|--help) [[ $# -eq 0 ]] || fail "$action takes no arguments"; usage ;;
version|--version) [[ $# -eq 0 ]] || fail "$action takes no arguments"; printf '%s %s\n' "$PROGRAM" "$PROGRAM_VERSION" ;;
preflight) [[ $# -eq 0 ]] || fail "preflight takes no arguments"; preflight ;;
verify-release) [[ $# -eq 1 ]] || fail "verify-release requires ARCHIVE"; verify_release_payload "$1" ;;
install-control) [[ $# -ge 2 && $# -le 3 ]] || fail "install-control requires ARCHIVE PUBLIC_HOST [HTTPS_PORT]"; install_control "$@" ;;
install-agent) [[ $# -eq 4 ]] || fail "install-agent requires ARCHIVE NODE_ID CONTROL_URL CA_CERT"; install_agent "$@" ;;
@ -961,10 +1267,11 @@ main() {
update-agent) [[ $# -eq 1 ]] || fail "update-agent requires ARCHIVE"; update_role agent "$1" ;;
rollback) [[ $# -ge 1 && $# -le 2 ]] || fail "rollback requires control|agent [BACKUP_ID]"; rollback_role "$@" ;;
node-code) [[ $# -eq 1 ]] || fail "node-code requires NODE_ID"; create_node_code "$1" ;;
control-device-code) [[ $# -eq 1 ]] || fail "control-device-code requires DEVICE_ID"; create_device_code "$1" ;;
node-token) [[ $# -eq 1 ]] || fail "node-token requires NODE_ID"; run_control_cli token-create "$1" ;;
control-ca-fingerprint) [[ $# -eq 0 ]] || fail "control-ca-fingerprint takes no arguments"; show_ca_fingerprint ;;
status) show_status ;;
uninstall-agent) [[ $# -le 1 ]] || fail "uninstall-agent accepts only [--purge]"; uninstall_agent "${1:-}" ;;
status) [[ $# -eq 0 ]] || fail "status takes no arguments"; show_status ;;
uninstall-agent) [[ $# -eq 0 || ( $# -eq 1 && "$1" == "--purge" ) ]] || fail "uninstall-agent accepts only [--purge]"; uninstall_agent "${1:-}" ;;
uninstall-control) [[ $# -eq 1 ]] || fail "uninstall-control requires confirmation"; uninstall_control "$1" ;;
*) fail "Unknown action: $action (run with --help)" ;;
esac

View file

@ -22,7 +22,8 @@ ProtectControlGroups=true
LockPersonality=true
RestrictSUIDSGID=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
ReadWritePaths=/var/lib/ochenstarik-server-monitor-manager
UMask=0077
ReadWritePaths=/var/lib/ochenstarik-server-monitor-manager/control
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,68 @@
using Microsoft.Extensions.Configuration;
namespace ServerMonitorManager.Agent;
internal static class AgentConfiguration
{
internal const string BindingError =
"Agent configuration binding failed; check SMM_ControlUrl and SMM_NodeId.";
internal const string ControlUrlError =
"Agent ControlUrl must be a secure HTTPS origin.";
internal static bool TryBind(
IConfiguration configuration,
string? environmentControlUrl,
string? environmentNodeId,
out AgentOptions options,
out string? error)
{
try
{
options = configuration.Get<AgentOptions>() ?? new AgentOptions();
}
catch (Exception)
{
options = new AgentOptions();
error = BindingError;
return false;
}
if (environmentControlUrl is not null
&& (!Uri.TryCreate(environmentControlUrl, UriKind.Absolute, out var expectedControlUrl)
|| options.ControlUrl != expectedControlUrl))
{
error = "Agent configuration binding failed for SMM_ControlUrl.";
return false;
}
if (environmentNodeId is not null
&& !string.Equals(options.NodeId, environmentNodeId, StringComparison.Ordinal))
{
error = "Agent configuration binding failed for SMM_NodeId.";
return false;
}
if (!IsSecureControlOrigin(options.ControlUrl))
{
error = ControlUrlError;
return false;
}
error = null;
return true;
}
private static bool IsSecureControlOrigin(Uri? controlUrl)
{
return controlUrl is not null
&& controlUrl.IsAbsoluteUri
&& string.Equals(controlUrl.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase)
&& !string.IsNullOrWhiteSpace(controlUrl.Host)
&& Uri.CheckHostName(controlUrl.IdnHost) != UriHostNameType.Unknown
&& string.IsNullOrEmpty(controlUrl.UserInfo)
&& string.IsNullOrEmpty(controlUrl.Query)
&& string.IsNullOrEmpty(controlUrl.Fragment)
&& controlUrl.AbsolutePath == "/"
&& controlUrl.Port is >= 1 and <= 65535;
}
}

View file

@ -2,17 +2,17 @@ namespace ServerMonitorManager.Agent;
public sealed class AgentOptions
{
public string NodeId { get; init; } = Environment.MachineName.ToLowerInvariant();
public Uri ControlUrl { get; init; } = new("https://127.0.0.1:7443");
public string StateDirectory { get; init; } = "/var/lib/ochenstarik-server-monitor-manager/agent";
public string CertificateAuthorityPath { get; init; } = "/etc/ochenstarik-server-monitor-manager/control-ca.crt";
public string ProvisioningSocketPath { get; init; } = "/run/ochenstarik-server-monitor-manager/provisioning.sock";
public string EnrollmentTokenDirectory { get; init; } = "/var/lib/ochenstarik-server-monitor-manager-enrollment";
public string? EnrollTokenFile { get; init; }
public int HeartbeatSeconds { get; init; } = 30;
public int BufferMaxSamples { get; init; } = 720;
public int BufferRecentSamples { get; init; } = 120;
public int BufferDownsampleFactor { get; init; } = 4;
public int UploadBatchSize { get; init; } = 20;
public int MaxRetrySeconds { get; init; } = 300;
public string NodeId { get; set; } = Environment.MachineName.ToLowerInvariant();
public Uri ControlUrl { get; set; } = new("https://127.0.0.1:7443");
public string StateDirectory { get; set; } = "/var/lib/ochenstarik-server-monitor-manager/agent";
public string CertificateAuthorityPath { get; set; } = "/etc/ochenstarik-server-monitor-manager/control-ca.crt";
public string ProvisioningSocketPath { get; set; } = "/run/ochenstarik-server-monitor-manager/provisioning.sock";
public string EnrollmentTokenDirectory { get; set; } = "/var/lib/ochenstarik-server-monitor-manager-enrollment";
public string? EnrollTokenFile { get; set; }
public int HeartbeatSeconds { get; set; } = 30;
public int BufferMaxSamples { get; set; } = 720;
public int BufferRecentSamples { get; set; } = 120;
public int BufferDownsampleFactor { get; set; } = 4;
public int UploadBatchSize { get; set; } = 20;
public int MaxRetrySeconds { get; set; } = 300;
}

View file

@ -6,7 +6,18 @@ var configuration = new ConfigurationBuilder()
.AddEnvironmentVariables("SMM_")
.AddCommandLine(args)
.Build();
var options = configuration.Get<AgentOptions>() ?? new AgentOptions();
var environmentControlUrl = Environment.GetEnvironmentVariable("SMM_ControlUrl");
var environmentNodeId = Environment.GetEnvironmentVariable("SMM_NodeId");
if (!AgentConfiguration.TryBind(
configuration,
environmentControlUrl,
environmentNodeId,
out var options,
out var bindingError))
{
Console.Error.WriteLine(bindingError);
return 2;
}
if (string.IsNullOrWhiteSpace(options.NodeId)
|| !options.NodeId.All(character => character is >= 'a' and <= 'z' or >= '0' and <= '9' or '-'))
{

View file

@ -5,6 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<InvariantGlobalization>true</InvariantGlobalization>
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
<Version>0.1.0</Version>
<AssemblyName>ochenstarik-smm-agent</AssemblyName>
</PropertyGroup>

View file

@ -0,0 +1,140 @@
using Microsoft.Extensions.Configuration;
using ServerMonitorManager.Agent;
using Xunit;
namespace ServerMonitorManager.Control.Tests;
public sealed class AgentConfigurationTests
{
[Fact]
public void TryBindFailsClosedWhenNodeEnvironmentValueDidNotBind()
{
var configuration = new ConfigurationBuilder().Build();
var success = AgentConfiguration.TryBind(
configuration,
environmentControlUrl: null,
environmentNodeId: "expected-node",
out _,
out var error);
Assert.False(success);
Assert.Contains("SMM_NodeId", error, StringComparison.Ordinal);
Assert.DoesNotContain("expected-node", error, StringComparison.Ordinal);
}
[Fact]
public void TryBindFailsClosedWhenControlUrlEnvironmentValueDidNotBind()
{
var configuration = new ConfigurationBuilder().Build();
var success = AgentConfiguration.TryBind(
configuration,
environmentControlUrl: "https://control.example:7443",
environmentNodeId: null,
out _,
out var error);
Assert.False(success);
Assert.Contains("SMM_ControlUrl", error, StringComparison.Ordinal);
Assert.DoesNotContain("control.example", error, StringComparison.Ordinal);
}
[Fact]
public void TryBindAcceptsMatchingEnvironmentValues()
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["NodeId"] = "expected-node",
["ControlUrl"] = "https://control.example:7443"
})
.Build();
var success = AgentConfiguration.TryBind(
configuration,
environmentControlUrl: "https://control.example:7443",
environmentNodeId: "expected-node",
out var options,
out var error);
Assert.True(success, error);
Assert.Equal("expected-node", options.NodeId);
Assert.Equal(new Uri("https://control.example:7443"), options.ControlUrl);
Assert.Null(error);
}
[Fact]
public void TryBindReturnsNonSecretErrorWhenControlUrlCannotBind()
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["ControlUrl"] = "not a URL"
})
.Build();
var success = AgentConfiguration.TryBind(
configuration,
environmentControlUrl: "not a URL",
environmentNodeId: null,
out _,
out var error);
Assert.False(success);
Assert.Equal("Agent configuration binding failed for SMM_ControlUrl.", error);
Assert.DoesNotContain("not a URL", error, StringComparison.Ordinal);
}
[Theory]
[InlineData("http://control.example:7443")]
[InlineData("https://user@control.example:7443")]
[InlineData("https://control.example:7443/path")]
[InlineData("https://control.example:7443?query=1")]
[InlineData("https://control.example:7443#fragment")]
[InlineData("https://control.example:0")]
public void TryBindRejectsUnsafeControlUrlsWithoutDisclosingThem(string controlUrl)
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["ControlUrl"] = controlUrl
})
.Build();
var success = AgentConfiguration.TryBind(
configuration,
environmentControlUrl: controlUrl,
environmentNodeId: null,
out _,
out var error);
Assert.False(success);
Assert.Equal(AgentConfiguration.ControlUrlError, error);
Assert.DoesNotContain(controlUrl, error, StringComparison.Ordinal);
}
[Theory]
[InlineData("https://control.example")]
[InlineData("https://control.example:7443")]
[InlineData("https://127.0.0.1:7443/")]
[InlineData("https://[2001:db8::1]:7443")]
public void TryBindAcceptsStructurallySafeHttpsControlUrls(string controlUrl)
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["ControlUrl"] = controlUrl
})
.Build();
var success = AgentConfiguration.TryBind(
configuration,
environmentControlUrl: controlUrl,
environmentNodeId: null,
out _,
out var error);
Assert.True(success, error);
}
}

View file

@ -5,15 +5,22 @@ IFS=$'\n\t'
archive="${1:?usage: run-native-systemd-smoke.sh ARCHIVE BOOTSTRAP}"
bootstrap="${2:?usage: run-native-systemd-smoke.sh ARCHIVE BOOTSTRAP}"
port="${SMM_SMOKE_PORT:-17443}"
system_bootstrap="/usr/local/sbin/ochenstarik-server-monitor-manager.sh"
probe_dir=""
cleanup() {
sudo "$bootstrap" uninstall-control --confirm-destroy-control >/dev/null 2>&1 || true
if [[ -n "$probe_dir" ]]; then
rm -rf -- "$probe_dir"
fi
sudo "$system_bootstrap" uninstall-agent --purge >/dev/null 2>&1 || true
sudo "$system_bootstrap" uninstall-control --confirm-destroy-control >/dev/null 2>&1 || true
}
trap cleanup EXIT
sudo "$bootstrap" preflight
sudo "$bootstrap" verify-release "$archive"
sudo "$bootstrap" install-control "$archive" 127.0.0.1 "$port"
sudo test -x "$system_bootstrap"
sudo test -x /usr/local/sbin/ochenstarik-smm-emergency
sudo /usr/local/sbin/ochenstarik-smm-emergency status
@ -29,9 +36,75 @@ sudo curl --fail --silent --show-error \
--cacert /etc/ochenstarik-server-monitor-manager/control-ca.crt \
"https://127.0.0.1:$port/healthz"
sudo "$bootstrap" install-control "$archive" 127.0.0.1 "$port"
sudo systemctl restart ochenstarik-smm-control.service
probe_dir="$(mktemp -d -t smm-agent-probe.XXXXXXXX)"
tar -xzf "$archive" -C "$probe_dir" agent
set +e
probe_output="$(env SMM_NodeId=INVALID_UPPER \
"$probe_dir/agent/ochenstarik-smm-agent" 2>&1)"
probe_status=$?
set -e
[[ "$probe_status" -eq 2 ]] || {
printf 'trimmed Agent invalid-environment probe returned %s, expected 2\n' "$probe_status" >&2
exit 1
}
grep -Fq 'NodeId must contain lowercase letters, digits, or hyphens.' <<<"$probe_output"
if grep -Fq 'agent.pfx' <<<"$probe_output"; then
printf '%s\n' 'trimmed Agent ignored SMM_NodeId and attempted to load agent.pfx' >&2
exit 1
fi
set +e
probe_output="$(env SMM_NodeId=smoke-probe SMM_ControlUrl=http://127.0.0.1:1 \
"$probe_dir/agent/ochenstarik-smm-agent" 2>&1)"
probe_status=$?
set -e
[[ "$probe_status" -eq 2 ]] || {
printf 'trimmed Agent plaintext-ControlUrl probe returned %s, expected 2\n' "$probe_status" >&2
exit 1
}
grep -Fq 'Agent ControlUrl must be a secure HTTPS origin.' <<<"$probe_output"
if grep -Eq 'agent\.pfx|Connection refused|127\.0\.0\.1' <<<"$probe_output"; then
printf '%s\n' 'trimmed Agent plaintext-ControlUrl probe reached certificate or network access' >&2
exit 1
fi
rm -rf -- "$probe_dir"
probe_dir=""
node_code="$(sudo "$system_bootstrap" node-code smoke-node)"
[[ "$node_code" == SMMNODE1.* || "$node_code" == SMMNODE2.* ]]
export SMM_ENROLL_CODE="$node_code"
export SMM_ACCEPT_CA_FINGERPRINT=1
sudo --preserve-env=SMM_ENROLL_CODE,SMM_ACCEPT_CA_FINGERPRINT \
"$system_bootstrap" install-node "$archive"
unset SMM_ENROLL_CODE SMM_ACCEPT_CA_FINGERPRINT
node_code=""
sudo test -s /var/lib/ochenstarik-server-monitor-manager/agent/agent.pfx
sudo systemctl is-active --quiet ochenstarik-smm-agent.service
sudo systemctl is-active --quiet ochenstarik-smm-control.service
[[ "$(sudo stat -c '%a:%U:%G' /var/lib/ochenstarik-server-monitor-manager)" == '711:root:root' ]]
[[ "$(sudo stat -c '%a:%U:%G' /var/lib/ochenstarik-server-monitor-manager/control)" \
== '700:ochenstarik-smm-control:ochenstarik-smm-control' ]]
[[ "$(sudo stat -c '%a:%U:%G' /var/lib/ochenstarik-server-monitor-manager/agent)" \
== '700:ochenstarik-smm-agent:ochenstarik-smm-agent' ]]
if sudo -u ochenstarik-smm-agent test -r \
/var/lib/ochenstarik-server-monitor-manager/control/control.db; then
printf '%s\n' 'Agent can read the Control database' >&2
exit 1
fi
if sudo -u ochenstarik-smm-agent id -nG | tr ' ' '\n' | \
grep -Fxq ochenstarik-smm-control; then
printf '%s\n' 'Agent unexpectedly belongs to the Control group' >&2
exit 1
fi
device_code="$(sudo "$system_bootstrap" control-device-code smoke-device)"
[[ "$device_code" == SMMDEV1-* ]]
device_code=""
sudo "$system_bootstrap" update-control "$archive"
sudo systemctl is-active --quiet ochenstarik-smm-control.service
sudo systemctl is-active --quiet ochenstarik-smm-agent.service
sudo curl --fail --silent --show-error --retry 15 --retry-all-errors --retry-delay 1 \
--cacert /etc/ochenstarik-server-monitor-manager/control-ca.crt \
"https://127.0.0.1:$port/healthz"

View file

@ -23,7 +23,7 @@ if grep -Fq 'SMM_EnrollToken=$ENROLL_TOKEN' "$bootstrap"; then
exit 1
fi
grep -Fq 'readonly ENROLLMENT_DIR="${STATE_DIR}-enrollment"' "$bootstrap"
grep -Fq 'install -d -m 0710 -o root -g "$AGENT_USER" "$ENROLLMENT_DIR"' "$bootstrap"
grep -Fq 'install -d -m 0700 -o "$AGENT_USER" -g "$AGENT_USER" "$ENROLLMENT_DIR"' "$bootstrap"
grep -Fq 'token_temp="$(mktemp "$ENROLLMENT_DIR/.enroll-token.XXXXXXXX")"' "$bootstrap"
if grep -Fq '$STATE_DIR/enrollment' "$bootstrap"; then
printf '%s\n' "enrollment directory is beneath Control-writable state" >&2
@ -57,9 +57,495 @@ grep -Fq "peer-add SMMPEER1_CODE" <<<"$help_output"
grep -Fq "mesh-status" <<<"$help_output"
grep -Fq "SMM_ENROLL_TOKEN" <<<"$help_output"
grep -Fq "node-code NODE_ID" <<<"$help_output"
grep -Fq "control-device-code DEVICE_ID" <<<"$help_output"
grep -Fq "verify-release ARCHIVE" <<<"$help_output"
grep -Fq "node-token NODE_ID" <<<"$help_output"
grep -Eq '^ochenstarik-server-monitor-manager [0-9]+\.[0-9]+\.[0-9]+-' <<<"$version_output"
extract_bootstrap_function() {
local name="$1"
awk -v signature="$name() {" '
$0 == signature { emitting = 1 }
emitting { print }
emitting && $0 == "}" { exit }
' "$bootstrap"
}
validate_port_definition="$(extract_bootstrap_function validate_port)"
validate_ipv4_literal_definition="$(extract_bootstrap_function validate_ipv4_literal)"
validate_control_url_definition="$(extract_bootstrap_function validate_control_url)"
for accepted_url in \
https://example.com \
https://host.example:7443 \
https://10.0.0.1:7443 \
'https://[2001:db8::1]:7443' \
'https://[::1]:7443' \
'https://[2001:db8::]' \
'https://[::]' \
'https://[::ffff:192.0.2.128]' \
'https://[2001:db8:3:4::192.0.2.33]:7443' \
'https://[1:2:3:4:5:6:192.0.2.1]' \
https://example.com/; do
if ! (fail() { exit 1; }; source <(printf '%s\n%s\n%s\n' "$validate_port_definition" "$validate_ipv4_literal_definition" "$validate_control_url_definition"); validate_control_url "$accepted_url"); then
printf 'valid Control URL was rejected: %s\n' "$accepted_url" >&2
exit 1
fi
done
for rejected_url in \
http://example.com \
https:// \
https://:7443 \
'https://[2001:db8::1' \
'https://[::::]' \
'https://[1:2:3]' \
'https://[::ffff:192.0.2.999]' \
'https://[::ffff:192.0.2]' \
'https://[::ffff:192.0.2.1.5]' \
'https://[::ffff:192.0.2.x]' \
'https://[1:2:3:4:5:6:7:192.0.2.1]' \
'https://[::ffff:192.0.2.1:]' \
'https://[1:2:3:4:5:6:192.0.2.1:]' \
'https://[1:2:3:4:5:6:7:8:]' \
'https://[:1:2:3:4:5:6:7]' \
'https://[::ffff:18446744073709551617.0.0.1]' \
'https://[::1]:18446744073709551696' \
'https://2001:db8::1:7443' \
'https://example.com:7443:7444' \
https://example..com \
https://999.0.0.1 \
https://18446744073709551617.0.0.1 \
https://example.com/path \
https://user@example.com \
'https://example.com:0' \
'https://example.com:65536' \
'https://example.com:18446744073709551696' \
'https://example.com?query=1' \
'https://example.com#fragment'; do
if (fail() { exit 1; }; source <(printf '%s\n%s\n%s\n' "$validate_port_definition" "$validate_ipv4_literal_definition" "$validate_control_url_definition"); validate_control_url "$rejected_url"); then
printf 'invalid Control URL was accepted: %s\n' "$rejected_url" >&2
exit 1
fi
done
for action in status version --version preflight help -h --help; do
if bash "$bootstrap" "$action" surplus >/dev/null 2>&1; then
printf 'bootstrap action accepted surplus arguments: %s\n' "$action" >&2
exit 1
fi
done
if ! bash "$bootstrap" >/dev/null 2>&1; then
printf '%s\n' 'bootstrap no-argument help form failed' >&2
exit 1
fi
for action in verify-release install-control install-agent install-node mesh-init peer-add \
update-control update-agent rollback node-code control-device-code node-token uninstall-control; do
if bash "$bootstrap" "$action" >/dev/null 2>&1; then
printf 'bootstrap action accepted missing arguments: %s\n' "$action" >&2
exit 1
fi
done
base64url_encode_definition="$(extract_bootstrap_function base64url_encode)"
create_device_code_definition="$(extract_bootstrap_function create_device_code)"
device_fixture="$(mktemp -d -t smm-device-code.XXXXXXXX)"
printf '%s\n' 'https://control.example:7443' >"$device_fixture/control-public-url"
printf '%s\n' 'fixture-ca' >"$device_fixture/control-ca.crt"
device_code="$(
ETC_DIR="$device_fixture"
require_root() { :; }
require_command() { :; }
validate_node_id() { [[ "$1" == 'desktop-device' ]]; }
validate_control_url() { [[ "$1" == 'https://control.example:7443' ]]; }
run_control_cli() {
[[ "$1" == 'device-token-create' && "$2" == 'desktop-device' ]]
printf '%043d\n' 0
}
openssl() { printf '%s' 'DER-fixture'; }
source <(printf '%s\n%s\n' "$base64url_encode_definition" "$create_device_code_definition")
create_device_code desktop-device
)"
[[ "$device_code" == SMMDEV1-* ]]
device_payload_encoded="${device_code#SMMDEV1-}"
device_payload_encoded="${device_payload_encoded//-/+}"
device_payload_encoded="${device_payload_encoded//_/\/}"
case $(( ${#device_payload_encoded} % 4 )) in
0) ;;
2) device_payload_encoded+='==' ;;
3) device_payload_encoded+='=' ;;
*) printf '%s\n' 'invalid generated SMMDEV1 base64url length' >&2; exit 1 ;;
esac
device_payload="$(printf '%s' "$device_payload_encoded" | base64 -d)"
grep -Fxq 'VERSION=1' <<<"$device_payload"
grep -Fxq 'DEVICE=desktop-device' <<<"$device_payload"
grep -Fxq 'TOKEN=0000000000000000000000000000000000000000000' <<<"$device_payload"
grep -Fxq 'URL=https://control.example:7443' <<<"$device_payload"
grep -Fxq "CA=$(printf '%s' 'DER-fixture' | base64 -w 0)" <<<"$device_payload"
rm -rf -- "$device_fixture"
grep -Fq 'readonly BOOTSTRAP_COMMAND="/usr/local/sbin/ochenstarik-server-monitor-manager.sh"' "$bootstrap"
grep -Fq 'staging="$(mktemp "$(dirname "$BOOTSTRAP_COMMAND")/.ochenstarik-server-monitor-manager.XXXXXXXX")"' "$bootstrap"
grep -Fq 'mv -fT -- "$staging" "$BOOTSTRAP_COMMAND"' "$bootstrap"
[[ "$(grep -Fc ' install_bootstrap_command' "$bootstrap")" -eq 3 ]]
update_role_definition="$(extract_bootstrap_function update_role)"
validation_line="$(grep -n -m1 'validate_control_state_migration' <<<"$update_role_definition" | cut -d: -f1)"
environment_validation_line="$(grep -n -m1 'validate_control_environment_migration' <<<"$update_role_definition" | cut -d: -f1)"
control_stop_line="$(grep -n -m1 'systemctl stop \"\$unit\"' <<<"$update_role_definition" | cut -d: -f1)"
(( validation_line < control_stop_line && environment_validation_line < control_stop_line ))
guard_arm_line="$(grep -n -m1 'CONTROL_UPDATE_RECOVERY_REQUIRED=1' <<<"$update_role_definition" | cut -d: -f1)"
guard_clear_line="$(grep -n -m1 'CONTROL_UPDATE_RECOVERY_REQUIRED=0' <<<"$update_role_definition" | cut -d: -f1)"
prepare_line="$(grep -n -m1 'prepare_control_state' <<<"$update_role_definition" | cut -d: -f1)"
rewrite_line="$(grep -n -m1 'rewrite_control_environment' <<<"$update_role_definition" | cut -d: -f1)"
bootstrap_line="$(grep -n -m1 'install_bootstrap_command' <<<"$update_role_definition" | cut -d: -f1)"
unit_line="$(grep -n -m1 'install_unit \"\$TEMP_DIR/deploy/\$CONTROL_UNIT\"' <<<"$update_role_definition" | cut -d: -f1)"
enable_line="$(grep -n -m1 'systemctl enable \"\$CONTROL_UNIT\"' <<<"$update_role_definition" | cut -d: -f1)"
binary_line="$(grep -n -m1 'install_tree_atomic \"\$TEMP_DIR/control\"' <<<"$update_role_definition" | cut -d: -f1)"
restart_line="$(grep -n -m1 'systemctl restart \"\$unit\"' <<<"$update_role_definition" | cut -d: -f1)"
active_line="$(grep -n -m1 'systemctl is-active --quiet \"\$unit\"' <<<"$update_role_definition" | cut -d: -f1)"
for guarded_line in "$control_stop_line" "$prepare_line" "$rewrite_line" "$bootstrap_line" \
"$unit_line" "$enable_line" "$binary_line" "$restart_line" "$active_line"; do
(( guard_arm_line < guarded_line && guarded_line < guard_clear_line ))
done
grep -Fq 'install_bootstrap_command' <<<"$update_role_definition"
grep -Fq 'install_unit "$TEMP_DIR/deploy/$CONTROL_UNIT" "$CONTROL_UNIT"' <<<"$update_role_definition"
grep -Fq 'install_tree_atomic "$TEMP_DIR/control" "$LIB_DIR/control" "$user"' <<<"$update_role_definition"
grep -Fq 'systemctl restart "$unit"' <<<"$update_role_definition"
grep -Fq 'systemctl is-active --quiet "$unit"' <<<"$update_role_definition"
grep -Fq 'install -d -m 0711 -o root -g root "$ETC_DIR" "$STATE_DIR"' "$bootstrap"
grep -Fq 'install -d -m 0700 -o "$CONTROL_USER" -g "$CONTROL_USER" "$STATE_DIR/control"' "$bootstrap"
grep -Fq 'Control__DatabasePath=$STATE_DIR/control/control.db' "$bootstrap"
grep -Fq 'Control__BackupDirectory=$STATE_DIR/control/backups' "$bootstrap"
validate_control_state_migration_definition="$(extract_bootstrap_function validate_control_state_migration)"
prepare_control_state_definition="$(extract_bootstrap_function prepare_control_state)"
control_state_fixture="$(mktemp -d -t smm-control-state.XXXXXXXX)"
mkdir -p "$control_state_fixture/backups"
printf '%s' database >"$control_state_fixture/control.db"
printf '%s' wal >"$control_state_fixture/control.db-wal"
printf '%s' backup >"$control_state_fixture/backups/manifest.json"
(
STATE_DIR="$control_state_fixture"
CONTROL_USER=fixture
fail() { printf '%s\n' "$*" >&2; exit 1; }
install() {
local arguments=()
while (( $# > 0 )); do
case "$1" in
-m|-o|-g) shift 2 ;;
*) arguments+=("$1"); shift ;;
esac
done
command install "${arguments[@]}"
}
chown() { :; }
source <(printf '%s\n%s\n' "$validate_control_state_migration_definition" "$prepare_control_state_definition")
validate_control_state_migration
prepare_control_state
)
[[ ! -e "$control_state_fixture/control.db" ]]
[[ "$(<"$control_state_fixture/control/control.db")" == database ]]
[[ "$(<"$control_state_fixture/control/control.db-wal")" == wal ]]
[[ "$(<"$control_state_fixture/control/backups/manifest.json")" == backup ]]
if [[ "$(uname -s)" != MINGW* ]]; then
[[ "$(stat -c '%a' "$control_state_fixture/control")" == 700 ]]
[[ "$(stat -c '%a' "$control_state_fixture/control/control.db")" == 600 ]]
fi
printf '%s' conflict >"$control_state_fixture/control.db"
if (
STATE_DIR="$control_state_fixture"
fail() { exit 1; }
source <(printf '%s\n' "$validate_control_state_migration_definition")
validate_control_state_migration
); then
printf '%s\n' 'conflicting legacy and role-isolated Control state was accepted' >&2
exit 1
fi
rm -rf -- "$control_state_fixture"
validate_control_environment_migration_definition="$(extract_bootstrap_function validate_control_environment_migration)"
rewrite_control_environment_definition="$(extract_bootstrap_function rewrite_control_environment)"
alpha7_fixture="$(mktemp -d -t smm-alpha7-update.XXXXXXXX)"
mkdir -p "$alpha7_fixture/state/backups" "$alpha7_fixture/etc"
printf '%s' alpha7-db >"$alpha7_fixture/state/control.db"
printf '%s' alpha7-wal >"$alpha7_fixture/state/control.db-wal"
printf '%s' alpha7-shm >"$alpha7_fixture/state/control.db-shm"
printf '%s' alpha7-backup >"$alpha7_fixture/state/backups/manifest.json"
cat >"$alpha7_fixture/etc/control.env" <<EOF
# alpha.7 fixture: preserve comments and every unrelated value
ASPNETCORE_URLS=https://0.0.0.0:7443
Control__DatabasePath=$alpha7_fixture/state/control.db
Control__CertificateAuthorityPath=/custom/control-ca.pfx
Control__BackupDirectory=$alpha7_fixture/state/backups
Control__LinkReconciliationSeconds=777
CUSTOM_VALUE=spaces are preserved exactly
EOF
cp "$alpha7_fixture/etc/control.env" "$alpha7_fixture/original.env"
(
STATE_DIR="$alpha7_fixture/state"
ETC_DIR="$alpha7_fixture/etc"
CONTROL_USER=fixture
fail() { printf '%s\n' "$*" >&2; exit 1; }
install() {
local arguments=()
while (( $# > 0 )); do
case "$1" in -m|-o|-g) shift 2 ;; *) arguments+=("$1"); shift ;; esac
done
command install "${arguments[@]}"
}
chown() { :; }
source <(printf '%s\n%s\n%s\n%s\n' \
"$validate_control_state_migration_definition" \
"$prepare_control_state_definition" \
"$validate_control_environment_migration_definition" \
"$rewrite_control_environment_definition")
validate_control_state_migration
validate_control_environment_migration
prepare_control_state
rewrite_control_environment
)
[[ "$(<"$alpha7_fixture/state/control/control.db")" == alpha7-db ]]
[[ "$(<"$alpha7_fixture/state/control/control.db-wal")" == alpha7-wal ]]
[[ "$(<"$alpha7_fixture/state/control/control.db-shm")" == alpha7-shm ]]
[[ "$(<"$alpha7_fixture/state/control/backups/manifest.json")" == alpha7-backup ]]
expected_env="$(sed \
-e "s|^Control__DatabasePath=.*|Control__DatabasePath=$alpha7_fixture/state/control/control.db|" \
-e "s|^Control__BackupDirectory=.*|Control__BackupDirectory=$alpha7_fixture/state/control/backups|" \
"$alpha7_fixture/original.env")"
[[ "$(<"$alpha7_fixture/etc/control.env")" == "$expected_env" ]]
printf '%s\n' "Control__DatabasePath=$alpha7_fixture/state/control/control.db" \
>>"$alpha7_fixture/etc/control.env"
if (
ETC_DIR="$alpha7_fixture/etc"
fail() { exit 1; }
source <(printf '%s\n' "$validate_control_environment_migration_definition")
validate_control_environment_migration
); then
printf '%s\n' 'conflicting Control environment paths were accepted' >&2
exit 1
fi
rm -rf -- "$alpha7_fixture"
record_control_legacy_state_definition="$(extract_bootstrap_function record_control_legacy_state)"
reverse_control_state_migration_definition="$(extract_bootstrap_function reverse_control_state_migration)"
restore_control_update_backup_definition="$(extract_bootstrap_function restore_control_update_backup)"
restore_control_binary_from_archive_definition="$(extract_bootstrap_function restore_control_binary_from_archive)"
recover_control_update_definition="$(extract_bootstrap_function recover_control_update)"
[[ -n "$record_control_legacy_state_definition" ]]
[[ -n "$reverse_control_state_migration_definition" ]]
[[ -n "$restore_control_update_backup_definition" ]]
[[ -n "$restore_control_binary_from_archive_definition" ]]
[[ -n "$recover_control_update_definition" ]]
recovery_fixture="$(mktemp -d -t smm-control-recovery.XXXXXXXX)"
recovery_root="$recovery_fixture/root"
archive_root="$recovery_fixture/archive-root"
mkdir -p \
"$recovery_root/var/lib/ochenstarik-server-monitor-manager/backups" \
"$recovery_root/etc/ochenstarik-server-monitor-manager" \
"$recovery_root/etc/systemd/system" \
"$recovery_root/usr/local/lib/ochenstarik-server-monitor-manager/control" \
"$archive_root/etc/ochenstarik-server-monitor-manager" \
"$archive_root/etc/systemd/system" \
"$archive_root/usr/local/lib/ochenstarik-server-monitor-manager/control"
printf '%s' original-db >"$recovery_root/var/lib/ochenstarik-server-monitor-manager/control.db"
printf '%s' original-wal >"$recovery_root/var/lib/ochenstarik-server-monitor-manager/control.db-wal"
printf '%s' original-shm >"$recovery_root/var/lib/ochenstarik-server-monitor-manager/control.db-shm"
printf '%s' original-backup >"$recovery_root/var/lib/ochenstarik-server-monitor-manager/backups/identity"
printf '%s' old-binary >"$archive_root/usr/local/lib/ochenstarik-server-monitor-manager/control/ochenstarik-smm-control"
printf '%s\n' 'Control__DatabasePath=/var/lib/ochenstarik-server-monitor-manager/control.db' \
>"$archive_root/etc/ochenstarik-server-monitor-manager/control.env"
printf '%s' old-unit >"$archive_root/etc/systemd/system/ochenstarik-smm-control.service"
mkdir -p "$recovery_fixture/bootstrap-backups"
tar -C "$archive_root" -czf "$recovery_fixture/bootstrap-backups/alpha7.tar.gz" \
usr/local/lib/ochenstarik-server-monitor-manager/control \
etc/ochenstarik-server-monitor-manager/control.env \
etc/systemd/system/ochenstarik-smm-control.service
(
STATE_DIR="$recovery_root/var/lib/ochenstarik-server-monitor-manager"
CONTROL_USER=fixture
CONTROL_UPDATE_LEGACY_ITEMS=()
fail() { printf '%s\n' "$*" >&2; exit 1; }
install() {
local arguments=()
while (( $# > 0 )); do
case "$1" in -m|-o|-g) shift 2 ;; *) arguments+=("$1"); shift ;; esac
done
command install "${arguments[@]}"
}
chown() { :; }
source <(printf '%s\n%s\n%s\n' \
"$record_control_legacy_state_definition" \
"$prepare_control_state_definition" \
"$reverse_control_state_migration_definition")
record_control_legacy_state
prepare_control_state
[[ "$(<"$STATE_DIR/control/control.db")" == original-db ]]
reverse_control_state_migration
[[ "$(<"$STATE_DIR/control.db")" == original-db ]]
[[ "$(<"$STATE_DIR/control.db-wal")" == original-wal ]]
[[ "$(<"$STATE_DIR/control.db-shm")" == original-shm ]]
[[ "$(<"$STATE_DIR/backups/identity")" == original-backup ]]
[[ ! -e "$STATE_DIR/control/control.db" ]]
)
printf '%s' new-binary >"$recovery_root/usr/local/lib/ochenstarik-server-monitor-manager/control/ochenstarik-smm-control"
printf '%s\n' 'Control__DatabasePath=/var/lib/ochenstarik-server-monitor-manager/control/control.db' \
>"$recovery_root/etc/ochenstarik-server-monitor-manager/control.env"
printf '%s' new-unit >"$recovery_root/etc/systemd/system/ochenstarik-smm-control.service"
(
STATE_DIR="$recovery_root/var/lib/ochenstarik-server-monitor-manager"
BACKUP_DIR="$recovery_fixture/bootstrap-backups"
CONTROL_UNIT=ochenstarik-smm-control.service
CONTROL_UPDATE_LEGACY_ITEMS=()
systemctl() { :; }
fail() { printf '%s\n' "$*" >&2; exit 1; }
source <(printf '%s\n%s\n%s\n%s\n' \
"$reverse_control_state_migration_definition" \
"$restore_control_update_backup_definition" \
"$restore_control_binary_from_archive_definition" \
"$recover_control_update_definition")
restore_control_binary_from_archive "$BACKUP_DIR/alpha7.tar.gz" "$recovery_root"
[[ "$(<"$recovery_root/usr/local/lib/ochenstarik-server-monitor-manager/control/ochenstarik-smm-control")" == old-binary ]]
[[ "$(<"$recovery_root/etc/ochenstarik-server-monitor-manager/control.env")" == \
'Control__DatabasePath=/var/lib/ochenstarik-server-monitor-manager/control/control.db' ]]
)
rm -rf -- "$recovery_root/var/lib/ochenstarik-server-monitor-manager/control"
mkdir -p "$recovery_root/var/lib/ochenstarik-server-monitor-manager/control/backups"
mv "$recovery_root/var/lib/ochenstarik-server-monitor-manager/control.db" \
"$recovery_root/var/lib/ochenstarik-server-monitor-manager/control/control.db"
mv "$recovery_root/var/lib/ochenstarik-server-monitor-manager/control.db-wal" \
"$recovery_root/var/lib/ochenstarik-server-monitor-manager/control/control.db-wal"
mv "$recovery_root/var/lib/ochenstarik-server-monitor-manager/control.db-shm" \
"$recovery_root/var/lib/ochenstarik-server-monitor-manager/control/control.db-shm"
mv "$recovery_root/var/lib/ochenstarik-server-monitor-manager/backups/identity" \
"$recovery_root/var/lib/ochenstarik-server-monitor-manager/control/backups/identity"
rmdir "$recovery_root/var/lib/ochenstarik-server-monitor-manager/backups"
printf '%s' new-binary >"$recovery_root/usr/local/lib/ochenstarik-server-monitor-manager/control/ochenstarik-smm-control"
printf '%s\n' 'Control__DatabasePath=/var/lib/ochenstarik-server-monitor-manager/control/control.db' \
>"$recovery_root/etc/ochenstarik-server-monitor-manager/control.env"
printf '%s' new-unit >"$recovery_root/etc/systemd/system/ochenstarik-smm-control.service"
(
STATE_DIR="$recovery_root/var/lib/ochenstarik-server-monitor-manager"
BACKUP_DIR="$recovery_fixture/bootstrap-backups"
CONTROL_UNIT=ochenstarik-smm-control.service
CONTROL_UPDATE_LEGACY_ITEMS=(control.db control.db-wal control.db-shm backups)
systemctl() { :; }
fail() { printf '%s\n' "$*" >&2; exit 1; }
source <(printf '%s\n%s\n%s\n' \
"$reverse_control_state_migration_definition" \
"$restore_control_update_backup_definition" \
"$recover_control_update_definition")
recover_control_update alpha7 "$recovery_root"
)
[[ "$(<"$recovery_root/var/lib/ochenstarik-server-monitor-manager/control.db")" == original-db ]]
[[ "$(<"$recovery_root/var/lib/ochenstarik-server-monitor-manager/control.db-wal")" == original-wal ]]
[[ "$(<"$recovery_root/var/lib/ochenstarik-server-monitor-manager/control.db-shm")" == original-shm ]]
[[ "$(<"$recovery_root/var/lib/ochenstarik-server-monitor-manager/backups/identity")" == original-backup ]]
[[ "$(<"$recovery_root/usr/local/lib/ochenstarik-server-monitor-manager/control/ochenstarik-smm-control")" == old-binary ]]
[[ "$(<"$recovery_root/etc/ochenstarik-server-monitor-manager/control.env")" == \
'Control__DatabasePath=/var/lib/ochenstarik-server-monitor-manager/control.db' ]]
[[ "$(<"$recovery_root/etc/systemd/system/ochenstarik-smm-control.service")" == old-unit ]]
rm -rf -- "$recovery_fixture"
cleanup_definition="$(extract_bootstrap_function cleanup)"
[[ -n "$cleanup_definition" ]]
for failure_step in stop state environment bootstrap unit enable binary restart active; do
guard_fixture="$(mktemp -d -t smm-control-guard.XXXXXXXX)"
marker="$guard_fixture.recovered"
set +e
(
set -Eeuo pipefail
PROGRAM=test-bootstrap
TEMP_DIR="$guard_fixture"
ENROLLMENT_TOKEN_FILE=""
ENROLLMENT_TOKEN_TEMP=""
CONTROL_UPDATE_BACKUP_ID=fixture-backup
CONTROL_UPDATE_RECOVERY_REQUIRED=1
marker="$marker"
failure_step="$failure_step"
log() { :; }
recover_control_update() { printf '%s' "$failure_step" >"$marker"; }
source <(printf '%s\n' "$cleanup_definition")
trap cleanup EXIT
false
) >/dev/null 2>&1
guard_status=$?
set -e
(( guard_status != 0 )) || {
printf 'injected Control update failure was ignored: %s\n' "$failure_step" >&2
exit 1
}
[[ -f "$marker" && "$(<"$marker")" == "$failure_step" ]] || {
printf 'Control recovery guard was not invoked for: %s\n' "$failure_step" >&2
exit 1
}
rm -f -- "$marker"
done
role_is_installed_definition="$(extract_bootstrap_function role_is_installed)"
remove_shared_ca_if_unused_definition="$(extract_bootstrap_function remove_shared_ca_if_unused)"
uninstall_agent_definition="$(extract_bootstrap_function uninstall_agent)"
uninstall_control_definition="$(extract_bootstrap_function uninstall_control)"
grep -Fq 'remove_shared_ca_if_unused' <<<"$uninstall_agent_definition"
grep -Fq 'remove_shared_ca_if_unused' <<<"$uninstall_control_definition"
if grep -Fq 'control-ca.crt' <<<"$uninstall_agent_definition$uninstall_control_definition"; then
printf '%s\n' 'a role uninstaller deletes the shared CA directly' >&2
exit 1
fi
role_fixture="$(mktemp -d -t smm-role-uninstall.XXXXXXXX)"
mkdir -p "$role_fixture/etc" "$role_fixture/lib/control" "$role_fixture/lib/agent"
printf '%s' ca >"$role_fixture/etc/control-ca.crt"
(
ETC_DIR="$role_fixture/etc"; LIB_DIR="$role_fixture/lib"
source <(printf '%s\n%s\n' "$role_is_installed_definition" "$remove_shared_ca_if_unused_definition")
rm -rf "$LIB_DIR/agent"
remove_shared_ca_if_unused
)
[[ -f "$role_fixture/etc/control-ca.crt" ]]
rm -rf "$role_fixture/lib/control"
(
ETC_DIR="$role_fixture/etc"; LIB_DIR="$role_fixture/lib"
source <(printf '%s\n%s\n' "$role_is_installed_definition" "$remove_shared_ca_if_unused_definition")
remove_shared_ca_if_unused
)
[[ ! -e "$role_fixture/etc/control-ca.crt" ]]
mkdir -p "$role_fixture/lib/control" "$role_fixture/lib/agent"
printf '%s' ca >"$role_fixture/etc/control-ca.crt"
(
ETC_DIR="$role_fixture/etc"; LIB_DIR="$role_fixture/lib"
source <(printf '%s\n%s\n' "$role_is_installed_definition" "$remove_shared_ca_if_unused_definition")
rm -rf "$LIB_DIR/control"
remove_shared_ca_if_unused
)
[[ -f "$role_fixture/etc/control-ca.crt" ]]
rm -rf "$role_fixture/lib/agent"
(
ETC_DIR="$role_fixture/etc"; LIB_DIR="$role_fixture/lib"
source <(printf '%s\n%s\n' "$role_is_installed_definition" "$remove_shared_ca_if_unused_definition")
remove_shared_ca_if_unused
)
[[ ! -e "$role_fixture/etc/control-ca.crt" ]]
rm -rf -- "$role_fixture"
grep -Fq 'UMask=0077' "$root/deploy/ochenstarik-smm-control.service"
grep -Fq 'ReadWritePaths=/var/lib/ochenstarik-server-monitor-manager/control' "$root/deploy/ochenstarik-smm-control.service"
native_smoke="$root/tests/bootstrap/run-native-systemd-smoke.sh"
grep -Fq 'node_code="$(sudo "$system_bootstrap" node-code smoke-node)"' "$native_smoke"
grep -Fq 'export SMM_ENROLL_CODE="$node_code"' "$native_smoke"
grep -Fq 'export SMM_ACCEPT_CA_FINGERPRINT=1' "$native_smoke"
grep -Fq 'sudo test -s /var/lib/ochenstarik-server-monitor-manager/agent/agent.pfx' "$native_smoke"
grep -Fq 'sudo systemctl is-active --quiet ochenstarik-smm-agent.service' "$native_smoke"
grep -Fq 'device_code="$(sudo "$system_bootstrap" control-device-code smoke-device)"' "$native_smoke"
grep -Fq '[[ "$device_code" == SMMDEV1-* ]]' "$native_smoke"
grep -Fq 'sudo --preserve-env=SMM_ENROLL_CODE,SMM_ACCEPT_CA_FINGERPRINT' "$native_smoke"
if grep -Fq 'sudo env SMM_ENROLL_CODE=' "$native_smoke"; then
printf '%s\n' 'native smoke exposes the enrollment code through env argv' >&2
exit 1
fi
grep -Fq 'rm -rf -- "$LIB_DIR/control" "$STATE_DIR/control"' "$bootstrap"
if grep -Fq 'install -d -m 0750 -o root -g "$AGENT_USER" "$ETC_DIR"' "$bootstrap"; then
printf '%s\n' 'Agent installation still takes ownership of the shared configuration parent' >&2
exit 1
fi
emergency_help="$(bash "$emergency" --help)"
grep -Fq 'mesh-disable' <<<"$emergency_help"
grep -Fq 'firewall-restore' <<<"$emergency_help"