H
hukadan
Guest
Motivations
The main reason to put a browser in a jail is quite simple : browsers cannot be trusted. They are too much exposed. Executing a browser inside a jail(8) is a way to be sure that the damages induced by a malicious software are contained (as much as possible). I decided to write this tutorial after posting in the Thread x11-applications-in-iocage-jails.53224 since I did not find any on the internet (including this forum). There is nothing new here but I just gathered information here and there to put everything together in one and the same place.
In this tutorial I will describe the steps allowing to execute a browser inside a jail(8). Of course, it can be adapted to other software such as e-mail clients. I will take Firefox (www/firefox) as browser but once again, it applies to others with almost no modification.
We will use different tools to achieve this goal. First, we will create a jail(8) using iocage(8) but you can do the same using other tools such as ezjail(8). We will also use pf(4) to NAT our jail(8) and nullfs(5) to share folders between the host and the jail(8). In order to launch the browser, I show two alternatives. With the first one, a ssh(1) server is used with X11 forwarding. With the second one, we use a tool called jailme() (sysutils/jailme) which does a work similar to jexec(8) but without needing the root credentials.
1- Let's begin with the jail network
In this section we will use pf(4) to NAT our jail(8). This configuration makes sure that my jails do not interfere with my house network and can also take advantage of my firewall settings.
Create a lo1 interface that will be used for the jail(8) network and assign an IP address to it.
In order to make this change permanent, add the following lines to /etc/rc.conf.
Next, configure pf(4) to NAT the future jail(8) by creating/editing the /etc/pf.conf file (this is a minimal configuration to NAT the jail and by no means a firewall configuration. You have to adapt it to your existing rules) and replacing re0 with your network interface name (you can have a list of your network interfaces with
Then launch pf(4) to activate this network.
Do not forget to make sure that pf(4) is launched at boot.
2- jail(8) creation using iocage(8)
Create a jail(8) named (more exactly tagged) injail with an IP address
Since mount_nullfs(8) will be used in the next part to mount jail(8) folders, it can be convenient to use the hack88 hack (introduced in 1.6.0) on our jail(8).
Then make sure the jail(8) is launched at boot. This is done in two steps. First, set the boot property of the jail(8) to on (this is not a jail(8) property per se but an iocage(8) specific property).
Then modify the /etc/rc.conf file.
The last step is to launch the jail(8), attach to it and create the jailuser user using pw(1).
If you plan to use jailme(), make sure that the username and user id match between host and jail(8) when creating this user (to know your user id, use id(1)).
3- Get the jail(8) "Firefox ready"
If you want to use Firefox, you have to install it.
Firefox needs devel/dbus to run. So make sure this service is started at jail boot by editing the /etc/rc.conf file on the jail.
Then, start the service.
4- jailme or ssh(1)
Now, you have the choice between two solutions. The first one requires a perfect match between the jail and the host for both the username and the user id. We also have to set xhost(1) in order to accept connections from the jail(8) to the host X server. The second one requires to setup a ssh(1) server on the jail(8), to install xauth(1), to modify the host file of the jail(8) and use public key authentication (this is not compulsory but it is really convenient).
Two points worth mentioning before going on :
4-1 jailme
jailme() works as jexec(8) except that you do not need root credentials to use it. It will let you execute a command in the jail(8) as normal user provided that your username and your id are identical on both the host and the jail(8). The trick here is to execute an X11 application on the jail(8) and make it use the X server on the host. For this to happen, you have to modify the access to your X server with the xhost(1) command. Since our jail(8) as the IP address
Add the following line to your ~/.xinitrc in order to allow the jail(8) when you login.
If you do not use ~/.xinitrc in your setup, I am sure there is another way to make this happen. Of course, we need to install jailme() on the host.
jailme() takes as argument the jail id or jail name and the command you want to launch. The problem is that the jail id can change and the jail name set automaticaly by iocage(8) is rather long and cannot be changed (as far as I know). So make a small script named
That's it. You can try it.
If you are not interested in the ssh(1) setup, you can skip the following paragraph and go to the one about sharing folders.
4-2 ssh(1) and X11 forwarding
With this solution, we use the well known X11 forwarding capabilities of ssh(1). For that, we need to configure the ssh(1) server on the jail. First attach to the jail(8).
Edit the /etc/ssh/sshd_config file and add the following lines.
Since we do not use localhost, make sure that the jail(8) can resolve its hostname by adding the following line to /etc/hosts on the jail.
The last step is to make sure that the ssh(1) server is started at jail boot adding the following line to /etc/rc.conf file.
Then, start the ssh server.
We also need to install xauth(1) to do X11 forwarding.
Then, come back to the host and configure de client side of ssh(1). It is quite boring to have to type password each time. So setup a public key authentication.
We also have to tell the ssh(1) client to use this public key and to activate the X11 forwarding. Edit/create the ~/.ssh/config file.
Check that everything works.
5- Sharing folders between the host and the jail(8) (optional)
It can be convenient to grab files you just downloaded on the jail(8) to use them on the host. nullfs(8) will be used to achieve this. First check the mount point of the jail.
For those who already use iocage(8), you can see that this mount point is shorter than usual. This is due to the hack88 we set earlier. With this information, edit the /etc/fstab file.
$JAIL_MOUNT_POINT has to be replaced by the mount point you got previously. The late option is here to avoid problems such as this one Thread nullfs-via-etc-fstab-on-zfs-only-system.11454.
We can check if everything is set properly.
You can also choose not to mount the entire jailuser home but only one folder (I personaly mount a Documents subfolder of the jailuser home on the host). Anyway, now you have access to your files in the jail(8) from the host.
6 - Sharing sound devices (optional)
The internet is nice, but with sound it is even better and jails can share devices with the host including sound devices. This done by editing the devfs.rules(1). The default ones, including jails, can be found in the /etc/default/devfs.rules file. If the default settings are fine for classic jails, it is not enough for a jail(8) executing a browser. Therefore, non default devfs.rules(1) settings have to be created. Create/edit the /etc/devfs.rules file and add the following lines.
This is simply the default settings plus the two last lines that give the jail(8) access to sound devices. Set the new devfs.rules(1) to your jail(8).
And restart the jail(8) (not using the soft restart)
You should be able to play sound using your jailed browser now.
Afterthoughts
I have tried the different steps of this tutorial thoroughly in order to make sure that if you follow them this should work for you as well. However, each system is different and may be this tutorial will not work right away for you. Please, do not hesitate to post if you are facing problems following this tutorial.
There is for sure smarter ways to achieve what I just described. I would really like to know those smarter ways so I can improve this tutorial and my FreeBSD knowledge. So do not hesitate to question my setup. We could also image a script that take a snapshot of the jail(8) before starting the browser and rollback to that snapshot when you exit so you always start with a clean system.
Concerning browsers, it would be nice to change "Firefox ready" by "Browser ready", so if you try this with other browsers, do not hesitate to come here and tell me. I will modify this tutorial accordingly.
Why only browsers ? Because it is the only peace of software I jailed so far. I can imagine that other software would need additional/different devices shared between the host and the jail(8). A word processor for instance would not need sound but could need a printer device (/dev/ulpt0 for instance). Therefore, do not hesitate to share your devfs.rules(1) for other software.
Finally, this is my first HowTo. So if some parts are not clear enough for you or you think I skipped an important step and therefore this tutorial need some clarifications, I would happily add them to this tutorial.
EDITS :
2015-09-07 :
The main reason to put a browser in a jail is quite simple : browsers cannot be trusted. They are too much exposed. Executing a browser inside a jail(8) is a way to be sure that the damages induced by a malicious software are contained (as much as possible). I decided to write this tutorial after posting in the Thread x11-applications-in-iocage-jails.53224 since I did not find any on the internet (including this forum). There is nothing new here but I just gathered information here and there to put everything together in one and the same place.
In this tutorial I will describe the steps allowing to execute a browser inside a jail(8). Of course, it can be adapted to other software such as e-mail clients. I will take Firefox (www/firefox) as browser but once again, it applies to others with almost no modification.
We will use different tools to achieve this goal. First, we will create a jail(8) using iocage(8) but you can do the same using other tools such as ezjail(8). We will also use pf(4) to NAT our jail(8) and nullfs(5) to share folders between the host and the jail(8). In order to launch the browser, I show two alternatives. With the first one, a ssh(1) server is used with X11 forwarding. With the second one, we use a tool called jailme() (sysutils/jailme) which does a work similar to jexec(8) but without needing the root credentials.
1- Let's begin with the jail network
In this section we will use pf(4) to NAT our jail(8). This configuration makes sure that my jails do not interfere with my house network and can also take advantage of my firewall settings.
Create a lo1 interface that will be used for the jail(8) network and assign an IP address to it.
# ifconfig lo1 create
# ifconfig lo1 inet 10.0.0.254/24
In order to make this change permanent, add the following lines to /etc/rc.conf.
Code:
cloned_interfaces="lo1"
ifconfig_lo1="inet 10.0.0.254 netmask 255.255.255.0"
ifconfig -l
).
Code:
ext_if="re0"
int_if="lo1"
localnet=$int_if:network
scrub in all fragment reassemble
set skip on lo0
set skip on lo1
#nat for jails
nat on $ext_if inet from $localnet to any -> ($ext_if)
# pfctl -e -f /etc/pf.conf
Do not forget to make sure that pf(4) is launched at boot.
Code:
pf_enable="YES"
pflog_enable="YES"
2- jail(8) creation using iocage(8)
Create a jail(8) named (more exactly tagged) injail with an IP address
10.0.0.1
and a hostname injail. # iocage create hostname=injail tag=injail ip4_addr="lo1|10.0.0.1"
# iocage set hostname=injail injail
Since mount_nullfs(8) will be used in the next part to mount jail(8) folders, it can be convenient to use the hack88 hack (introduced in 1.6.0) on our jail(8).
# iocage set hack88=1 injail
Then make sure the jail(8) is launched at boot. This is done in two steps. First, set the boot property of the jail(8) to on (this is not a jail(8) property per se but an iocage(8) specific property).
# iocage set boot=on injail
Then modify the /etc/rc.conf file.
Code:
iocage_enable="YES"
# iocage start injail
# iocage console injail
# pw user add jailuser -s /bin/tcsh -m
# passwd jailuser
If you plan to use jailme(), make sure that the username and user id match between host and jail(8) when creating this user (to know your user id, use id(1)).
3- Get the jail(8) "Firefox ready"
If you want to use Firefox, you have to install it.
# iocage console injail
# pkg install -y firefox
Firefox needs devel/dbus to run. So make sure this service is started at jail boot by editing the /etc/rc.conf file on the jail.
Code:
dbus_enable="YES"
# service dbus start
4- jailme or ssh(1)
Now, you have the choice between two solutions. The first one requires a perfect match between the jail and the host for both the username and the user id. We also have to set xhost(1) in order to accept connections from the jail(8) to the host X server. The second one requires to setup a ssh(1) server on the jail(8), to install xauth(1), to modify the host file of the jail(8) and use public key authentication (this is not compulsory but it is really convenient).
Two points worth mentioning before going on :
- if your version of x11-servers/xorg-server is equal or greater than 1.17, you have to make sure that the Xorg server listen to tcp. Using startx(1), the command is
% startx -- -listen tcp
. - if you plan to use multiple jails to launch multiple Firefox instances, do not forget to add the
--new-instance
option each time you start Firefox to avoid strange behavior like starting Firefox on one jail and see it executes on a another one (see this for more details).
4-1 jailme
jailme() works as jexec(8) except that you do not need root credentials to use it. It will let you execute a command in the jail(8) as normal user provided that your username and your id are identical on both the host and the jail(8). The trick here is to execute an X11 application on the jail(8) and make it use the X server on the host. For this to happen, you have to modify the access to your X server with the xhost(1) command. Since our jail(8) as the IP address
10.0.0.1
, allow only this address. % xhost + inet:10.0.0.1
Add the following line to your ~/.xinitrc in order to allow the jail(8) when you login.
Code:
xhost + inet:10.0.0.1
# pkg install -y jailme
.jailme() takes as argument the jail id or jail name and the command you want to launch. The problem is that the jail id can change and the jail name set automaticaly by iocage(8) is rather long and cannot be changed (as far as I know). So make a small script named
injail
that allows you to directly issue injail [I]command[/I]
.
Code:
#! /bin/sh
jail_hostname=injail
jail_id=$(jls -h jid host.hostname | grep $jail_hostname | cut -w -f 1)
jailme $jail_id $@
% injail firefox
If you are not interested in the ssh(1) setup, you can skip the following paragraph and go to the one about sharing folders.
4-2 ssh(1) and X11 forwarding
With this solution, we use the well known X11 forwarding capabilities of ssh(1). For that, we need to configure the ssh(1) server on the jail. First attach to the jail(8).
# iocage console injail
Edit the /etc/ssh/sshd_config file and add the following lines.
Code:
X11Forwarding yes
X11UseLocalhost no
Since we do not use localhost, make sure that the jail(8) can resolve its hostname by adding the following line to /etc/hosts on the jail.
Code:
10.0.0.1 injail
Code:
sshd_enable="YES"
# service sshd start
.We also need to install xauth(1) to do X11 forwarding.
# pkg install -y xauth
Then, come back to the host and configure de client side of ssh(1). It is quite boring to have to type password each time. So setup a public key authentication.
% ssh-keygen -t rsa -b 4096 -C "jailuser@example.com" -f ~/.ssh/injail
% ssh jailuser@10.0.0.1 mkdir .ssh
% cat ~/.ssh/injail.pub | ssh jailuser@10.0.0.1 'cat >> .ssh/authorized_keys'
We also have to tell the ssh(1) client to use this public key and to activate the X11 forwarding. Edit/create the ~/.ssh/config file.
Code:
Host injail
Hostname 10.0.0.1
Port 22
User jailuser
IdentityFile ~/.ssh/injail
ForwardX11 yes
ForwardX11Trusted yes
% ssh injail firefox
5- Sharing folders between the host and the jail(8) (optional)
It can be convenient to grab files you just downloaded on the jail(8) to use them on the host. nullfs(8) will be used to achieve this. First check the mount point of the jail.
# iocage get mountpoint injail
For those who already use iocage(8), you can see that this mount point is shorter than usual. This is due to the hack88 we set earlier. With this information, edit the /etc/fstab file.
Code:
$JAIL_MOUNT_POINT/root/home/jailuser /home/hostuser/whereyouwant nullfs rw,late 0 0
We can check if everything is set properly.
# mount /home/hostuser/whereyouwant
% ls /home/hostuser/whereyouwant
You can also choose not to mount the entire jailuser home but only one folder (I personaly mount a Documents subfolder of the jailuser home on the host). Anyway, now you have access to your files in the jail(8) from the host.
6 - Sharing sound devices (optional)
The internet is nice, but with sound it is even better and jails can share devices with the host including sound devices. This done by editing the devfs.rules(1). The default ones, including jails, can be found in the /etc/default/devfs.rules file. If the default settings are fine for classic jails, it is not enough for a jail(8) executing a browser. Therefore, non default devfs.rules(1) settings have to be created. Create/edit the /etc/devfs.rules file and add the following lines.
Code:
# Devices usually found in a desktop jail for sound.
#
[devfsrules_desktop_jail=5]
add include $devfsrules_hide_all
add include $devfsrules_unhide_basic
add include $devfsrules_unhide_login
add path zfs unhide
add path 'mixer*' unhide
add path 'dsp*' unhide
# iocage set devfs_ruleset=5 injail
And restart the jail(8) (not using the soft restart)
# iocage stop injail
# iocage start injail
You should be able to play sound using your jailed browser now.
Afterthoughts
I have tried the different steps of this tutorial thoroughly in order to make sure that if you follow them this should work for you as well. However, each system is different and may be this tutorial will not work right away for you. Please, do not hesitate to post if you are facing problems following this tutorial.
There is for sure smarter ways to achieve what I just described. I would really like to know those smarter ways so I can improve this tutorial and my FreeBSD knowledge. So do not hesitate to question my setup. We could also image a script that take a snapshot of the jail(8) before starting the browser and rollback to that snapshot when you exit so you always start with a clean system.
Concerning browsers, it would be nice to change "Firefox ready" by "Browser ready", so if you try this with other browsers, do not hesitate to come here and tell me. I will modify this tutorial accordingly.
Why only browsers ? Because it is the only peace of software I jailed so far. I can imagine that other software would need additional/different devices shared between the host and the jail(8). A word processor for instance would not need sound but could need a printer device (/dev/ulpt0 for instance). Therefore, do not hesitate to share your devfs.rules(1) for other software.
Finally, this is my first HowTo. So if some parts are not clear enough for you or you think I skipped an important step and therefore this tutorial need some clarifications, I would happily add them to this tutorial.
EDITS :
2015-09-07 :
- sections have been rearranged : optional steps have been put at the end ;
- multiple Firefox instances issue added : comment added on the
--new-instance
option.
- new Xorg version issue added : need to start Xorg(1) server with
-listen tcp
option starting from version 1.17 ; - $DISPLAY variable not strictly needed : if you attach your jail(8) using iocage(8), you need to set it. Otherwise, you can skip this step. The initial text that just followed the jailme() script in the jailme section was :
You can now use it to access to a shell on the jail(8) and set the $DISPLAY variable so the jail(8) uses the host X server.% injail /bin/tcsh
. From there, edit the ~/.cshrc file by adding the following line
Now the jail(8) will use the X server of the host. Note that those settings assume you use tcsh(1) or csh(1). If you decide to use another shell, you should set your variable in the corresponding setting file. Note also that you can set this variable system wide by editing the /etc/csh.cshrc file.Code:setenv DISPLAY 10.0.0.254:0.0
Last edited by a moderator: