Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DHCP client support #3102

Merged
merged 7 commits into from
Jan 7, 2020
Merged

DHCP client support #3102

merged 7 commits into from
Jan 7, 2020

Conversation

kris7t
Copy link
Collaborator

@kris7t kris7t commented Dec 30, 2019

As per my issue in #3026, I was trying to use DHCP to configure network interfaces in firejail. This patch implements integration with the ISC dhclient for both IPv4 and IPv6.

I left the commits as-is so that they can be reviewed individually, but I can squash them if that is preferred.

  • Added the options --ip=dhcp and --ip6=dhcp for IPv4 and IPv6 configuration by DHCP.
  • --ip=dhcp is handled similarly to --ip=none. In particular, firejail does not do any network configuration on its own, not even setting up routing.
  • dhclient is invoked in forking mode. Its main process exits when it successfully acquired a lease, while the daemon keeps running in the background. This ensures that firejail only proceeds when the network is working inside the container (running dhclient as a foreground process and getting notified about leases would be a bit trickier).
  • I added a capability filter for CAP_NET_BIND_SERVICE so that dhclient can bind to low ports.
  • PID files and lease files are written to /run/firejail/mnt/dhclient. The PID file is read (hopefully without race conditions) to find the PID of the dhclient daemon process. The sandbox may terminate if only the dhlient daemons are running.
  • No effort is made to properly terminate (-x) dhlient or release the DHCP lease (-r), as neither is required by the DHCP protocol. The dclient processes just die when the sandbox terminates. It would be possible to release the lease properly (this is allegedly required by some ISPs, but not in the more common situation when the sandboxes are connected to a local virtual bridged network, such as libvirt), but would require keeping some privileges until container termination (either in the firejail main process, or an auxiliary process just for communicating with dhclient).
  • Especially when used solely for DHCPv6, dhclient may fail to bind to the network interface if it has no link-local IPv6 address (or the LL address is only tentative). When both DHCPv4 and DHCPv6 is in use, the delay caused by waiting for the DHCPv4 lease is virtually always enough for IPv6 LL address to become active. I added a subcommand to fnet to wait for IPv6 LL addresses. Unfortunately, I had to use the rather complex rtnetlink interface, because this is the only way to access the tenative flag of an address.

Currently, --ip=dhcp is equivalent to --ip=none
and --ip6=dhcp does nothing either, except for parsing correctly
If the container has no IP address (because it will be assigned via DHCP later),
setting up a default route fails with a warning message.

While this is harmless, the default route should be omitted instead.
When dhclient is used to assign and IP to the container,
it should be able to overwrite resolv.conf

Therefore, we do the /etc mirroring similarly to the situation with
manually configured nameservers.

When both DHCP and manually set nameservers are in use,
warn that the manual ones will be overwritten
Refactored sbox_run to pass the varargs argument list as an array to an
auxiliary function.
The auxiliary function allows running programs with dynamically built
argument lists.
The new capability filter SBOX_CAPS_NET_SERVICE allows forked processes
to bind to low ports (privileged network services).

Because dhcp clients require both low ports and network administration
privileges, this patch also allows (bitwise) combination of capability filters
(except SBOX_CAPS_NONE, which completely drops any capabilities)
to grant both SBOX_CAPS_NETWORK and SBOX_CAPS_NET_SERVICE to a dhcp client.
This way, fnet and fnetfilter calls still do not get CAP_NET_BIND_SERVICE.
* In order to ensure that network interfaces are already configured when
  the sandboxed launches, we run dhclient in forking mode (no -d switch),
  which makes the dhclient command exit when it successfully acquired a lease.
  The dhclient daemon process keeps running in the background.
* We read the pid file for dhclient to find out the pid of the daemon process.
  Because dhclient only writes the pid file in the child process potentially
  after the forking parent process exits, there is some handling for possible
  race conditions.
* All lease files and pid files are under /run/firejail/dhclient/
* The v4 and v6 dhclient has a separate lease as recommended.
* The v4 client is set to generate a DUID, which is also used by the v6 client
  so that the server can associate the two leases if needed.
* /etc/resolv.conf is created in the sandbox just like with the --dns option,
  by mirroring /etc. When DHCP is used, /etc/resolv.conf is normally empty so
  that dhclient can overwrite it the nameservers from the DHCP server.

Current limitations:
* The dhclient processes in the background are not terminated properly
  (by SIGTERM or dhclient -x), nor is the DHCP lease released (by dclient -r).
  The reason for this is that firejail drops all capabilities and privileges
  before the application in the sandbox is launched, which makes it impossible
  to launch dhclient to release the lease or kill the dhclient processes still
  running with the effective user id of root. Instead the dhclient daemons
  die with the sandbox. According to the dhclient man page, releasing the lease
  is not required by the DHCP specification, so this is not a problem, however
  some ISPs may require releasing leases.
  A possible workaround would be to fork another process upon sandbox
  initialization that invokes dhclient -r when the sandbox is ready to exit.
  This would require communication with the main firejail process through
  a pipe, while keeping and required privileges. As this would add some
  complexity but the benefits have limited applicability (compatibility with
  esoteric DHCP server configurations), I chose not to implement this.
* When only an IPv6 address is requested, the interface may possible not have
  a link-local address when we run dhclient. This causes dhclient -6 fail,
  since DHCPv6 uses link-local addressing instead of layer 2 addressing,
  see e.g., https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=783387
  In a future commit, waiting for a link-local address will be added.
dhclient -6 fails if the interface to be configures has no link-local address.
This is especially problematic when only DHCPv6 is used
(e.g., --ip=none --ip6=dhcp), because the wait for a DHCPv4 lease is usually
ample time for the LL address to become available on the IPv6 link.
The LL address must not be tenative.

Therefore, this patch implements waiting for a non-tentative link-local
address in fnet for DHCPv6 configured interfaces.

The command fnet waitll <if> waits for an LL address on the interface <if>.

Currently, the maximum waiting time is 30 seconds,
and the kernel is polled through rtnetlink every 500 milliseconds.
These values seem sufficient for virtual bridged networks,
e.g., libvirt NAT networks.
@netblue30 netblue30 merged commit 94d018c into netblue30:master Jan 7, 2020
@netblue30
Copy link
Owner

All merged, thanks!

@rusty-snake
Copy link
Collaborator

We need to update the manpage (and --help).

@kris7t
Copy link
Collaborator Author

kris7t commented Jan 10, 2020

I will probably not have enough time to work on the documentation this weekend (qual exams are coming up), but will get to it some time next week.

@glitsj16
Copy link
Collaborator

@kris7t Would you have a look at #3174? Travis CI is broken due to this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants