Skip to content

Commit 3619ed3

Browse files
committed
fix(stemcell): fix audit rules and add arm64 systemd for multiarch warden
Audit rule corrections for Ubuntu Resolute: Time-change rule (32-bit): drop -S stime from the adjtimex/settimeofday line. On current Ubuntu/glibc, stime is not a usable syscall, so removing it matches what the image can actually ship and what auditd will accept. System-locale rules: reorder exit,always → always,exit to match common auditd ordering and CIS-style wording, consistent with the rest of the file. Adds the base_ubuntu_arm64_systemd stage for the "multiarch" warden variant (resolute-multiarch). This stage replaces x86_64 systemd ELF binaries with arm64 equivalents so that systemd runs natively on Apple Silicon arm64 kernels under Rosetta, fixing pidfd_open and pidfd_send_signal ENOSYS failures. Standard warden and cloud infrastructure builds are unaffected. Updates stage collection, kernel, grub, and test fixtures to reflect the Resolute stemcell layout.
1 parent 61738dc commit 3619ed3

14 files changed

Lines changed: 385 additions & 158 deletions

File tree

bosh-stemcell/lib/bosh/stemcell/stage_collection.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ def google_stages
198198
end
199199

200200
def warden_stages
201-
[
201+
stages = [
202202
:system_parameters,
203203
:base_warden,
204204
:bosh_clean,
@@ -211,6 +211,8 @@ def warden_stages
211211
:image_install_grub,
212212
:sbom_create
213213
]
214+
stages.insert(2, :base_ubuntu_warden_rosetta) if operating_system.variant == "rosetta"
215+
stages
214216
end
215217

216218
def azure_stages

bosh-stemcell/spec/assets/dpkg-list-ubuntu.txt

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -382,25 +382,7 @@ libyaml-0-2:amd64
382382
libzstd-dev:amd64
383383
libzstd1:amd64
384384
linux-base
385-
linux-firmware
386-
linux-firmware-amd-graphics
387-
linux-firmware-amd-misc
388-
linux-firmware-broadcom-wireless
389-
linux-firmware-intel-graphics
390-
linux-firmware-intel-misc
391-
linux-firmware-intel-wireless
392-
linux-firmware-marvell-prestera
393-
linux-firmware-marvell-wireless
394-
linux-firmware-mediatek
395-
linux-firmware-mellanox-spectrum
396-
linux-firmware-misc
397-
linux-firmware-netronome
398-
linux-firmware-nvidia-graphics
399-
linux-firmware-qlogic
400-
linux-firmware-qualcomm-graphics
401-
linux-firmware-qualcomm-misc
402-
linux-firmware-qualcomm-wireless
403-
linux-firmware-realtek
385+
linux-firmware-minimal
404386
linux-libc-dev:amd64
405387
linux-sysctl-defaults
406388
locales

bosh-stemcell/spec/os_image/ubuntu_spec.rb

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,13 @@
246246

247247
describe file("/lib/systemd/system/auditd.service") do
248248
it { should be_file }
249-
its(:content) { should match(/^ExecStartPost=-\/sbin\/augenrules --load$/) }
250-
its(:content) { should match(/^#ExecStartPost=-\/sbin\/auditctl/) }
249+
its(:content) { should match(/^Wants=audit-rules\.service$/) }
250+
its(:content) { should_not match(/^ExecStartPost=.*auditctl/) }
251+
end
252+
253+
describe file("/lib/systemd/system/audit-rules.service") do
254+
it { should be_file }
255+
its(:content) { should match(/^ExecStart.*augenrules/) }
251256
end
252257
end
253258

@@ -552,6 +557,35 @@
552557
end
553558
end
554559

560+
context "runit removed (Resolute Raccoon: no chpst)" do
561+
# Per Resolute RFC #1498, the runit package is removed from the stemcell.
562+
# Releases must migrate off chpst (use BPM, su, runuser, or setpriv instead).
563+
describe package("runit") do
564+
it { should_not be_installed }
565+
end
566+
567+
describe file("/usr/bin/chpst") do
568+
it { should_not be_file }
569+
end
570+
571+
describe file("/usr/bin/runsv") do
572+
it { should_not be_file }
573+
end
574+
575+
describe file("/usr/sbin/runit") do
576+
it { should_not be_file }
577+
end
578+
end
579+
580+
context "tmp.mount masked (systemd 259)" do
581+
# systemd 259 introduced a static tmp.mount unit that mounts /tmp as a tmpfs.
582+
# bosh already configures the VM's /tmp (as a tmpfs, sized smaller than
583+
# systemd's default). Mask tmp.mount so systemd cannot override that setup.
584+
describe file("/etc/systemd/system/tmp.mount") do
585+
it { should be_linked_to File::NULL }
586+
end
587+
end
588+
555589
describe "systemd-resolved modifications" do
556590
describe file("/lib/systemd/system/create-systemd-resolved-listener-address.service") do
557591
its(:content) { should match(/ip addr add 169\.254\.0\.53 dev lo/) }
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
require "spec_helper"
2+
3+
describe "Rosetta Warden Stemcell", stemcell_image: true do
4+
context "arm64 systemd binaries (Rosetta pidfd fix)" do
5+
# On Apple Silicon (arm64 kernel + Rosetta x86_64 emulation), systemd v256+
6+
# calls pidfd_open and pidfd_send_signal which Rosetta does not translate for
7+
# x86_64 processes, causing ENOSYS. These assertions verify that all key
8+
# systemd binaries have been replaced with arm64 ELF equivalents so they run
9+
# natively on the arm64 kernel without Rosetta translation.
10+
11+
# Only service daemons are replaced with arm64; user-facing CLI tools in
12+
# /usr/bin/ (systemctl, journalctl, etc.) remain x86-64 because they
13+
# communicate with PID1 over D-Bus and never call pidfd themselves.
14+
%w[
15+
/usr/lib/systemd/systemd
16+
/usr/lib/systemd/systemd-executor
17+
/usr/lib/systemd/systemd-journald
18+
/usr/lib/systemd/systemd-logind
19+
/usr/lib/systemd/systemd-networkd
20+
/usr/lib/systemd/systemd-resolved
21+
/usr/lib/systemd/systemd-shutdown
22+
].each do |bin|
23+
describe command("file #{bin}") do
24+
its(:stdout) { should match(/ELF 64-bit.*ARM aarch64/) }
25+
end
26+
end
27+
28+
# arm64 runtime libraries land in /lib/aarch64-linux-gnu/ via Ubuntu multiarch.
29+
# On Ubuntu 22.04+ (UsrMerge), /lib is a symlink to usr/lib; the real file is
30+
# at /lib/aarch64-linux-gnu/libc.so.6 (resolves via the UsrMerge symlink).
31+
describe file("/lib/aarch64-linux-gnu/libc.so.6") do
32+
it { should be_file }
33+
end
34+
35+
# The arm64 ELF interpreter is at the multiarch path; /lib/ld-linux-aarch64.so.1
36+
# is a symlink to it but ShelloutTypes::File cannot check symlink targets reliably.
37+
describe file("/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1") do
38+
it { should be_file }
39+
end
40+
41+
describe file("/usr/lib/aarch64-linux-gnu/systemd/libsystemd-shared-259.so") do
42+
it { should be_file }
43+
end
44+
end
45+
46+
context "Rosetta x86_64 emulation compatibility for Apple Silicon" do
47+
# These systemd drop-in overrides disable security features that conflict
48+
# with Rosetta's JIT compilation on Apple Silicon Macs.
49+
50+
rosetta_services = %w[
51+
systemd-journald
52+
systemd-resolved
53+
systemd-networkd
54+
systemd-logind
55+
systemd-timesyncd
56+
auditd
57+
]
58+
59+
rosetta_services.each do |service|
60+
describe file("/etc/systemd/system/#{service}.service.d/rosetta-compat.conf") do
61+
it { should be_file }
62+
its(:content) { should include("MemoryDenyWriteExecute=no") }
63+
its(:content) { should include("LockPersonality=no") }
64+
its(:content) { should include("NoNewPrivileges=no") }
65+
end
66+
end
67+
68+
describe file("/etc/systemd/system/systemd-binfmt.service") do
69+
it { should be_linked_to File::NULL }
70+
end
71+
end
72+
73+
context "SSH without socket activation (Rosetta/Colima ENOSYS)" do
74+
describe file("/etc/systemd/system/ssh.socket") do
75+
it { should be_linked_to File::NULL }
76+
end
77+
78+
describe file("/etc/systemd/system/ssh.service.d/warden-no-socket-activation.conf") do
79+
it { should be_file }
80+
its(:content) { should include("RefuseManualStart=no") }
81+
end
82+
end
83+
84+
context "auditd foreground (Rosetta/Colima Docker pidfd ENOSYS)" do
85+
# Under Docker/Colima with Rosetta emulation, systemd cannot create pidfd
86+
# references or cgroup entries for processes started with Type=forking + PIDFile.
87+
# Running auditd with -n (no-fork / foreground) avoids the fork-and-PIDFile
88+
# lifecycle entirely, so systemd tracks the process directly without pidfd.
89+
describe file("/etc/systemd/system/auditd.service.d/warden-auditd-foreground.conf") do
90+
it { should be_file }
91+
its(:content) { should include("Type=simple") }
92+
its(:content) { should include("ExecStart=/usr/sbin/auditd -n") }
93+
end
94+
end
95+
96+
context "restrict access to the su command CIS-9.5 (Rosetta PAM override)" do
97+
# The Rosetta stemcell replaces /etc/pam.d/su with a minimal config that
98+
# avoids unix-chkpwd (which AppArmor blocks under Lima/Rosetta, causing every
99+
# su invocation to fail with "Authentication failure" even for root).
100+
# pam_wheel.so use_uid is kept in the replacement config so that the CIS-9.5
101+
# requirement — only wheel-group members may use su — is still enforced.
102+
# This test verifies that the override did not inadvertently remove the wheel check.
103+
describe command('grep "^\s*auth\s*required\s*pam_wheel.so\s*use_uid" /etc/pam.d/su') do
104+
it("exits 0") { expect(subject.exit_status).to eq(0) }
105+
end
106+
end
107+
end

bosh-stemcell/spec/stemcells/ubuntu_spec.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,8 @@
376376
exclude_on_cloudstack: true,
377377
exclude_on_google: true,
378378
exclude_on_vsphere: true,
379-
exclude_on_azure: true
379+
exclude_on_azure: true,
380+
exclude_on_rosetta: true
380381
} do
381382
it "contains only the base set of packages for alicloud, aws, openstack, warden" do
382383
expect(subject.stdout.split("\n")).to match_array(dpkg_list_ubuntu.concat(dpkg_list_kernel_ubuntu))

bosh-stemcell/spec/stemcells/warden_spec.rb

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,49 +21,33 @@
2121
end
2222
end
2323

24-
context "Rosetta x86_64 emulation compatibility for Apple Silicon" do
25-
# These systemd drop-in overrides disable security features that conflict
26-
# with Rosetta's JIT compilation on Apple Silicon Macs
27-
28-
rosetta_services = %w[
29-
systemd-journald
30-
systemd-resolved
31-
systemd-networkd
32-
systemd-logind
33-
systemd-timesyncd
34-
auditd
35-
]
36-
37-
rosetta_services.each do |service|
38-
describe file("/etc/systemd/system/#{service}.service.d/rosetta-compat.conf") do
39-
it { should be_file }
40-
its(:content) { should include("MemoryDenyWriteExecute=no") }
41-
its(:content) { should include("LockPersonality=no") }
42-
its(:content) { should include("NoNewPrivileges=no") }
43-
end
24+
context "runit removed (Resolute Raccoon: no chpst)" do
25+
# Per the Resolute RFC #1498 the runit package is removed from the
26+
# stemcell. Releases must migrate off chpst (BPM / su / runuser / setpriv).
27+
#
28+
# This negative-assertion test should be removed in the next stemcell line.
29+
describe file("/usr/bin/chpst") do
30+
it { should_not be_file }
4431
end
4532

46-
describe file("/etc/systemd/system/systemd-binfmt.service") do
47-
it { should be_linked_to File::NULL }
33+
describe file("/usr/bin/runsv") do
34+
it { should_not be_file }
4835
end
49-
end
5036

51-
context "SSH without socket activation (warden containers)" do
52-
describe file("/etc/systemd/system/ssh.socket") do
53-
it { should be_linked_to File::NULL }
37+
describe file("/usr/sbin/runit") do
38+
it { should_not be_file }
5439
end
5540

56-
describe file("/etc/systemd/system/ssh.service.d/warden-no-socket-activation.conf") do
57-
it { should be_file }
58-
its(:content) { should include("RefuseManualStart=no") }
41+
describe package("runit") do
42+
it { should_not be_installed }
5943
end
6044
end
6145

62-
context "auditd foreground (warden / Docker systemd ENOSYS)" do
63-
describe file("/etc/systemd/system/auditd.service.d/warden-auditd-foreground.conf") do
64-
it { should be_file }
65-
its(:content) { should include("Type=simple") }
66-
its(:content) { should include("ExecStart=/usr/sbin/auditd -n") }
46+
context "/tmp tmpfs handled (systemd 259)" do
47+
# systemd 259 mounts /tmp as a world-writable tmpfs via the static tmp.mount
48+
# unit. Mask it so /tmp stays a hardened, disk-backed directory.
49+
describe file("/etc/systemd/system/tmp.mount") do
50+
it { should be_linked_to File::NULL }
6751
end
6852
end
6953
end

bosh-stemcell/spec/support/os_image_shared_examples.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@
590590

591591
describe "events that modify system date and time must be recorded (CIS-8.1.4)" do
592592
its(:content) { should match(/^-a always,exit -F arch=b64 -S adjtimex -S settimeofday -k time-change$/) }
593-
its(:content) { should match(/^-a always,exit -F arch=b32 -S adjtimex -S settimeofday -S stime -k time-change$/) }
593+
its(:content) { should match(/^-a always,exit -F arch=b32 -S adjtimex -S settimeofday -k time-change$/) }
594594
its(:content) { should match(/^-a always,exit -F arch=b64 -S clock_settime -k time-change$/) }
595595
its(:content) { should match(/^-a always,exit -F arch=b32 -S clock_settime -k time-change$/) }
596596
its(:content) { should match(/^-w \/etc\/localtime -p wa -k time-change$/) }
@@ -634,8 +634,8 @@
634634
end
635635

636636
describe "record events that modify system network environment (CIS-4.1.6)" do
637-
its(:content) { should match(/^-a exit,always -F arch=b64 -S sethostname -S setdomainname -k system-locale$/) }
638-
its(:content) { should match(/^-a exit,always -F arch=b32 -S sethostname -S setdomainname -k system-locale$/) }
637+
its(:content) { should match(/^-a always,exit -F arch=b64 -S sethostname -S setdomainname -k system-locale$/) }
638+
its(:content) { should match(/^-a always,exit -F arch=b32 -S sethostname -S setdomainname -k system-locale$/) }
639639
its(:content) { should match(/^-w \/etc\/issue -p wa -k system-locale$/) }
640640
its(:content) { should match(/^-w \/etc\/issue\.net -p wa -k system-locale$/) }
641641
its(:content) { should match(/^-w \/etc\/hosts -p wa -k system-locale$/) }

stemcell_builder/stages/base_ubuntu_packages/apply.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ source $base_dir/etc/settings.bash
88

99
# TODO: Decide if we want to include runit (which provides chpst) or break a lot of releases and tell them to use BPM or setpriv
1010
debs="libssl-dev lsof strace bind9-host dnsutils tcpdump iputils-arping \
11-
curl wget bison libreadline6-dev rng-tools \
11+
curl wget bison libreadline6-dev \
1212
libxml2-16 libxml2-dev libxslt1.1 libxslt1-dev zip unzip \
1313
flex psmisc apparmor-utils iptables nftables sysstat \
14-
rsync openssh-server traceroute libncurses5-dev quota \
14+
rsync openssh-server libncurses5-dev quota \
1515
libaio1t64 gdb libcap2-bin libcap2-dev libbz2-dev \
1616
cmake uuid-dev libgcrypt-dev ca-certificates \
1717
htop debhelper parted \

0 commit comments

Comments
 (0)