prd online

Personal space in cyber.


FreeBSD: Services in jails

(modified )

This uses my jail setup from previous post.

Nginx web proxy

SSL

Using Letsencrypt, install the certbot “standalone” client:

# pkg install py36-certbot

Close down nginx, so certbot can bind to 80/443 for obtaining the certs:

# service nginx stop

Get your certificates:

# certbot-3.6 certonly --standalone -d example.org [-d www.example.org]
# certbot-3.6 certonly --standalone -d another.com [-d www.another.com]

Certs and keys are stored in /usr/local/etc/letsencrypt/live/example.org.

Edit nginx config (/usr/local/etc/nginx/nginx.conf) to use the certs for your new domains:

server {
    listen 80;
    listen 443 ssl;
    server_name www.example.org example.org;
    ssl_certificate /usr/local/etc/letsencrypt/live/example.org/fullchain.pem;
    ssl_certificate_key /usr/local/etc/letsencrypt/live/example.org/privkey.pem;
    if ($scheme != "https") {
        return 301 https://$host$request_uri;
    }
    location / {
        root /mnt/example.org;
        index index.html index.htm;
    }
}

Start nginx and browse securely:

# service nginx start

To renew all certs automatically and non-interactive, quietly run certbot renew every night with hooks for shutting down the http server (and let certbot bind to the ports) and starting it once finished. The hooks will not run, unless there is a need for renewal.

# crontab -e
0 0 * * * /usr/local/bin/certbot-3.6 renew --quiet --pre-hook 'service nginx stop' --post-hook 'service nginx start'

SSL Client side

In case you have some cool scripts or web apps running, performing client requests by themselves, you probably need to install root certificates for that chain of trust to work:

# pkg install ca_root_nss

Proxy

I use the nginx server to proxy requests to some services in other jails, instead of exposing more ports in my firewall.

This is a typical proxy in /usr/local/etc/nginx/nginx.conf:

server {
    listen 80;
    server_name service.example.com;
    location / {
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_set_header   Host             $host;

        proxy_pass http://10.0.1.94:3000/;
    }
}

See nginx documentation for fine tuning.

CGI

To have CGI enabled in nginx, install fcgiwrap:

# pkg install fcgiwrap

Configure fcgiwrap to run as www user, so nginx can communicate via UNIX sockets.

# vi /etc/rc.conf

 fcgiwrap_enable=YES
 fcgiwrap_user=www
 fcgiwrap_group=www
 fcgiwrap_socket_owner=www
 fcgiwrap_socket_group=www

Start the service:

# service fcgiwrap start

cgit

I install this in the same jail as my nginx web service, since it is a CGI script. This requires the fcgiwrap part from the nginx installation.

 # pkg install cgit

cgit gets installed into /usr/local/www/cgit.

The file /usr/local/etc/cgitrc is read by cgit before handling a request (documentation).

Create it if it does not exist already, /usr/local/etc/cgitrc:

virtual-root=/
root-title=vty.se
root-desc=git repos from prd
css=/cgit.css
logo=

clone-url=https://git.vty.se/$CGIT_REPO_URL
snapshots=tar.gz tar.bz2 zip

enable-log-filecount=1
enable-log-linecount=1

mimetype.gif=image/gif
mimetype.html=text/html
mimetype.jpg=image/jpeg
mimetype.jpeg=image/jpeg
mimetype.pdf=application/pdf
mimetype.png=image/png
mimetype.svg=image/svg+xml

about-filter=/usr/local/bin/cgit-about.sh
readme=:README.md
readme=:readme.md
readme=:README.txt
readme=:readme.txt
readme=:README
readme=:readme
readme=:INSTALL.md
readme=:install.md
readme=:INSTALL.txt
readme=:install.txt
readme=:INSTALL
readme=:install

scan-path=/var/git

Create the about-filter script, /usr/local/bin/cgit-about.sh. This will wrap every readme above in a <pre> tag for proper white-space rendering and replace every < with its HTML entity to avoid breaking the page.

#!/bin/sh

echo "<pre>"
cat $- | sed 's/</\&lt;/g'
echo "</pre>"

Make the script executable:

# chmod +x /usr/local/bin/cgit-about.sh

Create the git repos folder. The repos will be mounted from outside the jail in my case:

# mkdir /var/git

Because of how I have the jails setup, the actual directory will be stored in the thinjail, which gets mounted in /skeleton from the jail’s point of view. And since var is one of those symlinked folders to keep it away from the base, the actual dir will be found in /skeleton/var/git from within the jail.

Edit and add in /usr/local/jails/httpd.fstab (outside the jail):

/data/git/pub     /usr/local/jails/httpd/skeleton/var/git nullfs  ro  0 0

Finally, edit and update the http section in /usr/local/etc/nginx/nginx.conf:

