Skip to content

Commit 51351b1

Browse files
committed
build-sys: Add tmpfs mount for /tmp, allow lint to see /tmp and /run
In C9S there's something leaking files in `/tmp` so let's just enforce use of tmpfs for `/run` at build time too. But fix `RUN bootc container lint` to *not* have those mounts becuase otherwise we don't actually see the leaked content. Assisted-by: Cursor (Opus 4.5) Signed-off-by: Colin Walters <walters@verbum.org>
1 parent ce39b5d commit 51351b1

3 files changed

Lines changed: 85 additions & 46 deletions

File tree

Dockerfile

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ FROM $base as buildroot
2323
ARG initramfs=1
2424
# This installs our buildroot, and we want to cache it independently of the rest.
2525
# Basically we don't want changing a .rs file to blow out the cache of packages.
26-
RUN --mount=type=tmpfs,target=/run \
26+
RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
2727
--mount=type=bind,from=packaging,src=/,target=/run/packaging \
2828
/run/packaging/install-buildroot
2929
# Now copy the rest of the source
@@ -32,11 +32,11 @@ WORKDIR /src
3232
# See https://www.reddit.com/r/rust/comments/126xeyx/exploring_the_problem_of_faster_cargo_docker/
3333
# We aren't using the full recommendations there, just the simple bits.
3434
# First we download all of our Rust dependencies
35-
RUN --mount=type=tmpfs,target=/run --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothome cargo fetch
35+
RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothome cargo fetch
3636

3737
FROM buildroot as sdboot-content
3838
# Writes to /out
39-
RUN --mount=type=tmpfs,target=/run /src/contrib/packaging/configure-systemdboot download
39+
RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp /src/contrib/packaging/configure-systemdboot download
4040

4141
# We always do a "from scratch" build
4242
# https://docs.fedoraproject.org/en-US/bootc/building-from-scratch/
@@ -49,17 +49,17 @@ RUN --mount=type=tmpfs,target=/run /src/contrib/packaging/configure-systemdboot
4949
FROM $base as target-base
5050
# Handle version skew between base image and mirrors for CentOS Stream
5151
# xref https://gitlab.com/redhat/centos-stream/containers/bootc/-/issues/1174
52-
RUN --mount=type=tmpfs,target=/run \
52+
RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
5353
--mount=type=bind,from=packaging,src=/,target=/run/packaging \
5454
/run/packaging/enable-compose-repos
55-
RUN --mount=type=tmpfs,target=/run /usr/libexec/bootc-base-imagectl build-rootfs --manifest=standard /target-rootfs
55+
RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp /usr/libexec/bootc-base-imagectl build-rootfs --manifest=standard /target-rootfs
5656

5757
FROM scratch as base
5858
COPY --from=target-base /target-rootfs/ /
5959
# SKIP_CONFIGS=1 skips LBIs, test kargs, and install configs (for FCOS testing)
6060
ARG SKIP_CONFIGS
61-
# Use tmpfs,target=/run with bind mounts inside to avoid leaking mount stubs into the image
62-
RUN --mount=type=tmpfs,target=/run \
61+
# Use tmpfs for /run and /tmp with bind mounts inside to avoid leaking mount stubs into the image
62+
RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
6363
--mount=type=bind,from=src,src=/src/hack,target=/run/hack \
6464
cd /run/hack/ && SKIP_CONFIGS="${SKIP_CONFIGS}" ./provision-derived.sh
6565
# Note we don't do any customization here yet
@@ -88,12 +88,12 @@ ARG pkgversion
8888
ARG SOURCE_DATE_EPOCH
8989
ENV SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH}
9090
# Build RPM directly from source, using cached target directory
91-
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothome RPM_VERSION="${pkgversion}" /src/contrib/packaging/build-rpm
91+
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothome RPM_VERSION="${pkgversion}" /src/contrib/packaging/build-rpm
9292

