summaryrefslogtreecommitdiff
path: root/rss.xml
blob: 430fecceb0f730c134f29c2be447a8f04c66daf9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
<rss version="2.0">
		<channel>
			<title>chudnick.com</title>
			<description>chudnick.com</description>
			<language>en-us</language>
			<link>https://chudnick.com/rss.xml</link>
			<item>
					<title>LUKS Block Device Encryption</title>
					<guid>https://www.chudnick.com/articles/luks.html</guid>
					<link>https://www.chudnick.com/articles/luks.html</link>
					<pubDate>Sat, 18 Jun 2022 15:46:21 -0400</pubDate>
					<description><![CDATA[<!DOCTYPE html>
<html lang=en>
    <head>
        <title></title>
        <meta charset="utf-8"/>
        <link rel="shortcut icon" href="favicon.ico"/>
        <link rel='stylesheet' href='../style.css'/>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
<body>
    <header><h1>LUKS Block Device Encryption</h1></header>
    <main>
			<p>Linux Unified Key Setup (LUKS) is a method for encrypting 
			block devices that is built-in to the Linux Kernel. In this tutorial 
			I will be showing how to create an encrypted USB drive with LUKS, but 
			this process is applicable to other types of storage. 
			<strong>This process will wipe all data on the device that you 
			encrypt so make backups beforehand if needed</strong>.</p>

			<h2>Install Packages</h2>
			<p>Install cryptsetup and its dependencies</p>

			<pre><code>apt install cryptsetup</code></pre>

			<h2>Prepare the Drive</h2>
			<p>If the device you are encrypting was previously used, you will 
			want to completely overwrite any data on it before encryption. This 
			can be done using <strong>dd</strong> as shown below. This could take 
			a very long time depending on the size and type of your drive. 
			Be sure that you enter the correct path to your drive as the 
			argument to <strong>of</strong>, and do not specify a partition:
			<strong>/dev/sdb</strong> is correct, <strong>/dev/sdb1</strong> is 
			wrong. Obviously this will destroy any data on the device so be 
			absolutely sure you specify the correct device in the command 
			and have backups of data previously stored if needed.</p>

			<pre><code>dd if=/dev/zero of=<em>/dev/sdX</em> status=progress</code></pre>

			<h2>Encrypt the Drive</h2>
			<p><strong>cryptsetup</strong> is the main command used to perform 
			LUKS-related tasks. This main command is followed by a subcommand 
			that specifies which action to perform. To create a LUKS partition 
			on a drive we need to use the subcommand <strong>luksFormat</strong>. 
			We also use the type option to explicitly say to use LUKS version 2 
			encryption. Run the command and then enter and confirm the 
			encryption password.</p>

			<pre><code>cryptsetup --type luks2 luksFormat <em>/dev/sdX</em></code></pre>

			<p>Now we need to open and map the encrypted LUKS device. This is done 
			by using the <strong>luksOpen</strong> subcommand. This subcommand takes 
			the device as the first argument and then a map target as the second. The 
			map target can be any string as long as a map does not already exist with 
			that name. In the example I will use <em>crypt</em>.

			<pre><code>cryptsetup luksOpen <em>/dev/sdX crypt</em></code></pre>

			<p>And then we just need to create a filesystem on the device; I will 
			be making an ext4 filesystem in the example. To 
			interact with an encrypted drive after it has been opened you need 
			to refer to its device mapper target, which is found under /dev/mapper. 
			Since we used <em>crypt</em> as the map target, our encrypted drive 
			is /dev/mapper/<em>crypt</em>. </p>

			<pre><code>mkfs.ext4 /dev/mapper/<em>crypt</em></code></pre>

			<p>After creating the filesystem created the encrypted drive 
			can now be mounted and used like any other device. Remember that 
			when mounting the device you refer to the device mapper target 
			(mount /dev/mapper/<em>crypt</em> /mnt/crypt).</p>

			<p>In addition to unmounting the filesystem you should always close 
			the device mapping before removing your encrypted drive. This is 
			done with the <strong>luksClose</strong> subcommand and takes 
			the device mapping as the argument.</p>

			<pre><code>cryptsetup luksClose /dev/mapper/<em>crypt</em></code></pre>

			<h2>The LUKS Header</h2>
			<p>The LUKS header sits at the front of your encrypted drive and is 
			responsible for managing access to the device. If the header is 
			damaged accessing your encrypted data will not be possible. Because of 
			this, you may want to make backups of the header, but before doing so 
			you need to weigh the risks and benefits.</p>

			<p>The obvious benefit is that 
			in the event of damage to the LUKS header, you can easily restore a 
			backup and regain access to the data. However, having a backup of the 
			header makes it harder to wipe the LUKS device. Without a header 
			backup, overwriting the LUKS header on the device is enough to securely 
			wipe the drive. With backups, you need to either destroy all header 
			backups or overwrite all encrypted data on the device. The other main risk 
			is that an encryption password that is valid at the time you make a 
			backup will always be valid for that backup. For example, say you change 
			the encryption password on your device after making a backup because it 
			has been compromised. An attacker would be able to restore the header 
			backup and then use the compromised password to access the data.</p>

			<p>The cryptsetup documentation refers to creating header backups 
			as making a trade-off between safety and security. You need to make a 
			decision based on your individual use-case. It's a good idea to 
			routinely update your header backups if you do choose to make them</p>

			<p>When creating or restoring a header backup you always refer to the 
			block device in the command, not the device mapper taget. To create 
			a header backup:</p>

			<pre><code>cryptsetup luksHeaderBackup <em>/dev/sdX</em> --header-backup-file <em>path/to/backup</em></code></pre>

			<p>To restore a header from a backup:</p>

			<pre><code>cryptsetup luksHeaderRestore <em>/dev/sdX</em> --header-backup-file <em>path/to/backup</em></code></pre>

			<p>Note that is not necessary to store header backups on an encrypted 
			device. I would recommend storing backups on at least one non-encrypted 
			drive in case of an emergency.</p>

    </main>
<p>
<hr>
Consider <a href=../donate.html>donating</a> if this article was useful.
<a class=qr href=../images/bitcoin.png>[BTC]</a>
</p>
 	</main>
    <footer>
	<a href=../kb.html>Knowledge Base</a>
	<br>
	<a href=../index.html>www.chudnick.com</a>
	</footer>
</body>
</html>]]></description>
			</item>
			<item>
					<title>PAM OATH Two Factor Authentication</title>
					<guid>https://www.chudnick.com/articles/pam-tfa.html</guid>
					<link>https://www.chudnick.com/articles/pam-tfa.html</link>
					<pubDate>Sat, 18 Jun 2022 15:45:01 -0400</pubDate>
					<description><![CDATA[<!DOCTYPE html>
<html lang=en>
    <head>
        <title></title>
        <meta charset="utf-8"/>
        <link rel="shortcut icon" href="favicon.ico"/>
        <link rel='stylesheet' href='../style.css'/>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
<body>
    <header><h1>PAM OATH Two Factor Authentication</h1></header>
    <main>
			<p>In this article we are going to look at configuring two factor 
			authentication via PAM using OATH. This is a simple and private way 
			to increase the security of your systems. Even if you are not familiar 
			with the term, it is likely that you 
			have used OATH before. OATH (specifically TOTP) is the rotating 6 
			digit code that you get from scanning a QR code when setting up 2FA 
			on an account.</p>

			<p>This example will show how to configure 2FA for SSH logins to a 
			server, but can easily be generalized to cover other programs or 
			even all authentication on a system. The two factors here will be 
			public key authentication and then the OATH/TOTP code. 
			<em>It is highly recommended that you remain SSHd into your server 
			until after testing to avoid locking yourself out in the event 
			of a configuration error.</em></p>

			<h2>Install Packages</h2>
			<p>You only need to install a single package on the server side.</p>

			<pre><code>apt install libpam-oath</code></pre>

			<p>On the client machine that will be SSHing to the server install 
			these two packages.</p>

			<pre><code>apt install oathtool qrencode</code></pre>

			<h2>Configure OATH</h2>
			<p>Create the OATH configuration file <strong>/etc/users.oath</strong>. 
			This file will contain the OATH secret keys so permissions need to be 
			set to only allow the root user to view it.</p>

			<pre><code>touch /etc/users.oath
chown root: /etc/users.oath
chmod 600 /etc/users.oath</code></pre>

			<p>Generate a secret key for the TOTP. Treat this secret key as you 
			would your SSH or GPG private key. Anyone who has this key will be able 
			to generate the code needed to authenticate.</p>

			<pre><code>openssl rand -hex 10</code></pre>

			<p>Now we define the TOTP configuration for our user. If you were 
			setting this up for multiple users you would make one entry per line. 
			Open <strong>/etc/users.oath</strong> and add this line. 
			<em>user</em> is the username of the account you will SSH into. 
			Replace the long string of numbers and letters with the secret key 
			you just generated.</p>

			<pre><code>HOTP/T30/6 <em>user</em> - <em>00112233445566aabbcc</em></code></pre>


			<h2>Configure PAM</h2>
			<p>Now we need to tell PAM to use OATH to authenticate sshd. Do that 
			by opening <strong>/etc/pam.d/sshd</strong> and adding the following 
			line to the top of the file.</p>

			<pre><code>auth sufficient pam_oath.so usersfile=/etc/users.oath window=30 digits=6</code></pre>

			<p>This tells PAM to consider a valid 6 digit code as fully authenticated 
			and to skip any other processing that may normally occur, such as 
			requesting a password.</p>

			<h2>Configure SSHD</h2>
			<p>We need to make a few changes to the sshd configuration to allow 
			OATH to work properly. Open the sshd configuration file at 
			<strong>/etc/ssh/sshd_config</strong> and make the following changes.</p>

			<pre><code>AuthenticationMethods publickey,keyboard-interactive
PubkeyAuthentication yes
PasswordAuthentication no
ChallengeResponseAuthentication yes
UsePAM yes</code></pre>

			<p>The <strong>AuthenticationMethods</strong> line specifically tells 
			sshd that a user needs to both have an authorized SSH key and know 
			the proper 6 digit code to login.</p>

			<p>Restart sshd to apply the changes</p>

			<pre><code>systemctl restart sshd</code></pre>

			<h2>Test the Changes</h2>
			<p>From your client ssh into your server as normal. Instead of 
			connecting as you have been, you should now see a prompt for your 
			one time password. You can use <strong>oathtool</strong> to get 
			the code. Again, replace the long string of numbers and letters 
			with the secret key you generated on the server.</p>

			<pre><code>oathtool --totp -d6 <em>00112233445566aabbcc</em></code></pre>

			<p>Enter that 6 digit code into the prompt and you will be logged 
			into your server.</p>

			<p>Now, in the unlikely event that your SSH private key is stolen, 
			an attacker still won't be able to access your server!</p>

			<h2>Managing your TOTP</h2>
			<p>You probably don't want to run the oathtool command everytime you 
			need your code, and while you could make an alias, that would require 
			storing your secret key in plaintext. Here are some better options.</p>

			<ul>
					<li><strong>pass otp</strong> is an extension to the command-line 
					password manager <strong>pass</strong> for handling TOTP. 
					Use this if you are already using pass</li> 
					<li><strong>KeePassXC</strong> is a graphical password 
					manager that can manage TOTP</li> 
					<li><strong>Gnome Authenticator</strong> is a graphical 
					TOTP manager for the GNOME desktop environment</li>
			</ul>

			<p>You may also want to generate a QR code for easy setup on another 
			device.  Rerun the same oathtool command as before with the -v flag 
			to get the base32 version of your secret key.</p>

			<pre><code>oathtool --totp -v -d6 <em>00112233445566aabbcc</em>
--------------------------------
Hex secret: 00112233445566aabbcc
Base32 secret: <strong>AAISEM2EKVTKVO6M</strong>
Digits: 6
Window size: 0
TOTP mode: SHA1
Step size (seconds): 30
</code></pre>

			<p>Then use qrencode to generate the QR code image.</p>

			<pre><code>qrencode -o <em>totp.png</em> 'otpauth://totp/<em>user</em>@<em>server</em>?secret=<em>AAISEM2EKVTKVO6M</em>'</code></pre>

    </main>
	
<p>
<hr>
Consider <a href=../donate.html>donating</a> if this article was useful.
<a class=qr href=../images/bitcoin.png>[BTC]</a>
</p>
 	</main>
    <footer>
	<a href=../kb.html>Knowledge Base</a>
	<br>
	<a href=../index.html>www.chudnick.com</a>
	</footer>
</body>
</html>]]></description>
			</item>
			<item>
					<title>FreeIPA Server Setup</title>
					<guid>https://www.chudnick.com/articles/freeipa-server.html</guid>
					<link>https://www.chudnick.com/articles/freeipa-server.html</link>
					<pubDate>Sat, 18 Jun 2022 15:44:21 -0400</pubDate>
					<description><![CDATA[<!DOCTYPE html>
<html lang=en>
    <head>
        <title></title>
        <meta charset="utf-8"/>
        <link rel="shortcut icon" href="favicon.ico"/>
        <link rel='stylesheet' href='../style.css'/>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
<body>
    <header><h1>FreeIPA Server Setup</h1></header>
    <main>
			<p>FreeIPA is a centralized idenity management solution developed 
			by Redhat. It is in my opinion the most functional libre alternative 
			to Microsoft's Active Directory.  Like AD, FreeIPA integrates all of 
			the pieces needed to setup a domain including LDAP, Kerberos, 
			a Certificate Authority, and much more.</p>

			<p>I will be using Fedora 35 in this tutorial. As of Debian 11, the 
			FreeIPA server is still not in the Debian repos. You will need either 
			a Fedora or a RHEL machine. A CentOS fork may work also but I have not 
			tested that.</p>

			<h2>FreeIPA in an Enterprise</h2>

			<p>For readers exploring the use of FreeIPA in a business 
			environment, note that FreeIPA documentation explicitly states that 
			it is not a replacement for Active Directory.  I have not personally 
			tried to join a Windows computer to a FreeIPA domain, and so I can't 
			speak to how well that would work. FreeIPA would also not be able to push 
			out policy to Windows machines as is done with Group Policy. FreeIPA 
			is though able to create inter-domain trusts with an existing AD 
			infrastructure.</p>

			<h2>The Case for FreeIPA at Home</h2>
			<p>Using a full Kerberos and LDAP identity management server may 
			seem like overkill at home. And if you only have a single computer 
			then it probably is. But scaling up even slightly, to perhaps a small 
			family each with their own computer, will make having FreeIPA 
			advantageous (<em>your family is all using Linux, right?</em>). This 
			will be especially apparent if you are hosting your own services. 
			If you are for instance hosting a Jellyfin media server that everyone 
			in your family accesses, you won't want them to juggle separate 
			passwords for Jellyfin when you could just have them use the same 
			password they do on the computer. This single/same sign-on capability is 
			one of the most practically useful aspects of FreeIPA.</p>

			<h2>Install Packages</h2>
			<p>We start as usual by installing the required packages.</p>

			<pre><code>dnf install freeipa-server freeipa-dns</code></pre>

			<h2>Set Hostname</h2>
			<p>The server will need to have a fully qualified hostname 
			before setting up IPA. You will need both a hostname for the server 
			itself and the domain name you will want for the FreeIPA domain. I 
			will be using <em>ipaserver.myhome.local</em>, where 
			<em>ipaserver</em> is the hostname and <em>myhome.local</em> is the 
			domain name.</p>

			<pre><code>hostnamectl set-hostname <em>ipaserver.home.local</em></code></pre>

			<p>We'll also need to add a hosts file entry to 
			<strong>/etc/hosts</strong>. Open that file in an editor and add a new 
			line with the IP of the server, the fully qualified name, and the 
			hostname.</p>

			<pre><code>192.168.1.10 ipaserver.myhome.local ipaserver</code></pre>

			
			<p>Make sure to reboot the server before continuing to complete 
			the hostname change.</p>

			<h2>Firewall Configuration</h2>
			<p>We'll need to allow several ports for FreeIPA to function properly. 
			Fedora 35 uses firewalld by default but I am going to disable that 
			in favor of UFW here.</p>

			<pre><code><em>#Install UFW</em>
dnf install ufw
<em># Stop and disable firewalld</em>
systemctl disable --now firewalld
<em># Configure UFW</em>
ufw enable
ufw allow ssh
ufw allow dns
ufw allow 88 comment kerberos
ufw allow 389 comment ldap
ufw allow 443 comment webui
ufw allow 636 comment ldaps
ufw default deny incoming
ufw reload</code></pre>

	<h2>Configure FreeIPA</h2>
	<p>Now we can run the FreeIPA setup script. This is an interactive but mostly 
	automatic process that will configure all of the IPA components. The 
	<strong>--mkhomedir</strong> flag will configure the server to create home 
	directories for IPA users on their first login and would otherwise have to be 
	done manually.</p>

	<pre><code>ipa-server-install --mkhomedir</code></pre>

	<p>That command will bring you into the install script.  You will be prompted 
	several times before the bulk of the configuration happens. Default values 
	are show in brackets after the prompt. Let's run through those prompts.<br><br>
	<strong>Do you want to configure integrated DNS (BIND)?</strong>: 
	<em>yes</em><br><br>
	<strong>Sever host name</strong>: the default value should be showing 
	<em>ipaserver.myhome.local</em> which is what we want. Simply hit enter to acecpt
	the default.<br><br>
	<strong>Please confirm the domain name</strong>: The default here should be 
	correct <em>myhome.local</em> so hit enter to accept that.<br><br>
	<strong>Please provide a realm name</strong>: This should just be the domain 
	name in all uppercase. If the default looks correct just hit enter.<br><br>
	<strong>Directory Manager password</strong>: This is the password for an 
	administrator account used by system services. You will not need this for daily 
	use so I recommend setting it to a long randomly generated string. I have found 
	myself that using an extremely long password here will cause the installation to 
	fail. A password under 40 characters should be safe.<br><br>
	<strong>IPA admin password</strong>: This is the password for your initial admin 
	user. Make this a strong password as this user has full admin rights for the 
	entire domain.<br><br>
	<strong>Do you want to configure DNS forwarders</strong>: This allows you to 
	configure the IPA server to forward DNS requests to another DNS server for 
	zones it is not authoratitve for. The DNS server is configured by default as 
	a recursive DNS server so answering no does not prevent internet access. If you 
	have another DNS server that should be used instead then answer yes and provide 
	the IP address when prompted.<br><br>

	<strong>Do you want to configure chrony with NTP server or pool address?</strong>
	: Here you can configure a custom NTP server or pool for the NTP daemon chrony. 
	If you already have an NTP server on your network answer yes and provide its IP. 
	If you want to leave the deafult chrony configuration then answer no. Time 
	synchronization is very important in Kerberos so you should consider how you 
	want to achieve that on your network. If you do not have an NTP server you may 
	want to configure the IPA server as one later.<br><br>

	<strong>Continue to configure the system with these values?</strong>: This is a 
	final confirmation before the script takes over and configures the IPA 
	components. Review the information printed and enter yes if it all looks correct.
	</p>

	<p>The install script will now run through configuration. This process usually 
	takes several minutes. When finished you should get a message saying 
	<strong>The ipa-server-install command was successful</strong>.</p>

	<p>To finish, run this command to receive a Kerberos TGT. Provide the 
	password for the admin user when prompted.</p>

	<pre><code>kinit admin</code></pre>

	<h2>Accessing the Web Interface</h2>
	
	<p>You are now able to manage FreeIPA through the web interface. You can 
	browse either to the IP or the hostname if your DNS is configured correctly.
	You should see a screen similar to this.</p>

	<img alt="FreeIPA Login Screen" src=../images/freeipa-webui.png>

	<p>Login with the username admin and the password you set during the 
	insallation. You are now ready to begin configuring your IPA domain.</p>
    </main>
<p>
<hr>
Consider <a href=../donate.html>donating</a> if this article was useful.
<a class=qr href=../images/bitcoin.png>[BTC]</a>
</p>
 	</main>
    <footer>
	<a href=../kb.html>Knowledge Base</a>
	<br>
	<a href=../index.html>www.chudnick.com</a>
	</footer>
</body>
</html>]]></description>
			</item>
			<item>
					<title>Linux Software RAID</title>
					<guid>https://www.chudnick.com/kb/mdadm-raid.html</guid>
					<link>https://www.chudnick.com/kb/mdadm-raid.html</link>
					<pubDate>Sun, 05 Jun 2022 15:19:11 -0400</pubDate>
					<description><![CDATA[<!DOCTYPE html>
<html lang=en>
    <head>
        <title></title>
        <meta charset="utf-8"/>
        <link rel="shortcut icon" href="favicon.ico"/>
        <link rel='stylesheet' href='../style.css'/>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
<body>
    <header><h1>Linux Software RAID</h1></header>
    <main>
			<p><strong>mdadm</strong> is a tool that allows for creation and
			management of software RAID arrays on Linux. Creating an array
			is a rather straightforward process.</p>

			<h2>Install packages</h2>
			<p>The only package needed is mdadm itself</p>
			<pre><code>apt install mdadm</code></pre>

			<h2>Partition disks</h2>
			<p>We'll need to parition the disks to be used in the array before
			creating it. This isn't anything complicated, we will just be
			creating a single partition using all the space on each disk.</p>

			<p>Use <strong>lsblk</strong> to get a list of disks attached to your system.</p>

			<img src=../images/raid/lsblk.png alt="lsblk command">

			<p>Then use <strong>fdisk</strong> to edit the partition table of the
			first disk. In my case this would be <strong>/dev/sdb</strong>.
			Be sure that you are selecting the correct disk as selecting the
			wrong one can result in data being lost.</p>

			<pre><code>fdisk /dev/sdb</code></pre>

			<p>Use <strong>g</strong> to create a new GUID partition table.
			Use <strong>n</strong> to create a new partition, and then just press
			enter at all of the prompts to accept the defaults. Finally use
			<strong>w</strong> to write the changes. You can use lsblk again
			to verify the change and you should see that /dev/sdb now has a
			partition <strong>/dev/sdb1</strong>.</p>

			<img src=../images/raid/fdisk.png alt="fdisk command">

			<p>Repeat this process for your other disks before continuing.</p>


			<h2>Create the array</h2>
			<p>Creating the array is done with a single command, but takes just a
			bit of planning.</p>
			<ul>
					<li>Define an ID for the array, which is just a
					number identifier. I use <em>0</em> in the command.</li>
					<li>Determine the RAID level you want to use. I am going to
					use RAID5 in this example. The argument to --level is the
					number of the RAID level</li>
					<li>Determine the devices that will be used in the array.
					Somewhat contrary to the argument name, these devices
					will actually be the partitions of the disks and not the
					disks themselves. In the command, give the number of
					partitions (<em>3</em>) and then a space-separated list of the
					partitions that will be active in the array
					(<em>/dev/sdb1 /dev/sdc1 /dev/sdd1</em>).</li>
					<li>If you want to have any spare devices in the array you
					will define them with the --spare-devices argument. These are
					defined in the exact same way as the active RAID devices. In
					the example I use <em>1</em> spare <em>/dev/sde1</em>. Spare
					devices are hot-spares that will automatically be inserted into
					the array if one of the disks fails.</li>
			</ul>

			<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>

			<p>After creating the array run the following command to get details
			and the status of the array. It will take a bit to initialize the
			array, you will know this is done when the state is clean. You do not
			need to wait for the array to completely intialize to continue.</p>

			<pre><code>mdadm --detail /dev/md<em>0</em></code></pre>

			<p>Take note of the UUID and name values as we will need them in the
			next step.</p>

			<p>Before moving on we need to make a filesystem on the array. I'm
			going to make a simple EXT4 filesystem here.</p>

			<pre><code>mkfs.ext4 /dev/md<em>0</em></code></pre>


			<h2>Configuration Files</h2>

			<p>Open the mdadm configuration file at
			<strong>/etc/mdadm/mdadm.conf</strong> and append this line. Replace
			<em>uuid</em> and <em>name</em> with the values you got when running
			mdadm --detail, replace <em>0</em> whatever ID you chose.</p>

			<pre><code>ARRAY /dev/md<em>0</em> metadata=1.2 UUID=<em>uuid</em> name=<em>name</em></code></pre>

			<p>Optionally, create an <strong>/etc/fstab</strong> entry for
			automounting of the array. Replace /mnt/raid with the directory
			where you want to mount the array. If you made a filesystem other than
			ext4 make sure to change that value.</p>

			<pre><code>/dev/md<em>0</em> /mnt/raid ext4 defaults 0 1</code></pre>

<p>
<hr>
Consider <a href=../donate.html>donating</a> if this article was useful.
<a class=qr href=../images/bitcoin.png>[BTC]</a>
</p>
 	</main>
    <footer>
	<a href=../kb.html>Knowledge Base</a>
	<br>
	<a href=../index.html>www.chudnick.com</a>
	</footer>
</body>
</html>

					]]></description>
					</item>
			<item>
					<title>Integrating InfluxDB and Icinga</title>
					<guid>https://www.chudnick.com/kb/icinga-influx.html</guid>
					<link>https://www.chudnick.com/kb/icinga-influx.html</link>
					<pubDate>Tue, 31 May 2022 06:22:43 -0400</pubDate>
					<description><![CDATA[<<!DOCTYPE html>
<html lang=en>
    <head>
        <title></title>
        <meta charset="utf-8"/>
        <link rel="shortcut icon" href="favicon.ico"/>
        <link rel='stylesheet' href='../style.css'/>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
<body>
    <header><h1>Integrating InfluxDB and Icinga</h1></header>
    <main>
	<p>Icinga2 has built-in support for writing monitoring data to InfluxDB. 
	This makes Icinga quite extensible as it allows for other programs to read 
	the gathered data from InfluxDB. For example, Icinga does not have built-in 
	graphing support. But monitoring data can be written to InfluxDB and then 
	consumed by a dedicated graphing tool like Grafana.</p>

	<h2>Install Packages</h2>
	<p>Let's install a few necessary packages</p>

	<pre><code>apt install influxdb influxdb-client ssl-cert</code></pre>

	<h2>Generate self-signed certificate</h2>
	<p>Now generate a self-signed certificate for accessing InfluxDB over TLS</p>

	<pre><code>make-ssl-cert generate-default-snakeoil
usermod -aG ssl-cert influxdb</code></pre>

	<h2>Create Database and Users</h2>
	<p>Start and enable the InfluxDB service</p>

	<pre><code>systemctl enable --now influxdb</code></pre>

	<p>Now we'll create our database and users. We'll be creating 3 users with 
	different access rights. An admin user with full control over the database, a 
	user with write access that Icinga will use to write data to the database, and 
	a read-only user to allow external programs to read data from the database.</p>

	<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;"
</code></pre>

<h2>Configuration Files</h2>

	<p>Then right InfluxDB's configuration file at 
	<em>/etc/influxdb/influxdb.conf</em></p>

	<pre><code>reporting-enabled = false
[meta]
  dir = "/var/lib/influxdb/meta"
[data]
  dir = "/var/lib/influxdb/data"
  wal-dir = "/var/lib/influxdb/wal"
[coordinator]
[retention]
[shard-precreation]
[monitor]
[http]
  enabled = true
  bind-address = ":8086"
  auth-enabled = true
  https-enabled = true
  https-certificate = "/etc/ssl/certs/ssl-cert-snakeoil.pem"
  https-private-key = "/etc/ssl/private/ssl-cert-snakeoil.key"
[ifql]
[logging]
[subscriber]
[[graphite]]
[[collectd]]
[[opentsdb]]
[[udp]]
[continuous_queries]
[tls]
  min-version = "tls1.2"</code></pre>

	<p>And restart InfluxDB to pickup the changes</p>

	<pre><code>systemctl restart influxdb</code></pre>

	<p>Then we need to configure Icinga to write data to our database. Start by 
	enabling the influxdb feature in Icinga</p>

	<pre><code>icinga2 feature enable influxdb</code></pre>

	<p>Now we tell Icinga how to write to our database. Open the configuration
	file at <em>/etc/icinga2/features-available/influxdb.conf</em> and replace
	with the following</p>

	<pre><code>object InfluxdbWriter \"influxdb\" {
  host = "127.0.0.1"
  port = 8086
  username = "icingauser"
  password = "<em>icinga_password</em>"
  ssl_enable = true
  database = "icinga2"
  flush_threshold = 1024
  flush_interval = 10s
  host_template = {
  measurement = "$host.check_command$"
    tags = {
      hostname = "$host.name$"
    }
  }
  service_template = {
    measurement = "$service.check_command$"
    tags = {
      hostname = "$host.name$"
      service = "$service.name$"
    }
  }
}</code></pre>

	<p>And finally restart Icinga to make those changes live.</p>

	<pre><code>systemctl restart icinga2</code></pre>

	<p>Icinga2 will now be writing the data it collects to your InfluxDB instance.</p>

    </main>
<p>
<hr>
Consider <a href=../donate.html>donating</a> if this article was useful.
<a class=qr href=../images/bitcoin.png>[BTC]</a>
</p>
 	</main>
    <footer>
	<a href=../kb.html>Knowledge Base</a>
	<br>
	<a href=../index.html>www.chudnick.com</a>
	</footer>
</body>
</html>
]]></description>
			</item>
			<item>
					<title>Postfix and Dovecot Mail Server</title>
					<guid>https://www.chudnick.com/kb/mail-server.html</guid>
					<link>https://www.chudnick.com/kb/mail-server.html</link>
					<pubDate>Mon, 30 May 2022 08:03:44 -0400</pubDate>
					<description><![CDATA[<!DOCTYPE html>
<html lang=en>
<head>
<title></title>
<meta charset="utf-8"/>
<link rel="shortcut icon" href="favicon.ico"/>
<link rel='stylesheet' href='../style.css'/>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<header><h1>Postfix and Dovecot Mail Server</h1></header>
<main>	

<p>Postfix and dovecot will be the two primary pieces of our mail sever.
Postfix is the mail transport agent that handles the sending and 
receiving of mail and dovecot is the IMAP server that will allow us to 
access our mail from a mail client such as mutt. The server will also
have several other supporting components, a complete list of which is:</p>

<ul>
		<li><em>SpamAssassin</em> for spam filtering</li> 
		<li><em>OpenDKIM</em> for DKIM verification and signing</li> 
		<li><em>Postgrey</em> for greylisting</li> 
		<li><em>Policyd-SPF</em> for SPF verification</li> 
		<li><em>OpenDMARC</em> for DMARC verification</li> 
</ul>

<p>You can use <a href=https://git.chudnick.com/mail-tools/server/deploy-server>
this script</a> I have written to automate this process, but I would
recommend that you run through the tutorial first to understand 
what is being done.</p>

<p>Please note that this tutorial is loosely intended for small personal mail 
servers. Using PAM for authentication, as is done here, is not a scalable solution 
for working with a large number of users. I do plan on covering Dovecot LDAP 
authentication at some point which would be a better solution in an enterprise 
setting.</p>

<h2>Install Packages</h2>
<p>Let's start by installing the required packages. Note that if you already 
have Apache installed on the server, replace <em>python3-certbot-nginx</em> 
with <em>python3-certbot-apache</em>.</p>
<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>
<p>During the installation of Postfix you will get a Debconf prompt in which 
you need to select "Internet Site" and then provide your domain name,
<strong>example.com</strong>.</p>

<h2>Get a certificate</h2>
<p>Now we'll use Certbot to get a certificate for our server. If you are 
using Apache replace <em>nginx</em> with <em>apache2</em>.</p>

<pre><code>systemctl stop nginx
certbot certonly --standalone -d <strong>mail.example.com</strong> 
systemctl start nginx</code></pre>

<h2>Postfix Main Configuration</h2>
<p>In this section we will be doing the bulk of the postfix configuration. 
The postconf command used throughout appends (or changes) 
the specified configuration item in /etc/postfix/main.cf</p>

<h3>Network Configuration</h3>

<p>Let's start by configuring some network and domain information.</p>

<pre><code>postconf -e "myorigin = <strong>example.com</strong>"
postconf -e "mydestination = \$myhostname, \$mydomain, localhost"
postconf -e "mynetworks = 127.0.0.0/8 [::1]/128"
postconf -e "myhostname = <strong>mail.example.com</strong>"
</code></pre>

<p>Next, point postfix to the cerbot key and certificate, as well as the distro's
CA certificates.</p>

<pre><code>postconf -e "smtpd_tls_key_file=/etc/letsencrypt/live/<strong>mail.example.com</strong>/privkey.pem"
postconf -e "smtpd_tls_cert_file=/etc/letsencrypt/live/<strong>mail.example.com</strong>/fullchain.pem"
postconf -e "smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt"
</code></pre>

<p>Harden the TLS configuration by forcing strong
protocols and ciphers, and requiring that authentication occur only over an
encrypted session.</p>
<pre><code><em># Require authentication over TLS and optionally use it for sending and receiving mail</em>
postconf -e "smtpd_tls_auth_only = yes"
postconf -e "smtpd_tls_security_level = may"
postconf -e "smtp_tls_security_level = may"

<em># Force the use of TLSv1.2 or TLSv1.3</em>
postconf -e 'smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1'
postconf -e 'smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1'
postconf -e 'smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1'
postconf -e 'smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1'

<em># Prefer server ciphers</em>
postconf -e "tls_preempt_cipherlist = yes"

<em># Force strong ciphers</em>
postconf -e "smtpd_tls_ciphers = high"
postconf -e "smtpd_tls_mandatory_ciphers = high"
postconf -e "smtp_tls_ciphers = high"
postconf -e "smtp_tls_mandatory_ciphers = high"
postconf -e "smtpd_tls_exclude_ciphers = aNULL, eNULL, EXP, LOW, MEDIUM, PSK, SRP, SHA1, kRSA, CAMELLIA, ARIA, DSS, RSA+AES, ADH, AECDH"
postconf -e "smtp_tls_exclude_ciphers = aNULL, eNULL, EXP, LOW, MEDIUM, PSK, SRP, SHA1, kRSA, CAMELLIA, ARIA, DSS, RSA+AES, ADH, AECDH"
</code></pre>

<h3>Local Recipients and Aliases</h3>

<p>Here we configure the bulk of the postfix built-in security settings which are
structured as a series of access restrictions. Do not edit these settings without 
first reading the Postfix documentation as an incorrect change could inadvertently 
make your server an open relay.</p>

<pre><code>postconf -e "smtpd_helo_required = yes"
postconf -e "smtpd_sender_login_maps = proxy:hash:/etc/postfix/login_maps"
postconf -e "smtpd_helo_restrictions = reject_unknown_helo_hostname, reject_non_fqdn_helo_hostname" 
postconf -e "smtpd_sender_restrictions = reject_sender_login_mismatch, reject_non_fqdn_sender, reject_unknown_sender_domain"
postconf -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"
postconf -e "smtpd_relay_restrictions = permit_sasl_authenticated, reject_unauth_destination"
postconf -e "smtpd_data_restrictions = reject_unauth_pipelining"

<em># Disable VRFY command to prevent harvesting of user accounts on system</em>
postconf -e "disable_vrfy_command = yes"

<em># Change smptd banner (hide distribution)</em>
postconf -e "smtpd_banner = \$myhostname ESMTP \$mail_name"</code></pre>

<p>Now, configure the local mail recipients and some aliases. We'll create 
an account called <strong>mailadmin</strong> to receive mail addressed to 
several other accounts. This is to keep <em>administrative</em> mail separate, but 
you can certainly alias these to your main account later if you would prefer to see 
it there.</p>

<pre><code><em># Set a custom local_recipient_maps here in order to avoid accepting mail for all local accounts</em>
postconf -e "local_recipient_maps = proxy:hash:/etc/postfix/local_maps \$alias_maps"

<em># You will need to manually set a password later to login as mailadmin</em>
adduser --disabled-login --shell /usr/sbin/nologin --gecos "" mailadmin
echo "# postfix aliases
postmaster:     mailadmin
root:           mailadmin
dmarc:          mailadmin
" &gt; /etc/aliases

<em># Update address databases</em>
echo "mailadmin@<em>mail.example.com</em>    mailadmin" &gt; /etc/postfix/login_maps
echo "mailadmin mailadmin" &gt; /etc/postfix/local_maps
newaliases
postmap /etc/postfix/login_maps
postmap /etc/postfix/local_maps
</code></pre>

<h3>Mail Delivery</h3>


<p>These commands configure our mail delivery preferences. Mail will be 
delivered inside a user's home folder with a maildir-style mailbox using
dovecot.</p>

<pre><code><em># Maildir delivery to $HOME/Mail/Inbox/</em>
postconf -e "home_mailbox = Mail/Inbox/"
<em># Deliver mail with Dovecot</em>
postconf -e "mailbox_command = /usr/lib/dovecot/deliver"</code></pre>

<h3>Header and Body Checks</h3>

<p>Header and body checks allow for some simple content filtering within Postfix. 
This is done by scanning a message line by line for a configured regex string, 
nothing more. For example, the first header check listed will reject a message 
with an attachment of <em>ransomware.exe</em> but will not block it if sent with 
no extension. This is mostly a protection against uneducated users and poorly 
written mail clients. Other checks block vulnerabilities and improve privacy.</p>

<p>Create a new file <strong>/etc/postfix/header_checks</strong>, then open it in a 
text editor and add the following</p>
<pre><code><em># Block files with common executable extensions</em>
/name=[^&gt;]*\.(exe|pif|com|dll|vbs|bat|sh|bash|so|zip|tar|gz|cpio)/ REJECT

<em># Block message/partial vulnerability</em>
/message\/partial/ REJECT

<em># Remove Received string that is created when spamassassin reinjects message into postfix</em>
<em># This is to prevent leaking the userid of the spamassassin user</em>
/^Received:.*userid.*/  IGNORE

<em># Remove User-Agent strings from headers</em>
/^User-Agent: .*/       IGNORE</code></pre>

<p>Create another new file <strong>/etc/postfix/body_checks</strong>, and add this</p>
<pre><code><em># Block messages with iframes</em>
/&lt;iframe/ REJECT" &gt; /etc/postfix/body_checks</code></pre>

<p>And then run these commands to point postfix to the check files.</p>
<pre><code>postconf -e "header_checks = regexp:/etc/postfix/header_checks"
postconf -e "body_checks = regexp:/etc/postfix/body_checks"</code></pre>

<h2>Postfix Master Configuration</h2>
<h3>SMTP client</h3>
<p>This simple command configures the SMTP client process that is responsible 
for sending your mail to other mail servers.</p>

<pre><code>postconf -M "smtp/unix=smtp unix - - y - - smtp"</code></pre>

<h3>Postscreen and SMTP Recipient</h3>
<p>Postscreen is a kind of firewall that sits in front of the Postfix SMTPD 
process and receives all incoming traffic. Postscreen will drop connections 
from IPs on a DNS blacklst, or from clients that violate the SMTP protocol by 
speaking out of turn or sending non-SMTP commands. This adds up to less spam 
connections and therefore a much lighter workload for your server.</p>

<pre><code>postconf -M "smtp/inet=smtp inet n - y - 1 postscreen"
postconf -M "smtpd/pass=smtpd pass - - y - - smtpd"
postconf -P "smtpd/pass/content_filter=spamassassin"
postconf -M "tlsproxy/unix=tlsproxy unix - - y - 0 tlsproxy"
postconf -M "dnsblog/unix=dnsblog unix - - y - 0 dnsblog"
postconf -e "postscreen_dnsbl_sites = zen.spamhaus.org"
postconf -e "postscreen_dnsbl_action = enforce"
postconf -e "postscreen_greet_action = enforce"
</code></pre>

<h3>Submission over TLS (submissions)</h3>
<p>Submission over TLS (aka submissions) is the process you will use to submit 
mail to your server from a mail client. These commands configure submissions to 
use a fully-encrypted session, as opposed to STARTTLS, and to only allow access 
to authenticated clients.</p>

<pre><code>postconf -M "submissions/inet=submissions inet n - y - - smtpd"
postconf -P "submissions/inet/smtpd_tls_wrappermode=yes"
postconf -P "submissions/inet/smtpd_tls_security_level=encrypt"
postconf -P "submissions/inet/smtpd_tls_auth_only=yes"
postconf -P "submissions/inet/smtpd_sasl_auth_enable=yes"
postconf -P "submissions/inet/smtpd_client_restrictions=permit_sasl_authenticated,reject"
postconf -P "submissions/inet/smtpd_helo_restrictions="
postconf -P "submissions/inet/smtpd_sender_restrictions=reject_sender_login_mismatch"
postconf -P "submissions/inet/smtpd_recipient_restrictions=reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject"
postconf -P "submissions/inet/syslog_name=postfix/submissions"
postconf -P 'submissions/inet/smtpd_tls_protocols=!SSLv2,!SSLv3,!TLSv1,!TLSv1.1'
postconf -P 'submissions/inet/smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3,!TLSv1,!TLSv1.1'</code></pre>

<h3>OPTIONAL - submission with mandatory STARTTLS</h3>
<p>Having configured submission over TLS on port 465 this step is optional. 
STARTTLS is considered by some to be less secure than full-session TLS and
may be vulnerable to exploitation.</p>

<pre><code>postconf -M "submission/inet=submission inet n - y - - smtpd"
postconf -P "submission/inet/smtpd_tls_security_level=encrypt"
postconf -P 'submission/inet/smtpd_tls_protocols=!SSLv2,!SSLv3,!TLSv1,!TLSv1.1'
postconf -P 'submission/inet/smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3,!TLSv1,!TLSv1.1'
postconf -P "submission/inet/smtpd_sasl_auth_enable=yes"
postconf -P "submission/inet/smtpd_tls_auth_only=yes"
postconf -P "submission/inet/syslog_name=postfix/submission"
postconf -P "submission/inet/smtpd_helo_restrictions="
postconf -P "submission/inet/smtpd_client_restrictions=permit_sasl_authenticated,reject"
postconf -P "submission/inet/smtpd_helo_restrictions="
postconf -P "submission/inet/smtpd_sender_restrictions=reject_sender_login_mismatch"
postconf -P "submission/inet/smtpd_recipient_restrictions=reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject"
</code></pre>

<h3>SpamAssassin Configuration</h3>
<p>Finally, this command tells Postfix how to interact with SpamAssassin.</p>

<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>

<h2>Dovecot Configuration</h2>
<p>Dovecot configuration is usually split up into many different files under
<strong>/etc/dovecot/conf.d/</strong> but here will be doing all of the
configuration in the primary config file 
<strong>/etc/dovecot/dovecot.conf</strong>. Open that file with your editor
of choice, clear all of its contents, and then replace it with the following.</p>

<pre><code><em># /etc/dovecot/conf.d/10-auth.conf</em>
disable_plaintext_auth = yes
auth_username_format = %n
auth_mechanisms = plain
userdb {
	driver = passwd
}
passdb {
	driver = pam
}

<em># /etc/dovecot/conf.d/10-mail.conf</em>
mail_location = maildir:~/Mail:INBOX=~/Mail/Inbox:LAYOUT=fs
namespace inbox {
	type = private
	prefix = 
	separator = /
	inbox = yes
	subscriptions = yes
	list = yes
}

<em># /etc/dovecot/conf.d/10-master.conf</em>
service imap-login {
# Run login processes in high-security mode (see: LoginProcess.txt in dovecot docs)
service_count = 1
# Disable unencrypted IMAP by setting port for plain IMAP to 0
	inet_listener imap {
		port = 0
	}
	inet_listener imaps {
		port = 993
		ssl = yes
	}
}

<em># Allow postfix to use dovecot SASL</em>
service auth {
	unix_listener /var/spool/postfix/private/auth {
		mode = 0660
		user = postfix
		group = postfix
	}
}

<em># /etc/dovecot/conf.d/10-ssl.conf</em>
ssl = required
ssl_key = &lt;/etc/letsencrypt/live/<strong>mail.example.com</strong>/privkey.pem
ssl_cert = &lt;/etc/letsencrypt/live/<strong>mail.example.com</strong>/fullchain.pem
ssl_client_ca_dir = /etc/ssl/certs
ssl_dh = &lt;/usr/share/dovecot/dh.pem

<em># Mozilla intermediate compatibility</em>
ssl_min_protocol = TLSv1.2
ssl_cipher_list = ECDHE+ECDSA+AESGCM:ECDHE+aRSA+AESGCM:ECDHE+ECDSA+CHACHA20:ECDHE+aRSA+CHACHA20:DHE+aRSA+AESGCM:!aNULL:!eNULL

ssl_prefer_server_ciphers = yes
ssl_client_require_valid_cert = yes


<em># /etc/dovecot/conf.d/15-lda.conf</em>
protocol lda {
	mail_plugins = \$mail_plugins sieve
}

<em># /etc/dovecot/conf.d/15-mailboxes.conf</em>
namespace inbox {
	mailbox Sent {
			special_use = \Sent
			auto = subscribe
	}
	mailbox Trash {
			special_use = \Trash
			auto = create
			autoexpunge = 30d
	}
	mailbox Drafts {
			special_use = \Drafts
			auto = subscribe
	}
	mailbox Spam {
			special_use = \Junk
			auto = create
			autoexpunge = 30d
	}
	mailbox Archive {
			special_use = \Archive
			auto = create
	}
}

<em># /etc/dovecot/conf.d/20-imap.conf</em>
imap_capability = +SPECIAL-USE

<em># /etc/dovecot/conf.d/90-sieve.conf</em>
plugin {
	sieve = ~/.dovecot.sieve
	sieve_default = /var/lib/dovecot/sieve/default.sieve
	sieve_global = /var/lib/dovecot/sieve/
}</code></pre>

<p>Then create the default sieve filtering script at
<strong>/var/lib/dovecot/sieve/default.sieve</strong></p>
<pre><code>require ["fileinto", "mailbox"];
/*
* Discard mail that has a spam score greater than or equal to 10
*/
if header :contains "X-Spam-Level" "**********" {
	discard;
	stop;
}
/*
* Discard messages marked as infected by a virus scanner
*/
if header :contains "X-Virus-Scan" "infected" {
	discard;
	stop;
}
/*
* If message is marked as spam (and falls below discard threshold) put into spam mailbox
*/
if header :contains "X-Spam-Flag" "YES" {
	fileinto "Spam";
}</code></pre>

<p>And compile the script</p>

<pre><code>sievec /var/lib/dovecot/sieve/default.sieve</code></pre>


<p>Finally, configure PAM authentication for dovecot at
<strong>/etc/pam.d/dovecot</strong>. Append these changes leaving any include
statements intact.</p>
<pre><code>auth    required        pam_unix.so
account required        pam_unix.so</code></pre>

<h2>OpenDKIM</h2>
<p>DKIM is a mail-verification method that cryptographically signs mail
to allow receivers to verify the authenticity of the sender. Our mail server
will use DKIM to validate signatures on incoming mail and sign outgoing mail. DKIM
requires a public key to be published via DNS, which will be done near the end of
the guide.</p>

<p>Start by generating the DKIM key</p>

<pre><code>opendkim-genkey -D /etc/dkimkeys -d <em>example.com</em> -s mail
chown opendkim: /etc/dkimkeys/*
chmod 600 /etc/dkimkeys/*
mv /etc/dkimkeys/mail.private /etc/dkimkeys/mail.pem</code></pre>

<p>Here we make a directory for the opendkim socket inside the postfix chroot and 
make it accessible to the postfix user.</p>

<pre><code>mkdir /var/spool/postfix/opendkim
chmod 770 /var/spool/postfix/opendkim
chown opendkim:opendkim /var/spool/postfix/opendkim
usermod -aG opendkim postfix</code></pre>

<p>Edit the configuration file at <strong>/etc/opendkim.conf</strong> 
to be as follows:</p>

<pre><code>On-BadSignature		reject
On-Security		reject
Syslog			yes
SyslogSuccess		yes
LogResults		yes
Canonicalization		simple
Mode			sv
OversignHeaders		From
Domain			<strong>example.com</strong>
Selector			mail
KeyFile			/etc/dkimkeys/mail.pem     
UserID			opendkim
UMask			007
Socket			local:/var/spool/postfix/opendkim/opendkim.sock
PidFile			/run/opendkim/opendkim.pid
TemporaryDirectory		/run/opendkim
InternalHosts		127.0.0.1
TrustAnchorFile		/usr/share/dns/root.key
RequireSafeKeys		True
AlwaysAddARHeader		True
</code></pre>

<h2>OpenDMARC</h2>
<p>DMARC is another mail-verification technology that provides verification of the 
address seen by end-users and either or both of SPF and DKIM. 

<p>Like with OpenDKIM, we need to make a directory inside the postfix chroot
for the socket and assign proper permissions.</p>
<pre><code>mkdir /var/spool/postfix/opendmarc
chmod 770 /var/spool/postfix/opendmarc
chown opendmarc:opendmarc /var/spool/postfix/opendmarc
usermod -aG opendmarc postfix
</code></pre>

<p>Now we write the configuration file at <strong>/etc/opendmarc.conf</strong></p>

<pre><code>PidFile 		/run/opendmarc/opendmarc.pid
PublicSuffixList	/usr/share/publicsuffix/public_suffix_list.dat
RejectFailures  	True
Socket  		local:/var/spool/postfix/opendmarc/opendmarc.sock
Syslog  		True
SyslogFacility  	mail
UMask   		002
UserID  		opendmarc
HistoryFile	/var/run/opendmarc/opendmarc.hist
SPFIgnoreResults	True
SPFSelfValidate	True
</code></pre>

<p>Then create the history file and set permissions.</p>

<pre><code>touch /var/run/opendmarc/opendmarc.hist
chown opendmarc:opendmarc /var/run/opendmarc/opendmarc.hist
chmod 664 /var/run/opendmarc/opendmarc.hist
</code></pre>

<p>Now that both OpenDKIM and OpenDMARC are configured we can define them as milters
in postfix. This will tell postfix to route mail through one or both of these milters
depending on whether it is incoming or outgoing.</p>

<pre><code>postconf -P "smtpd/pass/smtpd_milters=unix:opendkim/opendkim.sock,unix:opendmarc/opendmarc.sock"
postconf -P "submissions/inet/smtpd_milters=unix:opendkim/opendkim.sock"
<em># If you enabled submission on port 587 run this too</em>
postconf -P "submission/inet/smtpd_milters=unix:opendkim/opendkim.sock"
</code></pre>

<h2>Postgrey</h2>
<p>Postgrey implements a spam-filter technique known as greylisting, which
always rejects mail on the first try and for a period of time afterwards known
as the greylist period. The idea behind this being that legitimate senders will
send the mail again later, while spammers, in a rush to send as many messages as
possible before being blacklisted, will not.</p>

<p>Postgrey ships with an extensive whitelist domains that are known
to cause issues (mainly large providers that constantly send from different
addresses). This whitelist file is located at 
<strong>/etc/postgrey/whitelist_clients</strong> and can be appended to include
any domain you do not wish to be subject to greylisting.</p>

<p>The configuration needed here is minimal, just open 
<strong>/etc/default/postgrey</strong> and make these changes</p>

<pre><code>POSTGREY_OPTS="--unix=/var/spool/postfix/private/postgrey --privacy"
POSTGREY_TEXT="Greylisted - see https://www.greylisting.org"</code></pre>

<p>And then enable the service</p>

<pre><code>systemctl enable --now postgrey</code></pre>

<h2>Policyd-SPF</h2>
<p>SPF is yet another mail-verification technology that uses DNS records to 
delegate specific servers as being authorized to send mail for the domain 
(and implicitly all other servers as unauthorized). Policyd-SPF will perform
SPF checking of received mail and reject mail that fails SPF verfication.</p>

<p>First, tell postfix how to access Policyd-SPF</p>

<pre><code>postconf -e "policyd-spf_time_limit = 3600"
postconf -M "policyd-spf/unix=policyd-spf unix - n n - 0 spawn user=policyd-spf argv=/usr/bin/policyd-spf"</code></pre>

<p>And then edit the configuration file at 
<strong>/etc/postfix-policyd-spf-python/policyd-spf.conf</strong></p>

<pre><code>debugLevel = 1
TestOnly = 1
HELO_reject = Fail
Mail_From_reject = Fail
Header_Type = AR
<em># These settings increase false-positive risk</em>
<em># Comment them if you want to reduce that risk</em>
PermError_reject = True
TempError_Defer = True</code></pre>


<h2>SpamAssassin</h2>
<p>SpamAssassin is a spam-filter that will scan all received mail and assign
a spam score based on configured rules. SpamAssassin is much heavier and more
resource-intensive than any of the previous spam-filtering/verification programs
we have configured. The postfix spam-filtering philosophy emphasizes the use
of lightweight checks before passing to an external content filter such as 
SpamAssassin. Ideally, non-legitimate mail will have already been caught by one
of the previous methods, and SpamAssassin will only have to operate on a much
smaller subset of the mail that is sent to our server.</p>

<p>We have actually already told postfix to use SpamAssassin as a content filter
so in this section we just need to edit the configuration file 
<strong>/etc/spamassassin/local.cf</strong>.</p>

<pre><code><em># Clearly indicate message is spam to user</em>
rewrite_header Subject *****SPAM*****
rewrite_header From *****SPAM*****

<em># Set required score to be marked as spam, 5.0 is default.</em>
<em># Lower to make policy more strict or raise to be more lenient.</em>
required_score 5.0

<em># Attach original messages as text/plain instead of message/rfc822 to spam reports</em>
report_safe 2

<em>Do not implicitly trust mail based on IP address except localhost</em>
trusted_networks       127.0.0.1/32
</code></pre>

<p>And finally make a few changes to the defaults file at
<strong>/etc/default/spamassassin</strong></p>

<pre><code>OPTIONS="--listen /var/run/spamd.sock --max-children 5"
PIDFILE=/var/run/spamd.pid
CRON=1</code></pre>

<h2>Wrapping Up</h2>
<p>At this point we have done all of the necessary configuration of the mail
server programs.  We have just a few more minor tasks before your mail server
is operational.</p>

<h3>Configure Firewall</h3>
<p>We need to open the proper ports in the firewall. This example uses UFW.</p>

<pre><code>ufw allow 25 comment "smtp"
ufw allow 465 comment "submission over TLS"
<em># Run this next command only if you enabled submission on port 587</em>
ufw allow 587 comment "mail submission"
ufw allow 993 comment "IMAP over TLS"
ufw reload</code></pre>

<h3>Restart services</h3>
<p>Now let's restart the services to pick up any configuration changes.</p>

<pre><code>systemctl restart postfix
systemctl restart dovecot
systemctl restart opendkim
systemctl restart opendmarc
systemctl enable --now spamassassin
systemctl restart spamassassin
systemctl restart postgrey</code></pre>

<h3>DNS Entries</h3>
<p>Finally, we needs to set some required DNS records to enable mail flow and 
verification.  Begin by logging into your registrar or DNS host and editing
your DNS records.</p>

<h3>A Record</h3>
<p>If you did not set a wildcard A record earlier, you will need to set one now
for <strong>mail</strong>. 
Alternatively, if you are running the mail server on the same server as your 
website, you may want to instead make a CNAME record pointing mail to www.</p>

<h3>MX Record</h3>
<p>MX records tell servers attempting to send you mail where to send it. Open the 
MX records section on your registrar and add a new record. An MX
record consists of a priority and a destination. Set the priority to 10 and the 
destination to <strong>mail</strong>, or whatever your subdomain for this mail
server is. The host value can be left blank or may need to be set to "@" 
depending on your registrar.</p>

<h3>DKIM TXT Record</h3>
<p>Now we will set the three TXT records we need. Open the TXT records tab on 
your registrar.</p>

<p>We'll set the DKIM record first. The command we ran to 
generate our DKIM keys also generates a DNS record for us which will be helpful 
here. Print that to the screen with:</p>

<pre><code>cat /etc/dkimkeys/mail.txt</code></pre>

<p>You should get a lengthy output that looks something like the following. The 
bolded portion is the value.</p>

<pre><code>mail._domainkey	IN	TXT	( <strong>"v=DKIM1; h=sha256; k=rsa; "
	  "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz50PSYL0Ob+OlF/0B77rwlzLe7zF6JKnxQNtMqcOCZ0Dar2FPhSUSz1FR0YmNuoShjMogdgKeojIzgRUqwK5GZ5Lz456qiXWkfAtLPc6UQ/WPoyEBGbJpRBYPGWdN4VoNcHkk/I4csvXW6MOI55ghPOwDmootPkCzNPR6gmNAXMe0duS4Lb+bIjy9QMOxGYVUaQ/b+7xar+fWw"
	  "bA3DjQa3jTLCydzzJpjEMfVaKqNhQ4N+ve7O2Mb3LF5k5B977mtok/6POjVG5HY8g6Pba+GzMFItR6nJO5EE2fyfv6cNbRLsZiM+WQmqvDBst5ejaeapy86F5PdJFlX/TUgXjtuwIDAQAB"</strong> )  ; ----- DKIM key mail for example.com</code></pre>

<p>You can cleanup the spacing of the value as your registrar should automatically 
handle any needed splitting of the record. The parts you need to paste into your 
registrar's web interface should then look like this.</p>

<pre><code><em># Name/Host</em> 
mail._domainkey
<em># TXT Value</em>
"v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz50PSYL0Ob+OlF/0B77rwlzLe7zF6JKnxQNtMqcOCZ0Dar2FPhSUSz1FR0YmNuoShjMogdgKeojIzgRUqwK5GZ5Lz456qiXWkfAtLPc6UQ/WPoyEBGbJpRBYPGWdN4VoNcHkk/I4csvXW6MOI55ghPOwDmootPkCzNPR6gmNAXMe0duS4Lb+bIjy9QMOxGYVUaQ/b+7xar+fWwbA3DjQa3jTLCydzzJpjEMfVaKqNhQ4N+ve7O2Mb3LF5k5B977mtok/6POjVG5HY8g6Pba+GzMFItR6nJO5EE2fyfv6cNbRLsZiM+WQmqvDBst5ejaeapy86F5PdJFlX/TUgXjtuwIDAQAB"</code></pre>

<h3>DMARC TXT Record</h3>
<p>The DMARC record should be as follows:</p>

<pre><code><em># Name/Host</em> 
_dmarc
<em># Value</em> 
"v=DMARC1; p=reject; rua=mailto:dmarc@<strong>example.com</strong>; fo=1"
</code></pre>

<h3>SPF Record</h3>
<p>Your SPF record will look like this.  Remember to replace 
<strong>mail.example.com</strong> with your server name.</p>

<pre><code><em># Name/Host</em> 
@
<em># Value</em>
"v=spf1 a:<strong>mail.example.com</strong> -all"
</code></pre>

<h3>PTR Record</h3>
<p>Many mail servers rely on PTR records for verification purposes so we need 
to make sure our server's IP address resolves to the proper domain name. If 
your mail server is residing on a VPS, you will need to add this record on your 
VPS provider's interface, consult their documentation for details.</p>

<h2>Creating your own Mail User</h2>
<p>Your mail server is now up and running. Let's create an email for you to 
receive mail.</p>

<pre><code>useradd --shell /usr/sbin/nologin --create-home --user-group <strong>user</strong>
echo "<strong>user@example.com	user</strong>" &gt;&gt; /etc/postfix/login_maps
echo "<strong>user	user</strong>" &gt;&gt; /etc/postfix/local_maps
postmap /etc/postfix/login_maps
postmap /etc/postfix/local_maps
postfix reload
</code></pre>

<p>I have a script available for adding and removing users that you can find
<a href=https://git.chudnick.com/mail-tools/server/mailadm>here</a>.

<h3>Connecting From a Mail Client</h3>
<p>When connecting your account to a mail client you need to use these settings.</p>

<ul>
		<li>Username: <strong>user@example.com</strong> </li>

		<li>Password: the password for <strong>user@example.com</strong> </li>

		<li>Server name: <strong>mail.example.com</strong> </li>

		<li>IMAP Port: 993</li>

		<li>IMAP Connection: SSL/TLS</li>

		<li>SMTP Port: 465</li>

		<li>SMTP Connection Type: SSL/TLS</li>
		
</ul> 
<p>
<hr>
Consider <a href=../donate.html>donating</a> if this article was useful.
<a class=qr href=../images/bitcoin.png>[BTC]</a>
</p>
    </main>
    <footer>
			<a href=../kb.html>Knowledge Base</a>
			<br>
			<a href=../index.html>www.chudnick.com</a>
	</footer>
</body>
</html>]]></description>
</item>
<item>
					<title>Icinga2 Agent Configuration</title>
					<guid>https://www.chudnick.com/kb/icinga-agent.html</guid>
					<link>https://www.chudnick.com/kb/icinga-agent.html</link>
					<pubDate>Mon, 30 May 2022 08:03:44 -0400</pubDate>
					<description><![CDATA[<!DOCTYPE html>
<html lang=en>
    <head>
        <title></title>
        <meta charset="utf-8"/>
        <link rel="shortcut icon" href="favicon.ico"/>
        <link rel='stylesheet' href='../style.css'/>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
<body>
    <header><h1>Icinga Agent Node Installation and Configuration</h1></header>
    <main>
		<p>With the Icinga master node configured, the servers we want
		to monitor can now be added as agent nodes. As the names suggest,
		the Icinga master node pushes the desired configuration to agent
		nodes, while agent nodes report the configured status checks back
		to the master. Communication between the master and agent nodes is
		encrypted via TLS, with the master node acting as a certificate
		authority.</p>

		<p>You can find my script to automate this process 
		<a href=https://git.chudnick.com/server-scripts/monitoring/icinga-agent>
				here</a>.</p>

		<h2>Install Pakcages</h2>
		<p>Start by installing the required packages on the server to be 
		monitored.</p>

		<pre><code>apt install icinga2 monitoring-plugins 
monitoring-plugins-contrib</code></pre>

		<h2>Initialize PKI with master</h2>
		<p>Now we need to setup the PKI that will be used for the communication
		with the master node. The first step is to generate a certificate
		signing request. Replace <em>hostname</em> with the FQDN of the server.</p>

		<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>

		<p>Next we save the master node's public key certificate. Replace
		<em>master</em> with the FQDN of your master node.</p>

		<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>

		<p>Receive signed certificate from the master node.</p>

		<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>

		<h2>Deploy configuration files</h2>
		<p>Write Icinga configuration.</p>

		<pre><code><strong>/etc/icinga2/icinga2.conf</strong> 
include "constants.conf"
const NodeName = "$nodename"
include "zones.conf"
include "features-enabled/*.conf"
include &lt;itl&gt;
include &lt;plugins&gt;
include &lt;plugins-contrib&gt;
include &lt;manubulon&gt;
include &lt;windows-plugins&gt;
include &lt;nscp&gt;"</code></pre>

		<p>Write zones configuration.</p>

		<pre><code><strong>/etc/icinga2/zones.conf</strong> 
echo "object Endpoint "<em>hostname</em>" {}
object Zone "<em>hostname</em>" {
  parent = "<em>master</em>"
  endpoints = [ "<em>hostname</em>" ]
}
object Zone "<em>master</em>" {
  endpoints = [ "<em>master</em>" ]
}
object Endpoint "<em>master</em>" {
  host = "<em>master</em>"
}
object Zone "director-global" {
  global = true
}</code></pre>

		<p>Write API configuration file.</p>

		<pre><code><strong>/etc/icinga2/features-available/api.conf</strong> 
echo "object ApiListener \"api\" {
  accept_commands = true
  accept_config = true
}</code></pre>

		<h2>Enable API</h2>
		<p>Next, we need to enable the API on the agent.</p>

		<pre><code>icinga2 feature enable api

mkdir -p /var/lib/icinga2/certs

cp /etc/icinga2/pki/<em>hostname</em>.crt /etc/icinga2/pki/<em>hostname</em>.key /etc/icinga2/pki/ca.crt /var/lib/icinga2/certs/

chown -R nagios: /var/lib/icinga2/certs/</code></pre>

		<h2>Sign agent CSR on Master</h2>
		<p>The only action needed on the master node is to sign the agent's
		CSR. Logon to your master node and run the following:</p>

		<pre><code>fpr="$(icinga2 ca list | tail -1 | cut -d '|' -f 1)"
icinga2 ca sign $fpr</code></pre>

		<h2>Configure Firewall</h2>
		<p>Before finishing we need to open the proper firewall port.
		I will use UFW in the example here and allow traffic only only
		from the master node for best security.</p>

		<pre><code>ufw allow proto tcp from <em>master-ip</em> to any port 5665</code></pre>

		<h2>Restart Icinga on Agent</h2>
		<p>Finally, restart the icinga service on the agent node.</p>

		<pre><code>systemctl restart icinga2</code></pre>

		<p>The Icinga agent node will now pull down configuration from the master.
		You will know that this worked if <em>/var/lib/icinga2/api/zones</em> 
		begins to populate with new files.</p>
<p>
<hr>
Consider <a href=../donate.html>donating</a> if this article was useful.
<a class=qr href=../images/bitcoin.png>[BTC]</a>
</p>
    </main>
    <footer>
			<a href=../kb.html>Knowledge Base</a>
			<br>
			<a href=../index.html>www.chudnick.com</a>
	</footer>
</body>
</html>]]></description>
</item>
<item>
					<title>Icinga2 Director</title>
					<guid>https://www.chudnick.com/kb/icinga-director.html</guid>
					<link>https://www.chudnick.com/kb/icinga-director.html</link>
					<pubDate>Mon, 30 May 2022 08:03:44 -0400</pubDate>
					<description><![CDATA[<!DOCTYPE html>
<html lang=en>
    <head>
        <title></title>
        <meta charset="utf-8"/>
        <link rel="shortcut icon" href="favicon.ico"/>
        <link rel='stylesheet' href='../style.css'/>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
<body>
    <header><h1>Icinga Director</h1></header>
    <main>
		<p>Icinga Director is the web-based configuration tool for Icinga2.
		Director provides a simple interface for configuring the various parts
		of your monitoring environment.  Even if you would rather do all of 
		the configuration from a terminal, I still recommend using Director for
		its self-service API which allows new nodes to register with no interaction
		required on the node (i.e. via a script).</p>

		<p>Start by logging into your Icinga instance. If you followed the 
		<a href=icinga2-master.html>master installation guide</a> you should have
		a tab labeled <strong>Icinga Director</strong>. Click on that, and you should
		see something similar to this:</p>
		<img src=../images/director/director.png>

		<h2>Hosts</h2>
		<p>Let's start by looking at the hosts section. Clicking on the hosts
		button from the Director menu will present you with several options.
		Click on host templates, and then click add to define our first host
		template.  Host templates are the building blocks of Icinga, they allow
		for your nodes to be structured however you see fit, and then to have
		monitoring checks automatically applied to them based on that structure.</p>
		<p>I like to structure my host templates by operating system. For example,
		I have a template called <strong>Linux Server</strong>, which is designed
		to encompass all of the Linux servers in my environment. I then get more
		specific, creating templates based on distro. These templates are children
		of the Linux Server template, so they inherit whatever is applied to the
		parent, but then can have distro-specific checks applied to themselves.</p>
		<p>Start by giving your template a name - I will use <strong>Linux Server
		</strong> here. Groups can be left empty for now, but you may want to
		add groups and apply them to templates later. The check command should
		be set to hostalive. Expand <strong>Icinga Agent and zone settings</strong> 
		and set <em>Icinga2 Agent</em>, <em>Establish connection</em>,  
		and <em>Accepts config</em> to Yes. Click store to save the template.
		Your template should look like this:</p>

		<img src=../images/director/hosttemplate.png>

		<h2>Service templates</h2>
		<p>Let's turn to services now. Return to the Director menu and select 
		Services. The services menu is structured similar to hosts, and we will
		start with the service templates section. The idea behind service templates
		is very similar to host templates. Typically, a service template corresponds
		to a single monitoring command.</p>
		<p>As an example, we'll create a service template for a monitoring
		command that checks the status of a web server. Give the template a
		name, set the check command to http, and finally expand <strong>Icinga
		Agent and zone settings</strong> and set <strong>Run on agent
		</strong> to no. We set this to no because we want Icinga to query
		the web server externally instead of from the web server itself.</p>

		<img src=../images/director/service-template.png>

		<h2>Service sets</h2>
		<p>Service sets are simply groups of service templates. They can be
		structured however you see fit. Service sets can then be applied to
		hosts/host templates to have the checks be automatically applied.</p>

		<p>Add a new service set and give it a name. Then click on the services
		tab and add all of the services you want to group into that set.
		Here is an example of a service set <strong>Linux Standard</strong> 
		 that has service checks that should be applied to all Linux servers.</p>

		<img src=../images/director/service-set.png>

		<p>To bring service sets and host templates together, return to your host
		templates, select Linux Server, select the services tab, and then select
		add service set and choose your desired set from the dropdown menu.</p>

		<img src=../images/director/host-services.png>

		<h2>Render your config</h2>
		<p>When you have made all of the changes you need you will need to
		render the Director configuration. Return to the Director menu,
		select Config Deployment, and then select Render config.</p>

		<h2>Self Service API</h2>
		<p>In this last section we will look at what I think is the best feature
		of Director which is the self service API. To enroll a host template
		in the self service API, select the host template, select the agent
		tab, and select generate self service api key.  That's it!
		The string of letters and numbers is the API key associated with this
		host template. Hosts can be enrolled with this API key and Icinga
		will automatically assign the host to this template. With proper
		structuring, you can have hosts be completely provisioned without
		touching Director. In the next article, we will use this to enroll
		a host in Icinga with a shell script.</p>
<p>
<hr>
Consider <a href=../donate.html>donating</a> if this article was useful.
<a class=qr href=../images/bitcoin.png>[BTC]</a>
</p>
    </main>
    <footer>
			<a href=../kb.html>Knowledge Base</a>
			<br>
			<a href=../index.html>www.chudnick.com</a>
	</footer>

</body>
</html>]]></description>
</item>
<item>
					<title>Icinga2 Master Setup</title>
					<guid>https://www.chudnick.com/kb/icinga-master.html</guid>
					<link>https://www.chudnick.com/kb/icinga-master.html</link>
					<pubDate>Mon, 30 May 2022 08:03:44 -0400</pubDate>
					<description><![CDATA[<!DOCTYPE html>
<html lang=en>
    <head>
        <title></title>
        <meta charset="utf-8"/>
        <link rel="shortcut icon" href="favicon.ico"/>
        <link rel='stylesheet' href='../style.css'/>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
<body>
<header><h1>Icinga2 Master Installation</h1></header>
<main>
<p>
This tutorial will cover the installation of the Icinga2
monitoring application master node.  This includes the base
program, the web frontend, and the web-based configuration tool.
This guide was made for Debian but should be similar
on other distributions.
</p>
<p>
I have a script available to automate the steps described in this
tutorial available
<a href=https://git.chudnick.com/path/to/script>from my git repo</a>.
<h2>Install Packages</h2>
<p>Here we will install the required packages. Icinga can use either MySQL 
or PostgreSQL, however this tutorial will use MySQL/MariaDB.</p>
<pre><code>apt install icinga2 icingaweb2 icinga2-ido-mysql icingaweb2-module-director monitoring-plugins monitoring-plugins-contrib default-mysql-server</code></pre>
<h2>Secure MySQL</h2>
<p>This step is optional but strongly recommended.
The mysql_secure_installation script will harden your MySQL instance.</p>
<pre><code>mysql_secure_installation</code></pre>
<p>I recommend the following responses:
<ul>
	<li><em>Switch to unix_socket authentication?</em><strong> Y</strong></li>
	<li><em>Change the root password?</em><strong> Y</strong></li>
	<li><em>Remove anonymous users?</em><strong> Y</strong></li>
	<li><em>Disallow root login remotely?</em><strong> Y</strong></li>
	<li><em>Remove the test database and access to it?</em><strong> Y</strong></li>
	<li><em>Reload privilege tables now?</em><strong> Y</strong></li>
</ul>
</p>

<h2>Create Monitoring Database</h2>
<p>The next several sections will cover creating databases for the various
parts of Icinga. We'll start with the monitoring database.
The following command creates a MySQL database named <em>icinga2</em>
and grants permissions to a user named <em>ido_admin</em>. These values
are arbitrary, but I use them throughout the tutorial so I recommend leaving them
as is. You should definitely change the password though, which in the command
is <em>change me</em>.  You will need this password and the passwords for the
other databases later, so make sure you save them.</p>
<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>

<p>We then need to import the ido schema into the database.</p>

<pre><code>mysql -u root icinga2 &lt;/usr/share/icinga2-ido-mysql/schema/mysql.sql</code></pre>

<p>After importing the schema, we then write the configuration file that tells
the monitoring module how to connect to the database.</p>
<pre><code><strong>/etc/icinga2/features-available/ido-mysql.conf</strong>
library "db_ido_mysql"
object IdoMysqlConnection "ido-mysql" {
	user = "ido_admin",
	password = "<em>ido_password</em>",
	host = "localhost",
	database = "icinga2"
}"</code></pre>

<p>And finally we enable the monitoring module in Icinga.</p>
<pre><code>icinga2 feature enable ido-mysql</code></pre>

<h2>Create Icingaweb2 Database</h2>
<p>This step is nearly identical to the last. This time we create a database
named <em>icingaweb2</em> and grant permissions to the user named
<em>icingaweb2_admin</em>.</p>
<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>

<p>Again we will need to import required schema into the database.</p>
<pre><code>mysql -u root icingaweb2 &lt;/usr/share/icingawbe2/etc/schema/mysql.schema.sql</code></pre>


<p>In this step we create the initial admin user that will be used to login
to the web interface.  As is, this would create a user named <em>admin</em>
with the password <em>changme</em>. You should at least change the password.</p>
<pre><code>passhash="$(php -r "echo password_hash(\"<em>changeme</em>\", PASSWORD_DEFAULT);")"
mysql -u root -e "USE icingaweb2; INSERT INTO icingaweb_user (name, active, password_hash) VALUES (\"<em>admin</em>\", 1, \"$passhash\"); FLUSH PRIVILEGES;"</code></pre>	

<h2>Create Icinga Director Database</h2>
<p>Here we create the database for Director. Director will require more
configuration later, so for now we will just be creating the database.</p>
<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>

<h2>Setup Icinga2 API</h2>
<p>Run the following command to initialize the Icinga API.</p>
<pre><code>icinga2 api setup</code></pre>
<p>And then restart Icinga to apply the changes.</p>
<pre><code>systemctl restart icinga2</code></pre>

<h2>Configure Web Server</h2>
<p>In this section we will configure the web server for accessing
Icinga's web interface and Director configuration tool.
This tutorial will use nginx but apache could be used as well.
We'll start by installing the necessary packages.</p>
<pre><code>apt install nginx php-fpm</code></pre>
<p>Then we need to create the site configuration file.<p>
<pre><code><strong>/etc/nginx/sites-available/icingaweb2.conf</strong>
server {
  listen 80;
  server_name <em>monitoring.example.com</em>
  location ~ ^/icingaweb2/index\.php(.*)$ {
    fastcgi_pass unix:/var/run/php/php-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME /usr/share/icingaweb2/public/index.php;
    fastcgi_param ICINGAWEB_CONFIGDIR /etc/icingaweb2;
    fastcgi_param REMOTE_USER $remote_user;
  }

  location ~ ^/icingaweb2(.+)? {
    alias /usr/share/icingaweb2/public;
    index index.php;
    try_files $1 $uri $uri/ /icingaweb2/index.php$is_args$args;
  }

  <em># Not strictly necessary but allows you to get to icinga without 
  # specifying /icingaweb2 in the URL.</em>
  location = / {
    return 302 http://$host/icingaweb2;
  }

}</code></pre>
<p>And then restart nginx to pick up the changes.</p>
<pre><code>systemctl restart nginx</code></pre>

<p>At this point we are done with the Icinga setup module and so we
can disable it.</p>
<pre><code>icingacli module disable setup</code></pre>

<h2>Write Configuration Files</h2>
<p>In this section we will write several configuration files. Icinga uses
the INI format for its web interface configuration files.</p>
<p>In this first file we tell Icinga about the various resources it should have
access to. These resources are the three databases created previously.
Replace the password in each section with the corresponding password you set
for that database earlier.</p>
<pre><code><strong>/etc/icingaweb2/resources.ini</strong>
[icinga2]
type = "db"
db = "mysql"
host = "localhost"
port = ""
dbname = "icinga2"
username = "ido_admin"
password = "<em>ido password</em>"
charset = ""
use_ssl = "0"

[icingaweb2]
type = "db"
db = "mysql"
host = "localhost"
port = ""
dbname = "icingaweb2"
username = "icingaweb2_admin"
password = "<em>ido password</em>"
charset = ""
use_ssl = "0"


[director]
type = "db"
db = "mysql"
host = "localhost"
port = ""
dbname = "director"
username = "director"
password = "<em>director password</em>"
charset = "utf8"
use_ssl = "0"
</code></pre>

<p>This file controls the authentication settings for the web interface.
Here we tell Icinga to look at the icingaweb2 database for
authentication purposes.</p>
<pre><code><strong>/etc/icingaweb2/authentication.ini</strong>
[icingaweb2]
backend = "db"
resource = "icingaweb2"</code></pre>

<p>Now we tell icinga which users should have admin permissions.
If you changed the username value from <em>admin</em> previously, be sure to update
it here.</p>
<pre><code><strong>/etc/icingaweb2/roles.ini</strong>
[admins]
users = "<em>admin</em>"
resource = "icingaweb2"</code></pre>

<p>Enable the web interface monitoring module.</p>
<pre><code>icingacli module enable monitoring</code></pre>
<p>Then write the configuration file pointing the monitoring module to the 
monitoring database.</p>
<pre><code><strong>/etc/icingaweb2/modules/monitoring/backends.ini</strong>
[icinga]
type = "ido"
resource = "icinga2"</code></pre>

<p>Here we configure Icinga to use the API for communication. 
You will need to get your unique API password generated during the API setup from
from <strong>/etc/icinga2/conf.d/api-users.conf</strong>.
<em>hostname</em> should be the FQDN of the server.</p>
<pre><code><strong>/etc/icingaweb2/modules/monitoring/commandtransports.ini</strong>
[icinga2]
transport = "api"
host = <em>hostname</em>
port = "5665"
username = "root"
password = "<em>api password</em>"</code></pre>

<p>Lastly, tell Icinga to protect variables with potentially sensitive values.</p>
<pre><code><strong>/etc/icingaweb2/modules/monitoring/config.ini</strong>
[security]
protected_customvars = "*pw*,*pass*,*community*"</code></pre>


<h2>Configure Director</h2>
<p>This section will cover configuring Director configuration tool.</p>
<p>Create Director module configuration directory.</p>
<pre><code>mkdir -p /etc/icingaweb2/modules/director</code></pre>

<p>Write the Director configuration file.</p>
<pre><code><strong>/etc/icingaweb2/modules/director/config.ini</strong>
[db]
resource = "director"</code></pre>

<p>Enable Director module and run the initial migration.</p>
<pre><code>icingacli module enable director
icingacli director migration run</code></pre>

<p>Write Director kickstart configuration file.</p>
<pre><code><strong>/etc/icingaweb2/modules/director/kickstart.ini</strong>
[config]
endpoint = "<em>hostname</em>"
username = "root"
password = "<em>api password</em>"</code></pre>

<p>Kickstart Director, then render and deploy the configuration.</p>
<pre><code>icingacli director kickstart run
icingacli director config render
icingacli director config deploy</code></pre>

<p>Director is setup at this point so we will shred the unneeded configuration
file containing sensitive information.</p>
<pre><code>shred -uz /etc/icingaweb2/modules/director/kickstart.ini</code></pre>

<h2>Login to your Monitoring Instance</h2>
<p>You are now ready to login to your monitoring instance with the admin
user created previously. Open a web browser and go to 
http://<em>hostname</em>/icingaweb2.  You should see a screen similar to this:</p>
<a href=../images/icinga-login.png><img src=../images/icinga-login.png alt="Icinagweb2 Login Screen"></a>

<h2>Next Steps</h2>
<p>In the following articles we will go through setting up Icinga2 agents on servers, and configure your monitoring instance through Icinga Director.</p>
<p>
<hr>
Consider <a href=../donate.html>donating</a> if this article was useful.
<a class=qr href=../images/bitcoin.png>[BTC]</a>
</p>
    </main>
    <footer>
			<a href=../kb.html>Knowledge Base</a>
			<br>
			<a href=../index.html>www.chudnick.com</a>
	</footer>
</body>
</html>]]></description>
</item>
		</channel>
</rss>