Secure Containerized Browser

By default Chromium on OpenBSD (not so) recently got OpenBSD’s unveil(2) support. That means that of you run Chromium with --enable-unveil flag then it will be prevented from accessing anything other than the ~/Downloads directory. No such thing on FreeBSD exists. Firefox or Chromium have access to all files user can read – even to your system sshd(8) keys or even worse to your private keys laying in the ~/.ssh dir. On FreeBSD thanks to its FreeBSD Jails technology we can create secure containerized browser with only access to the specified directory. On my system its the ~/download dir.

You may want to check other desktop related articles in the FreeBSD Desktop series on the FreeBSD Desktop page.

Configuration

We will start with /etc/jail.conf file configuration. For the record – we will be using /jail for our FreeBSD Jails main dir. I will also use /jail dir for the ‘base’ FreeBSD versions tarballs as a convenient place. As I use 10.0.0.0/24 address space I will use 10.0.0.200 for our containerized browser. Feel free to pick other IP from which you will be able to reach the Internet. The /etc/jail.conf is shown below. One thing to note here. As I am using WiFi wlan0 interface I have put that into the Jail configuration. If you use LAN interface (for example em0) then put that instead into this Jail config. As you see from the example below we will be using Firefox browser in out example.

root@host # cat /etc/jail.conf

# GLOBAL
  exec.start = "/bin/sh /etc/rc";
  exec.stop = "/bin/sh /etc/rc.shutdown";
  exec.clean;
  exec.consolelog = "/var/log/jail_${name}_console.log";
  mount.devfs;
  host.hostname = ${name};
  path = /jail/${name};

# JAILS
  firefox {
    devfs_ruleset = 30;
    ip4.addr = 10.0.0.200;
    interface = wlan0;
    allow.raw_sockets;
    allow.sysvipc;
    mount.fstab = "/jail/firefox/etc/fstab";
  }

As you can see we will also be using devfs(8) rules in the /etc/devfs.rules file – shown below. This configuration is needed to have access to sound(4) in our FreeBSD Jail. If you do not need sound then you can delete devfs_ruleset = 30; from the /etc/jail.conf file and also do not add anything in the /etc/devfs.rules file.

root@host # cat /etc/devfs.rules
[sound=30]
add path 'mixer*' unhide
add path 'dsp*'   unhide

If we are about to share the ~/download dir with our containerized browser then we need to somehow add that information to our FreeBSD Jail. We will use the FreeBSD’s mount_nullfs(8) command to mount our currently existing ~/download dir into our FreeBSD Jail. We will use following /jail/firefox/etc/fstab for that purpose.

root@host # cat /jail/firefox/etc/fstab
#SOURCE         #MNT                                      #TYPE   #OPTS       #DUMP/PASS
/data/download  /jail/firefox/usr/home/vermaden/download  nullfs  rw,noatime  0 0

Of course you do not have to share any directory with your containerized browser.

You may as well would want to make this jails start everytime you boot your system. To do that add below lines to the /etc/rc.conf file as shown below.

jail_enable=YES
jail_parallel_start=YES
jail_list="firefox"

Create the Jail

As I use FreeBSD 13.0-RELEASE I would be using also the FreeBSD 13.0-RELEASE Jail for that purpose. If you are running for example FreeBSD 12.3-RELEASE then make sure that you will use FreeBSD 12.3-RELEASE Jail. The Jail version needs to be lower then the host system version. We will now fetch needed FreeBSD ‘base’ file and unpack it within /jail/firefox dir where our container would live. We will also configure several other basic files such as /etc/resolv.conf or /etc/hosts files.

root@host # mkdir -p /jail/BASE /jail/firefox /jail/firefox/usr/home/vermaden/download

root@host # fetch -o /jail/BASE/13.0-RELEASE-base.txz \
    http://ftp.freebsd.org/pub/FreeBSD/releases/amd64/13.0-RELEASE/base.txz

root@host # tar -xvpf /jail/BASE/13.0-RELEASE-base.txz -C /jail/firefox

root@host # echo nameserver 1.1.1.1 > /jail/firefox/etc/resolv.conf

root@host # echo 10.0.0.200 firefox >> /jail/firefox/etc/hosts

root@host # cat << EOF > /jail/firefox/etc/fstab
#SOURCE         #MNT                                      #TYPE   #OPTS       #DUMP/PASS
/data/download  /jail/firefox/usr/home/vermaden/download  nullfs  rw,noatime  0 0
EOF

We will now start our fresh FreeBSD Jail.

root@host # service jail onestart firefox

We can now also see two new mounts in the mount(8) output.

root@host # mount | tail -2
/data/download on /jail/firefox/usr/home/vermaden/download (nullfs, local, noatime)
devfs on /jail/firefox/dev (devfs)

root@host # mount -p | tail -2 | column -t
/data/download /jail/firefox/usr/home/vermaden/download nullfs rw,noatime 0 0
devfs /jail/firefox/dev devfs rw 0 0

