From 2d2b5d31d6dd91231a7cf8849119c95b74b0b101 Mon Sep 17 00:00:00 2001 From: Alex Huszagh Date: Thu, 2 Jun 2022 20:15:16 -0500 Subject: [PATCH 1/2] Add `thumbv7neon-*` targets. Add the `thumbv7neon-linux-androideabi` and `thumbv7neon-unknown-linux-gnueabihf` targets. Closes #254. --- .github/workflows/ci.yml | 2 + CHANGELOG.md | 1 + .../Dockerfile.thumbv7neon-linux-androideabi | 55 +++++++++++++++++++ ...erfile.thumbv7neon-unknown-linux-gnueabihf | 47 ++++++++++++++++ src/lib.rs | 1 + 5 files changed, 106 insertions(+) create mode 100644 docker/Dockerfile.thumbv7neon-linux-androideabi create mode 100644 docker/Dockerfile.thumbv7neon-unknown-linux-gnueabihf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 60ddecb43..f049d76d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -139,6 +139,7 @@ jobs: - { target: arm-unknown-linux-gnueabihf, os: ubuntu-latest, cpp: 1, dylib: 1, std: 1, run: 1 } - { target: armv7-unknown-linux-gnueabi, os: ubuntu-latest, cpp: 1, dylib: 1, std: 1, run: 1, runners: qemu-user qemu-system } - { target: armv7-unknown-linux-gnueabihf, os: ubuntu-latest, cpp: 1, dylib: 1, std: 1, run: 1, runners: qemu-user qemu-system } + - { target: thumbv7neon-unknown-linux-gnueabihf, os: ubuntu-latest, cpp: 1, dylib: 1, std: 1, run: 1, runners: qemu-user qemu-system } - { target: i586-unknown-linux-gnu, os: ubuntu-latest, cpp: 1, dylib: 1, std: 1, run: 1 } - { target: i686-unknown-linux-gnu, os: ubuntu-latest, cpp: 1, dylib: 1, std: 1, run: 1, runners: native qemu-user qemu-system } - { target: mips-unknown-linux-gnu, os: ubuntu-latest, cpp: 1, dylib: 1, std: 1, run: 1, runners: qemu-user qemu-system } @@ -167,6 +168,7 @@ jobs: - { target: aarch64-linux-android, os: ubuntu-latest, cpp: 1, std: 1, run: 1, runners: qemu-user } - { target: arm-linux-androideabi, os: ubuntu-latest, cpp: 1, std: 1, run: 1, runners: qemu-user } - { target: armv7-linux-androideabi, os: ubuntu-latest, cpp: 1, std: 1, run: 1, runners: qemu-user } + - { target: thumbv7neon-linux-androideabi, os: ubuntu-latest, cpp: 1, std: 1, run: 1, runners: qemu-user } - { target: i686-linux-android, os: ubuntu-latest, cpp: 1, std: 1, run: 1, runners: qemu-user } - { target: x86_64-linux-android, os: ubuntu-latest, cpp: 1, std: 1, run: 1, runners: qemu-user } - { target: x86_64-pc-windows-gnu, os: ubuntu-latest, cpp: 1, std: 1, run: 1 } diff --git a/CHANGELOG.md b/CHANGELOG.md index f0c54515c..bad783947 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added - #767 - added the `cross-util` and `cross-dev` commands. +- #745 - added `thumbv7neon-*` targets. - #741 - added `armv7-unknown-linux-gnueabi` and `armv7-unknown-linux-musleabi` targets. - #721 - add support for running doctests on nightly if `CROSS_UNSTABLE_ENABLE_DOCTESTS=true`. - #719 - add `--list` to known subcommands. diff --git a/docker/Dockerfile.thumbv7neon-linux-androideabi b/docker/Dockerfile.thumbv7neon-linux-androideabi new file mode 100644 index 000000000..203392d52 --- /dev/null +++ b/docker/Dockerfile.thumbv7neon-linux-androideabi @@ -0,0 +1,55 @@ +FROM ubuntu:20.04 +ARG DEBIAN_FRONTEND=noninteractive + +COPY common.sh lib.sh / +RUN /common.sh + +COPY cmake.sh / +RUN /cmake.sh + +COPY xargo.sh / +RUN /xargo.sh + +COPY android-ndk.sh / +RUN /android-ndk.sh arm 28 +ENV PATH=$PATH:/android-ndk/bin + +COPY android-system.sh / +RUN /android-system.sh arm + +COPY qemu.sh / +RUN /qemu.sh arm + +RUN cp /android-ndk/sysroot/usr/lib/arm-linux-androideabi/28/libz.so /system/lib/ + +COPY android-runner / + +# Libz is distributed in the android ndk, but for some unknown reason it is not +# found in the build process of some crates, so we explicit set the DEP_Z_ROOT +# likewise, the toolchains expect the prefix `thumbv7neon-linux-androideabi`, +# which we don't have, so just export every possible variable, such as AR. +# Also export all target binutils just in case required. +ENV CARGO_TARGET_THUMBV7NEON_LINUX_ANDROIDEABI_LINKER=arm-linux-androideabi-gcc \ + CARGO_TARGET_THUMBV7NEON_LINUX_ANDROIDEABI_RUNNER="/android-runner arm" \ + AR_thumbv7neon_linux_androideabi=arm-linux-androideabi-ar \ + AS_thumbv7neon_linux_androideabi=arm-linux-androideabi-as \ + CC_thumbv7neon_linux_androideabi=arm-linux-androideabi-gcc \ + CXX_thumbv7neon_linux_androideabi=arm-linux-androideabi-g++ \ + LD_thumbv7neon_linux_androideabi=arm-linux-androideabi-ld \ + NM_thumbv7neon_linux_androideabi=arm-linux-androideabi-nm \ + OBJCOPY_thumbv7neon_linux_androideabi=arm-linux-androideabi-objcopy \ + OBJDUMP_thumbv7neon_linux_androideabi=arm-linux-androideabi-objdump \ + RANLIB_thumbv7neon_linux_androideabi=arm-linux-androideabi-ranlib \ + READELF_thumbv7neon_linux_androideabi=arm-linux-androideabi-readelf \ + SIZE_thumbv7neon_linux_androideabi=arm-linux-androideabi-size \ + STRINGS_thumbv7neon_linux_androideabi=arm-linux-androideabi-strings \ + STRIP_thumbv7neon_linux_androideabi=arm-linux-androideabi-strip \ + BINDGEN_EXTRA_CLANG_ARGS_thumbv7neon_linux_androideabi="--sysroot=/android-ndk/sysroot" \ + DEP_Z_INCLUDE=/android-ndk/sysroot/usr/include/ \ + RUST_TEST_THREADS=1 \ + HOME=/tmp/ \ + TMPDIR=/tmp/ \ + ANDROID_DATA=/ \ + ANDROID_DNS_MODE=local \ + ANDROID_ROOT=/system + diff --git a/docker/Dockerfile.thumbv7neon-unknown-linux-gnueabihf b/docker/Dockerfile.thumbv7neon-unknown-linux-gnueabihf new file mode 100644 index 000000000..7468bdac4 --- /dev/null +++ b/docker/Dockerfile.thumbv7neon-unknown-linux-gnueabihf @@ -0,0 +1,47 @@ +FROM ubuntu:16.04 +ARG DEBIAN_FRONTEND=noninteractive + +COPY common.sh lib.sh / +RUN /common.sh + +COPY cmake.sh / +RUN /cmake.sh + +COPY xargo.sh / +RUN /xargo.sh + +RUN apt-get update && apt-get install --assume-yes --no-install-recommends \ + g++-arm-linux-gnueabihf \ + libc6-dev-armhf-cross + +COPY qemu.sh / +RUN /qemu.sh arm softmmu + +COPY dropbear.sh / +RUN /dropbear.sh + +COPY linux-image.sh / +RUN /linux-image.sh armv7 + +COPY linux-runner / + +# Export all target binutils just in case required. +ENV CARGO_TARGET_THUMBV7NEON_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \ + CARGO_TARGET_THUMBV7NEON_UNKNOWN_LINUX_GNUEABIHF_RUNNER="/linux-runner armv7" \ + AR_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-ar \ + AS_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-as \ + CC_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc \ + CXX_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++ \ + LD_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-ld \ + NM_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-nm \ + OBJCOPY_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-objcopy \ + OBJDUMP_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-objdump \ + RANLIB_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-ranlib \ + READELF_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-readelf \ + SIZE_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-size \ + STRINGS_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-strings \ + STRIP_thumbv7neon_unknown_linux_gnueabihf=arm-linux-gnueabihf-strip \ + BINDGEN_EXTRA_CLANG_ARGS_thumbv7neon_unknown_linux_gnueabihf="--sysroot=/usr/arm-linux-gnueabihf" \ + QEMU_LD_PREFIX=/usr/arm-linux-gnueabihf \ + RUST_TEST_THREADS=1 \ + PKG_CONFIG_PATH="/usr/lib/arm-linux-gnueabihf/pkgconfig/:${PKG_CONFIG_PATH}" diff --git a/src/lib.rs b/src/lib.rs index 367e32750..5ab9ffe8b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -230,6 +230,7 @@ impl Target { fn needs_docker_privileged(&self) -> bool { let arch_32bit = self.triple().starts_with("arm") + || self.triple().starts_with("thumb") || self.triple().starts_with("i586") || self.triple().starts_with("i686"); From 13f449723f853c79bc3651dfcb5d7649958af319 Mon Sep 17 00:00:00 2001 From: Alex Huszagh Date: Wed, 8 Jun 2022 12:02:39 -0500 Subject: [PATCH 2/2] 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. On Windows with Docker Desktop, we currently have an issue where Docker tries to read the seccomp profile, and then interpret that as the path, rather than load the profile from the path, which is tracked by the following issue: https://github.com/docker/for-win/issues/12760 On Podman (not inside WSL2), we have a separate issue where it expects a WSL path to be provided for the seccomp profile, despite the path being provided for the host. --- CHANGELOG.md | 1 + src/docker.rs | 57 ++++++++++++++-- src/file.rs | 17 ++++- src/lib.rs | 2 +- src/seccomp.json | 166 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 235 insertions(+), 8 deletions(-) create mode 100644 src/seccomp.json diff --git a/CHANGELOG.md b/CHANGELOG.md index f0c54515c..d0f4845cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - #762 - re-enabled `x86_64-unknown-dragonfly` target. - #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/docker.rs b/src/docker.rs index cc9011898..430e8731b 100644 --- a/src/docker.rs +++ b/src/docker.rs @@ -1,9 +1,11 @@ +use std::io::Write; use std::path::{Path, PathBuf}; use std::process::{Command, ExitStatus}; use std::{env, fs}; use crate::cargo::CargoMetadata; use crate::extensions::{CommandExt, SafeCommand}; +use crate::file::write_file; use crate::id; use crate::{errors::*, file}; use crate::{Config, Target}; @@ -14,16 +16,34 @@ 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"); + +#[derive(Debug, PartialEq, Eq)] +enum EngineType { + Docker, + Podman, + Other, +} // determine if the container engine is docker. this fixes issues with // any aliases (#530), and doesn't fail if an executable suffix exists. -fn get_is_docker(ce: std::path::PathBuf, verbose: bool) -> Result { +fn get_engine_type(ce: std::path::PathBuf, verbose: bool) -> Result { let stdout = Command::new(ce) .arg("--help") .run_and_get_stdout(verbose)? .to_lowercase(); - Ok(stdout.contains("docker") && !stdout.contains("emulate")) + if stdout.contains("podman") { + Ok(EngineType::Podman) + } else if stdout.contains("docker") && !stdout.contains("emulate") { + Ok(EngineType::Docker) + } else { + Ok(EngineType::Other) + } } pub fn get_container_engine() -> Result { @@ -172,7 +192,7 @@ pub fn run( let runner = config.runner(target)?; let mut docker = docker_command("run")?; - let is_docker = get_is_docker(get_container_engine().unwrap(), verbose)?; + let engine_type = get_engine_type(get_container_engine().unwrap(), verbose)?; for ref var in config.env_passthrough(target)? { validate_env_var(var)?; @@ -233,12 +253,37 @@ 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 seccomp = if engine_type == EngineType::Docker && cfg!(target_os = "windows") { + // docker on windows fails due to a bug in reading the profile + // https://github.com/docker/for-win/issues/12760 + "unconfined".to_string() + } else { + #[allow(unused_mut)] // target_os = "windows" + let mut 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())?; + } + #[cfg(target_os = "windows")] + if engine_type == EngineType::Podman { + // podman weirdly expects a WSL path here, and fails otherwise + path = wslpath(&path, verbose)?; + } + path.display().to_string() + }; + + docker.args(&["--security-opt", &format!("seccomp={}", seccomp)]); } // We need to specify the user for Docker, but not for Podman. - if is_docker { + if engine_type == EngineType::Docker { docker.args(&[ "--user", &format!( diff --git a/src/file.rs b/src/file.rs index f8a0181ef..55468df81 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, PathBuf}; @@ -35,3 +35,18 @@ fn _canonicalize(path: &Path) -> Result { Path::new(&path).canonicalize().map_err(Into::into) } } + +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!("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/lib.rs b/src/lib.rs index 367e32750..38f309181 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -228,7 +228,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" + ] + } + } + ] +}