Host a server with OpenBSD
- Disclaimer
-
A good start
- Why OpenBSD?
- Self-hosting: What is it?
- About this document
- What hardware should I use ?
- OpenBSD install
- Can someone else host OpenBSD for me?
-
Survival guide: which commands do I have to know?
- Tip #1: Tab
- Tip #2: ctrl-c
- Tip #3: "\"
- Tip #4: order history
- Tip #5: send a process to the background
- su and doas: How to get superuser privileges (root)
- ls: list the contents of a directory
- chmod: change permissions
- "symbolic" chmod
- "absolute" chmod
- chown: Owner and group
- File management
- pwd: display the current folder
- mkdir: create a directory
- cd: change directory
- cp: copy
- rm: delete
- mv: move
- less: reading (and searching through) a file
- man: to read manuals
- vi: to edit a file
- rcctl: Managing daemons
- Let's practice with commandline
- What sould you do after first boot ?
- Network addresses
- Reminder about network
- Configure your router and redirect ports
- Get a domain name
- DNS records
- Manage your server
- Keep your server on time (ntpd)
- Host a website (httpd)
-
About hosting emails
- DNS configuration for an email server
- Get certificates
- A mail server in 10 minutes (almost ^^)
- Full mail server with virtual users
- Manage multiple domains
- Redirecting mails
- How to configure you mail client ?
- DNS record for clients configuration (facultative)
- Do not loose mails : MX fields and backup
- Do not be considered as a spam (SPF, DKIM...)
- Blocking ISP : use external SMTP
- Avoid receiving spam : senderscore filter
- Avoid receiving spams : spamassassin
- Avoid receiving spams : rspamd
- Manage spams with dovecot
- Fight against spams before receiving one : OpenBSD's spamd
- Check everything works well
- What if smtp server doesn't work as expected?
- Domain name server
- Virtualization
- What is a VPN?
- Various
-
Tips, FAQ, comments...
- About apu2
- Check file checksum
- How to change password
- Periodic tasks (cron)
- Disk Partitioning
- How to find my network interface ?
- Logs
- Check for improvements with lynis
- How to deal with power loss ? Where my mails are going ?
- I have a problem, what can I do ?
- Generate good random passwords
- Split and reassemble files
- How to add/delete users?
- SSH Tunnel : Proxy or VPN
- Configuration examples
- Notes
Disclaimer ยง
โน๏ธ The english translation of this doc is quite new. Please be kind as I read again and fix missed mistakes. Feel free to suggest improvements.
This document is published under CC-BY license
It was written by an OpenBSD enthusiast (and contributors) who figured "If I can do it, anyone can".
There are no ads or tracking scripts on this site.
Only your contributions help me to pay hosting and time given to this project.
If you want and can, you can donate to help me. Thank you! โฅ
Do not hesitate to print this documentation on paper or on PDF ๐ณ. CSS is ready for a nice print rendering ๐.
Enjoy!
A good start ยง
You are about to dive into the world of self-hosting. This document is written to help you host at home or on a dedicated (rented) server some services unfortunately too often entrusted to third parties. The main goal is to keep things as simple as possible while learning gradually.
Of course, compromises were made. If you feel you want to learn more after reading this, that's great! ๐
Why OpenBSD? ยง
It is known to be safe.
It is also, in my opinion, easy to configure because the same syntax is shared by different included tools.
People say the OpenBSD documentation is great. How can this documentation be useful to me?
It is for sure! The manpages are amazing, use them as a reference. I see this documentation as an entry point, not a replacement, explaining the fundamentals along with a few tips. Anyway, manpages are great and you should read them too.
You'll see, hosting your server isn't that difficult and is mostly text-editing. Everyone should be able to do it.
Let's go!
Self-hosting: What is it? ยง
Most website you're used to reading -- emails, social networks... -- are hosted on computers somewhere in the world. They are only used to serve content to other computers, so we call them "servers". The biggest difference from most people's point of view is that "they don't have a screen".
When you want to read your mail, a client (a webmail, Thunderbird...) asks the server to retrieve your messages. A copy of them is then downloaded on your computer. In "real life", that would look like this:
Hey, mailman, do you have anything for me?
Yes, a postcard from your mom. I'll give it to you as soon as the copier has finished printing it.
Of course, you can ask the post office to delete the message. But how can you be sure ALL copies have been deleted?
Better become your own post office, don't you think? ๐
At first, everyone was supposed to make a part of the web. Now, most of us depend on private companies that don't respect our privacy.
Pros of self-hosting ยง
- Data stays at home. You retain control of it. You can be confident that your documents aren't found on a hard drive thrown out after Google renewed its hardware, or worse, sold to someone else.
- Your privacy is safe. Your mails aren't scanned to suggest targeted advertisement in your browser.
- You can have services that suit your needs.
- You can use low-powered hardware and be more environmentally friendly.
- Self-hosting is fun and rewarding.
Arguing that you don't care about the right to privacy because you have nothing to hide is no different than saying you don't care about free speech because you have nothing to say. -- E. Snowden
Read about the argument "Nothing to hide"
Cons of self-hosting ยง
- It's time consuming.
- The bandwidth of your internet connection might not be enough depending what you want to host. Without optical fiber, movies and big data transfer could be tricky, though not impossible. No problem for email.
- You have to take care of security. Fortunately, we use OpenBSD.
About this document ยง
In this document, we assume that:
- You use OpenBSD
- You understand that commands beginning with "#" mean "run as root or superuser" and those beginning with "$" are executed as regular user.
- Sometimes, we'll use "*". This means it must be replaced by anything that matches your needs. For example, "John D*" can mean "John Doe" or "John Deer", depending on the situation. Actually, it means both ๐
What about the official FAQ? ยง
This document is not a duplicate of the official OpenBSD FAQ. You should ALWAYS refer to official documentation and manpages when available.
For those who need to read the FAQ offline, I keep an up-to-date archive downloadable from my server.
openbsd-faq.tgz: download and extract to read it offline.
What hardware should I use ? ยง
You don't need phenomenal power to self-host. Start with a machine you have retired because it is too weak for office use.
If you want to buy new hardware, check if it's suported by OpenBSD first.
ARM architectures don't require much power.
If you don't know where to start, APU are quite amazing: not too expensive, small, silent, they require less than 10W and are well supported. Actually, this documentation is hosted on an apu2d0.
Take a look at bsd-hardware too and do not hesitate to contribute.
What about OpenBSD on a Raspberry Pi ? ยง
RPi are supported from 3rd version.
Read the instructions carefully as you will need some files too boot correctly.
OpenBSD install ยง
Make sure you read the official documentation on installing OpenBSD.
There really isn't much to say here because the installer explains a lot.
When in doubt, just use the defaults ๐. This is especially true for disk slicing.
Read the commands for the disklabel editor if necessary.
Oh, you may want to set up Full Disk Encryption during installation by the way.
You also should install all the sets or make sure you understand what you don't want to install.
Can someone else host OpenBSD for me? ยง
If you don't have reliable internet access, or if you want to experiment before setting it up at home, you can rent a server or a virtual host. I've had good experience with:
They offer virtual machines. The team help develop OpenBSD. Mischa is very nice.
Works very well. It's fast and reliable. A bit more expensive though. (The above link is a refferer link).
Similar setups can be found at ARP Networks:
Survival guide: which commands do I have to know? ยง
When you turn on your server, whether it is connected to a monitor or through SSH, you will see a command prompt:
acdc $ โ
Enter commands to manage your server.
There are a lot of them, however we usually use a few of them daily. You'll find more over time.
For now, let's see a few of them. Don't try to memorize everything all at once -- come back and get what you need when the time comes.
Tip #1: Tab ยง
By using the "tabulation" key โน, you can complete a command or a path to a file. Start writing the beginning, then press โน.
- There is only one possibility: the order is automatically completed, just press Enter โ.
- There are several possibilities: press tab a second time to see a list of proposals.
Tab is a-ma-zing! ๐
Tip #2: ctrl-c ยง
To erase what you are writing, press "ctrl" and "c" simultaneously. This will also stop most process.
Tip #3: "\" ยง
Although this is very rare, some filenames sometimes contain spaces " " or even strange symbols. However, a space is interpreted as a separator between files, so the shell will assume that you are referring to several files instead of just one.
In this case use "\" to "escape" the weird symbol. That way the shell will ignore it. For example:
/path/to/some/file\ with\ spaces.txt
In any case, avoid creating files with strange names.
If you need to treat a large number of them, check out the "detox" tool (port of the same name).
Tip #4: order history ยง
To find the history and quickly relaunch an old command, use the shortcut ctrl-R.
You must first enable it by adding this line to the file "~/.profile":
$ echo "export HISTFILE=~/.history" >> ~/.profile
The next time you log in, the history will be active.
Tip #5: send a process to the background ยง
You can send a process to the background with "ctrl-z".
You can now run new processes.
To resume the previous process when ready, run 'fg'.
If you have multiple processes in the background, run 'jobs -l'. Example:
> jobs -l [2] + 65085 Suspended systat vm [1] - 99389 Suspended top -s .5
'fg' will restore the previous process sent to background, in this case systat (+). You may also specify the desired job by its id:
$ fg 99389 # resume top
See the ksh(1) manpage for more :)
su and doas: How to get superuser privileges (root) ยง
Enter the command 'su -l', followed by the password for the root user.
WARNING: For this to work, your user must belong to the wheel group. This is the case for the first user created on a system.
You can also configure doas to run a command with superuser privileges as follows:
doas command
Edit/create the /etc/doas.conf file and add:
user-name permit
Replace "user-name" with your, well, username.
ls: list the contents of a directory ยง
Enter 'ls' on its own to list the current folder, or follow it with a path to some other folder.
The "-l" option also allows you to display permissions, owners, sizes and modification dates.
Example:
$ ls -l /etc drwxr-xr-x 7 root wheel 512 Apr 19 19:12 X11 drwx------ 2 root wheel 512 Apr 19 18:16 acme -rw-r--r-- 1 root wheel 1542 Apr 13 15:39 acme-client.conf -rw-r--r-- 1 root wheel 1764 Nov 28 13:56 adduser.conf drwxr-xr-x 2 root wheel 512 Apr 19 18:16 amd drwxr-xr-x 2 root wheel 512 Apr 19 18:16 authpf -rw-r--r-- 1 root wheel 30 Aug 2 2020 boot.conf [...]
We get one line per file/folder. Each line has these fields:
<permissions> <inode> <owner> <group> <size> <date of last access> <file name>
A simple and yet efficient method to secure your website -- and the server it lives on -- is to adjust the permissions and the owner of its files.
Read the sections on 'chmod' and 'chown' to learn more.
chmod: change permissions ยง
Let's take a closer look at what the return of the 'ls -l' command from earlier tells us.
The letters at the beginning of the line describe the permissions granted to the file. We can remember two things:
1. If the first character is "d", then it is a directory. Otherwise, it is a file (with exceptions).
2. The remaining characters are read by set of 3. Each "triplet" describes the permissions for the owner, for the group, and for everyone else, respectively.
For example, for this line:
drwxr-xr-x 2 www daemon 512 May 5 17:10 bin
We see that it is a directory. Then we see three triplets of letters:
rwx: The owner www can:
- Read it: "r"
- Write inside: "w"
- Execute it (move in for a directory): "x"
"r-x": Those belonging to the "daemon" group can
- Read it: "r"
- Execute it (move inside): "x"
"r-x": All others can:
- Read it: "r"
- Execute it (move inside): "x"
As a general rule, you should avoid giving write and execute rights to people other than the owner whereever you can. Sometimes, reading permission is also withdrawn on certain files (passwords, etc.).
To change permissions, there are several methods.
"symbolic" chmod ยง
Some use a set of numbers, like 'chmod 700'. I find this way not very explicit when you are not used to it yet. You may prefer to use chmod <identity>ยฑ<permission> where:
- <identity> can describe user (u), group (g), others (o) or all at once (a).
- + or - to add or remove permissions to for that identity
- <permission> which can be x, r or w.
Would you like a few examples?
- chmod g+r file: grants read permission to group members.
- chmod o-w file: denies write permission to people who are not members of the group or owners of the file.
- chmod og-rwx file: removes read, write, and execute permissions from the file for group members and other users.
These changes can be applied recursively (to everything below that folder) with the -R flag.
Tip: to allow moving in folders, without making the files executable, use X (uppercase) instead of x.
"absolute" chmod ยง
If you want to understand the numerical notation of a chmod:
- "r" is equivalent to 4.
- "w" is equivalent to 2.
- "x" is equivalent to 1.
There is no distinction between folders or files, so proceed with caution.
In the triplet passed to "chmod", the first digit represents the owner, the second the group, and the last the others.
For each digit of this triplet, we add the values that "r", "w" and "x" stand for. This means that "chmod 700" grants "rwx" permissions to the owner, and none to the group and others (7 = 4 + 2 + 1).
Finally, in order to define the permissions by distinguishing between the folder and the files, and not to make a file executable with a "chmod -R" (recursive), the "find" command is your friend:
- For folders: "find . -type d -exec chmod 755 {} \;"
- For files: "find . -type f -exec chmod 644 {} \;"
As always, the "man chmod" command will tell you more.
chown: Owner and group ยง
Each file has an owner and is part of a group. This will allow us to give certain permissions to the owners, which will not necessarily be the same as those given to the group member.
To modify the owner and the group, we use the chown command.
# chown <owner>:<group> filename
File management ยง
Before we talk about how to handle files, let's look at some key shorthand.
- "~" and "$HOME" refer to the current user's directory (/home/batman)
- "." refers to the current folder, the one we are in.
- ".." refers to the parent folder. If we are in the "/home/batman/documents" folder, then ".." refers to "/home/batman".
pwd: display the current folder ยง
With pwd you ask "where am I" ๐
mkdir: create a directory ยง
$ mkdir name_of_new_folder
Use the "-p" option to create a whole structure at once:
$ mkdir -p ~/folder/with/some/subfolders
cd: change directory ยง
To move to the "/var/www" folder:
$ cd /var/www
The "cd" command without argument moves you to your "$HOME".
cp: copy ยง
To copy a file:
$ cp source_file copy_file
To copy a folder and its contents:
$ cp -R source_folder copy_folder
rm: delete ยง
$ rm path_to_the_folder $ rm -R path_to_the_folder
mv: move ยง
$ mv source destination
This works like cut and paste.
less: reading (and searching through) a file ยง
To only view a file, use the "less" command.
Then you can search for any string of characters by pressing "/" and entering your search. Press "n" to go to the next occurrence, or "N" to go back.
To exit less, press "q".
This command will come in handy if you want to search through the content of your logs ๐.
man: to read manuals ยง
Here is the real reason why there aren't many support forums for OpenBSD but only a mailing list: the man pages are very comprehensive, complete with examples, and most of the time are sufficient to answer questions/problems encountered.
The "man" command allows you to display a man page.
Note that there are different sections for categorizing man pages:
- Section 1: general orders
- Section 3: for developers
- Section 7: for miscellaneous informations
- ...
Also, it sometimes happens that a man page exists in several different sections: its content is not the same. In order to differentiate them, one refers to a manpage as follows: "page_name(section)".
For example: "apm(8)", or "apm(4)". Or "man(1)" and "man(7)". Yes, "man" has a "man" page.
We use this command as follows, without parenthesis:
$ man (section) page
The section is optional.
To practice, run "man hier". Use arrows to scroll. As with "less", you can search with "/". Notice the "SEE ALSO" part which invites you to read other manpages that may be of interest. Exit with "q".
If you don't know what the name of the man page is, you can search for it with the "apropos" command:
$ apropos search_term
vi: to edit a file ยง
Knowing how to edit a file is crucial.
There are a lot of text editors (vim, nano ...).
vi The default editor on OpenBSD is vi. (There is ed too...)
It may be confusing to use at first, so some people may want to install another editor instead. However, vi is handy once you get it. If, on the contrary, you are already used to the emacs editor, you will find what you are looking for with the mg editor, also available by default.
Here are some tips for using vi through an example. To edit the /etc/iloverocknroll file, you would enter this:
$ vi /etc/iloverocknroll
The contents of this file will then appear in the terminal.
Most of the time, you will only do this:
- 1. Press the "i" key to be able to write (enter insert mode). You edit the file.
- 2. Press "esc" to exit insert mode.
- 3. Save and close vi by typing ":wq" then "Enter". (write, quit).
Are you still here ? ๐
So let's go a little further (but not too much, we promise ๐). Take note that there are three modes:
- The "visual" mode: you can move around the file with the "h", "j", "k", "l" keys.
- "Insert" mode: you can write text. You enter this mode with the "i" key. To exit, press "esc".
- The "edit" mode: this one is less useful when you start. You can make sweeping changes quickly, for example replacing text or deleting several lines at once using regex.
To save the changes, press ":" then "w". Confirm with enter. We can now quit by writing ":q". Note that you can go faster by typing directly ":wq".
To cancel a modification press "u". To remove multiple changes, press "u" then "." as many times as necessary. "." allows you to repeat the last action. To redo an action you undid previously, use ctrl-R
In order to search for a string, which is very useful in large files, press the "/" key and enter a search term.
If you want to exit without saving your changes then type: "q!".
Other very handy tips:
- cw: allows you to change a word
- c$: allows you to move the cursor to the end of the line
- 3G: go to line number 3
- ma: places an "a" mark at the cursor location. To get back to it quickly then, you will enter "'a". You can do this with all 26 letters.
- dd: delete the line.
- yy: copy the line.
- p: paste the copied or deleted line just before.
- d'a: remove from the cursor to the "a" mark.
- y'a: copy from the cursor to the "a" mark.
rcctl: Managing daemons ยง
In order to activate/deactivate daemons, the "rcctl" command is provided for this purpose. All available services are in the "/etc/rc.d" folder. Here is a short overview:
- Activate a service when the machine starts up: rcctl enable service
- Start a service: rcctl start service
- Stop a service: rcctl stop service
- Restart a service: rcctl restart service
- Reload a service: rcctl reload service
- Modify the options of a service: rcctl set service flags "-v -option"
If you prefer the manual method, then you can directly edit the "/etc/rc.conf.local" file which manages the services launched at startup.
Let's practice with commandline ยง
Let's practice with a little exercise. Follow the instructions below, then check if you got same thing as in the "Answer". Try to do it from memory first. If you get stuck, read the page again and look for what you are missing.
- Go to the "/tmp" folder.
- Create an "ah" folder, then move into it.
- Create a new file in which you will write a famous quote: "Allons-y". This file will be named "dw.txt".
- Make a copy of this file named "DrWho.txt" and delete "dw.txt".
- Make this file readable and writeable only for you.
- Go back to your personal folder.
- Display the name of the current folder.
- List the contents with the permissions of the "/tmp/ah" directory
- Display the contents of the "/tmp/ah/DrWho.txt" file.
The answer :
$ cd /tmp $ mkdir ah $ cd ah $ vi dw.txt $ cp dw.txt DrWho.txt $ chmod 600 DrWho.txt $ rm dw.txt $ cd ~ $ pwd /home/prx $ ls -l /tmp/ah total 2 -rw------- 1 prx wheel 22 May 5 21:10 DrWho.txt $ cat /tmp/DrWho.txt Allons-y
Note that we can replace "cd ~" by "cd $HOME", or even by "cd".
What sould you do after first boot ? ยง
First, read "afterboot(8)" and "intro(8)" manpages. Believe it or not, OpenBSD's developpers wrote a few words for you after install โ
$ man afterboot $ man intro
You'll learn how to read manpages actually.
Look configuration examples ยง
See examples in "/etc/examples".
If you add ports, read included instructions un "/usr/local/share/doc/pkg-readmes".
Set admin mail ยง
Edit "/etc/mail/aliases" and set root mail to get important reports:
root: batman@athome.tld
Shut down daemons ยง
You may want to disable non used daemons, such as sndiod in charge of audio (unless you need it of course).
By the way, let's talk about "rcctl" to deal with daemons.
List enabled daemons :
# rcctl ls on
To stop daemon, i.e audio server :
# rcctl stop sndiod
And to disable daemon for next boot :
# rcctl disable sndiod
You probably want to enable power management. As an example, below lines will enable apmd, add "-A" flag to daemon startup and actually start apmd :
# rcctl enable apmd # rcctl set apmd flags -A # rcctl start apmd
Read intro(8) to learn about default daemons availables on OpenBSD :
man 8 intro
Network addresses ยง
What is a network address ? ยง
IP means Internet Protocol. But first, let's talk about Santa ๐ .
To send a letter asking for all the presents you want, you write to the famous address "Santa Claus, North Pole, Rainbow road.". But how does Santa knows how where to answer? Of course you wrote your own address on the back of the envelope.
That's how works computers and servers : you know where is the video you want to watch, and the server hosting the video knows where to send data.
Every device has its own IP on the internet. There are numbers like 192.0.2.2, not as good looking as Santa's pretty address isn't it?
How devices knows what is the IP of domain name like wikipedia.org ? It there a directory?
Indeed there is something like that : DNS. We'll talk about it later.
There are differences between local (or private) IP ans public IP. Most of the time, to reach the internet, your ISP lend you a router so your network look like this :
+-----------------------+ | | | Local Network (home) | | PRIVATE IP | | | | | | laptop <---+------+ | 192.168.1.20 | | | | | | | | | Phone <---+--+ ++---------------+ | 192.169.1.21 | | | | | | +--+ Router (box) | +----------+ | | | |<---+ INTERNET | | Computer <---+-----+ PUBLIC IP: | +----------+ | 192.168.1.22 | | 192.0.2.2 | | | +--+ | | * * * * * * * * * | | ++---------------+ | * Your server <---+--+ | | * 192.168.1.23 * | | | * * * * * * * * * | | | | | | Game console <--+------+ | 192.168.1.24 | | | +-----------------------+
- Private IP is used between devices inside local network.
- Public IP is used outside the local network, mostly on the Internet.
To host a server, it is useful to have a satic IP delivered by your ISP. Some don't, so you may want to look for a VPN or dynamic DNS or change ISP :)
Reminder about network ยง
To run your own server, you must know what are its IP, public, local, and its gateway (router). At first, one may want to configure its network using dhcp then configure a static network when more confident.
Let's see a few words to do so.
Public or local/private address ยง
First, understand your server is probably behind a router given by your ISP. The router received a public IP. People "on the internet" can reach this IP.
The router allocate private addresses to devices connected in your local network. Each device has its own private IP : one for your phone, another for a printer, a computer, a console... They all share one public address.
Private addresses can be in three ranges :
- 10.0.0.0/8 : from 10.0.0.0 to 10.255.255.255.
- 172.16.0.0/12 : from 172.16.0.0 to 172.31.255.255
- 192.168.0.0/16 : from 192.168.0.0 to 192.168.255.255
Your private or local IP is the one your computer use, the one it knows.
To find your local IP, run "ifconfig" and filter for "inet". You'll see something like that :
# ifconfig |grep inet inet6 ::1 prefixlen 128 inet6 fe80::1%lo0 prefixlen 64 scopeid 0x3 inet 127.0.0.1 netmask 0xff000000 inet 192.168.1.2 netmask 0xffffff00 broadcast 192.168.1.255 inet6 fe80::feaa:14ff:fe65:5f86%re0 prefixlen 64 scopeid 0x1
inet means IPV4, inet6 means IPv6.
- ::1 and 127.0.0.1 are localhost, that's not what we are looking for.
- addresses starting with fe80: and 192.168 are local IP. The first is IPv6, the latter is IPv4.
Your public address is the one "used" when you browse the web. Websites you're used to read see this IP. If you don't know it, you can check one of these links (amongst many others):
Router ยง
The router is the box your ISP gave you. We also call it a gateway because on a side it is the "public" network, on the other your "private network". The router deals with redirections : if someone wants to reach your website, the router will lead him to your server's private address.
To find your gateway IP, use "route" command:
# route -n show Routing tables Internet: Destination Gateway Flags Refs Use Mtu Prio Iface default 192.168.1.1 UGS 35 4827 - 8 re0 224/4 127.0.0.1 URS 0 13 32768 8 lo0 127/8 127.0.0.1 UGRS 0 0 32768 8 lo0 127.0.0.1 127.0.0.1 UHhl 9 969 32768 1 lo0 ...
The first line is what you're looking for : 192.168.1.1 in this example.
Hostname ยง
Every device in your network has a name to identify it. It is the hostname.
On a computer, you can change this name. To do so on OpenBSD, edit the /etc/myname file and write the complete hostname:
server.athome.tld
DHCP ยง
To configure a network interface, you can use DHCP to autmatically get a local address. Be careful though, the "D" means "Dynamic", so this addres might change in the future depending on your router.
To do so, edit /etc/hostname.if file, and replace ".if" by the name of your inteface (run "ifconfig" to find it) and just write "autoconf" inside.
autoconf up
Static configuration ยง
A bit more complicated, static configuration gives you more control.
In /etc/hostname.if (replace "if" with your interface name) :
inet 192.168.1.2 255.255.255.0 192.168.1.255 inet alias 192.168.1.9 255.255.255.0 192.168.1.255 # and as many as you want # inet (alias) local_ip network_mask broadcast_add up
If you're not sure, at first configure using dhcp and look at the output of "ifconfig".
You must specify the route to your gateway. To do so, you can edit "/etc/mygate" file or add a "!route" command to "/etc/hostname.if". I prefer the latter as it avoid to split configuration in multiple places.
The gateway is you router's IP : 192.168.1.1 here.
inet 192.168.1.2 255.255.255.0 192.168.1.255 inet alias 192.168.1.9 255.255.255.0 192.168.1.255 !route add -inet default 192.168.1.1 up
See also :
IPv6 ยง
IPv6 is the new protocol to use facing the lack of ipv4 available. The main advantage is that you don't need a router doing NAT : your machine is directly reachable.
To configure your interface, magic happens in /etc/hostname.if with the "inet6" lines:
inet... inet6 2001:db8:1:1::2 64 # adresse inet6 autoconf # SLAAC, if you want !route add -inet6 default 2001:db8::1 up
You can mix inet and inet6 instructions.
Configure your router and redirect ports ยง
Most of your devices access the Internet through a router, probably given by your ISP. The router has a public IP address, making it reachable from the outside. We must learn how to tell the router to forward requests from the outide to your server (not anything else) depending which port number is used.
You can imagine your router as a big wall when seen from the outside. In this wall, there are doors : we call them "ports". When someone knocks at the door 80, he want to see a web serveur. Your router has a configuration line for this cas and will redirect the visitor to your server internal IP.
Most of the doors will remain closed, because you didn't configure a redirection for them, and that's fine to keep your other equipments safe.
+---------------+ | | | Router (box) | | *********** | | | +--------+-- door 443 | +---------+ | | | | |<--+ | | | Server | ?<-+---door 143 <-+--- [Evil bot ๐ฟ] | |<--+ | | +---------+ | | | ^ +--------+-- door 80 <--+--- [Guest ๐ผ] | | | | | | +-------------+-- door 22 | | | +---------------+
Your router is a GATEWAY with a janitor.
Understand that if your server is reachable with IPV6, no redirection is required : its IPV6 is routable and reachable. That's why it is important to always configure the firewall on your server too.
To configure your router, it might have a web interface reachable using a browser from a computer connected on your local network. Some ISP provide a configuration tool on their web panel. It depends, you'll have to find out or ask your ISP how their router work.
Most of the time, you can try these URI in a browser :
- http://192.168.0.1
- http://192.168.1.1
- http://192.168.1.254
Probably, a username/password will be required. Ask your ISP, or try "admin/admin" or "admin/password", or look what's written on the router.
Here are more clues :
https://www.wikihow.com/Configure-a-Router
DMZ ยง
If you do not want to bother with redirectionc, configure a DMZ to your server on your router : all traffic will be redirected to your server. If so, remember to configure your firewall accordingly ๐.
Get a domain name ยง
Instead of writing your public IP, someone might want to reach your server using something more human friendly : a domain name.
What is a domain name ?
One can register a domain, i.e. "something-cool.tld" as a sign pointing to your IP address. When an human enter "something-cool.tld", the computer will ask the registrar what IP is behind this sign to reach it. In this case, the domain is resolved.
It is easier to remember, it gives identity to your project, and it let you organise it using subdomains (webmail.athome.tld, cloud.athome.tld...).
Below is a little comic to explain a DNS query :
You can find free registrar, or rent a domain name. There are many registrars out there. As examples:
DNS records ยง
When a device try to reach "athome.tld", it ask a DNS resolver what IP is behind. DNS is like road signs.
To create this sign, you must link the domain name to your IP in a zone. It can be done in registrar panel if you don't host your own domain name server (see nsd later ๐). As example :
athome.tld A 192.0.2.2
Different records exists. You must at least know:
- A : point to an IPv4.
- AAAA : point to and IPv6.
- MX, NS, TXT... used for mail servers and domain name servers.
- CNAME. Sort of aliases, pointing to an A record. Useful to set subdomains :
blog.athome.tld CNAME athome.tld wiki.athome.tld CNAME athome.tld webmail.athome.tld CNAME athome.tld
Learn more with these links :
https://en.wikipedia.org/wiki/Domain_Name_System
Manage your server ยง
Once your server running, you must make sure everything works fine. Do not read any further if you don't plan to maintain it. Luckily, OpenBSD developpers made things easier.
Daily reports ยง
Every day (infact every night) a report from "Charlie Root" is generated and mailed to user "root". Inside, you can find :
- Space remaining on disk
- What file has been modified and what changed. If you didn't edit them, there might be a problem.
- New installed packages.
- ...
I look like this:
Running security(8): Checking mailbox ownership. user spamd.black mailbox is owned by root user spamd.black mailbox is -rw-r--r--, group wheel ====== /etc/rc.local diffs (-OLD +NEW) ====== --- /var/backups/etc_rc.local.current Sat Jun 4 03:46:48 2016 +++ /etc/rc.local Thu Oct 20 09:46:54 2016 @@ -1 +1 @@ -su pi -c "tmux new -s rtorrent -d rtorrent" +/usr/bin/su pi -c "/usr/bin/tmux new -s rtorrent -d /usr/local/bin/rtorrent" ====== /etc/spwd.db SHA-256 checksums ====== OLD: 075455a721ef[...]b942f590fb8e3edfd88c5dd4 NEW: 37eba7537dd7[...]42468cc4cbe768fcf496b230
You can read warning about file owners and changes in a file. Lines starting with "+" were added, those starting with a "-" removed.
In this example, at last, a checksum on the password database has changed : did you add a new user?
Well, that's nice, but how do I get it?
As you will see, it's very easy :
- 1. Edit /etc/mail/aliases ;
- 2. Add at the bottom : "root : toto@ouaf.xyz"
Of course, replace with a mail address you read daily.
Then, run "# newaliases".
That's it! ๐
Just to make sure, send yourself a test message :
echo "Hello sweetie!" | mail -s "test" root
You should get this mail at the address written in /etc/mail/aliases.
Now, it's up to you to read these report every day.
If you need to, you can change the domain name of the sender if you edit /etc/mail/mailname. As example :
athome.tld
File system check ยง
Add this line to /etc/daily.local to check filesystems every day :
CHECKFILESYSTEMS=1
Read man 8 daily to learn more
The Firewall (pf) ยง
The firewall? Already ?
Absolutely. You want to keep your fresh system safe, don't you?
We'll start with simple examples and suggest a few more for thoses interested.
On OpenBSD, the firewall is pf as in "packet filter". It is configured in "/etc/pf.conf". You will see, it's syntax is easy to understand.
First, you have to ask yourself what do you need. A web server (http)? Mail server? Depending on your answers, you won't open the same port number. We actually will close every port except the ones you really need.
To find the ports on which services listen, look at the contents of the "/etc/services" file. Thus, writing "www" or "80" is the same for pf, but is more readable for humans.
The rules you will define will be applied in the order they are written. This means next rules can override previous ones. To keep a rule state, use "match" instead of "pass" or "block". To apply directly a rule without looking forward, use "quick" keyword. You'll see, it's quite easy actually ๐.
Read the official PF's user guide to go further.
A first example for pf ยง
Ready? Let's go for a detailed but simple example. We are going to configure the firewall for a website with access on ports 80 (www) and 443 (https).
We start by blocking everything, and recording in the log the prohibited connections with the word "log" (obvious ๐).
block log
To make our life easier, we are going to put the ports to be opened in a variable. This will be particularly practical when there are more ports to manage.
tcp_pass = "{ 80 443 }"
Note that this is identical to 'tcp_pass = "{ www https }"'
Then, we allow potential visitors to access your server. These will be so-called "incoming" connections, so we will use the "in" keyword. Instead of looking for the name of your network interface (which depends on your hardware), we will specify the name of the group that these interfaces belongs to so that it works without worry: "egress".
pass in on egress proto tcp to port $tcp_pass
You're cute eh, but that's all gibberish!
Okay, we'll explain what this syntax means. We can translate the previous configuration line as follows: "let pass inward (pass in) through the egress interface (on egress) for the tcp protocol (proto tcp) to the ports in $tcp_pass (to port $tcp_pass).
Fiew! ๐
Finally, we allow all the output traffic (keyword "out"):
pass out on egress
Finally :
tcp_pass = "{80,443}" block log pass in on egress proto tcp to port $tcp_pass pass out on egress all
Easy, right? ๐
I suggest that you replace the last line with something a little more restrictive:
pass out on egress proto {tcp udp icmp ipv6-icmp}
We simply specify the protocols authorized on exit.
You will understand, pf allows us a first order control on our server. At first, you'll just want to choose which ports should be opened.
This makes a lot of new concepts, take the time to reread what has been written so far if necessary.
When you are more comfortable, know that we can filter incoming and outgoing traffic, and even redirect part of it to internal services (eg anti-spam). I invite you to read the part "going-further-with-pf". You will see how to ignore certain lists of IPs known as harmful that will be recorded in tables, rules against bruteforce attacks or even a proposed tool that adds potential hackers to the blacklist in real time.
I let you build your /etc/pf.conf file according to your needs. Start from the example seen above then add the ports to open. Do not hesitate to consult the example provided at the end of this document.
To be sure, can we have an example to open the SSH port?
If the configured SSH port is 22, then you will have in the pf configuration:
pass in on egress proto tcp to port 22
Of course, you can also just change the tcp_pass variable:
tcp_pass = "{80 443 22}" pass in ...
Convenient commands for pf ยง
Find below suggestions of useful commands to manage pf:
- Disable the firewall: "# pfctl -d"
- Activate the firewall with the "/etc/pf.conf" file: "# pfctl -ef /etc/pf.conf"
- Restart the firewall: "# pfctl -d && pfctl -ef /etc/pf.conf"
- Reload the firewall configuration: "# pfctl -f /etc/pf.conf"
- Empty the firewall rules (โ Warning!), It can be useful when you self-ban yourself from the server (yes, it happens even to the best ...): "# pfctl -F all"
- See in real time what the firewall is blocking (provided you have put the "log" keyword in the configuration): "# tcpdump -n -e -ttt -i pflog0"
- View the saved logs at any time: "# tcpdump -n -e -ttt -r /var/log/pflog"
- Display the list of blocked IPs in a table: "# pfctl -t table_name -T show"
- Add an IP in a table: "# pfctl -t name_of_the_table -T add 123.456.678.909"
- Remove an IP from a table "# pfctl -t table_name -T delete 123.456.678.909"
- List all active rules: "# pfctl -sr"
pf-badhost: IP blacklist ยง
Some IPs are known to be harmful. Between those that scan for an open port or those that attempt bruteforce attacks, the list goes on. Fortunately, we can configure the firewall so that it completely ignores these IPs and thus protect and lighten the load on our server.
This is what allows the pf-badhost project.
Author's blacklists ยง
I keep up to date lists of IP who recentrly tried bad stuff on my server.
Bruteforce: what is it? ยง
One method hackers use to attack a server is called "bruteforce". Such attacks consist of trying all possible username / password combinations until they find the correct one.
If you choosed strong passphrases, you should be safe. However, these attacks abuse resources on your server and can potentially succeed one day (admit that it would be bad luck).
In order to thwart these attacks, you should take the time to analyze the logs of the services you host (in "/var/log" and "/var/www/logs") for identification failures and notice where these attacks are coming from to banish the source. But let's be honest, it's both painful and time consuming.
Ideally, a tool that monitors logs for you and takes care of blacklisting "attacker" IPs on your firewall. Good thing, you can use vilain or sshguard which are made for that.
In the meantime, pf already integrates a protection which limits the number of connections on a port during a given time. It is very effective and remains light. Read the next paragraph ๐.
Anti-Bruteforce integrated into pf ยง
pf has a very interesting feature: "tables". This will allow us to remember certain IP addresses of hackers who would try to compromise the server. For example, to protect the SSH service, we will proceed as follows:
- 1. Creation of a table to record abusive IPs with our SSH server.
- 2. Block all the IPs that would be in the previous table, and do not go further for them.
- 3. Open the ssh port, but record in the previous table all the IPs that attempt to connect more than 3 times per minute (bruteforce attack).
So that gives us this:
ssh_port = "22" table <ssh_abuse> persist block in log quick proto tcp from <ssh_abuse> to any port $ssh_port pass in on egress proto tcp to any port $ssh_port modulate state \ (source-track rule, \ max-src-conn-rate 3/60, \ overload <ssh_abuse> flush global)
Note that we have recorded the port number used for the SSH server. It is useless, because one can put in the place of "$ssh_port" simply "ssh", the name of the service. However, you may want to change the default port of the SSH server, as described in the chapter on this service.
With the keyword "quick", further rules won't be read so abuser is blocked and that's it.
Here is another example for a website (http and https ports):
http_ports = "{www https}" # ports http (s) # If more than 40 connections every 5 seconds on the http (s) ports # or if she tries to connect more than 100 times # we add the ip to block it. pass in on egress proto tcp to any port $http_ports modulate state \ (source-track rule, \ max-src-conn 100, max-src-conn-rate 40/5,\ overload <http_abuse> flush global)
Anti-bruteforce in real time with vilain ยง
I would like to offer you a tool that I wrote with the help of Vincent Delft in order to blacklist possible hackers when the attack takes place.
The script in question is called vilain.
Anti-bruteforce in real time with sshguard ยง
Its installation in OpenBSD is not very complicated to set up. You will need to create a table in pf that will contain all the blacklisted IPs. Thus, we add in the /etc/pf.conf file a new table containing the IPs of the attackers who are immediately blocked:
table <sshguard> persist block in from <sshguard>
Reload the firewall with pfctl -f /etc/pf.conf.
Now, we install sshguard as we usually do:
# pkg_add sshguard
Copy the configuration file given as an example:
# cp /usr/local/share/examples/sshguard/sshguard.conf.sample /etc/sshguard.conf
Edit this file if you want to make it more or less harsh. The default options are quite reasonable.
We can now enable and run sshguard:
# rcctl enable sshguard # rcctl start sshguard
And there you go, your server is now safer.
You may want to whitelist some IP addresses. Edit "/etc/sshguard.friends" and write, one per line, IP or ranges to ignore:
46.23.93.119 192.168.1.0/24 10.0.0.1
Trap with pf and iblock ยง
solene@ wrote a tool named iblock.
iblock ban every IP trying to access your server on ports that are actually unused.
This is definitely a must-have.
SSH: administer remotely ยง
Configuring SSH ยง
SSH is a great tool ๐. You can access to your server from another computer. You can then manage it remotely without having to connect a keyboard and screen. Truth be told, with some exceptions, all servers are administered this way, but you are at home after all, so you can do whatever you want.
SSH is a protocol useful for much more: encrypted tunnels, SFTP file storage space...
Anyway, if you didn't enable SSH when installing OpenBSD, you can enable it with the following command:
# rcctl enable sshd
Although this protocol is reliable, it does not cost anything to take some safety precautions. We will edit the SSH configuration in the /etc/ssh/sshd_config file.
- You can change the port used by default, for example: Port 222. In any case, remember to open this port in the firewall and redirect it to the router. It is not necessarily essential.
- Very important, remove root's ability to connect in SSH with this line, in order to avoid bruteforce attacks directly on the "root" username:
PermitRootLogin no
To connect to the machine, you will need to create a simple user with the "adduser" command.
Once the changes have been made, restart the SSH daemon:
# rcctl reload sshd
Later, to connect to the server, you will use the following command from your computer (in a terminal, or with a client like putty under windows):
$ ssh toto@chezmoi.tld
And if you changed the default port:
$ ssh -p 222 toto@chezmoi.tld
It will ask for the password of the user toto, then you can administer the server remotely.
Login with keys (without password) ยง
It is quite possible to connect in SSH without using a password. Instead, we use a pair of keys. This is a good idea at least for the following reasons:
- It is much more secure.
- It avoids writing a password every time ๐.
- This is useful to run ssh commands in scripts.
However, this requires you to have the private key on the device used to connect, which is not always the case.
Here's the procedure to follow :
On the server, edit the /etc/ssh/sshd_config file so it contains this line:
PubkeyAuthentication yes
Now, on the computer that is used to access the server, we will generate a key pair with the following command:
ssh-keygen -t ed25519 -f ~/.ssh/sshkey -a 100
You will find two new files:
- ~/.ssh/sshkey: this is your private key, NEVER share it with anyone.
- ~/.ssh/sshkey.pub: the public key. Contrary to its name, it is like a lock that only the private key can enter.
The ed25519 algorithm is suggested because it is very reliable at the moment.
Do not enter a password and then wait for the keys to be generated.
On an OpenBSD or Linux system, you will take care to edit your user's ~/.ssh/config file to fill it out as follows:
Host your_server HostName verylong.reallylong.too.long User myusername Port 222 PasswordAuthentication no IdentityFile ~/.ssh/sshkey
Of course, you will change the domain name of your server as well as the name of the user who must connect. Then run the following command to copy the public key to the server.
ssh-copy-id -p xxx -i ~/.ssh/sshkey.pub "myusername@verylong.reallylong.too.long"
Here, replace "xxx" with the port used by SSH if it's not 22.
If the ssh-copy-id tool is not available, you must copy the public key manually. To display it, type
$ cat ~/.ssh/sshkey.pub
Connect to the server in SSH, then add in the file ~/.ssh/authorized_keys the contents of the file displayed previously.
Once this is done, you can log in without having to enter a password with just the command:
$ ssh your_server
Convenient, isn't it?
If that works as expected, you can disable password identification for good so that only the keyset is left as possibilities. Edit the /etc/ssh/sshd_config file to contain this line:
PasswordAuthentication no
BE CAREFUL not to lose your set of keys!
Upload a file ยง
You will definitely need to upload files to your server from time to time. Once SSH is configured, you will be able to use the scp command from any computer to copy a file to your server. For example :
$ scp -P <ssh port> user@athome.tld:/location/from/destination file-to-copy
It's very useful.
However, if you want to transfer many files, backup or store documents, consider SFTP instead, which has more features such as resuming a partial transfer.
If the files are too large, you can split them for multiple transfers.
SSHFP DNS records ยง
The first time you connect to your server using SSH, the client get the fingerprint of the server. Next time, it compares the previous checkprint and display a warning if it has changed because there might be a problem if you didn't touch your server since. It's called Trust On First Use (TOFU -- not the food, sorry).
To avoid TOFU, you can publish data necessary to check the fingerprint the first time. To do so, we use a DNS SSHFP record.
Enter "ssh-keygen -r athome.tld" and copy the result in your DNS zone. Don't forget to adjust the entries with a final dot "." after domain name or replacing it with a "@" if necessary.
As example :
@ IN SSHFP 1 1 97f5a9479fd16fbee1381c425297f7b2773a67a4 @ IN SSHFP 1 2 bd50a3c271e83dc769ea7c249a5c07aea1a817c62910129de56eba2763f93607 @ IN SSHFP 2 1 05fb8516842fc270ea5abda38d1c32b41e75da8a @ IN SSHFP 2 2 491489437e137d7d27959a4de3ba7660185ef98cbd3abc2287c82f82e8f032ab @ IN SSHFP 3 1 b7305417b0e981c4513642020a1f57ee03915af5 @ IN SSHFP 3 2 f15d33852df5f4043c77b6bf1c6134a5e11de0a513c1ec127fcbb8fae733b178 @ IN SSHFP 4 1 a1120fe90e69c0f534fece3dc837db6a262c3371 @ IN SSHFP 4 2 a36e418b517d95a74f1e0be1228d46e7aeaa8ed310359e91162d8c40b373eb55
We'll talk about DNS a bit further ๐.
In order the client check these records with the server fingerprint at first connection, add option "VerifyHostKeyDNS" to ssh. In "~/.ssh/config" :
Host * VerifyHostKeyDNS yes
Keep the system up to date ยง
To ensure your system security, it is essential to keep it up to date. In order to apply the security patches, you must update:
- The base system
- Ports of third-party programs that you may have installed if a newer version is available.
Short ๐:
# syspatch # pkg_add -u
Update ports (packages) ยง
Updating packages is as easy as :
# pkg_add -u
Yes that's all. ๐
Thanks to solene who made this possible.
Since this paragraph is a bit short, I take this opportunity to give you two tips ๐. I advise you to add to the /etc/daily.local file the command
pkg_add -nu
This way, in the daily mail from Charlie Root, you will see what might happen if upgrades for packages are availables. Nothing is done for real, because if an updated program needs to be restarted, it may not work until it is. Thus, you are just warned of any updates to apply, and with the -n option, the packages are kept in the cache in order to save time without risking to put a service down if it has to be restarted after the update. You will enter "pkg_add -u" when you get the chance.
If you still want to apply the updates automatically, I advise you to first install the "checkrestart" port which will tell you if a service must be restarted after the update. This will therefore look like this in /etc/daily.local:
pkg_add -u echo "Service to restart with rcctl restart:" checkrestart
You will still have to reload the services manually with rcctl.
Update the system ยง
First of all, a little reminder: OpenBSD is available in 3 "flavors":
- release: this is the released version that you probably downloaded to install OpenBSD.
- stable: this is the release version with several security fixes. This is what we would like to follow.
- current: this is the next version in preparation of OpenBSD, in the CVS repository (which is used for source code development).
It can indeed happen that bugs are discovered. Each time, fixes are quickly proposed. It is then recommended to apply the security patches.
Since version 6.1, this operation is very simple with the following command:
# syspatch
The binary patches are then downloaded and installed. That's it! ๐ It looked like this in OpenBSD 6.1 (a long time ago):
Get/Verify syspatch61-002_vmmfpu.tgz 100% | ******************************* | 9377 KB 00:49 Installing patch 002_vmmfpu Get/Verify syspatch61-003_libress ... 100% | ******************************* | 11391 KB 00:22 Installing patch 003_libressl ...
This tool is only available for i386, amd64, and more recently arm64 architectures.
Be notified of updates ยง
To find out if updates need to be applied, you can consult the errata page which contains the list of available security patches. It is located at
"https://www.openbsd.org/errataXX.html" where "XX" is the version number of your release, i.e "70" for OpenBSD 7.0.
You can also be notified by email (and that's great ๐). To receive important update messages available on the system, subscribe to the announce and security-announce lists. For that, send a first email to majordomo@OpenBSD.org simply containing:
subscribe announce
Then send a second message with:
subscribe security-announce
I also advise you to subscribe to the list indicating that there is a new version of ports by subscribing to the ports-security list by always sending to the same address a message containing:
subscribe ports-security
Upgrade from a version to another ยง
When a new major version of OpenBSD is available, the update procedure is always detailed on the official website. You must read the release notes when upgrading at "https://www.openbsd.org/faq/upgradeXX.html" where "XX" must be replaced by the version number you want to upgrade to.
Since version 6.5, it only takes a simple command to update to the latest publication release or to -current:
# sysupgrade
ALWAYS check upgrade notes anyway ๐.
Clean up after multiple updates ยง
If your installation is a bit outdated, you can check which files you may have forgotten to delete with the help of the "sysclean" port (command of the same name).
The files which are not supposed to be present in a base system will then be listed. Read the output carefully, most of them are configuration files you created and want to keep ๐.
Backup ยง
One can use its server to store important files.
In any case, you also must think to backup the server itself. You never know when the hard drive will crash, when a storm will generate a power overload or when your cat will rush trought the wires ๐ผ.
Automatic backup of / ยง
By default, you can backup / daily with OpenBSD
See The official FAQ about /altroot
It is relevant if you backup on a separate disk.
If one day the main hard drive has a failure, you can boot on the /altroot. When you see the prompt "boot>" :
Find the /altroot partition
boot> machine diskinfo
Then boot on the appropriate slice :
boot> boot -s hd1a:/bsd
โ WARNING : only / is saved, with /etc. If you need to backup /var or /usr/local or else, you must plan this. Read the next part to do so ๐.
Read also the official FAQ to duplicate filesystems.
Backup with rsync ยง
You may dedicate a partition to your backup, wether it's an external drive or an extra slice kept at OpenBSD install. Below, we use as example the mount point /mnt/bckp created for our needs.
We will use the "rsync" tool. You can install it throught ports, but take note there is "openrsync" already available in base install. It is a rewrite of rsync less powerful but at least avaiable in base. If the target device doesn't have rsync installed, add the option "--rsync-path=openrsync". The rsync protocol will detect modified or new files to copy and don't bother with the others.
As example, you can add those lines in /etc/weekly.local file to backup /var and /usr/local well, weekly :
/usr/local/bin/rsync -a --delete /var/ /mnt/bckp/var.bak/ /usr/local/bin/rsync -a --delete /usr/local/ /mnt/bckp/usrlocal.bak/
This is a local backup, but you can use rsync thought a SSH tunnel to send data from one computer to your server:
$ rsync -e "ssh" -avz --delete /directory/to/backup \ brucewayne@athome.tld:/mnt/bckp
Backup with tar ยง
Tar let you create archives of a whole directory structure, and is available in base of course.
Below is a suggestion to create a daily archive named with the current date.
- BACKDIR is the backup mountpoint. If it is encrypted, call "bioctl -p" accordingly in the script.
- Every directory in "BACKLIST" is archived.
BACKDIR="/mnt/bckp" BACKLIST="/var /home /etc" for i in $BACKLIST; do backupfile="${BACKDIR}/$(basename ${i})-$(date +%F).tar.gz" tar -czf "${backupfile}" "${i}" done chmod 700 "${BACKDIR}" # remove olds #find "${BACKDIR}" -type f -mtime +90 -delete
Uncomment the last line to delete achives older than 90 days.
SFTP : Secure file transfer ยง
One can see SFTP like FTP over SSH. Of course, it's a bit more than that ๐. It can continue partial transfer as example.
If you have an SSH access to your server, you can sftp ๐.
If you want to provide an SFTP server for multiple users, using a chroot might be helpful to keep everyone's data safe from others. We'll talk about it later.
sftp usage ยง
To copy files with the user account "batman", start a new sftp session :
$ sftp batman@athome.tld
If you configured key authentication (good idea!), use -i flag :
$ sftp -i ~/.ssh/sshkey batman@athome.tld
Now you're in front of a prompt. You can enter commands such as :
- put file : send a file to the server
- get file : download a file from the server
- ls : list files on the server
- lls : list local files
And also cd, mkdir, quit and help to get a list of availables commands.
One can prefer to use a graphical tool to deal with sftp. Look at Filezilla on WinSCP for SFTP clients.
Also, most file manager can handle sftp protocol. Try to open a path like :
sftp://batman@athome.tld:<port_number>
At least Gnome, KDE and XFCE default file browser can do this.
However, if your file manager don't support SFTP, you still can use "sshfs" to mount a remote SFTP location as any other mountpoints.
$ sshfs -d batman@athome.tld:/remote/dir /home/batman/sftp \ -o IdentityFile=/home/batman/.ssh/sshkey
chrooted SFTP ยง
To share a storage space between users, you might want to lock them is a specific directory. Doing so, they can't reach other files above their own dedicated repertory. It is interesting to keep everyone's data out of sight of the others, but also avoir sftp users to reach system files. This is called "chroot".
In other words, if an user is chrooted in "/var/sftp/batman", he can't read "/var/sftp" nor "/var" and even less "/". for this user, "/var/sftp/batman" is the new root "/".
For the example, we'll chroot every sftp users in "/var/sftp/user_name". You'll need to make the appropriates directories. They will all belong to a group named "sftpusers" in order to automatically chroot them when they sftp.
Create this groupe :
# groupadd sftpusers
Then, edit "/etc/ssh/sshd_config" to chroot every user belonging to group sftpusers in "/var/sftp"
Match Group sftpusers ChrootDirectory /var/sftp/ ForceCommand internal-sftp AllowTcpForwarding no
Note you can do the same for a specific user : you just use a section "Match User" instead.
If you wish users to authenticate using keys, then add to the above section :
PasswordAuthentication no
Reload ssh with "# rcctl reload sshd".
Now make appropriate directories with required permissions for the chroot (this is critical):
# mkdir -p /var/sftp # chown root:wheel /var/sftp # chmod 700 /var/sftp
Read the next part to see how to create sftp users and their own directories.
Add chrooted sftp users ยง
Any regular account belonging to sftpusers group will match the above rule. You might consider to restrict even more the access to sftp users :
- Set the shell "nologin" to force sftp only sessions and disable other shells and : "# useradd -G sftpusers -s /sbin/nologin -m new_user"
- Change the password : "# passwd new_user"
- Create the directory for the new user. He must be the only one with permissions to read it. Instead of using "mkdir" then "chmod", you can do it with "install" :
# install -d -o new_user -g new_user -m 700 "/var/sftp/home/new_user"
Now, new_user is chrooted in "/var/sftp/home/new_user" when he log in using sftp.
If you enable key authentication, then you'll have to fill the file "/home/user/.ssh/authorized_keys" with user's ssh public key. โ I'm reffering to the file "/home/user/.ssh/authorized_keys", NOT "/var/sftp/home/user/.ssh/authorized_keys".
Keep your server on time (ntpd) ยง
It is crucial to set your server's date and time correctly. Even if the hardware is supposed to keep track of time, it sometimes isn't reliable. However, one must avoid time shift or fear database corruption and unexpected behaviour.
OpenNTPD in OpenBSD's base installation will ensure your server is on time.
Nothing to do ๐, it's already there.
Just enable ntpd daemon and you're good to go :
# rcctl enable ntpd
Edit "/etc/ntpd.conf" file. You may copy the example at "/etc/examples/ntpd.conf"
# use a random selection of NTP Pool Time Servers # see http://support.ntp.org/bin/view/Servers/NTPPoolServers servers pool.ntp.org # time server with excellent global adjacency server time.cloudflare.com # use all detected timedelta sensors sensor * # get the time constraint from a well-known HTTPS site constraint from "9.9.9.9" # quad9 v4 without DNS constraint from "2620:fe::fe" # quad9 v6 without DNS constraints from "www.openbsd.org" # intentionally not 199.185.178.80
Actually, default configuration is fine ๐.
You just don't want to be a NTP server telling others what time it is, so let "listen on *" commented.
You choose a few public ntp server you "trust", then use every sensors availables. Finally, add some contraints from well known IP and end with a https website using it's domain name (a domain name resolution is required).
Finally, restart ntpd :
# rcctl restart ntpd
It's all good ๐.
Host a website (httpd) ยง
Host you own website is a great project ! ๐ It's the best way to have your personal space to do whatever you want without restrictions.
OpenBSD is shipped with "httpd", a http server. It is light and easy to configure. Most of the time, it is more than enough, but if you need even more features, look for "nginx" or "apache" in ports.
In this part, we'll see how to host a simple static website, then how to add PHP support to finally show some web applications examples.
Before going any further, take note that http is chrooted in "/var/www" by default.
What is that supposed to mean ?
To httpd, every file "above" "/var/www" can't be read. In other words, httpd see "/var/www" as the new root "/". For obvious security reasons, if httpd can't even reach system files, it reduce the risk that someone find sensitive data through a weak website.
Simple static website ยง
A static website is just a few html files delivered by httpd. It's safe and fast. That's a good starting point. Let's see how to host you own website.
First, create a directory to store the website :
# mkdir /var/www/htdocs/my_website
Now, copy your website html pages and files in that directory (index.html...). Just make sure httpd is able to read those files by adjusting permissions with "chmod a+r /var/www/htdocs/my_website" or setting owner :
# chown -R www:daemon /var/www/htdocs/my_website
You might change permissions later.
Now you must configure httpd to add a new section for your website. If it is served on "http://athome.tld", you'll edit "/etc/httpd.conf" file so it looks like this :
types { include "/usr/share/misc/mime.types" } server "athome.tld" { listen on * port 80 root "/htdocs/my_website" }
A few notes about the previous lines :
- "types ..." : This first line add more mimetypes to the default known by httpd. It helps clients to open the files you serve on your website with the appropriate software.
- "server "athome.tld" {" : Open a new section concerning the domain "athome.tld". As you might guess, you can serve multiple domains if you add more sections.
- "listen ..." : the server listen on every ip and interfaces ("*") on the default http port.
- "root ..." : location where are the files stored, relative to the chroot. Remember, to httpd, "/var/www" is "/".
You can now enable and start httpd :
# rcctl enable httpd # rcctl start httpd
Check http port (80) is open in your router and firewall, then check your website is reachable.
Every file you add in "/var/www/htdocs/my_website" will be served.
You can use SFTP previously described to upload your website ๐.
Get a SSL certificate ยง
SSL certificates allow to make TLS encryption and thus, among other things, serve your website through the more secure protocol HTTPS.
It is necessary when sensitive data is sent to/from your server.
Letsencrypt ยง
There is a client included in OpenBSD names "acme-client" to deal with such tasks. It currently requires a http server to generate certificates. That's why we'll discuss acme-client here. However, you certainly can use those certificates for other needs that https such as IMAPS.
โ First of all, check you opened port 80 in the firewall and eventually in your router. Acme-client needs to check for some files you'll make available for letsencrypt with httpd.
Actually, acme-client will ensure you manage the domain you request a certificate for. To do so, it ask an unique file to Let's Encrypt, kinda fingerprint, stored in "/var/www/acme". Then, Let's Encrypt try to get this "secret" file on your server at ".well-known/acme-challenge", asking for "http://athome.tld/.well-known/acme-challenge/secret_file". If it succeeds, the request is accepted and you get a certificate. Finally the file is deleted.
Remember your website is probably located in "/var/www/htdocs/website" or something. You need to make "/var/www/acme" for letsencrypt to get the secret file. So, add a "location" instruction in "/etc/httpd.conf" :
server "athome.tld" { listen on * port 80 location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } root "/htdocs/website" }
A few explanations :
- location "/.well-known/acme-challenge/* {" : we add new instructions when a client ask for any file ("*") in "/.well-known/acme-challenge".
- root "/acme" : in this scenario, a new root is defined : "/var/www/acme". This the place acme-client store files needed by letsencrypt.
- request strip 2 : we remove the ".well-known/acme-challenge" in the request made by the client so it leads to "/var/www/acme/requested-file".
โ This section will have to be added for every domain served by httpd, even alternative names specified in the acme-client configuration file below. You might find easier to use "include" instruction in "httpd.conf" later as it is described in the "Tips" part a bit further ๐.
Before calling acme-client, you must configure it by editing "/etc/acme-client.conf". You can use "/etc/examples/acme-client.conf" as template.
For the example, the file might look like this :
authority letsencrypt { api url "https://acme-v02.api.letsencrypt.org/directory" account key "/etc/acme/letsencrypt-privkey.pem" } authority letsencrypt-staging { api url "https://acme-staging-v02.api.letsencrypt.org/directory" account key "/etc/acme/letsencrypt-staging-privkey.pem" } domain athome.tld { alternative names { webmail.athome.tld www.athome.tld } domain key "/etc/ssl/private/athome.tld.key" domain full chain certificate "/etc/ssl/athome.tld.crt" sign with letsencrypt }
Of course, replace "athome.tld", "alternative names { ..." with the other domains and subdomains you need and eventually the location of key and full chain certificate.
Make sure necessary directories are here (they might already exist) :
# mkdir -p -m 700 /etc/ssl/private # mkdir -p -m 755 /var/www/acme
Check acme-client configuration with "# acme-client -n", it must return nothing.
Now you can get your certificates :
# acme-client -v athome.tld
At first, you might want to make sure everything works as expected using "sign with letsencrypt-staging" in "/etc/acme-client.conf" and switch back to "sign with letsencrypt" later. You can force getting certificates using "acme-client -F athome.tld".
Consider renewing certificates automatically using "/etc/weekly.local" file. It is a script executed every week.
/usr/sbin/acme-client -v athome.tld && /usr/sbin/rcctl reload httpd
Using "&&" make httpd to reload certificates only if there are renewed certificates. If at some point other daemons than httpd uses your certificates, you'll have to reload them too (relayd, dovecot...)
Now you've got a certificate, let's enable https in "/etc/httpd.conf" ๐. A new "tls" section appears. :
server "athome.tld" { listen on * port 80 # http version # [...snip...] } # snippet of /etc/httpd.conf server "athome.tld" { listen on * tls port 443 root "/htdocs/website" tls { certificate "/etc/ssl/athome.tld.crt" key "/etc/ssl/private/athome.tld.key" } hsts # add your website specific configuration here }
As you can see, now httpd listens on port 443 (https) : remember to configure your firewall for this.
You might want to redirect clients accessing from http to https. To do so, add an instruction matching all request ("location *"). The clients will be redirected to the https version except if the request matched the previous one for acme-client.
# snippet of /etc/httpd.conf server "athome.tld" { listen on * port 80 location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } location * { block return 301 "https://$SERVER_NAME$REQUEST_URI" } } server "athome.tld" { listen on * tls port 443 #[...]
Remember to add such section for every website you want to serve. You may want to use "include" instructions to ease your life. See next part ๐.
About "include" usage in httpd.conf ยง
You will certainly enable tls for multiple website. Instead of rewriting the same instructions again and again, you can include a file.
First, create a directory to keep multiple httpd configuration files :
# mkdir /etc/httpd.d
Then, we create "/etc/httpd.d/acme.conf" file to keep instructions related to acme seen earlier :
location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 }
You even can go a bit further by creating a file "/etc/httpd.d/tls.conf" for multiples lines about certificates :
tls { certificate "/etc/ssl/athome.tld.crt" key "/etc/ssl/private/athome.tld.key" } hsts
Now, the file "/etc/httpd.conf" can be simplified to those lines below. The whole file let you serve 3 websites : athome.tld, www.athome.tld and webmail.athome.tld :
server "www.athome.tld" { listen on * tls port 443 include "/etc/httpd.d/tls.conf" root "/htdocs/www.athome.tld" } server "www.athome.tld" { listen on * port 80 include "/etc/httpd.d/acme.conf" location * { block return 301 "https://$SERVER_NAME$REQUEST_URI" } } server "athome.tld" { listen on * tls port 443 include "/etc/httpd.d/tls.conf" block return 301 "https://www.$SERVER_NAME$REQUEST_URI" } server "athome.tld" { listen on * port 80 include "/etc/httpd.d/acme.conf" location * { block return 301 "https://$SERVER_NAME$REQUEST_URI" } } server "webmail.athome.tld" { listen on * tls port 443 include "/etc/httpd.d/tls.conf" root "/htdocs/webmail.athome.tld" } server "webmail.athome.tld" { listen on * port 80 include "/etc/httpd.d/acme.conf" location * { block return 301 "https://$SERVER_NAME$REQUEST_URI" } }
The above configuration redirect from athome.tld to www.athome.tld. Another website webmail.chez.tld is handled. For each domain, using acme is possible.
You may have noticed each section is quite a routine. You can imagine other usages to "include" instruction if you need it.
Thank you Grรฉgory Marchal for your suggestions on this part. ๐
Facultative : CAA records ยง
You can add a CAA record in your DNS zone to show you own the domain and YOU asked a certificate to Let's Encrypt. This is just a proof of honesty helping someone to trust the certificate used.
@ 300 IN CAA 0 issue "letsencrypt.org"
Generate a self-signed certificate ยง
You can generate your own self signed-certificate. At first connection, clients will see a warning asking if they trust or not the certificate.
To do so, see man 8 ssl in section "Generating RSA server certificates".
Check your server SSL configuration ยง
Find below a few tools to check how well you've configured your server (not only https) :
https://observatory.mozilla.org/
PHP ยง
The very minimal PHP support ยง
You may want to add PHP support to your website if you use a CMS as example.
Use OpenBSD's ports to install php (adjust the version number)
# pkg_add php-7.4.7
To see every PHP version availables :
# pkg_info -Q php
Then, enable and start php :
# rcctl enable php74_fpm # rcctl start php74_fpm
Here, "74" suggests you installed PHP version 7.4.
Now edit httpd configuration so it sends php files to the interpreter. In "/etc/httpd.conf":
server "athome.tld" { listen on * port 80 root "/htdocs/website" directory index index.php location "*.php*" { fastcgi socket "/run/php-fpm.sock" } }
Notice the "directory index index.php" instruction. By default, when someone reach "http://athome.tld/", it's equivalent to "http://athome.tld/index.php".
You're good to use PHP for most use cases.
Add PHP modules ยง
If you read "/usr/local/share/doc/pkg-readmes" php file, you already know what to do ๐. You have to add symlinks from "/etc/php-7.4.sample" to "/etc/php-7.4" (edit php version).
# cd /etc/php-7.4.sample # for i in *; do ln -sf ../php-7.4.sample/$i ../php-7.4/; done # rcctl restart php74_fpm
With main php package, most extension are already installed. You may add the following as they are quite common and useful :
- php-curl : online requests
- php-gd : handle images
- php-intl : internationalization
- php-zip : compression
- libmcrypt : encryption
- pear and "pecl-..."
Edit PHP configuration ยง
Edit "/etc/php-7.4.ini". Below is an example of some useful changes :
; Increase the size of uploadable files post_max_size = 10M upload_max_filesize = 10M ; let php download remote content allow_url_fopen = On ; Timezone date.timezone = Europe/Paris ; Enable cache to avoid every page regeneration opcache.enable=1 opcache.memory_consumption=128 opcache.interned_strings_buffer=8 opcache.max_accelerated_files=4000 opcache.enable_file_override=1
Configuration to deal with httpd's chroot ยง
Your php pages may need to download remote content. Therefore, it must be able to do domain name resolution, check tls certificates and more. The necessary is stored in "/etc". However, if you remember, httpd is chrooted. Do you remember where ?
In /var/www !!!
Indeed Jean-Michel! Good to have you here. ๐
We'll have to copy a few files usually stored in "/etc" to "/var/www/etc":
# cd /var/www # go in the chroot # mkdir etc/ # create etc directory # cp /etc/resolv.conf etc/resolv.conf # for Domain resolution # cp /etc/hosts etc/hosts # DN too # cp /etc/localtime etc/localtime # mkdir etc/ssl # Create another directory for tls certs # install -m 444 -o root -g bin /etc/ssl/cert.pem /etc/ssl/openssl.cnf /var/www/etc/ssl
Those files must be read only :
chmod -R 444 /var/www/etc/* chmod -R a+X /var/www/etc/
Les fichiers copiรฉs servent notamment ร :
Files in "/etc/ssl/*" must be updated periodically. Add in "/etc/monthly.local" :
install -m 444 -o root -g bin /etc/ssl/cert.pem /etc/ssl/openssl.cnf /var/www/etc/ssl
If you need PHP to send mails, you must copy "sh" in chroot (see "/usr/local/share/doc/pkg-readmes/femail-chroot*").
# cp /bin/sh /var/www/bin/
At last, reload php ๐.
A few tips for httpd ยง
man httpd ยง
You should read man "httpd.conf".
Really.
You'll find what's written here and more.
httpd logs ยง
Choose where to store logs.
By default, they are in "/var/www/logs"
log access "website-name.log"
Disable logs with "no log".
gzip compression ยง
Add "gzip-static" instruction in a domain configuration or a "location" section. Thus, httpd try to deliver the requested file with ".gz" suffix if it is present.
This is good for bandwidth, it can reduce 2x to 10x the weight to transfer. Compressoin ratio is good on text files (html, css, js, svg, ...)
To gzip a file before uploading on your server :
$ gzip -vk9 index.html index.html: 49.5% -- replaced with index.html.gz 1395 bytes in, 733 bytes out
Custom error pages ยง
Add "errdocs" intruction to tell which directory contains custom error pages. A generic "err.html" or multiple pages with error code as name can be used.
As example :
errdocs "/htdocs/athome.tld/err"
In /var/www/htdocs/athome.tld/err", there is the following "err.html" :
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" href="/favicon.png" type="image/png" /> <style type="text/css">body, html {height:100%; margin:0} #bg { position: relative; background-position: center; background-repeat: no-repeat; background-size: cover; background-image: url("/img/errimg.jpg"); height: 100%; padding:0; margin:0; } #content { padding:1.5em; } </style> <title> $RESPONSE_CODE : $HTTP_ERROR </title> </head> <body> <div id="bg"> <div id="content"> <h1>Error page ๐</h1> <p>Sorry!</p> </div> </div> </body> </html>
Improve disponibility ยง
To increase the number of server processes and thus serve simultaneously content to multiple clients, increase the default value of 3 :
prefork 10
Publish with utf-8 ยง
If you try to serve plain text, the web browser may have trouble to display some glyphs except if you specifically set it to utf-8.
To avoid clients to look for this setup, you can explicitly send the appropriate header. However, the semicolon has to be escaped making the configuration a bit weird. Here is an example for ".txt" and ".gmi" file extensions :
types { include "/usr/share/misc/mime.types" text/"plain;charset=UTF-8" gmi text/"plain;charset=UTF-8" txt text/"plain;charset=UTF-8" awk text/"plain;charset=UTF-8" sh text/"plain;charset=UTF-8" c }
Notice how we add lines about UTF-8 after including /usr/share/misc/mime.types to overwrite previous declarations.
Password restricted area ยง
Create login credentials with "htpasswd":
# htpasswd /var/www/secret.htpw login
Replace "login" with any username and set a strong password.
Do it again to add more users.
Set appropriate permissions on this file :
# chown www /var/www/secret.htpw # chmod 400 /var/www/secret.htpw
Finally, tell httpd to read this file for credentials and ask client to enter login + password when "/hidden_directory" is requested :
location "/hidden_directory/*" { authenticate "Restricted access" with "/secret.htpw" }
"secret.htpw" location is relative to httpd's chroot.
For a whole website :
location "/*"
Or add "authenticate" instruction at the very beginning without any "location".
httpd automatic index ยง
To display a list of files availables in a directory if no "index.html" is found :
location "/dir/*" { directory auto index }
Include configuration files ยง
If you have many websites, you can include files in "/etc/httpd.conf" instead of writing again and again the same thing :
include "/etc/httpd/site1.conf" include "/etc/httpd/site2.conf"
httpd TLS ยง
Add a "ticket session lifetime" to speed up TLS.
hsts preload tls { certificate "/etc/ssl/athome.tld.crt" key "/etc/ssl/private/athome.tld.key" ticket lifetime default }
How to set permission for website files ? ยง
Setting appropriate permissions on files is mandatory to keep your data safe. You want to keep an eye on who can read some files or not, and even more carefully on files to execute or not.
For a website, there are no perfect solution, so make sure you understand the following advices and make your own choices.
You should set owner and group to "www" and "daemon" so httpd can read the files :
# chown -R www:daemon /var/www/htdocs/website
For a static website (no PHP), remove execution (-x) and writing (-w) permissions. You should let one change current path in these directories (+X).
# chmod -R a-xw /var/www/htdocs/website # chmod -R ug+X /var/www/htdocs/website
First, you remove x and w permissions to everyone, and then add to owner and group permission to change into this directory.
For a dynamic website, you need to write in the directory and execute files in them :
# chmod -R a-xw /var/www/htdocs/website # remove all x and w permissions # chmod -R u+xwX /var/www/htdocs/website # owner can xwX # chmod -R g+rX /var/www/htdocs/website # group can read
WARNING : you really should edit permissions more carefully to suit your website case.
For information, most commercial web hosters use the following permissions (not saying that's the best) :
- Directories : read and write for owner, read for others (755).
- Files : owner can write in files, others can olny read (644).
To apply the previous permissions :
# chmod -R a-rwx /var/www/htdocs/site # remove all perms # chmod -R a+rX /var/www/htdocs # everyone can read and cd in directories # chmod -R u+w /var/www/htdocs # only owner can write
Notice the "X" (not "x").
Relayd and headers ยง
Because httpd can't manage headers itself, you can put relayd "before" httpd. It helps add headers or modify others.
As you can guess, relayd is included in base installation.
Visit this link to test your website headers.
Relayd configuration ยง
Edit /etc/relayd.conf to configure relayd. Inside, add the following lines as example :
http protocol "http" { match request header remove "Proxy" match response header set "X-Xss-Protection" value "1; mode=block" return error pass } relay "www" { listen on 192.0.2.2 port 80 protocol "http" forward to 127.0.0.1 port 80 }
Those lines means :
- http protocol "http" { : you open a new configuration section for protocol http called later.
- match request header remove "Proxy" : you remove the header "Proxy" to avoid httpoxy exploit.
- match response header set "X-Xss-Protection" value "1;..." : we add a headera to mitigate XSS attacks
- return error : if there is a problem, return an error
- pass : otherwise, go on
- relay "www" { : you add a section to redirect traffic to httpd daemomn.
- listen on 192.0.2.2 port 80 : we listen on port 80 on your public IP.
- protocol "http" : we use the previously defined protocol
- forward to localhost port 80 : we redirect to httpd now supposed to listen on localhost.
That's why you must edit httpd configuration accordingly :
# httpd's configuration listen on localhost port 80
To sum up, things goes like this now :
1. A client ask to see your website and knocks at port 80.
2. relayd modify a few headers and redirect to httpd which still serve the website.
Dont forget to reload relayd and httpd:
# rcctl enable relayd # rcctl restart httpd # rcctl start relayd
Notice logs will show incoming connections from 127.0.0.1 (relayd local address). You may want to use "forwarded" log format for httpd :
log style forwarded
relayd and TLS / https ยง
Find below an example to add TLS support to relayd. There are a few things to take care of about certificates and keys.
http protocol "https" { match request header remove "Proxy" match response header set "X-Xss-Protection" value "1; mode=block" return error pass tls keypair athome.tld tls keypair here.tld } relay "tlsforward" { listen on 192.0.2.2 port 443 tls protocol "https" forward with tls to 127.0.0.1 port 443 }
Look at the lines starting with "tls keypair". They define the certificate and keys to use for TLS. In the above example, two certificates for two different domains are used, you can add as much as you have certificates to use.
However, thos certificates MUST be stored with the appropriate filename in the correct location :
/etc/ssl/private/athome.tld.key /etc/ssl/athome.tld.crt
Make sure /etc/ssl/athome.tld.crt is the "full chain".
This means you should have such configuration in acme-client configuration :
domain athome.tld { domain key "/etc/ssl/private/athome.tld.key" domain certificate "/etc/ssl/athome.tld-cert.crt" domain chain certificate "/etc/ssl/athome.tld-chain.crt" domain full chain certificate "/etc/ssl/athome.tld.crt" sign with letsencrypt }
This way, relayd will automatically pick up the right certificate.
relayd and certificates renewal ยง
Remember to reload relayd after certificate renewal :
/usr/sbin/acme-client -v athome.tld && \ /usr/sbin/rcctl reload relayd
Relayd and IPv6 ยง
If you want to add ipv6 support with relayd, it is obviously possible.
First, make sure you set the local address in "/etc/hosts" :
127.0.0.1 localhost ::1 localhost
You can now use "localhost" to refer to both ipv4 or ipv6 local depending on context.
Now add two entries in relayd configuration:
relay "http" { listen on $ext_ip4 port 80 protocol "http" forward to 127.0.0.1 port 80 } relay "http6" { listen on $ext_ip6 port 80 protocol "http" forward to ::1 port 80 }
httpd's configuration will make use of "localhost" so it's simpler :
listen on localhost port 80
Relayd and security headers ยง
You can set a few headers to improve your website security. It's mostly useful if you host huge webapps, not really for static websites.
match request header remove "Proxy" match response header set "X-Xss-Protection" value "1; mode=block" match response header set "Frame-Options" value "SAMEORIGIN" match response header set "X-Frame-Options" value "SAMEORIGIN" match response header set "X-Robots-Tag" value "index,nofollow" match response header set "X-Permitted-Cross-Domain-Policies" value "none" match response header set "X-Download-Options" value "noopen" match response header set "X-Content-Type-Options" value "nosniff" match response header set "Permissions-Policy" value "interest-cohort=()"
If you only host one domain, add :
match response header set "Access-Control-Allow-Origin" value "athome.tld"
Optimize client cache and bandwidth usage ยง
You should consider to tune up th amount of request a client should make each time he checks on your website. As example, you can specify to keep in cache files such as pictures, stylesheets or fonts for a few days before asking again.
In "protocol" section, just before "pass" keyword, add :
match request path "/*.css" tag "CACHE" match request path "/*.js" tag "CACHE" match request path "/*.atom" tag "CACHE" match request path "/*.rss" tag "CACHE" match request path "/*.xml" tag "CACHE" match request path "/*.jpg" tag "CACHE" match request path "/*.png" tag "CACHE" match request path "/*.svg" tag "CACHE" match request path "/*.gif" tag "CACHE" match request path "/*.ico" tag "CACHE" match response tagged "CACHE" header set "Cache-Control" value "max-age=1814400"
Everytime a client ask for a file ending with ".css" or ".js" or ".atom" (...), relayd tag the resquest with "CACHE". At last we add a header to increase cache to 21 days to requests with this tag.
Set default encoding ยง
Following the same scheme, you can specify the default encoding according to file extension :
match request path "/*.html" tag "UTF8" match request path "*/" tag "UTF8" match response tagged "UTF8" header set "Content-Type" value "text/html;charset=UTF-8"
About relayd tags ยง
Understand you can't set multiple tags at once. If you want to apply the two above headers (cache-control and content-type), you must do it separately since relayd configuration is processed in order :
match request path "/*.html" tag "CACHE" match response tagged "CACHE" header set "Cache-Control" value "max-age=1814400" match request path "/*.html" tag "UTF8" match response tagged "UTF8" header set "Content-Type" value "text/html;charset=UTF-8"
Databases ยง
Databases are essentials to organize data linked to each others.
As example, a blog engine needs to know who commented which article, at which date and by whom. This comment has a link, the author may have left a mail address to get an alert when there is an answer... All those are tied together.
When you self host, you may not need a huge database engine. In this scenario, SQLite is a better choise : easy to backup and light even if there are less features.
SQlite ยง
It is easy and light. To backup, just copy a file. It is more than enough in mosts cases.
To install, just :
# pkg_add sqlite3.
To use with PHP, add "php-pdo_sqlite-*" and "php-sqlite3-*".
To backup the database, just copy the database file. That's it ๐.
MariaDB (MySQL) ยง
Very well known database engine, MySQL or MariaDB is often required in webapps. Make sure to understand how to secure your installation as it is a sensitive software.
Read "/usr/local/share/doc/pkg_readmes/*" related to mariadb install.
To use with PHP, install php-mysqli-* and php-pdo_mysql-* then enable extensions as explained in PHP's part.
Afin d'installer MariaDB, il faut lancer les commandes suivantes :
To install MariaDB :
# pkg_add mariadb-server # /usr/local/bin/mysql_install_db
The second command install a default database.
To start mysql :
# rcctl enable mysqld # rcctl start mysqld
Finally, use the following command to improve mysql safety :
# /usr/local/bin/mysql_secure_installation
To let httpd talk with MariaDB (it is chrooted), launch thos command to reproduce root structure with appropriate permissions :
# install -d -m 0711 -o _mysql -g _mysql /var/www/var/run/mysql
Add those lines to /etc/my.cnf to change MariaDB socket location so it is accessible to httpd :
[client] socket = /var/www/var/run/mysql/mysql.sock [mysqld] socket = /var/www/var/run/mysql/mysql.sock
At last, restart mysql :
# rcctl restart mysqld
Now you can add users and databases.
As example, we will show how to create a database for Wordpress.
Enter "mysql -u root -p" To get to the MariaDB shell. Below see a log of inputs and ouputs.
# mysql -u root -p Enter password: Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 3 Server version: 10.0.23-MariaDB-log openBSD port: mariadb-server-10.0.23p0v1 Copyright (c) 2000, 2015, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [(none)]> CREATE DATABASE wordpress_base; Query OK, 1 row affected (0.01 sec) MariaDB [(none)]> CREATE USER 'wp'@'localhost' IDENTIFIED BY 'password'; Query OK, 0 rows affected (0.01 sec) MariaDB [(none)]> GRANT ALL PRIVILEGES ON wordpress_base.* TO 'wp'@'localhost'; Query OK, 0 rows affected (0.00 sec) MariaDB [(none)]> FLUSH PRIVILEGES; Query OK, 0 rows affected (0.00 sec) MariaDB [(none)]> exit Bye
That's it ๐.
To get backup a MariaDB base (a dump) to db-name :
# mysqldump -u root -p db-name > /var/backup/db_backup
Of course, edit user "root" and "db-name".
To restore the database :
- 1. Delete old db : # mysql -u root -p -e "DROP DATABASE db-name"
- 2. Recreate the base, but empty : # mysql -u user -e "CREATE DATABASE db-name"
- 3. Import the previous dump : # mysql -u user -p db-name < /var/backup/db-backup
Notice the "<" backwards.
PostgreSQL ยง
PostgreSQL is another database engine. Install postgresql-server to use it.
With PHP, you'll need php-pgsql-* and php-pdo_pgsql-*.
Read /usr/local/share/doc/pkg-readmes/postgresql* carefully.
Create a default database :
# su - _postgresql $ mkdir /var/postgresql/data $ initdb -D /var/postgresql/data -U postgres -A scram-sha-256 -E UTF8 -W $ exit
Default user is postgres.
Edit /var/postgresql/data/postgresql.conf to suit your needs.
To let httpd access postgresql, you should have :
unix_socket_directories = '/var/www/tmp/postgresql, /tmp'
You have to edit permissions on this directory :
# mkdir -p /var/www/tmp/postgresql # chown _postgresql:www /var/www/tmp/postgresql
To start postgresql, as usual :
# rcctl enable postgresql # rcctl start postgresql
To get the postgresql shell :
# su _postgresql -c psql
Below a few examples to dael with postgresql :
Change admin password :
# psql -U postgres -c "ALTER USER postgres WITH PASSWORD 'new_password'";
Add "toto" user :
# psql -U postgres -c "CREATE USER toto WITH PASSWORD 'password';"
Crรฉer une nouvelle base et donner ร toto tous les droits dessus :
Create a new database and let toto do everything he wants with :
# psql -U postgres \connect template1 CREATE DATABASE "new_db" WITH ENCODING 'UTF-8'; GRANT ALL PRIVILEGES ON DATABASE "new_db" TO toto; ALTER DATABASE "new_db" OWNER TO toto; \q
To save a database with postgresql, you actually save all the instructions to recreate the database :
# pg_dump db-name > /var/backup.db
To restore the base :
# psql -U postgres db-name < /var/backup.db
Webapps you can host ยง
There are numbers of webapps : Wikis, Blogs, CMS, Webmails...
Most of the time, you'll need PHP and sometimes a database engine to run them.
โ Make sure to check official documentation related to each webapps, and check release notes as long as you host these apps to keeps your install secure.
Most of the time, the steps to install a webapp are the same.
- 1. Make a new directory dedicated to the app in "/var/www/htdocs" ;
- 2. Download and uncompress an archive of the application ;
- 3. Move files in the previous directory ;
- 4. Adjust permissions on the new files : "# chown -R www:daemon /var/www/htdocs/thesite". Fine tune this part, of course.
- 5. Edit "/etc/httpd.conf" following related documentation or looking in an eventual htaccess file to restrict some accesses ;
- 6. If necessary, increase upload size limit for httpd and PHP.
- 7. Reload httpd then end the install with a browser opened on your new website.
Nice webapp selection ยง
There are so many webapps you may ask yourselves which one you should consider. Below I suggest a few of them with a few requirements :
- Priority is given to tools already packaged for OpenBSD so you can enjoy tweaks from OpenBSD developpers. Thus, it is more secure and already well integrated.
- They must be as light as possible : if you self-host, you may not have very powerful hardware.
- They should be easy to install or administer. That's why we avoid complex databases and prefer SQLite.
One can appreciate :
- Cloud : Nextcloud. Just "# pkg_add nextcloud", then read "/usr/local/share/doc/pkg-readme/nextcloud". ๐
- Wiki : Dokuwiki. Just "# pkg_add dokuwiki", then read "/usr/local/share/doc/pkg-readme/dokuwiki". It is not only a wiki, and can be used as a blog engine or a CMS. It's quite an amazing tool. Also, read dokuwiki notes about security (https://www.dokuwiki.org/security) and on openbsd (https://www.dokuwiki.org/install:openbsd).
- Webmail : SnappyMail. It is easy to install as you can read a bit further. If you prefer, Roundcube is packaged for OpenBSD even so it's harder to administer. I highly recommend Squirrelmail too, it is old but well tested and works very well. See a configuration example here : https://si3t.ch/log/2022-09-14-squirrelmail-nice-webmail.xhtml
- Blog : Blogotext (very handy) or PluXML.
- Feed reader : Kriss feed (just 1 file ๐) or the bigger FreshRSS (available in ports tree).
Where can I find other apps to host ? ยง
This github selection is well filled.
Webmail attachments or tools requiring file upload ยง
Attachments size are 35M by default with smtpd mail server, that's why you should edit php-*.ini configuration :
post_max_size = 35M upload_max_filesize = 35M
You have to set this limit in httpd's configuration too :
connection max request body 36700160
About hosting emails ยง
To get in charge of your communication tools is a big deal to more freedom and independence. This part will describe how to host an email server at home. No database will be required because it has no benefit on a small server. Let's keep things simple !
Setting up a mail serer is often considered difficult. That's why we will split this tutorial in small easy steps : complex, but not complicated.
In order, you will :
- Configure specific DNS records;
- Create certificates to secure logins;
- Consider using virtual users ;
- Configure opensmtpd (smtpd), the daemon in charge of receiving and sending emails;
- Configure dovecot so you can read your emails from multiple devices with a client like Thunderbird;
- Ensure your messages won't be considered as spams
- Install antispams on different levels if you want to.
Of course, you'll learn how to anew accounts and how to configure a mail client.
You first have to open port 25 (smtp) in your firewall and router.
Some ISP are very restrictive with this port. You can ask them to be more permissive, but make inquiries first to avoid bad surprises. At last resort, we'll discuss how to use external smtp serices.
Read also opensmtpd main developper article about this subject.
DNS configuration for an email server ยง
Emails requires specific DNS records. You have add at least two new fields.
First, make sure you have an "A" record (probably) :
athome.tld. IN A 192.0.2.2
Of course, use your real IP here.
You must add a MX field pointing to the previous record :
athome.tld. IN MX 1 athome.tld.
Notice ending dots.
"1" describe the weight of the field. This parameter will be used to prioritize mails servers because some of them will be backups if the main server is unavailable. We'll talk about this later.
Some may want to use a specific domain, another A field to organize their mail servers. That is not mandatory, but interesting :
mail1.athome.tld. IN MX 1 mail1.athome.tld.. mail1.athome.tld. IN A 192.0.2.2
Below is another example using a different syntax :
$ORIGIN athome.tld [...] @ IN MX 10 mail1.athome.tld. @ IN A 192.0.2.2 mail1 IN A 192.0.2.3
Here, you can see the mail server for this zone is hosted on a different IP.
That's all for now.
Get certificates ยง
To encrypt traffic with your email server, you'll need TLS certificates.
It is explained in the chapter about httpd.
However, you can use self-signed certificates if you prefer, that is absolutely not an issue. (seel man ssl(8))
In the next parts, we'll consider using letsencrypt certificates to keep consistent.
Just remember to hae a certificate for the domain in the MX field of your server.
A mail server in 10 minutes (almost ^^) ยง
I you're in a hurry, here is a quick setup to have a working email server. It will also be a good starting point to build on later. With the following :
- Mail accounts are system user accounts.
- Messages are stored in a ~/Mailir folder in user's folder.
- An user "jdoe" will get an email address "jdoe@athome.tld".
- Just use "adduser" command to add an account. You should set for thos users the shell "/sbin/nologin".
- An username is valid for every domain names served.
Later, we'll discuss virtual users to fix these minor issues. But here, we're in a rush so let's go! ๐
smtpd.conf file ยง
Edit /etc/mail/smtpd.conf :
# Generic config # Tables table aliases "/etc/mail/aliases" # Certificates pki athome.tld key "/etc/ssl/private/athome.tld.key" pki athome.tld cert "/etc/ssl/athome.tld.crt" # Get messages listen on all tls pki athome.tld # To send with mail client listen on all port submission tls-require pki athome.tld auth # ACTIONS action "sendthismail" relay # System aliases action in_maildir maildir alias <aliases> # Get match for local action in_maildir match from any for domain athome.tld action in_maildir # Sending match for any action "sendthismail" match auth from any for any action "sendthismail"
That's it ๐
There a almost more comments than reals instructions.
Dovecot configuration (IMAP) ยง
Install dovecot : "# pkg_add dovecot".
In "/etc/dovecot/local.conf" add :
# listen on IPV4 and IPV6. listen = *, [::] # We use Maildir mail_location = maildir:~/Maildir # only username, not user@domain.tld to auth auth_username_format = %n # imap > pop protocols = imap # Safety. Edit those lines ! ssl = yes ssl_cert = </etc/ssl/athome.tld.crt ssl_key = </etc/ssl/private/athome.tld.key disable_plaintext_auth = yes # auth methods passdb { driver = bsdauth } userdb { driver = passwd }
Reload dovecot : "# rcctl restart dovecot".
Here you go, it's done, you can configure your mail client.
Full mail server with virtual users ยง
"Virtual users" means they are not UNIX system users with a shell. However, they are obviously using your mail server.
New accounts won't be created with "adduser" but you will edit a file with the username and it's password hash.
Here we'll discuss how to install a SMTP (Opensmtpd) and IMAP (Dovecot) server
One user to rule them all : _vmail ยง
We will create an user with the sweet name "_vmail" to manage every emails access. It won't have any shell, it's safer ๐.
# useradd -m -g =uid -c "Virtual Mail" -d /var/vmail -s /sbin/nologin _vmail
A new directory is created : /var/vmail. Emails will be stored inside, but following a structure with subdirectories for each virtual users. As example :
/var/vmail/athome.tld/batman/Maildir /var/vmail/athome.tld/user/Maildir /var/vmail/athome.tld/ninja/Maildir ...
/etc/mail/virtuals ยง
In this file, we write user's emails, one on each line. Actually, it works like the file /etc/mail/aliases. (read man aliases(5) ๐)
heroes@athome.tld batman@athome.tld,superman@athome.tld batman@athome.tld _vmail superman@athome.tld _vmail kiki@athome.tld _vmail
As you can see, every email addresses belongs to user _vmail.
Notice the first line : you see an example showing how to transfer emails sent to "heroes@athome.tld" to "batman@athome.tld" and "superman@athome.tld".
Notez que sur la premiรจre ligne, on a fait un alias ร titre d'exemple pour transfรฉrer un mail d'une adresse ร une autre.
/etc/mail/passwd ยง
Same as before, one line per passphrase :
batman@athome.tld:$2b$09$lerdFpdQtnu.Bs5EpAsVbeF851GjdD0aza8IDhho38i1DOHk.ujzi superman@athome.tld:$2b$09$VRU/CYJUS3QZHVUFP70xIOURPbiNQeyOEZHoZo6NOY3uO.XSpd2MW
Notice how ":" split email address and the hashed passphrase : <email>:<hash>.
- L'adresse mail du compte ;
- Le mot de passe chiffrรฉ ;
To hash passphrases, use "encrypt" command :
encrypt -b a -p
Or "smtpctl encrypt passphrase".
(Facultative) Fine tuning files permissions ยง
Previous files shouldn't be readable by everyone. Let's adjust permissions so root and mail daemons -- dovecot and smtpd -- can read passwords. It is not mandatory, but I suggest this part as good habits that can hurt anyone ๐.
Dovecot and smtpd run as users _dovecot and _smtpd. If one day, one of this process is compromised, it will avoid privilege escalation and threaten the whole system.
Let's create a new group _maildaemons for _dovecot and _smtpd to ease permissions :
# groupadd _maildaemons # usermod -G _maildaemons _smtpd # usermod -G _maildaemons _dovecot
Of course, if you haven't installed dovecot yet since we discuss about it later : "# pkg_add dovecot".
Now we can set owner and group for files storing logins and passwords :
# chown root:_maildaemons /etc/mail/passwd /etc/mail/virtuals
Finally, only root can modify these files and _maildaemons can only read. Others can't do anything :
# chmod 640 /etc/mail/passwd /etc/mail/virtuals
To check everything is as expected :
# ls -l /etc/mail/passwd -rw-r----- 1 root _maildaemons 17226 Nov 12 08:40 /etc/mail/passwd
Opensmtpd (smtpd) config ยง
Opensmtpd or smtpd is the default mail server shipped with OpenBSD. You just have to configure it.
First of all, make sure you opened thos ports : 25 (smtp), 587 (submission) and 993 (imaps). The latter will be user with dovecot. Do not care about 465 (smtps) since it is deprecated.
To configure smtpd, edit "/etc/mail/smtpd.conf". Instructions will be applied in order.
We will split it in 3 parts :
- Generic options for smtpd ;
- Actions we will apply on envelopes later;
- Rules matching envelopes and associated action to apply.
Adjust the example below to your needs :
# Generic configuration # Tables table aliases "/etc/mail/aliases" table passwd "/etc/mail/passwd" table virtuals "/etc/mail/virtuals" # Certificates pki athome.tld key "/etc/ssl/private/athome.tld.key" pki athome.tld cert "/etc/ssl/athome.tld.crt" # Listening ports # Reception listen on all tls pki athome.tld # Sending with a mail client listen on all port submission tls-require pki athome.tld auth <passwd> # ACTIONS action "sendthismail" relay action local_mail maildir alias <aliases> action virtual_maildir maildir "/var/vmail/%{dest.domain:lowercase}/%{dest.user:lowercase}/Maildir" virtual <virtuals> # In/Out # Reception # Message for virtual users match from any for domain athome.tld action virtual_maildir # Message for system users match from any for local action local_mail # Sending match auth from any for any action "sendthismail" match for any action "sendthismail"
There is almost nothing to change in this file, except domain name.
WAIIIIIT a minute! Tell me more!
Take a look at each lines :
First, there are generic instructions
- "table aliases ..." : This file is a aliases files, to forward envelopes between system users.
- "table passwd ..." : Hashed passwords are stored here
- "table virtuals ..." : Virtual users file. It is also a alias file.
- "pki ..." : Certificate and key location for encrypted traffic.
- "listen on all tls pki athome.tld" : smtpd listen (on port 25) to receive mails from other servers. Here, traffic is tls encrypted if possible with the certificated defined before.
- "listen on all port submission tls-require pki athome.tld auth <passwd>" : The server listen on port "submission" and require tls encryption and authentication matchin tabe <passwd>. It is necessary to send email using a mail client from another device than the server itself.
Then, we define actions applied on envelopes.
- "action "sendthismail" relay" : smtpd send the mail to the smtp server "written on the envelope".
- "action local_mail maildir alias <aliases>" : smtpd deliver the envelope in a maildir directory after matching with the file <aliases>. It is for system users.
- "action virtual_maildir maildir "/var/vmail/%{dest.domain:lowercase}/%{dest.user:lowercase}/Maildir" virtual <virtuals>" : Here, smtpd deliver in a maildir directory located in _vmail's directory. Envelopes are stored according to domain name and user recipient. Everything is lowercased to avoid mistakes. That's here the magic of virtual users.
At last, we apply actions according to envelope criterias.
If not mentioned, the rule is matching for a local mail. In other case, we add "from any".
- "match for local action local_maildir" : deliver local mails.
- "match from any for domain athome.tld action virtual_maildir" : is the message come from the outside, it is saved in the appropriate maildir according to domain name and recipient's name.
- "match auth from any for any action "sendthismail"" : If the envelope comes from a mail client outside after ahtentication, send the message to the recipient's smtp server.
- "match for any action "sendthismail"" : We send every other messages from the inside of the server.
Finally, in order to label your outgoing messages with the appropriate domain name, add in "/etc/mail/mailname" your email's domain name. It's the domain mentioned in the MX record.
athome.tld
Now enable and start smtpd :
# rcctl enable smtpd # rcctl restart smtpd
That's all for smtpd ๐.
Dovecot ยง
We will use Dovecot as IMAP server so you can read mails from a client like Thunderbird.
As usual, install dovecot with pkg_add :
# pkg_add dovecot
Now configure dovecot. Edit "/etc/dovecot/local.conf" :
# listen IPv4 and IPv6 listen = *, [::] # imap protocols = imap # Encryption. Edit those lines according to your certificates. ssl = yes ssl_cert = </etc/ssl/athome.tld.crt ssl_key = </etc/ssl/private/athome.tld.key disable_plaintext_auth = yes # where mails are stored. %d is domain, %n is username mail_location = maildir:/var/vmail/%d/%n/Maildir # essential since we edited persmission on /etc/mail/passwd service auth { user = $default_internal_user group = _maildaemons } # Auth methodes passdb { args = scheme=blf-crypt /etc/mail/passwd driver = passwd-file } # Mails are /var/vmail , belongs to _vmail userdb { driver = static args = uid=_vmail gid=_vmail home=/var/vmail/%d/%n/ }
I left comments above to help you understand what is done here.
Adjust ssl_cert and ssl_key according to your certificates.
By the way, ssl configuration is already available in "/etc/dovecot/conf.d/10-ssl.conf". It is supposed to help up with a script generating a self-signed certificate. However, you probably already have your own. You should comment lines in this file :
# /etc/dovecot/conf.d/10-ssl.conf #ssl_cert = </etc/ssl/dovecotcert.pem #ssl_key = </etc/ssl/private/dovecot.pem
Finally, reload mail daemons :
# rcctl enable dovecot # rcctl start dovecot # rcctl restart smtpd
Now you can use a mail client to read your messages.
Add a mail account ยง
Add a new line for the new account in "/etc/mail/virtuals" and "/etc/mail/passwd" then reload tables for smtpd (dovecot read them on the fly) :
smtpctl update table virtuals smtpctl update table passwd
Or "rcctl restart smtpd"
Manage multiple domains ยง
You can host an email server and manage multiple domain names.
However, you should organize how you set this up.
Below are a few notes on how to achieve this.
smtpd changes for multi-domains ยง
I suggest to create a file containing every hosted domain, one per line. Let's call il "/etc/mail/domains" :
athome.tld domain.tld other.bar
This, in "/etc/mail/smtpd.conf" you can write one line for multiple domains :
table domains "/etc/mail/domains" ... match from any for domain <domains> action virtual_maildir
Take care of used TLS certificates. If you have a certificate for each domain, you can specify each of them in smtpd.conf. Make sure you have a default certificate in the end ("*").
pki athome.tld key "/etc/ssl/private/athome.tld.key" pki athome.tld cert "/etc/ssl/athome.tld.crt" pki domain.tld key "/etc/ssl/private/domain.tld.key" pki domain.tld cert "/etc/ssl/domain.tld.crt" pki other.bar key "/etc/ssl/private/other.bar.key" pki other.bar cert "/etc/ssl/other.bar.crt" pki "*" key "/etc/ssl/private/athome.tld.key" pki "*" cert "/etc/ssl/athome.tld.crt" ... listen on all tls ... listen on all port submission tls-require auth <passwd>
HOWEVER, you can use only one certificate matching multiple domains. To do so, use "alternative names" in acme-client configuration. It is absolutely valid and much easier to manage. If so, configure smtpd as if there was only one certificate.
dovecot changes to support multiple domains ยง
Dovecot will need some care to handle certificates for each domain. Add sections "local_name" in its configuration so it looks like this :
ssl = yes ssl_cert = </etc/ssl/athome.tld.crt ssl_key = </etc/ssl/private/athome.tld.key # no plaintext disable_plaintext_auth = yes local_name domain.tld { ssl_cert = </etc/ssl/domain.tld.crt ssl_key = </etc/ssl/private/domain.tld.key } local_name other.bar { ssl_cert = </etc/ssl/other.bar.crt ssl_key = </etc/ssl/private/other.bar.key }
Here also, a single certificate for multiple domains is much more easier to set up.
Redirecting mails ยง
As explained in man aliases(5), you can redirect mails to various destination.
As example, you host jdoe@athome.tld and want to redirect every mails received by jdoe to batman@wayne.com.
Edit /etc/mail/virtuals and add :
jdoe.athome.tld: batman@wayne.com
You can set redirections in /etc/mail/aliases too. For system users, you can use their login only :
root: jdoe hostmaster: root postmaster: root webmaster: bruce jdoe: jdoe@domain.tld
Remember to restart smtpd so changes are considered :
# rcctl restart smtpd
Easy. ๐
How to configure you mail client ? ยง
To use your mail server with a client such as Thunderbird, use the parameters below :
- Server name : athome.tld
- Email : jdoe@athome.tld
- Username: full mail address as in "/etc/mail/virtuals"
- IMAP : port : 993 ; SSL : SSL/TLS
- SMTP : port : 587 ; SSL : STARTTLS
DNS record for clients configuration (facultative) ยง
To help clients finding automatically how to configure parameters to use your mail server, you can add specific DNS records.
_submission._tcp.athome.tld 86400 IN SRV 0 1 587 athome.tld _imap._tcp.athome.tld 86400 IN SRV 0 0 0 . _imaps._tcp.athome.tld 86400 IN SRV 0 1 993 athome.tld
Read rfc6186 about these records.
Do not loose mails : MX fields and backup ยง
If, for reasons, your server is offline for a few days or a week, mails could be lost. Normally, senders try again for a while if they couldn't deliver a message.
However, you can plan secondary mail servers to keep your messages if yours is unreachable. You only need :
- Another server (ahem... ๐)
- A friend with its own server ๐
It will keep messages for your server in queue and will deliver them as soon it gets back online.
On your side, add a new MX field in your DNS zone with a bigger weight. This field points to the secondary server :
@ IN MX 10 athome.tld. @ IN MX 70 mail.friend.eu.
In the above example, your server is ligther (10) than the secondary (70). This means a sending server will try first to deliver on your server, then on the secondary if the first failed to answer.
On his side, your friend just has to add your domain as a backup. If he uses smtpd, then /etc/mail/smtpd.conf should look like this :
action relaybackup relay backup mx "athome.tld" ... match from any for domain athome.tld action relaybackup
Of course, you should return the favor ๐.
Do not be considered as a spam (SPF, DKIM...) ยง
Some servers could consider your mails as spams. There are a few proofs of good faith you can setup if possible. They are not mandatory. Keep in mind that most spams comes from most well known servers (gmail, I'm looking at you) so don't be too hard on yourselves.
Reverse DNS ยง
Your ISP might let you configure a reverse DNS. As it suggests, a reverse DNS links your IP to your domain name.
Look in your ISP panel for reverse DNS, or ask them directly. They are responsible for this ๐.
If you can't and reaaaallly want a rDNS, you could rent a VPN and get a dedicated IP with a rDNS configured by the VPN provider. But in most cases, following next steps should be sufficient.
SPF ยง
SPF records show that only YOUR server is allowed to send mails for YOUR domain name. Since it's usually the server's admin who also deals with DNS record, it's a proof of good faith.
Add a DNS record of type SPF in your zone such as :
athome.tld. SPF "v=spf1 a mx ~all"
Or use a TXT field if SPF is not available :
athome.tld. TXT "v=spf1 a mx ~all"
Above is a very simple example that works for most cases. Consider reading about SPF records if you want to fine-tune this record.
DKIM signing ยง
With a private key, your server will sign outgoing emails. In DNS records, you will publish a public key to let recipient check if it matches the signature from your server.
Ahem... Say that again?
Here we go. We will generate a private and a public key.
Private key is used to sign mails. It is "private" because you must be the only ont able to add a signature to outgoind mails.
Public key displayed in DNS record -- viewable by all -- let one check signature authenticity. You can see it as a unique puzzle piece, the only one that can fit the puzzle.
We'll see two ways to sing outgoing messages : one with an smtpd extension, the other with dkimproxy. You also could to the same with rspamd, it is described later, following the same method to generate keys. Choose the one you prefer ๐.
Following commands below, you will create a directory for keys, set permissions on this folder and go inside before generating keys with openssl and set permissions on the private part :
# mkdir -p /etc/dkim/ # chmod 770 /etc/dkim/ # cd /etc/dkim/ # openssl genrsa -out private.key 2048 # openssl rsa -in private.key -pubout -out public.key # chmod 400 private.key
Add a DKIM or TXT field so anyone can check signature on your messages match what is published in DNS records.
Mails will receive a flag "dkimpubkey" when signed, it is used to identify the signature in DNS record.
- Field name : : "dkimpubkey._domainkey."
- Content : "v=DKIM1; k=rsa; p=..."
Replace "..." the content of the file "public.key" :
# cat /etc/dkim/public.key
The record will look like this :
dkimpubkey._domainkey IN TXT ( "v=DKIM1; k=rsa; t=s;p=v+Fb...vhP/oB")
Since smtpd has filters support, you can sign your messages with port opensmtpd-filter-dkimsign :
# pkg_add opensmtpd-filter-dkimsign
Make sure the script can read keys generated as seen before :
# chown -R _dkimsign:_dkimsign /etc/dkim/
In "/etc/mail/smtpd.conf", you add now a new filter with instructions on how to sign messages :
filter "dkimsign" proc-exec "filter-dkimsign \ -d <domain> \ -s <selector> \ -k /etc/dkim/private.key" \ user _dkimsign group _dkimsign
Replace "<domain>" with your domain name and "<selector>" by "dkimpubkey" : that's what we defined in the DNS field earlier.
Now, make sure outgoing mails are processed by this new filter. In "/etc/mail/smtpd.conf", the line for outgoing messages now look like this :
Enfin, il vous suffit de faire passer les mails sortants par ce filtre. On modifie la ligne correspondant ร l'envoi des messages dans "/etc/mail/smtpd.conf":
listen on all port submission tls-require auth <passwd> filter "dkimsign"
As an alternative, you can use dkimproxy if you prefer to sign mails.
As usual :
# pkg_add dkimproxy
Make sure dkimproxy can read keys :
# chown -R _dkimproxy:_dkimproxy /etc/dkim/
Now configure dkimproxy to match you configuration (domain and DNS selector) :
In "/etc/dkimproxy_out.conf" :
listen 127.0.0.1:10027 relay 127.0.0.1:10028 domain athome.tld signature dkim(c=relaxed) signature domainkeys(c=nofws) keyfile /etc/dkim/private.key selector dkimpubkey
Of course, edit domain and selector according to your DNS record.
Now, tell smtpd to listen for incoming signed and to-send messages on port 10028. They will be tagged "DKIM". Then we can send mails with this tag. On the contrary, they are forwarded to dkimproxy on port 10027.
In /etc/mail/smtpd.conf :
listen on lo0 port 10028 tag DKIM ... match tag DKIM for any action "sendthismail" match auth tag DKIM from any for any action "sendthismail" ... action dkimproxy relay host smtp://127.0.0.1:10027 ... match auth from any for any action dkimproxy match for any action dkimproxy
Finally enable dkimproxy and restart smtpd :
# rcctl enable dkimproxy_out # rcctl start dkimproxy_out # rcctl restart smtpd
Check it works ยง
You may read about dmarc and other advices if you want to fin tune your configuration. Remember your score is already better than most "big" provider. Last time I checked with a gmail address, I got 6.1/10... ๐
Blocking ISP : use external SMTP ยง
If your ISP restrict the use of smtp port (25), you can't send any email from your server. To fix this, you may :
- Change for another ISP ๐
- Use an external smtp relay. That's what we'll discuss here.
However, you need another smtp provider. Put the necessary credentials to access this mail account in "/etc/mail/secrets" :
# echo "secret_id user:passphrase" > /etc/mail/secrets
Make sure permissions are appropriate, you don't want everyone to know your password :
# chmod 640 /etc/mail/secrets # chown root:_smtpd /etc/mail/secrets
Then, edit "/etc/mail/smtpd.conf" so outgoing messages go throught external mail server :
... table secrets "/etc/mail/secrets" ... listen on all... ... action "relay" relay host smtp+tls://secret_id@smtp.example.com \ auth <secrets> mail-from "@athome.tld" ... match from any for any action "relay"
Some details :
- "table secrets ...": file where credentials to auth on external server are stored.
- "action "relay" relay ..." : new action so outgoing messages through external server. Edit smtp.example.com according to your mail provider.
- "smtp+tls ..." : Which protocol is used to communicate with external server.
- "mail-from "@athome.tld"" : It is important to show your messages comes from YOUR server, not the relay.
In the end, reload smtpd.
rcctl restart smtpd
Read the example in the end of smtpd manpage to learn more.
Avoid receiving spam : senderscore filter ยง
Senderscore keeps a database of mail server reputation.
Havin a moderate reputation doesn't mean much, but having a bad one means the server already has sent spams before.
You can use a filter in smtpd to recognize as spam incoming mails according to senderscore.
Install opensmtpd-filter-senderscore port.
Then, edit /etc/mail/smtpd.conf to add a filter :
filter senderscore \ proc-exec "filter-senderscore -junkBelow 70 -slowFactor 2000" ... listen on all tls pki athome.tld filter { senderscore }
Avoid receiving spams : spamassassin ยง
spamassassin is a well-known anti-spam. It is used by OpenBSD project with spamd for their mailing lists. Let's see how to use it with smtpd using tags since the setup is interesting if you want to understand how things works. You may instead want to use the filter opensmtpd-filter-spamassassin : install the port then read /usr/local/share/doc/pkg-readmes/opensmtpd-filter-spamassassin.
Install appropriate ports :
# pkg_add p5-Mail-SpamAssassin spampd
Run spamassassin daemons.
You may add a few flags to make sure it is running as we expect.
# rcctl enable spampd # rcctl set spampd flags "--relayhost=127.0.0.1:10026" # rcctl start spampd # rcctl enable spamassassin # rcctl start spamassassin
Every incoming mails now must be checked by spammassassin, which is listening on port 10025. Once they've been scanned, they will be delivered on port 10026 and tagged "SPAMASSASSIN" so we don't check them twice.
That's how it looks in "/etc/mail/smtpd.conf" :
... # mails checked by spammassassin listen on lo0 port 10026 tag SPAMASSASSIN ... action spamassassin relay host smtp://127.0.0.1:10025 ... # system mails accept from local for local alias <aliases> deliver to maildir "~/Maildir" # Mails scanned by SPAMASSASSIN match tag SPAMASSASSIN from any for domain "athome.tld" action virtual_maildir # not tagged mails, must be checked by spammassassin match from any for domain "athome.tld" action spamassassin ...
Spams subject are modified by spammassassin.
You can automatically delete them, but it's a risk in case of false positive.
In order to save spams in a junk folder, modify smtpd's action so it checks for "X-spam" header :
# /etc/mail/smtpd.conf action virtual_maildir maildir "/var/vmail/%{dest.domain:lowercase}/%{dest.user:lowercase}/Maildir" junk virtual <virtuals>
Avoid receiving spams : rspamd ยง
It is also very fast and efficient. If you want to use it, you should read it's official documentation. For now, let's see how to use it with OpenBSD's smtpd as an antispam and for DKIM.
Install rspamd ยง
# pkg_add rspamd redis opensmtpd-filter-rspamd # rcctl enable redis rspamd # rcctl start redis rspamd
Add rspamd to /etc/mail/smtpd.conf ยง
Just add a new filter named "filter-rspamd" and use it for incoming messages.
filter rspamd proc-exec "filter-rspamd" # filtre en reception listen on all tls pki athome.tld \ filter { rspamd }
DKIM with rspamd ยง
Since rspamd can handle DKIM signatures, you won't have to configure dkimproxy or another tool.
Create keys as described before and make sure they belong to _rspamd group.
# chown -R _rspamd:_rspamd /etc/dkim/
Remember to edit your DNS zone ๐.
Now create "/etc/rspamd/local.d/dkim_signing.conf" file :
# If true, username does not need to contain matching domain allow_username_mismatch = true; path = "/etc/dkim/private.key"; selector = "dkimpubkey";
Then add a few lines in "/etc/mail/smtpd.conf" to sign outgoing messages :
filter rspamd proc-exec "filter-rspamd" # Send and DKIM sign with rspamd listen on all port submission tls-require pki athome.tld auth \ filter { rspamd }
Greylisting ยง
Rspamd does greylisting by default. If you want to still use spamd instead, disable rspamd greylisting in file "/etc/rspamd/local.d/actions.conf" :
greylist = none;
And file /"etc/rspamd/local.d/greylist.conf" :
enabled = false;
Spamtraps ยง
Contrairement ร spamd, rspamd ne garde pas captif les spammeurs qui รฉcrivent sur une spamtrap. Cela sert tout de mรชme ร reconnaรฎtre des spammeurs pour plus tard.
You can use a spamtrap (like what spamd does) using the following lines in "/etc/rspamd/local.d/spamtrap.conf" :
action = "no action"; learn_spam = true; map = file://$LOCAL_CONFDIR/maps.d/spamtrap.map; enabled = true;
Then fill with regular expressions of fake trap mail addresses : "/etc/rspamd/maps.d/spamtrap.map"
/^trap@athome.tld$/ /^fake@athome.tld$/
Blacklists ยง
To do so, use multimap module.
rspamd WebUI ยง
Configure admin following these instructions
Then, dig a SSH tunnel from your computer and open in a browser http://localhost:9999.
ssh -N -L 9999:127.0.0.1:11334 sshuser@athome.tld
Enjoy wonderfull charts ๐
Manage spams with dovecot ยง
You might want to help the anti-spam to learn if it missed spams or got wrong for some messages.
If you use dovecot and a mail client with IMAP protocol, you can move spams in "Junk" folder so the anti-spam recognize them in the future. On the contrary, moving a mail from "Junk" to "INBOX" folder indicates it is legitimate.
No matter if you use rspamd or spammassassin, we will see both. 3 steps are necessary :
1. Install dovecot plugin.
2. Enable and configure the plugin.
3. Create scripts so the anti-spam can learn.
It is described in dovecot documentation (you should read ๐)
First, let's install dovecot pigeonhole plugin :
# pkg_add dovecot-pigeonhole
Add "imap_sieve" in plugins list to enable it at the end of "/etc/dovecot/local.conf" :
protocol imap { mail_plugins = $mail_plugins imap_sieve }
Then, still in dovecot's local.conf, configure sieve plugin si it run scrits according to where messages are moved..
plugin { sieve_plugins = sieve_imapsieve sieve_extprograms # When a message is moved in Junk folder; report as spam imapsieve_mailbox1_name = Junk imapsieve_mailbox1_causes = COPY imapsieve_mailbox1_before = file:/usr/local/lib/dovecot/sieve/report-spam.sieve # When a mail is removed from Junk folder : it is nor a spam imapsieve_mailbox2_name = * imapsieve_mailbox2_from = Junk imapsieve_mailbox2_causes = COPY imapsieve_mailbox2_before = file:/usr/local/lib/dovecot/sieve/report-ham.sieve sieve_pipe_bin_dir = /usr/local/lib/dovecot/sieve sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute }
Now create two files in the directory "/usr/local/lib/dovecot/sieve/".
The first is "report-spam.sieve":
require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"]; if environment :matches "imap.user" "*" { set "username" "${1}"; } pipe :copy "learn-spam.sh" [ "${username}" ];
The second is "report-ham.sieve" :
require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"]; if environment :matches "imap.mailbox" "*" { set "mailbox" "${1}"; } if string "${mailbox}" "Trash" { stop; } if environment :matches "imap.user" "*" { set "username" "${1}"; } pipe :copy "learn-ham.sh" [ "${username}" ];
Now reload dovecot and compile the above scripts :
# rcctl restart dovecot # sievec /usr/local/lib/dovecot/sieve/report-spam.sieve # sievec /usr/local/lib/dovecot/sieve/report-ham.sieve
As you may have notices, the above files call "learn-spam.sh" and "learn-ham.sh". Thoses scripts will be different wether you use rspamd or spammassassin since they don't learn spams/hams with the same command. Read the next part ๐.
Learning scripts for spamassassin ยง
Scripts below are stored in "/usr/local/lib/dovecot/sieve".
#!/bin/sh #learn-spam.sh /usr/local/bin/spamc -u ${1} -L spam -C report #!/bin/sh # learn-ham.sh /usr/loca/bin/spamc -u ${1} -L ham -C report
Learnin scripts for rspamd ยง
Scripts below are still in "/usr/local/lib/dovecot/sieve".
#!/bin/sh #learn-spam.sh exec /usr/local/bin/rspamc -d "${1}" learn_spam #!/bin/sh # learn-ham.sh exec /usr/local/bin/rspamc -d "${1}" learn_ham
Scripts for both ยง
Scripts must be executables :
# chmod +x /usr/local/lib/dovecot/sieve/learn-*.sh
At last, reload dovecot:
# rcctl reload dovecot
Here you go, now your anti-spam will learn when you'll move messages in approriate folders.
Learn more with dovecot official documentation.
Fight against spams before receiving one : OpenBSD's spamd ยง
You can sort incoming spams into a Junk folder. But imagine annoying spammers ๐? That's what spamd can do, faking being a mail server and holding spammers so they can't deliver spams while it's talking to them veeery slowly.
How does spamd work ? ยง
You can let somewhere on the web a fake mail address : a spamtrap. Everyone sending a message to this address will be blacklisted and trapped.
In the meantime, new senders are greylisted. They're told to try again later. Every proper email server will try again. Spammers won't, so you don't get the spam. Others who try again correctly are put on a whitelist.
There is also a blacklist-only mode, trapping IPs from lists you already have.
However, this means senders do things correctly. That's is sadly not always true for commercial senders. This said, it is easy to put a server on a whitelist.
To keep track of good and bad senders, the command "spamd-setup" should be run periodically.
Set spamd up ยง
# rcctl enable spamd # rcctl set spamd flags "-v -G 25:4:864" # rcctl start spamd
Also enable spamlogd to keep track of servers already whitelisted and to add to whitelist servers you write to.
# rcctl enable spamlogd # rcctl start spamlogd
spamd is running in front of smtpd (like a shield, or a janitor ๐). To do so, edit packet-filter configuration : spamd should redirect mails to smtpd if it is not a spam.
Add in /etc/pf.conf :
table <spamd-white> persist table <nospamd> persist file "/etc/mail/nospamd" pass in on egress proto tcp to any port smtp divert-to 127.0.0.1 port spamd pass in on egress proto tcp from <nospamd> to any port smtp pass in log on egress proto tcp from <spamd-white> to any port smtp
On line order :
- Define a table to keep track of legitimate senders : <spamd-white>
- Create a file /etc/mail/nospamd keeping custom-defined IP spamd should consider as not-spam.
- Everything coming on port smtp goes to spamd.
- We let in IPs listed in <nospamd> table.
- Same with <spamd-white>, IPs known as non-spammers can come in.
Remember to reload pf :
# pfctl -ef /etc/pf.conf
It is necessary to reload spamd blacklist in pf. Edit root's crontab with "# crontab -e" then uncomment the line below :
~ * * * * /usr/libexec/spamd-setup
With "~", spamd-setup is run at a random schedule ๐.
Spamtrap with spamd ยง
Let somewhere on the web fake email addresses : spamtraps. Everyone writing to these addresses is a spammer and will be trapped. It is important thos addresses don't really exist!
To keep these spamtraps for bots only, you can add such html snippets on your website so they are invisible for human eyes :
<a href="mailto:trap@athome.tld"></a>
You can copy it on public pastebins.
To teach spamd what are spamtraps, use the command :
# spamdb -T -a 'trap@athome.tld'
Use pre-existing black/white lists ยง
Some nice people gather IP lists and let everyone download them. To use such lists, edit /etc/mail/spamd.conf.
As example, the default configuration uses :
Add in /etc/mail/spamd.conf the following lines :
all:\ :nixspam # Nixspam recent sources list. # Mirrored from http://www.heise.de/ix/nixspam nixspam:\ :black:\ :msg="Your address %A is in the nixspam list\n\ See http://www.heise.de/ix/nixspam/dnsbl_en/ for details":\ :method=http:\ :file=www.openbsd.org/spamd/nixspam.gz
At the beginning, you can read "all". Then are written lists you want to use and configured below, separated with ":".
Use an URL to download the list with spamd-setup automatically or a file already on your server.
bsdly's blacklist ยง
Peter N.M.Hansteen publish a blacklist, gathered with its own spamtraps.
Avoid to download it too often or you'll be put on a blacklist. Be king with Peter's bandwidth ๐.
To get the list every hour past 20 minutes :
20 * * * * /usr/local/sbin/bsdly-spamd
Here is bsdly-spamd script you should copy and make executable :
#!/bin/sh # update bsdly.net traplist into DST URL="https://www.bsdly.net/~peter/bsdly.net.traplist" # alternative URL #URL="https://home.nuug.no/~peter/bsdly.net.traplist" DST=/var/db/bsdly.traplist ftp -o "${DST}" "${URL}"
Then add the new list in /etc/mail/spamd.conf so spamd uses /var/db/bsdly.traplist file :
all:\ :nixspam:bsdlyblack: nixspam:\ :black:\ :msg="Your address %A is in the nixspam list\n\ See http://www.heise.de/ix/nixspam/dnsbl_en/ for details":\ :method=https:\ :file=www.openbsd.org/spamd/nixspam.gz bsdlyblack:\ :black:\ :msg="SPAM. Your address %A has sent spam within the last 24 hours. See http://www.bsdly.net/~peter/traplist.shtml for details.":\ :method=file:\ :file=/var/db/bsdly.traplist
uceprotect black and whitelists ยง
Below we describe how to periodically download lists provided by uceprotect. We will use rsync to reduce bandwidth usage.
Add a new cronjob to call a script downloading lists. Not too often or you'll be blacklisted.
10 * * * * /usr/local/sbin/uceprotect-spamd
/usr/local/sbin/uceprotect-spamd is the script below :
#!/bin/sh RSYNC="/usr/bin/openrsync -a" URLS="rsync-mirrors.uceprotect.net::RBLDNSD-ALL/dnsbl-1.uceprotect.net rsync-mirrors.uceprotect.net::RBLDNSD-ALL/dnsbl-2.uceprotect.net rsync-mirrors.uceprotect.net::RBLDNSD-ALL/ips.whitelisted.org" OUT="/var/db/RBLDNSD-ALL/" mkdir -p "${OUT}" for URL in ${URLS}; do ${RSYNC} "${URL}" "${OUT}" done
Load the lises in /etc/mail/spamd.conf. There are white and blacklists.
all:\ :nixspam:bgp-spamd:bsdlyblack:\ rbldnsd-1:rbldnsd-2:rbldnsd-white: ... rbldnsd-1:\ :black:\ :msg="Your address %A is listed on UCEPROTECT-Level 1\n \ see http://www.uceprotect.net/en":\ :method=file:\ :file=/var/db/RBLDNSD-ALL/dnsbl-1.uceprotect.net rbldnsd-2:\ :black:\ :msg="Your address %A is listed on UCEPROTECT-Level 2\n \ see http://www.uceprotect.net/en":\ :method=file:\ :file=/var/db/RBLDNSD-ALL/dnsbl-2.uceprotect.net rbldnsd-white:\ :white:\ :method=file:\ :file=/var/db/RBLDNSD-ALL/ips.whitelisted.org
Do not hesitate do donate to uceprotect.net for all their work ๐.
Common issues with greylisting ยง
Some big mail provider uses multiple servers to send mails. If you do greylisting, the IP trying to send an email from a provider might change between two tries. This means spamd can't recognize a legitimate server, never whitelist the IP and you never get the mail.
However, you can whitelists those domains if you want.
Create a file containing domains not to greylist : /etc/mail/nospamd_domains_list.txt :
gmail.com hotmail.com facebookmail.com apple.com microsoft.com lists.openbsd.org linkedin.com freebsd.org twitter.com amazon.com yahoo.com yahoo.fr live.fr mail-out.ovh.net mxb.ovh.net gandi.net laposte.net protonmail.com
Add more domains if you need to.
Now create a script to get sending IP of above domains : /usr/local/sbin/generate-nospamd :
#!/bin/sh # /usr/local/sbin/generate-nospamd # Auteur : prx <prx@si3t.ch> # licence : MIT DOMAINS=/etc/mail/nospamd_domains_list.txt WHITELIST=/etc/mail/nospamd echo "#$(date)" > "$WHITELIST" smtpctl spf walk < "${DOMAINS}" >> "$WHITELIST" exit 0
Call this script daily with /etc/daily.local and reload nospamd table :
# /etc/daily.local /usr/local/sbin/generate-nospamd pfctl -t nospamd -T replace -f /etc/mail/nospamd
See spamd activity ยง
Run "spamdb" command to see what's happening :
WHITE|62.4.1.33|||1462699174|1462699174|1465809574|1|0 GREY|182.70.43.24|abts-mum-dynamic-024.43.70.182.airtelbroadband.in|<Estella32@thunderguy.co.uk>|<toto@athome.tld>|1473409924|1473424324|1473424324|1|0 GREY|14.183.132.63|static.vnpt.vn|<Abby5@toddelliott.com>|<kiki@athome.tld>|1473410586|1473424986|1473424986|1|0
Read spamdb(8) manpage to learn what it means.
To translate times in human-readable format, run "date" with -r flag :
$ date -r 1462699174 Sun May 8 11:19:34 CEST 2016
You can put on whitelist an IP with "spamdb -a" :
# spamdb -a "62.4.1.37"
Share spamd data between backups ยง
Remember to keep spamd databases synchroniszd between your server and backups (secondary MX). Read about -Y and -y flags in spamd manpage.
Check everything works well ยง
To make sure your mail server works as expected, you can send an email to friends. however, this leads to issues :
- You must wait for their answer :
- You won't get every data you may need ;
- You need friends ๐.
Luckily there are robots taht can automatically answer. As example :
- echo@generic-nic.net
- echo@nic.fr
- autoreply@dmarctest.org
- https://dkimvalidator.com/results website
Take a look at mxtoolbox and mail-tester too, the last one is very nice.
What if smtp server doesn't work as expected? ยง
You should look at smtpctl command to get details about what's happening.
As example, to have generic status :
# smtpctl show stats control.session=1 mda.envelope=0 mda.pending=0 mda.running=0 mda.user=0 mta.connector=1 mta.domain=1 mta.envelope=1 mta.host=2 mta.relay=1 mta.route=1 mta.session=1 mta.source=1 mta.task=1 mta.task.running=0 queue.evpcache.load.hit=1675 queue.evpcache.size=2 queue.evpcache.update.hit=6 scheduler.delivery.ok=826 scheduler.delivery.tempfail=6 scheduler.envelope=2 scheduler.envelope.incoming=0 scheduler.envelope.inflight=1 scheduler.ramqueue.envelope=2 scheduler.ramqueue.message=2 scheduler.ramqueue.update=0 smtp.session=0 smtp.session.inet4=787 smtp.session.local=31 smtp.tls=0 uptime=777861 uptime.human=9d4m21s
To see awaiting envelopes :
# smtpctl show queue 1101f6e60c169eac|inet4|mta||0100015b3a046476-f7d7955a-5842-49af-927c-4fa677f311c6-000000@bounces.duolingo.com|deux@domaine.eu|deux@domaine.eu|1491327053|1491672653|0|5|pending|3154|Network error on destination MXs 1a2123e1c2b3e462|inet4|mta||gitlab@framasoft.org|deux+framagit@domaine.eu|deux+framagit@domaine.eu|1491333449|1491679049|1491333849|1|inflight|50|Network error on destination MXs
To see in real time what's happening :
# smtpctl monitor
And of course; read "man smtpctl" ๐.
Domain name server ยง
DNS (Domain Name System), are road signs of the Internet. That's how a human can recognize server's names, like a big directory but for Internet.
It is also used to know whre a mail should be delivered, and many others things hidden to normal human beings but crucial to have a reliable network.
DNS is based on the idea of a "zone". First, you start at the root represented by a dot ".". The root list addresses of all authoritary name servers : tld (fr, de, com, net, org...). Each tld has a list of servers for the next levels.
That's how it looks like :
. | | +-------+-------+---+--+-------+-------+ | | | | | | v v v v v v .fr .com .tld .xyz .org .net | +-------------+--------------+ | | | v v v site.tld athome.tld other.tld | +------------------+ | | v v webmail.athome.tld wiki.athome.tld
If you start at the root, you can find the IP of any domain name. Searching through the zone is called "resolving". A server hosting data about a zone is "authoritary".
When a resolver retrieve a device IP, for a website or other purpose, it is kept in cache, in memory, as long as it is specified in the Time To Live (TTL) of the zone. So it is not reached too often.
This validity expiration time means there will be a delay before every resolver update a fresh zone. However, a bigger TTL means less network stress. It's about balance.
DNSSEC ยง
A few years ago, clever people started worrying since DNS is critical. It had to be secured.
It is actually quite easy to be redirected on fake addresses. We need a way to ensure DNS authenticity.
To do so, domain name owner wite a zone and sign it. Resolvers check if the signature is correct.
Thus, you are certain to have reached the correct IP behind a domain name, not hijacked on a fake website.
Zone example ยง
Zone files uses a standard format understood by every domain name servers.
Below is an example of "/var/nsd/zones/athome.tld" :
$TTL 1D @ IN SOA master.athome.tld. hostmaster.athome.tld. ( ; domain of DNS server ; followed by admin email address ; Here : hostmaster@athome.tld ; "@" is replaced by a "." 2014110502 ; serial number to increment ; after each changes 86400 ; Refresh 7200 ; Retry 3600000 ; Expire 172800 ) ; Negative Cache TTL $ORIGIN athome.tld. @ IN NS master @ IN NS secondary @ IN MX 10 mail1 @ IN MX 20 mail2 master IN A 192.0.2.2 master IN AAAA 2001:db8:1:1::2 mail1 IN A 192.0.2.10 mail2 IN A 192.0.2.11 ipv4only IN A 192.0.2.15 ipv6only IN AAAA 2001:db8:1:1::400 dualstack IN A 192.0.2.200 dualstack IN AAAA 2001:db8:1:1::200 gate IN AAAA %%ipv6_gate master IN A %%ip_pub_master master IN AAAA %%ipv6_master secondary IN A %%ip_pub_second secondary IN AAAA %%ipv6_second ...
DNS records and how to use them ยง
DNS records are usually written like this :
NAME TTL CLASS TYPE DATA(RDATA)
NAME is what you're looking for when you ask a resolver.
TTL is Time To Live, delay while the data is considered as valid.
Class means internet (IN). At the beginning, other classe were used but not anymore.
Type is the kind of data in the record.
At last, RDATA is the data relative to the Name.
@, $ORIGIN, $TTL ยง
"$ORIGIN" is the complete zone name. As example, "athome.tld".
When a zone file don't have a "$ORIGIN" instruction, the domain name server will create one with the zone name.
"@" is replaced by "$ORIGIN".
Read this page if you want to learn more about this instruction.
"$TTL" is the validity duration of datas. It is recommended to set to 1 day (1D).
Each DNS record can have its own TTL if you want to reduce network stress for very stable fiels such as MX and NS.
"$ORIGIN" and "$TTL" must be written at the beginning of the zone file.
SOA ยง
The first record with "$ORIGIN" and "$TTL" is called SOA as in Source Of Authority. It is crucial.
The first field after SOA indicate the origin name server, in example master.athome.tld. and the last field indicates the domain administrator mail address. "hostmaster.athome.tld." will turn into "hostmaster@athome.tld". The first dot is turned into a "@". You may have set an alias on hostmaster when you configured the mail server. If you can't and have to use a dot before the "@" in the admin mail (you like it rough... ๐ฒ), you have to write a "\" before :
john\.doe.athome.tld.
Autoritary domain name server and administrator mail may be on different servers or domains.
The serial number can have multiple formats, but always must increase everytime the zone is updated. Thus, secondary servers notice there is an update to download. Some admins use a timestamp, others start from 1 and increment each time. It's up to you.
SOA values (refresh, retry, expire, negative) and TTL in the example are those recommended in standard (RFC). Of course you can use your own values, but in doubt you have a robust example.
By default, values are seconds. You can write them as hours (H), days (D) or weeks (W).
"Refresh" and "retry" describe when secondary autoritative servers should reload the zone. Nowadays, most servers send alerts to secondary servers so it's automatic.
"Expire" tells how many time we can still use data in the zone if servers are unreachable. It is not TTL.
"Negative" is the delay a NXDOMAIN answer (non existent data requested) is kept in cache.
A, AAAA ยง
It may be one of the most important record type. Like others, they follow this format : {NAME, TTL, CLASS, TYPE, RDATA}.
Addresses of device master.athome.tld are recorded like this :
master IN A 192.0.2.2 master IN AAAA 2001:db8:1:1::2
- "master" has no dot "." at the end so it is completed to root : master.athome.tld.
- TTL field is empty so it's default. 1 day here.
- Class is IN, meaning INTERNET. It is the only one available nowadays.
- Type here is A (ipv4) or AAAA (ipv6).
- RDATA is the data requested, 192.0.2.2 for A and 2001:db8:1:1::2 for AAAA.
Remember the last dot in addresses is very important. It represent the root zone. If a data doesn't end with a dot, it is expanded or will be buggy.
As example :
@ 1W IN NS machine
Is a record to machine.athome.tld.
However,
@ 1W IN NS machine.other.tld.
Is not completed.
CNAME ยง
CNAME, means Canonical NAME, it is an alias.
In the following example, the real name of www is master.athome.tld.
www IN CNAME master
It is used for virtual hosts such as blog.athome.tld, webmail.athome.tld, ...
NS ยง
A DNS zone should have at least 2 NS records since they describe where are the authoritative servers. Actually, the zone will work with only one as long as the domain name server doesn't encounter any problem.
There is no NS servers maximum limit in theory. If you got two, that's a good start : yours and a friend's server.
athome.tld. IN NS master.athome.tld.
This record means : for zone athome.tld, authoritative name server (NS) is master.athome.tld. It also can be written this way :
@ IN NS master
In this case, the "@" is replaced by "$ORIGIN", actually its full name to the root, and master hasn't an ending dot so it is expanded with $ORIGIN too : "master.athome.tld".
Name servers must be known to the rest of the world, you have to register then in the registrar, in the tld zone. This is actually one of the two only records you have to do in your registrar panel with DNSSEC keys records. Later, once authoritative server are published, everythings happens on your server.
When recording NS on the registrar, you generally give two fields : the full hostname of the server, master.athome.tld here, and its addresses. In this case, we talk about "Glue Record". How to know master.athome.tld even though it's it who knows addresses for zone athome.tld? In this specific case, the address is written in the registrar.
To record authoritative servers on the registrar, you have to log on your registrar panel
You'll find a panel "GLUE" in your domain setup.
Once the new GLUE recorded, you can modify the name server list for your domain.
Read more about registrars and self-hosting with this article.
MX ยง
MX, a little like NS, tells where is a kind of service for the zone. In this case, where mail should be delivered. The record is build the same way as before :
@ IN MX 10 mail1 @ IN MX 20 mail2
For the zone athome.tld, the mails server (MX) is mail1.athome.tld.
The only significant differences is the "10", indicating the priority or "weight". When you have multiple servers, which one has priority to receive the mail? The one with the lowest weight.
If you self-host, you can choose to use the main domain name instead of a subdomain with the same address in the A record, it is still valid:
@ IN MX 10 athome.tld.
MX and NS can't be redirections (CNAME), they must point to A or AAAA records, or in last resort to IP.
TXT ยง
TXT records are used to publish various data about your server. It is useful to share public keys as example. It looks like this :
@ IN TXT "v=spf1 a mx ~all"
Ending words ยง
That's it for this long part about DNS. Do not hesitate to read again later, there are many things to understand here.
Of course, you can do so much more with DNS (SRV, SSHFP, ...), we'll describe eache case later if necessary.
Validating DNS resolver : unwind ยง
Unwind is available dy default on OpenBSD. It resolve domains (DNS) on your own device instead of asking your ISP or external provider. Results are kept in cache. This increase your server's performances and I strongly suggest to enable it ๐.
Notice unwind only work locally and can't do resolution for other devices. To provide such feature, look at unbound.
To use unwind, enable is as usual :
# rcctl enable unwind # rcctl start unwind
Edit "/etc/resolv.conf" file so your server asks unwind to resolve domain names. This is not necessary if you use DHCP.
nameserver 127.0.0.1
Here you go, your server now resolve domain names on its own.
You can try how well unwind works using dig command to see DNS requests results:
$ dig si3t.ch [...] ;; Query time: 61 msec
61 ms were necessary to get an answer. Now try again :
$ dig si3t.ch [...] ;; Query time: 0 msec
Yay, the address in in cache, speeding up future requests until TTL.
If you want to go further, read :
Authoritative name server : nsd ยง
You absolutely can host the authoritative server for your own zone, one step further to independence ๐. That is exactly the purpose of nsd available in OpenBSD base.
Configuring nsd ยง
To configure nsd, edit "/var/nsd/etc/nsd.conf".
server: hide-version: yes zonesdir: "/var/nsd/zones" ip-address: 192.168.1.2 ip-address: 2001:db8:1:1::2 debug-mode: no verbosity: 1 # master zones zone: name: "athome.tld" zonefile: "master/athome.tld"
We will follow this structure: zones are categorized wether the server is authoritative (master) or secondary (slave). Each zone is a file with the name of the corresponding domain. As example "/var/nsd/zones/master/athome.tld". Or course you can choose to do things differently if you prefer.
Do not forget to adjust the above example with your own IP.
Once setup, start nsd as usual :
rcctl enable nsd rcctl start nsd
Remember to open (and redirect) port 53 (domain) UDP and TCP since it's used by nsd.
Once your nsd is up, you can set in the registrar your server's public ip in the list of authoritative servers for your zone.
An nsd example is available at the end of this documentation.
A few notes about DNS zone signing ยง
DNSSEC requires two kind of keys : ZSK (Zone Signing Key) light and short time living, and KSK (Key Signing Key) bigger with a long time usage.
Why such differences ?
It is recommended to renew KSK regularly -- about monthly, some do it weekly. However, it is quite annoying to update this monthly in the registrar.
That's why we created another kind of key to sign the firsts, but used only for this task. Those should be stronger, heavier since they are less used with a longer lifetime. Those will be recorded in the registrar.
Actually, we won't record KSK in the registrar but a checksum instead in the upper zone. It's the same idea with each zone publishing their NS in the upper zone.
Keep in mind changes won't propagate instantly. There is TTL, lifetime and validity of keys and signatures, so you can't expect to see things working right away.
Especially, you must publish in advance keys that will be used in the near future. That's why many administrators have 2 ZSK in the meantime: one currently used and another that will be used when the first has expired. That's what we'll do below. KSK are also published early, however rarely two in the same time.
Setup DNSSEC with ldnscripts ยง
Signing your DNS zone ensure its integrity.
You have to do it periodically since signature have an expiration date. You'll need a tool to sign and a bit of automation.
We will describe how to use ldnscripts designed for this task (thanks to 22decembre).
Note there are tools more complete/complicated such as OpenDNSSEC or KNOT.
With ldnscripts, you'll only need the package "ldns-utils" :
# pkg_add ldns-utils
Automation relay on tools already available on OpenBSD, I mean scripts /etc/weekly, /etc/monthly...
Let's follow ldnscripts' author's intructions :
$ cd /tmp $ ftp https://framagit.org/22decembre/ldnscripts/-/archive/master/ldnscripts-master.tar.gz $ tar xvzf ldnscripts-master.tar.gz $ cd ldnscripts-master* # make install
Configure ldnscripts in the file "/etc/ns/ldnscripts.conf". There is an example in the previous archive. Configuration should look like this :
# repository where to find unsigned zone file and specific conf NS_REP=/etc/ns # serial : unixtime SERIAL=unixtime # algorithm to use. They are listed : ldns-keygen -a list ALG=ECDSAP384SHA384 # length of the zsk ZSK_BITS=1024 KSK_BITS=2048 # validity of signatures in days VALIDITY=9 #NSEC3 NSEC3_ALG=SHA-384 RUN=24 # Verbose - set to 1 if needed VERBOSE=1
- NS_REP is the repository where lies zones to sign. Each file should be named according to the domain you want to serve the zone. As example "/etc/ns/athome.tld". You must create this file.
- SERIAL: Serial number format. It can be "unixtime" or "date".
- ALG: Algorithm used for keys. Check what your registrar can support.
- ZSK_BITS et KSK_BITS: Size of keys.
- VALIDITY: Number of days while a signature is valid. Beware, this duration should be at least as long as the delay before you renew signatures (see "sign" command later).
- NSEC3_ALG and RUN: Algorithm used for signatures and how many rolls are required to generate the signature.
- VERBOSE: Well, 1 means you'll see more outputs.
If you want, you can set a configuration file per domain: they should be named "/etc/ns/domain-name.tld.conf".
That's it for configuration ๐
To actually start using ldnscript, run with "init" parameter to create everything required: first keys, directories...
# ldnscript init athome.tld
You can read such output (below, VERBOSE is enabled):
This script will initialize your zone athome.tld with the general configuration or the one set at /etc/ns/athome.tld.conf. If you are not happy with it, modify your configuration (by copying the conf file to /etc/ns/athome.tld.conf and then editing it) and run this script again. The script will create one KSK and two ZSK and finally sign the zone (which will triger a reload of your nsd server on the athome.tld zone). The key Kathome.tld.+010+25115 has been generated with these arguments : -a RSASHA512 -b 1024 athome.tld The key Kathome.tld.+010+34655 has been generated with these arguments : -a RSASHA512 -b 1024 athome.tld The key Kathome.tld.+010+12321 has been generated with these arguments : -k -a RSASHA512 -b 2048 athome.tld A new KSK key has been generated. Make sure to update the DS record in your registrar quickly. The key is Kathome.tld.+010+12321 DS content : athome.tld. IN DS 12321 10 2 f6f91afd522680a3c459e1956e75f8eda078f99b8cf07114f0d299161bff0145 create a complete zone file for athome.tld with the current time as SOA Signing zone Zone is verified and complete
A directory "/var/ldnscripts/athome.tld/" is created, containing ZSK and KSK.
Signed zone file is in /var/nsd/zones/signed/athome.tld. Adjust nsd configuration to serve it :
server: zonesdir: "/var/nsd/zones" ... # master zones zone: name: "athome.tld" zonefile: "signed/athome.tld"
Notice nsd is a static server, that's why ldnscript will reload it's configuration.
Warning, you must publish in your registrar public keys you can find in "/var/ldnscript/athome.tld/ksk" with ".key" extension. Depending on your registrar, you may have to publish DS records in ".ds" files (not at GANDI).
It's not over yet, we will set up a signing process with key renewal when necessary. Everything is ready, don't worry ๐.
ldnscript offers those actions :
- "sign": Sign the zone for VALIDITY duration. You have to run this action just before the and of validity of the previous signature, and each time you modify the zone or renew a key.
- "rollover": to renew keys. There will be 3 keys to anticipate the publication of keys that will be used later: a retired key, the current key and the next one. This action manage deletion of obsolete keys. You have to run this monthly.
- "zsk": to create new ZSK manually. They will be enabled and used at the next rollover. At each rollover, a new ZSK is created using this action.
- "ksk": create a KSK.
Let's do this. First, edit "/etc/monthly.local" to add the rollover :
/usr/local/sbin/ldnscript rollover all
Then, make sure signatures are renewed before the end of VALIDITY parameter in configuration file. Above, we set 9 days so you can sign each week 2 day early. Edit "/etc/weekly.local" :
/usr/local/sbin/ldnscript sign all
Finally, every year, you will create a new KSK :
/usr/local/sbin/ldnscript ksk athome.tld
To remember, you can add a new crontab to send you a message every May the 2nd (someone's birthday ๐):
# crontab -e 0 0 2 5 * echo 'renew ksk' | mail -s "KSK ALERT" root
Remember to publish this new key in the registrar. The script rollover will delete the old key automatically. At this point, you may remove its record in the registrar.
That's it! Everything else is handled by ldnscript, it requires some efforts only the first time.
Notice in the example, you deal with multiples zones automatically.
Add or remove keys in the registrar ยง
When you renew KSKs, you must add in your registrar the public key. Make sure to do it early enough si it is correctly propagated before the old one is dismissed.
Ldnscript will display the number of the new key (keytag) and its checksum (DS) useful to record it in the registrar.
The public key is located at "/var/ldnscripts/zones/athome.tld/ksk/Kathome.tld*.key".
Once a key is dismissed, you can delete the previous DS record.
Add a secondary DNS server ยง
Sometimes called "slave", a secondary server (NS) can serve your zone in case the primary (master) is unreachable for reasons : network unreachable, nsd daemon crashed, meteor shower, frog invasion or worse : pancakes shortage.
You could ask a friend with a domain name server (not necessarily OpenBSD nor nsd either, even if it's what we describe here) to host your zone as a slave on your domain. You can even install a secondary server on your own -- on a VM rented at openbsd.amsterdam as example ๐. It's important that the secondary server is in another network, and even the same area : a few hundreds of kilometers are a good plan to mitigate power outage or network issues ๐.
Note you can absolutely set up secondary servers for your friend's domain, being mutually the slave of the other's domain name server.
We will describe both side of the picture. You could be administrator of master server, secondary server or both at the same time.
First, we prepare a little bit of authentication.
Yes, I know, it's hard work. But we want to do things right ๐? Here, we ensure our zone is updated by a legitimate server.
We will create a shared secret key, identical on both primary and secondary.
WIP \_o>
We use ldns-keygen command available in ldns-utils package you already have installed if you followed the previous part. It will create a keypair with a "secret" code inside :
$ cd /tmp $ ldns-keygen -a hmac-sha256 -b 160 athome.tld
You have two new files, display the content of the private key :
$ cat Kathome.tld.+159+54791.private Private-key-format: v1.2 Algorithm: 159 (HMAC_SHA256) Key: H8D/Ka9RerEtmC0jN5hSQeATxNI=
Copy "Key" string in nsd configuration for master and slave servers in a "secret:" parameter. Remember to adjust with the proper algorithm:
key: name: "transfer" algorithm: hmac-sha256 secret: "H8D/Ka9RerEtmC0jN5hSQeATxNI="
The "name:" parameter -- "transfer" here -- doesn't matter. It's just a mark.
You can delete previously created files in /tmp now.
On the master server:
In the configuration of the domain name server, you add two lines to alert the slave server so it retrieve the zone. For this purpose, you add instructions "notify" and "provide-xfr" :
# master zone zone: name: "athome.tld" zonefile: "signed/athome.tld" notify: 192.0.2.1 transfer provide-xfr: 192.0.2.1 transfer key: name: "transfer" algorithm: hmac-sha256 secret: "H8D/Ka9RerEtmC0jN5hSQeATxNI="
Everytime you update the zone on your primary domain name server, it will alert the server on the address 192.0.2.1 so the latter update the zone.
Many online services -- such as registrars -- offer to host your zone as slaves for free. In this case, it is not necessary to notify : servers get the zone on schedule. Keep the "provide" without authentication : "NOKEY" in place of the key name.
To see how to use secondary server of GANDI, follow this link.
You can find GANDI's slave server :
$ dig ns6.gandi.net +short 217.70.177.40
Now add in your zone and in the registrar the secondary servers for your zone (NS) :
$ORIGIN athome.tld. $TTL 86400 @ IN SOA master.athome.tld. hostmaster.athome.tld. ( 2014110502 ; 86400 ; refresh 7200 ; retry 3600000 ; expire 172800 ) ; negative @ IN NS master.athome.tld. @ IN NS secondairy.athome.tld. @ IN NS other.domain.tld. ; a friend is ; helping maitre IN A 192.0.2.2 maitre IN AAAA 2001:db8:1:1::2 secondaire IN A 192.0.2.3
On the secondary server now:
Just add a bit of configuration for nsd:
# slave zone zone: name: "athome.tld" zonefile: "slave/athome.tld" allow-notify: 192.0.2.2 transfer request-xfr: 192.0.2.2 transfer key: name: "transfer" algorithm: hmac-sha256 secret: "H8D/Ka9RerEtmC0jN5hSQeATxNI="
Notice both transfer keys share exactly the same configuration on each server.
Zones files should stay untouched on the secondary server. They will be updated automatically when needed. Edit the nsd configuration if you need to change their location : zones will be fetched again.
You can check if zones are in sync with "dig":
$ dig -q @master.athome.tld athome.tld $ 192.0.2.10 โฆ $ dig -q @secondairy.athome.tld athome.tld $ 192.0.2.10
Make sure you added secondary servers in the name servers lists in your registrar.
Check your zone works as expected ยง
You may use the following links to check if your zone is correctly propagating over the world, has no errors and if DNSSEC is correct :
Complete DNSSEC checks with diagrams
Complete and free example with nic.eu.org ยง
nic.eu.org offer domain names ending with "eu.org".
We'll see how to set up a zone with this registrar.
First, check availables domains open for registration then choose one you like.
For the example, we'll use "athome.tld.ca.eu.org"
Create the domain zone. Since we'll use "ldnscripts" later to enable DNSSEC, we write it in "/etc/ns/athome.tld.ca.eu.org" :
$TTL 1D $ORIGIN athome.tld.ca.eu.org. @ IN SOA ns1.athome.tld.ca.eu.org. batman.athome.tld. ( 2017111301 1D 2H 2W 2D ) @ IN NS ns1.athome.tld.ca.eu.org. @ IN A 192.0.2.2 @ IN AAAA 2001:db8:1:1::2 ns1 IN A 192.0.2.2 ns1 IN AAAA 2001:db8:1:1::2 ns2 IN A 192.0.2.3
This is a pretty simple zone with two name servers, "ns1" and "ns2", the latter only available on IPV4.
We add a new section in nsd for "nsd1" :
# cat /var/nsd/etc/nsd.conf key: name: "transfert" algorithm: hmac-sha256 secret: "Hsd/Ka9RerEtmC0jsd5d5eATxNI=" zone: name: "athome.tld.ca.eu.org" zonefile: "signed/athome.tld.ca.eu.org" provide-xca: 192.0.2.3 transfert notify: 192.0.2.3 transfert
Do the same on secondary server "ns2" :
# cat /var/nsd/etc/nsd.conf key: name: "transfert" algorithm: hmac-sha256 secret: "Hsd/Ka9RerEtmC0jsd5d5eATxNI=" zone: name: "athome.tld.ca.eu.org" zonefile: "slave/athome.tld.ca.eu.org" allow-notify: 192.0.2.3 transfert request-xca: 192.0.2.3 transfert
reload nsd :
# rcctl reload nsd
Enable the zone with ldnscripts and prepare for DNSSEC :
# ldnscript init athome.tld.ca.eu.org
That's ready. Now it is served, you can register the domain.
Create an annount on nic.eu.org and connect.
Choose to create a New Domain.
Fill the fields with the full domain name you want to register and data about yourself.
Then, fill "Name server" section. You must link the domain name and NS servers for the zone. The zone should already be managed by the servers. You can use IPV4 and IPV6 both. In other words, fill with the "NS" records in your zone.
At last, after validating, you may read something like that if everything works as expected :
---- Servers and domain names check Accepted IP for NS1.ATHOME.TLD.CA.EU.ORG: 2001:db8:1:1::2 192.0.2.2 Accepted IP for NS2.ATHOME.TLD.CA.EU.ORG: 192.0.2.3 ---- Checking SOA records for athome.tld.ca.eu.org SOA caom NS1.ATHOME.TLD.CA.EU.ORG at 2001:db8:1:1::2: serial 2019100702 (21.005 ms) SOA caom NS1.ATHOME.TLD.CA.EU.ORG at 192.0.2.2: serial 2019100702 (6.006 ms) SOA caom NS2.ATHOME.TLD.CA.EU.ORG at 192.0.2.3: serial 2019100702 (73.715 ms) ---- Checking NS records for athome.tld.ca.eu.org NS caom NS1.ATHOME.TLD.CA.EU.ORG at 2001:db8:1:1::2: ok (20.674 ms) NS caom NS1.ATHOME.TLD.CA.EU.ORG at 192.0.2.2: ok (5.953 ms) NS caom NS2.ATHOME.TLD.CA.EU.ORG at 192.0.2.3: ok (65.559 ms) No error, storing for validation... Saved as request 20191007195509-arf-42318 Done
Check your emails. You soon will get a configmation message about your registration.
Once it's done, you can enable DNSSEC in the dedicated panel.
Copy the DS record in nic.eu.org field :
cat /var/ldnscript/athome.tld.ca.eu.org/ds
Now you can check dnssec is correctly enabled.
Virtualization ยง
It is realy easy to virtualize an operating system with OpenBSD thanks to vmd.
Well, that's nice, but what virtualizing means ?
Instead of installing different OS on different computers, you can install an OS inside your current OS. The virtualizer fake to be a full device and run an OS installed on a disk, which is in this case a simple file.
It is very handy since :
- You can use multiple system simultaneously.
- Each system is independent. If one is compromised, others should be safe. It's better than a chroot.
- Backup is really easy: just copy the disk file.
- It's great to try things.
However, it require more resources.
Before going further, make sure we use the same vocabulary :
- "host" is your server. The host virtualize other systems.
- "client" are hosted systems.
OpenBSD offer 3 tools for virtualization :
- vmd : a daemon to manage multiple clients.
- vmctl : To control clients and sending commands to vmd.
- vmm : To watch over clients.
First of all, check if your hardware can virtualize :
$ dmesg | egrep '(VMX/EPT|SVM/RVI)'
If the result isn't empty, it's all good ๐.
Don't forget to upgrade firmwares if needed : "# fw_update".
To add a new virtual machine, you will always :
- 1. Install in a new disk file with vmctl.
- 2. Configure vmd to manage this client automatically.
How to virtualize OpenBSD ? ยง
Actually, everything is already well documented on OpenBSD's website. This chapter will just focus on a few tips.
You should definitely read OpenBSD's FAQ on this topic ๐.
Use other installation media ยง
To use img disk (installXX.img or minirootXX.img) :
# vmctl start -c -m 1G -L -i 1 -d installXX.img -d /var/vm/obsdvm.qcow2 openbsdvm
Or if you don't want to download any file, you still can use the bsd.rd you probably already have :
# vmctl start -c -m 1G -L -i 1 -b /bsd.rd -d /var/vm/obsdvm.qcow2 openbsdvm
However, such install requires to previously configure a network access for clients.
In /etc/pf.conf :
# using quad9 DNS pass in quick proto { tcp udp } from 100.64.0.0/10 to any port domain \ rdr-to 9.9.9.9 port domain match out on egress from 100.64.0.0/10 to any nat-to (egress)
Even better, you can use unwind if it is already configured on host, which is an excellent idea ๐ :
pass in proto { tcp udp } from 100.64.0.0/10 to any port domain \ rdr-to localhost port domain match out on egress from 100.64.0.0/10 to any nat-to (egress)
In "/etc/sysctl.conf" :
net.inet.ip.forwarding=1 net.inet6.ip6.forwarding=1
Virtualize debian ยง
Like most linux distro, you'll have to set the installer and bootloader to start the client with a serial console at speed 15200.
# download debian image ftp "https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-10.5.0-amd64-netinst.iso" # create disk vmctl create -s 50G /var/vm/debian.qcow2 # start the VM vmctl start -c -m 1G -L -i 1 -r debian*.iso -d /var/vm/debian.qcow2 debianvm
Choose install menu without validating โ .
Press TAB then edit the line to change vga and console parameters :
/install.amd/vmlinuz vga=off initrd=/install.amd/initrd.gz --- quiet console=ttyS0,115200n8
Press Enter.
After installing and rebooting on the fresh debian install, edit "/etc/default/grub" so serial console is still used.
GRUB_TIMEOUT=1 GRUB_CMDLINE_LINUX_DEFAULT="" GRUB_CMDLINE_LINUX="console=tty1 console=ttyS0,115200" GRUB_TERMINAL="console serial" GRUB_SERIAL_COMMAND="serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1"
Then reload grub:
# update-grub
Virtualize Alpine Linux ยง
Alpine Linux is a very light distro.
Here again, you need to boot using serial console.
# vmctl create -s 50G /var/vm/linux.qcow2 # vmctl start -c -m 1G -L -i 1 -r image.iso -d /var/vm/linux.qcow2 linux
When starting virual machine, press "TAB" to see available image (i.e. "lts" or "virt"). Add appropriate parameter to use serial console :
virt console=ttyS0,115200
Then press Enter and it's all good.
What is a VPN? ยง
VPN stands for "virtual private network". As the "private" suggests, the network created is "hidden" from the great Internet.
Most of the time, VPN are used to bypass ISP restrictions, secure a public WiFi access or hide from a government monitoring.
There are various VPN tools, each offering pros and cons.
Here, we'll discuss Wireguard, OpenIKED and a bit of SSH. The two lasts are already very well documented in OpenBSD documentation. They all are available in base install.
In the following pages, we'll use as designations :
- Your computer or device using the VPN : client
- your server : server (What a surprise! ๐).
We'll configure "roadwarriors".
roadwarrior... what does that mean ?
Roadwarrior describe a setup allowing a client from any origin to reach the Internet through the VPN. Thus, the VPN servers appears to be the actual identity of the client, hiding it in the process.
A bit further, we'll describe how to set up your server so you get a fixed IP provided by the VPN. At this point, no more roadwarrior is mentioned and VPN configuration is simpler, but we'll discuss firewall configuration to redirect requests. It becomes useful if your ISP doesn't provide static IP or if your server is travelling for some reasons.
Wireguard ยง
Wireguard is probably the easiest VPN to set up while one of the most secure since it requires only recent encryption methods. For clients, it is handy since it stay up even if the device's IP changes -- switching from wireless to wired as example. IPv4 and IPv6 are supported so you can get an IPV6 if your ISP doesn't offer one. Wireguard is supported natively since OpenBSD 6.8.
Many software for various platforms are availables (android, MacOS...) so it's even more handy.
It might be the best choice if you want to self-host a VPN.
If you want to learn how Wireguard works, look at the whitepaper.
Setting up an exit point ("roadwarrior") ยง
Below we describe the following structure:
- The server is an exit point for clients. Clients will appear with server's public IP on the Internet.
- Clients traffic goes through route 0 to get to the server (rdomain 0).
- Clients still can use their usual Internet access through route 1 -- rdomain 1 -- but route 0 is preffered.
Understand that route's changes are for OpenBSD's clients and won't be a question with other OS clients.
In this example, IPs inside the VPN are in 10.0.0.0/24 subnet :
- 10.0.0.1 : server's IP
- 10.0.0.2, 10.0.0.3, ... clients IPs.
Here's how it will look like :
+-------------+ | server | wg0: 10.0.0.1 port 4545 | |---------------+ +-------------+ | | Public IP | | 192.0.2.2 | | | | | /##########\ |WireGuard | Internet | |VPN \############/ | | | | | |rdomain 1 | +-------------+ | | client |---------------+ +-------------+ wg0: 10.0.0.2 rdomain 0 (default)
By default, traffic goes through 10.0.0.2 unless you explicitly ask to use another route (ie : "route -T1 exec ping openbsd.org")
VPN is set up by creating wgN interfaces, where "N" is a number from 0 to 9 as example. Such interface is created by filling a file "/etc/hostname.wgN".
Server will listen on port 4545 UDP. Any other port can be used, just check it isn't already reserved in "/etc/services".
Create keys for Wireguard ยง
Use the following to create a key :
openssl rand -base64 32
It returns as example : "uA1ggvRv66QslZ0ygorWvcLVTxzFauffqMigXTrmLVY="
Once an interface receive a private key, you can retrieve the associated public key with ifconfig :
# ifconfig wgN
Create a key on the server:
# openssl rand -base64 32 r8uSGD6vyycE5n5/atU9/NX9JQPo4SJryNGpjbQG+rA=
Create wg0 interface with the previous private key :
# ifconfig wg0 create wgkey r8uSGD6vyycE5n5/atU9/NX9JQPo4SJryNGpjbQG+rA= wgport 4545
Now get the matching public key :
# ifconfig wg0 wg0: flags=8082<BROADCAST,NOARP,MULTICAST> mtu 1420 index 5 priority 0 llprio 3 wgport 4545 wgpubkey x9VXlh4AMa2YRjTMRVE39pQRsFHRJHUYrATL6vkqFmU= groups: wg
The line starting with "wgpubkey" indicates the interface's public key. You'll need it for clients so they can authentify the server, so write it down.
Of course, take note of server's private key: we'll write it later in a file to automate interface creation.
Now on a client, we do pretty much the same: create a private key and set up a wg interface without specifying a port:
# openssl rand -base64 32 q/7uIx6wBIRUIdxOi5D6OWEQRVUt2AXhMj7j29W/s3s= # ifconfig wg0 create wgkey q/7uIx6wBIRUIdxOi5D6OWEQRVUt2AXhMj7j29W/s3s= # ifconfig wg0 |grep wgpubkey wgpubkey V3pCAhxnRl0QEL8luB9D4EvTVxGT7QGDDCZ3O26kY3A=
Once again, write down keys.
Redirecting traffic through server ยง
Here, we want the server to be a sort of relay between the client and the rest of the world.
You have to redirect IP forwarding on server :
# sysctl net.inet.ip.forwarding=1
Add the following line in "/etc/sysctl.conf" so changes are applied at reboot:
net.inet.ip.forwarding=1
For IPv6 :
net.inet6.ip6.forwarding=1
Then, add a nat rule in "/etc/pf.conf" :
# open 4545 UDP pass in proto udp to port 4545 # open wg0 interface for incoming pass in on wg0 # What's from VPN (wg0) is NATted to public servers iface pass out quick on egress from (wg0:network) to any nat-to (egress)
Reload pf ๐
Dig the Wireguard tunnel ยง
Now we've got all the material to authenticate clients and server, we can dig up the VPN. To do so, we'll indicate public keys on each devices and which IP are allowed to use the tunnel.
To make things easier, we'll edit "/etc/hostname.wg0" files now. When rebooting, our configuration will remain.
Pay attention to keys, they are the same we got above. ๐
On the server :
"/etc/hostname.wg0" :
inet 10.0.0.1/24 wgkey r8uSGD6vyycE5n5/atU9/NX9JQPo4SJryNGpjbQG+rA= wgport 4545 wgpeer V3pCAhxnRl0QEL8luB9D4EvTVxGT7QGDDCZ3O26kY3A= wgaip 10.0.0.2/32 up
- inet: wg0's IP.
- wgkey: server's private key to decipher traffic. It MUST remain secret.
- wgport: listening port.
- wgpeer: client public key to encrypt traffic to client.
- wgaip: Allowed IP for the client. Notice the "/32" to be able to add more IP later.
- up: enable wg0 at startup.
You can add as many "wgpeer" line as you want. However, each client must have its own IP :
wgpeer V3pCAhxnRl0QEL8luB9D4EvTVxGT7QGDDCZ3O26kY3A= wgaip 10.0.0.2/32 wgpeer m7K/gfmMPYRJx1IOP01zYrNbEuMnnZ29xN4OBgRoRXo= wgaip 10.0.0.3/32 wgpeer qnuq5MgezCDHXsYYGmrcegPCNcJvz9EOIG3XyHp1DBk= wgaip 10.0.0.4/32
On client :
Here you must specify where the client can find the server ("wgendpoint") and set default routes so traffic goes through the tunnel.
wgkey q/7uIx6wBIRUIdxOi5D6OWEQRVUt2AXhMj7j29W/s3s= wgpeer x9VXlh4AMa2YRjTMRVE39pQRsFHRJHUYrATL6vkqFmU= wgendpoint athome.tld 4545 wgaip 0.0.0.0/0 inet 10.0.0.2/24 wgrtable 1 !route add -net default 10.0.0.1 up
- wgkey: client private key
- wgpeer: server public key.
- wgendpoint: server's public IP or domain name. If you choose the domain name, understand your device must be able to do domain name resolution. In doubt, I recommend to use the IP.
- wgaip: With "0.0.0.0/0" value, we allow to send packets to any IP.
- inet: client's IP in VPN.
- wgrtable: VPN is on route table 1.
- "!route add -net default 10.0.0.1": We set default route to the exit point of the tunnel, which is server IP.
Dig the tunnel on client and server with :
# sh /etc/netstart wg0
Edit client's interface used to reach the internet so it uses routing table nยฐ1. As example in "/etc/hostname.em0":
autoconf rdomain 1 up
You can now check client's IP : it's the same as the server's.
Configure clients using other OSes ยง
Below is the minimal requirements to set up a client :
Interface :
- Generate key pairs. Copy public key in "/etc/hostname.wg0" on server.
- Adresses : 10.0.0.5/24 as example.
Peer :
- Public key of server
- allowed IP : 0.0.0.0/0
- Ending point : 192.0.2.2:4545 (server-ip:port)
Ressources ยง
This page is heavily inspired from :
- https://xosc.org/wireguard.html
- https://lipidity.com/openbsd/wireguard/
- https://man.openbsd.org/wg
- https://codimd.laas.fr/s/NMc3qt5PQ
Thank you very much to Solรจne Rapenne who had the brilliant idea to use "rdomain".
VPN with OpenIKED ยง
OpenIKED is a free implementation of IKEv2 protocol.
As it is already very well documented, and istead of duplicating good documentation, I suggest you to read OpenBSD's official documentation about IKEv2:
Get a static IP thanks to a VPN ยง
If your ISP don't offer static IP, then you could set up a VPN to use endpoint's static IP.
It also gets handy if :
- Your ISP give you an IP with a bad reputation and your mails get always in spams folders.
- You don't trust your ISP and want to hide traffic to your server.
Here is a scheme of what we're going to achieve :
+--~sniper~-+ +-------~tank~--------+ +--------------+ | | | | | | | Your own +<------>+ Endpoint Server +<-----+ Visitor | | Server | VPN | with static IP | | | | | | | | | +-----------+ +---------------------+ +--------------+ hidden IP Public IP
To reach your server, one will use Endpoint Public IP.
To avoid confusion, I'll refer your server with "sniper" and the VPN endpoint with "tank".
Warning for pf ยง
โ Remember to adjust your pf.conf. Now, incoming traffic is on VPN interface, i.e. "wg0" if you use Wireguard or "enc0" with IKED.
The easiest is maybe to replace "egress" by a macro with a list of all interfaces you want to handle. As example :
pass in quick on egress proto tcp to port $tcp_pass
become
ifaces = "{ em0 wg0 }" # ifaces = "{ egress wg0 }" # alternative pass in quick on $ifaces proto tcp to port $tcp_pass
Prerequisite : simple VPN tunnel ยง
Tank don't have to do nat (yet), and you don't need to change sniper's default routes.
For a Wireguard tunnel, here is a short summary of what you may have configured :
On tank :
# cat /etc/hostname.wg0 inet 10.0.0.1/24 wgkey [..snip..] wgport 4545 wgpeer [...snip...] wgaip 10.0.0.2/32 up # cat /etc/sysctl.conf net.inet.ip.forwarding=1 net.inet6.ip6.forwarding=1 # cat /etc/pf.conf [...] # wireguard tunnel pass in proto udp to port 4545 # no nat-to [...]
On sniper :
# cat /etc/hostname.wg0 wgkey [..snip..] wgpeer [..snip..] wgendpoint chezmoi.tld 4545 wgaip 0.0.0.0/0 inet 10.0.0.2/24 up # cat /etc/pf.conf [...] # paranoid commented below # pass in on wg0 from 10.0.0.1 # pass out on wg0 # less paranoid: pass on wg0 [...]
Setup a new IP ยง
From now on, we suppose there is a tunnel between sniper and tank.
- sniper's IP is IP 10.0.0.2 in VPN.
- tank's IP is 10.0.0.1 in VPN (anyway) and its public IP is 192.0.2.2.
Now edit pf.conf on tank to add "nat-to" and "rdr-to" rules :
serv_int = "10.0.0.2" serv_ext = "217.69.0.97" int_if = "wg0" ext_if = "egress" # change-me maybe set skip on lo block # let vpn on pass in proto udp to port 4545 pass in on wg0 ### REDIRECT TO SNIPER pass in on $ext_if proto tcp from any to $serv_ext \ rdr-to $serv_int match out on $ext_if from $serv_int to any \ nat-to $serv_ext static-port # One can replace the two previous rules with : # match on $ext_if from $serv_int to any binat-to $serv_ext match out on $int_if from any to $serv_int \ received-on $ext_if nat-to $int_if ### pass out
Make sure to edit "serv_*" and maybe "$ext_if", the public interface.
The "rdr-to" rule link what's coming from anywhere ("any") on the public interface "$ext_if" to sniper ("$serv_int").
Then, the "nat-to" does the same the other way. We use a "match" so the state remains until "pass out".
It is equivalent to a "binat-to" rule, but it won't let you specify ports numbers (see later).
In the end, another "nat-to" is necessary since "$ext_if" and "$int_if" are different.
Reload pf and you're done ๐.
Some may want to fine tune their firewall and redirect only a few port numbers :
ports_tcp = "{ ssh www https smtp submission imaps domain }" ports_udp = "{ domain spamd-sync }" # pass in on $ext_if from any to $serv_ext rdr-to $serv_int # previous line is replaced by pass in on $ext_if proto tcp from any to $serv_ext port $ports_tcp rdr-to $serv_int pass in on $ext_if proto udp from any to $serv_ext port $ports_udp rdr-to $serv_int [...]
Warning about SSH and host ยง
If you access your server with SSH, understand the above rules will redirect you to you client automatically. You have to set up an alternative SSH port on the VPN endpoint and configure pf.conf to avoir redirecting yourself throught the VPN when you want to administer the host.
Edit "/etc/ssh/sshd_config" and set port to i.e. 2222 and add in "/etc/pf.conf" BEFORE VPN redirection. Something like :
pass in quick on egress proto tcp port 2222 # alternative ssh port
Edit DNS ยง
Now, in your DNS zone, your domain name should point to tank's IP.
Get an IPv6 thanks to a VPN ยง
Despite the year we live on, some ISP still don't provide IPv6 connectivity. You still can get an IPv6 thanks to a VPN if the exit point is IPv6 ready. As example, there are openbsd.amsterdam or vultr amongst others.
We will set up a wireguard tunnel as before and add IPv6 conntectivity to the client.
Prerequisite to get an IPv6 ยง
- You need a remote server with IPv6 connectivity. A VM as example.
- A private IPv6 subnet. In this example, we will use fd9c:f774:0bfa:acfc. It could be the simpler fd42::1, fd42::2, ... It would work, but it is not very rigorous.
This website can help to generate a private IPv6 range
On the remote server ยง
Remember to enable ip forwarding in "/etc/sysctl.conf"
net.inet.ip.forwarding=1 net.inet6.ip6.forwarding=1
In file "/etc/hostname.wg0" to configure wireguard interface, you must specify IPv6 of VPN exit point. Here, it's "fd9c:f774:0bfa:acfc::1/64".
Each client should be able to get its own IPv6. We add a new option "wgaip" after the previous one. The configuration will look like this :
# cat /etc/hostname.wg0 inet 10.0.0.1/24 inet6 fd9c:f774:0bfa:acfc::1/64 wgkey [...snip...] wgport 4545 # peer 1 wgpeer [...snip...] wgaip 10.0.0.2/32 wgaip fd9c:f774:0bfa:acfc::2/128 # peer 2 wgpeer [...snip...] wgaip 10.0.0.3/32 wgaip fd9c:f774:0bfa:acfc::3/128 # peer 3 wgpeer [...snip...] wgaip 10.0.0.4/32 wgaip fd9c:f774:0bfa:acfc::4/128 up
โ Each client's IPv6 MUSt end with "/128".
On the client wanting an IPv6 ยง
In "/etc/hostname.wg0", you have to add a few things:
- In "wgpeer", you MUST add "wgaip ::0/0" to let IPv6 access.
- Set interface IPv6 in the VPN with an "inet6" instruction.
- Add a new route so IPv6 traffic use VPN endpoint as exit.
Now, Wireguard interface on the client look like this :
# cat /etc/hostname.wg0 wgkey [...snip...] wgpeer [...snip...] \ wgendpoint <XX.XX.XX.XX> 4545 \ wgaip 0.0.0.0/0 \ # <--- ! wgaip ::0/0 \ wgpka 25 inet 10.0.0.3/24 inet6 fd9c:f774:0bfa:acfc::3/64 # <--- ! wgrtable 1 up !route add -inet default 10.0.0.1 !route add -inet6 default fd9c:f774:0bfa:acfc::1 # <--- !
Here you go, now you have an IPv6 on the Internet.
You can see it with the following command:
curl -6 https://ifconfig.co
Various ยง
Gemini (vger) ยง
Gemini is a new internet protocol which is heavier than gopher, is lighter than the web, will not replace either, strives for maximum power to weight ratio, takes user privacy very seriously.
It is quite an amazing protocol to post your writings and focus first on content.
Actually, this current documentation is also available with gemini. ๐
There a various servers, but I'd like to give a few advices to install vger, a gemini server designed for OpenBSD involving some of its mitigation mecanism (unveil, pledge...)
In order to keep vger as simple as possible, Solรจne -- vger's developer -- had the brilliant idea to use tools already in OpenBSD base install :
- relayd to deal with TLS
- inetd to daemonize vger.
Install vger as a package :
# pkg_add vger
If you read the README, you can learn how to set up a new capsule (a gemini website).
Edit "/etc/inetd.conf" to set how vger will be run, with the required flags.
127.0.0.1:11965 stream tcp nowait _vger /usr/local/bin/vger vger
By default, vger look for requested files in "/var/gemini".
You can add flags according to the manual. As example, if you want to serve multiple capsules, each one stored in a directory named after the domain name requested in "/var/gemini" (/var/gemini/athome.tld, /var/gemini/other.tld,...) with -v, enable auto index with -i :
# serve files in /var/gemini/domain localhost:11965 stream tcp6 nowait _vger /usr/local/bin/vger vger -v -d /var/gemini/ -i localhost:11965 stream tcp nowait _vger /usr/local/bin/vger vger -v -d /var/gemini/ -i
Pay attention to the lines above. inetd listens on localhost on port 11965 and send the incoming request to vger run as user _vger to avoid privilege escalation. A second line with "tcp6" is added to serve on the IpV6. However, you should have filled "/etc/hosts" accordingly so localhost resolve to local ipv6 :
127.0.0.1 localhost ::1 localhost
Then, you can add a new part to relayd in "/etc/relayd.conf":
ext_ip4 = "192.0.2.2" ext_ip6 = "2001:db8::2" log connection tcp protocol "gemini" { tls keypair chezmoi.tld } relay "gemini" { listen on $ext_ip4 port 1965 tls protocol "gemini" forward to localhost port 11965 } relay "gemini6" { listen on $ext_ip6 port 1965 tls protocol "gemini" forward to localhost port 11965 }
A few words :
- ext_ip4 and ext_ip6: public IP of your server.
- log connection: Keep log of incoming connections in "/var/log/daemon"
- tcp protocol "gemini"... Here we specify TLS keys location. If you followed instructions before, they might be at "/etc/ssl/private/athome.tld.key" and "/etc/ssl/athome.tld.crt". You probably should use a self-signed certificate to avoid clients a warning after each certificate renewal. If you want, set an expiration date very far in the future to the certificate.
- relay "gemini" : incoming traffic on external port 1965 must be forwarded to port 11965 localhost for inetd.
This is what happens when someone reach your capsule:
1965 11965 Visitor ---> Relayd ---> inetd ---> vger
Finally, enable and reload daemons :
# rcctl enable inetd relayd # rcctl start inetd relayd
Don't forget to open 1965/TCP in "/etc/pf.conf"
To go further :
Gopher (geomyidae) ยง
Gopher protocol is the precursor of widely used http. However, some still use it to transfer files and serve mostly text content.
You'll have to open 70 port.
Put the files you want to serve in "/var/gopher", they will be available on "gopher://athome.tld".
geomyidae server is written in C by one of suckless developpers.
To install it :
# pkg_add geomyidae # rcctl enable geomyidae # rcctl start geomyidae
That's it, now fill "/var/gopher" ๐.
However, I strongly recomment to read geomyidae manpage to edit default flags. As example, you may want something like this :
# rcctl set geomyidae flags -e -h athome.tld -b /var/gopher/athome.tld
Logs are in "/var/log/geomyidae.log".
mlmmj : Mailing List ยง
While there are hundreds of social networks, with their own policies and ads, instant messenging apps, forums and so on, remember mailing lists are a thing. Mailing lists are the future because :
- Mails just works for everyone
- Mails will exist in 10 years.
- It's easy to manage
- Each user choose how to deal with emails
- You just need an email address to use mailing list, and everyone has an email.
Here, we'll talk about mlmmj since it perform well on OpenBSD, is easy and secured.
Install mlmmj ยง
# pkg_add mlmmj
DNS records for the list ยง
Make sure a MX record is registered for the domain you'll use for your list.
Create a new mailing list ยง
Use command "mlmmj-make-ml" and follow instructions.
Below is an example to create list "pizza" on the domain "list.athome.tld", so "pizza@list.athome.tld".
# mlmmj-make-ml Creating Directorys below /var/spool/mlmmj. Use '-s spooldir' to change What should the name of the Mailinglist be? [mlmmj-test] : pizza The Domain for the List? [] : athome.tld The emailaddress of the list owner? [postmaster] : batman@athome.tld For the list texts you can choose between the following languages or give a absolute path to a directory containing the texts. Available languages: ast cs de en fi fr gr it pt sk zh-cn The path to texts for the list? [en] : Don't forget to add this to /etc/aliases: pizza: "|/usr/local/bin/mlmmj-receive -L /var/spool/mlmmj/pizza/" If you're not starting mlmmj-maintd in daemon mode, don't forget to add this to your crontab: 0 */2 * * * "/usr/local/bin/mlmmj-maintd -F -L /var/spool/mlmmj/pizza/" ** FINAL NOTES ** 1) The mailinglist directory have to be owned by the user running the mailserver (i.e. starting the binaries to work the list) 2) Run newaliases
Make sure permissions are correct:
# chown -R _smtpd:_smtpd /var/spool/mlmmj/pizza
Edit root's crontab ("# crontab -e") to add the line given by mlmmj-make-ml :
0 */2 * * * /usr/bin/mlmmj-maintd -F -L /var/spool/mlmmj/pizza
Make smtpd ready for mlmmj ยง
Edit "/etc/mail/smtpd.conf" so it handles mailing list messages correctly :
table aliases "/etc/mail/aliases" [...] action local_mail maildir alias <aliases> [...] match from any for domain "list.athome.tld" action local_mail
It is important that action ("local_mail" here) handle aliases.
That's why you must edit "/etc/mail/aliases" to pipe incoming messages to mlmmj for the mailing list:
pizza:"|/usr/local/bin/mlmmj-receive -L /var/spool/mlmmj/pizza/"
End with "# newaliases" command or restart smtpd.
Customize a mailing list ยง
You can customize a list by editing the files in "/var/spool/mlmmj/pizza/control".
If files don't exist, just create them.
You can filter incoming messages depending on the sender, force plaintext, use custom text in templates, modify headers to keep users privacy and much more.
Look at the official documentation to learn more
Monitoring ยง
Below are a few suggestions to keep an eye on your server's charge.
systat ยง
- "systat vm" : top-like for disks, processes, memory and network.
- "systat ifstat" : look at network interfaces stress.
- "systat sensors" : temperature.
cpu0.temp0 51.00 degC acpitz0.temp0 26.80 degC zone temperature
Type "q" to quit.
vmstat ยง
Quick look at system load :
$ vmstat procs memory page disks traps cpu r s avm fre flt re pi po fr sr sd0 sd1 int sys cs us sy id 1 274 1500M 1253M 657 0 0 0 0 0 1 4 190 15482 1826 2 1 97
Others ยง
Look at symon/symux/syweb if you wan real-time graphs. It requires PHP.
Look at monit to get alerts when the load gets over a threshold.
SmokePing is a deluxe latency measurement tool.
Seedbox ยง
With rtorrent ยง
rtorrent is a light and efficient torrent client.
It's text-based interface is nice if you don't want to bother with a webapp and remote control your seedbox with SSH.
# pkg_add rtorrent
Add a dedicated user _rtorrent for privileges separation.
Now log in as _rtorrent :
# su _rtorrent
Create required directories :
$ mkdir -p seedbox/{download,session,torrents}
Now create ~/.rtorrent.rc from the example :
$ cp /usr/local/share/examples/rtorrent/rtorrent.rc ~/.rtorrent.rc
Edit that file.
# Global upload and download rate in KiB. "0" for unlimited. download_rate = 0 upload_rate = 20 directory = ~/seedbox/download session = ~/seedbox/session # When a torrent file is copied in torrents dir, it's added to rtorrent schedule = watch_directory,5,5,load_start=~/seedbox/torrents/*.torrent schedule = untied_directory,5,5,stop_untied=~/seedbox/torrents/*.torrent check_hash = yes use_udp_trackers = yes encryption = allow_incoming,try_outgoing,enable_retry dht = auto peer_exchange = yes # Run script to get alerts when download finish system.method.set_key = event.download.finished,notify_me,"execute=~/.rtorrent_mail.sh,$d.get_name=" # add dht node so magnets works fine schedule2 = dht_node_1, 5, 0, "dht.add_node=router.utorrent.com:6881" schedule2 = dht_node_2, 5, 0, "dht.add_node=dht.transmissionbt.com:6881" schedule2 = dht_node_3, 5, 0, "dht.add_node=router.bitcomet.com:6881" schedule2 = dht_node_4, 5, 0, "dht.add_node=dht.aelitis.com:6881"
Fill the script "~/.rtorrent_mail.sh" to get alerts when a download is complete.
#!/bin/sh echo "$(date) : $1 - Download completed." | mail -s "[rtorrent] - Download completed : $1" root
To add a new torrent file, you may use scp :
$ scp *.torrent _rtorrent@chezmoi.tld:/home/_rtorrent/seedbox/torrents/
To have rtorrent automatically started at boot, edit _rtorrent user's crontab and add :
@reboot /usr/bin/tmux new -s rtorrent -d /usr/local/bin/rtorrent
We use tmux to put rtorrent in the background.
If you need to display rtorrent, log in as _rtorrent with ssh and run "tmux a -t rtorrent". Press ctrl-b then "d" to detach.
To add a magnet link, press "backspace" and copy the link.
When in doubt :
$ rtorrent -h
Transmission ยง
Transmission works very well and offer a web interface.
# pkg_add transmission
We start and stop daemon so we can create then edit configuration file.
# rcctl enable transmission_daemon # rcctl start transmission_daemon # rcctl stop transmission_daemon
Creates directories to download files and store .torrent.
# mkdir -p /var/transmission/{downloads,incomplete,torrents} # chown -R _transmission:_transmission /var/transmission
If others can see the above directories :
# chmod a+rX /var/transmission
Now edit this file to configure transmission :
/var/transmission/.config/transmission-daemon/settings.json
You may set :
"download-dir": "/var/transmission/downloads", "encryption": 2, "incomplete-dir": "/var/transmission/incomplete", "incomplete-dir-enabled": true, "peer-port-random-on-start": true,
I suggest to add the following lines to automatically start downloading torents you copied (with SFTP as example) in "/var/transmission/torrents".
"watch-dir": "/var/transmission/torrents", "watch-dir-enabled": true
To get an alert when a download is complete :
"script-torrent-done-enabled": true, "script-torrent-done-filename": "/var/transmission/dl-done.sh",
"dl-done.sh" script looks like this :
#!/bin/sh echo "$(date) : $TR_TORRENT_NAME - Download completed." | mail -s "[transmission] - Download completed : $TR_TORRENT_NAME" toto@example.com
Remember it must be executable :
# chmod +x /var/transmission/dl-done.sh
When you're done configuring, restart transmission :
# rcctl start transmission_daemon
The easiest to display the web interface is to use an SSH tunnel. From your computer, dig a tunnel to the server :
ssh -N -L 9999:127.0.0.1:9091 batman@athome.tld
Now open a browser at "http://localhost:9999".
Syncthing ยง
Syncthing is an amazing tool to keep your data on multiple devices.
It is supported on all platforms.
Data is encrypted by default.
It is really well done ๐.
Install and configure ยง
# pkg_add syncthing
Let it run as a daemon:
# rcctl enable syncthing # rcctl start syncthing
By default, Syncthing configuration is stored in "/var/syncthing".
You may edit those files to add new shares and configure various options, but it is quite complex.
Instead, I suggest to open the UI through a SSH tunnel ๐.
From your computer, run:
ssh -N -L 9999:127.0.0.1:8384 batman@athome.tld
Then open a browser to "http://localhost:9999".
You will learn what to do if you encounter issues or to run synthing inside a SSH tunnel between clients only.
TOR : relay and hidden services ยง
Tor is software helping protect privacy on the Internet.
It relies on multiple layers onion-like router, that's why this project needs volunteers to run relay nodes.
Configure a tor relay ยง
Tor may need to open lots of connexions, so you'll need to edit "/etc/sysctl.conf" to increase limits:
kern.maxfiles=20000
Install and enable tor:
# pkg_add tor # rcctl enable tor
Then, make sure you open port 9001.
Then, edit "/etc/tor/torrc", with the following lines :
SOCKSPort 0 ORPort 9001 Nickname nick RelayBandwidthRate 75 KB RelayBandwidthBurst 100 KB ContactInfo yourname <adress AT email dot tld> ExitPolicy reject *:* # no exits allowed
Adjust values according to your needs and how much bandwidth you want to allocate.
Finally, restart tor and look at such messages in "var/log/messages" :
May 12 12:20:41 athome Tor[12059]: Bootstrapped 80%: Connecting to the Tor network May 12 12:20:41 athome Tor[12059]: Bootstrapped 85%: Finishing handshake with first hop May 12 12:20:42 athome Tor[12059]: Bootstrapped 90%: Establishing a Tor circuit May 12 12:20:44 athome Tor[12059]: Tor has successfully opened a circuit. Looks like client functionality is working. May 12 12:20:44 athome Tor[12059]: Bootstrapped 100%: Done May 12 12:20:44 athome Tor[12059]: Now checking whether ORPort 109.190.xxx.xxx:9001 is reachable... (this may take up to 20 minutes -- look for log messages indicating success) May 12 12:21:10 athome Tor[12059]: Self-testing indicates your ORPort is reachab
Configure a hidden service ยง
Hidden services are availables with ".onion" URL.
โ Understand it is strongly discouraged to run a relay AND a hidden service.
It can be very handy, especially when you notice that the onion URL will remain unchanged even if your IP is modified for reasons in the future.
Enable a hidden service in "/etc/tor/torrc" :
SOCKSPort 0 HiddenServiceDir /var/tor/hidden/ HiddenServicePort 80 localhost:80
Reload Tor with "rcctl restart tor". Two new files are in "/var/tor/hidden" : "hostname" and "private_key". Find your onion URL in hostname file :
# cat /var/tor/hidden/hostname 5rud2tr7sm3oskw5.onion
KEEP private_key safe and secret.
In the above example, we provide a website (port 80 on localhost). You can add a new section in httpd.conf to serve this URL :
server "5rud2tr7sm3oskw5.onion" { listen on localhost port 80 # emplacement du site root "/htdocs/athome.tld" directory index index.html [...] }
XMPP ยง
Prosody is a light and easy to set up XMPP server
Here are a few notes to install prosody on OpenBSD.
Install prosody ยง
# pkg_add prosody
Add DNS fields for XMPP ยง
A record :
xmpp.athome.tld
SRV records :
_xmpp-client._tcp.athome.tld. 18000 IN SRV 0 5 5222 xmpp.athome.tld. _xmpp-server._tcp.athome.tld. 18000 IN SRV 0 5 5269 xmpp.athome.tld.
If you host MUCs :
_xmpp-server._tcp.conference.athome.tld. 18000 IN SRV 0 5 5269 xmpp.athome.tld.
Prosody's Configuration ยง
Edit "/etc/prosody/prosody.cfg.lua"
VirtualHost "athome.tld" ssl = { certificate = "/etc/prosody/certs/athome.tld.crt"; key = "/etc/prosody/certs/athome.tld.key"; }
Certificate must be readable by _prosody user. If you got them with acme-client, then you need to install them in prosody directory :
install -g _prosody -o _prosody -m 400 /etc/ssl/private/athome.tld.key /etc/prosody/certs/ install -g _prosody -o _prosody -m 400 /etc/ssl/athome.tld.crt /etc/prosody/certs/
Add the previous commands to your periodic task (cron? weekly.local?) when certificates are renewed.
Add the admin :
# prosodyctl adduser batman@athome.tld
Check it's allright :
prosodyctl check config
Prosody's Ports ยง
Open 5222 (xmpp-client) and 5269 (xmpp-server).
Prosody's logs ยง
Edit "/etc/newsyslog.conf" :
/var/prosody/prosody.log 644 5 300 * Z /var/prosody/prosody.err 644 5 300 * Z
mod_http_file_share ยง
If you enable http_file_share, make sure you open 5280 and 5281 ports.
Also, add the domain for file sharing in tls certificate.
More on Prosody ยง
Look official instructions ๐
Tips, FAQ, comments... ยง
About apu2 ยง
apu2 is a great board that require few power and well supported by OpenBSD.
However, you can't plug it to a screen but have to use the serial port to install and do admin task. Actually, it may be a good thing if you already have a laptop and don't want to buy an extra screen and keyboard.
Here, we tallk about apu2 tips.
For the record, this documentation is hosted on an apu2d0.
Where and what can I buy an apu2? ยง
Make sure you buy at least the following components if you don't use a pack :
- A board apu2d0 or other model.
- A box for cooling, there are no fans so no noise, but you must ensure heat is correctly dissipated.
- Serial cable db89 female-female + USB dongle USB.
- mSATA disk or external hard drive. You also can use a SATA disk if you have the appropriate cable, but only from apu2d2.
Assemble the apu2 ยง
Look at the page "apu cooling assembly"
Get to the apu2 console ยง
Plus the usb dongle or directly the serial cable to a computer. To get to the serial console, you can run on OpenBSD :
cu -s 115200 -l /dev/cuaU0
You should run the above as root or add your username to "dialer" group before:
doas usermod -G dialer $(whoami)
Install OpenBSD on APU2 ยง
Just proceed as usual with an USB key as installation media. In the console, before booting, add the following options so the installer knows we're in serial console :
boot> stty com0 115200 set tty com0
No need to do if afterwards, the installer save this option.
Upgrade apu2 bios ยง
You can flash the firmware with flashrom tool :
# pkg_add flashrom
Now read "/usr/local/share/doc/pkg-readmes/flashrom" for up-to-date instructions.
Check file checksum ยง
When you transfer files, you may ensure the authenticity of data.
To do so, computing a file checksum is useful. Use the "sha256" command :
$ sha256 debian-9.6.0-amd64.qcow2.gz SHA256 (debian-9.6.0-amd64.qcow2.gz) = 91831ba15446f3ab418ae8a5c2a8ac0d852dc5d43bd595a70b88bd6ea4ded397
Do the same on both ends : the output must be the same.
Notice that on GNU/Linux, "sha256" don't exist. Its equivalent is "sha256sum".
If you are paranoid, you can go further and check signatures with "signify" tool.
How to change password ยง
Use "passwd" command :
# passwd jdoe
To change superuser password, you have to make sure to be correctly logged with appropriate environment variables. To do so, use "-l" flag:
# su -l # passwd root
Periodic tasks (cron) ยง
Everything is ready on OpenBSD to run tasks periodically. You can edit the following scripts :
- /etc/daily.local
- /etc/weekly.local
- /etc/monthly.local
Make sure you use the full path of commands you need to run as PATH variable can be different than your user's. As example :
echo "You are so handsome" | mail -s "Hi" root # Not OK
echo "You are so handsome" | /usr/bin/mail -s "Hi" root # GOOD
Use "which command" to find absolute paths.
If you need more accurate timing, use cron.
Enter "crontab -e" and edit the file to specify when the user will run automatically a command. As example, to run it hourly (actually, every time the clock display "0 minutes"):
0 * * * * /path/to/command
With the example below, the command is run every 5 minutes :
*/5 * * * * /path/to/command
To learn more, read man crontab(5) manpage.
Look at this website to help you writing crontasks.
crontabs are very handy ๐.
Disk Partitioning ยง
If you need to set up another disk, we describe here how to prepare it.
โ Notice all this can be achieved after disk encryption and havig a RAID identified as "sd1" later.
Identifiy the disk ยง
Plug the disk and run "dmesg". You'll see something like that :
umass0 at uhub0 port 1 configuration 1 interface 0 "Western Digital Ext HDD 1021" rev 2.00/20.21 addr 2 umass0: using SCSI over Bulk-Only scsibus2 at umass0: 2 targets, initiator 0 sd1 at scsibus2 targ 1 lun 0: <WD, Ext HDD 1021, 2021> SCSI2 0/direct fixed serial.10581021383235373034 sd1: 1907727MB, 512 bytes/sector, 3907024896 sectors
Here, the disk is identified as "sd1". Adjust according to your case later.
Partitioning ยง
Actually, we create slices with disklabel :
# disklabel -E sd1
A prompt show up. Now we can create slices.
When in doubt, enter "p" to display the current state.
To create the first slice, enter "a a". It meas "Add a slice a".
Let default offset. Set the size according to your needs. To do so, you may use :
- An unit : "4G";
- A disk percentage: "50%";
- A percentage of remaining free space : "25&".
Choose filesystem "4.2BSD".
Add more slices if you want with "a d", "a e", "a f"...
โ Notice you can't create a "c" slice since it's reserved to identify the whole disk. Also, "b" is often used for swap, but it's not mandatory.
Once done, enter "q" to exit and apply changes.
Now enter "disklabel sd1" to see disk state :
# /dev/rsd1c: type: SCSI disk: SCSI disk label: Ext HDD 1021 duid: 782f1ddb783cdd13 flags: bytes/sector: 512 sectors/track: 63 tracks/cylinder: 255 sectors/cylinder: 16065 cylinders: 243201 total sectors: 3907024896 boundstart: 64 boundend: 3907024065 drivedata: 0 16 partitions: # size offset fstype [fsize bsize cpg] a: 629153472 64 4.2BSD 4096 32768 1 d: 3277870464 629153536 4.2BSD 8192 65536 1 c: 3907024896 0 unused
Notice the "duid" :
duid: 782f1ddb783cdd13
You can use it later to identify the disk and avoid confusions if there are several. You can find it also with this command :
# sysctl hw.disknames hw.disknames=wd0:bfb4775bb8397569,cd0:,wd1:56845c8da732ee7b,wd2:f18e359c8fa2522b
You're almost done, you still have to create the filesystem for each slices :
# newfs /dev/rsd1a # newfs /dev/rsd1d
Don't forget the "r" ๐
Now you can edit "/etc/fstab" to mount the previous slices easyly. Notice the use of the previous duid :
... 782f1ddb783cdd13.a /home/prx/music/ ffs rw,softdep,noatime,nodev,nosuid,noauto 0 0 782f1ddb783cdd13.d /mnt/backup/ ffs rw,softdep,noatime,nodev,nosuid 1 2
That's a lot of options, you'll learn more in man fstab(5).
In this example :
- The first slice is mounted on a directory /home/prx/music. Options increase read/write operations (softdep,noatime). The "noauto" avoid auto-mounting at boot. "0"s disable filesystem check at reboot. It is probably an external drive not always plugged.
- The second slice is mounted on /mnt/backup at each boot.
To mount every mountpoint listed in "/etc/fstab", enter :
# mount -a
How to find my network interface ? ยง
Use "ifconfig" command.
Your interface might have an "inet" (IP) and might belong to "egress" group.
As example
$ ifconfig lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 32768 priority: 0 groups: lo inet 127.0.0.1 netmask 0xff000000 re0: flags=218843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,MPSAFE,AUTOCONF6> mtu 1500 lladdr fc:aa:14:65:5f:86 priority: 0 groups: egress media: Ethernet autoselect (100baseTX full-duplex,rxpause,txpause) status: active inet 192.168.1.2 netmask 0xffffff00 broadcast 192.168.1.255 enc0: flags=0<> priority: 0 groups: enc status: active pflog0: flags=141<UP,RUNNING,PROMISC> mtu 33144 priority: 0 groups: pflog
Here, "lo0" is not the interface we're looking for: it's local interface used by some program. "enc0" and "pflog0" are not our interface, they are respectively the encrypted interface used by IKED and pf logging inteface.
It remains "re0", the ethernet as written in "media" section.
Logs ยง
Your server keep records of its activity. It is often stored in text files called "logs". You can find most of them in "/var/log" and "/var/www/logs".
You'll consider amongst others :
- /var/log/authlog : every login attempts, using ssh as example.
- /var/log/daemon : most daemon logs running in background. It is useful to see why one of them is malfunctioning.
- /var/log/maillog : mail server activity.
- /var/log/messages : system messages, interesting to read besides daemon.
- /var/log/php-fpm.log : PHP logs of course ๐
There are more.
I advice to read them from time to time to check everything works as expected. You can see them in realtime with the following command :
tail -f /var/log/messages /var/log/daemon /var/log/maillogs /var/log/authlog
Soon, you'll notice new files ending with ".0.gz", ".1.gz"... Those are archived logs. To avoid endlessly growing logs, OpenBSD run periodically "newsyslog" command and compress archives if necessary.
You can configure newsyslog if you edit "/etc/newsyslog.conf". You may add new lines if an application create logs not listed :
... /var/log/messages 644 5 300 * Z /var/log/authlog root:wheel 640 7 * 168 Z ...
As usual, read man newsyslog(8) to learn more about this file.
Check for improvements with lynis ยง
Lynis is a tool to scan your system looking for improvements in terms of security.
# pkg_add lynis # lynis audit system
You'll see a lot of messages. Read "/var/log/lynis-report.dat" carefully then :
# less /var/log/lynis-report.dat
As example, you may read :
suggestion[]=SSH-7408|Consider hardening SSH configuration|AllowTcpForwarding (YES --> NO)|-| suggestion[]=SSH-7408|Consider hardening SSH configuration|ClientAliveCountMax (3 --> 2)|-| suggestion[]=SSH-7408|Consider hardening SSH configuration|Compression (YES --> NO)|-|
To filter for suggestions :
# grep suggestion /var/log/lynis-report.dat
How to deal with power loss ? Where my mails are going ? ยง
Well, that happens...
You can buy an uninterruptible power supply (UPS). It's a kind of backup battery where you plug your server and router. in case of powerloss, the UPS keep your server running giving about 20 minutes or more to shutdown properly or find another solution.
In case a daemon crashed because of some bug, OpenBSD notice in its daily report daemons that should be running but aren't. You can check then.
So, no big deal if sometimes your server is down.
What happens when a mail server is, for some reasons, unreachable ?
SMTP protocol is designed to be reliable.
If your server is unreachable when someone tries to send you an email, things happens like in real life : the mailmain try again after some time. After trying, it may give up, sending a message to the author indicating there is a problem with the recipient.
There are no rules about time between each periodic delivery attemps. Each 4h, then each days, depending on the sender's configuration. It may give up after a week however.
To prevent the worst case where your server has burned, configure a backup mail server -- secondary -- so it keeps your emails while you are fixing your server. It is described in the mail part of this documentation.
I have a problem, what can I do ? ยง
Daemon issue ยง
If a daemon isn't starting, try starting it by hand to see errors ouput. As example, with httpd, you can enter :
rcctl -d start http
Read Manpages ยง
Check the manpage of the tool having an issue.
Look into "/usr/local/share/doc/pkg-readmes" if there are specific instructions.
Problem with a port/package ยง
Use "pkg_check" command to fix the state of your ports usage.
Look into logs ยง
Look at logs with "tail" command to see if there are error messages :
# tail -f /var/log/messages /var/log/daemon
For a website:
# tail -f /var/www/logs/*
For login issues :
# tail -f /var/log/authlog
Also, run "dmesg" to see if a device has an issue.
Chech what does the Firewall ยง
Look at firewall traffic with tcpdump to see where it's failing :
# tcpdump -n -e -ttt -i pflog0
Corrupted filesystem or loop reboots ยง
Try to boot on the ramdisk bsd.rd.
boot> bsd.rd [Enter]
Then choose to reinstall sets with "U: Upgrade" or use the shell to fix filesystem with fsck.
You also can boot on a disk prepared with "minirootXX.img".
Then choose "S : Shell".
Look after your disk with "dmesg". In this example, we use sd1.
Make sure it is ready with :
cd /dev && sh MAKEDEV sd1
Then, check and fix the filesystem :
fsck -y sd1a fsck -y sd1d
Do it for each slices. Use "disklabel sd1" to list them.
Ask for help ยง
Generate good random passwords ยง
The longer the better !
The more combinations a cracking tool will have to try, the more secure is your passphrase.
You may :
- Turn a sentence into a password. As example : "Sniffingpeppermakemecoughalot"
- Turn a few letters in the sentence : "Sn1ffingp3ppermakemec0ugal0t".
You can use OpenBSD's tools to generate random passwords. Below are examples for 15 charater long passwords :
$ openssl rand 15 -base64
$ dd if=/dev/urandom count=128 bs=1M 2>&1 | md5 | cut -b-15
$ jot -rcs '' 20 32 126
$ tr -cd ' -~' < /dev/urandom # only alnum $ tr -cd '[:alnum:]' < /dev/urandom
Here are some interesting links on the topic :
Online tool to generate a password
Split and reassemble files ยง
If y ou have huge files to transfer, you can split them.
$ gzip -c big_file | split -b 100m - big_file.gz
You get max 100mb files sorted alphabetically :
$ ls -l -rw-r--r-- 1 xavier xavier 104857600 Dec 12 16:52 big_file.gzaa -rw-r--r-- 1 xavier xavier 104857600 Dec 12 16:52 big_file.gzab -rw-r--r-- 1 xavier xavier 88204071 Dec 12 16:52 big_file.gzac
To recreate the original file, use cat :
cat big_file.gz_* | gunzip -c > big_file
Nice isnt it ? ๐
How to add/delete users? ยง
Add user ยง
Creating users let you :
- Get a login for ssh access
- Create a new mail address
- Split privileges
- ...
According to the user's taks, you won't create the account the same way.
- Leave defaults for an user with SSH access
- You can add an user to a group to manage groups permissions for multiple user at once.
- You can set a fake shell if you want to disable shell access for an user : "/sbin/nologin".
To add an user, use "adduser". It is interactive istead of "useradd". Yeah, I know...
That's how it looks like :
root@votreserveur[~] # adduser Use option '-silent' if you don't want to see all warnings and questions. Reading /etc/shells Check /etc/master.passwd Check /etc/group Ok, let's go. Don't worry about mistakes. There will be a chance later to correct any input. Enter username []: toto Enter full name []: Jean-Eudes Enter shell csh ksh nologin sh [nologin]: nologin Uid [1005]: Login group toto [toto]: Login group is ``toto''. Invite toto into other groups: guest no [no]: Login class authpf bgpd daemon default dovecot pbuild staff unwind [default]: Enter password []: Enter password again []: Name: toto Password: *************** Fullname: Jean-Eudes Uid: 1005 Gid: 1005 (toto) Groups: toto Login Class: default HOME: /home/toto Shell: /sbin/nologin OK? (y/n) [y]: y Added user ``toto'' Add another user? (y/n) [y]: n Goodbye!
Make sure to set a good passphrase.
Delete an user ยง
# userdel toto # groupdel toto
Here you go! ๐
SSH Tunnel : Proxy or VPN ยง
SSH let you create a PROXY to encapsulate some traffic and even creating a rudimentary VPN.
SOCKS Proxy ยง
To appear with the remote server's IP, dig an SSH tunnel :
From your computer :
ssh -D 9999 -NT batman@athome.tld
Then, configure applications to use a SOCKS proxy on localhost port 9999.
With Firefox, it is configured in "Network parameters".
SSH VPN ยง
See man ssh(1) at "SSH-BASED VIRTUAL PRIVATE NETWORKS".
Configuration examples ยง
/etc/dovecot/local.conf ยง
# listen both ipv4 and ipv6 listen = *, [::] # imap better than pop protocols = imap ssl = yes ssl_cert = </etc/ssl/athome.tld.crt ssl_key = </etc/ssl/private/athome.tld.key disable_plaintext_auth = yes service auth { user = $default_internal_user group = _maildaemons } passdb { args = scheme=blf-crypt /etc/mail/passwd driver = passwd-file } userdb { driver = static args = uid=_vmail gid=_vmail home=/mnt/bigstorage/_vmail/%d/%n/ } # Plugins mail_plugins = $mail_plugins quota zlib protocol imap { mail_plugins = $mail_plugins imap_quota imap_zlib imap_sieve } plugin { quota = maildir:User quota quota_rule = *:storage=1G quota_rule2 = Trash:storage=+100M quota_grace = 50%% quota_status_success = DUNNO quota_status_nouser = DUNNO quota_status_overquota = "552 5.2.2 Mailbox is full" zlib_save_level = 9 # 1..9; default is 6 zlib_save = gz # or bz2, xz or lz4 sieve_plugins = sieve_imapsieve sieve_extprograms sieve_default = /usr/local/lib/dovecot/sieve/default.sieve imapsieve_mailbox1_name = Junk imapsieve_mailbox1_causes = COPY imapsieve_mailbox1_before = file:/usr/local/lib/dovecot/sieve/report-spam.sieve imapsieve_mailbox2_name = * imapsieve_mailbox2_from = Junk imapsieve_mailbox2_causes = COPY imapsieve_mailbox2_before = file:/usr/local/lib/dovecot/sieve/report-ham.sieve sieve_pipe_bin_dir = /usr/local/lib/dovecot/sieve sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.environment }
/etc/httpd.conf ยง
types { include "/usr/share/misc/mime.types" } server "default" { listen on * port 80 root "/htdocs/athome.tld" } server "athome.tld" { listen on * port 80 block return 301 "https://$SERVER_NAME$REQUEST_URI" } server "athome.tld" { alias "www.athome.tld" listen on * tls port 443 root "/htdocs/athome.tld" directory index index.html log style combined hsts preload tls { certificate "/etc/ssl/athome.tld.crt" key "/etc/ssl/private/athome.tld.key" } location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } location "/Blog/" { directory index index.php } location "*.php*" { fastcgi socket "/run/php-fpm.sock" } location "/DL/PDF/" { directory auto index } location "/private/" { authenticate "education" with "/htdocs/private.htpw" directory auto index } } server "site2.athome.tld" { alias "www.site2.athome.tld" listen on * port 80 listen on * tls port 443 root "/htdocs/site2" directory index index.html log access "site2.log" hsts tls { certificate "/etc/ssl/athome.tld.crt" key "/etc/ssl/private/athome.tld.key" } location "/.well-known/acme-challenge/*" { root "/acme" request strip 2 } location "*.php*" { fastcgi socket "/run/php-fpm.sock" } location "/downloads/" { directory index index.php } }
/var/nsd/etc/nsd.conf ยง
server: hide-version: yes verbosity: 2 database: "" # disable database zonesdir: "/var/nsd/zones/" ip-address: 46.23.92.148 ip-address: 2a03:6000:9137::148 remote-control: control-enable: yes key: name: "secretkey" algorithm: hmac-sha256 secret: "i8f4FgDsldD11pHAqo9Ko=" zone: name: "reiva.xyz" zonefile: "signed/reiva.xyz" provide-xfr: 109.190.128.23 secretkey notify: 109.190.128.23 secretkey # GANDI provide-xfr: 217.70.177.40 NOKEY notify: 217.70.177.40 NOKEY # slaves zone: name: "si3t.ch" zonefile: "slave/si3t.ch" allow-notify: 109.190.128.23 secretkey request-xfr: 109.190.128.23 secretkey zone: name: "ouaf.xyz" zonefile: "slave/ouaf.xyz" allow-notify: 109.190.128.23 secretkey request-xfr: 109.190.128.23 secretkey zone: name: "3hg.fr" zonefile: "slave/3hg.fr" allow-notify: 109.190.128.23 secretkey request-xfr: 109.190.128.23 secretkey
/etc/pf.conf ยง
# See pf.conf(5) and /etc/examples/pf.conf # Macros ## Interfaces to take care. egress should be enough ## but it's an example ^^ ifaces = "{ egress em0 em1 }" ## various ports mail_ports = "{ submission imaps smtp }" tcp_pass = "{ www https domain 1965 xmpp-client xmpp-server 5280 5281 62882 }" # 5280-5281 are xmpp-http, 62882 transmission udp_pass = "{ domain 62882 }" # 62882 dht rtorrent blocking_tcp="{ ftp ftp-data telnet finger sunrpc epmap netbios-ns netbios-dgm netbios-ssn microsoft-ds ipp ldaps ldp ms-sql-s ms-sql-m pptp mysql postgresql rfb rdp 27019 1194 ldap 8080 kerberos socks }" # Tables table <evils> persist table <bruteforce> persist table <sshguard> persist table <pfbadhost> persist file "/etc/pf-badhost.txt" table <solene> persist file "/etc/solene-block.txt" table <spamd> persist # Options ## increase limit for huge blocking table files set limit table-entries 409600 ## no not filter local set skip on { lo } # Avoid spoofing antispoof for $ifaces # Rules ## block by default block anchor "relayd/*" # so relayd works properly ## "quick" rules : the rest won't be read if it matches. ## This filter bad ip ### block unwanted sources, and don't go further block log quick from <bruteforce> label "BRUTES" block log quick from <evils> label "EVILS" block log quick from <sshguard> label "SSHGUARD" block log quick on $ifaces from <pfbadhost> label "PFBADHOST" block log quick on $ifaces from <solene> label "SOLENE" ### Let in local network, or it is blocked by pfbadhost pass in quick from 192.168.1.0/24 modulate state ### iblock : everything else is banned pass in quick on $ifaces inet proto tcp to port $blocking_tcp rdr-to 127.0.0.1 port 2507 pass in quick on $ifaces inet6 proto tcp to port $blocking_tcp rdr-to ::1 port 2507 ## Allow some incoming traffic ### spamd traps in blacklist only pass in on $ifaces inet proto tcp from <spamd> to any port smtp \ divert-to 127.0.0.1 port spamd modulate state ### let ssh in, with anti bruteforce pass in on $ifaces proto tcp to port ssh modulate state \ (source-track rule, \ max-src-conn 8, max-src-conn-rate 15/5, \ overload <bruteforce> flush global) ### same with email pass in on $ifaces proto tcp to port $mail_ports modulate state \ (source-track rule, \ max-src-conn 100, max-src-conn-rate 50/100, \ overload <bruteforce> flush global) ### let some ports in pass in on $ifaces proto tcp to port $tcp_pass modulate state pass in on $ifaces proto udp to port $udp_pass ### allow ping, in and out pass on $ifaces inet6 proto ipv6-icmp all icmp6-type echoreq pass on $ifaces inet proto icmp all icmp-type echoreq ### Let all out pass out on $ifaces proto { tcp udp }
/etc/relayd.conf ยง
ext_ip4 = "192.0.2.2" ext_ip6 = "2001:db8::2 tcp protocol "tlsrelay" { tls keypair si3t.ch tcp { nodelay, sack } } relay "gemini" { listen on $ext_ip4 port 1965 tls protocol "tlsrelay" forward to 127.0.0.1 port 11965 } relay "gemini6" { listen on $ext_ip6 port 1965 tls protocol "tlsrelay" forward to ::1 port 11965 } http protocol "http" { include "/etc/relayd.proxy.conf" } http protocol "https" { include "/etc/relayd.proxy.conf" match query hash "sessid" tls keypair si3t.ch } relay "http" { listen on $ext_ip4 port 80 protocol "http" forward to 127.0.0.1 port 80 } relay "http6" { listen on $ext_ip6 port 80 protocol "http" forward to ::1 port 80 } relay "https" { listen on $ext_ip4 port 443 tls protocol "https" forward with tls to 127.0.0.1 port 443 } relay "tlsforward6" { listen on $ext_ip6 port 443 tls protocol "https" forward with tls to ::1 port 443 }
/etc/relayd.proxy.conf :
return error return error style "body { background: silver; color: black; text-align:center } hr {border:0; background-color:silver; color:silver; height:1px; width:30%; margin-top:50px;}" match request header set "X-Forwarded-For" \ value "$REMOTE_ADDR" match request header set "X-Forwarded-By" \ value "$SERVER_ADDR:$SERVER_PORT" match header set "Keep-Alive" value "$TIMEOUT" block quick path "/wp-*" label '<em>Stop scanning for wordpress</em>.' match request header remove "Proxy" match response header set "X-Xss-Protection" value "1; mode=block" match response header set "Frame-Options" value "SAMEORIGIN" match response header set "X-Frame-Options" value "SAMEORIGIN" match response header set "X-Robots-Tag" value "index,nofollow" match response header set "X-Permitted-Cross-Domain-Policies" value "none" match response header set "X-Download-Options" value "noopen" match response header set "X-Content-Type-Options" value "nosniff" match response header set "Referrer-Policy" value "no-referrer" match response header set "Content-Security-Policy" value "upgrade-insecure-requests" match response header set "Permissions-Policy" value "interest-cohort=()" match response header set "X-Powered-By" value "Powered by OpenBSD" match request path "/*.css" tag "CACHE" match request path "/*.js" tag "CACHE" match request path "/*.atom" tag "CACHE" match request path "/*.rss" tag "CACHE" match request path "/*.xml" tag "CACHE" match request path "/*.jpg" tag "CACHE" match request path "/*.png" tag "CACHE" match request path "/*.svg" tag "CACHE" match request path "/*.gif" tag "CACHE" match request path "/*.ico" tag "CACHE" match request path "/*.html" tag "CACHE" match request path "/*.gmi" tag "CACHE" match request path "*/" tag "CACHE" match response tagged "CACHE" header set "Cache-Control" value \ "public, max-age=86400" match request path "/*.html" tag "HTML" match response tagged "HTML" header set "Content-Type" value "text/html; charset=utf-8" match request path "/*.txt" tag "TXT" match request path "/*.md" tag "TXT" match request path "/*.gmi" tag "TXT" match response tagged "TXT" header set "Content-Type" value "text/plain; charset=utf-8" pass
/etc/mail/smtpd.conf ยง
Example for multiple domains
Example 1 (rspamd) ยง
# install : # opensmtpd-filter-rspamd # opensmtpd-filter-senderscore table aliases "/etc/mail/aliases" table domains "/etc/mail/domains" table passwd "/etc/mail/passwd" table virtuals "/etc/mail/virtuals" pki athome.tld key "/etc/ssl/private/athome.tld.key" pki athome.tld cert "/etc/ssl/athome.tld.crt" pki domaine2.net key "/etc/ssl/private/domaine2.net.key" pki domaine2.net cert "/etc/ssl/domaine2.net.crt" pki autredomaine.xyz key "/etc/ssl/private/autredomaine.xyz.key" pki autredomaine.xyz cert "/etc/ssl/autredomaine.xyz.crt" # certificat par defaut pki "*" key "/etc/ssl/private/athome.tld.key" pki "*" cert "/etc/ssl/athome.tld.crt" filter senderscore \ proc-exec "filter-senderscore -junkBelow 70 -slowFactor 2000" filter rspamd proc-exec "filter-rspamd" listen on all tls pki athome.tld \ filter { senderscore, rspamd } listen on all port submission tls-require pki athome.tld auth <passwd> \ filter rspamd action "relay" relay action relaybackup relay backup tls helo "si3t.ch" action "local_mail" maildir alias <aliases> action virtual_maildir maildir "/home/_vmail/%{dest.domain:lowercase}/%{dest.user:lowercase}/Maildir" junk virtual <virtuals> match from any for domain <domains> action virtual_maildir match from any for local action local_mail match from any for domain friend.eu action relaybackup match auth from any for any action "relay" match for any action "relay"
Example 2 (spamassassin + dkimproxy) ยง
Only one certificate matching all domains is used here.
We add some options on queue, because.
table aliases "/etc/mail/aliases" table passwd "/etc/mail/passwd" table virtuals "/etc/mail/virtuals" table domains "/etc/mail/domains" pki athome.tld key "/etc/ssl/private/athome.tld.key" pki athome.tld cert "/etc/ssl/athome.tld.crt" queue compression # less disk space queue encryption 7dbecabecabeca45bce4aebc # encrypt all o/ filter senderscore \ proc-exec "filter-senderscore -junkBelow 70 -slowFactor 2000" listen on lo0 port 10028 tag DKIM listen on lo0 port 10026 tag SPAMASSASSIN listen on all tls pki athome.tld filter { senderscore } listen on all port submission tls-require pki athome.tld auth <passwd> action "envoi" relay action dkimproxy relay host smtp://127.0.0.1:10027 action spamassassin relay host smtp://127.0.0.1:10025 action local_mail maildir alias <aliases> action relaybackup relay backup mx "athome.tld" helo "athome.tld" action virtual_maildir maildir "/var/vmail/%{dest.domain:lowercase}/%{dest.user:lowercase}/Maildir" junk virtual <virtuals> match for local action local_mail match tag SPAMASSASSIN from any for domain <domains> action virtual_maildir match from any for domain <domains> action spamassassin match tag DKIM for any action "envoi" match auth tag DKIM from any for any action "envoi" match from any for domain copain.eu action relaybackup match auth from any for any action dkimproxy match for any action dkimproxy
/etc/mail/domains ยง
All MX records
athome.tld domaine2.net other.xyz
/etc/mail/spamd.conf ยง
all:\ :nixspam:bgp-spamd:bsdlyblack:whitelist: # Nixspam recent sources list. # Mirrored from http://www.heise.de/ix/nixspam nixspam:\ :black:\ :msg="Your address %A is in the nixspam list\n\ See http://www.heise.de/ix/nixspam/dnsbl_en/ for details":\ :method=http:\ :file=www.openbsd.org/spamd/nixspam.gz bsdlyblack:\ :black:\ :msg="Your address %A is in the bsdly.net list":\ :method=http:\ :file=www.bsdly.net/~peter/bsdly.net.traplist bgp-spamd:\ :black:\ :msg="Your address %A has sent mail to a spamtrap\n\ within the last 24 hours":\ :method=file:\ :file=/var/spamd.black whitelist:\ :white:\ :method=file:\ :file=/etc/mail/whitelist.txt
/etc/webalizer.conf ยง
LogFile /var/www/logs/access.log OutputDir /var/www/htdocs/chezmoi.tld/stats ReportTitle Statistiques pour HostName chezmoi.tld LinkReferrer yes HTMLHead <style type="text/css"> HTMLHead body {background:#eceff4;color:#2e3440;line-height:1.4;margin:auto} HTMLHead table {border: 1px solid; padding:1ex} HTMLHead a {color:#5e81ac} HTMLHead th, td {border: 0} HTMLHead tr:nth-child(even){background-color: #e5e9f0;} HTMLHead tr:hover {background-color: #d8dee9;} HTMLHead </style> TopSites 75 TopURLs 50 TopReferrers 100 AllSites yes AllURLs yes AllReferrers yes AllSearchStr yes AllErrors yes HideSite *chezmoi.tld HideReferrer chezmoi.tld HideURL *.gif HideURL *.GIF HideURL *.jpg HideURL *.JPG HideURL *.png HideURL *.PNG HideURL *.css HideURL *.woff GroupReferrer google. Google Intl HideReferrer google. IgnoreURL /atom.xml IgnoreURL /sitemap.* IgnoreURL /favicon.* IgnoreURL /robots.txt ColorBackground eceff4 ColorText 2e3440 ColorLink 5e81ac ColorVLink 81a1c1 ColorALink 88c0d0 ColorHeadline d8dee9 ColorCounter 4c566a ColorHit 5e81ac ColorFile bf616a ColorSite d08770 ColorKbyte ebcb8b ColorPage a3be8c ColorVisit b48ead ColorMisc 8fbcbb ChartBackgroundColor eceff4 ChartLegendColor 2e3440 ChartShadowColor1 eceff4 ChartShadowColor2 d8dee9 TableBorder 0 ChartBorder 0
Notes ยง
How to contribute? ยง
If you want and if you can, you can contribute by donating money.
Sources can be downloaded with this link.
If you notice an error or want to suggest improvements, please follow these guidelines :
- Write gemtext. See https://gemini.circumlunar.space/docs/specification.html
- Use relative links for images so it can be read offline
- Domain name : athome.tld
- IPV4 : 192.0.2.2
- IPV6 : 2001:db8::2 ; fd19:6f4d:bea8:f63a::2
- SSL certificate : /etc/ssl/athome.tld.crt
- SSL certificate key : /etc/ssl/private/athome.tld.key
- No header level lower than 3. Level 1 is for first title of a chapter.
Send diff to prx@si3t.ch.
$ cp -r src/ src-mod ### edit files in src-mod $ diff -ur src/ src-mod/ > my-changes.diff ### send my-changes.diff file to prx@si3t.ch
Thank you !
Thanks ยง
Thank you to :
Henning Stedtnitz for proofreading.
And so much more I may have forgotten ๐ .
Special thanks to my beloved one who reboot the server when I'm away.