You may want to update the FreeBSD version to the most up to date one with freebsd-update(8) commands.

root@host # freebsd-update -b /jail/firefox fetch
root@host # freebsd-update -b /jail/firefox install

Install Needed Packages

Before installing anything we will first switch to the latest branch for the pkg(8) packages to have most up to date software. We will then process to installing the Firefox package. We will also need x11/xauth package for X11 Forwarding process.

root@host # sed -i '' s.quarterly.latest.g /jail/firefox/etc/pkg/FreeBSD.conf

root@host # grep latest /jail/firefox/etc/pkg/FreeBSD.conf
  url: "pkg+http://pkg.FreeBSD.org/${ABI}/latest",

root@host # jls
   JID  IP Address      Hostname                      Path
     1  10.0.0.200      firefox                       /jail/firefox

root@host # jexec 1

(root@jail) # pkg install -y firefox xauth

Create Matching User and Configure sshd(8) Daemon

We will now enter our FreeBSD Jail again for several other needed tasks for our containerized browser to be working. First is creating inside similar user as you currently use inside. Especially with the same UID/GID to have files with proper permissions in your real ~/download directory instead of files with other UID/GID that you will have to chown(8) with root user. As my vermaden user uses UID/GID 1000 I will also use that inside. I will also set simple password that You will only use once – to copy your public SSH key there.

root@host # jexec 1

(root@jail) # echo your-username-password-goes-here | pw user add -u 1000 -n vermaden -m -s /bin/sh -h 0

Now we need to run /usr/local/bin/dbus-uuidgen --ensure once to make sure DBUS is initialized properly. Firefox and many other apps would not start if we omit that step.

(root@jail) # /usr/local/bin/dbus-uuidgen --ensure

Now the sshd(8) daemon. The only thing we need to do is to add it to the system startup and also add X11UseLocalhost no option to its config file.

(root@jail) # sysrc sshd_enable=YES
sshd_enable: NO -> YES

(root@jail) # echo X11UseLocalhost no >> /etc/ssh/sshd_config

(root@jail) # service sshd start
Generating RSA host key.
2048 SHA256:VnrvItf0tl738C5Oc2St6T63/6o8zaDlfUskB+NrElo root@firefox (RSA)
Generating ECDSA host key.
256 SHA256:ZAjcAGqlrVwvY+J9MuVzErx9QUOqIOJE3nJX/Oqwtpk root@firefox (ECDSA)
Generating ED25519 host key.
256 SHA256:JdzUql2D2+X8iBn3c1jWDHQRNQMKqWGOcL4J16fIX0E root@firefox (ED25519)
Performing sanity check on sshd configuration.
Starting sshd.

Copy Public SSH Key and Start

Copying your public SSH key is optional but if you omit this step then you would have to type your FreeBSD Jail user password every time you would want to start your secure Firefox instance.

vermaden@host % ssh-copy-id -i ~/.ssh/id_rsa vermaden@10.0.0.200
Password:

Now you can start your containerized browser. I have added some useful flags for ssh(1) client like compression with -C and fastest supported encryption with -c aes128-ctr option. The -X is for X11 Forwarding option. I also added GDK_SYNCHRONIZE=1 to make Firefox yell less πŸ™‚

vermaden@host % ssh -C -c aes128-ctr -X vermaden@10.0.0.200 env GDK_SYNCHRONIZE=1 firefox --new-instance

Now without password you should see fresh Firefox instance.

firefox-fresh

I will now try to play some random video. I can not show you that from an image but the sound also works πŸ™‚

firefox-youtube

Similar setup can be created for other browser if Firefox is not your browser of choice of course. If you are curious how much space it uses its about this:

root@host # du -smx /jail/BASE/13.0-RELEASE-base.txz /jail/firefox 
181 /jail/BASE/13.0-RELEASE-base.txz
1603 /jail/firefox

root@host # du -smx -A /jail/BASE/13.0-RELEASE-base.txz /jail/firefox
181 /jail/BASE/13.0-RELEASE-base.txz
2601 /jail/firefox

I also added the -A flag in second the du(1) command to show you how much more space would be used without the ZFS LZ4 compression.

UPDATE 1 – Use XPRA Instead of X11 Forwarding

Some people complained that this is quite good setup but they were not happy with using X11 Forwarding for the connection method. I decided to add additional XPRA method to connect to our secure containerized browser. First thing you need to do is to install the x11/xpra package on both the host system and also inside the jail container.

root@host # pkg install -y xpra
(root@jail) # pkg install -y xpra

Now – after logging into your user in the Jail container – vermaden in may case – we will use the xpra commands to create new session with Firefox browser.

Lets see if any xpra sessions currently exists.

(vermaden@jail) % xpra list
Warning: XDG_RUNTIME_DIR is not defined
 and '/run/user/1000' does not exist
 using '/tmp'
No xpra sessions found

Seems not. We can not start our Firefox session.

