Good TV Series

Here is a list of TV series that I consider worth watching.

  • Babylon 5 1993 – SciFi with a good story and special effects that were outstanding by 1993 standards and still quite decent by 2024 standards.
  • The West Wing 1999 – one of the best political drama shows. This is NOT like Yes Minister (which was good but is very dated), it’s a serious show.
  • Gene Roddenberry’s Andromeda 2000 – SciFi series about the fall of the “Commonwealth” and attempts to rebuild it. Interesting plot lines about parasites trying to break away from their genetic programming and AIs being somewhat human.
  • Veronica Mars 2004 – detective noir show based around a teenage (in the first seasons) detective.
  • The Big Bang Theory 2007 – OK comedy series with almost all the main characters being Autistic.
  • Dollhouse 2009 – SciFi about renting out people’s bodies with constructed personalities and skillsets downloaded. When logical analysis of possible scientific developments leads to horror. But it’s presented as action/drama.
  • Person of Interest 2011 – started as an action show but became more SciFi with two competing AI systems.
  • The OA 2016 – strange SciFi/Fantasy series, first season was great but the second season was boring IMHO. This isn’t the type of show that people like a bit, probably most people will love it or hate it.
  • Dark Matter 2015 (NOT the 2024 series). About people who wake from cryogenic sleep in a star ship with no memory of their names or background and find themselves in a war.
  • The Good Place 2016, comedy series about people going to heaven with a lot of discussion of philosophy. Could be used to teach children about philosophy.
  • Travellers 2016 – time travel by reprogramming the brains of people in the past who were about to die.
  • Russian Doll Season 1 2019 – strange time-travel comedy. Season 2 wasn’t nearly as good so maybe skip it.
  • Upload 2020 – amusing SciFi show about uploading consciousness to computers and the horrors of capitalism.
  • The Lazarus Project 2022 – interesting new take on time travel, avoid spoilers.
  • Severance 2022 – SciFi show about a technology to separate work memories from outside memories. Similar ideas have been explored in other shows but this one has a particularly interesting take.
  • Baby Reindeer 2024 – black comedy/drama about a comedian who is stalked and sexually abused. Starring Richard Gadd and based on his real experiences. It gives an insight into such things that is rarely offered.

Good Movies

  • Blade Runner 1982 – still a good movie but interpreted differently by today’s standards. The sequel sucks.
  • Dune 1984 – the original version, I didn’t like the remake.
  • Subway 1985 – a weird French film that’s strangely captivating.
  • Alien Nation 1988 – former slaves from outer space seek refuge in the US.
  • DOA 1988 – a man who is murdered by a slow poison tries to catch his murderer. It’s a remake of a 1950 movie and there is another remake from 2022. I don’t know how the 3 movies compare but the 1988 movie is great!
  • They Live 1988 – classic SciFi movie about aliens brainwashing everyone on Earth.
  • La Femme Nikita 1990 – one of the classics of spy action drama.
  • Clerks 1994 – very funny and moderately offensive.
  • Léon: The Professional 1994 – interesting take on a hitman and the young girl he guards.
  • The Usual Suspects 1995 – famous and memorable film.
  • The Celebration (Danish: Festen) 1998 – a very memorable and artistic movie notable for it’s lack of special effects. Avoid spoilers.
  • Lock, Stock and Two Smoking Barrels 1998 – British crime noir.
  • Pi 1998 – while Wall St is crashing a mathematician tries to discover the nature of god and the universe. I didn’t like the end.
  • Ronin 1998 – one of the better spy/action movies with a good car chase.
  • Being John Malkovich 1999 – strange and interesting movie, avoid spoilers.
  • Office Space 1999 – insightful satire of office work.
  • Memento 2000 – an unusual film about memory loss, avoid spoilers.
  • Snatch 2000 – British crime-comedy noir.
  • The Road to Perdition 2002 – one of the best crime movies.
  • Eternal Sunshine of the Spotless Mind 2004 – interesting movie about memory erasure.
  • The Man from Earth 2007 – a professor claims to be a long-lived caveman.
  • Moon 2009 – Interesting SciFi movie about a man working on a moon base on his own for which you should avoid spoilers.
  • Inception 2010 – interesting movie about invading dreams.
  • Bright 2017 – elves and orcs are living alongside humans in the US. Has some similar elements to Alien Nation but fantasy.
  • The Circle 2017 – a movie about a Facebook like company. Interesting and amusing.
  • Bullet Train 2022 – funny and absurd movie.
  • The Menu 2022 – psychological drama about an obsessed gourmet chef and his fans.
  • Poker Face 2022 – very Australian take on a hostage movie.
  • Vespa/Vesper 2022 – artistic movie about biohacking.
  • Robots 2023 – comedy with a new take on robots impersonating people.

