FreeBSD Server Setup - HVboom/HowTo-DigitalOcean GitHub Wiki

Get an account on Digital Ocean to setup a new droplet.

I choose:

  • Standard pricing plan for 20$ a month

  • FreeBSD 11.0 zfs

  • Select additional options Private Networking and IPv6

  • FQDN: HVboom.org (living on 46.101.195.202 - IPv6: 2a03:b0c0:3:d0::4b:3001)

  • Create the necessary SSH keys for the default user freebsd and paste the public key to DigitalOcean

    # Options:
    #   -t ed25519 = newest standard encryption algorithm
    #   -a 150     = how many times the generated key is mangled around - good values are between 80 and 200 turnaround
    #   -b 521     = number of bits for the key - allowed values are 256, 384 or 521 bits
    # the usage of a passphrase is recommended
    ssh-keygen -t ed25519 -a 150 -b 521
    
    Generating public/private ed25519 key pair.
    Enter file in which to save the key (/home/freebsd/.ssh/id_ed25519): .ssh/id_ed25519
    Enter passphrase (empty for no passphrase): 
    Enter same passphrase again: 
    Your identification has been saved in /home/freebsd/.ssh/id_ed25519.
    Your public key has been saved in /home/freebsd/.ssh/id_ed25519.pub.
    The key fingerprint is:
    SHA256:d+GfdZeyxCZNsfunSg8AgKxRThawbJP8v1OOSl5P83E
    The key's randomart image is:
    +--[ED25519 256]--+
    |  .+=o.      .   |
    | o.*o  .      o  |
    |  Bo.   .   .o   |
    | ..o     . .+.. .|
    |    .   S o.oB .+|
    |     .  .. o+.+oo|
    |    . o+o . E.o..|
    |   o .o+.o + o ..|
    |    o.... . ..o  |
    +----[SHA256]-----+
    
    cat $HOME/.ssh/id_ed25519.pub

Doing the basic setup to secure the VPS and install the basics:

Setup DNS

  • Setup DNS to reach the droplet by name see DNS Setup

    Record type Name IP address Comment
    A @ 46.101.195.202
    AAAA @ 2a03:b0c0:3:d0::2e68:6001
    CNAME * hvboom.org. enable virtual hosts
    TXT @ v=spf1 a -all legitimate mail server

Basic setup

  • Follow the instructions for the Initial Server Setup

  • Update everything to the latest versions:

    • Do not install source code (/usr/src/) for core system, because I do no plan to compile it on my own. Change the configuration file /etc/freebsd-update.conf:

      sudo vi /etc/freebsd-update.conf
      ...
      # Components of the base system which should be kept updated.
      # Components src world kernel
      Components world kernel
      ...
    • Update core system

      sudo freebsd-update fetch install
      # Restart server after upgrading core system (not really necessary - its just a matter of safety)
      sudo shutdown -r now
    • Upgrade installed packages

      sudo pkg upgrade
    • Optional: Upgrade installed ports - I do not use ports at all

      1. First time to upgrade installed ports

        sudo portsnap fetch update
        # Install portupgrade & use it
        cd /usr/ports/ports-mgmt/portupgrade/
        sudo make install clean
        sudo portupgrade -a
      2. All other times

        sudo bash -c 'portsnap fetch update && portupgrade -a'
  • Automatically check for new system patches

    • Adding following line to /etc/crontab:

      ...
      # Check for core system patches 
      @daily                                  root    freebsd-update -t freebsd cron
    • Adding following line to /home/freebsd/.profile, because this user is informed about core system updates by mail:

      # Check for mail - like core system update messages
      mail

