Bubblewrap¶
Bubblewrap is a lightweight sandbox application developed from Flatpak with a small installation footprint and minimal resource requirements. See their wiki for details. It has been integrated into LiteSpeed Web Server.
Installing bubblewrap¶
You must install bubblewrap first to use it with LiteSpeed Web Server.
Warning
Versions of bubblewrap before 0.3.1 do not support --ro-bind-try
, which is one of the defaults with LiteSpeed. So even if you have it installed, verify the version using bwrap --version
. If it's not 0.3.1 or higher follow the instructions below to upgrade it.
Ubuntu 20.04¶
sudo apt install bubblewrap
CentOS 7¶
sudo yum localinstall -y https://dl.fedoraproject.org/pub/archive/epel/testing/7.7/x86_64/Packages/b/bubblewrap-0.3.3-2.el7.x86_64.rpm
CentOS 8¶
sudo dnf install -y https://ewr.edge.kernel.org/centos/8-stream/BaseOS/x86_64/os/Packages/bubblewrap-0.4.0-1.el8.x86_64.rpm
Ubuntu 16.04 and 18.04 and Similar Releases¶
We strongly recommend you have a supported version 0.3.3 or higher. With Ubuntu 16.04 and 18.04 you will need to build it. The steps to do it are:
sudo apt install pkg-config libcap-dev automake
git clone https://github.com/containers/bubblewrap.git
cd bubblewrap
git checkout v0.4.1
./autogen.sh
make
sudo make install
sudo ln -s /usr/local/bin/bwrap /bin/bwrap
sudo mv /usr/bin/bwrap /usr/bin/bwrap.dist
sudo ln -s /usr/local/bin/bwrap /usr/bin/bwrap
Configure LiteSpeed Web Server for bubblewrap¶
Once installed you must configure LiteSpeed Web Server to use it. In the LiteSpeed Web Server Server Configuration > Security tab, Bubblewrap Container group there are two fields:
- Bubblewrap Container. It is required that bubblewrap be enabled at the virtual host level (and set to
Off
here)- Server Level
Disabled
+ Virtual Host LevelOn
=Off
- Server Level
On
+ Virtual Host LevelOff
=Off
- Server Level
Off
+ Virtual Host LevelOn
=On
- Server Level
- Bubblewrap Command. Lets you set the full bubblewrap command line used to start the CGI or PHP program.
If you set Bubblewrap Container On
and then do not set Bubblewrap Command it will use the default of:
/bin/bwrap --ro-bind /usr /usr --ro-bind /lib /lib --ro-bind-try /lib64 /lib64 --ro-bind /bin /bin --ro-bind /sbin /sbin --dir /var --ro-bind-try /var/www /var/www --dir /tmp --proc /proc --symlink ../tmp var/tmp --dev /dev --ro-bind-try /etc/localtime /etc/localtime --ro-bind-try /etc/ld.so.cache /etc/ld.so.cache --ro-bind-try /etc/resolv.conf /etc/resolv.conf --ro-bind-try /etc/ssl /etc/ssl --ro-bind-try /etc/pki /etc/pki --ro-bind-try /etc/man_db.conf /etc/man_db.conf --ro-bind-try /usr/local/bin/msmtp /etc/alternatives/mta --ro-bind-try /usr/local/bin/msmtp /usr/sbin/exim --bind-try $HOMEDIR $HOMEDIR --bind-try /var/lib/mysql/mysql.sock /var/lib/mysql/mysql.sock --bind-try /home/mysql/mysql.sock /home/mysql/mysql.sock --bind-try /tmp/mysql.sock /tmp/mysql.sock --bind-try /run/mysqld/mysqld.sock /run/mysqld/mysqld.sock --bind-try /var/run/mysqld/mysqld.sock /var/run/mysqld/mysqld.sock '$COPY-TRY /etc/exim.jail/$USER.conf $HOMEDIR/.msmtprc' --unshare-all --share-net --die-with-parent --dir /run/user/$UID '$PASSWD 65534' '$GROUP 65534'
This particular default is intended to give you a very secure environment to run your CGI or PHP scripts from.
LSWS Native Virtual Host¶
Enable Bubblewrap at the virtual host configuration level in the Security tab, Bubblewrap Container group. It is required that it be configured at the virtual host level to give you fine control of the domains that are being affected.
You must also run your virtual host in suEXEC mode. To enable suEXEC mode, in the virtual host configuration level in the Basic tab, Security group, you must specify External App Set UID Mode to Doc Root UID
and you control the user the application runs as by modifying the user that owns the file you are executing (PHP or CGI script).
Besides the normal requirement of doing a restart of LiteSpeed after a configuration change, if your application uses PHP, you will need to configure a virtual host level PHP handler and kill any running lsphp
instances after any configuration change.
Apache Virtual Host¶
For Apache virtual hosts in a control panel environment, you can control bubblewrap by adding the following to the virtual host's Apache configuration file:
<IfModule LiteSpeed>
BubbleWrap on|off
</IfModule>
Bubblewrap automatically creates an isolated environment for the current user associated with the site. Only the user's home directory is visible.
Is it working?¶
The easiest way to tell if bubblewrap has negatively affected your environment is to run the phpinfo.php
program which will show you whether a PHP script will run. Of course you can run your application as well.
You will want to know not just whether it has broken your program, but also if it's providing any additional protection. One way to do that would be to create a simple script, we've named dirlist.php
which you place in a location where a browser can be used to run it, in /usr/local/lsws/DEFAULT/html
if you are using the default configuration:
<?php
$dir = '/';
$files1 = scandir($dir);
print_r($files1);
?>
When you load it in your browser you should see a very small number of entries, just the directories you specified mounted from the root:
Array ( [0] => . [1] => .. [2] => bin [3] => dev [4] => etc [5] => lib [6] => lib64 [7] => proc [8] => run [9] => sbin [10] => tmp [11] => usr [12] => var )
Set Bubblewrap Container Off
, stop and restart LiteSpeed and you might see something much larger (an example, yours will be different):
Array ( [0] => . [1] => .. [2] => bin [3] => bob [4] => boot [5] => cdrom [6] => dev [7] => etc [8] => home [9] => lib [10] => lib32 [11] => lib64 [12] => libx32 [13] => lost+found [14] => media [15] => mnt [16] => opt [17] => proc [18] => root [19] => run [20] => sbin [21] => snap [22] => srv [23] => swapfile [24] => sys [25] => tmp [26] => usr [27] => var )
Manual Configuration¶
The bubblewrap configuration is in the Security group. Note that virtual host configuration overrides this value, unless it it disabled here.
- bubbleWrap set to
0
to disable (the default or not set value),1
to turn off at this level,2
to turn on at this level. It is required that bubblewrap be configured enabled at the virtual host level (set to 1 or 2 here). - bubbleWrapCmd is optional and can be set to the fully qualified and specified bubblewrap command line. The example above is the default. A very simple one might be
/bin/bwrap --dev-bind / /
.
At the Virtual Host configuration level, the option is:
- bubbleWrap set to
0
to not set, which is the default and indicates to not use bubblewrap;1
is to turn off at this level and2
is to turn on at this level. It is in the Security configuration file group.
Customizing¶
To create a customized bubblewrap environment, you will want to know the following:
- All parameters are much like the command line when run from bash, space separated parameters as documented.
- The first parameter must always be the exact location of the bubblewrap executable. You must change the default if it is not installed in /bin/bash or /usr/bin/bwrap in your environment.
- You must change the default if it is missing access to Unix Domain Sockets that you may need to access your databases (other than the default for mysql).
- The current user's home directory can be specified with the token
$HOMEDIR
and is used in the default. - The current user name can be specified with the token
$USER
. - The current user ID can be specified with the token
$UID
. - The current group ID can be specified with the token
$GID
. - A virtual file /etc/passwd can be created with the token
$PASSWD
with command line parameters of the users you wish to preserve (by ID). By default the current effective user ID is the only entry; you can add others by specifying the numbers, after the entry, quoted. For example:'$PASSWD 65534’
. It is used in the default. - A virtual file /etc/group can be created with the token
$GROUP
with command line parameters of the groups you wish to preserve (by ID). By default the current effective group ID is the only entry; you can add others by specifying the numbers, after the entry, quoted. For example:‘$GROUP 65534’
. It is used in the default. - If you wish you can copy a file from a source to a destination in the new namespace with
$COPY
(which requires the source to exist), or$COPY-TRY
(which will not fail the operation if the source doesn't exist). The operator,$COPY
or$COPY-TRY
will copy the entire file from the source to the destination. The operator must be specified as a single quoted parameter, separating the token from the source with a space and the source from the destination with a space. You can use the tokens above ($USER
,$HOMEDIR
, etc.) in the operation. Thus the default of'$COPY-TRY /etc/exim.jail/$USER.conf $HOMEDIR/.msmtprc'
will copy/etc/exim.jail/(user name).conf
(if it exists) to a file at the root of the user's home directory named.msmtprc
. - All space separated parameters are assumed to be individual parameters. If you need to embed a space in a parameter (like for a space in a directory name), you can enclose the parameter in single quotes:
'parameter'
or double quotes:"parameter"
. - Any attempt to run a shell from within bwrap will be denied.
- We always recommend that you specify the option
--die-with-parent
so that you do not have orphaned tasks.
Troubleshooting¶
As always, the best places to look for resolution to bubblewrap problems are the error.log
and stderr.log
in the LiteSpeed logs directory.
Note
After every configuration change, if your application uses PHP, you will need to kill lsphp
so it does not continue using the old configuration.
Note
If you are using caching, you may see an error like: wp-cache.php is not writable
preceded by a directory. If this is the case, then you will need to note that directory and create a customized Bubblewrap Command that includes a --bind
, followed by the directory twice (source and target) for that directory.
In some cases it can be quite difficult to troubleshoot a bubblewrap problem because the application may not be able to properly report an error after a successful launch of bubblewrap. One tool that can be useful is nsenter
, which allows you to enter the environment where the namespace is running.
The steps to use nsenter
are (as root):
- Find the process which is running the bwrap process:
ps -ef|grep bwrap
. Often it islsphp
, and often it is the second one running bwrap. The process ID is in the second column. - Enter that environment. For example if the pid is 107263 you would specify:
nsenter --mount=/proc/107263/ns/mnt --uts=/proc/107263/ns/uts --ipc=/proc/107263/ns/ipc --pid=/proc/107263/ns/pid
- An example session: find the process, enter it, list the contents of
/etc/passwd
:# ps -ef|grep bwrap nobody 107262 107205 0 13:38 ? 00:00:00 /bin/bwrap --ro-bind /usr /usr --ro-bind /lib /lib --ro-bind-try /lib64 /lib64 --ro-bind /bin /bin --ro-bind /sbin /sbin --dir /var --ro-bind-try /var/www /var/www --dir /tmp --proc /proc --symlink ../tmp var/tmp --dev /dev --ro-bind-try /etc/localtime /etc/localtime --ro-bind-try /etc/ld.so.cache /etc/ld.so.cache --ro-bind-try /etc/resolv.conf /etc/resolv.conf --ro-bind-try /etc/ssl /etc/ssl --ro-bind-try /etc/pki /etc/pki --ro-bind-try /etc/man_db.conf /etc/man_db.conf --ro-bind-try /home/nobody /home/nobody --bind-try /var/lib/mysql/mysql.sock /var/lib/mysql/mysql.sock --bind-try /home/mysql/mysql.sock /home/mysql/mysql.sock --bind-try /tmp/mysql.sock /tmp/mysql.sock --bind-try /run/mysqld/mysqld.sock /run/mysqld/mysqld.sock --unshare-all --share-net --die-with-parent --dir /run/user/65534 --file 3 /etc/passwd --file 4 /etc/group /usr/local/lsws/lsphp74/bin/lsphp nobody 107263 107262 0 13:38 ? 00:00:00 /bin/bwrap --ro-bind /usr /usr --ro-bind /lib /lib --ro-bind-try /lib64 /lib64 --ro-bind /bin /bin --ro-bind /sbin /sbin --dir /var --ro-bind-try /var/www /var/www --dir /tmp --proc /proc --symlink ../tmp var/tmp --dev /dev --ro-bind-try /etc/localtime /etc/localtime --ro-bind-try /etc/ld.so.cache /etc/ld.so.cache --ro-bind-try /etc/resolv.conf /etc/resolv.conf --ro-bind-try /etc/ssl /etc/ssl --ro-bind-try /etc/pki /etc/pki --ro-bind-try /etc/man_db.conf /etc/man_db.conf --ro-bind-try /home/nobody /home/nobody --bind-try /var/lib/mysql/mysql.sock /var/lib/mysql/mysql.sock --bind-try /home/mysql/mysql.sock /home/mysql/mysql.sock --bind-try /tmp/mysql.sock /tmp/mysql.sock --bind-try /run/mysqld/mysqld.sock /run/mysqld/mysqld.sock --unshare-all --share-net --die-with-parent --dir /run/user/65534 --file 3 /etc/passwd --file 4 /etc/group /usr/local/lsws/lsphp74/bin/lsphp root 107324 106866 0 13:40 pts/0 00:00:00 grep --color=auto bwrap # nsenter --mount=/proc/107263/ns/mnt --uts=/proc/107263/ns/uts --ipc=/proc/107263/ns/ipc --pid=/proc/107263/ns/pid -bash-5.0# cat /etc/passwd nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
- In Debian (or other distributions), you may see in
stderr.log
the error:We've seen conditions where user namespaces do not function correctly with bubblewrap in these environments and since user namespaces provide minimal protection anyway, you should use a custom bubblewrap configuration which does not do an[STDERR] bwrap: setting up uid map: No such file or directory
unshare-all
, and enumerates the options you wish to use, not including--unshare-user
. For example:/usr/bin/bwrap --ro-bind /usr /usr --ro-bind /lib /lib --ro-bind-try /lib64 /lib64 --ro-bind /bin /bin --ro-bind /sbin /sbin --dir /var --ro-bind-try /var/www /var/www --dir /tmp --proc /proc --symlink ../tmp var/tmp --dev /dev --ro-bind-try /etc/localtime /etc/localtime --ro-bind-try /etc/ld.so.cache /etc/ld.so.cache --ro-bind-try /etc/resolv.conf /etc/resolv.conf --ro-bind-try /etc/ssl /etc/ssl --ro-bind-try /etc/pki /etc/pki --ro-bind-try /etc/man_db.conf /etc/man_db.conf --ro-bind-try /usr/local/bin/msmtp /etc/alternatives/mta --ro-bind-try /usr/local/bin/msmtp /usr/sbin/exim --bind-try $HOMEDIR $HOMEDIR --bind-try /var/lib/mysql/mysql.sock /var/lib/mysql/mysql.sock --bind-try /home/mysql/mysql.sock /home/mysql/mysql.sock --bind-try /tmp/mysql.sock /tmp/mysql.sock --bind-try /run/mysqld/mysqld.sock /run/mysqld/mysqld.sock --bind-try /var/run/mysqld/mysqld.sock /var/run/mysqld/mysqld.sock '$COPY-TRY /etc/exim.jail/$USER.conf $HOMEDIR/.msmtprc' --unshare-ipc --unshare-pid --unshare-uts --dir /run/user/$UID '$PASSWD 65534' '$GROUP 65534'