Time Travel and Alternate Universe

  • Run Lola Run 1998 – a pioneer in the redo it genre.
  • Primer 2004 – interesting time-travel movie.
  • Coherence 2013 – really good acting and production, but I can’t understand the hostility.
  • Arrival 2016 – really different, AVOID SPOILERS.
  • Tenet 2020 – really needs to be watched in 4K with subtitles. Interesting time-travel movie. Some similarities to Primer but more action. Don’t worry about spoilers because it’s hard enough to understand anyway.

RAM Speed according to Memtest86+

Here are some speed results for RAM according to Memtest86+ on some machines that I have run. Note that the reported speed varies between runs by a few percent.

Thinkpad 600e PentiumII 400Mhz PC-66 RAM (2 DIMMs) 174MB/s
Compaq P3-866MHz PC133 RAM (3 DIMMs, 2*128 + 256) 190MB/s
Compaq Athlon 1GHz PC133 RAM (3 DIMMs) 219MB/s
Compaq P3-800MHz PC133 RAM (1 DIMM) 270MB/s
Compaq P3-800MHz PC133 RAM (3 DIMMs, 2*128 + 256) 240MB/s
Compaq P4 1.5GHz PC133 RAM (3 DIMMs) 486MB/s
Compaq P4 1.5GHz PC133 RAM (1 or 2 DIMMs) 490MB/s
EeePC 701, DDR2-665 PC2-5300 running at DDR2-333 speed 798MB/s
HP Celeron 1.8GHz PC2100/DDR266 (1 DIMM) 824MB/s
HP Celeron 2.4GHz PC2100/DDR266 RAM (2 DIMMs) 984MB/s
Celeron D (32bit) 2.93GHz PC2400/DDR300 PC3200 RAM 1,140MB/s
HP Celeron 2.4GHz PC2700/DDR333 RAM (2 DIMMs) 1,027MB/s
HP Celeron 2.4GHz PC2700/DDR333 RAM (2 DIMMs) 1,375MB/s
Dell PowerEdge T105 Dual-core Opteron 1212 (2GHz) single DDR2-667 ECC RAM 1,670MB/s
Dell PowerEdge T105 Dual-core Opteron 1212 (2GHz) pair of DDR2-667 ECC RAM 1,826MB/s
NEC Pentium E2160 1.8GHz DDR663 (two mismatched DIMMs) 2,307MB/s
IBM Pentium E2160 1.8GHz DDR2-667 PC2-5300 (single DIMM) 2,371MB/s
IBM Pentium E2160 1.8GHz PC2-4200 (paired DIMMs) 2,436MB/s
NEC Pentium E4600 2.4GHz DDR2-667 PC2-5300 (single DIMM) 2,528MB/s
NEC Pentium D 2.8GHz DDR 533 (unpaired DIMMS) 1,600MB/s
NEC Pentium D 2.8GHz DDR 533 (paired DIMMS) 2,600MB/s
Thinkpad T61 DDR2-665 PC2-5300 (single DIMM) 2,023MB/s
Thinkpad T61 DDR2-665 PC2-5300 (paired or mismatched DIMMs) 2,823MB/s
Core Gen2 2492MHz 3,804MB/s
Core Gen2 2492MHz 2 DIMMs 3,930MB/s
Q8400 2*PC6400 4,830MB/s
Dell PowerEdge T320 E5-2430 2.2GHz, DDR3-1600 PC3-12800 (single DIMM) 7,692MB/s
Intel X3450 2.67GHz, DDR3-1066 (4 DIMMs) 10.0GB/s
Thinkpad X1 Yoga Gen3 i5-8350U 1.7GHz, LPDDR3-2133 (paired DIMMs) Battery/AC 7,140MB/s 10.3GB/s
Dell PowerEdge T320 E5-2430 2.2GHz, DDR3-1600 PC3-12800 (pair DIMMs) 11.3GB/s
Dell Lattitude 5590 laptop I5-8350U 1.7GHz, DDR4-2400 PC4-19200 (paired DIMMs) 12.2GB/s
Dell PowerEdge T30 Core E3-1225 3.3GHz, DDR4-2133 PC4-17000 (single DIMM) 12.5GB/s
Dell PowerEdge T320 E5-2430 2.2GHz, DDR3-1600 PC3-12800 (3 DIMMs) 13.5GB/s
Dell Latitude 7480 i7-7660U, DDR4-2400 (paired DIMMs) 13.7GB/s
Dell Lattitude 7410 Laptop i7-10610U DDR4 14.6GB/s
Dell Lattitude 7400 2in1 Laptop i7-8665U DDR3 14.8GB/s
HP Z640 E5-1650v3, DDR4 (pair 2133+ DIMMs) 15.2GB/s
HP Z640 E5-1650v3, DDR4 (four 2133+ DIMMs) 15.3GB/s
HP Z640 E5-1650v3, DDR4 (paired 2133+ LRDIMMs) 15.3GB/s
Thinkpad X1 Carbon Gen5 i5-6300U 2.40GHz, DDR3-1867 (paired DIMMs) Battery/AC 7,640MB/s 16.2GB/s
i7-2600 3.4GHz, DDR3-1333 PC3-10600 (paired DIMMs) 16.6GB/s
AMD Ryzen 5 3600 DDR4-3200 (4*8G DIMMs) 17.3GB/s
Dell PowerEdge T110-II Core i3-3220 3.3GHz, DDR3-1600 PC3-12800 (paired DIMMs) 17.9GB/s

