Skip to content

Commit

Permalink
[antlir2][rootless] rootless receive
Browse files Browse the repository at this point in the history
Summary:
`image.prebuilt` now works (for some use cases) without running under `sudo`.

NOTE: unfortunately receiving btrfs sendstreams only works on devservers, not
yet OnDemand because `btrfs-receive` requires root if receiving somewhere other
than subvolid=5. However, the build appliance (which is the main image that
*must* be received for any image build is now packaged in the `CasDir` format
which suppports rootless receives.

Test Plan:
```name="Rootless image is owned by build user"
❯ buck2 build --show-output fbcode//antlir/antlir2/facebook/images/build_appliance/centos9:build-appliance.cas[rootless]
Buck UI: https://www.internalfb.com/buck2/3e334c5f-f266-49a7-b2aa-9118a256ad31
BUILD SUCCEEDED
fbcode//antlir/antlir2/facebook/images/build_appliance/centos9:build-appliance.cas[rootless] buck-out/v2/gen/fbcode/c270e3f8d8b03bc8/antlir/antlir2/facebook/images/build_appliance/centos9/__build-appliance.cas__/subvol_symlink_rootless

❯ stat buck-out/v2/gen/fbcode/c270e3f8d8b03bc8/antlir/antlir2/facebook/images/build_appliance/centos9/__build-appliance.cas__/subvol_symlink_rootless/
Access: (0755/drwxr-xr-x)  Uid: (115203/  vmagro)   Gid: (  100/   users)
```

```name="Existing image is owned by root"
❯ buck2 build --show-output fbcode//antlir/antlir2/facebook/images/build_appliance/centos9:build-appliance.cas
fbcode//antlir/antlir2/facebook/images/build_appliance/centos9:build-appliance.cas buck-out/v2/gen/fbcode/ca3f1b68f56b9220/antlir/antlir2/facebook/images/build_appliance/centos9/__build-appliance.cas__/subvol_symlink
❯ stat buck-out/v2/gen/fbcode/ca3f1b68f56b9220/antlir/antlir2/facebook/images/build_appliance/centos9/__build-appliance.cas__/subvol_symlink/
Access: (0555/dr-xr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
```

```name="Rootless receive works on OnDemand!!!"
[vmagro@14753.od ~/fbcode (ff6f5e6f4)]$ buck2 build --show-output fbcode//antlir/antlir2/facebook/images/build_appliance/centos9:build-appliance.cas[rootless]
Buck UI: https://www.internalfb.com/buck2/8e0e715c-de12-4adf-8535-d6e6cbaa783f
Network: Up: 0B  Down: 0B
Jobs completed: 4. Time elapsed: 0.1s.
BUILD SUCCEEDED
fbcode//antlir/antlir2/facebook/images/build_appliance/centos9:build-appliance.cas[rootless] buck-out/v2/gen/fbcode/c270e3f8d8b03bc8/antlir/antlir2/facebook/images/build_appliance/centos9/__build-appliance.cas.rootless__/subvol_symlink
[vmagro@14753.od ~/fbcode (ff6f5e6f4)]$ ls ../buck-out/v2/gen/fbcode/c270e3f8d8b03bc8/antlir/antlir2/facebook/images/build_appliance/centos9/__build-appliance.cas.rootless__/subvol_symlink
afs  bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
```

Reviewed By: justintrudell

Differential Revision: D51723387

fbshipit-source-id: 2a309e437e93c6bacd40cec7b8f79a6d7b32d730
  • Loading branch information
vmagro authored and facebook-github-bot committed Dec 14, 2023
1 parent ff8fd11 commit b568c44
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 10 deletions.
2 changes: 2 additions & 0 deletions antlir/antlir2/antlir2_receive/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ rust_binary(
deps = [
"anyhow",
"clap",
"nix",
"tempfile",
"tracing",
"tracing-glog",
"tracing-subscriber",
"//antlir/antlir2/antlir2_btrfs:antlir2_btrfs",
"//antlir/antlir2/antlir2_cas_dir:antlir2_cas_dir",
"//antlir/antlir2/antlir2_rootless:antlir2_rootless",
"//antlir/antlir2/antlir2_working_volume:antlir2_working_volume",
"//antlir/buck/buck_label:buck_label",
],
Expand Down
22 changes: 22 additions & 0 deletions antlir/antlir2/antlir2_receive/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ use anyhow::Result;
use buck_label::Label;
use clap::Parser;
use clap::ValueEnum;
use nix::sched::unshare;
use nix::sched::CloneFlags;
use nix::unistd::Uid;
use tracing::trace;
use tracing_subscriber::prelude::*;

Expand All @@ -38,6 +41,9 @@ pub(crate) struct Receive {
output: PathBuf,
#[clap(flatten)]
setup: SetupArgs,
#[clap(long)]
/// Use an unprivileged usernamespace
rootless: bool,
}

#[derive(Debug, Copy, Clone, ValueEnum)]
Expand Down Expand Up @@ -72,6 +78,22 @@ impl Receive {
trace!("setting up WorkingVolume");
let working_volume = WorkingVolume::ensure(self.setup.working_dir.clone())
.context("while setting up WorkingVolume")?;

let is_real_root = Uid::effective().is_root();

if self.rootless {
// It's actually surprisingly tricky to make the same code paths
// work when both unprivileged and running as real root, so just
// don't allow it.
ensure!(
!is_real_root,
"cannot be real root if --rootless is being used"
);

antlir2_rootless::unshare_new_userns().context("while setting up userns")?;
unshare(CloneFlags::CLONE_NEWNS).context("while unsharing mount")?;
}

let dst = self.prepare_dst(&working_volume)?;

match self.format {
Expand Down
8 changes: 6 additions & 2 deletions antlir/antlir2/bzl/image/depgraph.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@ def build_depgraph(
format: str,
subvol: Artifact | None,
dependency_layers: list[LayerInfo],
identifier_prefix: str = "") -> Artifact:
identifier_prefix: str = "",
rootless: bool = False) -> Artifact:
if rootless:
identifier_prefix += "_rootless_"
output = ctx.actions.declare_output(identifier_prefix + "depgraph." + format + (".pre" if not subvol else ""))
ctx.actions.run(
cmd_args(
# Inspecting already-built images often requires root privileges
"sudo" if subvol else cmd_args(),
"sudo" if (subvol and not rootless) else cmd_args(),
ctx.attrs.antlir2[RunInfo],
"depgraph",
cmd_args(str(ctx.label), format = "--label={}"),
Expand All @@ -31,6 +34,7 @@ def build_depgraph(
cmd_args([li.depgraph for li in dependency_layers], format = "--image-dependency={}"),
cmd_args(subvol, format = "--add-built-items={}") if subvol else cmd_args(),
cmd_args(output.as_output(), format = "--out={}"),
cmd_args("--rootless") if rootless else cmd_args(),
),
category = "antlir2_depgraph",
identifier = identifier_prefix + format + ("/pre" if not subvol else ""),
Expand Down
42 changes: 34 additions & 8 deletions antlir/antlir2/bzl/image/prebuilt.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -38,32 +38,45 @@ def _impl(ctx: AnalysisContext) -> list[Provider]:
# antlir2-receive treats them the same
format = "sendstream"

subvol_symlink = ctx.actions.declare_output("subvol_symlink")
ctx.actions.run(
_make_recv_action = lambda rootless, out: ctx.actions.run(
cmd_args(
"sudo", # this requires privileged btrfs operations
# this usually requires privileged btrfs operations
"sudo" if not rootless else cmd_args(),
ctx.attrs.antlir2_receive[RunInfo],
"--working-dir=antlir2-out",
cmd_args(str(ctx.label), format = "--label={}"),
cmd_args(format, format = "--format={}"),
cmd_args(src, format = "--source={}"),
cmd_args(subvol_symlink.as_output(), format = "--output={}"),
cmd_args(out.as_output(), format = "--output={}"),
cmd_args("--rootless") if rootless else cmd_args(),
),
category = "antlir2_prebuilt_layer",
category = "antlir2_prebuilt_layer" + ("_rootless" if rootless else ""),
# needs local subvolumes
local_only = True,
env = {
"RUST_LOG": "antlir2=trace",
},
)
depgraph_output = build_depgraph(

subvol_symlink = ctx.actions.declare_output("subvol_symlink")
subvol_symlink_rootless = ctx.actions.declare_output("subvol_symlink_rootless")

_make_recv_action(False, subvol_symlink)
_make_recv_action(True, subvol_symlink_rootless)

_make_depgraph = lambda rootless, subvol: build_depgraph(
ctx = ctx,
parent_depgraph = None,
features_json = None,
format = "json",
subvol = subvol_symlink,
subvol = subvol,
dependency_layers = [],
rootless = rootless,
)

depgraph_output = _make_depgraph(False, subvol_symlink)
depgraph_output_rootless = _make_depgraph(True, subvol_symlink_rootless)

if not ctx.attrs.antlir_internal_build_appliance and not ctx.attrs.flavor:
fail("only build appliance images are allowed to be flavorless")
return [
Expand All @@ -73,8 +86,21 @@ def _impl(ctx: AnalysisContext) -> list[Provider]:
subvol_symlink = subvol_symlink,
mounts = [],
flavor = ctx.attrs.flavor,
rootless = False,
),
DefaultInfo(subvol_symlink),
DefaultInfo(subvol_symlink, sub_targets = {
"rootless": [
DefaultInfo(subvol_symlink_rootless),
LayerInfo(
label = ctx.label,
depgraph = depgraph_output_rootless,
subvol_symlink = subvol_symlink_rootless,
mounts = [],
flavor = ctx.attrs.flavor,
rootless = True,
),
],
}),
]

_prebuilt = rule(
Expand Down
1 change: 1 addition & 0 deletions antlir/antlir2/bzl/types.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ LayerInfo = provider(fields = [
"mounts", # List of mount features
"parent", # Dependency for the parent of the layer, if one exists
"subvol_symlink", # symlink pointing to the built subvol
"rootless", # image was built with unprivileged user namespaces
])

0 comments on commit b568c44

Please sign in to comment.