---
title: "Install"
date: 2020-08-21T17:09:10+03:00
draft: false

categories:
- HowTo
- For users
tags:
- install

---

# Setting up mail server on NixOS

This document will guide you through steps of setting up [NixOS mailserver](https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/tree/master). This mailserver deployment includes:

- SMTP Server (**postfix**)
- IMAP/POP3 Server (**dovecot**)
- SSL/TLS/STARTLS support
- Automatic certificates assignment with **ACME** and **LetsEncrypt**
- Spam filtering via **rspamd**
- Virus scanning via **clamav**

This server is incredibly fast to deploy and incredibly easy to manage

## System Requirements

- 1 vCPU
- At least 1 GB RAM
- At least 10 GB of storage
- NixOS 20.03
- Dedicated domain or subdomain
- Set of ports, allowed by your external firewall and ISP (both TCP and UDP)
  - HTTP (80)
  - HTTPS (443)
  - SMTP (25)
  - Submission (587)
  - STARTLS IMAP (143)
  - STARTLS POP3 (110)

If your system and network setup meets all of the requirements, we can start setting up your system. There's several options for you to choose from, when we speak about server setup

## Automated installation(using a script)

## Requirements for automated install

- ACME Support by your domain registrar
- Hetzner account
  - Hetzner API token (see below, how to get it)
- ClodFlare account
  - CloudFlare API token (see below, how to get it)
- AWS account 
  - AWS Secret Access Key (see below, how to get it)
  - AWS Access Key ID (see below, how to get it)

## Getting reuqired tokens, keys and IDs

1. Get Hetzner API key

{{< rawhtml >}}
<video class="tab" controls>Your browser does not support the &lt;video&gt; tag.
  <source src="/img/hetzner/Hetzner.mp4"/>
</video>
{{< /rawhtml >}}

2. Generate CloudFlare Token

{{< rawhtml >}}
<video class="tab" controls>Your browser does not support the &lt;video&gt; tag.
  <source src="/img/cloudflare/CloudFlare.mp4"/>                                                                       
</video>
{{< /rawhtml >}}

3. Get AWS Secret Access Key

{{< rawhtml >}}
<video class="tab" controls>Your browser does not support the &lt;video&gt; tag.
  <source src="/img/aws/AWS.mp4"/>                                                                       
</video>
{{< /rawhtml >}}

## Running a script