The Wikipedia page on SDRAM lists the theoretical speeds and the various names of the different types of DDR RAM (each type seems to have at least two names).

DDR266 theoretically can do 2100MB/s, but I’ve only seen it do 984MB/s (with two DIMMs).



Wikileaks/Assange supports Russia

How to Talk to Science Deniers

Pro-lifers really want misogyny

A short summary of bad things that Trump did with full support from conservatives

Fake news is almost entirely a right wing thing

PsyPost has an interesting article on the correlation between watching Fox News and lacking knowledge of science and of society

Reserve Bank of Australia inflation calculator

Australian reserve bank interest rates in the past

To The Next Mass Shooter, A Modest Proposal is an insightful comment on mass shootings

The Al Capone theory of sexual harassment

The Right Wing in a Few WordsThey can tell people what to do, no-one can tell them what to do, etc

Nick Fuentes “sex with women is gay”

News Tribune, Dove of Oneness and NESARA


The modern definition of the word “Libertarian” means anarcho-capitalist

Ayn Rand was a sociopath who admired a serial killer

Murray Rothbard believed that parents should be allowed to starve their children to death

A Libertarian walks into a bear, Libertarians take over a town and get attacked by bears

Galt Gulch Chile, one of the failed attempts at a Libertarian town


“Why I Make Terrible Decisions, Or, Poverty Thoughts” is an essay by Linda Tirado AKA killermartinis about her experiences with poverty in the US. A large part of this is addressing advice offered by well-meaning people who don’t know what it’s like and isn’t helpful.


Why Evangelism aims to alienate the non-believers

Has “Homosexual” Always Been in the Bible? No, it was originally about pedophilia.

The Only Moral Abortion is My Abortion – when anti-choice women choose abortion.

