summaryrefslogtreecommitdiff
path: root/articles
diff options
context:
space:
mode:
authorSam Chudnick <sam@chudnick.com>2022-12-08 20:44:37 -0500
committerSam Chudnick <sam@chudnick.com>2022-12-08 20:44:37 -0500
commit3fb2b9563d9b58a9683808c6620832dc71f76b20 (patch)
tree9c208db70957ba26fbcc2d87f3b91df7117617c8 /articles
Initial commit
Diffstat (limited to 'articles')
-rw-r--r--articles/freeipa-server.html176
-rw-r--r--articles/icinga-agent.html135
-rw-r--r--articles/icinga-director.html112
-rw-r--r--articles/icinga-influx.html133
-rw-r--r--articles/icinga-master.html276
-rw-r--r--articles/luks.html132
-rw-r--r--articles/mail-server.html723
-rw-r--r--articles/mdadm-raid.html120
-rw-r--r--articles/mutt.html27
-rw-r--r--articles/pam-tfa.html157
-rw-r--r--articles/template.html27
11 files changed, 2018 insertions, 0 deletions
diff --git a/articles/freeipa-server.html b/articles/freeipa-server.html
new file mode 100644
index 0000000..80825d0
--- /dev/null
+++ b/articles/freeipa-server.html
@@ -0,0 +1,176 @@
1<!DOCTYPE html>
2<html lang=en>
3 <head>
4 <title></title>
5 <meta charset="utf-8"/>
6 <link rel="shortcut icon" href="favicon.ico"/>
7 <link rel='stylesheet' href='../style.css'/>
8 <meta name="viewport" content="width=device-width, initial-scale=1">
9 </head>
10<body>
11 <header><h1>FreeIPA Server Setup</h1></header>
12 <main>
13 <p>FreeIPA is a centralized idenity management solution developed
14 by Redhat. It is in my opinion the most functional libre alternative
15 to Microsoft's Active Directory. Like AD, FreeIPA integrates all of
16 the pieces needed to setup a domain including LDAP, Kerberos,
17 a Certificate Authority, and much more.</p>
18
19 <p>I will be using Fedora 35 in this tutorial. As of Debian 11, the
20 FreeIPA server is still not in the Debian repos. You will need either
21 a Fedora or a RHEL machine. A CentOS fork may work also but I have not
22 tested that.</p>
23
24 <h2>FreeIPA in an Enterprise</h2>
25
26 <p>For readers exploring the use of FreeIPA in a business
27 environment, note that FreeIPA documentation explicitly states that
28 it is not a replacement for Active Directory. I have not personally
29 tried to join a Windows computer to a FreeIPA domain, and so I can't
30 speak to how well that would work. FreeIPA would also not be able to push
31 out policy to Windows machines as is done with Group Policy. FreeIPA
32 is though able to create inter-domain trusts with an existing AD
33 infrastructure.</p>
34
35 <h2>The Case for FreeIPA at Home</h2>
36 <p>Using a full Kerberos and LDAP identity management server may
37 seem like overkill at home. And if you only have a single computer
38 then it probably is. But scaling up even slightly, to perhaps a small
39 family each with their own computer, will make having FreeIPA
40 advantageous (<em>your family is all using Linux, right?</em>). This
41 will be especially apparent if you are hosting your own services.
42 If you are for instance hosting a Jellyfin media server that everyone
43 in your family accesses, you won't want them to juggle separate
44 passwords for Jellyfin when you could just have them use the same
45 password they do on the computer. This single/same sign-on capability is
46 one of the most practically useful aspects of FreeIPA.</p>
47
48 <h2>Install Packages</h2>
49 <p>We start as usual by installing the required packages.</p>
50
51 <pre><code>dnf install freeipa-server freeipa-dns</code></pre>
52
53 <h2>Set Hostname</h2>
54 <p>The server will need to have a fully qualified hostname
55 before setting up IPA. You will need both a hostname for the server
56 itself and the domain name you will want for the FreeIPA domain. I
57 will be using <em>ipaserver.myhome.local</em>, where
58 <em>ipaserver</em> is the hostname and <em>myhome.local</em> is the
59 domain name.</p>
60
61 <pre><code>hostnamectl set-hostname <em>ipaserver.home.local</em></code></pre>
62
63 <p>We'll also need to add a hosts file entry to
64 <strong>/etc/hosts</strong>. Open that file in an editor and add a new
65 line with the IP of the server, the fully qualified name, and the
66 hostname.</p>
67
68 <pre><code>192.168.1.10 ipaserver.myhome.local ipaserver</code></pre>
69
70
71 <p>Make sure to reboot the server before continuing to complete
72 the hostname change.</p>
73
74 <h2>Firewall Configuration</h2>
75 <p>We'll need to allow several ports for FreeIPA to function properly.
76 Fedora 35 uses firewalld by default but I am going to disable that
77 in favor of UFW here.</p>
78
79 <pre><code><em>#Install UFW</em>
80dnf install ufw
81<em># Stop and disable firewalld</em>
82systemctl disable --now firewalld
83<em># Configure UFW</em>
84ufw enable
85ufw allow ssh
86ufw allow dns
87ufw allow 88 comment kerberos
88ufw allow 389 comment ldap
89ufw allow 443 comment webui
90ufw allow 636 comment ldaps
91ufw default deny incoming
92ufw reload</code></pre>
93
94 <h2>Configure FreeIPA</h2>
95 <p>Now we can run the FreeIPA setup script. This is an interactive but mostly
96 automatic process that will configure all of the IPA components. The
97 <strong>--mkhomedir</strong> flag will configure the server to create home
98 directories for IPA users on their first login and would otherwise have to be
99 done manually.</p>
100
101 <pre><code>ipa-server-install --mkhomedir</code></pre>
102
103 <p>That command will bring you into the install script. You will be prompted
104 several times before the bulk of the configuration happens. Default values
105 are show in brackets after the prompt. Let's run through those prompts.<br><br>
106 <strong>Do you want to configure integrated DNS (BIND)?</strong>:
107 <em>yes</em><br><br>
108 <strong>Sever host name</strong>: the default value should be showing
109 <em>ipaserver.myhome.local</em> which is what we want. Simply hit enter to acecpt
110 the default.<br><br>
111 <strong>Please confirm the domain name</strong>: The default here should be
112 correct <em>myhome.local</em> so hit enter to accept that.<br><br>
113 <strong>Please provide a realm name</strong>: This should just be the domain
114 name in all uppercase. If the default looks correct just hit enter.<br><br>
115 <strong>Directory Manager password</strong>: This is the password for an
116 administrator account used by system services. You will not need this for daily
117 use so I recommend setting it to a long randomly generated string. I have found
118 myself that using an extremely long password here will cause the installation to
119 fail. A password under 40 characters should be safe.<br><br>
120 <strong>IPA admin password</strong>: This is the password for your initial admin
121 user. Make this a strong password as this user has full admin rights for the
122 entire domain.<br><br>
123 <strong>Do you want to configure DNS forwarders</strong>: This allows you to
124 configure the IPA server to forward DNS requests to another DNS server for
125 zones it is not authoratitve for. The DNS server is configured by default as
126 a recursive DNS server so answering no does not prevent internet access. If you
127 have another DNS server that should be used instead then answer yes and provide
128 the IP address when prompted.<br><br>
129
130 <strong>Do you want to configure chrony with NTP server or pool address?</strong>
131 : Here you can configure a custom NTP server or pool for the NTP daemon chrony.
132 If you already have an NTP server on your network answer yes and provide its IP.
133 If you want to leave the deafult chrony configuration then answer no. Time
134 synchronization is very important in Kerberos so you should consider how you
135 want to achieve that on your network. If you do not have an NTP server you may
136 want to configure the IPA server as one later.<br><br>
137
138 <strong>Continue to configure the system with these values?</strong>: This is a
139 final confirmation before the script takes over and configures the IPA
140 components. Review the information printed and enter yes if it all looks correct.
141 </p>
142
143 <p>The install script will now run through configuration. This process usually
144 takes several minutes. When finished you should get a message saying
145 <strong>The ipa-server-install command was successful</strong>.</p>
146
147 <p>To finish, run this command to receive a Kerberos TGT. Provide the
148 password for the admin user when prompted.</p>
149
150 <pre><code>kinit admin</code></pre>
151
152 <h2>Accessing the Web Interface</h2>
153
154 <p>You are now able to manage FreeIPA through the web interface. You can
155 browse either to the IP or the hostname if your DNS is configured correctly.
156 You should see a screen similar to this.</p>
157
158 <img alt="FreeIPA Login Screen" src=../images/freeipa-webui.png>
159
160 <p>Login with the username admin and the password you set during the
161 insallation. You are now ready to begin configuring your IPA domain.</p>
162 </main>
163<p>
164<hr>
165Consider <a href=../donate.html>donating</a> if this article was useful.
166<a class=qr href=../images/bitcoin.png>[BTC]</a>
167</p>
168 </main>
169 <footer>
170 <a href=../kb.html>Knowledge Base</a>
171 <br>
172 <a href=../index.html>www.chudnick.com</a>
173 </footer>
174</body>
175</html>
176
diff --git a/articles/icinga-agent.html b/articles/icinga-agent.html
new file mode 100644
index 0000000..e0aa6c0
--- /dev/null
+++ b/articles/icinga-agent.html
@@ -0,0 +1,135 @@
1<!DOCTYPE html>
2<html lang=en>
3 <head>
4 <title></title>
5 <meta charset="utf-8"/>
6 <link rel="shortcut icon" href="favicon.ico"/>
7 <link rel='stylesheet' href='../style.css'/>
8 <meta name="viewport" content="width=device-width, initial-scale=1">
9 </head>
10<body>
11 <header><h1>Icinga Agent Node Installation and Configuration</h1></header>
12 <main>
13 <p>With the Icinga master node configured, the servers we want
14 to monitor can now be added as agent nodes. As the names suggest,
15 the Icinga master node pushes the desired configuration to agent
16 nodes, while agent nodes report the configured status checks back
17 to the master. Communication between the master and agent nodes is
18 encrypted via TLS, with the master node acting as a certificate
19 authority.</p>
20
21 <p>You can find my script to automate this process
22 <a href=https://git.chudnick.com/server-scripts/tree/monitoring/icinga-agent>
23 here</a>.</p>
24
25 <h2>Install Pakcages</h2>
26 <p>Start by installing the required packages on the server to be
27 monitored.</p>
28
29 <pre><code>apt install icinga2 monitoring-plugins
30monitoring-plugins-contrib</code></pre>
31
32 <h2>Initialize PKI with master</h2>
33 <p>Now we need to setup the PKI that will be used for the communication
34 with the master node. The first step is to generate a certificate
35 signing request. Replace <em>hostname</em> with the FQDN of the server.</p>
36
37 <pre><code>icinga2 pki new-cert --cn "<em>hostname</em>" --cert "/etc/icinga2/pki/<em>hostname</em>.crt" --csr "/etc/icinga2/pki/<em>hostname</em>.csr" --key "/etc/icinga2/pki/<em>hostname</em>.key"</code></pre>
38
39 <p>Next we save the master node's public key certificate. Replace
40 <em>master</em> with the FQDN of your master node.</p>
41
42 <pre><code>icinga2 pki save-cert --host "<em>master</em>" --port 5665 --key "/etc/icinga2/pki/<em>hostname</em>.key" --trustedcert "/etc/icinga2/pki/trusted-master.crt"</code></pre>
43
44 <p>Receive signed certificate from the master node.</p>
45
46 <pre><code>icinga2 pki request --host "<em>master</em>" --port 5665 --key "/etc/icinga2/pki/<em>hostname</em>.key" --cert "/etc/icinga2/pki/<em>hostname</em>.crt" --trustedcert "/etc/icinga2/pki/trusted-master.crt" --ca "/etc/icinga2/pki/ca.crt"</code></pre>
47
48 <h2>Deploy configuration files</h2>
49 <p>Write Icinga configuration.</p>
50
51 <pre><code><strong>/etc/icinga2/icinga2.conf</strong>
52include "constants.conf"
53const NodeName = "$nodename"
54include "zones.conf"
55include "features-enabled/*.conf"
56include &lt;itl&gt;
57include &lt;plugins&gt;
58include &lt;plugins-contrib&gt;
59include &lt;manubulon&gt;
60include &lt;windows-plugins&gt;
61include &lt;nscp&gt;"</code></pre>
62
63 <p>Write zones configuration.</p>
64
65 <pre><code><strong>/etc/icinga2/zones.conf</strong>
66echo "object Endpoint "<em>hostname</em>" {}
67object Zone "<em>hostname</em>" {
68 parent = "<em>master</em>"
69 endpoints = [ "<em>hostname</em>" ]
70}
71object Zone "<em>master</em>" {
72 endpoints = [ "<em>master</em>" ]
73}
74object Endpoint "<em>master</em>" {
75 host = "<em>master</em>"
76}
77object Zone "director-global" {
78 global = true
79}</code></pre>
80
81 <p>Write API configuration file.</p>
82
83 <pre><code><strong>/etc/icinga2/features-available/api.conf</strong>
84echo "object ApiListener \"api\" {
85 accept_commands = true
86 accept_config = true
87}</code></pre>
88
89 <h2>Enable API</h2>
90 <p>Next, we need to enable the API on the agent.</p>
91
92 <pre><code>icinga2 feature enable api
93
94mkdir -p /var/lib/icinga2/certs
95
96cp /etc/icinga2/pki/<em>hostname</em>.crt /etc/icinga2/pki/<em>hostname</em>.key /etc/icinga2/pki/ca.crt /var/lib/icinga2/certs/
97
98chown -R nagios: /var/lib/icinga2/certs/</code></pre>
99
100 <h2>Sign agent CSR on Master</h2>
101 <p>The only action needed on the master node is to sign the agent's
102 CSR. Logon to your master node and run the following:</p>
103
104 <pre><code>fpr="$(icinga2 ca list | tail -1 | cut -d '|' -f 1)"
105icinga2 ca sign $fpr</code></pre>
106
107 <h2>Configure Firewall</h2>
108 <p>Before finishing we need to open the proper firewall port.
109 I will use UFW in the example here and allow traffic only only
110 from the master node for best security.</p>
111
112 <pre><code>ufw allow proto tcp from <em>master-ip</em> to any port 5665</code></pre>
113
114 <h2>Restart Icinga on Agent</h2>
115 <p>Finally, restart the icinga service on the agent node.</p>
116
117 <pre><code>systemctl restart icinga2</code></pre>
118
119 <p>The Icinga agent node will now pull down configuration from the master.
120 You will know that this worked if <em>/var/lib/icinga2/api/zones</em>
121 begins to populate with new files.</p>
122<p>
123<hr>
124Consider <a href=../donate.html>donating</a> if this article was useful.
125<a class=qr href=../images/bitcoin.png>[BTC]</a>
126</p>
127 </main>
128 <footer>
129 <a href=../kb.html>Knowledge Base</a>
130 <br>
131 <a href=../index.html>www.chudnick.com</a>
132 </footer>
133</body>
134</html>
135
diff --git a/articles/icinga-director.html b/articles/icinga-director.html
new file mode 100644
index 0000000..3993949
--- /dev/null
+++ b/articles/icinga-director.html
@@ -0,0 +1,112 @@
1<!DOCTYPE html>
2<html lang=en>
3 <head>
4 <title></title>
5 <meta charset="utf-8"/>
6 <link rel="shortcut icon" href="favicon.ico"/>
7 <link rel='stylesheet' href='../style.css'/>
8 <meta name="viewport" content="width=device-width, initial-scale=1">
9 </head>
10<body>
11 <header><h1>Icinga Director</h1></header>
12 <main>
13 <p>Icinga Director is the web-based configuration tool for Icinga2.
14 Director provides a simple interface for configuring the various parts
15 of your monitoring environment. Even if you would rather do all of
16 the configuration from a terminal, I still recommend using Director for
17 its self-service API which allows new nodes to register with no interaction
18 required on the node (i.e. via a script).</p>
19
20 <p>Start by logging into your Icinga instance. If you followed the
21 <a href=icinga2-master.html>master installation guide</a> you should have
22 a tab labeled <strong>Icinga Director</strong>. Click on that, and you should
23 see something similar to this:</p>
24 <img src=../images/director/director.png>
25
26 <h2>Hosts</h2>
27 <p>Let's start by looking at the hosts section. Clicking on the hosts
28 button from the Director menu will present you with several options.
29 Click on host templates, and then click add to define our first host
30 template. Host templates are the building blocks of Icinga, they allow
31 for your nodes to be structured however you see fit, and then to have
32 monitoring checks automatically applied to them based on that structure.</p>
33 <p>I like to structure my host templates by operating system. For example,
34 I have a template called <strong>Linux Server</strong>, which is designed
35 to encompass all of the Linux servers in my environment. I then get more
36 specific, creating templates based on distro. These templates are children
37 of the Linux Server template, so they inherit whatever is applied to the
38 parent, but then can have distro-specific checks applied to themselves.</p>
39 <p>Start by giving your template a name - I will use <strong>Linux Server
40 </strong> here. Groups can be left empty for now, but you may want to
41 add groups and apply them to templates later. The check command should
42 be set to hostalive. Expand <strong>Icinga Agent and zone settings</strong>
43 and set <em>Icinga2 Agent</em>, <em>Establish connection</em>,
44 and <em>Accepts config</em> to Yes. Click store to save the template.
45 Your template should look like this:</p>
46
47 <img src=../images/director/hosttemplate.png>
48
49 <h2>Service templates</h2>
50 <p>Let's turn to services now. Return to the Director menu and select
51 Services. The services menu is structured similar to hosts, and we will
52 start with the service templates section. The idea behind service templates
53 is very similar to host templates. Typically, a service template corresponds
54 to a single monitoring command.</p>
55 <p>As an example, we'll create a service template for a monitoring
56 command that checks the status of a web server. Give the template a
57 name, set the check command to http, and finally expand <strong>Icinga
58 Agent and zone settings</strong> and set <strong>Run on agent
59 </strong> to no. We set this to no because we want Icinga to query
60 the web server externally instead of from the web server itself.</p>
61
62 <img src=../images/director/service-template.png>
63
64 <h2>Service sets</h2>
65 <p>Service sets are simply groups of service templates. They can be
66 structured however you see fit. Service sets can then be applied to
67 hosts/host templates to have the checks be automatically applied.</p>
68
69 <p>Add a new service set and give it a name. Then click on the services
70 tab and add all of the services you want to group into that set.
71 Here is an example of a service set <strong>Linux Standard</strong>
72 that has service checks that should be applied to all Linux servers.</p>
73
74 <img src=../images/director/service-set.png>
75
76 <p>To bring service sets and host templates together, return to your host
77 templates, select Linux Server, select the services tab, and then select
78 add service set and choose your desired set from the dropdown menu.</p>
79
80 <img src=../images/director/host-services.png>
81
82 <h2>Render your config</h2>
83 <p>When you have made all of the changes you need you will need to
84 render the Director configuration. Return to the Director menu,
85 select Config Deployment, and then select Render config.</p>
86
87 <h2>Self Service API</h2>
88 <p>In this last section we will look at what I think is the best feature
89 of Director which is the self service API. To enroll a host template
90 in the self service API, select the host template, select the agent
91 tab, and select generate self service api key. That's it!
92 The string of letters and numbers is the API key associated with this
93 host template. Hosts can be enrolled with this API key and Icinga
94 will automatically assign the host to this template. With proper
95 structuring, you can have hosts be completely provisioned without
96 touching Director. In the next article, we will use this to enroll
97 a host in Icinga with a shell script.</p>
98<p>
99<hr>
100Consider <a href=../donate.html>donating</a> if this article was useful.
101<a class=qr href=../images/bitcoin.png>[BTC]</a>
102</p>
103 </main>
104 <footer>
105 <a href=../kb.html>Knowledge Base</a>
106 <br>
107 <a href=../index.html>www.chudnick.com</a>
108 </footer>
109
110</body>
111</html>
112
diff --git a/articles/icinga-influx.html b/articles/icinga-influx.html
new file mode 100644
index 0000000..8d96c88
--- /dev/null
+++ b/articles/icinga-influx.html
@@ -0,0 +1,133 @@
1<!DOCTYPE html>
2<html lang=en>
3 <head>
4 <title></title>
5 <meta charset="utf-8"/>
6 <link rel="shortcut icon" href="favicon.ico"/>
7 <link rel='stylesheet' href='../style.css'/>
8 <meta name="viewport" content="width=device-width, initial-scale=1">
9 </head>
10<body>
11 <header><h1>Integrating InfluxDB and Icinga</h1></header>
12 <main>
13 <p>Icinga2 has built-in support for writing monitoring data to InfluxDB.
14 This makes Icinga quite extensible as it allows for other programs to read
15 the gathered data from InfluxDB. For example, Icinga does not have built-in
16 graphing support. But monitoring data can be written to InfluxDB and then
17 consumed by a dedicated graphing tool like Grafana.</p>
18
19 <h2>Install Packages</h2>
20 <p>Let's install a few necessary packages</p>
21
22 <pre><code>apt install influxdb influxdb-client ssl-cert</code></pre>
23
24 <h2>Generate self-signed certificate</h2>
25 <p>Now generate a self-signed certificate for accessing InfluxDB over TLS</p>
26
27 <pre><code>make-ssl-cert generate-default-snakeoil
28usermod -aG ssl-cert influxdb</code></pre>
29
30 <h2>Create Database and Users</h2>
31 <p>Start and enable the InfluxDB service</p>
32
33 <pre><code>systemctl enable --now influxdb</code></pre>
34
35 <p>Now we'll create our database and users. We'll be creating 3 users with
36 different access rights. An admin user with full control over the database, a
37 user with write access that Icinga will use to write data to the database, and
38 a read-only user to allow external programs to read data from the database.</p>
39
40 <pre><code>influx -ssl -unsafeSsl -execute "create database icinga2; create user admin with password '<em>changeme</em>'; create user icingauser with password '<em>changeme</em>'; create user readonly with password '<em>changeme</em>'; grant all to admin; grant write on icinga2 to icingauser; grant read on icinga2 to readonly;"
41</code></pre>
42
43<h2>Configuration Files</h2>
44
45 <p>Then write InfluxDB's configuration file at
46 <em>/etc/influxdb/influxdb.conf</em></p>
47
48 <pre><code>reporting-enabled = false
49[meta]
50 dir = "/var/lib/influxdb/meta"
51[data]
52 dir = "/var/lib/influxdb/data"
53 wal-dir = "/var/lib/influxdb/wal"
54[coordinator]
55[retention]
56[shard-precreation]
57[monitor]
58[http]
59 enabled = true
60 bind-address = ":8086"
61 auth-enabled = true
62 https-enabled = true
63 https-certificate = "/etc/ssl/certs/ssl-cert-snakeoil.pem"
64 https-private-key = "/etc/ssl/private/ssl-cert-snakeoil.key"
65[ifql]
66[logging]
67[subscriber]
68[[graphite]]
69[[collectd]]
70[[opentsdb]]
71[[udp]]
72[continuous_queries]
73[tls]
74 min-version = "tls1.2"</code></pre>
75
76 <p>And restart InfluxDB to pickup the changes</p>
77
78 <pre><code>systemctl restart influxdb</code></pre>
79
80 <p>Then we need to configure Icinga to write data to our database. Start by
81 enabling the influxdb feature in Icinga</p>
82
83 <pre><code>icinga2 feature enable influxdb</code></pre>
84
85 <p>Now we tell Icinga how to write to our database. Open the configuration
86 file at <em>/etc/icinga2/features-available/influxdb.conf</em> and replace
87 with the following</p>
88
89 <pre><code>object InfluxdbWriter \"influxdb\" {
90 host = "127.0.0.1"
91 port = 8086
92 username = "icingauser"
93 password = "<em>icinga_password</em>"
94 ssl_enable = true
95 database = "icinga2"
96 flush_threshold = 1024
97 flush_interval = 10s
98 host_template = {
99 measurement = "$host.check_command$"
100 tags = {
101 hostname = "$host.name$"
102 }
103 }
104 service_template = {
105 measurement = "$service.check_command$"
106 tags = {
107 hostname = "$host.name$"
108 service = "$service.name$"
109 }
110 }
111}</code></pre>
112
113 <p>And finally restart Icinga to make those changes live.</p>
114
115 <pre><code>systemctl restart icinga2</code></pre>
116
117 <p>Icinga2 will now be writing the data it collects to your InfluxDB instance.</p>
118
119 </main>
120<p>
121<hr>
122Consider <a href=../donate.html>donating</a> if this article was useful.
123<a class=qr href=../images/bitcoin.png>[BTC]</a>
124</p>
125 </main>
126 <footer>
127 <a href=../kb.html>Knowledge Base</a>
128 <br>
129 <a href=../index.html>www.chudnick.com</a>
130 </footer>
131</body>
132</html>
133
diff --git a/articles/icinga-master.html b/articles/icinga-master.html
new file mode 100644
index 0000000..0cafcdd
--- /dev/null
+++ b/articles/icinga-master.html
@@ -0,0 +1,276 @@
1<!DOCTYPE html>
2<html lang=en>
3 <head>
4 <title></title>
5 <meta charset="utf-8"/>
6 <link rel="shortcut icon" href="favicon.ico"/>
7 <link rel='stylesheet' href='../style.css'/>
8 <meta name="viewport" content="width=device-width, initial-scale=1">
9 </head>
10<body>
11<header><h1>Icinga2 Master Installation</h1></header>
12<main>
13<p>
14This tutorial will cover the installation of the Icinga2
15monitoring application master node. This includes the base
16program, the web frontend, and the web-based configuration tool.
17This guide was made for Debian but should be similar
18on other distributions.
19</p>
20<p>
21I have a script available to automate the steps described in this
22tutorial available
23<a href=https://git.chudnick.com/server-scripts/tree/monitoring/icinga-master>from my git repo</a>.
24<h2>Install Packages</h2>
25<p>Here we will install the required packages. Icinga can use either MySQL
26or PostgreSQL, however this tutorial will use MySQL/MariaDB.</p>
27<pre><code>apt install icinga2 icingaweb2 icinga2-ido-mysql icingaweb2-module-director monitoring-plugins monitoring-plugins-contrib default-mysql-server</code></pre>
28<h2>Secure MySQL</h2>
29<p>This step is optional but strongly recommended.
30The mysql_secure_installation script will harden your MySQL instance.</p>
31<pre><code>mysql_secure_installation</code></pre>
32<p>I recommend the following responses:
33<ul>
34 <li><em>Switch to unix_socket authentication?</em><strong> Y</strong></li>
35 <li><em>Change the root password?</em><strong> Y</strong></li>
36 <li><em>Remove anonymous users?</em><strong> Y</strong></li>
37 <li><em>Disallow root login remotely?</em><strong> Y</strong></li>
38 <li><em>Remove the test database and access to it?</em><strong> Y</strong></li>
39 <li><em>Reload privilege tables now?</em><strong> Y</strong></li>
40</ul>
41</p>
42
43<h2>Create Monitoring Database</h2>
44<p>The next several sections will cover creating databases for the various
45parts of Icinga. We'll start with the monitoring database.
46The following command creates a MySQL database named <em>icinga2</em>
47and grants permissions to a user named <em>ido_admin</em>. These values
48are arbitrary, but I use them throughout the tutorial so I recommend leaving them
49as is. You should definitely change the password though, which in the command
50is <em>change me</em>. You will need this password and the passwords for the
51other databases later, so make sure you save them.</p>
52<pre><code>mysql -u root -e "CREATE DATABASE icinga2; GRANT SELECT, INSERT, UPDATE, DELETE, DROP, CREATE VIEW, INDEX, EXECUTE ON icinga2.* TO <em>ido_admin</em>@'localhost' IDENTIFIED BY '<em>change me</em>'; FLUSH PRIVILEGES;</code></pre>
53
54<p>We then need to import the ido schema into the database.</p>
55
56<pre><code>mysql -u root icinga2 &lt;/usr/share/icinga2-ido-mysql/schema/mysql.sql</code></pre>
57
58<p>After importing the schema, we then write the configuration file that tells
59the monitoring module how to connect to the database.</p>
60<pre><code><strong>/etc/icinga2/features-available/ido-mysql.conf</strong>
61library "db_ido_mysql"
62object IdoMysqlConnection "ido-mysql" {
63 user = "ido_admin",
64 password = "<em>ido_password</em>",
65 host = "localhost",
66 database = "icinga2"
67}"</code></pre>
68
69<p>And finally we enable the monitoring module in Icinga.</p>
70<pre><code>icinga2 feature enable ido-mysql</code></pre>
71
72<h2>Create Icingaweb2 Database</h2>
73<p>This step is nearly identical to the last. This time we create a database
74named <em>icingaweb2</em> and grant permissions to the user named
75<em>icingaweb2_admin</em>.</p>
76<pre><code>mysql -u root -e "CREATE DATABASE icingaweb2;GRANT ALL ON icingaweb2.* TO 'icingaweb2_admin'@'localhost' IDENTIFIED BY '<em>changeme</em>'; FLUSH PRIVILEGES;</code></pre>
77
78<p>Again we will need to import required schema into the database.</p>
79<pre><code>mysql -u root icingaweb2 &lt;/usr/share/icingawbe2/etc/schema/mysql.schema.sql</code></pre>
80
81
82<p>In this step we create the initial admin user that will be used to login
83to the web interface. As is, this would create a user named <em>admin</em>
84with the password <em>changme</em>. You should at least change the password.</p>
85<pre><code>passhash="$(php -r "echo password_hash(\"<em>changeme</em>\", PASSWORD_DEFAULT);")"
86mysql -u root -e "USE icingaweb2; INSERT INTO icingaweb_user (name, active, password_hash) VALUES (\"<em>admin</em>\", 1, \"$passhash\"); FLUSH PRIVILEGES;"</code></pre>
87
88<h2>Create Icinga Director Database</h2>
89<p>Here we create the database for Director. Director will require more
90configuration later, so for now we will just be creating the database.</p>
91<pre><code>mysql -u root -e "CREATE DATABASE director CHARACTER SET 'utf8'; GRANT ALL on director.* TO 'director'@'localhost' IDENTIFIED BY '$director_password';FLUSH PRIVILEGES;"</code></pre>
92
93<h2>Setup Icinga2 API</h2>
94<p>Run the following command to initialize the Icinga API.</p>
95<pre><code>icinga2 api setup</code></pre>
96<p>And then restart Icinga to apply the changes.</p>
97<pre><code>systemctl restart icinga2</code></pre>
98
99<h2>Configure Web Server</h2>
100<p>In this section we will configure the web server for accessing
101Icinga's web interface and Director configuration tool.
102This tutorial will use nginx but apache could be used as well.
103We'll start by installing the necessary packages.</p>
104<pre><code>apt install nginx php-fpm</code></pre>
105<p>Then we need to create the site configuration file.<p>
106<pre><code><strong>/etc/nginx/sites-available/icingaweb2.conf</strong>
107server {
108 listen 80;
109 server_name <em>monitoring.example.com</em>
110 location ~ ^/icingaweb2/index\.php(.*)$ {
111 fastcgi_pass unix:/var/run/php/php-fpm.sock;
112 fastcgi_index index.php;
113 include fastcgi_params;
114 fastcgi_param SCRIPT_FILENAME /usr/share/icingaweb2/public/index.php;
115 fastcgi_param ICINGAWEB_CONFIGDIR /etc/icingaweb2;
116 fastcgi_param REMOTE_USER $remote_user;
117 }
118
119 location ~ ^/icingaweb2(.+)? {
120 alias /usr/share/icingaweb2/public;
121 index index.php;
122 try_files $1 $uri $uri/ /icingaweb2/index.php$is_args$args;
123 }
124
125 <em># Not strictly necessary but allows you to get to icinga without
126 # specifying /icingaweb2 in the URL.</em>
127 location = / {
128 return 302 http://$host/icingaweb2;
129 }
130
131}</code></pre>
132<p>And then restart nginx to pick up the changes.</p>
133<pre><code>systemctl restart nginx</code></pre>
134
135<p>At this point we are done with the Icinga setup module and so we
136can disable it.</p>
137<pre><code>icingacli module disable setup</code></pre>
138
139<h2>Write Configuration Files</h2>
140<p>In this section we will write several configuration files. Icinga uses
141the INI format for its web interface configuration files.</p>
142<p>In this first file we tell Icinga about the various resources it should have
143access to. These resources are the three databases created previously.
144Replace the password in each section with the corresponding password you set
145for that database earlier.</p>
146<pre><code><strong>/etc/icingaweb2/resources.ini</strong>
147[icinga2]
148type = "db"
149db = "mysql"
150host = "localhost"
151port = ""
152dbname = "icinga2"
153username = "ido_admin"
154password = "<em>ido password</em>"
155charset = ""
156use_ssl = "0"
157
158[icingaweb2]
159type = "db"
160db = "mysql"
161host = "localhost"
162port = ""
163dbname = "icingaweb2"
164username = "icingaweb2_admin"
165password = "<em>ido password</em>"
166charset = ""
167use_ssl = "0"
168
169
170[director]
171type = "db"
172db = "mysql"
173host = "localhost"
174port = ""
175dbname = "director"
176username = "director"
177password = "<em>director password</em>"
178charset = "utf8"
179use_ssl = "0"
180</code></pre>
181
182<p>This file controls the authentication settings for the web interface.
183Here we tell Icinga to look at the icingaweb2 database for
184authentication purposes.</p>
185<pre><code><strong>/etc/icingaweb2/authentication.ini</strong>
186[icingaweb2]
187backend = "db"
188resource = "icingaweb2"</code></pre>
189
190<p>Now we tell icinga which users should have admin permissions.
191If you changed the username value from <em>admin</em> previously, be sure to update
192it here.</p>
193<pre><code><strong>/etc/icingaweb2/roles.ini</strong>
194[admins]
195users = "<em>admin</em>"
196resource = "icingaweb2"</code></pre>
197
198<p>Enable the web interface monitoring module.</p>
199<pre><code>icingacli module enable monitoring</code></pre>
200<p>Then write the configuration file pointing the monitoring module to the
201monitoring database.</p>
202<pre><code><strong>/etc/icingaweb2/modules/monitoring/backends.ini</strong>
203[icinga]
204type = "ido"
205resource = "icinga2"</code></pre>
206
207<p>Here we configure Icinga to use the API for communication.
208You will need to get your unique API password generated during the API setup from
209from <strong>/etc/icinga2/conf.d/api-users.conf</strong>.
210<em>hostname</em> should be the FQDN of the server.</p>
211<pre><code><strong>/etc/icingaweb2/modules/monitoring/commandtransports.ini</strong>
212[icinga2]
213transport = "api"
214host = <em>hostname</em>
215port = "5665"
216username = "root"
217password = "<em>api password</em>"</code></pre>
218
219<p>Lastly, tell Icinga to protect variables with potentially sensitive values.</p>
220<pre><code><strong>/etc/icingaweb2/modules/monitoring/config.ini</strong>
221[security]
222protected_customvars = "*pw*,*pass*,*community*"</code></pre>
223
224
225<h2>Configure Director</h2>
226<p>This section will cover configuring Director configuration tool.</p>
227<p>Create Director module configuration directory.</p>
228<pre><code>mkdir -p /etc/icingaweb2/modules/director</code></pre>
229
230<p>Write the Director configuration file.</p>
231<pre><code><strong>/etc/icingaweb2/modules/director/config.ini</strong>
232[db]
233resource = "director"</code></pre>
234
235<p>Enable Director module and run the initial migration.</p>
236<pre><code>icingacli module enable director
237icingacli director migration run</code></pre>
238
239<p>Write Director kickstart configuration file.</p>
240<pre><code><strong>/etc/icingaweb2/modules/director/kickstart.ini</strong>
241[config]
242endpoint = "<em>hostname</em>"
243username = "root"
244password = "<em>api password</em>"</code></pre>
245
246<p>Kickstart Director, then render and deploy the configuration.</p>
247<pre><code>icingacli director kickstart run
248icingacli director config render
249icingacli director config deploy</code></pre>
250
251<p>Director is setup at this point so we will shred the unneeded configuration
252file containing sensitive information.</p>
253<pre><code>shred -uz /etc/icingaweb2/modules/director/kickstart.ini</code></pre>
254
255<h2>Login to your Monitoring Instance</h2>
256<p>You are now ready to login to your monitoring instance with the admin
257user created previously. Open a web browser and go to
258http://<em>hostname</em>/icingaweb2. You should see a screen similar to this:</p>
259<a href=../images/icinga-login.png><img src=../images/icinga-login.png alt="Icinagweb2 Login Screen"></a>
260
261<h2>Next Steps</h2>
262<p>In the following articles we will go through setting up Icinga2 agents on servers, and configure your monitoring instance through Icinga Director.</p>
263<p>
264<hr>
265Consider <a href=../donate.html>donating</a> if this article was useful.
266<a class=qr href=../images/bitcoin.png>[BTC]</a>
267</p>
268 </main>
269 <footer>
270 <a href=../kb.html>Knowledge Base</a>
271 <br>
272 <a href=../index.html>www.chudnick.com</a>
273 </footer>
274</body>
275</html>
276
diff --git a/articles/luks.html b/articles/luks.html
new file mode 100644
index 0000000..d2bbc5e
--- /dev/null
+++ b/articles/luks.html
@@ -0,0 +1,132 @@
1<!DOCTYPE html>
2<html lang=en>
3 <head>
4 <title></title>
5 <meta charset="utf-8"/>
6 <link rel="shortcut icon" href="favicon.ico"/>
7 <link rel='stylesheet' href='../style.css'/>
8 <meta name="viewport" content="width=device-width, initial-scale=1">
9 </head>
10<body>
11 <header><h1>LUKS Block Device Encryption</h1></header>
12 <main>
13 <p>Linux Unified Key Setup (LUKS) is a method for encrypting
14 block devices that is built-in to the Linux Kernel. In this tutorial
15 I will be showing how to create an encrypted USB drive with LUKS, but
16 this process is applicable to other types of storage.
17 <strong>This process will wipe all data on the device that you
18 encrypt so make backups beforehand if needed</strong>.</p>
19
20 <h2>Install Packages</h2>
21 <p>Install cryptsetup and its dependencies</p>
22
23 <pre><code>apt install cryptsetup</code></pre>
24
25 <h2>Prepare the Drive</h2>
26 <p>If the device you are encrypting was previously used, you will
27 want to completely overwrite any data on it before encryption. This
28 can be done using <strong>dd</strong> as shown below. This could take
29 a very long time depending on the size and type of your drive.
30 Be sure that you enter the correct path to your drive as the
31 argument to <strong>of</strong>, and do not specify a partition:
32 <strong>/dev/sdb</strong> is correct, <strong>/dev/sdb1</strong> is
33 wrong. Obviously this will destroy any data on the device so be
34 absolutely sure you specify the correct device in the command
35 and have backups of data previously stored if needed.</p>
36
37 <pre><code>dd if=/dev/zero of=<em>/dev/sdX</em> status=progress</code></pre>
38
39 <h2>Encrypt the Drive</h2>
40 <p><strong>cryptsetup</strong> is the main command used to perform
41 LUKS-related tasks. This main command is followed by a subcommand
42 that specifies which action to perform. To create a LUKS partition
43 on a drive we need to use the subcommand <strong>luksFormat</strong>.
44 We also use the type option to explicitly say to use LUKS version 2
45 encryption. Run the command and then enter and confirm the
46 encryption password.</p>
47
48 <pre><code>cryptsetup --type luks2 luksFormat <em>/dev/sdX</em></code></pre>
49
50 <p>Now we need to open and map the encrypted LUKS device. This is done
51 by using the <strong>luksOpen</strong> subcommand. This subcommand takes
52 the device as the first argument and then a map target as the second. The
53 map target can be any string as long as a map does not already exist with
54 that name. In the example I will use <em>crypt</em>.
55
56 <pre><code>cryptsetup luksOpen <em>/dev/sdX crypt</em></code></pre>
57
58 <p>And then we just need to create a filesystem on the device; I will
59 be making an ext4 filesystem in the example. To
60 interact with an encrypted drive after it has been opened you need
61 to refer to its device mapper target, which is found under /dev/mapper.
62 Since we used <em>crypt</em> as the map target, our encrypted drive
63 is /dev/mapper/<em>crypt</em>. </p>
64
65 <pre><code>mkfs.ext4 /dev/mapper/<em>crypt</em></code></pre>
66
67 <p>After creating the filesystem created the encrypted drive
68 can now be mounted and used like any other device. Remember that
69 when mounting the device you refer to the device mapper target
70 (mount /dev/mapper/<em>crypt</em> /mnt/crypt).</p>
71
72 <p>In addition to unmounting the filesystem you should always close
73 the device mapping before removing your encrypted drive. This is
74 done with the <strong>luksClose</strong> subcommand and takes
75 the device mapping as the argument.</p>
76
77 <pre><code>cryptsetup luksClose /dev/mapper/<em>crypt</em></code></pre>
78
79 <h2>The LUKS Header</h2>
80 <p>The LUKS header sits at the front of your encrypted drive and is
81 responsible for managing access to the device. If the header is
82 damaged accessing your encrypted data will not be possible. Because of
83 this, you may want to make backups of the header, but before doing so
84 you need to weigh the risks and benefits.</p>
85
86 <p>The obvious benefit is that
87 in the event of damage to the LUKS header, you can easily restore a
88 backup and regain access to the data. However, having a backup of the
89 header makes it harder to wipe the LUKS device. Without a header
90 backup, overwriting the LUKS header on the device is enough to securely
91 wipe the drive. With backups, you need to either destroy all header
92 backups or overwrite all encrypted data on the device. The other main risk
93 is that an encryption password that is valid at the time you make a
94 backup will always be valid for that backup. For example, say you change
95 the encryption password on your device after making a backup because it
96 has been compromised. An attacker would be able to restore the header
97 backup and then use the compromised password to access the data.</p>
98
99 <p>The cryptsetup documentation refers to creating header backups
100 as making a trade-off between safety and security. You need to make a
101 decision based on your individual use-case. It's a good idea to
102 routinely update your header backups if you do choose to make them</p>
103
104 <p>When creating or restoring a header backup you always refer to the
105 block device in the command, not the device mapper taget. To create
106 a header backup:</p>
107
108 <pre><code>cryptsetup luksHeaderBackup <em>/dev/sdX</em> --header-backup-file <em>path/to/backup</em></code></pre>
109
110 <p>To restore a header from a backup:</p>
111
112 <pre><code>cryptsetup luksHeaderRestore <em>/dev/sdX</em> --header-backup-file <em>path/to/backup</em></code></pre>
113
114 <p>Note that is not necessary to store header backups on an encrypted
115 device. I would recommend storing backups on at least one non-encrypted
116 drive in case of an emergency.</p>
117
118 </main>
119<p>
120<hr>
121Consider <a href=../donate.html>donating</a> if this article was useful.
122<a class=qr href=../images/bitcoin.png>[BTC]</a>
123</p>
124 </main>
125 <footer>
126 <a href=../kb.html>Knowledge Base</a>
127 <br>
128 <a href=../index.html>www.chudnick.com</a>
129 </footer>
130</body>
131</html>
132
diff --git a/articles/mail-server.html b/articles/mail-server.html
new file mode 100644
index 0000000..12a3264
--- /dev/null
+++ b/articles/mail-server.html
@@ -0,0 +1,723 @@
1<!DOCTYPE html>
2<html lang=en>
3<head>
4<title></title>
5<meta charset="utf-8"/>
6<link rel="shortcut icon" href="favicon.ico"/>
7<link rel='stylesheet' href='../style.css'/>
8<meta name="viewport" content="width=device-width, initial-scale=1">
9</head>
10<body>
11<header><h1>Postfix and Dovecot Mail Server</h1></header>
12<main>
13
14<p>Postfix and dovecot will be the two primary pieces of our mail sever.
15Postfix is the mail transport agent that handles the sending and
16receiving of mail and dovecot is the IMAP server that will allow us to
17access our mail from a mail client such as mutt. The server will also
18have several other supporting components, a complete list of which is:</p>
19
20<ul>
21 <li><em>SpamAssassin</em> for spam filtering</li>
22 <li><em>OpenDKIM</em> for DKIM verification and signing</li>
23 <li><em>Postgrey</em> for greylisting</li>
24 <li><em>Policyd-SPF</em> for SPF verification</li>
25 <li><em>OpenDMARC</em> for DMARC verification</li>
26</ul>
27
28<p>You can use <a href=https://git.chudnick.com/mail-tools/tree/server/deploy-server>
29this script</a> I have written to automate this process, but I would
30recommend that you run through the tutorial first to understand
31what is being done.</p>
32
33<p>Please note that this tutorial is loosely intended for small personal mail
34servers. Using PAM for authentication, as is done here, is not a scalable solution
35for working with a large number of users. I do plan on covering Dovecot LDAP
36authentication at some point which would be a better solution in an enterprise
37setting.</p>
38
39<h2>Install Packages</h2>
40<p>Let's start by installing the required packages. Note that if you already
41have Apache installed on the server, replace <em>python3-certbot-nginx</em>
42with <em>python3-certbot-apache</em>.</p>
43<pre><code>apt install postfix dovecot-imapd dovecot-sieve opendkim opendkim-tools spamassassin gnupg postgrey postfix-policyd-spf-python opendmarc dbconfig-no-thanks certbot python3-certbot-nginx</code></pre>
44<p>During the installation of Postfix you will get a Debconf prompt in which
45you need to select "Internet Site" and then provide your domain name,
46<strong>example.com</strong>.</p>
47
48<h2>Get a certificate</h2>
49<p>Now we'll use Certbot to get a certificate for our server. If you are
50using Apache replace <em>nginx</em> with <em>apache2</em>.</p>
51
52<pre><code>systemctl stop nginx
53certbot certonly --standalone -d <strong>mail.example.com</strong>
54systemctl start nginx</code></pre>
55
56<h2>Postfix Main Configuration</h2>
57<p>In this section we will be doing the bulk of the postfix configuration.
58The postconf command used throughout appends (or changes)
59the specified configuration item in /etc/postfix/main.cf</p>
60
61<h3>Network Configuration</h3>
62
63<p>Let's start by configuring some network and domain information.</p>
64
65<pre><code>postconf -e "myorigin = <strong>example.com</strong>"
66postconf -e "mydestination = \$myhostname, \$mydomain, localhost"
67postconf -e "mynetworks = 127.0.0.0/8 [::1]/128"
68postconf -e "myhostname = <strong>mail.example.com</strong>"
69</code></pre>
70
71<p>Next, point postfix to the cerbot key and certificate, as well as the distro's
72CA certificates.</p>
73
74<pre><code>postconf -e "smtpd_tls_key_file=/etc/letsencrypt/live/<strong>mail.example.com</strong>/privkey.pem"
75postconf -e "smtpd_tls_cert_file=/etc/letsencrypt/live/<strong>mail.example.com</strong>/fullchain.pem"
76postconf -e "smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt"
77</code></pre>
78
79<p>Harden the TLS configuration by forcing strong
80protocols and ciphers, and requiring that authentication occur only over an
81encrypted session.</p>
82<pre><code><em># Require authentication over TLS and optionally use it for sending and receiving mail</em>
83postconf -e "smtpd_tls_auth_only = yes"
84postconf -e "smtpd_tls_security_level = may"
85postconf -e "smtp_tls_security_level = may"
86
87<em># Force the use of TLSv1.2 or TLSv1.3</em>
88postconf -e 'smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1'
89postconf -e 'smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1'
90postconf -e 'smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1'
91postconf -e 'smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1'
92
93<em># Prefer server ciphers</em>
94postconf -e "tls_preempt_cipherlist = yes"
95
96<em># Force strong ciphers</em>
97postconf -e "smtpd_tls_ciphers = high"
98postconf -e "smtpd_tls_mandatory_ciphers = high"
99postconf -e "smtp_tls_ciphers = high"
100postconf -e "smtp_tls_mandatory_ciphers = high"
101postconf -e "smtpd_tls_exclude_ciphers = aNULL, eNULL, EXP, LOW, MEDIUM, PSK, SRP, SHA1, kRSA, CAMELLIA, ARIA, DSS, RSA+AES, ADH, AECDH"
102postconf -e "smtp_tls_exclude_ciphers = aNULL, eNULL, EXP, LOW, MEDIUM, PSK, SRP, SHA1, kRSA, CAMELLIA, ARIA, DSS, RSA+AES, ADH, AECDH"
103</code></pre>
104
105<h3>Local Recipients and Aliases</h3>
106
107<p>Here we configure the bulk of the postfix built-in security settings which are
108structured as a series of access restrictions. Do not edit these settings without
109first reading the Postfix documentation as an incorrect change could inadvertently
110make your server an open relay.</p>
111
112<pre><code>postconf -e "smtpd_helo_required = yes"
113postconf -e "smtpd_sender_login_maps = proxy:hash:/etc/postfix/login_maps"
114postconf -e "smtpd_helo_restrictions = reject_unknown_helo_hostname, reject_non_fqdn_helo_hostname"
115postconf -e "smtpd_sender_restrictions = reject_sender_login_mismatch, reject_non_fqdn_sender, reject_unknown_sender_domain"
116postconf -e "smtpd_recipient_restrictions = reject_non_fqdn_recipient, reject_unknown_recipient_domain, permit_sasl_authenticated, reject_unauth_destination, check_policy_service unix:private/postgrey, check_policy_service unix:private/policyd-spf, reject_rbl_client zen.spamhaus.org"
117postconf -e "smtpd_relay_restrictions = permit_sasl_authenticated, reject_unauth_destination"
118postconf -e "smtpd_data_restrictions = reject_unauth_pipelining"
119
120<em># Disable VRFY command to prevent harvesting of user accounts on system</em>
121postconf -e "disable_vrfy_command = yes"
122
123<em># Change smptd banner (hide distribution)</em>
124postconf -e "smtpd_banner = \$myhostname ESMTP \$mail_name"</code></pre>
125
126<p>Now, configure the local mail recipients and some aliases. We'll create
127an account called <strong>mailadmin</strong> to receive mail addressed to
128several other accounts. This is to keep <em>administrative</em> mail separate, but
129you can certainly alias these to your main account later if you would prefer to see
130it there.</p>
131
132<pre><code><em># Set a custom local_recipient_maps here in order to avoid accepting mail for all local accounts</em>
133postconf -e "local_recipient_maps = proxy:hash:/etc/postfix/local_maps \$alias_maps"
134
135<em># You will need to manually set a password later to login as mailadmin</em>
136adduser --disabled-login --shell /usr/sbin/nologin --gecos "" mailadmin
137echo "# postfix aliases
138postmaster: mailadmin
139root: mailadmin
140dmarc: mailadmin
141" &gt; /etc/aliases
142
143<em># Update address databases</em>
144echo "mailadmin@<em>mail.example.com</em> mailadmin" &gt; /etc/postfix/login_maps
145echo "mailadmin mailadmin" &gt; /etc/postfix/local_maps
146newaliases
147postmap /etc/postfix/login_maps
148postmap /etc/postfix/local_maps
149</code></pre>
150
151<h3>Mail Delivery</h3>
152
153
154<p>These commands configure our mail delivery preferences. Mail will be
155delivered inside a user's home folder with a maildir-style mailbox using
156dovecot.</p>
157
158<pre><code><em># Maildir delivery to $HOME/Mail/Inbox/</em>
159postconf -e "home_mailbox = Mail/Inbox/"
160<em># Deliver mail with Dovecot</em>
161postconf -e "mailbox_command = /usr/lib/dovecot/deliver"</code></pre>
162
163<h3>Header and Body Checks</h3>
164
165<p>Header and body checks allow for some simple content filtering within Postfix.
166This is done by scanning a message line by line for a configured regex string,
167nothing more. For example, the first header check listed will reject a message
168with an attachment of <em>ransomware.exe</em> but will not block it if sent with
169no extension. This is mostly a protection against uneducated users and poorly
170written mail clients. Other checks block vulnerabilities and improve privacy.</p>
171
172<p>Create a new file <strong>/etc/postfix/header_checks</strong>, then open it in a
173text editor and add the following</p>
174<pre><code><em># Block files with common executable extensions</em>
175/name=[^&gt;]*\.(exe|pif|com|dll|vbs|bat|sh|bash|so|zip|tar|gz|cpio)/ REJECT
176
177<em># Block message/partial vulnerability</em>
178/message\/partial/ REJECT
179
180<em># Remove Received string that is created when spamassassin reinjects message into postfix</em>
181<em># This is to prevent leaking the userid of the spamassassin user</em>
182/^Received:.*userid.*/ IGNORE
183
184<em># Remove User-Agent strings from headers</em>
185/^User-Agent: .*/ IGNORE</code></pre>
186
187<p>Create another new file <strong>/etc/postfix/body_checks</strong>, and add this</p>
188<pre><code><em># Block messages with iframes</em>
189/&lt;iframe/ REJECT" &gt; /etc/postfix/body_checks</code></pre>
190
191<p>And then run these commands to point postfix to the check files.</p>
192<pre><code>postconf -e "header_checks = regexp:/etc/postfix/header_checks"
193postconf -e "body_checks = regexp:/etc/postfix/body_checks"</code></pre>
194
195<h2>Postfix Master Configuration</h2>
196<h3>SMTP client</h3>
197<p>This simple command configures the SMTP client process that is responsible
198for sending your mail to other mail servers.</p>
199
200<pre><code>postconf -M "smtp/unix=smtp unix - - y - - smtp"</code></pre>
201
202<h3>Postscreen and SMTP Recipient</h3>
203<p>Postscreen is a kind of firewall that sits in front of the Postfix SMTPD
204process and receives all incoming traffic. Postscreen will drop connections
205from IPs on a DNS blacklst, or from clients that violate the SMTP protocol by
206speaking out of turn or sending non-SMTP commands. This adds up to less spam
207connections and therefore a much lighter workload for your server.</p>
208
209<pre><code>postconf -M "smtp/inet=smtp inet n - y - 1 postscreen"
210postconf -M "smtpd/pass=smtpd pass - - y - - smtpd"
211postconf -P "smtpd/pass/content_filter=spamassassin"
212postconf -M "tlsproxy/unix=tlsproxy unix - - y - 0 tlsproxy"
213postconf -M "dnsblog/unix=dnsblog unix - - y - 0 dnsblog"
214postconf -e "postscreen_dnsbl_sites = zen.spamhaus.org"
215postconf -e "postscreen_dnsbl_action = enforce"
216postconf -e "postscreen_greet_action = enforce"
217</code></pre>
218
219<h3>Submission over TLS (submissions)</h3>
220<p>Submission over TLS (aka submissions) is the process you will use to submit
221mail to your server from a mail client. These commands configure submissions to
222use a fully-encrypted session, as opposed to STARTTLS, and to only allow access
223to authenticated clients.</p>
224
225<pre><code>postconf -M "submissions/inet=submissions inet n - y - - smtpd"
226postconf -P "submissions/inet/smtpd_tls_wrappermode=yes"
227postconf -P "submissions/inet/smtpd_tls_security_level=encrypt"
228postconf -P "submissions/inet/smtpd_tls_auth_only=yes"
229postconf -P "submissions/inet/smtpd_sasl_auth_enable=yes"
230postconf -P "submissions/inet/smtpd_client_restrictions=permit_sasl_authenticated,reject"
231postconf -P "submissions/inet/smtpd_helo_restrictions="
232postconf -P "submissions/inet/smtpd_sender_restrictions=reject_sender_login_mismatch"
233postconf -P "submissions/inet/smtpd_recipient_restrictions=reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject"
234postconf -P "submissions/inet/syslog_name=postfix/submissions"
235postconf -P 'submissions/inet/smtpd_tls_protocols=!SSLv2,!SSLv3,!TLSv1,!TLSv1.1'
236postconf -P 'submissions/inet/smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3,!TLSv1,!TLSv1.1'</code></pre>
237
238<h3>OPTIONAL - submission with mandatory STARTTLS</h3>
239<p>Having configured submission over TLS on port 465 this step is optional.
240STARTTLS is considered by some to be less secure than full-session TLS and
241may be vulnerable to exploitation.</p>
242
243<pre><code>postconf -M "submission/inet=submission inet n - y - - smtpd"
244postconf -P "submission/inet/smtpd_tls_security_level=encrypt"
245postconf -P 'submission/inet/smtpd_tls_protocols=!SSLv2,!SSLv3,!TLSv1,!TLSv1.1'
246postconf -P 'submission/inet/smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3,!TLSv1,!TLSv1.1'
247postconf -P "submission/inet/smtpd_sasl_auth_enable=yes"
248postconf -P "submission/inet/smtpd_tls_auth_only=yes"
249postconf -P "submission/inet/syslog_name=postfix/submission"
250postconf -P "submission/inet/smtpd_helo_restrictions="
251postconf -P "submission/inet/smtpd_client_restrictions=permit_sasl_authenticated,reject"
252postconf -P "submission/inet/smtpd_helo_restrictions="
253postconf -P "submission/inet/smtpd_sender_restrictions=reject_sender_login_mismatch"
254postconf -P "submission/inet/smtpd_recipient_restrictions=reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject"
255</code></pre>
256
257<h3>SpamAssassin Configuration</h3>
258<p>Finally, this command tells Postfix how to interact with SpamAssassin.</p>
259
260<pre><code>postconf -M "spamassassin/unix=spamassassin unix - n n - - pipe user=debian-spamd argv=/usr/bin/spamc --socket=/var/run/spamd.sock -e /usr/sbin/sendmail -oi -f \${sender} \${recipient}" </code></pre>
261
262<h2>Dovecot Configuration</h2>
263<p>Dovecot configuration is usually split up into many different files under
264<strong>/etc/dovecot/conf.d/</strong> but here will be doing all of the
265configuration in the primary config file
266<strong>/etc/dovecot/dovecot.conf</strong>. Open that file with your editor
267of choice, clear all of its contents, and then replace it with the following.</p>
268
269<pre><code><em># /etc/dovecot/conf.d/10-auth.conf</em>
270disable_plaintext_auth = yes
271auth_username_format = %n
272auth_mechanisms = plain
273userdb {
274 driver = passwd
275}
276passdb {
277 driver = pam
278}
279
280<em># /etc/dovecot/conf.d/10-mail.conf</em>
281mail_location = maildir:~/Mail:INBOX=~/Mail/Inbox:LAYOUT=fs
282namespace inbox {
283 type = private
284 prefix =
285 separator = /
286 inbox = yes
287 subscriptions = yes
288 list = yes
289}
290
291<em># /etc/dovecot/conf.d/10-master.conf</em>
292service imap-login {
293# Run login processes in high-security mode (see: LoginProcess.txt in dovecot docs)
294service_count = 1
295# Disable unencrypted IMAP by setting port for plain IMAP to 0
296 inet_listener imap {
297 port = 0
298 }
299 inet_listener imaps {
300 port = 993
301 ssl = yes
302 }
303}
304
305<em># Allow postfix to use dovecot SASL</em>
306service auth {
307 unix_listener /var/spool/postfix/private/auth {
308 mode = 0660
309 user = postfix
310 group = postfix
311 }
312}
313
314<em># /etc/dovecot/conf.d/10-ssl.conf</em>
315ssl = required
316ssl_key = &lt;/etc/letsencrypt/live/<strong>mail.example.com</strong>/privkey.pem
317ssl_cert = &lt;/etc/letsencrypt/live/<strong>mail.example.com</strong>/fullchain.pem
318ssl_client_ca_dir = /etc/ssl/certs
319ssl_dh = &lt;/usr/share/dovecot/dh.pem
320
321<em># Mozilla intermediate compatibility</em>
322ssl_min_protocol = TLSv1.2
323ssl_cipher_list = ECDHE+ECDSA+AESGCM:ECDHE+aRSA+AESGCM:ECDHE+ECDSA+CHACHA20:ECDHE+aRSA+CHACHA20:DHE+aRSA+AESGCM:!aNULL:!eNULL
324
325ssl_prefer_server_ciphers = yes
326ssl_client_require_valid_cert = yes
327
328
329<em># /etc/dovecot/conf.d/15-lda.conf</em>
330protocol lda {
331 mail_plugins = \$mail_plugins sieve
332}
333
334<em># /etc/dovecot/conf.d/15-mailboxes.conf</em>
335namespace inbox {
336 mailbox Sent {
337 special_use = \Sent
338 auto = subscribe
339 }
340 mailbox Trash {
341 special_use = \Trash
342 auto = create
343 autoexpunge = 30d
344 }
345 mailbox Drafts {
346 special_use = \Drafts
347 auto = subscribe
348 }
349 mailbox Spam {
350 special_use = \Junk
351 auto = create
352 autoexpunge = 30d
353 }
354 mailbox Archive {
355 special_use = \Archive
356 auto = create
357 }
358}
359
360<em># /etc/dovecot/conf.d/20-imap.conf</em>
361imap_capability = +SPECIAL-USE
362
363<em># /etc/dovecot/conf.d/90-sieve.conf</em>
364plugin {
365 sieve = ~/.dovecot.sieve
366 sieve_default = /var/lib/dovecot/sieve/default.sieve
367 sieve_global = /var/lib/dovecot/sieve/
368}</code></pre>
369
370<p>Then create the default sieve filtering script at
371<strong>/var/lib/dovecot/sieve/default.sieve</strong></p>
372<pre><code>require ["fileinto", "mailbox"];
373/*
374* Discard mail that has a spam score greater than or equal to 10
375*/
376if header :contains "X-Spam-Level" "**********" {
377 discard;
378 stop;
379}
380/*
381* Discard messages marked as infected by a virus scanner
382*/
383if header :contains "X-Virus-Scan" "infected" {
384 discard;
385 stop;
386}
387/*
388* If message is marked as spam (and falls below discard threshold) put into spam mailbox
389*/
390if header :contains "X-Spam-Flag" "YES" {
391 fileinto "Spam";
392}</code></pre>
393
394<p>And compile the script</p>
395
396<pre><code>sievec /var/lib/dovecot/sieve/default.sieve</code></pre>
397
398
399<p>Finally, configure PAM authentication for dovecot at
400<strong>/etc/pam.d/dovecot</strong>. Append these changes leaving any include
401statements intact.</p>
402<pre><code>auth required pam_unix.so
403account required pam_unix.so</code></pre>
404
405<h2>OpenDKIM</h2>
406<p>DKIM is a mail-verification method that cryptographically signs mail
407to allow receivers to verify the authenticity of the sender. Our mail server
408will use DKIM to validate signatures on incoming mail and sign outgoing mail. DKIM
409requires a public key to be published via DNS, which will be done near the end of
410the guide.</p>
411
412<p>Start by generating the DKIM key</p>
413
414<pre><code>opendkim-genkey -D /etc/dkimkeys -d <em>example.com</em> -s mail
415chown opendkim: /etc/dkimkeys/*
416chmod 600 /etc/dkimkeys/*
417mv /etc/dkimkeys/mail.private /etc/dkimkeys/mail.pem</code></pre>
418
419<p>Here we make a directory for the opendkim socket inside the postfix chroot and
420make it accessible to the postfix user.</p>
421
422<pre><code>mkdir /var/spool/postfix/opendkim
423chmod 770 /var/spool/postfix/opendkim
424chown opendkim:opendkim /var/spool/postfix/opendkim
425usermod -aG opendkim postfix</code></pre>
426
427<p>Edit the configuration file at <strong>/etc/opendkim.conf</strong>
428to be as follows:</p>
429
430<pre><code>On-BadSignature reject
431On-Security reject
432Syslog yes
433SyslogSuccess yes
434LogResults yes
435Canonicalization simple
436Mode sv
437OversignHeaders From
438Domain <strong>example.com</strong>
439Selector mail
440KeyFile /etc/dkimkeys/mail.pem
441UserID opendkim
442UMask 007
443Socket local:/var/spool/postfix/opendkim/opendkim.sock
444PidFile /run/opendkim/opendkim.pid
445TemporaryDirectory /run/opendkim
446InternalHosts 127.0.0.1
447TrustAnchorFile /usr/share/dns/root.key
448RequireSafeKeys True
449AlwaysAddARHeader True
450</code></pre>
451
452<h2>OpenDMARC</h2>
453<p>DMARC is another mail-verification technology that provides verification of the
454address seen by end-users and either or both of SPF and DKIM.
455
456<p>Like with OpenDKIM, we need to make a directory inside the postfix chroot
457for the socket and assign proper permissions.</p>
458<pre><code>mkdir /var/spool/postfix/opendmarc
459chmod 770 /var/spool/postfix/opendmarc
460chown opendmarc:opendmarc /var/spool/postfix/opendmarc
461usermod -aG opendmarc postfix
462</code></pre>
463
464<p>Now we write the configuration file at <strong>/etc/opendmarc.conf</strong></p>
465
466<pre><code>PidFile /run/opendmarc/opendmarc.pid
467PublicSuffixList /usr/share/publicsuffix/public_suffix_list.dat
468RejectFailures True
469Socket local:/var/spool/postfix/opendmarc/opendmarc.sock
470Syslog True
471SyslogFacility mail
472UMask 002
473UserID opendmarc
474HistoryFile /var/run/opendmarc/opendmarc.hist
475SPFIgnoreResults True
476SPFSelfValidate True
477</code></pre>
478
479<p>Then create the history file and set permissions.</p>
480
481<pre><code>touch /var/run/opendmarc/opendmarc.hist
482chown opendmarc:opendmarc /var/run/opendmarc/opendmarc.hist
483chmod 664 /var/run/opendmarc/opendmarc.hist
484</code></pre>
485
486<p>Now that both OpenDKIM and OpenDMARC are configured we can define them as milters
487in postfix. This will tell postfix to route mail through one or both of these milters
488depending on whether it is incoming or outgoing.</p>
489
490<pre><code>postconf -P "smtpd/pass/smtpd_milters=unix:opendkim/opendkim.sock,unix:opendmarc/opendmarc.sock"
491postconf -P "submissions/inet/smtpd_milters=unix:opendkim/opendkim.sock"
492<em># If you enabled submission on port 587 run this too</em>
493postconf -P "submission/inet/smtpd_milters=unix:opendkim/opendkim.sock"
494</code></pre>
495
496<h2>Postgrey</h2>
497<p>Postgrey implements a spam-filter technique known as greylisting, which
498always rejects mail on the first try and for a period of time afterwards known
499as the greylist period. The idea behind this being that legitimate senders will
500send the mail again later, while spammers, in a rush to send as many messages as
501possible before being blacklisted, will not.</p>
502
503<p>Postgrey ships with an extensive whitelist domains that are known
504to cause issues (mainly large providers that constantly send from different
505addresses). This whitelist file is located at
506<strong>/etc/postgrey/whitelist_clients</strong> and can be appended to include
507any domain you do not wish to be subject to greylisting.</p>
508
509<p>The configuration needed here is minimal, just open
510<strong>/etc/default/postgrey</strong> and make these changes</p>
511
512<pre><code>POSTGREY_OPTS="--unix=/var/spool/postfix/private/postgrey --privacy"
513POSTGREY_TEXT="Greylisted - see https://www.greylisting.org"</code></pre>
514
515<p>And then enable the service</p>
516
517<pre><code>systemctl enable --now postgrey</code></pre>
518
519<h2>Policyd-SPF</h2>
520<p>SPF is yet another mail-verification technology that uses DNS records to
521delegate specific servers as being authorized to send mail for the domain
522(and implicitly all other servers as unauthorized). Policyd-SPF will perform
523SPF checking of received mail and reject mail that fails SPF verfication.</p>
524
525<p>First, tell postfix how to access Policyd-SPF</p>
526
527<pre><code>postconf -e "policyd-spf_time_limit = 3600"
528postconf -M "policyd-spf/unix=policyd-spf unix - n n - 0 spawn user=policyd-spf argv=/usr/bin/policyd-spf"</code></pre>
529
530<p>And then edit the configuration file at
531<strong>/etc/postfix-policyd-spf-python/policyd-spf.conf</strong></p>
532
533<pre><code>debugLevel = 1
534TestOnly = 1
535HELO_reject = Fail
536Mail_From_reject = Fail
537Header_Type = AR
538<em># These settings increase false-positive risk</em>
539<em># Comment them if you want to reduce that risk</em>
540PermError_reject = True
541TempError_Defer = True</code></pre>
542
543
544<h2>SpamAssassin</h2>
545<p>SpamAssassin is a spam-filter that will scan all received mail and assign
546a spam score based on configured rules. SpamAssassin is much heavier and more
547resource-intensive than any of the previous spam-filtering/verification programs
548we have configured. The postfix spam-filtering philosophy emphasizes the use
549of lightweight checks before passing to an external content filter such as
550SpamAssassin. Ideally, non-legitimate mail will have already been caught by one
551of the previous methods, and SpamAssassin will only have to operate on a much
552smaller subset of the mail that is sent to our server.</p>
553
554<p>We have actually already told postfix to use SpamAssassin as a content filter
555so in this section we just need to edit the configuration file
556<strong>/etc/spamassassin/local.cf</strong>.</p>
557
558<pre><code><em># Clearly indicate message is spam to user</em>
559rewrite_header Subject *****SPAM*****
560rewrite_header From *****SPAM*****
561
562<em># Set required score to be marked as spam, 5.0 is default.</em>
563<em># Lower to make policy more strict or raise to be more lenient.</em>
564required_score 5.0
565
566<em># Attach original messages as text/plain instead of message/rfc822 to spam reports</em>
567report_safe 2
568
569<em>Do not implicitly trust mail based on IP address except localhost</em>
570trusted_networks 127.0.0.1/32
571</code></pre>
572
573<p>And finally make a few changes to the defaults file at
574<strong>/etc/default/spamassassin</strong></p>
575
576<pre><code>OPTIONS="--listen /var/run/spamd.sock --max-children 5"
577PIDFILE=/var/run/spamd.pid
578CRON=1</code></pre>
579
580<h2>Wrapping Up</h2>
581<p>At this point we have done all of the necessary configuration of the mail
582server programs. We have just a few more minor tasks before your mail server
583is operational.</p>
584
585<h3>Configure Firewall</h3>
586<p>We need to open the proper ports in the firewall. This example uses UFW.</p>
587
588<pre><code>ufw allow 25 comment "smtp"
589ufw allow 465 comment "submission over TLS"
590<em># Run this next command only if you enabled submission on port 587</em>
591ufw allow 587 comment "mail submission"
592ufw allow 993 comment "IMAP over TLS"
593ufw reload</code></pre>
594
595<h3>Restart services</h3>
596<p>Now let's restart the services to pick up any configuration changes.</p>
597
598<pre><code>systemctl restart postfix
599systemctl restart dovecot
600systemctl restart opendkim
601systemctl restart opendmarc
602systemctl enable --now spamassassin
603systemctl restart spamassassin
604systemctl restart postgrey</code></pre>
605
606<h3>DNS Entries</h3>
607<p>Finally, we needs to set some required DNS records to enable mail flow and
608verification. Begin by logging into your registrar or DNS host and editing
609your DNS records.</p>
610
611<h3>A Record</h3>
612<p>If you did not set a wildcard A record earlier, you will need to set one now
613for <strong>mail</strong>.
614Alternatively, if you are running the mail server on the same server as your
615website, you may want to instead make a CNAME record pointing mail to www.</p>
616
617<h3>MX Record</h3>
618<p>MX records tell servers attempting to send you mail where to send it. Open the
619MX records section on your registrar and add a new record. An MX
620record consists of a priority and a destination. Set the priority to 10 and the
621destination to <strong>mail</strong>, or whatever your subdomain for this mail
622server is. The host value can be left blank or may need to be set to "@"
623depending on your registrar.</p>
624
625<h3>DKIM TXT Record</h3>
626<p>Now we will set the three TXT records we need. Open the TXT records tab on
627your registrar.</p>
628
629<p>We'll set the DKIM record first. The command we ran to
630generate our DKIM keys also generates a DNS record for us which will be helpful
631here. Print that to the screen with:</p>
632
633<pre><code>cat /etc/dkimkeys/mail.txt</code></pre>
634
635<p>You should get a lengthy output that looks something like the following. The
636bolded portion is the value.</p>
637
638<pre><code>mail._domainkey IN TXT ( <strong>"v=DKIM1; h=sha256; k=rsa; "
639 "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz50PSYL0Ob+OlF/0B77rwlzLe7zF6JKnxQNtMqcOCZ0Dar2FPhSUSz1FR0YmNuoShjMogdgKeojIzgRUqwK5GZ5Lz456qiXWkfAtLPc6UQ/WPoyEBGbJpRBYPGWdN4VoNcHkk/I4csvXW6MOI55ghPOwDmootPkCzNPR6gmNAXMe0duS4Lb+bIjy9QMOxGYVUaQ/b+7xar+fWw"
640 "bA3DjQa3jTLCydzzJpjEMfVaKqNhQ4N+ve7O2Mb3LF5k5B977mtok/6POjVG5HY8g6Pba+GzMFItR6nJO5EE2fyfv6cNbRLsZiM+WQmqvDBst5ejaeapy86F5PdJFlX/TUgXjtuwIDAQAB"</strong> ) ; ----- DKIM key mail for example.com</code></pre>
641
642<p>You can cleanup the spacing of the value as your registrar should automatically
643handle any needed splitting of the record. The parts you need to paste into your
644registrar's web interface should then look like this.</p>
645
646<pre><code><em># Name/Host</em>
647mail._domainkey
648<em># TXT Value</em>
649"v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz50PSYL0Ob+OlF/0B77rwlzLe7zF6JKnxQNtMqcOCZ0Dar2FPhSUSz1FR0YmNuoShjMogdgKeojIzgRUqwK5GZ5Lz456qiXWkfAtLPc6UQ/WPoyEBGbJpRBYPGWdN4VoNcHkk/I4csvXW6MOI55ghPOwDmootPkCzNPR6gmNAXMe0duS4Lb+bIjy9QMOxGYVUaQ/b+7xar+fWwbA3DjQa3jTLCydzzJpjEMfVaKqNhQ4N+ve7O2Mb3LF5k5B977mtok/6POjVG5HY8g6Pba+GzMFItR6nJO5EE2fyfv6cNbRLsZiM+WQmqvDBst5ejaeapy86F5PdJFlX/TUgXjtuwIDAQAB"</code></pre>
650
651<h3>DMARC TXT Record</h3>
652<p>The DMARC record should be as follows:</p>
653
654<pre><code><em># Name/Host</em>
655_dmarc
656<em># Value</em>
657"v=DMARC1; p=reject; rua=mailto:dmarc@<strong>example.com</strong>; fo=1"
658</code></pre>
659
660<h3>SPF Record</h3>
661<p>Your SPF record will look like this. Remember to replace
662<strong>mail.example.com</strong> with your server name.</p>
663
664<pre><code><em># Name/Host</em>
665@
666<em># Value</em>
667"v=spf1 a:<strong>mail.example.com</strong> -all"
668</code></pre>
669
670<h3>PTR Record</h3>
671<p>Many mail servers rely on PTR records for verification purposes so we need
672to make sure our server's IP address resolves to the proper domain name. If
673your mail server is residing on a VPS, you will need to add this record on your
674VPS provider's interface, consult their documentation for details.</p>
675
676<h2>Creating your own Mail User</h2>
677<p>Your mail server is now up and running. Let's create an email for you to
678receive mail.</p>
679
680<pre><code>useradd --shell /usr/sbin/nologin --create-home --user-group <strong>user</strong>
681echo "<strong>user@example.com user</strong>" &gt;&gt; /etc/postfix/login_maps
682echo "<strong>user user</strong>" &gt;&gt; /etc/postfix/local_maps
683postmap /etc/postfix/login_maps
684postmap /etc/postfix/local_maps
685postfix reload
686</code></pre>
687
688<p>I have a script available for adding and removing users that you can find
689<a href=https://git.chudnick.com/mail-tools/tree/server/mailadm>here</a>.
690
691<h3>Connecting From a Mail Client</h3>
692<p>When connecting your account to a mail client you need to use these settings.</p>
693
694<ul>
695 <li>Username: <strong>user@example.com</strong> </li>
696
697 <li>Password: the password for <strong>user@example.com</strong> </li>
698
699 <li>Server name: <strong>mail.example.com</strong> </li>
700
701 <li>IMAP Port: 993</li>
702
703 <li>IMAP Connection: SSL/TLS</li>
704
705 <li>SMTP Port: 465</li>
706
707 <li>SMTP Connection Type: SSL/TLS</li>
708
709</ul>
710<p>
711<hr>
712Consider <a href=../donate.html>donating</a> if this article was useful.
713<a class=qr href=../images/bitcoin.png>[BTC]</a>
714</p>
715 </main>
716 <footer>
717 <a href=../kb.html>Knowledge Base</a>
718 <br>
719 <a href=../index.html>www.chudnick.com</a>
720 </footer>
721</body>
722</html>
723
diff --git a/articles/mdadm-raid.html b/articles/mdadm-raid.html
new file mode 100644
index 0000000..dd2a014
--- /dev/null
+++ b/articles/mdadm-raid.html
@@ -0,0 +1,120 @@
1<!DOCTYPE html>
2<html lang=en>
3 <head>
4 <title></title>
5 <meta charset="utf-8"/>
6 <link rel="shortcut icon" href="favicon.ico"/>
7 <link rel='stylesheet' href='../style.css'/>
8 <meta name="viewport" content="width=device-width, initial-scale=1">
9 </head>
10<body>
11 <header><h1>Linux Software RAID</h1></header>
12 <main>
13 <p><strong>mdadm</strong> is a tool that allows for creation and
14 management of software RAID arrays on Linux. Creating an array
15 is a rather straightforward process.</p>
16
17 <h2>Install packages</h2>
18 <p>The only package needed is mdadm itself</p>
19 <pre><code>apt install mdadm</code></pre>
20
21 <h2>Partition disks</h2>
22 <p>We'll need to parition the disks to be used in the array before
23 creating it. This isn't anything complicated, we will just be
24 creating a single partition using all the space on each disk.</p>
25
26 <p>Use <strong>lsblk</strong> to get a list of disks attached to your system.</p>
27
28 <img src=../images/raid/lsblk.png alt="lsblk command">
29
30 <p>Then use <strong>fdisk</strong> to edit the partition table of the
31 first disk. In my case this would be <strong>/dev/sdb</strong>.
32 Be sure that you are selecting the correct disk as selecting the
33 wrong one can result in data being lost.</p>
34
35 <pre><code>fdisk /dev/sdb</code></pre>
36
37 <p>Use <strong>g</strong> to create a new GUID partition table.
38 Use <strong>n</strong> to create a new partition, and then just press
39 enter at all of the prompts to accept the defaults. Finally use
40 <strong>w</strong> to write the changes. You can use lsblk again
41 to verify the change and you should see that /dev/sdb now has a
42 partition <strong>/dev/sdb1</strong>.</p>
43
44 <img src=../images/raid/fdisk.png alt="fdisk command">
45
46 <p>Repeat this process for your other disks before continuing.</p>
47
48
49 <h2>Create the array</h2>
50 <p>Creating the array is done with a single command, but takes just a
51 bit of planning.</p>
52 <ul>
53 <li>Define an ID for the array, which is just a
54 number identifier. I use <em>0</em> in the command.</li>
55 <li>Determine the RAID level you want to use. I am going to
56 use RAID5 in this example. The argument to --level is the
57 number of the RAID level</li>
58 <li>Determine the devices that will be used in the array.
59 Somewhat contrary to the argument name, these devices
60 will actually be the partitions of the disks and not the
61 disks themselves. In the command, give the number of
62 partitions (<em>3</em>) and then a space-separated list of the
63 partitions that will be active in the array
64 (<em>/dev/sdb1 /dev/sdc1 /dev/sdd1</em>).</li>
65 <li>If you want to have any spare devices in the array you
66 will define them with the --spare-devices argument. These are
67 defined in the exact same way as the active RAID devices. In
68 the example I use <em>1</em> spare <em>/dev/sde1</em>. Spare
69 devices are hot-spares that will automatically be inserted into
70 the array if one of the disks fails.</li>
71 </ul>
72
73 <pre><code>mdadm --create /dev/md<em>0</em> --level=<em>5</em> --raid-devices=<em>3</em> <em>/dev/sdb1 /dev/sdc1 /dev/sdd1</em> --spare-devices=<em>1</em> <em>/dev/sde1</em></code></pre>
74
75 <p>After creating the array run the following command to get details
76 and the status of the array. It will take a bit to initialize the
77 array, you will know this is done when the state is clean. You do not
78 need to wait for the array to completely intialize to continue.</p>
79
80 <pre><code>mdadm --detail /dev/md<em>0</em></code></pre>
81
82 <p>Take note of the UUID and name values as we will need them in the
83 next step.</p>
84
85 <p>Before moving on we need to make a filesystem on the array. I'm
86 going to make a simple EXT4 filesystem here.</p>
87
88 <pre><code>mkfs.ext4 /dev/md<em>0</em></code></pre>
89
90
91 <h2>Configuration Files</h2>
92
93 <p>Open the mdadm configuration file at
94 <strong>/etc/mdadm/mdadm.conf</strong> and append this line. Replace
95 <em>uuid</em> and <em>name</em> with the values you got when running
96 mdadm --detail, replace <em>0</em> whatever ID you chose.</p>
97
98 <pre><code>ARRAY /dev/md<em>0</em> metadata=1.2 UUID=<em>uuid</em> name=<em>name</em></code></pre>
99
100 <p>Optionally, create an <strong>/etc/fstab</strong> entry for
101 automounting of the array. Replace /mnt/raid with the directory
102 where you want to mount the array. If you made a filesystem other than
103 ext4 make sure to change that value.</p>
104
105 <pre><code>/dev/md<em>0</em> /mnt/raid ext4 defaults 0 1</code></pre>
106
107<p>
108<hr>
109Consider <a href=../donate.html>donating</a> if this article was useful.
110<a class=qr href=../images/bitcoin.png>[BTC]</a>
111</p>
112 </main>
113 <footer>
114 <a href=../kb.html>Knowledge Base</a>
115 <br>
116 <a href=../index.html>www.chudnick.com</a>
117 </footer>
118</body>
119</html>
120
diff --git a/articles/mutt.html b/articles/mutt.html
new file mode 100644
index 0000000..962ae33
--- /dev/null
+++ b/articles/mutt.html
@@ -0,0 +1,27 @@
1<!DOCTYPE html>
2<html lang=en>
3 <head>
4 <title></title>
5 <meta charset="utf-8"/>
6 <link rel="shortcut icon" href="favicon.ico"/>
7 <link rel='stylesheet' href='../style.css'/>
8 <meta name="viewport" content="width=device-width, initial-scale=1">
9 </head>
10<body>
11 <header><h1>Mutt - Terminal Email Client</h1></header>
12 <main>
13 </main>
14<p>
15<hr>
16Consider <a href=../donate.html>donating</a> if this article was useful.
17<a class=qr href=../images/bitcoin.png>[BTC]</a>
18</p>
19 </main>
20 <footer>
21 <a href=../kb.html>Knowledge Base</a>
22 <br>
23 <a href=../index.html>www.chudnick.com</a>
24 </footer>
25</body>
26</html>
27
diff --git a/articles/pam-tfa.html b/articles/pam-tfa.html
new file mode 100644
index 0000000..7bdc551
--- /dev/null
+++ b/articles/pam-tfa.html
@@ -0,0 +1,157 @@
1<!DOCTYPE html>
2<html lang=en>
3 <head>
4 <title></title>
5 <meta charset="utf-8"/>
6 <link rel="shortcut icon" href="favicon.ico"/>
7 <link rel='stylesheet' href='../style.css'/>
8 <meta name="viewport" content="width=device-width, initial-scale=1">
9 </head>
10<body>
11 <header><h1>PAM OATH Two Factor Authentication</h1></header>
12 <main>
13 <p>In this article we are going to look at configuring two factor
14 authentication via PAM using OATH. This is a simple and private way
15 to increase the security of your systems. Even if you are not familiar
16 with the term, it is likely that you
17 have used OATH before. OATH (specifically TOTP) is the rotating 6
18 digit code that you get from scanning a QR code when setting up 2FA
19 on an account.</p>
20
21 <p>This example will show how to configure 2FA for SSH logins to a
22 server, but can easily be generalized to cover other programs or
23 even all authentication on a system. The two factors here will be
24 public key authentication and then the OATH/TOTP code.
25 <em>It is highly recommended that you remain SSHd into your server
26 until after testing to avoid locking yourself out in the event
27 of a configuration error.</em></p>
28
29 <h2>Install Packages</h2>
30 <p>You only need to install a single package on the server side.</p>
31
32 <pre><code>apt install libpam-oath</code></pre>
33
34 <p>On the client machine that will be SSHing to the server install
35 these two packages.</p>
36
37 <pre><code>apt install oathtool qrencode</code></pre>
38
39 <h2>Configure OATH</h2>
40 <p>Create the OATH configuration file <strong>/etc/users.oath</strong>.
41 This file will contain the OATH secret keys so permissions need to be
42 set to only allow the root user to view it.</p>
43
44 <pre><code>touch /etc/users.oath
45chown root: /etc/users.oath
46chmod 600 /etc/users.oath</code></pre>
47
48 <p>Generate a secret key for the TOTP. Treat this secret key as you
49 would your SSH or GPG private key. Anyone who has this key will be able
50 to generate the code needed to authenticate.</p>
51
52 <pre><code>openssl rand -hex 10</code></pre>
53
54 <p>Now we define the TOTP configuration for our user. If you were
55 setting this up for multiple users you would make one entry per line.
56 Open <strong>/etc/users.oath</strong> and add this line.
57 <em>user</em> is the username of the account you will SSH into.
58 Replace the long string of numbers and letters with the secret key
59 you just generated.</p>
60
61 <pre><code>HOTP/T30/6 <em>user</em> - <em>00112233445566aabbcc</em></code></pre>
62
63
64 <h2>Configure PAM</h2>
65 <p>Now we need to tell PAM to use OATH to authenticate sshd. Do that
66 by opening <strong>/etc/pam.d/sshd</strong> and adding the following
67 line to the top of the file.</p>
68
69 <pre><code>auth sufficient pam_oath.so usersfile=/etc/users.oath window=30 digits=6</code></pre>
70
71 <p>This tells PAM to consider a valid 6 digit code as fully authenticated
72 and to skip any other processing that may normally occur, such as
73 requesting a password.</p>
74
75 <h2>Configure SSHD</h2>
76 <p>We need to make a few changes to the sshd configuration to allow
77 OATH to work properly. Open the sshd configuration file at
78 <strong>/etc/ssh/sshd_config</strong> and make the following changes.</p>
79
80 <pre><code>AuthenticationMethods publickey,keyboard-interactive
81PubkeyAuthentication yes
82PasswordAuthentication no
83ChallengeResponseAuthentication yes
84UsePAM yes</code></pre>
85
86 <p>The <strong>AuthenticationMethods</strong> line specifically tells
87 sshd that a user needs to both have an authorized SSH key and know
88 the proper 6 digit code to login.</p>
89
90 <p>Restart sshd to apply the changes</p>
91
92 <pre><code>systemctl restart sshd</code></pre>
93
94 <h2>Test the Changes</h2>
95 <p>From your client ssh into your server as normal. Instead of
96 connecting as you have been, you should now see a prompt for your
97 one time password. You can use <strong>oathtool</strong> to get
98 the code. Again, replace the long string of numbers and letters
99 with the secret key you generated on the server.</p>
100
101 <pre><code>oathtool --totp -d6 <em>00112233445566aabbcc</em></code></pre>
102
103 <p>Enter that 6 digit code into the prompt and you will be logged
104 into your server.</p>
105
106 <p>Now, in the unlikely event that your SSH private key is stolen,
107 an attacker still won't be able to access your server!</p>
108
109 <h2>Managing your TOTP</h2>
110 <p>You probably don't want to run the oathtool command everytime you
111 need your code, and while you could make an alias, that would require
112 storing your secret key in plaintext. Here are some better options.</p>
113
114 <ul>
115 <li><strong>pass otp</strong> is an extension to the command-line
116 password manager <strong>pass</strong> for handling TOTP.
117 Use this if you are already using pass</li>
118 <li><strong>KeePassXC</strong> is a graphical password
119 manager that can manage TOTP</li>
120 <li><strong>Gnome Authenticator</strong> is a graphical
121 TOTP manager for the GNOME desktop environment</li>
122 </ul>
123
124 <p>You may also want to generate a QR code for easy setup on another
125 device. Rerun the same oathtool command as before with the -v flag
126 to get the base32 version of your secret key.</p>
127
128 <pre><code>oathtool --totp -v -d6 <em>00112233445566aabbcc</em>
129--------------------------------
130Hex secret: 00112233445566aabbcc
131Base32 secret: <strong>AAISEM2EKVTKVO6M</strong>
132Digits: 6
133Window size: 0
134TOTP mode: SHA1
135Step size (seconds): 30
136</code></pre>
137
138 <p>Then use qrencode to generate the QR code image.</p>
139
140 <pre><code>qrencode -o <em>totp.png</em> 'otpauth://totp/<em>user</em>@<em>server</em>?secret=<em>AAISEM2EKVTKVO6M</em>'</code></pre>
141
142 </main>
143
144<p>
145<hr>
146Consider <a href=../donate.html>donating</a> if this article was useful.
147<a class=qr href=../images/bitcoin.png>[BTC]</a>
148</p>
149 </main>
150 <footer>
151 <a href=../kb.html>Knowledge Base</a>
152 <br>
153 <a href=../index.html>www.chudnick.com</a>
154 </footer>
155</body>
156</html>
157
diff --git a/articles/template.html b/articles/template.html
new file mode 100644
index 0000000..f908cab
--- /dev/null
+++ b/articles/template.html
@@ -0,0 +1,27 @@
1<!DOCTYPE html>
2<html lang=en>
3 <head>
4 <title></title>
5 <meta charset="utf-8"/>
6 <link rel="shortcut icon" href="favicon.ico"/>
7 <link rel='stylesheet' href='../style.css'/>
8 <meta name="viewport" content="width=device-width, initial-scale=1">
9 </head>
10<body>
11 <header><h1>Article</h1></header>
12 <main>
13 </main>
14<p>
15<hr>
16Consider <a href=../donate.html>donating</a> if this article was useful.
17<a class=qr href=../images/bitcoin.png>[BTC]</a>
18</p>
19 </main>
20 <footer>
21 <a href=../kb.html>Knowledge Base</a>
22 <br>
23 <a href=../index.html>www.chudnick.com</a>
24 </footer>
25</body>
26</html>
27