From now, you`re ready to start setting up a mailbox. To run a script, type:

```
bash <(curl -s https://selfprivacy.org/server.sh)
```

## Manual Installation

See instructions below

## Setting up the system

If you are planning to run NixOS mailserver on physical on-premise machine, please reffer to [NixOS Installation Guide](https://nixos.wiki/wiki/NixOS_Installation_Guide)

If you running your server on Cloud Providers like: DigitalOcean, Scaleway, Hetzner etc. we will use [nixos-infect](https://github.com/elitak/nixos-infect) script to transform your Ubuntu, Debian or Fedora system into NixOS.

## A few words about [nixos-infect](https://github.com/elitak/nixos-infect)

[NixOS-Infect](https://github.com/elitak/nixos-infect) - user-made script for replacing crucial Linux system files of the distro, supported by your cloud provider, transforming it into NixOS. This script performs such tasks, as:

- Network settings collection (interfaces, routing)
- openSSH server settings collection
- Environment variables collection
- NixOS rootfs extraction
- Post-installation system builds and bootloader config

All data, collected by nixos-infect, will be converted into NixOS configuration files and then, applied to the new system, when it will be ready

One thing, you should bare in mind. **NixOS-Infect supports only limited list of Cloud Providers. Make sure that your provider is supported, otherwise use generic provider (which do not guarantee you any success)**
Providers that are currently supported:

- DigitalOcean
- Vultr
- Hetzner
- ~~OVH VPS~~ (experimental)

### Using nixos-infect script
If your provider is supported or you decided to use generic provider, run the following command that corresponds to your choice:

#### DigitalOcean

```
curl https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | PROVIDER=digitalocean NIX_CHANNEL=nixos-20.03 bash 2>&1 | tee /tmp/infect.log
```

#### Vultr

```
curl https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | PROVIDER=vultr NIX_CHANNEL=nixos-20.03 bash
```

#### Hetzner Cloud

```
curl https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | NIX_CHANNEL=nixos-20.03 bash 2>&1 | tee /tmp/infect.log
```

#### Generic (tested on Scaleway)

```
curl https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | PROVIDER=vultr NIX_CHANNEL=nixos-20.03 bash
```

Wait, until the process is finished. If everything worked correctly, your system should reboot. After system boots up, you will appear in the NixOS shell. From this moment, we can start setting up NixOS mail

## Populating DNS server with necessary records

Each and every mailserver in the world, requires some DNS configuration to work properly. During further reading, you'll find all necessory DNS record templates. There's also DNS zonefile template available for direct export into CloudFlare. 

### **A Records**

Firstly, you need to point domain name, you would like to use for mailserver, directly to your machine's external IP. For example, if you have domain name ```example.com```, you need to create this record:

- Name: @
- TTL: 1 Hour
- Points to: "Your external IPv4 address"

```
example.com.	1	IN	A	<Your.external.IP>
```

But, if you have domain with some sort of subdomain (for example: subdomain.example.com), your record should look like this:

- Name: subdomain
- TTL: 1 Hour
- Points to: "Your external IPv4 address"

### **AAAA Records (IPv6 only)**

If you have an IPv6 address, then you also need to perform this step. AAAA record does exactly the same thing as A record (directs domain to IP address), but for IPv6

- Name: @
- TTL: 1 Hour
- Points to: "Your external IPv6 address"

```
subdomain.example.com.	1	IN	A	<Your.external.IP>
```

### **MX Records**

After that, you'll need to define, which domain or IP, handles mail on your specific domain (in this example it's: example.com). We are going describe specific scenario, when mail for your domain is being handled on the same domain, because 99% of mail servers configured this way.

- Name: @
- Points to: @
- Priority: 10
- TTL: 1 Hour

```
example.com.	1	IN	MX	10 example.com.
```

At this point, basic DNS configuration is finished, but we'll be required to return to this a bit later.

## Configuring NixOS firewall

Open ```/etc/nixos/configuration.nix``` file for editing. Find ```networking.allowPing = true``` line, and insert this two lines after it:

```
networking.firewall.allowedTCPPorts = [ 22 443 80 143 993 587 25 465 ];
networking.firewall.allowedUDPPorts = [ 80 443 143 993 587 25 465 ];
```

## Installing and configuring NixOS mailserver

First, you need to copy mailserver config to your NixOS machine. It can be done via ```scp``` using SSH (**Please note that this requires opened 22 port**). 

```
{ config, pkgs, ... }:
{
  imports = [
    (builtins.fetchTarball {
      # Pick a commit from the branch you are interested in
      url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/4008d0cb/nixos-mailserver-4008d0cb.tar.gz";
      # And set its hash
      sha256 = "1y9svi3nrg24ky1gqbpa3zhnhhin399h0cmnf81hb4yn75mlqiqx";
    })
  ];


  mailserver = {
    enable = true;
    fqdn = "<your-fqdn>";
    domains = [ "<your-subdomains>" ];

    # A list of all login accounts. To create the password hashes, use
    # mkpasswd -m sha-512 "super secret password"
    loginAccounts = {
        "<user>@<your.domain>" = {
            hashedPassword = "<your-hashed-password>";

            aliases = [
                "<user>@<your.domain2>"
            ];

            # Make this user the catchAll address for domains example.com and
            # example2.com
            catchAll = [
                "<your-domain>"
            ];
        };

    };

    # Extra virtual aliases. These are email addresses that are forwarded to
    # loginAccounts addresses.
    extraVirtualAliases = {
        # address = forward address;
        "<alias>@<your-domain>" = "<user>@<your-domain>";
    };

    # Use Let's Encrypt certificates. Note that this needs to set up a stripped
    # down nginx and opens port 80.
    certificateScheme = 3;

    # Enable IMAP and POP3
    enableImap = true;
    enablePop3 = true;
    enableImapSsl = true;
    enablePop3Ssl = true;
    #certificateFile = "~/cert.pem";
    #keyFile = "~/privKey.pem";

    # Enable the ManageSieve protocol
    enableManageSieve = true;

    # whether to scan inbound emails for viruses (note that this requires at least
    # 1 Gb RAM for the server. Without virus scanning 256 MB RAM should be plenty)
    virusScanning = true;
  };
  security.acme = {
    acceptTerms = true;
    # Replace the email here!
    email = "<your-existing-email-address>";
};
}
```

Now, let's look at this config in more details

### **Setting your Fully Qualified Domain Name**

Variable ```fqdn``` corresponds to FQDN in mailserver config. For example, if your domain, dedicated for mailserver is: ```example.com```, then your fqdn variable will look like this

```
fqdn = "example.com";
```
If you set up mailserver on sort of subdomain (for example: ```subdomain.example.com```), then your variable will look like this

```
fqdn = "subdomain.example.com"
```

### **Defining domains list, to use with mailserver**

At the next string after ```fqdn``` variable, goes ```domains``` array. There should be specified all domains, that will be associated with this mailserver. For example: if you are going to run mailserver on domains: ```example.com```, ```mail.example.com```, ```example2.com```, your ```domains``` array should look like this:

```
domains = [ "example.com", "mail.example.com", "example2.com" ]
```
### **Defining login accounts**

Next goes ```loginAccounts = { ... }``` scope. There, you'll need to define username, hashed password and domain for assigning this user to it. For example, if you want to create user ```mailuser``` on domain ```example.com``` with password ```VerySecurePassword```, your config should look like this:

```
loginAccounts = {
        "mailuser@example.com" = {
            hashedPassword = "$6$5AiHele5DmyzzAuu$4fNrQD9fGi3J29dxQjRqQvwKw.PTbPideuYp9MTeaArnHBQHBZ/EJttKP5etWml2httbY3mOajPwLwr2ZbiV40";

            aliases = [
                "mailuser@example.com"
            ];
            catchAll = [
                "mailuser@example.com"
            ];
        };

    };
```

#### Hashing your password

Your password can be hashed with this command: ```mkpasswd -m sha-512 "YourPassword"```

Please pay attention, that mailserver requires SHA512 hashing algorithm

#### catchAll list

This list is being used to define, which mail users will receive all of the mail, from all domains and all users associated with this mailserver

### **Setting up virtual addresses**

Mailserver allows you to create virtual mail addresses if you need. Mail from such addresses will be stored on real user's mailbox. Main difference between virtual addresses and aliases, that you can use virtual address to log in into user account to which, this virtual address associated.

```
extraVirtualAliases = {
        # address = forward address;
        "abuse@ruleit.stream" = "user@ruleit.stream";
    };
```

### **Choosing certificate resolution method**

As NixOS mailserver supports STARTLS/STARSSL, there's necessity in a security certificate, which will prove your identity in further client-server communication.

Currently, there's three ways of provisioning certificate to a mail server

1. Manual provisioning
2. Generate self-signed
3. LetsEncrypt certificate resolution with ACME

To choose one of this methods, you have to assign corresponding value to ```certificateScheme``` variable, like this:

```
certificateScheme = 3;
```

1 - for manual provisioning, 2 - for automated self-signed certificate generation, 3 - for LetsEncrypt certificate resolution with ACME.

#### Manual provisioning

To use manual provisioning, you have to define two additional variables (they're commented in the example config): ```certificateFile``` and ```keyFile```.

```
certificateFile = "~/cert.pem";
keyFile = "~/privKey.pem";
```

#### ACME resolution

**WARNING!!!** For ACME resolution, your DNS provider have to directly support ACME protocol.

#### Accpeting LetsEncrypt Terms of Service

At the end of example config, you can find ```security.acme``` code snippet. There's several variables, you should assign values to

##### acceptTerms

Defines, whenever you accept terms of service, or not. Can be ```true``` or ```false```

##### email

Defines e-mail address to which send notifications on certificate expiry. Can be your existing email or email, you're currently setting up

#### Setting up DKIM selector 

For further DNS configuration, you'll need a random word or letters set, to act as a DKIM selector. For setting up DKIM selector, please assign value to ```dkimSelector``` variable, like this:

```
dkimSelector = "selector";
```

This value will be used to set up some sort of forgery protection to your mail, later.

## SPF/DMARC/DKIM setup

**At this point, you'll have to be very careful, because the smallest mistake during this step, may result in improper mailserver work. All of your mails may be treated as spam and you risk to put your domain in all blacklists of this world.**

### **SPF Record**

SPF - is a framework that has been designed to protect your mail from forgery and MITM attacks. It's very easy to configure. All, you have to add into your DNS record is:

```
example.com.	1	IN	TXT	"v=spf1 a mx ip4:<your IPv4> ip6:<Your IPv6 (if any)> -all"
```

If you do not have IPv6, then your record will look like this:

```
example.com.	1	IN	TXT	"v=spf1 a mx ip4:<your IPv4> -all"
```

### **DKIM Record**

DKIM - is a way of signing your messages to be able to prove identity of this mails later. DKIM requires you to define key, to sign all mail you send with it. This key is being generated by NixOS Simple Mailserver during first startup. By default, it can be found in ```/var/dkim``` directory. If your domain is: ```example.com```, your key will be stored in a file ```/var/dkim/example.com.yourDKIMSelector.txt```. Inside this file you'll find something similar to this:

```
yourDKIMSelector._domainkey     IN      TXT     ( "v=DKIM1; k=rsa; " "p=REALLY-LONG-KEY" )  ; ----- DKIM key selector for example.com
```

When you got content of that file, you have to add new record of type ```TXT``` to your DNS server

```
<yourDKIMSelector>._domainkey.example.com.	18000	IN	TXT	"v=DKIM1; p=REALLY-LONG-KEY"
```

### **DMARC Record**

DMARC is used to define, how you would like to treat mail that violates your SPF/DKIM records and policy. It's added as simple as that:

```
_dmarc.example.com.	18000	IN	TXT	"v=DMARC1; p=none"
```

#### DMARC Policy

Please pay attention to ```p=none``` in your record. This is policy variable. Replace this to ```p=quarantine``` if you want to treat all mail with invalid headers as spam. Replace this to ```p=reject``` if you do not want to receive mail with invalid headers at all.

## Performing SPF/DKIM validity and integrity tests.

### SPF test

Visit [SPF testing tool](https://mxtoolbox.com/spf.aspx) for SPF validation. Give there your domain name and IP address. If your record is correct, you'll see your SPF record content in green, like this:

```
v=spf1 a mx -all
```

### DKIM test

Visit [DKIM validation tool](https://mxtoolbox.com/dkim.aspx) for DKIM integrity check. Input there your domain and DKIM selector, which we defined earlier. If everything in your record is correct, you will see your DKIM record content in green