Setup Bash profile for all users

  • Add following lines to the end of /etc/profile:

    # Source global definitions
    globalSettings="/usr/local/share/bash"
    if [ -e "$globalSettings/profile" ]
    then
      . "$globalSettings/profile"
    fi
  • Create a global profile file /usr/local/share/bash/profile:

    # setup directories
    globalSettings="/usr/local/share/bash"
    userSettings="$HOME"
    
    # no access for others :-)
    umask 007
    
    # start SSH agent to simplify the passphrase usage
    if [ -r "$globalSettings/.ssh_agent" ]
    then
      . "$globalSettings/.ssh_agent"
    fi
    
    # create user specific directories: bin and tmp
    if [ -z $FIRST_RUN ]
    then
      mkdir -p "$HOME/bin"
      mkdir -p "$HOME/tmp"
    fi
    
    set -o vi
    set editor=vi
    set pager=less
    # set term=rxvt
    [ -z "$SSH_CLIENT" ] && stty istrip
    
    # define prompt
    export PS1=`whoami`'@'`hostname`':${PWD##$HOME}> '
    
    # copy default .profile
    if [ ! -r "$HOME/.profile" ]
    then
      cp "$globalSettings/.profile" "$HOME"
    fi
    
    # copy default RVM options
    if [ ! -r "$HOME/.rvmrc" ]
    then
      cp "$globalSettings/.rvmrc" "$HOME"
    fi
    
    # copy default VIM options
    if [ ! -r "$HOME/.vimrc" ]
    then
      cp "$globalSettings/.vimrc" "$HOME"
    fi
    
    # set user scripts as first choice
    if [ -d "$HOME/bin" ] && [ -z $FIRST_RUN ]
    then
      export PATH="$HOME/bin:$PATH"
    fi
    
    # global settings first
    if [ -r "$globalSettings/.alias" ]
    then
      . "$globalSettings/.alias"
    fi
    # user settings
    if [ -r "$userSettings/.alias" ]
    then
      . "$userSettings/.alias"
    fi
    
    # user settings
    if [ -r "$userSettings/.profile" ]
    then
      . "$userSettings/.profile"
    fi
    if [ -r "$userSettings/.bashrc" ]
    then
      . "$userSettings/.bashrc"
    fi
    
    # some parts are to be executed only ones
    export FIRST_RUN="FALSE"
  • Create a global alias file /usr/local/share/bash/.alias:

    alias ls='ls -AF'
    alias ll='ls -la'
    alias lll='ll | more'
    alias ltr='ll -tr'
    
    alias more='less'
    alias cls='clear'
    alias mkdir='mkdir -p'
    alias myps='ps -Ax | grep `whoami` | sort -b -k 6,6'
    alias count='ls -1 | wc -l'
    alias r='fc -s'
    
    # use VIM, if installed
    if [ -x `which vim` ]
    then
      alias vi='vim'
      alias view='vim -R'
    fi
    
    # find shortcuts
    alias fn='find . -name '
    alias fh='find . -name "*.h*" 2>/dev/null | xargs grep '
    alias fcpp='find . -name "*.cpp" 2>/dev/null | xargs grep '
    alias fm='find . -name "[mM]akefile*" 2>/dev/null | xargs grep '
    alias fpy='find . -name "*.py" 2>/dev/null | xargs grep '
    alias fru='find . -name "*.ru" 2>/dev/null | xargs grep '
    alias gf='find . -type f 2>/dev/null | xargs grep '

Setup SSH

  • Allow login with SSH key only changing following settings in /etc/ssh/sshd_config (see wiki page):

    # Change to yes to enable built-in password authentication.
    PasswordAuthentication no
    PermitEmptyPasswords no
    
    # Change to no to disable PAM authentication
    ChallengeResponseAuthentication no 
    • Restart SSH deamon to use the new configuration files sudo service sshd restart
  • Hardening the Security

    sudo ssh-keygen -M generate -O bits=3072 moduli
    sudo ssh-keygen -M screen -f moduli moduli-final
    sudo mv /etc/ssh/moduli /etc/ssh/moduli.SAVE
    sudo mv moduli-final /etc/ssh/moduli
    sudo service sshd restart
SSH agent
  • Running the SSH agent automatically by creating /usr/local/share/bash/.ssh_agent:

    #!/usr/local/bin/bash
    
    SSH_DIR="${HOME}/.ssh"
    SSH_ENV="${SSH_DIR}/environment"
    SSH_ED25519_ID="${SSH_DIR}/id_ed25519"
    SSH_RSA_ID="${SSH_DIR}/id_rsa"
    
    function start_agent {
      if [ -e "${SSH_ED25519_ID}" ] || [ -e "${SSH_ID}" ]; then
        echo "Initialising new SSH agent..."
        mkdir -p "${SSH_DIR}" && chmod 750 "${SSH_DIR}"
        /usr/bin/ssh-agent | sed 's/^echo/#echo/' > "${SSH_ENV}"
        chmod 600 "${SSH_ENV}"
        . "${SSH_ENV}" > /dev/null
        /usr/bin/ssh-add
        ps -efp ${SSH_AGENT_PID} > /dev/null && echo "succeeded"
      fi
    }
    
    # Source SSH settings, if applicable
    if [ -f "${SSH_ENV}" ]; then
      . "${SSH_ENV}" > /dev/null
      ps -efp ${SSH_AGENT_PID} > /dev/null || {
        start_agent;
      }
    else
      start_agent;
    fi
  • run that file automatically on login by adding following code to the global /usr/local/share/bash/profile:

    # Start the SSH agent for GitHub
    if [ -r "$globalSettings/.ssh_agent" ]; then
      . "$globalSettings/.ssh_agent"
    fi

Setup Firewall (only the TCP part)

  • Define firewall rules by editing /etc/rc.conf:

    # Allow SSH connections
    sshd_enable="YES"
    
    # Firewall settings
    firewall_enable="YES"
    firewall_quiet="YES"
    firewall_type="workstation"
    # 8080: default port for Tomcat
    # firewall_myservices="ssh http https sftp 8080"
    firewall_myservices="ssh http https"
    firewall_allowservices="any"
    firewall_logdeny="YES"  
  • Limit login attempts by editing /etc/sysctl.conf:

    ...
    # Firewall restrictions
    net.inet.ip.fw.verbose_limit=5 
  • Restart service: sudo service ipfw restart

