netblue30 / firejail

Linux namespaces and seccomp-bpf sandbox
https://firejail.wordpress.com
GNU General Public License v2.0
5.75k stars 561 forks source link

Adding binaries outside standard "bin dirs" ala private-bin? #3598

Closed curiositycasualty closed 4 years ago

curiositycasualty commented 4 years ago

I've been tasked with sandboxing R (on Ubuntu 20.04). I've already got a trustworthy AppArmor profile that seems to fit over-top of firejail correctly; except AppArmor doesn't offer much in the way of network-sandboxing and my sandboxed R ultimately needs to be able to talk to a webserver on localhost over ports 80 and 443. So firejail's networking capability and /etc/firejail/webserver.net piqued my interest. The webserver's R integration is either through an R package/client (needing to talk to localhost on ports 80 and 443), And/or writing an R script and input files to disk, executing the R script, and then reading the output file generated by executing the R script. The firejail sandboxing needs to support both methods.

However, I think I'm running into a devilishly devised snare caused by a combination of R's overlapping binary names and firejail's generous private-* search behaviour.

R essentially has a single, binary interpreter: /usr/lib/R/bin/exec/R but wraps it in a handful of shell scripts, some of which are also named R (like: /usr/bin/R and /usr/lib/R/bin/R):

$ for path in /usr/{bin/R,lib/R/bin/{R,exec/R}}; do md5sum $path; file $path; done
85dbed86ecb6e7eb4d1e6c723791a8c8  /usr/bin/R
/usr/bin/R: Bourne-Again shell script, ASCII text executable
b04165152acad17d8cbbee9a42d5247b  /usr/lib/R/bin/R
/usr/lib/R/bin/R: Bourne-Again shell script, ASCII text executable
2fadb0933787a5c06ea6d9bbeb4dd837  /usr/lib/R/bin/exec/R
/usr/lib/R/bin/exec/R: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=4b219bb915d9f3306be0b183bf78e52c3b50a5d8, for GNU/Linux 3.2.0, stripped

For the moment, I'm willfully ignoring the libraries potentially compiled/installed by installed R packages, which generally wind up in a lib dir under the package's own dir within /usr/local/lib/R/site-library:

$ sudo find /usr/local/lib/R/site-library/ -name '*.so*'
/usr/local/lib/R/site-library/digest/libs/digest.so
/usr/local/lib/R/site-library/htmltools/libs/htmltools.so
/usr/local/lib/R/site-library/markdown/libs/markdown.so
/usr/local/lib/R/site-library/jsonlite/libs/jsonlite.so
/usr/local/lib/R/site-library/rlang/libs/rlang.so
/usr/local/lib/R/site-library/base64enc/libs/base64enc.so
/usr/local/lib/R/site-library/yaml/libs/yaml.so
/usr/local/lib/R/site-library/xfun/libs/xfun.so
/usr/local/lib/R/site-library/glue/libs/glue.so
/usr/local/lib/R/site-library/mime/libs/mime.so
/usr/local/lib/R/site-library/stringi/libs/stringi.so

Where I'm stuck is trying to construct a profile with a private-bin and a private-lib that accommodates /usr/lib/R/bin/exec/R's runtime libraries:

$ R_HOME=/usr/lib/R ldd /usr/lib/R/bin/exec/R
    linux-vdso.so.1 (0x00007fff11b59000)
    libR.so => /lib/libR.so (0x00007fc793f9f000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc793dad000)
    libblas.so.3 => /lib/x86_64-linux-gnu/libblas.so.3 (0x00007fc7923ae000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fc79225f000)
    libreadline.so.8 => /lib/x86_64-linux-gnu/libreadline.so.8 (0x00007fc79220f000)
    libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007fc79219c000)
    liblzma.so.5 => /lib/x86_64-linux-gnu/liblzma.so.5 (0x00007fc792173000)
    libbz2.so.1.0 => /lib/x86_64-linux-gnu/libbz2.so.1.0 (0x00007fc792160000)
    libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007fc792144000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fc79213e000)
    libicuuc.so.66 => /lib/x86_64-linux-gnu/libicuuc.so.66 (0x00007fc791f58000)
    libicui18n.so.66 => /lib/x86_64-linux-gnu/libicui18n.so.66 (0x00007fc791c59000)
    libgomp.so.1 => /lib/x86_64-linux-gnu/libgomp.so.1 (0x00007fc791c15000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fc791bf2000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fc79444d000)
    libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 (0x00007fc791bc2000)
    libicudata.so.66 => /lib/x86_64-linux-gnu/libicudata.so.66 (0x00007fc790101000)
    libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fc78ff20000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fc78ff03000)

It seems to me that binaries along private-bin and libs along private-lib paths are scanned with ldd to collect runtime library requirements, but since the R interpreter sits under /usr/lib/..., I'm unable to add it to a private-bin line in my profile.

I'm also concerned that upon finding multiple "R"s, firejail is overwriting them when placing them into the jail-- as there seems to be an implicit assumption that there will only ever be 1 binary of a given name (or there only the first binary of a given name will be added to the jail) found along PATH.