The Henry Walker villains wiki page shows the depravity of Jack Chick

Evangelicals believe that Hitler was sent by God

7 Tenets of Satanism

1969 Christmas Mass Incident – how to do communion epically

The different theologies of Jesus and Paul


Things I won’t Work With – blog posts about nasty chemicals like FOOF and ClF3

How a Brisbane school limited the spread of Covid though checking air quality.

Foreign Accent Syndrome, accents can be based on brain issues not on learning.

Audiophiles claiming that different implementations of memcpy change the sound of audio software.


Between 5 and 10 people in the UK die each year putting on socks

Opening the “curtains of science”


How to use a phone as a desktop PC

Linux in a Pixel Shader, how to run a VM remotely on a GPU via VRChat without the permission of the receiver


Why unstructured job interviews (most job interviews) don’t work

The OSS (CIS predecessor) guide to sabotage in WW2 – the section on Interference with Organisations covers things that many corporations do


List of Red Team and Physical Entry Gear with YouTube videos

SSH Audit


Scrotum Self Repair


ETBE Mon is my fork of the traditional Unix system and network monitoring system “Mon” by Jim Trocki. My aim with this fork is to take the best patches available for the existing Mon codebase and the best “contrib” monitoring scripts and include them in one package. I also aim to write all scripts necessary for typical Linux systems. I don’t believe that a working system should involve “mon”, “mon-contrib”, and searching the net for scripts, it should be just the etbemon package.


The Mon mailing list is at [1] . Please use the list instead of mailing me directly so other people can benefit from the answers.

I have created a Wiki for documenting etbemon [2].

If you need commercial support for Mon that is available too. As a basic offer for $100US by Paypal I’ll write a basic monitor script for any Linux service or RFC standard service. If there’s any other commercial support you want then just ask.


  • Version 1.3.8. Improvements to loadavg.monitor, freespace.monitor, and sslcert.monitor.
  • Version 1.3.7. Improvements to sslcert.monitor, deleted-mapped.monitor, ps.monitor, freespace.monitor, and btrfs.monitor. Added xmpp.monitor.
  • Version 1.3.6. Added dell-temp.monitor and hp-temp.monitor to monitor server temperatures. Add -x option to ps.monitor to find process name according to Perl. Make it compile with glibc 2.32. Add selinux.monitor. Add -M option to smartctl.monitor for MegaRAID AKA PERC support and added support for NVMe devices. Improvements to imapnew.monitor deleted-mapped.monitor. Make loadavg.monitor use PSI and cgroups for more useful info.
  • Version 1.3.5. Added monitor for SMART data (smartctl). Made the sslcert check search all IPv6 and IPv4 addresses for each hostname. Make freespace check look for filesystem mountpoint. Made smtpswaks check use latest swaks command-line.
  • Version 1.3.4. Added monitor for deleted mapped files.
  • Version 1.3.3. Improved sslcert.monitor, smtpswaks.monitor, and mailxmpp.alert.
  • Version 1.3.2. Improved http.monitor, dns.monitor, zfs.monitor, freespace.monitor, sslcert.monitor, loadavg.monitor, and mailxmpp.alert. Also improved systemd support.
  • Version 1.3.1. Improvements to freespace.monitor, remote.monitor, loadavg.monitor, and msql-mysql.monitor. Increased the summary length for monshow.
  • This is the first release, numbered 1.3.0 to be higher than other releases of Mon. It is the same as the Debian version of Mon 1.2.0 (which had a dozen patches against Trocki Mon 1.2.0 from 2010).



Memlockd is a daemon that locks files into memory. Then if a machine starts paging heavily the chance of being able to login successfully is significantly increased. The default configuration will lock all the files needed for login to a Debian GNU/Linux system via the console or via ssh.

Free Short Stories

Here are some free short science-fiction stories that I enjoyed reading:

Cory Doctorow has released enough great sci-fi stories that he deserves his own section:

Robert Reed has released enough great free sci-fi stories that he deserves his own section:

Debian Repositories

Here is a list of the Debian repositories I maintain.