(vermaden@jail) % xpra start --bind-tcp=:14500 --start='firefox --new-instance'
Warning: XDG_RUNTIME_DIR is not defined
 and '/run/user/1000' does not exist
 using '/tmp'
Entering daemon mode; any further errors will be reported to:
  /tmp/xpra/S19958.log
Actual display used: :0
Actual log file name is now: /tmp/xpra/:0.log

We can see in the xpra list command that new session appeared.

(vermaden@jail) % xpra list
Warning: XDG_RUNTIME_DIR is not defined
 and '/run/user/1000' does not exist
 using '/tmp'
Found the following xpra sessions:
/home/vermaden/.xpra:
        LIVE session at :0

We can also see that xpra is now listening on the 14500 port.

(vermaden@jail) % sockstat -l4
USER     COMMAND    PID   FD PROTO  LOCAL ADDRESS         FOREIGN ADDRESS
vermaden python3.8  20781 3  tcp4   10.0.0.200:14500      *:*
root     sshd       58454 3  tcp4   10.0.0.200:22         *:*
root     syslogd    48568 5  udp4   10.0.0.200:514        *:*

We will now move to out host and start graphical xpra client to connect to our FreeBSD Jail with Firefox process.

update1-xpra-main

After clicking the large Connect button we can now enter our Jail address.

update1-xpra-connect

After again clicking the Connect button on the bottom this time we can now se our Firefox browser from our secure environment.

update1-xpra-firefox

After we done our job at more secure Firefox we can now end our xpra session on the jail system.

(vermaden@jail) % xpra stop
Warning: XDG_RUNTIME_DIR is not defined
 and '/run/user/1000' does not exist
 using '/tmp'
xpra initialization error:
 cannot find any live servers to connect to

(vermaden@jail) % xpra list
Warning: XDG_RUNTIME_DIR is not defined
 and '/run/user/1000' does not exist
 using '/tmp'
No xpra sessions found

As XPRA provides OpenGL acceleration you may verify that fact from your host system using below command.

vermaden@host % xpra opengl
Warning: XDG_RUNTIME_DIR is not defined
 and '/run/user/1000' does not exist
 using '/tmp'
Warning: cannot handle window transparency
 screen is not composited
Warning: vendor 'Intel Open Source Technology Center' is greylisted,
 you may want to turn off OpenGL if you encounter bugs
Warning: window 0xffffffff changed its transparency attribute
 from False to True, behaviour is undefined
GLU.version=1.3
GLX=1.4
accelerate=3.1.5
accum-alpha-size=0
accum-blue-size=0
accum-green-size=0
accum-red-size=0
alpha-size=0
aux-buffers=0
blue-size=8
buffer-size=24
depth=24
depth-size=0
direct=True
display_mode=ALPHA, DOUBLE
double-buffered=True
green-size=8
level=0
max-viewport-dims=16384, 16384
opengl=3.0
pyopengl=3.1.5
red-size=8
renderer=Mesa DRI Intel(R) HD Graphics 3000 (SNB GT2)
rgba=True
safe=True
shading-language-version=1.30
stencil-size=0
stereo=False
success=True
texture-size-limit=8192
transparency=True
vendor=Intel Open Source Technology Center
zerocopy=True

You can also use VNC or other methods of course.

Hope that helps πŸ™‚

EOF

7 thoughts on “Secure Containerized Browser

  1. BSDfan

    DziΔ™ki! CzegoΕ› takiego szukaΕ‚em πŸ™‚ Jak wyglΔ…da kwestia akceleracji sprzΔ™towej? Filmy w HD/4k chodzΔ… pΕ‚ynnie?
    I jeszcze, uΕΌywasz czegoΕ› do zarzΔ…dzania jailami? Bastille, CBSD, iocage, pot?

    Like

    Reply
    1. vermaden Post author

      Akcelracji sprzetowej nie ma. Cokolwiek duzego odtwarza sie wolno (mniej klatek na sekunde).

      Troche pomaga XPRA ale szalu tez nie ma.

      Ja uzywam glownie ‘czystych’ Jails czyli bez zadnych zarzadzaczy, ale z tych co sa dostepne to wybralbym BastilleBSD. Jezeli nie to to chociaz iocage – jest swietnie opisane w ksiazce FreeBSD Mastery: Jails wiadomego autora.

      Like

      Reply
      1. BSDfan

        A dodanie do /etc/devfs.rules (podobnie jak z audio) jakichΕ› /dev/dri, /dev/drm czy czegoΕ› tam co dotyczy grafiki nie wΕ‚Δ…czy magicznie akceleracji? πŸ˜‰

        Like

  2. Pingback: Valuable News – 2021/12/20 | πšŸπšŽπš›πš–πšŠπšπšŽπš—

  3. Pingback: OpenBSD – containerizing Chromium | 0ddn1x: tricks with *nix

  4. neimsaci

    Firefox starts and works fine without dbus. You may have compiled firefox with dbus (or any other application for that matter) and in that case it should not be surprise at all if app does not start without dbus.

    Like

    Reply

Leave a comment