@calltelemetry/ct-lab-mcp 0.8.98 → 0.8.99

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/assets/stig/SHA256SUMS +18 -0
  2. package/assets/stig/files/ct-luks-firstboot.service +13 -0
  3. package/assets/stig/files/ct-luks-firstboot.sh +35 -0
  4. package/assets/stig/guest/keyhash.sh +4 -0
  5. package/assets/stig/guest/read-recovery.sh +2 -0
  6. package/assets/stig/guest/scan-disa.sh +17 -0
  7. package/assets/stig/guest/scan.sh +22 -0
  8. package/assets/stig/guest/shred-recovery.sh +2 -0
  9. package/assets/stig/guest/test-recovery.sh +3 -0
  10. package/assets/stig/guest/trim.sh +3 -0
  11. package/assets/stig/guest/verify-root.sh +9 -0
  12. package/assets/stig/guest/verify-user.sh +9 -0
  13. package/assets/stig/ks.cfg.pkrtpl +95 -0
  14. package/assets/stig/scripts/assess.sh +39 -0
  15. package/assets/stig/scripts/errata.sh +19 -0
  16. package/assets/stig/scripts/remediate.sh +127 -0
  17. package/assets/stig/scripts/seal.sh +46 -0
  18. package/assets/stig/tailoring.xml +16 -0
  19. package/assets/stig/template.pkr.hcl +252 -0
  20. package/dist/src/generated-version.d.ts +1 -1
  21. package/dist/src/generated-version.js +1 -1
  22. package/dist/src/stig/live.d.ts +116 -0
  23. package/dist/src/stig/live.d.ts.map +1 -0
  24. package/dist/src/stig/live.js +339 -0
  25. package/dist/src/stig/live.js.map +1 -0
  26. package/dist/src/stig/model.d.ts +160 -0
  27. package/dist/src/stig/model.d.ts.map +1 -0
  28. package/dist/src/stig/model.js +384 -0
  29. package/dist/src/stig/model.js.map +1 -0
  30. package/dist/src/stig/ports.d.ts +149 -0
  31. package/dist/src/stig/ports.d.ts.map +1 -0
  32. package/dist/src/stig/ports.js +7 -0
  33. package/dist/src/stig/ports.js.map +1 -0
  34. package/dist/src/stig/receipt.d.ts +35 -0
  35. package/dist/src/stig/receipt.d.ts.map +1 -0
  36. package/dist/src/stig/receipt.js +112 -0
  37. package/dist/src/stig/receipt.js.map +1 -0
  38. package/dist/src/stig/runner.d.ts +109 -0
  39. package/dist/src/stig/runner.d.ts.map +1 -0
  40. package/dist/src/stig/runner.js +471 -0
  41. package/dist/src/stig/runner.js.map +1 -0
  42. package/dist/src/tools/appliance-claim-spec-schema.v1.json +1 -1
  43. package/dist/src/tools/appliance-claim-tools.d.ts +10 -4
  44. package/dist/src/tools/appliance-claim-tools.d.ts.map +1 -1
  45. package/dist/src/tools/appliance-claim-tools.js +10 -2
  46. package/dist/src/tools/appliance-claim-tools.js.map +1 -1
  47. package/dist/src/tools/claim-creation-projection.d.ts +1 -1
  48. package/dist/src/tools/claim-creation-projection.js +1 -1
  49. package/dist/src/tools/claim-custody.d.ts +7 -7
  50. package/dist/src/tools/claimed-import-qcow.d.ts +2 -2
  51. package/dist/src/tools/host-maintenance-shutdown.d.ts +4 -4
  52. package/dist/src/tools/host-migrate-vm.d.ts +4 -4
  53. package/dist/src/tools/index.d.ts.map +1 -1
  54. package/dist/src/tools/index.js +3 -0
  55. package/dist/src/tools/index.js.map +1 -1
  56. package/dist/src/tools/purge-slots.d.ts +1 -1
  57. package/dist/src/tools/sandbox-tools.d.ts +8 -8
  58. package/dist/src/tools/stig-tools.d.ts +47 -0
  59. package/dist/src/tools/stig-tools.d.ts.map +1 -0
  60. package/dist/src/tools/stig-tools.js +125 -0
  61. package/dist/src/tools/stig-tools.js.map +1 -0
  62. package/dist/src/tools/vm-action.d.ts +2 -2
  63. package/package.json +2 -1
@@ -0,0 +1,18 @@
1
+ a5f48191effe1b243c4d4fc5565cbbe31b6a6085aedf9a3b924e68d0b9ae399c files/ct-luks-firstboot.service
2
+ 5dc732b176181200ed18e6cf2448551d7c5e1a31cbd7bd9f4cbda4d30a4ad2eb files/ct-luks-firstboot.sh
3
+ dc035a0b24f24ff1a0e75fbbb9ff20262c1f83ef41d6c4e35a9bf7895ffd72e5 ks.cfg.pkrtpl
4
+ 97cda67f5851fcfd05a9a8dd4b0f885f9b5bad2ff85570935f14555356edbcdf scripts/assess.sh
5
+ 7c8b9f536f7d2fa81b26b125a8edccf7bc64c9959e5d5ce6482bab231e21ecd2 scripts/errata.sh
6
+ 9780d258e74fb8eb997a27c6866ddc3fdd78f54c5e61eb616618c5a3445056ef scripts/remediate.sh
7
+ 5e3760cfd6a752b5c97e6e1f6f557df385f4be3d2a4acc1b125480f785b0dbcb scripts/seal.sh
8
+ 43560c40096a28b180685fe1383344a6d454a3a18666f2ead4a1ddd3d72ed7fd tailoring.xml
9
+ 9a7e78eefcb3d8ce28dc137e56dc43ae6b6f3ba4bdecf9f3c0abe64658432105 template.pkr.hcl
10
+ eb119e742a4791404ca298c962515ffe3492814dfc72aecac16c420ccb3b0ccc guest/keyhash.sh
11
+ 3a964e9dc60fcb7492fabf2117fa04d91e0f5e34d0b3b1fe003aa7253f0daeeb guest/read-recovery.sh
12
+ 921ce015bb3f5188ccd861aef30fb1aed2588e729c371c3c8379802ce0c24be1 guest/scan.sh
13
+ 9abadcf3c9a1334447a6f349c6c78105d090550d6e8c09adb0320fc1a73e5019 guest/shred-recovery.sh
14
+ 0af1b98d2acf8827ac3861a0ec25f507483a3dc902f00ed215ce13b6dc4db0dc guest/test-recovery.sh
15
+ 6a63f43c312eed5941f0b86044ebe8b3457ce9412c5d9a1ceda67316d16a4209 guest/trim.sh
16
+ 6b3eab512392c9b68499cb3b4a753377bcc499b810e7259d8ede5e10861bf674 guest/verify-root.sh
17
+ 770cbf1c4ea374772d6e80734b1fe4d7d7f5826c8c528a5371f4dfea196479d7 guest/verify-user.sh
18
+ b4a37a0425369c99dfe9b63086155b7ffaa0ef73f98b158143ecb0cd84d45d94 guest/scan-disa.sh
@@ -0,0 +1,13 @@
1
+ [Unit]
2
+ Description=CallTelemetry clone first boot: re-encrypt LUKS with a new volume key, bind TPM2
3
+ ConditionPathExists=/etc/ct-luks-bootstrap.key
4
+ After=cloud-final.service
5
+ Wants=cloud-final.service
6
+
7
+ [Service]
8
+ Type=oneshot
9
+ ExecStart=/usr/bin/bash /usr/local/sbin/ct-luks-firstboot.sh
10
+ TimeoutStartSec=0
11
+
12
+ [Install]
13
+ WantedBy=multi-user.target
@@ -0,0 +1,35 @@
1
+ #!/bin/bash
2
+ # First boot of a clone (D-020). The template ships LUKS2 on the LVM PV unlocked by a
3
+ # template-wide bootstrap key embedded in the initramfs. Here, once per clone:
4
+ # 1. online re-encrypt (new volume key),
5
+ # 2. add a per-clone recovery passphrase (controller escrows it, then removes the file),
6
+ # 3. bind unlock to this clone's own TPM2 (clevis),
7
+ # 4. remove the bootstrap keyslot and key from the initramfs, then reboot.
8
+ set -euo pipefail
9
+ exec >>/var/log/ct-luks-firstboot.log 2>&1
10
+ echo "== $(date -u +%FT%TZ) ct-luks-firstboot start"
11
+ key=/etc/ct-luks-bootstrap.key
12
+ [ -f "$key" ] || { echo "bootstrap key absent; nothing to do"; exit 0; }
13
+ [ -c /dev/tpmrm0 ] || { echo "ERROR: no TPM2 device (/dev/tpmrm0); clone needs its own tpmstate0"; exit 1; }
14
+ dev="$(blkid -t TYPE=crypto_LUKS -o device | head -n1)"
15
+ [ -n "$dev" ] || { echo "ERROR: no LUKS device"; exit 1; }
16
+
17
+ cryptsetup reencrypt --key-file "$key" --pbkdf pbkdf2 --resilience none --batch-mode "$dev"
18
+ echo "reencrypt done $(date -u +%FT%TZ)"
19
+
20
+ umask 077
21
+ openssl rand -base64 60 | tr -dc 'A-Za-z0-9' | head -c 40 > /root/.luks-recovery
22
+ cryptsetup luksAddKey --key-file "$key" --pbkdf pbkdf2 --batch-mode "$dev" /root/.luks-recovery
23
+
24
+ clevis luks bind -f -d "$dev" -k "$key" tpm2 '{"pcr_bank":"sha256","pcr_ids":"7"}'
25
+
26
+ cryptsetup luksRemoveKey --batch-mode "$dev" "$key"
27
+ sed -i "s#${key}#none#" /etc/crypttab
28
+ rm -f /etc/dracut.conf.d/ct-luks-bootstrap.conf
29
+ shred -u "$key"
30
+ dracut -f --regenerate-all
31
+ fstrim -av || true # give the fully re-encrypted thin disk back (discards pass through LUKS)
32
+ touch /var/lib/ct-luks-firstboot.done
33
+ systemctl disable ct-luks-firstboot.service
34
+ echo "== $(date -u +%FT%TZ) done; rebooting to prove unattended TPM unlock"
35
+ systemctl reboot
@@ -0,0 +1,4 @@
1
+ #!/bin/bash
2
+ # sha256 of the LUKS volume key (never printed): proves two clones have different keys.
3
+ # The recovery passphrase arrives on stdin after the sudo password line.
4
+ o=$(cryptsetup luksDump --dump-master-key --batch-mode --key-file - $(blkid -t TYPE=crypto_LUKS -o device|head -n1)) && grep -q "MK dump" <<<"$o" && printf "%s" "$o" | sha256sum | cut -d" " -f1
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ test -f /root/.luks-recovery && cat /root/.luks-recovery
@@ -0,0 +1,17 @@
1
+ #!/bin/bash
2
+ # Second scan of a v3 clone: DISA's official V2R8 SCAP benchmark (profile MAC-1_Classified), next to the SSG scan.
3
+ # Writes into /var/tmp/ct-scan (created by scan.sh). Templates built before the dual-scan recipe do not carry the
4
+ # benchmark; then only a marker is written and the caller decides whether that is acceptable.
5
+ set -e
6
+ e=/var/tmp/ct-scan
7
+ ev=/root/stig-evidence
8
+ b=$ev/U_RHEL_8_V2R8_STIG_SCAP_1-3_Benchmark.xml
9
+ mkdir -p $e
10
+ if [ ! -f "$b" ]; then echo absent > $e/clone-disa-absent; chown -R ctadmin $e; exit 0; fi
11
+ drc=0
12
+ oscap xccdf eval --profile xccdf_mil.disa.stig_profile_MAC-1_Classified --results-arf $e/clone-disa-arf.xml --report $e/clone-disa-report.html $b > $e/clone-disa-stdout.txt 2>&1 || drc=$?
13
+ echo oscap_disa_exit=$drc
14
+ for r in pass fail notapplicable notchecked informational error unknown; do echo result_$r=$(grep -c "^Result.*$r\$" $e/clone-disa-stdout.txt || true); done > $e/clone-disa-summary.txt
15
+ grep -B2 -E '^Result.*(fail|error)$' $e/clone-disa-stdout.txt | grep '^Rule' > $e/clone-disa-failed-rules.txt || true
16
+ sha256sum $b | cut -d' ' -f1 > $e/clone-disa-benchmark.sha256
17
+ chown -R ctadmin $e
@@ -0,0 +1,22 @@
1
+ #!/bin/bash
2
+ # oscap on the clone itself (pinned datastream + tailoring baked into the template). Run as root.
3
+ # Output goes to /var/tmp/ct-scan, never under /home: root-owned scan output there would itself trip the
4
+ # home-file ownership rules. Faithful to the `do_scan` block of the known-good clone.sh.
5
+ set -e
6
+ e=/var/tmp/ct-scan
7
+ mkdir -p $e; ev=/root/stig-evidence; rc=0
8
+ oscap xccdf eval --tailoring-file $ev/tailoring.xml --profile xccdf_com.calltelemetry_profile_stig_ct --results-arf $e/clone-arf.xml --report $e/clone-report.html $ev/ssg-rhel8-ds.xml > $e/clone-stdout.txt 2>&1 || rc=$?
9
+ echo oscap_exit=$rc
10
+ for r in pass fail notapplicable notchecked informational error; do echo result_$r=$(grep -c "^Result.*$r\$" $e/clone-stdout.txt || true); done > $e/clone-summary.txt
11
+ grep -B2 -E '^Result.*(fail|error)$' $e/clone-stdout.txt | grep '^Rule' > $e/clone-failed-rules.txt || true
12
+ grep -B3 -E '^Result.*notchecked$' $e/clone-stdout.txt | grep -E '^(Title|Rule)' > $e/clone-notchecked.txt || true
13
+ { echo '## encrypt_partitions: lsblk -f'; lsblk -f
14
+ echo; echo '## encrypt_partitions: LUKS header (no key material)'; cryptsetup luksDump $(blkid -t TYPE=crypto_LUKS -o device|head -n1) | grep -vE '^[[:space:]]+(Salt|Digest|Key material|AF stripes|AF hash):' | head -60
15
+ echo; echo '## security_patches_up_to_date: outstanding security advisories'; dnf -q updateinfo list security 2>&1 | grep -c . || true
16
+ echo; echo '## selinux_user_login_roles: semanage login -l'; semanage login -l
17
+ echo; echo '## configured_firewalld_default_deny / configure_firewalld_ports'; firewall-cmd --get-default-zone; firewall-cmd --zone=drop --list-all
18
+ echo; echo '## account_temp_expire_date: accounts with an expiry'; awk -F: '$1!="root"{print $1}' /etc/passwd | while read u; do chage -l $u | grep -H --label=$u 'Account expires'; done | grep -v never || echo 'none (no temporary accounts)'
19
+ echo; echo '## auditd partition size'; df -h /var/log/audit | tail -n1
20
+ echo; echo '## ssh_keys_passphrase_protected: private keys in user homes'; find /home /root -maxdepth 3 -name 'id_*' ! -name '*.pub' 2>/dev/null | grep . || echo 'none'
21
+ echo; echo '## sssd certmap trust anchor'; grep -c certmap /etc/sssd/sssd.conf; } > $e/clone-manual-evidence.txt 2>&1
22
+ chown -R ctadmin $e
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ shred -u /root/.luks-recovery
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ # Reads the recovery passphrase from stdin (after the sudo password line) and proves it opens the LUKS volume.
3
+ cryptsetup open --test-passphrase --key-file - $(blkid -t TYPE=crypto_LUKS -o device|head -n1) && echo ok
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ # Discards pass through LUKS: give the re-encrypted (fully allocated) thin disk back.
3
+ fstrim -av
@@ -0,0 +1,9 @@
1
+ #!/bin/bash
2
+ # Privileged half of the clone verification. Prints key=value lines; never key material.
3
+ dev=$(blkid -t TYPE=crypto_LUKS -o device|head -n1)
4
+ echo sudo_id=$(id -Z)
5
+ echo luks_type=$(cryptsetup isLuks -v $dev >/dev/null && cryptsetup luksDump $dev | sed -n "s/^Version:[[:space:]]*//p")
6
+ echo luks_passphrase_slots=$(cryptsetup luksDump $dev | grep -cE "^ [0-9]+: luks2")
7
+ echo clevis_tpm2_tokens=$(cryptsetup luksDump $dev | grep -c "clevis")
8
+ echo bootstrap_key_file=$(test -e /etc/ct-luks-bootstrap.key && echo present || echo absent)
9
+ echo tpm_bound=$(clevis luks list -d $dev | grep -c tpm2)
@@ -0,0 +1,9 @@
1
+ #!/bin/bash
2
+ # Unprivileged half of the clone verification (known-good clone.sh `verify`).
3
+ echo fips_enabled=$(cat /proc/sys/crypto/fips_enabled)
4
+ echo selinux=$(getenforce)
5
+ echo host=$(hostname)
6
+ echo context=$(id -Z)
7
+ ls /dev/tpmrm0 >/dev/null 2>&1 && echo tpm=present
8
+ grep ^PRETTY_NAME= /etc/os-release
9
+ echo kernel=$(uname -r)
@@ -0,0 +1,95 @@
1
+ # RHEL 8.10 STIG template install. The installer applies the DISA STIG profile
2
+ # from the DVD's scap-security-guide; Packer then re-remediates and assesses with
3
+ # the pinned SSG release. The PV is LUKS2 with a template-wide bootstrap key; each clone
4
+ # re-encrypts with a new volume key and binds its own TPM2 at first boot (D-020).
5
+ text --non-interactive
6
+ cdrom
7
+ lang en_US.UTF-8
8
+ keyboard --xlayouts=us
9
+ timezone UTC --utc
10
+ rootpw --lock
11
+ selinux --enforcing
12
+ firewall --enabled --service=ssh
13
+ network --bootproto=dhcp --device=link --onboot=yes --activate --hostname=${hostname}
14
+ services --enabled=sshd,qemu-guest-agent,chronyd,auditd --disabled=kdump
15
+ firstboot --disable
16
+ eula --agreed
17
+ skipx
18
+
19
+ zerombr
20
+ clearpart --all --initlabel --disklabel=gpt
21
+ bootloader --location=mbr --iscrypted --password=${grub_pbkdf2}
22
+ part /boot/efi --fstype=efi --size=1024 --fsoptions="umask=0077,shortname=winnt,nosuid,nodev"
23
+ part /boot --fstype=xfs --size=2048 --fsoptions="nosuid,nodev"
24
+ part pv.01 --size=1 --grow --encrypted --luks-version=luks2 --pbkdf=pbkdf2 --passphrase=${luks_bootstrap}
25
+ volgroup vg_root pv.01
26
+ logvol / --vgname=vg_root --name=root --fstype=xfs --size=24576
27
+ logvol /home --vgname=vg_root --name=home --fstype=xfs --size=16384 --fsoptions="nodev,nosuid,noexec"
28
+ logvol /tmp --vgname=vg_root --name=tmp --fstype=xfs --size=8192 --fsoptions="nodev,nosuid,noexec"
29
+ logvol /var --vgname=vg_root --name=var --fstype=xfs --size=24576 --fsoptions="nodev"
30
+ logvol /var/log --vgname=vg_root --name=log --fstype=xfs --size=16384 --fsoptions="nodev,nosuid,noexec"
31
+ logvol /var/log/audit --vgname=vg_root --name=audit --fstype=xfs --size=8192 --fsoptions="nodev,nosuid,noexec"
32
+ logvol /var/tmp --vgname=vg_root --name=vartmp --fstype=xfs --size=8192 --fsoptions="nodev,nosuid,noexec"
33
+ logvol /opt --vgname=vg_root --name=opt --fstype=xfs --size=8192
34
+ logvol swap --vgname=vg_root --name=swap --size=8192
35
+
36
+ # Temporary build-only administrator (D-019). Removed by seal.sh.
37
+ user --name=${ssh_username} --groups=wheel --iscrypted --password=${build_password_hash}
38
+ sshkey --username=${ssh_username} "${ssh_public_key}"
39
+
40
+ %packages --excludedocs
41
+ @^minimal-environment
42
+ openssh-server
43
+ openssh-clients
44
+ qemu-guest-agent
45
+ sudo
46
+ chrony
47
+ dracut-fips
48
+ crypto-policies-scripts
49
+ cloud-init
50
+ cloud-utils-growpart
51
+ openscap-scanner
52
+ scap-security-guide
53
+ aide
54
+ audit
55
+ rsyslog
56
+ fapolicyd
57
+ usbguard
58
+ tmux
59
+ cryptsetup
60
+ clevis
61
+ clevis-luks
62
+ clevis-dracut
63
+ tpm2-tools
64
+ sssd
65
+ sssd-tools
66
+ openssl
67
+ firewalld
68
+ subscription-manager
69
+ policycoreutils-python-utils
70
+ %end
71
+
72
+ %addon com_redhat_kdump --disable
73
+ %end
74
+
75
+ %addon org_fedora_oscap
76
+ content-type = scap-security-guide
77
+ profile = xccdf_org.ssgproject.content_profile_stig
78
+ %end
79
+
80
+ %post --erroronfail
81
+ # Template-wide LUKS bootstrap key, embedded in the initramfs so the template boots
82
+ # unattended. Replaced per clone by ct-luks-firstboot (new volume key + TPM2).
83
+ printf '%s' '${luks_bootstrap}' > /etc/ct-luks-bootstrap.key
84
+ chmod 0400 /etc/ct-luks-bootstrap.key
85
+ sed -i 's#^\(luks-[^ ]* [^ ]* \)none#\1/etc/ct-luks-bootstrap.key#' /etc/crypttab
86
+ cat > /etc/dracut.conf.d/ct-luks-bootstrap.conf <<'DRACUT'
87
+ add_dracutmodules+=" crypt clevis "
88
+ install_items+=" /etc/ct-luks-bootstrap.key "
89
+ DRACUT
90
+ dracut -f --regenerate-all
91
+ # The build user must be usable over key-only SSH with no TTY (Packer): no expired password.
92
+ chage -d "$(date +%F)" -M 99999 -E -1 ${ssh_username}
93
+ %end
94
+
95
+ reboot
@@ -0,0 +1,39 @@
1
+ #!/bin/bash
2
+ # Final STIG assessment of the rebooted image. Reports are copied to the controller.
3
+ set -euo pipefail
4
+
5
+ evidence=/root/stig-evidence
6
+ out=/home/${BUILD_USER}/stig-report
7
+ rc=0
8
+ oscap xccdf eval \
9
+ --tailoring-file "$evidence/tailoring.xml" \
10
+ --profile xccdf_com.calltelemetry_profile_stig_ct \
11
+ --results-arf "$evidence/final-arf.xml" \
12
+ --report "$evidence/final-report.html" \
13
+ "$evidence/ssg-rhel8-ds.xml" > "$evidence/final-stdout.txt" 2>&1 || rc=$?
14
+ echo "oscap eval exit=$rc"
15
+ [ "$rc" -eq 0 ] || [ "$rc" -eq 2 ]
16
+
17
+ {
18
+ echo "fips_enabled=$(cat /proc/sys/crypto/fips_enabled)"
19
+ echo "selinux=$(getenforce)"
20
+ echo "kernel=$(uname -r)"
21
+ grep -E '^(NAME|VERSION_ID)=' /etc/os-release
22
+ rpm -q scap-security-guide openscap-scanner
23
+ echo "tailoring_sha256=$(cut -d" " -f1 "$evidence/tailoring.sha256")"
24
+ echo "ds_sha256=$(sha256sum "$evidence/ssg-rhel8-ds.xml" | cut -d" " -f1)"
25
+ echo "dnf_security_outstanding=$(dnf -q updateinfo list security 2>/dev/null | grep -c . || true)"
26
+ lsblk -f > "$evidence/lsblk.txt"; cryptsetup luksDump "$(blkid -t TYPE=crypto_LUKS -o device | head -n1)" > "$evidence/luksdump-template.txt" 2>&1 || true
27
+ for r in pass fail notapplicable notchecked informational error; do
28
+ echo "result_${r}=$(grep -c "^Result.*${r}\$" "$evidence/final-stdout.txt" || true)"
29
+ done
30
+ } > "$evidence/summary.txt"
31
+ cat "$evidence/summary.txt"
32
+ rpm -qa --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' | sort > "$evidence/rpm-qa-final.txt"
33
+ grep -B2 -E '^Result.*fail$' "$evidence/final-stdout.txt" | grep '^Rule' | sed 's/^Rule[[:space:]]*//' > "$evidence/failed-rules.txt" || true
34
+
35
+ rm -rf "$out"
36
+ install -d -o "${BUILD_USER}" -g "${BUILD_USER}" -m 0700 "$out"
37
+ cp "$evidence"/{lsblk.txt,luksdump-template.txt,tailoring.xml,tailoring.sha256,errata.txt,final-arf.xml,final-report.html,final-stdout.txt,summary.txt,failed-rules.txt,remediate-report.html,remediate-stdout.txt,rpm-qa-final.txt} "$out/"
38
+ cp -r "$evidence/install-time-oscap" "$out/" 2>/dev/null || true
39
+ chown -R "${BUILD_USER}:${BUILD_USER}" "$out"
@@ -0,0 +1,19 @@
1
+ #!/bin/bash
2
+ # Apply current RHEL 8.10 errata through RHSM, then reboot into the updated kernel.
3
+ # Registration is removed again by seal.sh. Credentials arrive as environment variables
4
+ # and are never echoed.
5
+ set -euo pipefail
6
+
7
+ : "${RHSM_ORG:?}" "${RHSM_KEY:?}"
8
+ subscription-manager register --org "$RHSM_ORG" --activationkey "$RHSM_KEY" --force >/dev/null
9
+ subscription-manager repos --list-enabled | grep -E '^Repo ID' || true
10
+ dnf -y update
11
+ install -d -m 0700 /root/stig-evidence
12
+ rpm -qa --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' | sort > /root/stig-evidence/rpm-qa-post-errata.txt
13
+ {
14
+ echo "kernel_installed=$(rpm -q kernel --qf '%{VERSION}-%{RELEASE}.%{ARCH}\n' | sort -V | tail -n1)"
15
+ echo "security_advisories_outstanding=$(dnf -q updateinfo list security 2>/dev/null | grep -c . || true)"
16
+ echo "errata_applied=$(date -u +%FT%TZ)"
17
+ } > /root/stig-evidence/errata.txt
18
+ cat /root/stig-evidence/errata.txt
19
+ (sleep 3; systemctl reboot) >/dev/null 2>&1 &
@@ -0,0 +1,127 @@
1
+ #!/bin/bash
2
+ # CallTelemetry organization settings, then the pinned SSG STIG remediation with the
3
+ # organization tailoring, then reboot. oscap exits 2 when rules still fail after
4
+ # remediation; that is a finding, not a build failure.
5
+ set -euo pipefail
6
+
7
+ ds="$SSG_DATASTREAM"
8
+ echo "${SSG_DATASTREAM_SHA256} ${ds}" | sha256sum -c -
9
+ profile=xccdf_com.calltelemetry_profile_stig_ct
10
+
11
+ evidence=/root/stig-evidence
12
+ install -d -m 0700 "$evidence"
13
+ cp "$ds" "$evidence/ssg-rhel8-ds.xml"
14
+ cp "$TAILORING" "$evidence/tailoring.xml"
15
+ sha256sum "$evidence/tailoring.xml" > "$evidence/tailoring.sha256"
16
+ rpm -qa --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' | sort > "$evidence/rpm-qa-pre-remediate.txt"
17
+ cp /root/anaconda-ks.cfg "$evidence/" 2>/dev/null || true
18
+ cp -r /root/openscap_data "$evidence/install-time-oscap" 2>/dev/null || true
19
+
20
+ # --- clone first-boot encryption (D-020) ---
21
+ install -m 0755 "$FILES_DIR/ct-luks-firstboot.sh" /usr/local/sbin/ct-luks-firstboot.sh
22
+ install -m 0644 "$FILES_DIR/ct-luks-firstboot.service" /etc/systemd/system/ct-luks-firstboot.service
23
+ systemctl daemon-reload
24
+ systemctl enable ct-luks-firstboot.service
25
+ restorecon -F /usr/local/sbin/ct-luks-firstboot.sh /etc/systemd/system/ct-luks-firstboot.service
26
+
27
+ # --- GRUB: non-default superuser (grub2_uefi_admin_username), keep the PBKDF2 password ---
28
+ sed -i 's/set superusers="root"/set superusers="grubadmin"/; s/password_pbkdf2 root /password_pbkdf2 grubadmin /' /etc/grub.d/01_users
29
+ grep -q 'superusers="grubadmin"' /etc/grub.d/01_users
30
+ grub2-mkconfig -o /boot/grub2/grub.cfg >/dev/null 2>&1
31
+ grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg >/dev/null 2>&1 || true
32
+
33
+ # --- DNS: two lab nameservers, independent of DHCP (network_configure_name_resolution) ---
34
+ cat > /etc/NetworkManager/conf.d/90-ct-dns.conf <<NM
35
+ [global-dns-domain-*]
36
+ servers=${DNS_SERVERS}
37
+ NM
38
+ systemctl restart NetworkManager
39
+ sleep 5
40
+
41
+ # --- firewalld: default zone drop, ssh allowed (set_firewalld_default_zone) ---
42
+ firewall-cmd --permanent --zone=drop --add-service=ssh >/dev/null
43
+ firewall-cmd --zone=drop --add-service=ssh >/dev/null
44
+ firewall-cmd --set-default-zone=drop >/dev/null
45
+
46
+ # --- chrony: server directives only (chronyd_server_directive) ---
47
+ sed -i '/^pool /d' /etc/chrony.conf
48
+ for s in ${NTP_SERVERS//,/ }; do
49
+ grep -q "^server $s " /etc/chrony.conf || echo "server $s iburst maxpoll 16" >> /etc/chrony.conf
50
+ done
51
+ systemctl restart chronyd
52
+
53
+ # --- SSSD with a certmap rule (sssd_enable_certmap); CAC matchrule is a placeholder ---
54
+ cat > /etc/sssd/sssd.conf <<'SSSD'
55
+ [sssd]
56
+ config_file_version = 2
57
+ services = nss, pam
58
+ domains = ctlocal
59
+
60
+ [domain/ctlocal]
61
+ id_provider = files
62
+
63
+ [pam]
64
+ pam_cert_auth = True
65
+
66
+ [certmap/ctlocal/dod_cac]
67
+ matchrule = <SAN>.*EDIPI@mil
68
+ maprule = (userCertificate;binary={cert!bin})
69
+ domains = ctlocal
70
+ SSSD
71
+ chmod 0600 /etc/sssd/sssd.conf
72
+ systemctl enable sssd
73
+
74
+ # --- sudo: elevate SELinux context (selinux_context_elevation_for_sudo); password still required ---
75
+ cat > /etc/sudoers.d/ct-selinux-elevation <<'SUDOERS'
76
+ %wheel ALL=(ALL) TYPE=sysadm_t ROLE=sysadm_r ALL
77
+ SUDOERS
78
+ chmod 0440 /etc/sudoers.d/ct-selinux-elevation
79
+ visudo -cf /etc/sudoers.d/ct-selinux-elevation
80
+
81
+ # --- cloud-init default user: ctadmin in wheel, password sudo only (no NOPASSWD) ---
82
+ cat > /etc/cloud/cloud.cfg.d/99-ct-stig.cfg <<'CLOUD'
83
+ system_info:
84
+ default_user:
85
+ name: ctadmin
86
+ gecos: CallTelemetry administrator
87
+ groups: [wheel, adm, systemd-journal]
88
+ sudo: false
89
+ lock_passwd: false
90
+ shell: /bin/bash
91
+ ssh_pwauth: false
92
+ CLOUD
93
+
94
+ # --- SELinux user mapping: administrators log in as staff_u and elevate to sysadm_r via sudo ---
95
+ semanage login -m -s staff_u -r s0-s0:c0.c1023 __default__
96
+
97
+ # --- init-file and host-key permissions that must also hold on clones ---
98
+ # New users inherit /etc/skel; root's init files must be <= 0740 (file_permission_user_init_files*).
99
+ # The build user (removed at seal) was created before /etc/skel was fixed.
100
+ chmod 0740 /home/${BUILD_USER}/.bash_logout /home/${BUILD_USER}/.bash_profile /home/${BUILD_USER}/.bashrc
101
+ # cloud-init already created ctadmin on the build VM's own first boot (removed again at seal).
102
+ chmod 0740 /home/ctadmin/.bash_logout /home/ctadmin/.bash_profile /home/ctadmin/.bashrc 2>/dev/null || true
103
+ chmod 0740 /etc/skel/.bash_logout /etc/skel/.bash_profile /etc/skel/.bashrc /root/.bash_logout /root/.bash_profile /root/.bashrc /root/.cshrc /root/.tcshrc 2>/dev/null || true
104
+ # sshd-keygen creates host keys 0640 root:ssh_keys on every clone's first boot; the STIG
105
+ # (file_permissions_sshd_private_key) requires 0600. Re-assert it whenever sshd starts.
106
+ install -d /etc/systemd/system/sshd.service.d
107
+ cat > /etc/systemd/system/sshd.service.d/10-ct-hostkey-perms.conf <<'DROPIN'
108
+ [Service]
109
+ ExecStartPre=/usr/bin/bash -c 'chmod 0600 /etc/ssh/ssh_host_*_key'
110
+ DROPIN
111
+ systemctl daemon-reload
112
+ chmod 0600 /etc/ssh/ssh_host_*_key
113
+
114
+ # --- SSG STIG remediation with the organization tailoring ---
115
+ rc=0
116
+ oscap xccdf eval \
117
+ --tailoring-file "$evidence/tailoring.xml" \
118
+ --profile "$profile" \
119
+ --remediate \
120
+ --results-arf "$evidence/remediate-arf.xml" \
121
+ --report "$evidence/remediate-report.html" \
122
+ "$evidence/ssg-rhel8-ds.xml" > "$evidence/remediate-stdout.txt" 2>&1 || rc=$?
123
+ echo "oscap remediate exit=$rc"
124
+ [ "$rc" -eq 0 ] || [ "$rc" -eq 2 ]
125
+
126
+ fips-mode-setup --check
127
+ (sleep 3; systemctl reboot) >/dev/null 2>&1 &
@@ -0,0 +1,46 @@
1
+ #!/bin/bash
2
+ # Generalize the image for cloning and remove the temporary build access (D-019).
3
+ set -euo pipefail
4
+
5
+ # Drop the RHSM registration and entitlement certificates (errata step registered this build).
6
+ subscription-manager unregister >/dev/null 2>&1 || true
7
+ subscription-manager clean >/dev/null 2>&1 || true
8
+ rm -rf /etc/pki/entitlement/* /etc/pki/consumer/* /var/lib/rhsm/*
9
+
10
+ # cloud-init created ctadmin on this build VM; clones create their own (from the fixed /etc/skel).
11
+ userdel --remove ctadmin 2>/dev/null || true
12
+
13
+ # Per-instance identity is regenerated on each clone's first boot.
14
+ rm -f /etc/ssh/ssh_host_*
15
+ truncate -s 0 /etc/machine-id
16
+ rm -f /var/lib/dbus/machine-id
17
+ cloud-init clean --logs --seed
18
+ rm -f /etc/NetworkManager/system-connections/*.nmconnection
19
+ rm -rf /root/stig-evidence/*.xml.bak /var/tmp/* /tmp/* 2>/dev/null || true
20
+ journalctl --rotate && journalctl --vacuum-time=1s >/dev/null
21
+ find /var/log -type f -name '*.log' -exec truncate -s 0 {} +
22
+ dnf clean all >/dev/null
23
+ history -c || true
24
+
25
+ # Clones authenticate with the claim's cloud-init key, never the build key.
26
+ rm -rf "/home/${BUILD_USER}/.ssh" "/home/${BUILD_USER}/stig-report" "/home/${BUILD_USER}/ssg-rhel8-ds.xml"
27
+ usermod --lock --expiredate 1 "${BUILD_USER}"
28
+ # Delete the account when Packer shuts the VM down, after this session ends.
29
+ unit=/etc/systemd/system/ct-remove-build-user.service
30
+ cat > "$unit" <<EOF
31
+ [Unit]
32
+ Description=Remove temporary template build user at shutdown
33
+
34
+ [Service]
35
+ Type=oneshot
36
+ RemainAfterExit=yes
37
+ ExecStart=/bin/true
38
+ ExecStop=/usr/sbin/userdel --remove --force ${BUILD_USER}
39
+ ExecStop=/bin/rm -f ${unit} /etc/systemd/system/multi-user.target.wants/ct-remove-build-user.service
40
+
41
+ [Install]
42
+ WantedBy=multi-user.target
43
+ EOF
44
+ systemctl daemon-reload
45
+ systemctl enable --now ct-remove-build-user.service
46
+ sync
@@ -0,0 +1,16 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!-- Organization-defined values for the DISA RHEL 8 STIG profile (SSG 0.1.82).
3
+ Only values the STIG leaves to the organization are set; no rule is selected out.
4
+ accounts_authorized_local_users: the STIG requires the organization to define the
5
+ authorized local accounts. The list is the RHEL 8 minimal system accounts present on
6
+ the image, the CallTelemetry administrator (ctadmin, created by cloud-init on clones),
7
+ and ctbuild (temporary Packer build account, deleted when the template is sealed). -->
8
+ <xccdf-1.2:Tailoring xmlns:xccdf-1.2="http://checklists.nist.gov/xccdf/1.2" id="xccdf_com.calltelemetry_tailoring_stig_ct">
9
+ <xccdf-1.2:benchmark href="ssg-rhel8-ds.xml"/>
10
+ <xccdf-1.2:version time="2026-10-16T00:00:00">1</xccdf-1.2:version>
11
+ <xccdf-1.2:Profile id="xccdf_com.calltelemetry_profile_stig_ct" extends="xccdf_org.ssgproject.content_profile_stig">
12
+ <xccdf-1.2:title>DISA RHEL 8 STIG - CallTelemetry organization-defined values</xccdf-1.2:title>
13
+ <xccdf-1.2:description>Extends the SSG STIG profile; sets only organization-defined variables.</xccdf-1.2:description>
14
+ <xccdf-1.2:set-value idref="xccdf_org.ssgproject.content_value_var_accounts_authorized_local_users_regex">^(root|bin|daemon|adm|lp|sync|shutdown|halt|mail|operator|games|ftp|nobody|dbus|systemd-coredump|systemd-resolve|tss|polkitd|clevis|unbound|sssd|fapolicyd|chrony|sshd|postfix|ctadmin|ctbuild)$</xccdf-1.2:set-value>
15
+ </xccdf-1.2:Profile>
16
+ </xccdf-1.2:Tailoring>