Setup Timezone

  • Select appropriate timezone for the server located in Frankfurt:

    • sudo tzsetup
    • 8 Europe
    • 15 Germany
    • 1 most locations
    • confirm CEST
  • Define timezone synchronisation rules by editing /etc/rc.conf:

    ...
    # Timezone synchronisation
    ntpd_enable="YES"
    ntpd_sync_on_start="YES"

Extra swap space

Upgrade to version 13.0

  • Create a snapshot of your droplet, before starting the update
  • Install the new version with sudo /usr/sbin/freebsd-update -r 13.0-RELEASE upgrade
  • Further details can be found in the article on cyberciti.biz

Attention: Same fix as described for version 11.1 of /etc/rc.subr has to be considered.

Upgrade to version 12.1

  • Create a snapshot of your droplet, before starting the update
  • Install the new version with sudo /usr/sbin/freebsd-update -r 12.1-RELEASE upgrade

Issue

  • After upgrade Phusion Passenger is not able to serve Rails applications anymore
    [ W 2019-11-03 09:44:21.0860 2933/T4 age/Wat/AgentWatcher.cpp:97 ]: Passenger core (pid=57246) crashed with signal SIGSEGV, restarting it...
    [ W 2019-11-03 09:44:21.1853 57268/T1 Ser/AcceptLoadBalancer.h:295 ]: Cannot disable Nagle's algorithm on a TCP socket: Invalid argument (errno=22)
    [ W 2019-11-03 09:44:21.1874 57268/T1 Ser/Server.h:798 ]: [ApiServer] Cannot disable Nagle's algorithm on a TCP socket: Invalid argument (errno=22)

Solution

  1. Upgrade to newest ruby and gem versions
  • Reinstall RVM for the passenger user: rvm implode; setup_rvm
  • Install latest ruby version: rvm list known; rvm install 2.6.5
    • If you get following errors, please try rvm install 2.6.5 --disable-binary
       ruby-2.6.5 - #importing gemset /home/passenger/.rvm/gemsets/global.gems.................there was an error installing gem gem-wrappers
      ..................there was an error installing gem rubygems-bundler
      ..................there was an error installing gem rake
      ..................there was an error installing gem rvm
      ..................there was an error installing gem bundler
  • Update the Gemfile to the newest ruby and passenger versions and run bundle install
  • Create the Apache module: passenger-install-apache2-module --languages ruby,nodejs
  • Update the module configuration with the newest links: vi /usr/local/etc/apache24/modules.d/080_mod_passenger.conf
  • Check, if Apache can be restarted: sudo service apache24 restart
  • Reboot the system: sudo shutdown -r now
  1. Install passenger package
  • Installation: sudo pkg install rubygem-passenger-apache-6.0.4_1
  • Adjust /usr/local/etc/apache24/modules.d/080_mod_passenger.conf
    LoadModule passenger_module /usr/local/lib/ruby/gems/2.6/gems/passenger-6.0.4/buildout/apache2/mod_passenger.so
    
    <IfModule mod_passenger.c>
      # Write a debug file with warnings
      # https://www.phusionpassenger.com/library/config/apache/reference/#logging-and-debugging-options
      # Log level from 0 (crit) - 7 (debug 3)
      PassengerLogLevel 2
      PassengerLogFile /var/log/passenger_debug.log
    
      # Turn on extended error pages
      # https://www.phusionpassenger.com/library/config/apache/reference/#passengerfriendlyerrorpages
      PassengerFriendlyErrorPages on
    
      PassengerRoot /usr/local/lib/ruby/gems/2.6/gems/passenger-6.0.4
      # Do not set the default ruby, because otherwise you have to set the ruby version in the vhost too
      # PassengerDefaultRuby /home/passenger/.rvm/gems/ruby-2.6.0@PhusionPassenger/wrappers/ruby
    </IfModule>

Update to version 11.1

Issue

  • After upgrading the kernel and a reboot the next call to sudo /usr/sbin/freebsd-update fetch install the server could not be rebooted again

Solution

  • The file /etc/rc.subr provided with the update on the Digital Ocean platform has to be substituted
    cd /etc
    sudo mv rc.subr rc.subr.save
    sudo wget "https://raw.githubusercontent.com/freebsd/freebsd/release/11.1.0/etc/rc.subr"
    sudo chmod 644 rc.subr
  • Add the Digital Ocean specific changes to the file /etc/rc.subr before line 1369
    load_rc_config()
    {
      local _name _rcvar_val _var _defval _v _msg _new _d
      _name=$1
    
          # Digital Ocean specific addition
          if [ -L /etc/rc.digitalocean.d/droplet.conf -a -f /etc/rc.digitalocean.d/droplet.conf ]
          then
              . /etc/rc.digitalocean.d/droplet.conf
          fi
    
      if ${_rc_conf_loaded:-false}; then
        :
      else
    ...

Hint usage of the Digital Ocean console

  • ensure, that you have a password set for the freebsd user to be able to login through the console
  • configure your keyboard layout to US, because that is strictly enforced by the console
⚠️ **GitHub.com Fallback** ⚠️