I'm in a special kind of hell. Please help me.

bbhtt commented 4 years ago

For the moment, I'm willfully ignoring the libraries potentially compiled/installed by installed R packages

You can choose that location, for example when I was making a profile, R choose the my shell dir working directory, private-lib is unwritable so it installed packages to ~/R/x86_64-pc-linux-gnu-library/4.0/. This will need to set noexec home and tmp (the packages are downloaded to /tmp/RTmpXXXX).

Where I'm stuck is trying to construct a profile with a private-bin and a private-lib that accommodates /usr/lib/R/bin/exec/R's runtime libraries...

You can express directories relative to /lib to private-lib: Here's mine that I used to download: install.packages("Rcmdr", dependencies=TRUE)

private-lib R,libreadline.so.8,libblas.so.3,libpcre2-8.so.0,libpcre2-8.so.0,liblzma.so.5,libbz2.so.1.0,libz.so.1,libtirpc.so.3,libtirpc.so.3,libicuuc.so.67,libicui18n.so.67,libgomp.so.1,liblapack.so.3,libcurl.so.4,libtiff.so.5

and my:

private-bin R,bash,less,lessecho,sh,uname,which,rm,tar,sed,mv
private-etc alternatives,ca-certificates,crypto-policies,host.conf,hostname,hosts,inputrc,ld.so.cache,ld.so.conf,ld.so.conf.d,ld.so.preload,locale,locale.alias,locale.conf,os-release,mime.types,nsswitch.conf,pki,resolv.conf,R,Renviron.site,Rprofile.site,selinux,ssl,sysless

The three above probably need some refining...

I think, making a profile for it doesn't seem practical,since it acts as a package manager too, it'll need exeutables/libs to be listed depending on the package you install and this profile won't be portable to another system/distro. And private-lib only works on amd64 I think.

curiositycasualty commented 4 years ago

I guess I should close the loop. Thank you @kortewegdevries for your suggestions.

What I ended up doing was using ldd on /usr/lib/R/bin/exec/R to generate a list of libs for a firejail profile. In my case:

exec_R_libs: %x(ldd /usr/lib/R/bin/exec/R).lines.map do |line|
  line.match(/\/([^ \/]+)(?: \(0x)/)
end.reject(&:nil?).map{|match| match[1]}.join(',')

This is for my very narrow use-case of an <app> that is a tomcat application w/ R script and execution integration. Not pictured here:

In so far as I can tell, it supports xvfb use and pandoc's pdf generation.

# Firejail profile for R
# Description: general markup converter

name r-sandbox
join-or-start r-sandbox

include disable-common.inc
include disable-devel.inc
include disable-exec.inc
include disable-interpreters.inc
include disable-passwdmgr.inc
include disable-programs.inc
include disable-xdg.inc

# breaks pdf output
# include whitelist-usr-share-common.inc

# although its tempting to use pre-baked profiles, many include "net none"
# which overlaps with our R profile
# include pandoc.profile

# needed since tomcat user shell is /bin/false
shell none

env R_HOME=/usr/lib/R
env R_HOME_DIR=/usr/lib/R

env CURLOPT_TIMEOUT=3
env CURLOPT_VERBOSE=1

nodvd
nosound
noautopulse
notv
nou2f
no3d

noroot

quiet

machine-id

x11 xvfb

hostname sandbox

hosts-file /etc/firejail/R.hosts
net br0

private-bin R,Rscript,basename,bash,bzip2,cat,curl,dash,mktexfmt,pandoc,pdflatex,pdftex,rm,sed,sh,tar,unzip,wget,which,zip,shims/uname

private-etc R,alternatives,ca-certificates,host.conf,hostname,hosts,inputrc,ld.so.cache,ld.so.conf,ld.so.conf.d,ld.so.preload,locale.alias,mime.types,nsswitch.conf,os-release,pki,resolv.conf,selinux,ssl,localtime,fonts,services,pandoc,texmf,texlive

private-lib R,R/modules/*.so,R/library/*/libs/*.so,R/site-library/*/libs/*.so,libR.so,libc.so.6,libblas.so.3,libm.so.6,libreadline.so.8,libpcre.so.3,liblzma.so.5,libbz2.so.1.0,libz.so.1,libdl.so.2,libicuuc.so.66,libicui18n.so.66,libgomp.so.1,libpthread.so.0,ld-linux-x86-64.so.2,libtinfo.so.6,libicudata.so.66,libstdc++.so.6,libgcc_s.so.1,x86_64-linux-gnu/openblas-pthread/libblas.so.3,x86_64-linux-gnu/lapack/liblapack.so.3

private-tmp

private-dev

whitelist /usr/share/pandoc
whitelist /var/lib/pandoc
whitelist /usr/share/texmf
whitelist /var/lib/texmf
whitelist /usr/share/texlive
whitelist /var/lib/texlive

read-only /usr/local/share/R

read-write /<app>/tomcat-tmp/reports_temp
read-only /<app>/<app>/files

noexec /tmp
noexec ${HOME}