Valid XHTML Icon Valid CSS Icon Valid SVG Icon Unicode UTF-8 Icon
Add to del.icio.us Digg this! Post to reddit Share on Facebook Add to StumbleUpon Add to Google Bookmarks

FreeBSD 10.1 Guide

Creative Commons LogoThis work is licensed under the Creative Commons Attribution Non-Commercial 2.0 UK: England & Wales Licence. This means that you are free to: copy; distribute; and modify this work. It also means that you cannot use it for commercial purposes. Additionally, you must attribute this work to the original author, Thomas Guymer, ideally with a link.

FreeBSD LogoThis is an installation and configuration guide for FreeBSD. I now use FreeBSD for my server which acts both as a NAS for all of my data and as a media server for my TV. I have managed to add some pretty neat features such as a Time Capsule for my MacBook and a PVR for BBC iPlayer. This guide describes the FreeBSD installation, ZFS configuration and user-land software installation and configuration. I include all of my configuration files so that you can see how I have set it all up.

  1. Installation Notes
  2. Configuration Notes
    1. General Setup
    2. Adding Users
    3. Installing Software
    4. Configuring Cron
    5. Configuring Software
      1. Time Machine
      2. Web Server
      3. Media Servers
      4. Samba
      5. SSH
      6. SMART Daemon
  3. Workarounds
  4. Usage Notes
  5. References
  6. Word Of Warning

§1 Installation Notes

I used a bootable USB image to install FreeBSD, you can grab one from the FreeBSD download page. During the first section of the installer I disabled "games" and "ports". The automatic partitioner decided to go with:

Device Size Type Mount Point
ada0 149GB
ada0p1 64KB freebsd-boot
ada0p2 117GB freebsd-ufs /
ada0p3 32GB freebsd-swap none