All my repositories support the amd64 architecture.


I’ve got a bunch of old Buster repositories. Apart from the SE Linux one I don’t think that any of them provide much benefit at this time. I’m not supporting Buster SE Linux nowadays except as a consulting service.

deb buster selinux
deb buster misc
deb buster wordpress


Bullseye are the main supported repositories at the moment. The WordPress repository is probably most useful. The misc repository has backports of mon packages and packages for my custom management scripts which are probably most useful for the source.

deb bullseye selinux
deb bullseye misc
deb bullseye wordpress


Bookworm is for the next release of Debian, probably not very useful to other people right now.

deb bullseye misc

SE Linux Policy Writing Training

This training exercise is based on a Debian/Bullseye system running SE Linux in a default configuration in Enforcing mode. To set it up put the following in /usr/local/sbin/ and make it mode 0755:


echo -100 > /proc/$$/oom_score_adj

while true ; do
  /sbin/runuser -u nobody -- /bin/nc -q0 -l -p 4000 < $0
  /bin/sleep 1

Then put the following in /etc/systemd/system/adaemon.service:

Description=a daemon



To start it run the following commands:

systemctl enable adaemon.service
systemctl start adaemon.service
  1. Under the /root/pol directory create the file adaemon.fc:

    /usr/local/sbin/ -- gen_context(system_u:object_r:adaemon_exec_t,s0)

    Under the /root/pol directory create the file adaemon.te:

    #require {
    type adaemon_t;
    type adaemon_exec_t;
    init_daemon_domain(adaemon_t, adaemon_exec_t)
  2. After creating those files run the command "make load" (notice there is a symlink for the Makefile). That command compiles the policy module adaemon.pp and loads it into the running policy.

    After loading the policy run "restorecon -v /usr/local/sbin/" to label the file, note that it should tell you that it has labelled it as adaemon_exec_t.

  3. The daemon is still running, run the command "nc localhost 4000" on your VM to observe it's output.

    Run the command "ps axZ|grep init_t" to see processes running as init_t (should only be process 1), in this case is running in the wrong context.

  4. Try to restart the daemon (which will fail) and analyse the audit log for actions that were denied:

    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log
  5. To generate rules suitable for adding to policy the command "audit2allow -l < /var/log/audit/audit.log -R" should be used, the "-R" means to generate policy using macros. The first rule it will suggest adding is:


    Add the above rule to adaemon.te and run the following commands:

    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R

    The above audit2allow gives the following output:


    Setting the attributes of the localization files is not what we want the daemon to do. If you run the command "audit2allow -l < /var/log/audit/audit.log -R -v" it gives a list of all the possible macros that can be used, for the localization access one of the options is miscfiles_read_localization which is a good match for what you expect the daemon to need.

  6. corecmd_search_bin(adaemon_t)

    Add the above to adaemon.te and run the following commands:

    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R
  7. After the previous change the audit2allow output is:


    This is something we want, but checking whether programs are executable isn't sufficient. Run the command "audit2allow -l < /var/log/audit/audit.log -R -v" and you will see that one of the options given is "corecmd_exec_bin(adaemon_t)". So add the following to adaemon.te:


    After adding that rule run the following commands:

    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R

    Below is the output of audit2allow:

    allow adaemon_t self:capability { setgid sys_resource };
    allow adaemon_t self:netlink_audit_socket create;
    allow adaemon_t self:unix_dgram_socket create;
  8. Add the output of audit2allow from the previous step and run:
    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R

    That will give the following output:

    allow adaemon_t self:netlink_audit_socket write;
    allow adaemon_t self:unix_dgram_socket connect;

    Add the above to adaemon.te. You can remove the "kernel_search_fs_sysctls(adaemon_t)" line as that's included in the kernel_read_kernel_sysctls macro, or you could leave it as it doesn't do any harm. Then run the following:

    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R
  9. allow adaemon_t init_runtime_t:dir search;
    allow adaemon_t self:netlink_audit_socket nlmsg_relay;

    The audit2allow command from the previous step gave the above output. Access to init_runtime_t shouldn't be needed and would need a require statement in the policy. So just add the following line to adaemon.te:

    allow adaemon_t self:netlink_audit_socket nlmsg_relay;

    Then run the following commands:

    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R
  10. allow adaemon_t init_runtime_t:dir search;
    allow adaemon_t self:capability audit_write;
    allow adaemon_t self:netlink_audit_socket read;

    The audit2allow from the previous step gave the above. Now as the source of the daemon is visible it would be good to know how that maps to an audit socket. To discover that run the following command:

    grep netlink_audit_socket /var/log/audit/audit.log

    That shows that runuser is the responsible program. It makes sense that a program which is designed to change privileges of a child process will need audit access to log what it does, so this is correct.

    Add the following to adaemon.te:

    allow adaemon_t self:capability audit_write;
    allow adaemon_t self:netlink_audit_socket read;

    Then run the following commands:

    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R
  11. allow adaemon_t init_runtime_t:dir search;
    allow adaemon_t init_t:dir search;
    allow adaemon_t self:capability setuid;
    allow adaemon_t self:process { setrlimit setsched };

    The output from audit2allow in the previous step is above. The init_t and init_runtime_t lines aren't needed (the macro init_daemon_domain gives all the init interaction the daemon needs), so add the following to the policy:

    allow adaemon_t self:capability setuid;
    allow adaemon_t self:process { setrlimit setsched };

    Then run the following:

    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R
  12. allow adaemon_t self:tcp_socket create;

    The above was the only relevant output of the previous audit2allow. Allowing the daemon to create a socket alone isn't enough, what we want is to allow it to listen, accept, and transfer data. Add the following line to the policy:

    allow adaemon_t self:tcp_socket create_stream_socket_perms;

    Then run the following:

    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R
  13. allow adaemon_t unreserved_port_t:tcp_socket name_bind;

    The previous step gave the above output. To address that add the following policy:


    Then run the following:

    make load
    systemctl restart adaemon
    systemctl status adaemon
    audit2allow -l < /var/log/audit/audit.log -R
  14. After the previous step you should get no relevant audit2allow messages and the following command should work:
    nc -q0 localhost 4000

