Setup Email Server From Scratch On FreeBSD #2 - 01 Server Setup
We believe in data independence, and support others who want data independence.
This tutorial is partially complete 2025-08-07
This is version 2 and everthing works up to and including Roundcube.
################
# Server Setup #
################
Go to your prefered registrar or use an existing domainname and choose a
hostname something like mail.domain.com or mx.domain.com. DNS and MX records
setup will be done after determining the new mail servers IP addresses.
The following assumes the domain name is okbsd.com so change it to your domain
name and change the IPV4 and IPV6 addresses to your own IP addresses. Also
change 'user' to your username where needed.
Setup a server with a hosting provider of choice. I have used IBM, One Provider,
Kamatera,, MS Azure, and OVHcloud - Vint Hill and Hillsboro. I recommend a vps or
dedicated server with OVH at Hillsboro. I only recently started using them but
they are the cheapest and the chat support has always been prompt and helpful.
Even if they couldn't add FreeBSD to their list of OS's for vps range of servers.
An important issue is that sometimes hosting providers have IP address ranges
which are on a blacklist and some email servers may reject mail from servers
using these ranges. In particular check with a dns checker like dnschecker.org
to to see if the IP you get assigned is on UCEPROTECTL3. It is pretty difficult
to get off a IP range blacklist and the best solution is to ask the hosting
provider to give you a different IP ... and ask them to monitor their customers
email habits more rigorously. That being said even if you are on one of these IP
range spam lists it may not be a big issue. I tested with okbsd.com which was on
such one such list and google accepted the email while outlook accepted it but
put it in the junk folder. Listing it as not junk will improve your domain's
reputation as long as you're not doing mass mailing campaigns or sending spam.
I previously setup a Debian 12 server on Kamatera in Dallas with 2 availability
cores 4GB RAM and 40GB disk space and network speed and performance are good for
$19 a month. I changed to OVH in Hillsboro and setup my second Debian mail server
VPS instance for $11 month.
If you find initial ssh connection to FreeBSD to be slightly slower, enable packet
reassembly in ipfw.
This is the second setup and 2nd version of this howto for FreeBSD. For this
mail stack setup we installed on OVH Cloud, KS-5, with 4 cores 8 threads 32GB
RAM 2TB GB Raid 1 SSD's. This has more than enough resources to run mail and
other services. Hosting cost is $20 for setup, plus $20 plus tax per month. I
used the chat windows and asked them to allow install of FreeBSD with their vps
service and they said not possible so and they setup an invoice that waived the
setup fee for a dedicated server where installing a custom image is allowed.
During install I botched the network configuration (don't configure the second
network interface) and lost Serial Over LAN and had to re-install, it went smoothly
the second time.
# Install on OVH was a little more tricky since they don't support FreeBSD by
# default. You can try installing from the Java KVM console or use the BYOI
# option.
# Use the OVH installer with the bring your own custom image option. Usually I
# use the DVD image since includes ports but download would be faster if using
# the disk1 image.
Installation from an OVHcloud template
Type of OS: Custom
CUSTOM(2): Bring Your Own Image
-> Next
Image URL: https://download.freebsd.org/releases/amd64/amd64/ISO-IMAGES/14.3/FreeBSD-14.3-RELEASE-amd64-disc1.iso
Image Type: raw
Path of the EFI bootloader from the OS installed on the server: /boot/efi/efi/boot/bootx64.efi
# It takes a few minutes for the machine to provision then switch to the IPMI/KVM
# tab. You can use the java applet if you have that installed but it is simpler to
# choose Serial Over LAN.
# Setup Java on a local FreeBSD, Debian, or Windows machine for KVM access. For FreeBSD
# the proceedure is similar to Debian but uses linux compatability. Newer IPMI versions
# need jdk1.8.0_451. For complete FreeBSD, Debian, and Windows instructions see . . .
https://okbsd.com/ipmi-supermicro-console.php
# Download jdk-8u451-linux-x64.tar.gz from oracle
mkdir /usr/java
tar xfzv jdk-8u451-linux-x64.tar.gz -C /usr/java
nano /etc/environment
# ---
# JAVA_HOME="/usr/java/jdk1.7.0_80"
JAVA_HOME="/usr/java/jdk1.8.0_451"
# ---
source/etc/environment
nano ~/.bashrc
# ---
#export JAVA_HOME="/usr/java/jdk1.7.0_80"
#export PATH="$PATH:/usr/java/jdk1.7.0_80/bin"
export JAVA_HOME="/usr/java/jdk1.8.0_451"
export PATH="$PATH:/usr/java/jdk1.8.0_451/bin"
# ---
update-alternatives --install "/usr/bin/java" "java" "/usr/java/jdk1.8.0_451/bin/java" 1
update-alternatives --config java
# Select the correct version
0 /usr/java/jdk1.8.0_451/bin/java 1 auto mode
1 /usr/java/jdk1.7.0_80/bin/java 1 manual mode
* 2 /usr/java/jdk1.8.0_451/bin/java 1 manual mode
java -version
/usr/java/jdk1.8.0_451/bin/ControlPanel
Security -> Select Lowest Security Possible -> High
# OVHCloud Dedicated Server -> Select Server -> IPMI/KVM -> From Java Applet KVM
# This will download a jnlp file, run it.
/usr/java/jdk1.8.0_451/bin/javaws /root/Downloads/ns102415-ip-147-135-37-us_.jnlp
# Choose Don't Update - Later -> Run -> console will open
# OVHCloud does not allow Java KVM on all machines, usually not available for
# vps. There is an option to use Java KVM and boot with an iso and potentially
# reinstall the OS without the OVH control panel, but only for Dedicated Servers.
# Install FreeBSD
# We'll use hostname okbsd.com and the IP addresses assigned by OVH, change your
# setup accordingly.
Select Install
Continue with Default Keymap
Set Hostname : okbsd.com
Distribution Select: Use the defaults plus src and ports.
If you need kernel sources and ports can be installed later.
Manual paritioning isn't needed since with ZFS you can change the partitions
later but I bumped the swap size to 4g. Another option is to choose UFS with
or without separate partitions if you never plan to try and increase the disk
size.
Partitioning: Auto (ZFS)
Pool Type: default stripe
ZFS Configuration: you can change swap to 4G if you want
Select disks to create zfs pool "zroot" on "ada1"
>>> Install
# It is very important to confirm you know the root password before leaving the
# installer.
Change password for root
New Password:
# For Networking configure the first interface only, do not configure the second
# interface. On first install I lost network after running configure on the
# second interface during install and lost SOLAN access (no network no LAN) and
# had to restart the install from the beginning.
IPv4: DHCP
# Enter valued from OVH Server Page - these is little point to configure ipv6 yet
IPv6 Address: 2604:2dc0:200:187::1
IPv6 Gateway: 2604:2dc0:200:1ff:ff:ff:ff:ff
Search: okbsd.com
DNS1: 9.9.9.9
DNS2: 1.1.1.1
Select Timezone ...
Enable: unbound sshd ntp ntp_sync_on_start
System Hardening: I skip all of these though random pid might be an idea
# The IPMI/KVM or SOL are poor and it is almost impossible to cut and paste any
# command into the IPMI console. While you can get in later with IPMI if you
# know the root password it is better to setup a user account in group wheel or
# sudo, and confirm root access before leaving the installer.
Add User Accounts: jack
Full Name: Jack Pumpkin
Set user password: ***
# It is important to invite the user 'jack' to group 'wheel' which allows su access to
# the root account. Initially ssh access to root is denied, so you will need to
# login as jack then su - root to get root access.
Invite to other groups: wheel
# At this point you have an option to drop to shell and use chroot to change
# root or jack's passwords. Make sure you know your passwords.
Choose EXIT THE INSTALLER
<Continue and Reboot>
# Stay logged in to the web console (SOL) or KVM and wait for reboot and login as the
# user jack with the jack's password.
# You may also be able to login directly with ssh
ssh jack@147.135.37.135
<jacks password>
# SOL console access is poor at best. If the network configuration is broken SOL
# may not work, and the simplest fix is restart the installation in OVH Cloud
# control panel.
# Use terminal or cmd shell to ssh to the server
homeuser@homebox:~ $ ssh jack@147.135.37.135
<password>
jack@okbsd:~$ su - root
<rootpassword>
root@okbsd.com:~# ssh-keygen
<enter>
<enter>
<enter>
pkg update
pkg upgrade
pkg install bash bind-tools sudo nano lsblk bind920
# Configure sudo access, to use nano set nano as your editor or use builtin vi.
# For vi use 'x' to remove and <esc> <esc> 'wq' to write and quit
pw group add sudo -g 995
pw group mod sudo -m jack
export EDITOR='/usr/local/bin/nano'
visudo
# --- uncomment these lines --
%wheel ALL=(ALL:ALL) ALL
%sudo ALL=(ALL:ALL) ALL
# ---
# Test sudo
root@okbsd.com:~# su - jack
jack@okbsd.com:~$ pkg install nano
pkg: Insufficient privileges to install packages
jack@okbsd.com:~$ sudo pkg install nano
Password: <jacks_password>
The most recent versions of packages are already installed
# Exit jack user shell and continue as root.
jack@okbsd.com:~$ exit
root@okbsd.com:~#
# Setup ssh key pair access to your server
# Open a console on your local machine and get or create a ssh key pair.
# FreeBSD, Linux, or Mac - open a console on your local machine and create a key
# pair if none, from terminal.
jill@home:~ $ ssh-keygen
<enter>
<enter>
<enter>
# FreeBSD, Linux, or Mac - get a copy of the public key.
jill@homebox:~ $ cat ~/.ssh/id_rsa.pub
# Windows - open a cmd shell and type ssh-keygen -t rsa -b 2048.
DOS> ssh-keygen -t rsa -b 2048
# Windows - get a copy of the public key.
DOS> more .ssh\id_rsa.pub
DOS> cd .ssh
DOS> more id_rsa.pub
# Install the authorized public key copied above, for nano '^' means control key
# so <ctrl>-o to write and <ctrl>-x to exit. Command list is at the bottom.
nano ~/.ssh/authorized_keys
# ---
<paste the public ssh key all as one line>
# ---
# Configure ssh to allow root login only with ssh key pair
nano /etc/ssh/sshd_config
# --- change the line PermitRootLogin ---
# PermitRootLogin no
PermitRootLogin prohibit-password
# PermitRootLogin yes
# ---
service sshd restart
# Open another terminal or command shell and try passwordless root access.
# Leave the first terminal open, so if the you can't get access you can fix it
# and restart sshd without getting locked out. Test root ssh access with the key
# pair from the local machine with terminal cmd shell or Putty. If using Putty
# use puttygen to import the ~/.ssh/id_rsa file and save it as a ppk file and
# configure Putty under ssh -> auth -> keyfile, and save your putty configuration.
# You can also ssh directly from cmd shell on Windows.
DOS> ssh root@147.135.37.135
<no password required>
root@okbsd:~#
# SV - To change root password if needed
root@okbsd.com:~# passwd
# SV - To reset jacks password if needed
root@okbsd.com:~# passwd jack
root@okbsd:~ # exit
# Setup the jack's ssh key pairs
jack@okbsd:~ $ ssh-keygen
<enter>
<enter>
<enter>
jack@okbsd:~ $ nano ~/.ssh/authorized_keys
# ---
<paste the public ssh key all as one line>
# ---
# SV - To change root password from the jack account with sudo
jack@okbsd:~ $ sudo passwd root
jack@okbsd:~ $ exit
# I'll leave off the prompt from now on to facilitate cut and pasting commands.
# If you want to add another user use...
adduser
# or use pw to add a user
pw adduser -n jack -s /usr/local/bin/bash -m
# Delete a user and home directory, you may need to umount the zfs dataset
pw userdel -n jack
umount zroot/home/jack
rm -rf /home/jack
# To change groupid, change sudo gid to 995
pw groupmod -n sudo -g 995
# Change your shell with chsh but this is a vi editor so move around with the
# arrow keys, use x to delete in command mode, use i to insert before the cursor,
# x to delete a character
# i insert text before the cursor
# a append text after the cursor
# Escape to exit insert or append mode
# Escape Escape :wq to write quit
# Excape Escape :q! to quit without saving and start over
# Or use export EDITOR='/usr/local/bin/nano'
# Change shell for root
# Get as list of shells
export EDITOR='/usr/local/bin/nano'
cat /etc/shells
chsh
Shell: /usr/local/bin/bash
# Change shell for user also
chsh jack
Shell: /usr/local/bin/bash
# Modify bash settings and create a personal binary 'bin' directory
mkdir -p ~/bin
# If PATH doesn't have ~/bin eg /root/bin or /home/user/bin add ONE of the following
# lines to .profile on FreeBSD or to .bashrc on Debian as below, change user as
# appropriate. If your PATH already includes ~/bin don't add it again!
nano ~/.profile
# --- ADD ONLY ONE IF NEEDED ---
export PATH="$PATH:~/bin"
export PATH="$PATH:$HOME/bin"
# ---
# LSCOLORS is almost unreadable with some terminal programs but comes out nicely
# with Xterm or Xterm-color.
# If you manage many servers which may by default have same short hostname like
# 'pbx.domain.com' change PS1 so as to easily identify the server with the full
# hostname, PS1='\u@$(hostname -f):\w\$ '
# Get rid of cookie hints!
# --- add or modify these lines
nano ~/.profile
export EDITOR=/usr/local/bin/nano
export PS1='\u@$(hostname -f):\w\$ '
export LSCOLORS="ExGxFxdxCxegDxabagacad"
export XTERM_LOCALE="en_US.UTF-8"
export LANG="en_US.UTF-8"
# Display a random cookie on each login.
# if [ -x /usr/bin/fortune ] ; then /usr/bin/fortune freebsd-tips ; fi
# my aliases
alias rm='rm -i'
alias ls-a='/bin/ls -aGF'
alias ls-l='/bin/ls -lhGF'
alias lsl='/bin/ls -lhIGF'
alias ls='/bin/ls -IGF'
alias pine='alpine'
# ---
# Test your colors
cd
mkdir -p ~/Downloads ~/bin
ls ~/Downloads ~/bin
# Make nano pretty on FreeBSD, syntax highlighting
nano ~/.nanorc
# ---
include /usr/local/share/nano/default.nanorc
include /usr/local/share/nano/php.nanorc
include /usr/local/share/nano/perl.nanorc
include /usr/local/share/nano/html.nanorc
include /usr/local/share/nano/python.nanorc
include /usr/local/share/nano/c.nanorc
include /usr/local/share/nano/sh.nanorc
# ---
# Repeat the above .profile setup as the user and comment out the fortune line
su - jack
nano .profile
.....
#################################
# Setup Hostname and Networking #
#################################
# Change autoboot delay - default is 10 seconds
nano /boot/loader.conf
# ---
autoboot_delay="2"
# ---
# Edit rc.conf and setup hostname and network interfaces
# The last number provided by OVH :1 but using :2 or :10 also work. I use the
# chat support in OVH frequently and was told you can chnage the ipv6 address in
# the OVH panel it's easier to just leave it.
# Set a Reverse PTR in OVH. Choose a hostname like mx mail smtp imap mx1 mx2 ...
# or any of your choice, we'll use something short like mx here... change as
# needed. Set the reverse path to the main mail server hostname. Do the same for
# both ipv4 and ipv6 addresses.
Bare Metal Cloud -> Dedicated Servers -> select server -> General Information ->
Network -> select 3 dots -> Manage -> 3 dots to far right -> Modify Reverse Path
-> mail.okbsd.com
# Add the static IPV6 from OVH but a prefixlen of 64 will put the router address
# provided by OVH on a different subnet which won't work. IPv6 addresses have
# 4 bits per hexidecimal character. Expand the part of the address that is
# shared with the router which is 2604:2dc0:0200:01... and count the bits to get
# the mask, 14 char x 4 bits = 56 bits.
<--- 64 bits ---->
2604:2dc0:200:0187::1
2604:2dc0:200:01ff:ff:ff:ff:ff
<--- 56 bits -->
nano /etc/rc.conf
# -- comment out the default inet6 line and add the OVH ipv6 address and router --
hostname="okbsd.com"
ifconfig_igb0="DHCP"
# ifconfig_igb0_ipv6="inet6 accept_rtadv"
ifconfig_igb0_ipv6="inet6 2604:2dc0:200:187::1 prefixlen 56"
ipv6_defaultrouter="2604:2dc0:200:1ff:ff:ff:ff:ff"
sshd_enable="YES"
ntpd_enable="YES"
ntpd_sync_on_start="YES"
moused_nondefault_enable="NO"
# Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable
dumpdev="AUTO"
zfs_enable="YES"
# dns server choices
# local_unbound_enable="NO"
named_enable="YES"
# ---
# Edit the hosts file
nano /etc/hosts
# ---
# External WAN Interface
147.135.37.135 okbsd.com mail.okbsd.com smtp.okbsd.com imap.okbsd.com autoconfig.okbsd.com autodiscover.okbsd.com
2604:2dc0:200:187::1 okbsd.com mail.okbsd.com smtp.okbsd.com imap.okbsd.com autoconfig.okbsd.com autodiscover.okbsd.com
# ---
# Check dns your resolver. For a web, dns, or mail server updating nameserver
# entries dynamically is probably not necessary and could lead to complications
# so disable the resolvconf service.
nano /etc/resovlconf.conf
# ---
resolvconf=NO
# ---
# Make sure /etc/resolv.conf is not a symbolic link, if it is remove it.
rm /etc/resolv.conf
# Update your nameservers as you like, if you are going to run your own dns server the
# first entry here should be nameserver 127.0.0.1. But until we get bind running leave
# it with valid external nameserver entries or you won't be able to download packages.
nano /etc/resolv.conf
# ---
nameserver 9.9.9.9
nameserver 1.1.1.1
search okbsd.com
# ---
# I use Namecheap as my registar and DNS is free so I don't run my own master
# zone. name servers. Add some A records and mx records to Namecheap's DNS. The
# following assumes the domain is okbsd.com and the IP ADDRESS for my OVH Cloud
# Dedicated Servers so change to your own domain name and IP addresses.
# Add DNS Records with Namecheap
Namecheap -> Account -> Domains -> DNS -> Advanced DNS
# Remove the redirect record.
# Remove the www CNAME record.
# Add New Records
A @ 147.135.37.135
AAAA @ 2604:2dc0:200:187::1
A mail 147.135.37.135
AAAA mail 2604:2dc0:200:187::1
A imap 147.135.37.135
AAAA imap 2604:2dc0:200:187::1
A smtp 147.135.37.135
AAAA smtp 2604:2dc0:200:187::1
# Add the www CNAME record ...
CNAME www okbsd.com
# Add the autoconfig and autodiscover CNAME records ...
CNAME autoconfig mail.okbsd.com
CNAME autodiscover mail.okbsd.com
Type Service Protocol Priority Weight Port Target
SRV Record _autodiscover _tcp 5 0 443 mail.okbsd.com
# Do not use CNAME, use A and AAAA records for mx, mail exchange hosts. You
# could have added another ipv6 address in rc.conf as an alias and then use one
# for mx and one for mail but this isn't necessary. We can add dmarc and spf and
# dmarc records now and will create admin@147.135.37.135 later.
# Please do not add the following 4 records unless you are setting up a mail
# server with the mailbox specified. Use any valid email address for the rua and
# ruf records but RFC guidelines require a postmaster@domain.tld mailbox or
# alias so it is a logical choice.
# ADD MX Records - scroll down in namecheap - change mx forwarding to custom mx
MX @ smtp.okbsd.com 0
MX @ mail.okbsd.com 10
# ADD DMARC and SPF TXT Records
TXT @ v=spf1 ip4:147.135.37.135 ip6:2604:2dc0:200:187::1/112 mx ~all
TXT _dmarc v=DMARC1; p=quarantine; rua=mailto:postmaster@okbsd.com; ruf=mailto:postmaster@okbsd.com; sp=quarantine
# Make sure to enable DNSSEC in Namecheap or your own provider. The toggle is
# invisible just click on the area next to DNSSEC till it appears.
Namecheap -> Account -> Domains -> DNS -> Advanced DNS -> Below Host Records -> DNS Sec
# Setup a local caching nameserver to speed up dns requests. Unbound, dnsmasq,
# and bind all have strengths and weaknesses. Dnsmasq is good with DHCP clients
# but doesn't support DNSSEC. Both bind and unbound are dnssec capable and one
# of them should be used if running your own nameserver with dkim certs. A local
# full caching DNS resolver is needed if using real time blacklists DNSRBL so
# choose either unbound or bind. Bind supports master/slave zones transfer,
# NAPTR records, and it is fast. Basic bind configuration isn't difficult.
pkg install bind920
# Enable bind in /etc/rc.conf
sysrc named_enable="YES"
service named start
service named status
rndc reload
# Check bind setup, you want to enable recursion for the server itself and any
# networks you trust. Also enable DNSSEC.
cd /usr/local/etc/namedb
nano /usr/local/etc/namedb/named.conf
# ---
options {
// All file and path names are relative to the chroot directory,
// if any, and should be fully qualified.
directory "/usr/local/etc/namedb/working";
pid-file "/var/run/named/pid";
dump-file "/var/dump/named_dump.db";
statistics-file "/var/stats/named.stats";
// DNSSEC
dnssec-validation auto;
// RECURSION
recursion yes;
allow-recursion {
127.0.0.1;
::1;
// okbsd external
147.135.37.135;
2604:2dc0:200:187::1;
// okdeb slave
15.204.113.148;
2604:2dc0:202:300::3645;
// my trusted home network(s)
trusted_net/cidr;
};
allow-query { any; };
// ...
listen-on {
127.0.0.1;
147.135.37.135;
};
listen-on-v6 {
::1;
2604:2dc0:200:187::1;
};
// if this is master, define slaves here
//allow-notify { 15.204.113.148; };
//allow-transfer { localhost; 15.204.113.148; };
//notify yes;
};
# ---
# Do not define any master zones unless you plan to run your own DNS, it's not needed
# for mail server setup.
rndc reload
# Test Bind
nslookup google.com 127.0.0.1
Server: 127.0.0.1
Address: 127.0.0.1#53
Non-authoritative answer:
Name: google.com
Address: 142.250.217.110
Name: google.com
Address: 2607:f8b0:400a:80b::200e
nslookup okbsd.com 127.0.0.1
Server: 127.0.0.1
Address: 127.0.0.1#53
Name: okbsd.com
Address: 147.135.37.135
Name: okbsd.com
Address: 2604:2dc0:200:187::1
# Make sure resolv.conf isn't a symbolic link, if it is remove it and recreate it.
ls -l /etc/resolv.conf
-rw-r--r-- 1 root wheel 91 Aug 1 12:03 /etc/resolv.conf
# Modify /etc/resolv.conf to point to your local bind server.
nano /etc/resolv.conf
# ---
nameserver 127.0.0.1
nameserver 1.1.1.1
nameserver 9.9.9.9
options edns0 trust-ad
search .
# ---
# Check if DNSSEC works, look for for 'ad' flag.
dig @8.8.8.8 okbsd.com +dnssec +multiline
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
# Bind on the server itself will display the 'aa' flag.
dig @127.0.0.1 okbsd.com +dnssec +multiline
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
# Firewall Setup - Once you start enabing services and adding DNS entries random
# addresses will start hitting your server trying to find a way in for nefarious
# purposes, so you will need a firewall. On FreeBSD I recommend a kernel based
# firewall called ipfw. I set ipfw default to accept so I won't get locked out
# if there is a script error. The last rule in the script is a global deny all.
# I have used ipfw for nat so I usually enable it. When making changes copy the
# script to a temporary file and test that first before copying it to the active
# script used at boot.
nano /boot/loader.conf
# --- add these lines to end
ipfw_load="YES"
ipfw_nat_load="YES"
net.inet.ip.fw.default_to_accept="1"
# ---
nano /etc/rc.conf
# --- add these lines
# ipfw firewall
firewall_type="open" # Firewall type (see /etc/rc.firewall)
firewall_script="/etc/ipfw.rules"
firewall_enable="YES"
firewall_logging="YES" # Set to YES to enable events logging
# gateway_enable="YES"
# firewall_nat_enable="YES"
# firewall_nat_interface="igb0"
ipv6_firewall_enable="YES"
ipv6_firewall_type="open" # see rc.firewall6 for what goes here
ipv6_firewall_quiet="NO"
ipv6_firewall_logging="YES"
ipv6_firewall_script=""
# ---
# Double check your default gateway
netstat -r
Destination Gateway Flags Netif Expire
default 147.135.37.254 UGS igb0
# Make your live firewall rules, completely open for testing
nano /etc/ipfw.rules
# ---
#!/bin/sh
ipfw -q -f flush
fwcmd="ipfw -q"
# Global Allow
${fwcmd} add allow all from any to any
# ---
# Make your test firewall rules, to test manually. If you get locked out reboot
# your server and it will run the file above and allow access to modify the test
# file again. Run the test, if not locked out copy to the live file.
nano /etc/ipfw.testrules
# --- change your addresses and interface
#!/bin/sh
ipfw -q -f flush
fwcmd="ipfw -q"
oif="igb0"
ip="147.135.37.135"
net="147.135.37.0/24"
gateway="147.135.37.254"
mask="255.255.255.0"
ip6="2604:2dc0:200:187::1"
net6="2604:2dc0:200:187::1/56"
gateway6="2604:2dc0:200:1ff:ff:ff:ff:ff"
${fwcmd} add allow ip from any to any via lo0
${fwcmd} add allow ipv6 from any to any via lo0
${fwcmd} add deny log ip from any to 127.0.0.0/8
${fwcmd} add deny log ip from 127.0.0.0/8 to any
# deny ipv6 to local if not lo0 above
${fwcmd} add deny log ipv6 from any to ::1
${fwcmd} add deny log ipv6 from ::1 to any
# Test - Global Allow
# ${fwcmd} add allow all from any to any
# Test - SSH TEMP
# ${fwcmd} add allow tcp from any to me 22 in via ${oif}
# You need the link-local addresses for IPv6's equivalent of ARP, which is
# done with ICMP and uses link-local addresses. Removing the link-local
# addresses would be similar to blocking ARP on IPv4, it's going to stop
# all communication.
${fwcmd} add allow ipv6-icmp from :: to ff02::/16
${fwcmd} add allow ipv6-icmp from fe80::/10 to fe80::/10
${fwcmd} add allow ipv6-icmp from any to any icmp6types 128,133,134,135,136,137
# deny all other ipv6 link local connections
${fwcmd} add deny log ipv6 from fe80::/10 to any
${fwcmd} add deny log ipv6 from any to fe80::/10
# Allow limited broadcast traffic from my own net.
${fwcmd} add pass all from ${net} to 255.255.255.255
# Allow IP fragments / Reassemble inbound packets
# ${fwcmd} add allow all from any to any frag in via ${oif}
# Reassemble packets - initial ssh connections will be faster is allow reassemble
${fwcmd} add reass all from any to any in via ${oif}
#${fwcmd} add deny all from any to any frag in via ${oif}
# PING6 - allow all ipv6 ping
${fwcmd} add allow ipv6-icmp from any to any
${fwcmd} add allow icmp from any to any
# Stateful Outbound DNS - only keep state for these
# Stateful rules are needed to resolve ipv6 mail server hostname
# For better preformance use as few stateful rules as possible
${fwcmd} add allow ip from me to any proto udp dst-port 53 keep-state
${fwcmd} add allow ip from me to any proto udp src-port 53 keep-state
# Stateful Outbound Rules - OUT IPV4:IPV6
# Enable these instead if if the stateless rules below don't work
# ${fwcmd} add allow tcp from me to any setup keep-state
# ${fwcmd} add allow udp from me to any keep-state
# ${fwcmd} add allow icmp from me to any keep-state
# Stateless Inbound Rule - for high performance servers
${fwcmd} add allow tcp from any to me 25,80,143,443,465,587,993 in via ${oif}
# Stateless Inbound DNS authoritative server
${fwcmd} add allow udp from any to me 53
${fwcmd} add allow tcp from any to me 53
# openvpn all allow / this is not needed / allow only trusted nets
# ${fwcmd} add pass tcp from any to me 1194 in via ${oif}
# IPv6 Rules
# Other Server
${fwcmd} add allow ipv6 from ff75:a791:abcd:dcba:1234::1 to me6 in via ${oif}
# Home
${fwcmd} add allow ipv6 from ff87:a5ab:27bd:15::/64 to me6 22 in via ${oif}
${fwcmd} add allow ipv6 from ff87:a790:b700::/40 to me6 22 in via ${oif}
${fwcmd} add allow ipv6 from ff87:a7e2::/32 to me6 22 in via ${oif}
# Stateless Outbound Rule - for high performance servers
${fwcmd} add allow tcp from me to any out via ${oif}
# Reassemble inbound packets
${fwcmd} add reass all from any to any in via ${oif}
# Deny IP fragments
${fwcmd} add deny all from any to any frag in via ${oif}
# Stateful Allow Established - IN IPV4:IPV6 - including DNS
${fwcmd} add allow ip from any to any established
# LAN6 - deny ssh allow other
${fwcmd} add deny log tcp from ${net6} to ${net6} 22 in via ${oif}
${fwcmd} add allow tcp from ${net6} to ${net6}
# IPv4 Rules
# LAN4 - deny ssh allow other
${fwcmd} add deny log tcp from ${net} to ${net} 22 in via ${oif}
${fwcmd} add allow tcp from ${net} to ${net}
# Other Server
${fwcmd} add pass all from 192.168.77.77 to me in via ${oif}
# Home Internet Example
${fwcmd} add pass tcp from 178.25.25.10:255.255.0.0 to me 22 in via ${oif}
# Office Network SSH PING TRACEROUTE - no ipv6
${fwcmd} add pass tcp from 10.77.21.0/24 to me 22 in via ${oif}
${fwcmd} add pass icmp from 10.77.21.0/24 to me
${fwcmd} add pass udp from 10.77.21.0/24 to me
# Trusted Location SSH PING TRACEROUTE - single ip
${fwcmd} add pass tcp from 172.16.17.18 to me 22 in via ${oif}
${fwcmd} add pass icmp from 172.16.17.18 to me
${fwcmd} add pass udp from 172.16.17.18 to me
# deny and log all other SSH requests
${fwcmd} add deny log tcp from any to me 22 in via ${oif}
# Global Deny - with logging
# ${fwcmd} add deny log ip from any to any
# Global Deny - without logging
${fwcmd} add deny ip from any to any
# ---
<reboot>
shutdown -r now
# The server should come back up and run /etc/ipfw.rules which doesn't block
# anything. Run the test rules.
ipfw list
sh /etc/ipfw.testrules
ipfw list
# Confirm you still have access to the server.
ssh root@147.135.37.135
# After access is confirmed copy the test file to the live file which will be used
# on reboots.
cp /etc/ipfw.testrules /etc/ipfw.rules
# To make changes edit /etc/ipfw.testrules.
nano /etc/ipfw.testrules
# ---
<make changes>
# ---
sh /etc/ipfw.test.rules
# Test and if not working, reboot from OVH console to use the last working
# ipfw.rules. If ok...
cp /etc/ipfw.testrules /etc/ipfw.rules
# Some of the keep-state rules may be redundant but the keep-state 53 rules
# was needed for IPv6 DNS issues.
# Check your active rules with ...
ipfw list
# Continue with FAMP setup on the next page.