Note: to access the DigitalOcean web console, open outbound tcp port 5000.
Create a droplet through the DO web tool.
Add our ssh keys.
Log in via ssh, using the user name ‘freebsd’:
% ssh -l freebsd nnn.nnn.nnn.nnn
Add a new user (and make them part of the wheel group):
% sudo adduser
(sudo is install by default, and wheel are sudoers.)
Add ssh keys to new user account:
% sudo mkdir /home/myuser/.ssh
% sudo cp ~/.ssh/authorized_keys /home/myuser/.ssh/
% sudo chown myuser:myuser /home/myuser/.ssh
% sudo chown myuser:myuser /home/myuser/.ssh/authorized_keys
Test to sure we can ssh in and sudo with this account.
Generate ssh keys for this account:
% ssh-keygen -b 4096
Add a minimal .tcshrc for the new account:
set promptchars="%#"
set prompt = "--- %m %h %c %# "
setenv EDITOR vim
setenv VISUAL vim
setenv PAGER less
set autolist
alias l 'ls -FG'
alias la 'ls -FGa'
alias ll 'ls -FGlha'
alias h 'history 60 | sort -k2 | uniq -f2 | sort -bn'
setenv LSCOLORS "gxfxcxdxbxxggdabagacad"
bindkey "^W" backward-delete-word
Install updates:
% sudo freebsd-update fetch
% sudo freebsd-update install
% sudo pkg update
% sudo pkg upgrade
Install out some packages:
% sudo pkg install vim-lite git-lite zsh sshguard-ipfw curl tmux alpine python ezjail haproxy wget
Edit /etc/rc.conf:
hostname="inky.example.com"
gateway_enable="YES"
sshd_enable="YES"
sshguard_enable="YES"
sshguard_safety_thresh="30"
sshguard_pardon_min_interval="3600"
sshguard_prescribe_interval="7200"
ntpd_enable="YES"
ntpd_sync_on_start="YES"
cloned_interfaces="lo1"
ifconfig_lo1="inet 172.16.0.1/24"
firewall_enable="YES"
firewall_script="/etc/ipfw.rules"
firewall_logging="YES"
firewall_nat_enable="YES"
firewall_nat_interface="vtnet0"
haproxy_enable="YES"
ezjail_enable="YES"
Edit /etc/sysctl.conf:
net.inet.ip.fw.verbose_limit=5
Edit /etc/ipfw.rules:
ipfw -q -f flush
add="ipfw -q add"
oif="vtnet0"
jails="lo1"
natout="9000"
tcpout="https,http,domain,ssh,ntp,43,67,68"
udpout="domain,ntp,67,68"
ipfw -q nat 1 config if $oif reset unreg_only same_ports
$add 10 allow all from any to any via lo0
$add 20 allow all from any to any via $jails
$add 100 deny all from any to 127.0.0.0/8
$add 110 deny ip from 127.0.0.0/8 to any
$add 120 deny all from any to ::1
$add 130 deny all from ::1 to any
$add 500 deny all from any to any not antispoof in
$add 510 deny all from any to 10.0.0.0/8 via $oif
$add 520 deny all from any to 172.16.0.0/12 via $oif
$add 530 deny all from any to 192.168.0.0/16 via $oif
$add 540 deny all from any to 0.0.0.0/8 via $oif
$add 550 deny all from any to 169.254.0.0/16 via $oif
$add 560 deny all from any to 224.0.0.0/4 via $oif
$add 570 deny all from any to 240.0.0.0/4 via $oif
# Work main office connection:
$add 990 allow tcp from NNN.N.NNN.NNN to me ssh,https setup keep-state
# sshguard (dyamic rules are added to table 22):
$add 1000 deny ip from table\(22\) to any
$add 2000 allow tcp from any to me ssh setup keep-state
$add 2100 allow tcp from any to me http,https setup keep-state
$add 5000 nat 1 all from any to any in recv $oif
$add 5001 check-state
$add 6000 skipto $natout tcp from any to any $tcpout out xmit $oif keep-state
$add 6100 skipto $natout udp from any to any $udpout out xmit $oif keep-state
$add 8999 deny log all from any to any
$add 9000 nat 1 all from any to any out xmit $oif
$add 9001 allow all from any to any
Set the timezone:
% sudo tzsetup
Select “No” for “Is this machine’s CMOS clock set to UTC?”.
Disable “freebsd” user account:
% sudo pw lock freebsd
Edit /etc/ssh/sshd_config:
PermitRootLogin no
Edit /etc/aliases to send system mail to an account we’ll read:
root: myuser
…and then rebuild the aliases:
% sudo newaliases
Reboot.
Set up jails.
Create the ezjail base:
# wget http://ftp.freebsd.org/pub/FreeBSD/releases/ISO-IMAGES/10.2/FreeBSD-10.2-RELEASE-amd64-disc1.iso
# mdconfig -a -t vnode -f FreeBSD-10.2-RELEASE-amd64-disc1.iso
# mount -t cd9660 /dev/md0 /mnt
# ezjail-admin install -h file://mnt/usr/freebsd-dist
# umount /mnt
# mdconfig -d -u 0
Update base and port for jail base:
# ezjail-admin update -u
# ezjail-admin update -P
Create a first jail for MySQL/MariaDB:
# ezjail-admin create mysql.example.com 'lo1|172.16.0.2'
# ezjail-admin start mysql.example.com
# ezjail-admin console mysql.example.com
(It’s important that each jail has a FQDN, or weird stuff will go wrong, like local mail delivery.)
Basic jail config:
root@mysql:~ # echo 'nameserver 8.8.8.8' > /etc/resolv.conf
root@mysql:~ # echo 'nameserver 8.8.4.4' >> /etc/resolv.conf
root@mysql:~ # tzsetup
And set the root password for the jail (passwd
).
# ezjail-admin console mysql
root@mysql:~ # pkg install mariadb100-server mariadb100-client
root@mysql:~ # echo 'mysql_enable="YES"' >> /etc/rc.conf
root@mysql:~ # service mysql-server start
Set up a jail for our default website:
# ezjail-admin create www.example.org 'lo1|172.16.0.3'
# ezjail-admin start www.example.org
Set the nameservers, timezone, and root password.
root@www:~ # pkg install php56 php56-extensions php56-mysqli php56-mbstring php56-mcrypt nginx vim-lite
root@www:~ # echo 'nginx_enable="YES"' >> /etc/rc.conf
root@www:~ # echo 'php_fpm_enable="YES"' >> /etc/rc.conf
In /usr/local/etc/php-fpm.conf:
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 2
And:
root@www:~ # service php-fpm start
root@www:~ # service nginx start
Create SSL certs on host (outside jails)
# openssl genrsa -rand -genkey -out /etc/ssl/cert.key 2048
# mkdir /etc/ssl/haproxy
# openssl req -new -x509 -days 2190 -key /etc/ssl/cert.key -out /etc/ssl/www.example.org.crt -sha256
# openssl req -new -x509 -days 2190 -key /etc/ssl/cert.key -out /etc/ssl/www.example.com.crt -sha256
# cat www.example.com.crt /etc/ssl/cert.key > /etc/ssl/haproxy/www.example.com.pem
# cat www.example.org.crt /etc/ssl/cert.key > /etc/ssl/haproxy/www.example.org.pem
(We must supply the correct FQDN for cert generation when asked by openssl.)
Edit /usr/local/etc/nginx/nginx.conf:
- location / {
- root /usr/local/www/nginx;
- index index.html index.htm;
- }
+ root /usr/local/www/example.org;
+ index index.html index.htm index.php;
+ autoindex on;
+ location ~ \.php$ {
+ include fastcgi_params;
+ fastcgi_pass 127.0.0.1:9000;
+ fastcgi_index index.php;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+ }
And actually do mkdir /usr/local/www/example.com
.
Further nginx considerations:
- No Apache-like .htaccess
- For basic auth, see:
https://www.nginx.com/resources/admin-guide/restricting-access/
- The order of nginx.conf entries matters. E.g., it stops processing at the *first* matching location with a matching regex.
- We forbid access to sensitive files in /usr/local/etc/nginx/nginx.conf:
- We do only http (80) with nginx. haproxy handles https (443).
location = /config.php {
deny all;
}
Do the same thing to set up the second site: www.example.com.
Outside the jail, edit /usr/local/etc/haproxy.conf:
global
daemon
maxconn 1024
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend http
bind 162.243.53.217:80
bind 162.243.53.217:80 ssl crt /etc/ssl/haproxy/
default_backend test.example.org
acl test.example.org hdr(host) -i test.example.org
acl test.example.com hdr(host) -i test.example.com
use_backend test.example.org if test.example.org
use_backend test.example.com if test.example.com
backend test.example.org
server test.example.org 172.16.0.3:80
backend test.example.com
server test.example.com 172.16.0.4:80
Create databases and users.
# ezjail-admin console mysql
root@mysql:~ # mysql -uroot
MariaDB [(none)]> CREATE DATABASE blog;
MariaDB [(none)]> CREATE DATABASE wp;
MariaDB [(none)]> CREATE DATABASE ttrss;
MariaDB [(none)]> CREATE USER 'blog'@'localhost' IDENTIFIED BY '****************';
MariaDB [(none)]> GRANT SELECT, INSERT, UPDATE, DELETE ON blog.* TO 'blog'@'172.16.0.3';
MariaDB [(none)]> CREATE USER 'wp'@'172.16.0.4' IDENTIFIED BY '********************';
MariaDB [(none)]> GRANT ALL ON wp.* TO 'wp'@'172.16.0.4';
MariaDB [(none)]> CREATE USER 'ttrss'@'172.16.0.4' IDENTIFIED BY '********************';
MariaDB [(none)]> GRANT ALL ON ttrss.* TO 'ttrss'@'172.16.0.4';
MariaDB [(none)]> FLUSH PRIVILEGES;
Restore databases:
root@mysql:~ # mysql -u root -p blog < blog-db.sql
root@mysql:~ # mysql -u root -p wp < devilghost-wp-db.sql
root@mysql:~ # mysql -u root -p ttrss < devilghost-ttrss-db.sql
Set MariaDB root password:
MariaDB [(none)]> SET PASSWORD FOR 'root'@'localhost' = PASSWORD('***********************');
Create the .htpasswd file:
--- www.paulgorman.org 174 ~ # echo -n 'paulgorman:' >> /usr/local/etc/nginx/.htpasswd
--- www.paulgorman.org 177 ~ # openssl passwd -apr1 >> /usr/local/etc/nginx/.htpasswd
--- www.devilghost.com 16 ~ # echo -n 'paulgorman:' >> /usr/local/etc/nginx/.htpasswd
--- www.devilghost.com 17 ~ # openssl passwd -apr1 >> /usr/local/etc/nginx/.htpasswd
Set up rewrite rule for example.org/blog/ by adding a location to /usr/local/etc/nginx/nginx.conf:
location ~* /blog/(new|save|all|delete) {
auth_basic "Login";
auth_basic_user_file /usr/local/etc/nginx/.htpasswd;
rewrite ^/blog/((\w|\/)+)$ /blog/index.php?url=$1;
}
location ~ /blog {
rewrite ^/blog/((\w|\/)+)$ /blog/index.php?url=$1;
}
If we want to add a cron job for a system user in a jail, had root edit the user’s crontabl:
$ crontab -u www -e
Set up backups.
Create /root/bin/backup.sh, and make it executable:
#!/bin/sh
DAY=$(date +'%d')
tar -czf /home/paulgorman/backups/backup-inky-${DAY}.tgz \
/root/bin \
/etc/rc.conf \
/etc/ipfw.rules \
/etc/sysctl.conf \
/etc/ssl \
/etc/ssh \
/etc/aliases \
/var/cron \
/home/paulgorman/.ssh \
/home/paulgorman/repo \
/usr/local/etc/ \
/usr/jails/*/etc/rc.conf \
/usr/jails/*/root/bin \
/usr/jails/*/usr/local/etc/ \
/usr/jails/*/var/cron \
/usr/jails/*/usr/local/www/ \
/usr/jails/mysql/root/inky-alldatabases.sql
chmod 640 /root/backup-inky-${DAY}.tgz
Create a root cron job:
1 3 2,16 * * /root/bin/backup.sh
Create the target directory:
# sudo mkdir /home/paulgorman/backups
# sudo chmod 740 /home/paulgorman/backups
Set up a cron job on another machine to fetch the backup files.
In the mysql jail, create /root/bin/sqlbackup.sql, and make it executable:
#!/bin/sh
mysqldump -u root --password='***********************' --all-databases > /root/inky-alldatabases.sql
chmod 640 /root/inky-alldatabases.sql
In the mysql jail, create a root cron job:
1 2 2,16 * * /root/bin/sqlbackup.sh
set promptchars=“%#” set prompt = “— %M %h %c %# ” setenv EDITOR vim setenv VISUAL vim setenv PAGER less set autolist alias l ‘ls -FG’ alias la ‘ls -FGa’ alias ll ‘ls -FGlha’ alias h ‘history 60 | sort -k2 | uniq -f2 | sort -bn’ setenv LSCOLORS “gxfxcxdxbxxggdabagacad” bindkey “^W” backward-delete-word
set nocompatible
set encoding=utf-8
set viminfo=‘500,f1,<200,:200,@200,/200,s20,h
set t_Co=256
syntax off
set ignorecase
set smartcase
set incsearch
set showmatch
set wildmenu
set autoindent
set nosmartindent
set tabstop=4
set shiftwidth=4
set expandtab
set backspace=indent,eol,start
set smarttab
set ruler
set showmode
set nu
set rnu
let mapleader=" " " Default is \
nmap
Add to /etc/rc.conf:
sendmail_enable="NO"
sendmail_submit_enable="NO"
sendmail_outbound_enable="NO"
sendmail_msp_queue_enable="NO"
smtpd_enable="YES"
Stop sendmail, and install opensmtpd:
# service sendmail stop
# killall sendmail
# pkg install opensmtpd
Edit /etc/mail/mailer.conf:
sendmail /usr/local/sbin/smtpctl
send-mail /usr/local/sbin/smtpctl
mailq /usr/local/sbin/smtpctl
makemap /usr/local/libexec/opensmtpd/makemap
newaliases /usr/local/libexec/opensmtpd/makemap
Edit /usr/local/etc/mail/smtpd.conf:
# See smtpd.conf(5) for more information.
# To accept external mail, replace with: listen on all
listen on localhost
# If you edit the file, you have to run "smtpctl update table aliases"
table aliases file:/etc/mail/aliases
# Uncomment the following to accept external mail for domain "example.org"
#accept from any for domain "example.org" alias <aliases> deliver to mbox
accept for local alias <aliases> deliver to mbox
accept for any relay
Start opensmtpd:
# service smtpd start