Skip to content

Commit f9ef4ec

Browse files
committed
Add workaround for Ubuntu 23.10
Ubuntu 23.10 is slowly choking off our access to unprivileged user namespaces. In particular, they have made it so that `unconfined` isn't actually unconfined, and is unable to create unprivileged user namespaces. This is super frustrating, because there's no way to launch a process using unpriv userns without needing superuser intervention (e.g. to create an apparmor profile). Furthermore, it's annoying to even install one, as the path to our executable is unreliable, since it's in a content-addressed location. And so, I regret more and more every day that I don't just give up and reimplement everything on top of podman. Until then, just ask the user to turn off these annoying protections:
1 parent 3a1ddf5 commit f9ef4ec

File tree

3 files changed

+60
-5
lines changed

3 files changed

+60
-5
lines changed

src/Sandbox.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ for f in (:run, :success)
120120
stdin=open_or_devnull(config.stdin),
121121
stdout=temp_stdout,
122122
stderr=temp_stderr,
123-
)
123+
)
124124
if config.verbose
125125
@info("Running sandboxed command", user_cmd.exec)
126126
end

src/UserNamespaces.jl

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,13 @@ function executor_available(::Type{T}; verbose::Bool=false) where {T <: UserName
6868
return false
6969
end
7070
return with_executor(T) do exe
71-
return check_kernel_version(;verbose) &&
71+
ret = check_kernel_version(;verbose) &&
7272
check_overlayfs_loaded(;verbose) &&
7373
probe_executor(exe; verbose)
74+
if T == UnprivilegedUserNamespacesExecutor && !ret
75+
check_restricted_unprivileged_userns()
76+
end
77+
return ret
7478
end
7579
end
7680

@@ -249,3 +253,51 @@ function build_executor_command(exe::UserNamespacesExecutor, config::SandboxConf
249253

250254
return sandbox_cmd
251255
end
256+
257+
_userns_sysctl_props = ("apparmor_restrict_unprivileged_userns", "apparmor_restrict_unprivileged_unconfined")
258+
function check_restricted_unprivileged_userns()
259+
if !Sys.islinux()
260+
return
261+
end
262+
263+
# Ubuntu 23.10 is slowly choking off our access to unprivileged user namespaces.
264+
# In particular, they have made it so that `unconfined` isn't actually unconfined,
265+
# and is unable to create unprivileged user namespaces. This is super frustrating,
266+
# because there's no way to launch a process using unpriv userns without needing
267+
# superuser intervention (e.g. to create an apparmor profile). Furthermore, it's
268+
# annoying to even install one, as the path to our executable is unreliable, since
269+
# it's in a content-addressed location.
270+
#
271+
# And so, I regret more and more every day that I don't just give up and reimplement
272+
# everything on top of podman. Until then, just ask the user to turn off these
273+
# annoying protections:
274+
if Sys.which("sysctl") !== nothing
275+
failed_props = String[]
276+
for prop in _userns_sysctl_props
277+
if readchomp(`sysctl kernel.$(prop)`) == "kernel.$(prop) = 1"
278+
push!(failed_props, prop)
279+
end
280+
end
281+
if !isempty(failed_props)
282+
@error("""
283+
You have likely run into an issue due to over-zealous apparmor protections:
284+
https://ubuntu.com/blog/ubuntu-23-10-restricted-unprivileged-user-namespaces
285+
286+
To disable these protections, run: `Sandbox.disable_apparmor_userns_restrictions()`
287+
This will require your sudo password.
288+
""")
289+
end
290+
end
291+
end
292+
293+
function disable_apparmor_userns_restrictions()
294+
sysctl_commands = IOBuffer()
295+
for prop in _userns_sysctl_props
296+
println(sysctl_commands, "kernel.$(prop) = 0")
297+
end
298+
seekstart(sysctl_commands)
299+
run(pipeline(`sudo tee /etc/sysctl.d/99-sandbox-unprivileged-user-namespaces.conf`, stdin=sysctl_commands, stdout=devnull))
300+
for prop in _userns_sysctl_props
301+
run(`sudo sysctl -w kernel.$(prop)=0`)
302+
end
303+
end

src/utils.jl

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@ function max_directory_ctime(prefix::String)
1515
end
1616

1717
function get_mounts(;verbose::Bool = false)
18+
mounts_file = "/proc/self/mounts"
1819
# Get a listing of the current mounts. If we can't do this, just give up
19-
if !isfile("/proc/mounts")
20+
if !isfile(mounts_file)
2021
if verbose
21-
@info("Couldn't open /proc/mounts, returning...")
22+
@info("Couldn't open $(mounts_file), returning...")
2223
end
2324
return Tuple{String,SubString{String}}[]
2425
end
25-
mounts = String(read("/proc/mounts"))
26+
# We use `cat` here because for some reason, Julia v1.11 is truncating the file if we read directly.
27+
mounts = String(read(`cat $(mounts_file)`))
2628

2729
# Grab the fstype and the mountpoints
2830
mounts = [split(m)[2:3] for m in split(mounts, "\n") if !isempty(m)]
@@ -376,5 +378,6 @@ function find_persist_dir_root(rootfs_path::String, dir_hints::Vector{String} =
376378
end
377379

378380
# Not able to find a SINGLE persistent directory location that works!
381+
check_restricted_unprivileged_userns()
379382
return (nothing, false)
380383
end

0 commit comments

Comments
 (0)