From 6305fc7d30e1f522ffeff38c18eab36aa9fb0c13 Mon Sep 17 00:00:00 2001 From: Alex Huszagh Date: Sat, 4 Jun 2022 11:02:34 -0500 Subject: [PATCH] Limit permissions for Android images. Remove the use of the `--privileged` flag for Android images and instead use an seccomp permissions. The provided profile is derived from the docker documentation, with slight modifications to allow `clone` and `clone3`. The documentation is [docker seccomp](https://docs.docker.com/engine/security/seccomp/#significant-syscalls-blocked-by-the-default-profile), which details the syscalls blocked by docker. The same is true for podman. We merely modified these settings to allow `personality` syscall, which then allows us to use our Android images. --- CHANGELOG.md | 1 + src/cli.rs | 2 +- src/docker.rs | 23 ++++++- src/file.rs | 18 ++++- src/main.rs | 2 +- src/seccomp.json | 166 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 207 insertions(+), 5 deletions(-) create mode 100644 src/seccomp.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a59ae19b..6a08a506e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Changed - #747 - reduced android image sizes. +- #746 - limit image permissions for android images. - #377 - update WINE versions to 7.0. - #734 - patch `arm-unknown-linux-gnueabihf` to build for ARMv6, and add architecture for crosstool-ng-based images. - #709 - Update Emscripten targets to `emcc` version 3.1.10 diff --git a/src/cli.rs b/src/cli.rs index 64f78e47f..ae1ab97ee 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -2,7 +2,7 @@ use std::str::FromStr; use std::{env, path::PathBuf}; use crate::cargo::Subcommand; -use crate::errors::Result; +use crate::errors::*; use crate::rustc::TargetList; use crate::Target; diff --git a/src/docker.rs b/src/docker.rs index bc7c4c36e..e9a4b85f2 100644 --- a/src/docker.rs +++ b/src/docker.rs @@ -1,3 +1,4 @@ +use std::io::Write; use std::path::{Path, PathBuf}; use std::process::{Command, ExitStatus}; use std::{env, fs}; @@ -5,6 +6,7 @@ use std::{env, fs}; use crate::cargo::Root; use crate::errors::*; use crate::extensions::{CommandExt, SafeCommand}; +use crate::file::write_file; use crate::id; use crate::{Config, Target}; use atty::Stream; @@ -14,6 +16,11 @@ const DOCKER_IMAGES: &[&str] = &include!(concat!(env!("OUT_DIR"), "/docker-image const CROSS_IMAGE: &str = "ghcr.io/cross-rs"; const DOCKER: &str = "docker"; const PODMAN: &str = "podman"; +// secured profile based off the docker documentation for denied syscalls: +// https://docs.docker.com/engine/security/seccomp/#significant-syscalls-blocked-by-the-default-profile +// note that we've allow listed `clone` and `clone3`, which is necessary +// to fork the process, and which podman allows by default. +const SECCOMP: &str = include_str!("seccomp.json"); fn get_container_engine() -> Result { if let Ok(ce) = env::var("CROSS_CONTAINER_ENGINE") { @@ -170,8 +177,20 @@ pub fn run( docker.arg("--rm"); - if target.needs_docker_privileged() { - docker.arg("--privileged"); + // docker uses seccomp now on all installations + if target.needs_docker_seccomp() { + let path = env::current_dir() + .wrap_err("couldn't get current directory")? + .canonicalize() + .wrap_err_with(|| "when canonicalizing current_dir".to_string())? + .join("target") + .join(target.triple()) + .join("seccomp.json"); + if !path.exists() { + write_file(&path, false)?.write_all(SECCOMP.as_bytes())?; + } + + docker.args(&["--security-opt", &format!("seccomp={}", path.display())]); } // We need to specify the user for Docker, but not for Podman. diff --git a/src/file.rs b/src/file.rs index 2386cef53..6a2c54f08 100644 --- a/src/file.rs +++ b/src/file.rs @@ -1,4 +1,4 @@ -use std::fs::File; +use std::fs::{self, File}; use std::io::Read; use std::path::Path; @@ -19,3 +19,19 @@ fn read_(path: &Path) -> Result { .wrap_err_with(|| format!("couldn't read {}", path.display()))?; Ok(s) } + +pub fn write_file(path: impl AsRef, overwrite: bool) -> Result { + let path = path.as_ref(); + fs::create_dir_all(&path.parent().ok_or_else(|| { + eyre::eyre!(format!( + "could not find parent directory for `{}`", + path.display() + )) + })?) + .wrap_err_with(|| format!("couldn't create directory `{}`", path.display()))?; + fs::OpenOptions::new() + .write(true) + .create_new(!overwrite) + .open(&path) + .wrap_err(format!("could't write to file `{}`", path.display())) +} diff --git a/src/main.rs b/src/main.rs index 04e24e26d..0d2a237d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -211,7 +211,7 @@ impl Target { !native && (self.is_linux() || self.is_windows() || self.is_bare_metal()) } - fn needs_docker_privileged(&self) -> bool { + fn needs_docker_seccomp(&self) -> bool { let arch_32bit = self.triple().starts_with("arm") || self.triple().starts_with("i586") || self.triple().starts_with("i686"); diff --git a/src/seccomp.json b/src/seccomp.json new file mode 100644 index 000000000..b3991bd5b --- /dev/null +++ b/src/seccomp.json @@ -0,0 +1,166 @@ +{ + "defaultAction": "SCMP_ACT_ALLOW", + "syscalls": [ + { + "names": [ + "add_key", + "get_kernel_syms", + "keyctl", + "move_pages", + "nfsservctl", + "perf_event_open", + "pivot_root", + "query_module", + "request_key", + "sysfs", + "_sysctl", + "uselib", + "userfaultfd", + "ustat" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1 + }, + { + "names": [ + "acct" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1, + "excludes": { + "caps": [ + "CAP_SYS_PACCT" + ] + } + }, + { + "names": [ + "bpf", + "lookup_dcookie", + "mount", + "quotactl", + "quotactl_fd", + "setns", + "swapon", + "swapoff", + "umount", + "umount2", + "unshare", + "vm86", + "vm86old", + "pciconfig_read", + "pciconfig_write", + "salinfo_log_open", + "salinfo_event_open", + "sys_cacheflush", + "rtas" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1, + "excludes": { + "caps": [ + "CAP_SYS_ADMIN" + ] + } + }, + { + "names": [ + "clock_adjtime", + "clock_settime", + "settimeofday", + "stime" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1, + "excludes": { + "caps": [ + "CAP_SYS_TIME" + ] + } + }, + { + "names": [ + "create_module", + "delete_module", + "finit_module", + "init_module" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1, + "excludes": { + "caps": [ + "CAP_SYS_MODULE" + ] + } + }, + { + "names": [ + "get_mempolicy", + "mbind", + "set_mempolicy" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1, + "excludes": { + "caps": [ + "CAP_SYS_NICE" + ] + } + }, + { + "names": [ + "ioperm", + "iopl" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1, + "excludes": { + "caps": [ + "CAP_SYS_RAWIO" + ] + } + }, + { + "names": [ + "kcmp", + "process_vm_readv", + "process_vm_writev", + "ptrace" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1, + "excludes": { + "caps": [ + "CAP_SYS_PTRACE" + ] + } + }, + { + "names": [ + "kexec_file_load", + "kexec_load", + "reboot" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1, + "excludes": { + "caps": [ + "CAP_SYS_BOOT" + ] + } + }, + { + "names": [ + "name_to_handle_at", + "open_by_handle_at" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 1, + "excludes": { + "caps": [ + "CAP_DAC_READ_SEARCH" + ] + } + } + ] +}