Postfix Training

Basic MTA Configuration

All instructions here are for Postfix. If you want to use a different MTA then you will mostly be working without instructions.

  1. To install Postfix run “apt install postfix” (or for the preconfigured images I setup reconfigure it by running “rm /etc/postfix/ ; dpkg-reconfigure postfix“.
    1. Select “Internet Site” for the type of mail configuration.
    2. Enter the domain name you selected for the mail name (for the VMs I host it will be
    3. When it asks for a user to receive root mail set it to the name of a test user (EG “test”). Make the user with the command “useradd -m test
    4. When it asks for a comma separated list of domains delete the default text for that field and replace it with
    5. For “Force synchronous updates on mail queue” select No as apparently modern filesystems don’t need it and it significantly increases system load. If you say Yes this causes shell code equivalent to the following to be run:
      cd /var/spool/postfix
      chattr +S incoming active bounce defer deferred flush saved corrupt
    6. For “local networks” and the other settings the defaults should be good.
    7. After running the Debian package configuration create the account you specified for the root email, if it was “test” then run “useradd -m test“.
  2. The main Postfix configuration file is /etc/postfix/ Change the myhostname setting to the fully qualified name of the system, something like
    You can edit /etc/postfix/ with vi (or any other editor) or use the postconf command to change it, eg “postconf -e“.
  3. Add “home_mailbox=Maildir/” to the Postfix configuration to make it deliver to a Maildir spool in the user’s home directory (the trailing / is important).
  4. Restart Postfix to apply the changes.
  5. Test delivery by installing swaks via “apt install swaks libio-socket-inet6-perl” and then running the command “swaks -f -t -s localhost“. Note that swaks displays the SMTP data so you can see exactly what happens and if something goes wrong you will see everything about the error.
  6. Inspect /var/log/mail.log to see the messages about the delivery. View the message which is in ~test/Maildir/new.
  7. When other students get to this stage run the same swaks command but with the -t changed to the address in their domain, check the mail.log to see that the messages were transferred and view the mail with less to see the received lines.

Certbot Configuration

To avoid password sniffing we need to use SSL for both authenticated sending of mail and for receiving mail via POP/IMAP.

Run the following commands to install letsencrypt (also the gnutls-bin utilities to test SSL and the libnet-ssleay-perl library for swaks with SSL support) and request a SSL certificate:

apt install certbot gnutls-bin libnet-ssleay-perl
letsencrypt certonly --standalone -m $EMAIL_ADDRESS -d

Run the command “gnutls-cli --starttls-proto=smtp” to check the certificate used by Postfix, it should say that the certificate is not trusted.

Run the following commands to change the Postfix configuration to use the TLS certificate and then repeat the gnutls-cli command to show that it works:

postconf -e smtpd_tls_cert_file=/etc/letsencrypt/live/
postconf -e smtpd_tls_key_file=/etc/letsencrypt/live/
postconf -e smtpd_tls_CAfile=/etc/letsencrypt/live/
postconf -e smtpd_tls_received_header=yes
postconf -e smtpd_tls_loglevel=1
systemctl restart postfix.service

To test the SMTP TLS the command go to another system and run “swaks -f -t -s -tls -tlsp tlsv1_3“, this specifies that the connection must be encrypted via TLS version 1.3 (the most secure SSL protocol).

Basic Pop/IMAP Configuration

All instructions here are for Dovecot. Other POP/IMAP servers are available, but there is less reason for choosing an alternative than there is for the MTA.

  1. Run “apt install dovecot-pop3d dovecot-imapd” to install Dovecot POP and IMAP servers.
    Run “netstat -tln” to see the ports that have daemons listening on them, observe that ports 110, 143, 993, and 995 are in use.
  2. Edit /etc/dovecot/conf.d/10-mail.conf and change “mail_location = mbox:~/mail:INBOX=/var/mail/%u” to “mail_location = maildir:~/Maildir“. Then restart Dovecot.
  3. Run the command “nc localhost 110” to connect to POP, then run the following commands to get capabilities, login, and retrieve mail:
    user test
    retr 1
  4. Run the command “nc localhost 143” to connect to IMAP, then run the following commands to list capabilities, login, and logout:
    a capability
    b login test WHATEVERYOUMADEIT
    c logout
  5. For the above commands make note of the capabilities, we will refer to that later.

Now you have a basically functional mail server on the Internet!


Edit /etc/dovecot/conf.d/10-ssl.conf, remove the following 2 lines:

ssl_cert = </etc/dovecot/private/dovecot.pem
ssl_key = </etc/dovecot/private/dovecot.key

Add the following 2 lines:

ssl_cert =</etc/letsencrypt/live/
ssl_key = </etc/letsencrypt/live/

Restart Dovecot, then run the command “gnutls-cli” to connect via encrypted POP and after the connection is established you can use the same POP commands as before.


SASL is the system of SMTP authentication for mail relaying. It is needed to permit devices without fixed IP addresses to send mail through a server.

Run the following commands to configure Postfix for SASL:

# allow parts of Postfix to work with the same configuration regardless of whether they are running in a chroot
mkdir -p /var/spool/postfix/var/spool
ln -s ../.. /var/spool/postfix/var/spool/postfix
# configure Postfix for SASL with Dovecot as authentication server
postconf -e smtpd_sasl_auth_enable=yes
postconf -e smtpd_sasl_type=dovecot
postconf -e smtpd_sasl_path=/var/spool/postfix/private/auth
postconf -e broken_sasl_auth_clients=yes
postconf -e smtpd_sasl_authenticated_header=yes
systemctl restart postfix.service

Edit /etc/dovecot/conf.d/10-master.conf, uncomment the following lines, and then restart Dovecot:

unix_listener /var/spool/postfix/private/auth {
 mode = 0666

Edit /etc/postfix/, uncomment the line for the submission service, and restart Postfix. This makes Postfix listen on port 587 which is allowed through firewalls.

From another system (IE not the virtual machine you are working on) run “swaks -f -t YOURADDRESS -s -tls -tlsp tlsv1_3” (where YOURADDRESS is an address you use) and note that the message is rejected with “Relay access denied“.

Now run “swaks -f -t YOURADDRESS -s -tls -tlsp tlsv1_3 --auth-user test --auth-password WHATEVER and note that the message is accepted and observe that the mail is delivered (subject to anti-spam measures at the recipient).

Configuring a MUA

If every part of the previous 3 sections is complete then you should be able to setup your favourite MUA. Use “test” as the user-name for SMTP and IMAP, for the SMTP/IMAP server and it should just work!

Note that as the VMs I run are only accessible by SMTP and IMAP over IPv6 you need to have IPv6 on your workstation to connect. If you don’t have IPv6 then run a text mode MUA such as mutt on the VM.

Anti Spam

  1. Header checks allows rejecting mail based on known bad headers (such as blocking known spammers.
    1. Run the following command to send a test message (should work):
      swaks -f -t -s localhost
    2. Create the file /etc/postfix/header_checks with the following contents:
      /^From:.* REJECT

      This consists of a regular expression matching a header line and a policy (generally only REJECT makes sense).

    3. Run the following commands to enable it:
      postconf -e header_checks=regexp:/etc/postfix/header_checks
      systemctl restart postfix.service
    4. Test it with the following swaks command that should be rejected:
      swaks -f -t -s localhost
    1. Run the following command to install the postfix-policyd-spf-python package, this is a daemon that is launched by the Postfix master process for the use of other Postfix processes:
      apt install postfix-policyd-spf-python
    2. Add the following line to /etc/postfix/
      policyd-spf  unix  -       n       n       -       0       spawn user=policyd-spf argv=/usr/bin/policyd-spf
    3. Run the following command to set the smtpd_recipient_restrictions (which hasn’t been set previously in this exercise):
      postconf -e smtpd_recipient_restrictions=permit_sasl_authenticated,permit_mynetworks,check_policy_service unix:private/policyd-spf,reject_unauth_destination
    4. Then restart postfix and SPF checks will be enabled.
    1. Run the following command to install SpamAssassin packages:
      apt install spamassassin spamc spamass-milter
    2. Configure Postfix to use SpamAssassin as a milter (a mail filter program supporting the Milter interface from Sendmail):
      postconf -e smtpd_milters=unix:/var/spool/postfix/spamass/spamass.sock
    3. Edit /etc/default/spamass-milter and set the following as the OPTIONS line:
      OPTIONS="-u spamass-milter -i -r 5 -- -s 100485760"

      This sets a SpamAssassin score of 5 be the criteria for rejecting mail (the default for the SpamAssassin spamd daemon, spamass-milter should match spamd in this regard) and sets 10MB as the amount of data to be scanned in a message (the default is that mail that isn’t small bypasses some tests, the -s option is passed to spamc the SpamAssassin utility that talks to the daemon. Note that any typo in this line will cause things to break in unexpected ways that are difficult to debug.

    4. Run the following commands to restart all daemons and then test sending mail:
      systemctl restart spamassassin.service
      systemctl restart spamass-milter.service
      systemctl restart postfix.service
    5. Get the GTUBE spam test and try sending it to verify that spam mail is rejected:
      apt install wget
      cd /tmp
      swaks -f -t -s --body /tmp/gtube.txt

      This message should be rejected because GTUBE is a standard pattern for testing anti-spam systems.

    1. Create the file /etc/tmpfiles.d/opendkim.conf with the following contents:
      d /run/opendkim 0750 opendkim opendkim - -

      Apply and verify that configuration with the following command:

      systemd-tmpfiles --create /etc/tmpfiles.d/opendkim.conf
      ls -ld /run/opendkim
    2. Run the following command to install DKIM packages:
      apt install opendkim opendkim-tools