server {
    listen 80;
    listen 443 ssl;
    server_name git.vty.se;
    ssl_certificate /usr/local/etc/letsencrypt/live/vty.se/fullchain.pem;
    ssl_certificate_key /usr/local/etc/letsencrypt/live/vty.se/privkey.pem;
    if ($scheme != "https") {
        return 301 https://$host$request_uri;
    }

    root /usr/local/www/cgit;
    try_files $uri @cgit;

    location @cgit {
          include fastcgi_params;
          fastcgi_param SCRIPT_FILENAME /usr/local/www/cgit/cgit.cgi;
          fastcgi_param PATH_INFO $uri;
          fastcgi_param QUERY_STRING $args;
          fastcgi_param HTTP_HOST $server_name;
          fastcgi_pass unix:/var/run/fcgiwrap/fcgiwrap.sock;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/local/www/nginx-dist;
    }
}

Plex media server

Kudos to https://nbari.com/post/plex-jail/.

For downloading metadata, Plex uses 127.0.0.1. Therefore the jail needs to have a loopback interface.

Create a lo1 interface. Add to rc.conf:

cloned_interfaces="lo1"

Add a made-up loopback IP in /etc/jail.conf for the appropriate jail:

plex {
    ip4.addr  = lo1|127.0.92.1;
    ip4.addr += em0|10.0.1.92;
}

This will add 127.0.92.1 as the main IP. This works as a specific setting for this jail, even though there is a lot of global stuff for the other jails.

Inside a jail, access to the loopback address 127.0.0.1 is redirected to the first IP address assigned to the jail. To make the jail loopback correspond with the new lo1 interface, that interface must be specified first in the list of interfaces and IP addresses given when creating a new jail.

Finally, edit /etc/hosts inside the jail so that localhost is mapped correctly:

127.0.92.1       localhost  localhost.your.domain

Profit.

Transmission torrent server

Create a new jail as described in previous post.

Mount external folder into the jail by appending to /usr/local/jails/transmission.fstab:

/data/torrents     /usr/local/jails/transmission/mnt nullfs  rw  0 0

Install transmission:

# pkg install transmission-daemon transmission-web

Take note of the group ID given to the group name transmission by the installation. In my case, it was 921.

Add the same group name with the same ID in the host machine for managing your data:

# pw groupadd transmission -g 921
# chgrp -R transmission /data/torrents

Start the transmission daemon upon jail startup (back inside the jail):

# echo 'transmission_enable="YES"' >> /etc/rc.conf
# service transmission start

Shut it down and configure transmission-daemon to allow incoming connections from other than localhost, by requiring RPC authentication and disable RPC whitelist. Change the umask from 18 to 2 (which will give rw to owner and group).

# service transmission stop

/usr/local/etc/transmission/home/settings.json:

"download-dir": "/mnt",
"rpc-authentication-required": true,
"rpc-bind-address": "0.0.0.0",
"rpc-password": "XXXXXXXXXXXXXXXXXXX",
"rpc-port": 9091,
"rpc-url": "/",
"rpc-username": "prd",
"rpc-whitelist-enabled": false,
"umask": 2,

To properly set the download-dir, add it as a flag to the transmission-daemon:

/etc/rc.conf:

transmission_download_dir="/mnt"

Start:

# service transmission start

Browse to http://a.b.c.d/web/ (remember the trailing slash) and start downloading/seeding!

mstream music server

A streaming music player written in node, https://mstream.io.

Create a new jail. Mount any music directories you would like inside, by editing /usr/local/jails/<jail>.fstab. Start the jail and enter it.

Install mstream using git and node package manager:

# pkg install git npm
# cd /usr/local/
# git clone https://github.com/IrosTheBeggar/mStream.git
# cd mStream
# npm install
# npm link

Run the setup wizard:

# mstream --wizard

Start the server and try it out:

# mstream -j /usr/local/mStream/save/default.json

Move the config file to /usr/local/etc/ for consistency:

# mv /usr/local/mStream/save/default.json /usr/local/etc/mstream.conf

Create a new service shell script, pointing to your configuration.

/usr/local/etc/rc.d/mstream:

#!/bin/sh
#
# PROVIDE: mstream
# REQUIRE: LOGIN FILESYSTEMS
# KEYWORD: shutdown

. /etc/rc.subr

name=mstream
rcvar=mstream_enable

command="/usr/local/bin/mstream"

load_rc_config $name

# Set PATH so mstream can find node
PATH=$PATH:/usr/local/bin

#
# DO NOT CHANGE THESE DEFAULT VALUES HERE
# SET THEM IN THE /etc/rc.conf FILE
#
mstream_enable=${mstream_enable-"NO"}
pidfile=${mstream_pidfile-"/var/run/mstream.pid"}

run_rc_command "$1"

Make executable:

# chmod +x /usr/local/etc/rc.d/mstream

Enable in /etc/rc.conf and set the configuration flag:

mstream_enable="YES"
mstream_flags="-j /usr/local/etc/mstream.conf"

Reboot the jail. Since mstream doesn’t seem to have a real background daemon setting, it will eat your terminal if you service start mstream. Do it to verify that everything is working, but reboot if you would like it running in the background. It will eat your terminal if you jail -c mstream as well. I just close my tmux pane and carry on with my life…

This is something of an ugly hack, but it works for now…