9393
FROM buildroot as sdboot-signed
9494
# The secureboot key and cert are passed via Justfile
9595
# We write the signed binary into /out
96-
RUN --network=none --mount=type=tmpfs,target=/run \
96+
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
9797
--mount=type=bind,from=sdboot-content,src=/,target=/run/sdboot-package \
9898
--mount=type=secret,id=secureboot_key \
9999
--mount=type=secret,id=secureboot_cert \
@@ -104,22 +104,22 @@ FROM build as units
104104
# A place that we're more likely to be able to set xattrs
105105
VOLUME /var/tmp
106106
ENV TMPDIR=/var/tmp
107-
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothome make install-unit-tests
107+
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothome make install-unit-tests
108108

109109
# This just does syntax checking
110110
FROM buildroot as validate
111-
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothome make validate
111+
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp --mount=type=cache,target=/src/target --mount=type=cache,target=/var/roothome make validate
112112

113113
# Common base for final images: configures variant, rootfs, and injects extra content
114114
FROM base as final-common
115115
ARG variant
116-
RUN --network=none --mount=type=tmpfs,target=/run \
116+
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
117117
--mount=type=bind,from=packaging,src=/,target=/run/packaging \
118118
--mount=type=bind,from=sdboot-content,src=/,target=/run/sdboot-content \
119119
--mount=type=bind,from=sdboot-signed,src=/,target=/run/sdboot-signed \
120120
/run/packaging/configure-variant "${variant}"
121121
ARG rootfs=""
122-
RUN --network=none --mount=type=tmpfs,target=/run \
122+
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
123123
--mount=type=bind,from=packaging,src=/,target=/run/packaging \
124124
/run/packaging/configure-rootfs "${variant}" "${rootfs}"
125125
COPY --from=packaging /usr-extras/ /usr/
@@ -128,10 +128,13 @@ COPY --from=packaging /usr-extras/ /usr/
128128
# Use with: podman build --target=final --build-context packages=path/to/packages
129129
# We use --build-context instead of -v to avoid volume mount stubs leaking into /run.
130130
FROM final-common as final
131-
RUN --network=none --mount=type=tmpfs,target=/run \
131+
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
132132
--mount=type=bind,from=packaging,src=/,target=/run/packaging \
133133
--mount=type=bind,from=packages,src=/,target=/run/packages \
134134
/run/packaging/install-rpm-and-setup /run/packages
135-
# Use tmpfs on /run to hide any content created by podman for DNS resolution
136-
# (e.g., /run/systemd/resolve/stub-resolv.conf on Ubuntu hosts)
137-
RUN --network=none --mount=type=tmpfs,target=/run bootc container lint --fatal-warnings
135+
RUN --network=none <<EORUN
136+
set -xeuo pipefail
137+
# workaround for https://github.com/containers/buildah/pull/6233
138+
rm -vrf /run/systemd
139+
bootc container lint --fatal-warnings
140+
EORUN

Dockerfile.cfsuki

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ ARG base=localhost/bootc
33
FROM $base AS base
44

55
FROM base as kernel
6-
# Use tmpfs on /run to prevent podman's DNS resolver files from being committed
7-
RUN --mount=type=tmpfs,target=/run <<EORUN
6+
# Use tmpfs on /run and /tmp to prevent podman's DNS resolver files from being committed
7+
RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp <<EORUN
88
set -xeuo pipefail
99
. /usr/lib/os-release
1010
case $ID in
@@ -18,7 +18,7 @@ dnf -y install systemd-ukify sbsigntools
1818
EORUN
1919
# Must be passed
2020
ARG COMPOSEFS_FSVERITY
21-
RUN --network=none --mount=type=tmpfs,target=/run \
21+
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
2222
--mount=type=secret,id=secureboot_key \
2323
--mount=type=secret,id=secureboot_cert \
2424
--mount=type=bind,from=base,src=/,target=/target \
@@ -47,7 +47,7 @@ RUN --network=none --mount=type=tmpfs,target=/run \
4747
EOF
4848

4949
FROM base as final
50-
RUN --network=none --mount=type=tmpfs,target=/run \
50+
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp \
5151
--mount=type=bind,from=kernel,src=/,target=/run/kernel <<EOF
5252
set -xeuo pipefail
5353
kver=$(cd /usr/lib/modules && echo *)
@@ -61,8 +61,7 @@ rm -v /usr/lib/modules/${kver}/{vmlinuz,initramfs.img}
6161
# Symlink into the /usr/lib/modules location
6262
ln -sr $target /usr/lib/modules/${kver}/$(basename $kver.efi)
6363
EOF
64-
# Use tmpfs on /run to ensure lint sees empty /run (hiding any podman-created content)
65-
RUN --network=none --mount=type=tmpfs,target=/run bootc container lint --fatal-warnings
64+
RUN --network=none bootc container lint --fatal-warnings
6665

6766
FROM base as final-final
6867
COPY --from=final /boot /boot

crates/xtask/src/buildsys.rs

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const DOCKERFILE_NETWORK_CUTOFF: &str = "external dependency cutoff point";
1313
///
1414
/// - Reproducible builds for the RPM
1515
/// - Dockerfile network isolation after cutoff point
16-
/// - Dockerfile tmpfs on /run for all RUN instructions
16+
/// - Dockerfile tmpfs on /run and /tmp for all RUN instructions
1717
#[context("Checking build system")]
1818
pub fn check_buildsys(sh: &Shell, dockerfile_path: &Utf8Path) -> Result<()> {
1919
check_package_reproducibility(sh)?;
@@ -75,11 +75,14 @@ fn check_dockerfile_rules(dockerfile_path: &Utf8Path) -> Result<()> {
7575
}
7676

7777
const RUN_NETWORK_NONE: &str = "RUN --network=none";
78-
const RUN_TMPFS: &str = "--mount=type=tmpfs,target=/run";
78+
const RUN_TMPFS_RUN: &str = "--mount=type=tmpfs,target=/run";
79+
const RUN_TMPFS_TMP: &str = "--mount=type=tmpfs,target=/tmp";
7980

8081
/// Verify Dockerfile rules:
81-
/// - All RUN instructions must include `--mount=type=tmpfs,target=/run` to prevent
82-
/// podman's DNS resolver files from leaking into the image
82+
/// - All RUN instructions must include `--mount=type=tmpfs,target=/run` and
83+
/// `--mount=type=tmpfs,target=/tmp` to prevent podman's DNS resolver files
84+
/// and temporary files from leaking into the image (except `bootc container lint`
85+
/// which doesn't create files)
8386
/// - After the network cutoff, all RUN instructions must start with `--network=none`
8487
///
8588
/// Returns Ok(()) if all RUN instructions comply, or an error listing violations.
@@ -103,12 +106,25 @@ pub fn verify_dockerfile_rules(dockerfile: &str) -> Result<()> {
103106

104107
// Check if this is a RUN instruction
105108
if trimmed.starts_with("RUN ") {
106-
// All RUN instructions must include tmpfs mount on /run
107-
if !trimmed.contains(RUN_TMPFS) {
108-
errors.push(format!(
109-
" line {}: RUN instruction must include `{}`",
110-
line_num, RUN_TMPFS
111-
));
109+
// bootc container lint doesn't create files, so it doesn't need tmpfs protection
110+
let is_lint = trimmed.contains("bootc container lint");
111+
112+
if !is_lint {
113+
// All RUN instructions must include tmpfs mount on /run
114+
if !trimmed.contains(RUN_TMPFS_RUN) {
115+
errors.push(format!(
116+
" line {}: RUN instruction must include `{}`",
117+
line_num, RUN_TMPFS_RUN
118+
));
119+
}
120+
121+
// All RUN instructions must include tmpfs mount on /tmp
122+
if !trimmed.contains(RUN_TMPFS_TMP) {
123+
errors.push(format!(
124+
" line {}: RUN instruction must include `{}`",
125+
line_num, RUN_TMPFS_TMP
126+
));
127+
}
112128
}
113129

114130
// After cutoff, must start with exactly "RUN --network=none"
@@ -139,35 +155,56 @@ mod tests {
139155
fn test_dockerfile_rules_valid() {
140156
let dockerfile = r#"
141157
FROM base
142-
RUN --mount=type=tmpfs,target=/run echo "before cutoff, network allowed"
158+
RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp echo "before cutoff, network allowed"
143159
# external dependency cutoff point
144-
RUN --network=none --mount=type=tmpfs,target=/run echo "good"
145-
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=bind,from=foo,target=/bar some-command
160+
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp echo "good"
161+
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp --mount=type=bind,from=foo,target=/bar some-command
162+
RUN --network=none bootc container lint --fatal-warnings
146163
"#;
147164
verify_dockerfile_rules(dockerfile).unwrap();
148165
}
149166

150167
#[test]
151-
fn test_dockerfile_rules_missing_tmpfs_before_cutoff() {
168+
fn test_dockerfile_rules_missing_tmpfs_run_before_cutoff() {
152169
let dockerfile = r#"
153170
FROM base
154-
RUN echo "bad - missing tmpfs"
171+
RUN --mount=type=tmpfs,target=/tmp echo "bad - missing /run tmpfs"
155172
# external dependency cutoff point
156-
RUN --network=none --mount=type=tmpfs,target=/run echo "good"
173+
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp echo "good"
157174
"#;
158175
let err = verify_dockerfile_rules(dockerfile).unwrap_err();
159176
let msg = err.to_string();
160177
assert!(msg.contains("line 3"), "error should mention line 3: {msg}");
161-
assert!(msg.contains("tmpfs"), "error should mention tmpfs: {msg}");
178+
assert!(
179+
msg.contains("target=/run"),
180+
"error should mention target=/run: {msg}"
181+
);
182+
}
183+
184+
#[test]
185+
fn test_dockerfile_rules_missing_tmpfs_tmp_before_cutoff() {
186+
let dockerfile = r#"
187+
FROM base
188+
RUN --mount=type=tmpfs,target=/run echo "bad - missing /tmp tmpfs"
189+
# external dependency cutoff point
190+
RUN --network=none --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp echo "good"
191+
"#;
192+
let err = verify_dockerfile_rules(dockerfile).unwrap_err();
193+
let msg = err.to_string();
194+
assert!(msg.contains("line 3"), "error should mention line 3: {msg}");
195+
assert!(
196+
msg.contains("target=/tmp"),
197+
"error should mention target=/tmp: {msg}"
198+
);
162199
}
163200

164201
#[test]
165202
fn test_dockerfile_rules_missing_network_flag_after_cutoff() {
166203
let dockerfile = r#"
167204
FROM base
168-
RUN --mount=type=tmpfs,target=/run echo "before cutoff"
205+
RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp echo "before cutoff"
169206
# external dependency cutoff point
170-
RUN --mount=type=tmpfs,target=/run echo "bad - missing network flag"
207+
RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp echo "bad - missing network flag"
171208
"#;
172209
let err = verify_dockerfile_rules(dockerfile).unwrap_err();
173210
let msg = err.to_string();
@@ -182,9 +219,9 @@ RUN --mount=type=tmpfs,target=/run echo "bad - missing network flag"
182219
fn test_dockerfile_rules_missing_tmpfs_after_cutoff() {
183220
let dockerfile = r#"
184221
FROM base
185-
RUN --mount=type=tmpfs,target=/run echo "before cutoff"
222+
RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp echo "before cutoff"
186223
# external dependency cutoff point
187-
RUN --network=none echo "bad - missing tmpfs"
224+
RUN --network=none echo "bad - missing both tmpfs"
188225
"#;
189226
let err = verify_dockerfile_rules(dockerfile).unwrap_err();
190227
let msg = err.to_string();
@@ -197,9 +234,9 @@ RUN --network=none echo "bad - missing tmpfs"
197234
// --network=none must come immediately after RUN
198235
let dockerfile = r#"
199236
FROM base
200-
RUN --mount=type=tmpfs,target=/run echo "before cutoff"
237+
RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp echo "before cutoff"
201238
# external dependency cutoff point
202-
RUN --mount=type=tmpfs,target=/run --network=none echo "bad - network flag not first"
239+
RUN --mount=type=tmpfs,target=/run --mount=type=tmpfs,target=/tmp --network=none echo "bad - network flag not first"
203240
"#;
204241
let err = verify_dockerfile_rules(dockerfile).unwrap_err();
205242
let msg = err.to_string();

0 commit comments

Comments
 (0)