netblue30 / firejail

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

Configuring network interface with DHCP #3026

Closed kris7t closed 4 years ago

kris7t commented 4 years ago

I am using the network isolation feature of firejail with a network bridge managed by libvirt (virbr0), so I can set up networking for sandboxes similarly to virtual machines. While firejail can auto-assign an IPv4 address by ARP scanning for a free address, there is no such feature for IPv6. Moreover, I would like to avoid clashes between sandboxed and (powered-off) virtual machines, so I would like to let the DHCP server (dnsmasq) managed by libvirt to handle address allocation of my sandboxes. (While I could assign an address manually to each and every sandbox, that would be much more error prone. I prefer to do manual assignments in one place in the libvirt config by MAC address.)

I set up DHCP for IPv4 (from a /24 block) and stateful DHCP6 for IPv6 (from a /112 prefix) in libvirt. I was experimenting with running dhcpcd in a --noprofile sandbox (in a real setup, an sbox_run call during sandbox initialization would probably replace sudo and --noprofile, but I didn't get that far) as follows:

$ firejail --net=virbr0 --ip=none --noprofile --debug bash
Autoselecting /usr/bin/zsh as shell
Building quoted command line: 'bash' 
Command name #bash#
get interface virbr0 configuration
MTU of virbr0 is 1500.
Bridge device virbr0 at 192.168.122.1/24
DISPLAY=:0 parsed as 0
Parent pid 667124, child pid 667125
Initializing child process
sbox run: /usr/lib/firejail/fnet create veth veth667126eth0 eth0 virbr0 667125 (null) 
Set caps filter 3000
Host network configured
PID namespace installed
Mounting tmpfs on /run/firejail/mnt directory
Creating empty /run/firejail/mnt/seccomp directory
Creating empty /run/firejail/mnt/seccomp/seccomp.protocol file
Creating empty /run/firejail/mnt/seccomp/seccomp.postexec file
sbox run: /run/firejail/lib/fnet ifup lo (null) 
Set caps filter 3000
sbox run: /run/firejail/lib/fnet ifup eth0 (null) 
Set caps filter 3000
Warning: cannot configure default route
Network namespace enabled

sbox run: /run/firejail/lib/fnet printif (null) 
Set caps filter 3000
Interface        MAC                IP               Mask             Status
lo                                  127.0.0.1        255.0.0.0        UP    
Default gateway configuration failed

Basic read-only filesystem:
Mounting read-only /etc
Mounting noexec /etc
Mounting read-only /var
Mounting noexec /var
Mounting read-only /bin
Mounting read-only /lib
Mounting read-only /usr
Mounting tmpfs on /var/lock
Mounting tmpfs on /var/tmp
Mounting tmpfs on /var/log
Create the new utmp file
Mount the new utmp file
Cleaning /home directory
Cleaning /run/user directory
Sanitizing /etc/passwd, UID_MIN 1000
Sanitizing /etc/group, GID_MIN 1000
Disable /home/kris/.config/firejail
Disable /run/firejail/network
Disable /run/firejail/bandwidth
Disable /run/firejail/name
Disable /run/firejail/x11
Remounting /proc and /proc/sys filesystems
Remounting /sys directory
Disable /sys/firmware
Disable /sys/hypervisor
Disable /sys/power
Disable /sys/kernel/debug
Disable /sys/kernel/vmcoreinfo
Disable /proc/sys/fs/binfmt_misc
Disable /proc/sys/kernel/core_pattern
Disable /proc/sys/kernel/modprobe
Disable /proc/sysrq-trigger
Disable /proc/sys/vm/panic_on_oom
Disable /proc/irq
Disable /proc/bus
Disable /proc/config.gz
Disable /proc/sched_debug
Disable /proc/timer_list
Disable /proc/kcore
Disable /proc/kallsyms
Disable /usr/lib/modules (requested /lib/modules)
Disable /boot
Disable /dev/port
Disable /run/user/1000/gnupg
Disable /run/user/1000/systemd
Disable /dev/kmsg
Disable /proc/kmsg
Disable /sys/fs
Disable /sys/module
Mounting noexec /run/firejail/mnt/pulse
3589 3566 0:157 /pulse /home/kris/.config/pulse rw,nosuid,nodev,noexec - tmpfs tmpfs rw,mode=755
mountid=3589 fsname=/pulse dir=/home/kris/.config/pulse fstype=tmpfs
Current directory: /home/kris
DISPLAY=:0 parsed as 0
Mounting read-only /run/firejail/mnt/seccomp
Drop privileges: pid 1, uid 1000, gid 1000, nogroups 0
starting application
LD_PRELOAD=(null)
Running 'bash'  command through /usr/bin/zsh
execvp argument 0: /usr/bin/zsh
execvp argument 1: -c
execvp argument 2: 'bash' 
Child process initialized in 510.91 ms
monitoring pid 5

[kris@KRiS-Blushweaver ~]$ ip link show eth0
2: eth0@if40: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 22:2d:13:ea:04:60 brd ff:ff:ff:ff:ff:ff link-netnsid 0
[kris@KRiS-Blushweaver ~]$ sudo dhcpcd eth0
[sudo] password for kris: 
eth0: if_init: Read-only file system
eth0: interface not found or invalid
dhcpcd exited

I get the same message even if I mount tmpfs to /var/lib/dhcp, or when I run the firejail command as root.

If there a way to auto-configure networking inside the sandbox by DHCP? Are there any security implications I should be aware of?

netblue30 commented 4 years ago

This is a bug, thanks. It works fine for ISC DHCP client (isc-dhcp-client package on debian). You do need to start the sandbox as root in order to run the client. You would start the client as "dhclient -v eth0".

I get the same message even if I mount tmpfs to /var/lib/dhcp

/var/lib/dhcp is used by isc-dhcp-client. Apparently dhcpcd uses /var/lib/dhclient, try a --tmpfs=/var/lib/dhclient. For /var/lib/dhcp we mount by default a tmpfs on top of it. I'll try to bring in a fix.

kris7t commented 4 years ago

Thanks!

(Sorry if this derails a bit, or I maybe should open a new issue, but: ) Is there any chance I could do DHCP without running the container as root? I guess the main difficulty is that the client has to run while the sandbox is active (I can't just acquire an IP calling a DHCP client once in sandbox_if_up, because the lease may run out).

netblue30 commented 4 years ago

OK, seems to be a dhcpcd problem with the way they detect the ethernet interfaces, but idc-dhcp-client works fine.

Is there any chance I could do DHCP without running the container as root?

No, you need to be root to run a DHCP client. I'll add support for dhcp client support directly in the sandbox. At startup, the sandbox is root, so it should be able to run a client this way. I'll mark it as an enhancement for now.

kris7t commented 4 years ago

Thanks for the pointers! I was playing around a bit with this:

$ sudo firejail --net=virbr0 --ip=none --noprofile --dns=fd00::1:8:1 --dns=192.168.122.1 --shell=/usr/bin/bash
Parent pid 38815, child pid 38816
The new log directory is /proc/38816/root/var/log
Warning: cannot configure default route

Interface        MAC                IP               Mask             Status
lo                                  127.0.0.1        255.0.0.0        UP
Default gateway configuration failed
DNS server fd00::1:8:1
DNS server 192.168.122.1

Child process initialized in 533.89 ms
[root@KRiS-Blushweaver kris]# dhclient -4 -sf /usr/local/bin/dhclient-script-firejail eth0
[root@KRiS-Blushweaver kris]# dhclient -6 -sf /usr/local/bin/dhclient-script-firejail eth0
[root@KRiS-Blushweaver kris]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0@if18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 0e:8c:80:1e:fa:ba brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.122.174/24 brd 192.168.122.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fd00::1:8:fff7/128 scope global dynamic
       valid_lft 3597sec preferred_lft 3597sec
    inet6 fe80::c8c:80ff:fe1e:faba/64 scope link
       valid_lft forever preferred_lft forever
[root@KRiS-Blushweaver kris]# killall dhclient
[root@KRiS-Blushweaver kris]# exit
exit

Parent is shutting down, bye...

Looks like dhclient on its own won't work with a read-only /etc, because it tries to create a new resolv.conf. As a workaround, dhclient-script can be modified to skip hostname and DNS resolver setting, and we can rely on the DNS servers passed to firejail on the command line. Furthermore, the dhclient processes must be killed before the sandbox can cleanly shut down.

I still have some wonkyness regarding IPv6 forwarding, but I suspect that's because I am doing a very evil thing (stateful NAT66).