Further on in the process I decided to disable IPv6 and "dumpdev". Once the process finished I decided to not immediately reboot into the new system, instead I opened a shell and typed gpart set -a active ada0 to set the drive as active before the first boot (which was needed to side-step a nasty bug which meant that you couldn't boot into your nice new system).

§2 Configuration Notes

Now you should have a working fresh installation of FreeBSD. The majority of this tutorial covers the configuration of this fresh clean system. Firstly I talk about how I configured FreeBSD itself, then I add users, then I install user-land software, and finally I configure that user-land software.

§2.1 General Setup

Firstly, I tweaked the start-up configuration file,"/boot/loader.conf" , so that some additional kernel modules would be loaded during start-up. These modules allow the CPU temperature to be obtained (for my Intel CPUs), enable ACPI (for my ASUS motherboard) and finally enable disk encryption to be used (if the user wants). I did this with the following commands (as root) - the changes do not take effect until the system is rebooted, hence the final command.

echo 'coretemp_load="YES"' >> /boot/loader.conf
echo 'acpi_asus_load="YES"' >> /boot/loader.conf
echo 'geom_eli_load="YES"' >> /boot/loader.conf
reboot

Next, I added some lines to the other start-up configuration file so that some additional software would be loaded during start-up. The "/etc/rc.conf" file is very important in FreeBSD as it contains most of the custom configuration for my system. The commands (as root) I used are below - likewise, the changes do not take effect until the system is rebooted, however, I do not reboot immediately as some further tweaking can be performed during this boot.

The first command enables ZFS to be used (if the user wants); the second sets the default encryption settings for GELI; the third enables the inet daemon (which is often called the "super server"); the fourth clears "/tmp" during boot; the fifth enables the daemon to automatically mount removeable storage (such as USB drives); and the final command enables memory disks (a.k.a. RAM disks).

echo 'zfs_enable="YES"' >> /etc/rc.conf
echo 'geli_default_flags="-a HMAC/SHA256 -l 256 -s 4096"' >> /etc/rc.conf
echo 'inetd_enable="YES"' >> /etc/rc.conf
echo 'clear_tmp_enable="YES"' >> /etc/rc.conf
echo 'autofs_enable="YES"' >> /etc/rc.conf
echo 'md_enable="YES"' >> /etc/rc.conf

Next I configured the system to automatically mount removeable storage (such as USB drives). The instructions for this can be found in the section on USB disks in the FreeBSD handbook. My only comment is that you don't have to add all the lines to "/etc/devd.conf" as they are already there, just move the */ line further up the file and then they will not be commented out anymore.

# Enable mounting in /media
vi /etc/auto_master
    un-comment "/media     -media      -nosuid"
# Set up mounting
vi /etc/devd.conf
    un-comment the block:
        notify 100 {
            match "system" "GEOM";
            match "subsystem" "DEV";
            action "/usr/sbin/automount -c";
        };

Before rebooting I edited "/etc/fstab" to create a RAM disk (to hold temporary copies of sensitive un-encrypted data) and to encrypt the swap partition. This final step ensures that any sensitive data that is written to swap (because the RAM is full) is unrecoverable if the machine is powered off (just like the data in RAM). Of course, the swap has no knowledge of which data it stores is sensitive and which isn't: this method encrypts the whole partition. The downside to this is that your machine cannot be hibernated (which requires reading the swap during wake-up) or debugged completely (as memory dumps are sometimes written to swap). I believe that these two hindrances are acceptable.

# Make mount target
mkdir /mnt/volatile
# Create both file-systems
echo "md    /mnt/volatile    mfs    rw,-s8M    0    0" >> /etc/fstab
vi /etc/fstab
    add ".eli" to end of /dev/ada0p3
# Setup "vim"
echo "syntax on" > ~/.vimrc
# Reboot
reboot

Next I cleaned the beginning of all of my data disks that were going to be my ZFS array. WARNING: This will make any existing data on the drive inaccessible. I then created the pool and created some useful partitions on the pool. You'll notice that I enabled LZ4 compression and set the flags for snapshots on all partitions except "timecapsule" (which has its own snapshots in Mac OS X). The "autoexpand=on" line is needed if in future I decide to replace the disks (one-by-one) with larger ones.

# Remove partition table from the disk
gpart destroy -F /dev/ada1
gpart destroy -F /dev/ada2
gpart destroy -F /dev/ada3
gpart destroy -F /dev/ada4
gpart destroy -F /dev/ada5
# Clean the disks
dd if=/dev/zero of=/dev/ada1 bs=1m count=10
dd if=/dev/zero of=/dev/ada2 bs=1m count=10
dd if=/dev/zero of=/dev/ada3 bs=1m count=10
dd if=/dev/zero of=/dev/ada4 bs=1m count=10
dd if=/dev/zero of=/dev/ada5 bs=1m count=10
# Create the ZFS pool
zpool create pool1 raidz /dev/ada1 /dev/ada2 /dev/ada3 /dev/ada4 /dev/ada5
# Enable auto-expansion of the ZFS pool
zpool set autoexpand=on pool1
# Enable transparent compression
zfs set compression=lz4 pool1
# Create partitions
zfs create -p pool1/home
zfs create -p pool1/mnt/data
zfs create -p pool1/mnt/timecapsule
zfs create -p pool1/usr/local/share/doc
zfs create -p pool1/usr/local/www/thomasguymer.co.uk
zfs create -p pool1/usr/ports
zfs create -p pool1/usr/src
zfs create -p pool1/var/db
zfs create -p pool1/var/log
zfs create -p pool1/var/mail
# Mark them for snapshots
zfs set com.sun:auto-snapshot=true pool1/home
zfs set com.sun:auto-snapshot=true pool1/mnt/data
zfs set com.sun:auto-snapshot=true pool1/usr/local/share/doc
zfs set com.sun:auto-snapshot=true pool1/usr/local/www/thomasguymer.co.uk
zfs set com.sun:auto-snapshot=true pool1/usr/ports
zfs set com.sun:auto-snapshot=true pool1/usr/src
zfs set com.sun:auto-snapshot=true pool1/var/db
zfs set com.sun:auto-snapshot=true pool1/var/log
zfs set com.sun:auto-snapshot=true pool1/var/mail

Next I mounted the partitions so that they could be used straight away (whilst keeping copies of any data in the two targets that already existed).

# Move already existing versions
mv /usr/local/share/doc /usr/local/share/TEMPdoc
mv /var/log /var/TEMPlog
# Make mount targets
mkdir /home
mkdir /mnt/data
mkdir /mnt/timecapsule
mkdir /usr/local/share/doc
mkdir /usr/local/www/thomasguymer.co.uk
mkdir /usr/ports
mkdir /var/db
mkdir /var/log
# Mount the partitions
zfs set mountpoint=/home pool1/home
zfs set mountpoint=/mnt/data pool1/mnt/data
zfs set mountpoint=/mnt/timecapsule pool1/mnt/timecapsule
zfs set mountpoint=/usr/local/share/doc pool1/usr/local/share/doc
zfs set mountpoint=/usr/local/www/thomasguymer.co.uk pool1/usr/local/www/thomasguymer.co.uk
zfs set mountpoint=/usr/ports pool1/usr/ports
zfs set mountpoint=/usr/src pool1/usr/src
zfs set mountpoint=/var/db pool1/var/db
zfs set mountpoint=/var/log pool1/var/log
zfs set mountpoint=/var/mail pool1/var/mail
# Move already existing files into new versions
mv /usr/local/share/TEMPdoc/* /usr/local/share/doc/
mv /var/TEMPlog/* /var/log/
# Remove old versions
rmdir /usr/local/share/TEMPdoc
rmdir /var/TEMPlog
# Reboot
reboot

I like using BASH as my shell, which is not included in the default install of FreeBSD. I installed BASH using the port system, which means that I had to set up the port system first. The following command (as root) will download the port tree and extract it to the local directory.

portsnap fetch extract

Once that is done I created the "/etc/make.conf" file so that all ports were installed using the settings I wished. Use the Vi editor (by typing the command vi) to create the file and make it look like mine below.

# Set build options
BUILD_OPTIMIZED=yes
CPUTYPE=native
NO_INET6=yes
WITH_PKGNG=yes

# Set configure options
OPTIONS_SET=AVX AVX2 ICONV MANPAGES MMX OPENGL OPTIMIZATION OPTIMIZED_CFLAGS OPTIMIZED_FLAGS SIMD SSE SSE2 THREADS UNICODE X11
OPTIONS_UNSET=DEBUG DOCS EXAMPLES HTMLDOCS IPV6 PHP TEST

Finally, I installed BASH by typing make -C /usr/ports/shells/bash config-recursive first to configure it (deselect "implicitcd" when the first configuration screen appears) and then make -C /usr/ports/shells/bash install to actually install it. BASH will not work straight away as it needs two specific file-systems to exist. You can make them with appear at boot with the following two commands.

echo "fdesc    /dev/fd    fdescfs    rw    0    0" >> /etc/fstab
echo "proc    /proc    procfs    rw    0    0" >> /etc/fstab

BASH should now be there ready for you to use - after you do a reboot.

§2.2 Adding Users

I created all of the infrastructure for users to use the ZFS file-system, have a data store and a Time Capsule. This resulted in quite a few ZFS commands, but hopefully they will all make sense (and most are duplicates).

# Create their home
zfs create pool1/home/user1
zfs create pool1/home/user2
zfs create pool1/home/user3
zfs create pool1/home/user4
# Limit their home
zfs set quota=100G pool1/home/user1
zfs set quota=100G pool1/home/user2
zfs set quota=100G pool1/home/user3
zfs set quota=100G pool1/home/user4
# Create their data store
zfs create pool1/data/user1
zfs create pool1/data/user2
zfs create pool1/data/user3
zfs create pool1/data/user4
# Create their time machine store
zfs create pool1/timecapsule/user1
zfs create pool1/timecapsule/user2
zfs create pool1/timecapsule/user3
zfs create pool1/timecapsule/user4

Note that there is no need to put quotas on the Time Capsules as they are handled in the configuration file later on and work quite well. Users are added using the aptly named adduser command. Once that is done I modified their groups and the permissions of their directories.

# add all users to "staff"
pw groupmod staff -m user1,user2,user3,user4
# add "user1" to "wheel"
pw groupmod wheel -m user1
# add SFTP-only users to custom group
pw groupadd sftp_only -m user3,user4
# forbid "user4" from logging in
pw usermod user4 -s /usr/sbin/nologin
# Set secure umasks
echo umask=0077 > /home/user1/.profile
echo umask=0077 > /home/user2/.profile
echo umask=0077 > /home/user3/.profile
echo umask=0077 > /home/user4/.profile
# Setup "vim"
echo "syntax on" > /home/user1/.vimrc
echo "syntax on" > /home/user2/.vimrc
echo "syntax on" > /home/user3/.vimrc
echo "syntax on" > /home/user4/.vimrc
# Make users own their folders
chown user1:staff /home/user1 /mnt/data/user1 /mnt/timecapsule/user1
chown user2:staff /home/user2 /mnt/data/user2 /mnt/timecapsule/user2
chown user3:staff /home/user3 /mnt/data/user3 /mnt/timecapsule/user3
chown user4:staff /home/user4 /mnt/data/user4 /mnt/timecapsule/user4
# Make private folders private
chmod 700 /home/user1 /home/user2 /home/user3 /home/user4
chmod 700 /mnt/timecapsule/user1 /mnt/timecapsule/user2 /mnt/timecapsule/user3 /mnt/timecapsule/user4
# Make shared (parent) folders group read-only
chmod 755 /mnt/data/user1 /mnt/data/user2 /mnt/data/user3 /mnt/data/user4
# Make sure that users can get to their folders
chgrp staff /home /mnt /mnt/data /tmp

§2.3 Installing Software

The first thing that I did at this stage is to download the source code for FreeBSD. If you are just going to use this machine as a server or some such then you don't need to do this. However, if you intend on using it as a build platform for other FreeBSD machines (such as ARM boards) then you'll need the source code. You can grab it with the command svnlite co https://svn0.us-west.freebsd.org/base/head /usr/src/.

Next, I installed Portmaster as it is a handy tool for installing software from the ports tree. To install it you'll need to run the command make -C /usr/ports/ports-mgmt/portmaster install.

Now I start installing other software that I need, using Portmaster instead of Make. Below you'll find a list of all of the software that I have installed. I do each one in turn using the command portmaster -y foo/bar (i.e., portmaster -y editors/vim). Next to some of them is a description of what configuration settings I changed from default, some of them are configuration options for [grand-]daughters.

That will take a long time to run/wait for. Once it is all done I like to create a link to Python using this command cd /usr/local/bin && ln -s python2.7 python. I then append some lines to the end of "/etc/rc.conf" so that some of the recently installed software starts automatically. You can do this with the following commands.

# Enables SMART monitoring of hard drives
echo 'smartd_enable="YES"' >> /etc/rc.conf
# Enables the Time Capsules
echo 'netatalk_enable="YES"' >> /etc/rc.conf
echo 'cnid_metad_enable="YES"' >> /etc/rc.conf
echo 'afpd_enable="YES"' >> /etc/rc.conf
# Enable UPnP/DNLA
echo 'mediatomb_enable="YES"' >> /etc/rc.conf
echo 'mediatomb_flags="-i 192.168.1.110 -p 49152"' >> /etc/rc.conf
# Enable SMB
echo 'samba_enable="YES"' >> /etc/rc.conf
# Configure NTP
echo 'ntpd_sync_on_start="YES"' >> /etc/rc.conf
echo 'ntpd_hosts="uk.pool.ntp.org"' >> /etc/rc.conf
# Enable mail
echo 'sendmail_enable="YES"' >> /etc/rc.conf
# Enable DAAP
echo 'firefly_enable="YES"' >> /etc/rc.conf
# Enable web server
echo 'nginx_enable="YES"' >> /etc/rc.conf
# Enable protection of SSH
echo 'sshguard_enable="YES"' >> /etc/rc.conf
# HACK: Bug 174018 from https://www.freshports.org/security/sshguard
echo 'sshguard_blacklist=""' >> /etc/rc.conf

At this point it is probably worth doing a reboot just to check that everything is working OK for you. A lot of the daemons that have just been installed will probably not run because we have not configured them yet, but the fact that they print an error message means that they are at least trying to start up - which is good.

§2.4 Configuring Cron

Here is a copy of my Cron file, I think that it is fairly self-explanatory. All it does is correct permissions of user's private/shared folders, check for updates in both FreeBSD and the ports system; and finally it performs some maintenance of the ZFS pool.

# Define environment variables
LANG=en_GB.UTF-8
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/games:/usr/local/bin:/usr/local/sbin
SHELL=/usr/local/bin/bash

# Ensure user/group/permissions are correct for personal/shared folders
0 0 * * * /usr/bin/find /mnt/data/user2/ ! -user user2 -exec chown user2 {} \;
0 0 * * * /usr/bin/find /mnt/data/user2/ ! -group staff -exec chgrp staff {} \;
0 0 * * * /usr/bin/find /mnt/data/user2/ -type f ! -perm 600 -exec chmod 600 {} \;
0 0 * * * /usr/bin/find /mnt/data/user2/ -depth +0 -type d ! -perm 700 -exec chmod 700 {} \;
0 0 * * * /usr/bin/find /mnt/data/user3/ ! -user user3 -exec chown user3 {} \;
0 0 * * * /usr/bin/find /mnt/data/user3/ ! -group staff -exec chgrp staff {} \;
0 0 * * * /usr/bin/find /mnt/data/user3/ -type f ! -perm 600 -exec chmod 600 {} \;
0 0 * * * /usr/bin/find /mnt/data/user3/ -depth +0 -type d ! -perm 700 -exec chmod 700 {} \;
0 0 * * * /usr/bin/find /mnt/data/user4/ ! -user user4 -exec chown user4 {} \;
0 0 * * * /usr/bin/find /mnt/data/user4/ ! -group staff -exec chgrp staff {} \;
0 0 * * * /usr/bin/find /mnt/data/user4/ -type f ! -perm 644 -exec chmod 644 {} \;
0 0 * * * /usr/bin/find /mnt/data/user4/ -depth +0 -type d ! -perm 755 -exec chmod 755 {} \;

# Fetch updates for FreeBSD and Ports then clean the ports tree
0 0 * * * /usr/sbin/freebsd-update cron > /dev/null
0 0 * * * /usr/sbin/portsnap cron update > /dev/null
0 2 * * * /usr/local/sbin/portmaster -y --clean-distfiles > /dev/null
2 2 * * * /usr/local/sbin/portmaster -y --clean-packages > /dev/null
4 2 * * * /usr/local/sbin/portmaster --check-depends

# Provide ZFS usage statistics
0 0 * * * /sbin/zfs list -o name,used,avail,compressratio -r pool1/home > /home/USAGE.txt
0 0 * * * /sbin/zfs list -o name,used,avail,compressratio -r pool1/mnt/data > /mnt/data/USAGE.txt
0 0 * * * /sbin/zfs list -o name,used,avail,compressratio -r pool1/mnt/timecapsule > /mnt/timecapsule/USAGE.txt

# Scrub ZFS
0 0 * * 1 /sbin/zpool scrub pool1

# Create ZFS snapshots
0 0 * * 1-6 /usr/local/sbin/zfs-auto-snapshot daily  6
0 0 * *   0 /usr/local/sbin/zfs-auto-snapshot weekly 5

§2.5 Configuring Software

I have highlighted a few keys pieces of software (such as media servers and the like) to describe how I configured them for my needs.

§2.5.1 Time Machine

The settings for the Time Capsules are kept in "/usr/local/etc/AppleVolumes.default". Use your favourite editor (I use Vim) to change the file so that the un-commented lines look like the ones below. Now Mac computers should be able to see, and use, your FreeBSD machine as a Time Capsule.

:DEFAULT: options:upriv,usedots

/mnt/timecapsule/user1 Time Capsule-user1 options:tm volsizelimit:500000 allow:user1
/mnt/timecapsule/user2 Time Capsule-user2 options:tm volsizelimit:500000 allow:user2
/mnt/timecapsule/user3 Time Capsule-user3 options:tm volsizelimit:500000 allow:user3
/mnt/timecapsule/user4 Time Capsule-user4 options:tm volsizelimit:500000 allow:user4

§2.5.2 Web Server

Above I installed Nginx so that I could have a very light weight webserver on my local network. The settings for Nginx are in "/usr/local/etc/nginx/nginx.conf". Use your favourite editor (I use Vim) to change the file so that it looks like the one below.

# Basic settings
worker_processes            auto;

events {
    worker_connections          1024;
}

http {
    # Basic settings
    charset                     utf8;
    gzip_static                 on;
    server_tokens               off;
    sendfile                    off;
    tcp_nopush                  on;
    limit_conn_zone             $binary_remote_addr zone=addr:10m;

    # Buffers
    client_body_buffer_size     1k;
    client_header_buffer_size   1k;
    client_max_body_size        1k;
    large_client_header_buffers 2 1k;

    # Timeouts
    client_body_timeout         10;
    client_header_timeout       10;
    keepalive_timeout           5 5;
    send_timeout                10;

    # Define the default MIME types for files
    include                     mime.types;
    default_type                application/octet-stream;

    server {
        # Basic settings
        listen                      80;
        server_name                 www.thomasguymer.co.uk;
        if ($request_method !~ ^(GET|HEAD)$ ) {
            return                      444;
        }

        location / {
            root                        /usr/local/www/thomasguymer.co.uk/www;
            index                       index.html;
            limit_conn                  addr 5;
        }

        # List all available MIME types
        types {
            application/javascript      js;
            application/pdf             pdf;
            application/rss+xml         rss;
            application/xml             xml;
            application/zip             zip;
            image/x-icon                ico;
            image/jpeg                  jpg;
            image/png                   png;
            image/svg+xml               svg;
            text/css                    css;
            text/html                   html;
            text/plain                  log txt;
            text/x-suse-ymp             ymp;
        }
    }
}

§2.5.3 Media Servers

I have installed two separate media servers to serve my music and videos over different protocols: Firefly (a.k.a. mt-daapd) serves content over DAAP for Apple devices; and Mediatomb serves content over UPnP/DNLA for Android, Linux and Windows devices. The two configuration files for these pieces of software are "/usr/local/etc/firefly/mt-daapd.conf" and "/usr/local/etc/mediatomb/config.xml" respectively. Below I have included both configuration files - Firefly first and Mediatomb second.

[general]
web_root = /usr/local/share/mt-daapd/admin-root
port = 3689
admin_pw = PASSWORD
db_type = sqlite3
db_parms = /var/db/firefly
mp3_dir = /mnt/data/user1/Music
servername = HOST
runas = daapd
extensions = .mp3
logfile = /var/log/mt-daapd.log
debuglevel = 1
rescan_interval = 1800
scan_type = 2

[plugins]
plugin_dir = /usr/local/lib/mt-daapd/plugins

[scanning]
process_playlists = 0
process_itunes = 0
process_m3u = 0
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://mediatomb.cc/config/2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2" xsi:schemaLocation="http://mediatomb.cc/config/2 http://mediatomb.cc/config/2.xsd">
    <server>
        <ui enabled="yes" show-tooltips="yes">
            <accounts enabled="yes" session-timeout="30">
                <account user="mediatomb" password="PASSWORD"/>
            </accounts>
        </ui>
        <name>HOST</name>
        <udn>uuid:9dafbdf1-904a-4016-9af9-b0bc756c7537</udn>
        <home>/var/mediatomb</home>
        <webroot>/usr/local/share/mediatomb/web</webroot>
        <storage caching="yes">
            <sqlite3 enabled="yes">
                <database-file>mediatomb.db</database-file>
            </sqlite3>
            <mysql enabled="no">
                <host>localhost</host>
                <username>mediatomb</username>
                <database>mediatomb</database>
            </mysql>
        </storage>
        <protocolInfo extend="no"/>
        <custom-http-headers>
            <add header="transferMode.dlna.org: Streaming"/>
            <add header="contentFeatures.dlna.org: DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=017000 00000000000000000000000000"/>
        </custom-http-headers>
        <extended-runtime-options>
            <ffmpegthumbnailer enabled="yes">
                <thumbnail-size>128</thumbnail-size>
                <seek-percentage>5</seek-percentage>
                <filmstrip-overlay>no</filmstrip-overlay>
                <workaround-bugs>no</workaround-bugs>
                <image-quality>8</image-quality>
            </ffmpegthumbnailer>
            <mark-played-items enabled="no" suppress-cds-updates="yes"/>
        </extended-runtime-options>
    </server>
    <import hidden-files="no">
        <scripting script-charset="UTF-8">
            <common-script>/usr/local/share/mediatomb/js/common.js</common-script>
            <playlist-script>/usr/local/share/mediatomb/js/playlists.js</playlist-script>
            <virtual-layout type="builtin">
                <import-script>/usr/local/share/mediatomb/js/import.js</import-script>
            </virtual-layout>
        </scripting>
        <mappings>
            <extension-mimetype ignore-unknown="no">
                <map from="flac" to="audio/x-flac"/>
                <map from="flv" to="video/x-flv"/>
                <map from="m3u" to="audio/x-mpegurl"/>
                <map from="mp3" to="audio/mpeg"/>
                <map from="mp4" to="video/mp4"/>
                <map from="mpg" to="video/mpeg"/>
                <map from="pls" to="audio/x-scpls"/>
                <map from="wma" to="audio/x-ms-wma"/>
                <map from="wmv" to="video/x-ms-wmv"/>
            </extension-mimetype>
            <mimetype-upnpclass>
                <map from="audio/*" to="object.item.audioItem.musicTrack"/>
                <map from="image/*" to="object.item.imageItem"/>
                <map from="video/*" to="object.item.videoItem"/>
            </mimetype-upnpclass>
            <mimetype-contenttype>
                <treat mimetype="audio/mpeg" as="mp3"/>
                <treat mimetype="audio/x-flac" as="flac"/>
                <treat mimetype="audio/x-mpegurl" as="playlist"/>
                <treat mimetype="audio/x-scpls" as="playlist"/>
                <treat mimetype="audio/x-wav" as="pcm"/>
                <treat mimetype="image/jpeg" as="jpg"/>
                <treat mimetype="video/x-msvideo" as="avi"/>
            </mimetype-contenttype>
        </mappings>
    </import>
    <transcoding enabled="no"/>
</config>

For advice on mt-daapd then check out this excellent article. I feel like the Mediatomb one can still be trimmed out some more. It is derived from the one that is included in the package but I do feel that a lot of the MIME-type related lines are superfluous.

§2.5.4 Samba

I found Chapter 9 of the Samba documentation particularly useful for this part. The settings for Samba are kept in "/usr/local/etc/smb4.conf". Use your favourite editor (I use Vim) to change the file so that the un-commented lines look like the ones below.

[global]
    netbios name = HOST
    encrypt passwords = yes
    server string = HOST Samba Folders
    workgroup = WORKGROUP
    hosts allow = 192.168.1.
    load printers = no
    log file = /var/log/samba4.log
    map to guest = bad user

[Music]
    comment = Music Share
    path = /mnt/data/user1/Music
    read only = yes
    guest ok = yes
    only guest = yes

[Videos]
    comment = Videos Share
    path = /mnt/data/user1/Videos
    read only = yes
    guest ok = yes
    only guest = yes

[Origin]
    comment = Origin Backup
    path = /mnt/data/user4
    read only = no
    create mask = 0644
    directory mask = 0755
    valid users = user4

Now you need to go ahead and actually create the Samba users (and their passwords), which are in addition to the FreeBSD users. To create the guest account run adduser like below.

Username: guest
Full name: guest
Uid (Leave empty for default):
Login group [guest]: staff
Login group is staff. Invite guest into other groups? []: sftp_only
Login class [default]:
Shell (sh csh tcsh bash rbash git-shell nologin) [sh]: nologin
Home directory [/home/guest]:
Home directory permissions (Leave empty for default):
Use password-based authentication? [yes]:
Use an empty password? (yes/no) [no]:
Use a random password? (yes/no) [no]:
Enter password:
Enter password again:
Lock out the account after creation? [no]:
Username   : guest
Password   : *****
Full Name  : guest
Uid        : 1011
Class      :
Groups     : staff sftp_only
Home       : /home/guest
Home Mode  :
Shell      : /usr/sbin/nologin
Locked     : no
OK? (yes/no): yes
adduser: INFO: Successfully added (guest) to the user database.
Add another user? (yes/no): no
Goodbye!

Now it is time to add the Samba users: this is done by using smbpasswd like below.

smbpasswd -a user1
smbpasswd -a user2
smbpasswd -a user3
smbpasswd -a user4
smbpasswd -e user1
smbpasswd -e user2
smbpasswd -e user3
smbpasswd -e user4

§2.5.5 SSH

The settings for the SSH server are kept in "/etc/ssh/sshd_config". Use your favourite editor (I use Vim) to change the file so that the un-commented lines look like the ones below.

# Accept environment variables LANG, LC_*, TIMEFORMAT and TZ
AcceptEnv LANG LC_* TIMEFORMAT TZ

# Show banner before logging in
Banner /etc/banner

# Make sure that users are still connected
ClientAliveInterval 5m

# Kill in-active login attempts
LoginGraceTime 5m

# Log all activity
LogLevel VERBOSE

# Number of password attempts
MaxAuthTries 4

# Number of hanging connections
MaxStartups 4

# Disable root login
PermitRootLogin no

# Enable port forwarding
PermitTunnel yes

# Maintain privacy
PrintLastLog no

# Use internal SFTP process with increased logging and file mode restrictions
Subsystem sftp internal-sftp -l VERBOSE -u 077

# Disable reverse lookup
UseDNS no

# Enable X11 forwarding
X11Forwarding yes

# Specific options for "sftp_only" group members
Match group sftp_only
    # Disallow agent forwarding
    AllowAgentForwarding no

    # Disallow TCP forwarding
    AllowTCPForwarding no

        # Chroot to the ZFS data partition
        ChrootDirectory /mnt/data

    # Force all connections to be SFTP
    ForceCommand internal-sftp -l VERBOSE -u 077

    # Disable port forwarding
    PermitTunnel no

    # Disable X11 forwarding
    X11Forwarding no

# Specific options for "user1" user
Match user user1
    # Hide banner
    Banner none

All the options are pretty self-explanatory I think but I would like to draw your attention to the line ForceCommand internal-sftp -l VERBOSE -u 077 under the section Match group sftp_only. These two lines mean that users who are members of the group "sftp_only" ("user3" and "user4" in this tutorial) cannot run commands over SSH on my server but do have the ability to use it's storage over an SFTP connection. If one of those users tries to connect using ssh user3@HOST then it will always fail but they can still transfer files using an encrypted SFTP connection. In essence, my server is simply a data store for them and it will refuse all attempts to log in, run commands, forward ports or open GUIs.

§2.5.6 SMART Daemon

The settings for the SMART daemon are kept in "/usr/local/etc/smartd.conf". Use your favourite editor (I use Vim) to change the file so that the un-commented lines look like the ones below. This will start a short self-test on all disks between 2AM and 3AM on Tuesday morning and any changes in their health will be emailed to you.

DEFAULT -m user@example.com
/dev/ada0 -s S/../../2/02
/dev/ada1 -s S/../../2/02
/dev/ada2 -s S/../../2/02
/dev/ada3 -s S/../../2/02
/dev/ada4 -s S/../../2/02
/dev/ada5 -s S/../../2/02

§3 Workarounds

For some reason the default installation of Python from the ports system does not load up the machine's SSL certificates correctly. This means that Python scripts which connect to servers securely, such as youtube-dl, fail with "CERTIFICATE_VERIFY_FAILED" errors. After a fair amount of searching I found other people had similar issues. I was able to implement a workaround by typing the following command as root ln -s /usr/local/etc/ssl/cert.pem /etc/ssl/cert.pem.

§4 Usage Notes

To find a port in the port tree run whereis bar.

To perform an audit of all known security holes in the installed software run pkg audit. Before you update the software using portmaster foo/bar you should check UPDATING to see if there are any issues for updating any specific package.

To update the base of FreeBSD run freebsd-update fetch install.

To restart servers run one of the following commands:

service mediatomb restart
service mt-daapd restart
service netatalk restart
service samba restart

To update the time after a long down period run (during which the clock may have drifted) ntpdate -v -b uk.pool.ntp.org.

To upgrade all installed ports in the correct order run portmaster -afy.

To list all ports that have upgrades available run portmaster -L | grep "New version available" | sort.

To upgrade from 10.0-RELEASE to 10.1-RELEASE (according to the Installation Notes) run the following commands (I have added the -m DISABLE_VULNERABILITIES=yes because sometimes updating FreeBSD to a secure version is more important than ensuring all installed packages are themselves secure):

freebsd-update fetch install
reboot
freebsd-update upgrade -r 10.1-RELEASE
freebsd-update install
reboot
freebsd-update install
portmaster -B -f -m DISABLE_VULNERABILITIES=yes -a
# Run "portmaster -B -R -f -m DISABLE_VULNERABILITIES=yes -a" if you were interrupted and need to restart from where you left off
freebsd-update install
reboot

If you have changed the umask of the root account then some permissions will be more restrictive than intended. To correct port's documentation installation (which assumes default umask) run the following commands:

find /usr/local/share/doc ! -perm 755 -type d -exec chmod 755 {} \;
find /usr/local/share/doc ! -perm 644 -type f -exec chmod 644 {} \;

If you have changed the umask of the root account then some permissions will be more restrictive than intended. To correct a PIP installation (which assumes default umask) run the following commands:

find /usr/local/lib/python2.7/site-packages ! -perm 755 -type d -exec chmod 755 {} \;
find /usr/local/lib/python2.7/site-packages ! -perm 644 -type f -exec chmod 644 {} \;

To update all PHP 5.6 packages, for example, run portmaster $(portmaster -l | egrep -o php56.+ | sed "s/-5.6.*//g;s/-1.0//g").

§5 References

For advice on which folders to copy on Windows to backup Origin downloads then check out this informative article (remembering to connect to the SMB share as "WORKGROUP\user4" from Windows).

§6 Word Of Warning

A xkcd comic about installing *BSD
© xkcd

This page was last modified on 05/05/2017.

The Unfriendly Web

Map of total firewall denials on my servers

Update (September 2015)

This site has changed quite a lot since I bought the domain in November 2005 and first started hosting my own content. During that time the internet has evolved an awful lot; as I write this in 2015 it is almost unrecognisable to what it once was. Gone are the days of hosting your own photo albums from your holidays on your own website: now you create an album on Facebook to share with your friends and family. Got some special photos that you are particularly proud of? Then deviantART or Flickr are the places for you to showcase them. Found an interesting page and wish to share it with your friends? Twitter and Facebook will update them immediately wherever they are. Written some pieces of source code that you think other people might find useful? GitHub will version track and syntax highlight it in an instant.

Consequently, this site no longer has photo albums and panoramas taken from my travels: the special ones are in my deviantART gallery and the normal ones are on my private Facebook page. I don't have a WordPress blog at the minute so I will still keep my articles on fixing technological problems ("Releases" and "Tutorials") on here for archival - if they're useful to you then that's great. If I ever restart publishing code it'll be on my GitHub page.

I have learned a lot since I first started writing (non-public) web pages in 2002. As testimony, this site: does not set any cookies; barely has any JavaScript on it; and is no longer dynamically generated using PHP. Rather, it is completely static with updates propagated using make every midnight thanks to cron.

© 2002-2017 Thomas Guymer. See the Copyright Statement.