This was the last public version of rsiddharth's propellor config (2020-04-29).

-- This is the main configuration file for Propellor, and is used to build
-- the propellor program.    https://propellor.branchable.com/

-- Copyright © 2019 rsiddharth <s@ricketyspace.net>
-- License: BSD2
--
--  _________
-- < s' lazy >
--  ---------
--  \                   .,
--    \         .      .TR   d'
--      \      k,l    .R.b  .t .Je
--        \   .P q.   a|.b .f .Z%
--            .b .h  .E` # J: 2`     .
--       .,.a .E  ,L.M'  ?:b `| ..J9!`.,
--        q,.h.M`   `..,   ..,""` ..2"`
--        .M, J8`   `:       `   3;
--    .    Jk              ...,   `^7"90c.
--     j,  ,!     .7"'`j,.|   .n.   ...
--    j, 7'     .r`     4:      L   `...
--   ..,m.      J`    ..,|..    J`  7TWi
--   ..JJ,.:    %    oo      ,. ....,
--     .,E      3     7`g.M:    P  41
--    JT7"'      O.   .J,;     ``  V"7N.
--    G.           ""Q+  .Zu.,!`      Z`
--    .9.. .         J&..J!       .  ,:
--       7"9a                    JM"!
--          .5J.     ..        ..F`
--             78a..   `    ..2'
--                 J9Ksaw0"'
--                .EJ?A...a.
--                q...g...gi
--               .m...qa..,y:
--               .HQFNB&...mm
--                ,Z|,m.a.,dp
--             .,?f` ,E?:"^7b
--             `A| . .F^^7'^4,
--              .MMMMMMMMMMMQzna,
--          ...f"A.JdT     J:    Jp,
--           `JNa..........A....af`
--                `^^^^^'`

import Propellor
import qualified Propellor.Property.Apt as Apt
import qualified Propellor.Property.Cron as Cron
import qualified Propellor.Property.Dns as Dns
import qualified Propellor.Property.Fail2Ban as Fail2Ban
import qualified Propellor.Property.File as File
import qualified Propellor.Property.Group as Group
import qualified Propellor.Property.Locale as Locale
import qualified Propellor.Property.Nginx as Nginx
import qualified Propellor.Property.Postfix as Postfix
import qualified Propellor.Property.Service as Service
import qualified Propellor.Property.Ssh as Ssh
import qualified Propellor.Property.Sudo as Sudo
import qualified Propellor.Property.User as User



main :: IO ()
main = defaultMain hosts

-- The hosts propellor knows about.
hosts :: [Host]
hosts = publicHosts ++ [ grus ]

publicHosts :: [Host]
publicHosts = [ lyra, virgo, ara ] ++ [ vela ] ++ m31


-- keys.
sCanonicalSshPubKey :: [Char]
sCanonicalSshPubKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDd7kT6tpH4zZ/hFlRmSVH1oJZZJJUvoMd89AiskXAq5rBrvZC90WVOF12OTQVQqslUVV2ze7BCC13UEfK5F2xP+7F6FDqSFApV4lBsJWLNbtDlZY23lTYqi/L6muq3x5tbqJLQjUK5ItORe0Ecqqz1ymSy+Zk+kHmHfnqyoWAQ7Z5GJkRu1B4J9uT3LJDIgLE8m4nJEOoCJ5vnycJfs0LCwHNZ67H38FV3Uw/sGibCNyCSJHQcG+nkKGYzABDcbXmWXedUq0MlRY2TjU22cOzjaAH0mf6M7m6KQCQeXjdxyyLaI3lNOzhBOU7j8/H9GqdRDH8pZ5e4xp+AG3tcrWi2E+47Qp4J9qv0YPgfj7JZ4oJhvCKGkgldOQZ8mkvjDLvAMGte0zpk2SPDlfJeFgfDHMre3nxAAzIfmhaIX2j86LdUh717BmcXDYD//9SubRLdAZrOKh4Iapcotm0STOFUlDa6nvh27DuKIIvq7v/+ID6P4fSNb4h1ktC/3lrhI21ei8ZjBulonJ9XV+BPGlnIzmWL7g5j4dMm90AqZXRRKQMAX9UIizLoCh58KR5gESszQ/8MSrELclI1fUwiY4Wxlvf6ZsGsg3c22xumxlnc85eiAYQ1LEPWNghcyqE96yrIhXrph3unLRsEbZcGxgXw/dcUtjIL9tjDO9zSN6ZuOQ== rsd@grus"

sAraSshPubKey :: [Char]
sAraSshPubKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDTn+zicq75blG6yWY0GIkba93FDbWHOo95k2oqrUVr3llmpuRnYCdp6F7LIiRF4NxDA6hBL5JAlxwS0mKgpbzJjbav7S7XxV1HxrtbO/bQqbcH3pvGCuSvltpZg072pB/g1Uhcv9S2FiOHJsedqlGiqUfucuFdRINYk3a8cQVTZ6XB7AxpqGLova3b+1rdhF6c5DSxgMscvZ+zGANN8tmv/0iGERRRdpvePR7iKZp7ZpqrsS4zXHJO537T6Z05lhaD+Fk/sgz5AUT/fkxIVvsNYL23mVS2fQg1htBAZi4vZ7joZn643bc1w6B7oa8nKEagRu4EWw+LbMeKzecIv0pB s@cygnus.ricketyspace.net"

sLyraSshPubKey :: [Char]
sLyraSshPubKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0UdjqvAo5lICg0BwGv6aQVSO+Ew6ZXFwbwpZiXhZ03+P/Wk98mJJUln+NaKC9lbjQnAShviBy5BIPbexQJ21vvP6mh13Z6xPPtHWRYUy+Y0GYvnc8yKfsnJ2bDaXobpAprfwWpwhW01ZC2xehaZxE+w8dwdAP4v53w5XlLU733f19vRqis4Y0/jyse2BMpsVGzWiLRLprjeLOgdcDB+yLP+EnM46Yj5z0xchbO2uCozloX1yhExDJ2Z4MITx397+/3GYaPEytcMVD/YmIScU4y4nqwa8O/sg5miamD6HUzAWKOHkHgf1HosDbz4JHcEwJqgSvqTeVJ5UWWuPK7YfJ s@cygnus.ricketyspace.net"

-- configure ara
grus :: Host
grus = host "grus.ricketyspace.net" $props
       & osDebian Unstable X86_64
       & Locale.available "en_US.UTF-8"
       & File.hasContent "/etc/motd" (["At grus."])
       & dockerCEInstalled

-- configure ara
ara :: Host
ara = host "ara.ricketyspace.net" $props
       & osDebian (Stable "buster") X86_64
       & Locale.available "en_US.UTF-8"
       & ipv4 "207.246.85.24"
       & File.hasContent "/etc/motd" (["At ara."])
       -- apt
       & Apt.stdSourcesList
       & Apt.unattendedUpgrades
       & Apt.safeUpgrade
       & Apt.installed ["htop", "tmux", "git"]
       -- sshd
       & Ssh.passwordAuthentication False
       -- system
       -- & Fail2Ban.installed
       -- root config
       & File.hasContent "/root/.tmux.conf" tmuxConf
       & Ssh.authorizedKey (User "root") sCanonicalSshPubKey
       & Ssh.authorizedKey (User "root") sAraSshPubKey
       & Ssh.userKeyAt (Just "/root/.ssh/id_rsa")
        (User "root") hostContext (SshRsa, sAraSshPubKey)
       & File.hasPrivContent "/root/switchblade.ovpn"
       (Context "ara.ricketyspace.net")
       -- s config
       & User.accountFor(User "s")
       & User.hasPassword (User "s")
       & User.hasLoginShell (User "s") "/bin/bash"
       & Sudo.enabledFor (User "s")
       & Ssh.authorizedKey (User "s") sCanonicalSshPubKey
       & Ssh.userKeyAt (Just "/home/s/.ssh/id_rsa")
        (User "s") hostContext (SshRsa, sAraSshPubKey)
      & File.hasContent "/home/s/.tmux.conf" tmuxConf
      & File.ownerGroup "/home/s" (User "s") (Group "s")


-- configure virgo
virgo :: Host
virgo = host "virgo.ricketyspace.net" $props
       & osDebian (Stable "buster") X86_64
       & ipv4 "207.246.94.218"
       & Locale.available "en_US.UTF-8"
       & File.hasContent "/etc/motd" (["At virgo."])
       -- apt
       & Apt.stdSourcesList
       & Apt.unattendedUpgrades
       & Apt.safeUpgrade
       & Apt.installed ["htop", "tmux"]
       -- sshd
       & Ssh.passwordAuthentication False
       -- system
       & Fail2Ban.installed
       --- nginx
       & Apt.removed ["apache2"]
       & Nginx.installed
       & ricketyspaceNetDhparamPem
       & ricketyspaceNetCert `onChange` Nginx.restarted
       & ricketyspaceNetCertKey
       & ricketyspaceNetGitCert
       & ricketyspaceNetGitCertKey
       & ricketyspaceNetLyraCert
       & ricketyspaceNetLyraCertKey
       & Nginx.siteEnabled "ricketyspace.net" ricketyspaceNetNginx
       & Nginx.siteEnabled "git.ricketyspace.net" ricketyspaceNetGitNginx
       -- crons
       & stagitGenCron
       -- web
       & Group.exists (Group "pubs") Nothing
       -- web - root
       & File.dirExists "/var/www/root"
       & File.ownerGroup "/var/www/root" (User "www-data") (Group "pubs")
       & File.mode "/var/www/root/" 0O0770
       -- web - git
       & File.dirExists "/var/www/git.rs"
       & File.ownerGroup "/var/www/git.rs" (User "www-data") (Group "pubs")
       & File.mode "/var/www/git.rs/" 0O0770
       -- www-data config
       & User.hasGroup (User "www-data") (Group "pubs")
       -- root config
       & File.hasContent "/root/.tmux.conf" tmuxConf
       & Ssh.authorizedKey (User "root") sCanonicalSshPubKey
       -- g config
       & User.accountFor (User "g")
       & User.hasGroup (User "g") (Group "pubs")
       & Ssh.authorizedKey (User "g") sCanonicalSshPubKey
       & Ssh.authorizedKey (User "g") sAraSshPubKey
       --- c dir config
       & File.dirExists "/home/g/c"
       & File.ownerGroup "/home/g/c" (User "g") (Group "pubs")
       & File.mode "/home/g/c" 0O0770
       -- s config
       & User.accountFor (User "s")
       & User.hasPassword (User "s")
       & Sudo.enabledFor (User "s")
       & Ssh.authorizedKey (User "s") sCanonicalSshPubKey
       -- git-daemon
       -- & rsGitDaemonDefaults `onChange` Service.restarted "git-daemon"
       -- opendkim
       -- & File.dirExists "/etc/opendkim"
       -- & File.containsLines "/etc/opendkim.conf" ricketyspaceNetOpenDkimConf
       -- & File.containsLines "/etc/opendkim/KeyTable" ricketyspaceNetOpenDkimKeyTable
       -- & File.containsLines "/etc/opendkim/SigningTable" ricketyspaceNetOpenDkimSigningTable
       -- & File.containsLines "/etc/opendkim/TrustedHosts" ricketyspaceNetOpenDkimTrustedHosts
       -- & File.dirExists "/etc/dkimkeys/ricketyspace.net"
       -- & File.ownerGroup "/etc/dkimkeys/ricketyspace.net" (User "opendkim") (Group "opendkim")
       -- & File.hasPrivContent "/etc/dkimkeys/ricketyspace.net/mail.private" (Context "ricketyspace.net")
       -- & File.ownerGroup "/etc/dkimkeys/ricketyspace.net/mail.private"
       --      (User "opendkim") (Group "opendkim")
       -- postfix
       & rsMailAliases
       & rsPostfixHeaderChecks
       & rsPostfixRBLOverride
       & File.hasContent "/etc/postfix/main.cf" rsPostfixMainCf
       & File.hasContent "/etc/postfix/master.cf" rsPostfixMasterCf `onChange` Postfix.reloaded



-- configure lyra
lyra :: Host
lyra = host "lyra.ricketyspace.net" $props
       & osDebian (Stable "stretch") X86_64
       & Locale.available "en_US.UTF-8"
       & ipv4 "159.89.229.91"
       & ipv6 "2604:a880:400:d1::77f:8001"
       & alias "ricketyspace.net"
       & alias "git.ricketyspace.net"
       & alias "dingy.space"
       & alias "nfsw.dingy.space"
       & File.hasContent "/etc/motd" (["At lyra."])
       -- apt
       & Apt.stdSourcesList
       & Apt.unattendedUpgrades
       & Apt.safeUpgrade
       & Apt.installed ["git", "etckeeper"
                        , "htop", "sudo", "zsh", "tmux"
                        , "emacs", "rsync", "git-annex"
                        , "cgit", "fcgiwrap", "git-daemon-sysvinit"
                        , "bind9", "bind9-doc"
                        , "opendkim", "opendkim-tools",
                          "postfix", "postfix-doc"
                        , "postfix-policyd-spf-python", "postfix-pcre"
                        , "dovecot-imapd", "python3-markdown", "python3-docutils"
                        , "pandoc", "cvs"
                        , "libgit2-dev", "musl-dev", "make"
                       ]
       -- sshd
       & Ssh.passwordAuthentication False
       -- system
       & Fail2Ban.installed
       --- nginx
       & Apt.removed ["apache2"]
       & Nginx.installed
       & Nginx.siteEnabled "ricketyspace.net" ricketyspaceNetNginx
       & Nginx.siteEnabled "git.ricketyspace.net" ricketyspaceNetGitNginx
       & Nginx.siteEnabled "dingy.space" dingySpaceNginx
       & Nginx.siteEnabled "nfsw.dingy.space" nfswDingySpaceNginx
       & ricketyspaceNetDhparamPem
       & ricketyspaceNetCert `onChange` Nginx.restarted
       & ricketyspaceNetCertKey
       & ricketyspaceNetGitCert
       & ricketyspaceNetGitCertKey
       & ricketyspaceNetLyraCert
       & ricketyspaceNetLyraCertKey
       & dingySpaceCert
       & dingySpaceCertKey
       & nfswDingySpaceCert
       & nfswDingySpaceCertKey
       -- git-daemon
       & rsGitDaemonDefaults `onChange` Service.restarted "git-daemon"
       -- opendkim
       & File.dirExists "/etc/opendkim"
       & File.containsLines "/etc/opendkim.conf" ricketyspaceNetOpenDkimConf
       & File.containsLines "/etc/opendkim/KeyTable" ricketyspaceNetOpenDkimKeyTable
       & File.containsLines "/etc/opendkim/SigningTable" ricketyspaceNetOpenDkimSigningTable
       & File.containsLines "/etc/opendkim/TrustedHosts" ricketyspaceNetOpenDkimTrustedHosts
       --- opendkim for ricketyspace.net
       & File.dirExists "/etc/dkimkeys/ricketyspace.net"
       & File.ownerGroup "/etc/dkimkeys/ricketyspace.net" (User "opendkim") (Group "opendkim")
       & File.hasPrivContent "/etc/dkimkeys/ricketyspace.net/mail.private" (Context "ricketyspace.net")
       & File.ownerGroup "/etc/dkimkeys/ricketyspace.net/mail.private"
            (User "opendkim") (Group "opendkim")
       --- opendkim for dingy.space
       & File.dirExists "/etc/dkimkeys/dingy.space"
       & File.ownerGroup "/etc/dkimkeys/dingy.space" (User "opendkim") (Group "opendkim")
       & File.hasPrivContent "/etc/dkimkeys/dingy.space/mail.private" (Context "dingy.space")
       & File.ownerGroup "/etc/dkimkeys/dingy.space/mail.private"
            (User "opendkim") (Group "opendkim")
       -- postfix
       & rsMailAliases
       & rsPostfixHeaderChecks
       & rsPostfixRBLOverride
       & File.hasContent "/etc/postfix/main.cf" rsPostfixMainCf
       & File.hasContent "/etc/postfix/master.cf" rsPostfixMasterCf `onChange` Postfix.reloaded
       & File.dirExists "/etc/postfix/virtual"
       & rsPostfixVirtualAddresses
       & rsPostfixVirtualDomains
       -- dovecot
       & File.hasContent "/etc/dovecot/dovecot.conf" rsDovecotConf
             `onChange` Service.restarted "dovecot"
       -- bind
       & Dns.primary publicHosts "ricketyspace.net"
       (Dns.mkSOA "lyra.ricketyspace.net" 20180129)
       [
         (RootDomain, NS $ RelDomain "lyra")
       , (RootDomain, NS $ AbsDomain "ns6.gandi.net")
       , (RootDomain, MX 0 $ AbsDomain "lyra.ricketyspace.net")
       , (RootDomain, TXT "v=spf1 mx -all")
       , (RelDomain "_dmarc", TXT "v=DMARC1; p=none; rua=mailto:root@ricketyspace.net")
       , (RelDomain "mail._domainkey", TXT "v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6ClUrJTSt/UISOTEoZy36SfCjuyuajJVGEzYrhuysn2CA3MNt6y3dsHrjalA04Bix02KySMVEHcbScsd54MHhvk364pOkapuAEFIAmvY9SiZGRWsKMK5tgq/aSgH6xFg6M1CupV4QHWFgk193juiEdnB8uwXKTxOdKP/P/xbU3h98MFvXmNzT8sEr5VMZHiTdzqcxCLLfhx27iwjFJh4td6y+0n5YO/M2zf3n9ikXIof/dw4lA5Xo2icI3G88LMl9Tk4vcY0UVXXXulKmrnrn96Nyow0zU31kB/NUb1HbOxaVLz7KJThS+U9NV/66vZ5blwg7aExJXkPfVEtLTca+wIDAQAB")
       ]
       & Dns.primary publicHosts "dingy.space"
       (Dns.mkSOA "lyra.ricketyspace.net" 20191028)
       [
         (RootDomain, NS $ AbsDomain "lyra.ricketyspace.net")
       , (RootDomain, NS $ AbsDomain "ns6.gandi.net")
       , (RootDomain, MX 0 $ AbsDomain "lyra.ricketyspace.net")
       , (RootDomain, TXT "v=spf1 mx -all")
       , (RelDomain "_dmarc", TXT "v=DMARC1; p=reject; rua=mailto:root@dingy.space")
       , (RelDomain "mail._domainkey", TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyxnjDmXyu4Z+RKCDobnebr9Sp+w20Dmn7KFhjgVtKGirQGnemMZTxlXrO+5Ro8T0Ecq+D472EKxQbXPUr5GG7zxIeqldjU3kOChjAoOE8TUsR9pxShdp2qJylGdqvZqPTMApbzKJPXJC8j7yBA9DspLyaPluj50o8XqmbrWkZaZ4/+05E3MfjqbTJsqr0yDl/3/KoB4Qut3LqTCWrpzvGG4Ixu0wUz5FLPq6U6m7xiEnDMtTMnRDuz2XLcNtQXwHtxrkB9TdViDPA//FQONTtnXtaQjwuEh3FMScElkcYAl3tZr+ZRfgzZanH2YIDf80Og0ZQZkD06i1HQxWrM4SQwIDAQAB")
       ]
       -- crons
       & stagitGenCron
       -- web
       & Group.exists (Group "pubs") Nothing
       -- web - root
       & File.dirExists "/var/www/root"
       & File.ownerGroup "/var/www/root" (User "www-data") (Group "pubs")
       & File.mode "/var/www/root/" 0O0770
       -- web - git
       & File.dirExists "/var/www/git.rs"
       & File.ownerGroup "/var/www/git.rs" (User "www-data") (Group "pubs")
       & File.mode "/var/www/git.rs/" 0O0770
       -- root config
       & Ssh.authorizedKey (User "root") sCanonicalSshPubKey
       & File.hasContent "/root/.tmux.conf" tmuxConf
       -- www-data config
       & User.hasGroup (User "www-data") (Group "pubs")
       -- gitdaemon config
       & User.hasGroup (User "gitdaemon") (Group "pubs")
       -- w config
       & User.accountFor (User "w")
       & User.hasGroup (User "w") (Group "pubs")
       & Ssh.authorizedKey (User "w") sCanonicalSshPubKey
       -- g config
       & User.accountFor (User "g")
       & User.hasGroup (User "g") (Group "pubs")
       & Ssh.authorizedKey (User "g") sCanonicalSshPubKey
       & Ssh.authorizedKey (User "g") sAraSshPubKey
       -- c dir config
       & File.dirExists "/home/g/c"
       & File.ownerGroup "/home/g/c" (User "g") (Group "pubs")
       & File.mode "/home/g/c" 0O0770
       -- s config
       & User.accountFor (User "s")
       & User.hasPassword (User "s")
       & Sudo.enabledFor (User "s")
       & Ssh.authorizedKey (User "s") sCanonicalSshPubKey
       & Ssh.authorizedKey (User "s") sAraSshPubKey
       & Ssh.userKeyAt (Just "/home/s/.ssh/id_rsa")
        (User "s") hostContext (SshRsa, sLyraSshPubKey)

vela :: Host
vela = host "vela.dingy.space" $props
       & ipv4 "45.77.145.168"


--- nginx
ricketyspaceNetNginx :: [String]
ricketyspaceNetNginx = [
  "# Adapted from https://gist.github.com/konklone/6532544"
  , ""
  , "server {"
  , "    listen 80;"
  , "    listen [::]:80;"
  , "    listen 443 ssl http2;"
  , "    listen [::]:443 ssl http2;"
  , "    server_name lyra.ricketyspace.net;"
  , "    return 301 https://ricketyspace.net;"
  , ""
  , "    ssl_certificate /etc/ssl/certs/lyra.rs.net.chained.le.pem;"
  , "    ssl_certificate_key /etc/ssl/private/lyra.rs.net.d.le.key;"
  , "}"
  , ""
  , "server {"
  , "    listen 80;"
  , "    listen [::]:80;"
  , "    server_name ricketyspace.net;"
  , "    return 301 https://$host$request_uri;"
  , "}"
  , ""
  , "# The 'spdy' at the end of the listen command below turns on SPDY support."
  , ""
  , "server {"
  , "    listen 443 ssl http2;"
  , "    listen [::]:443 ssl http2;"
  , "    server_name ricketyspace.net;"
  , ""
  , "    root /var/www/root;"
  , "    error_page 404 /404.html;"
  , "    error_page 403 /403.html;"
  , "    default_type text/plain;"
  , ""
  , "    rewrite /gpg.txt /pgp/ permanent;"
  , ""
  , "    location /git-difme/releases/ {"
  , "         autoindex on;"
  , "    }"
  , ""
  , "    location /nsfw/ {"
  , "        autoindex on;"
  , "    }"
  , ""
  , "    location /nfsw/ {"
  , "        autoindex on;"
  , "    }"
  , ""
  , "    location /tmp/x200/ {"
  , "        autoindex on;"
  , "    }"
  , ""
  , "    # Path to certificate and private key."
  , "    # The .crt may omit the root CA cert, if it's a standard CA that ships with clients."
  , "    ssl_certificate /etc/ssl/certs/rs.net.chained.le.pem;"
  , "    ssl_certificate_key /etc/ssl/private/rs.net.d.le.key;"
  , ""
  , "    add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';"
  , ""
  , "    ssl_prefer_server_ciphers on;"
  , "    ssl_ciphers 'kEECDH+ECDSA+AES128 kEECDH+ECDSA+AES256 kEECDH+AES128 kEECDH+AES256 kEDH+AES128 kEDH+AES256 DES-CBC3-SHA +SHA !aNULL !eNULL !LOW !MD5 !EXP !DSS !PSK !SRP !kECDH !CAMELLIA !RC4 !SEED';"
  , ""
  , "    ssl_protocols TLSv1.2 TLSv1.1 TLSv1;"
  , ""
  , "    # as recommended by http://nginx.org/en/docs/http/configuring_https_servers.html"
  , "    ssl_session_cache   shared:SSL:10m;"
  , "    ssl_session_timeout 10m;"
  , "    keepalive_timeout   70;"
  , ""
  , "    # nginx 1.5.9+ ONLY"
  , "    ssl_buffer_size 1400; "
  , ""
  , "    #"
  , "    # Generated by OpenSSL with the following command:"
  , "    #   openssl dhparam -outform pem -out dhparam2048.pem 2048"
  , "    ssl_dhparam /etc/ssl/certs/dhparam4096.pem;"
  , "}"
  ]

ricketyspaceNetGitNginx :: [String]
ricketyspaceNetGitNginx = [
  "# Adapted from https://gist.github.com/konklone/6532544"
  , ""
  , "server {"
  , "    listen 80;"
  , "    listen [::]:80;"
  , "    server_name git.ricketyspace.net;"
  , "    return 301 https://$host$request_uri;"
  , "}"
  , ""
  , "server {"
  , "    listen 443 ssl http2;"
  , "    listen [::]:443 ssl http2;"
  , "    server_name git.ricketyspace.net;"
  , ""
  , "    access_log  /var/log/nginx/git-access.log;"
  , "    error_log   /var/log/nginx/git-error.log;"
  , ""
  , ""
  , "    root /var/www/git.rs/html;"
  , "    error_page 404 /404.html;"
  , "    error_page 403 /403.html;"
  , "    default_type text/plain;"
  , ""
  , "    location ~ /((style|logo|favicon)\\.(css|png|ico))$ {"
  , "         alias /var/www/git.rs/$1;"
  , "    }"
  , ""
  , "    rewrite ^(.*)(tree|plain)/(.*)$ $1file/$3.html permanent;"
  , ""
  , "    # Path to certificate and private key."
  , "    # The .crt may omit the root CA cert, if it's a standard CA that ships with clients."
  , "    ssl_certificate /etc/ssl/certs/git.rs.net.chained.le.pem;"
  , "    ssl_certificate_key /etc/ssl/private/git.rs.net.d.le.key;"
  , ""
  , "    add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';"
  , ""
  , "    ssl_prefer_server_ciphers on;"
  , "    ssl_ciphers 'kEECDH+ECDSA+AES128 kEECDH+ECDSA+AES256 kEECDH+AES128 kEECDH+AES256 kEDH+AES128 kEDH+AES256 DES-CBC3-SHA +SHA !aNULL !eNULL !LOW !MD5 !EXP !DSS !PSK !SRP !kECDH !CAMELLIA !RC4 !SEED';"
  , ""
  , "    ssl_protocols TLSv1.2 TLSv1.1 TLSv1;"
  , ""
  , "    # as recommended by http://nginx.org/en/docs/http/configuring_https_servers.html"
  , "    ssl_session_cache   shared:SSL:10m;"
  , "    ssl_session_timeout 10m;"
  , "    keepalive_timeout   70;"
  , ""
  , "    # nginx 1.5.9+ ONLY"
  , "    ssl_buffer_size 1400;"
  , ""
  , "    #"
  , "    # Generated by OpenSSL with the following command:"
  , "    #   openssl dhparam -outform pem -out dhparam2048.pem 2048"
  , "    ssl_dhparam /etc/ssl/certs/dhparam4096.pem;"
  , "}"
  ]


dingySpaceNginx :: [String]
dingySpaceNginx = [
  "# Adapted from https://gist.github.com/konklone/6532544"
  , ""
  , "server {"
  , "    listen 80;"
  , "    listen [::]:80;"
  , "    server_name dingy.space;"
  , "    return 301 https://$host$request_uri;"
  , "}"
  , ""
  , "server {"
  , "    listen 443 ssl http2;"
  , "    listen [::]:443 ssl http2;"
  , "    server_name dingy.space;"
  , ""
  , "    access_log  /var/log/nginx/dingy.space.log;"
  , "    error_log   /var/log/nginx/dingy.space.log;"
  , ""
  , ""
  , "    root /var/www/dingy.space;"
  , "    error_page 404 /404.html;"
  , "    error_page 403 /403.html;"
  , "    default_type text/plain;"
  , ""
  , "    # Path to certificate and private key."
  , "    # The .crt may omit the root CA cert, if it's a standard CA that ships with clients."
  , "    ssl_certificate /etc/ssl/certs/ds.chained.le.pem;"
  , "    ssl_certificate_key /etc/ssl/private/ds.d.le.key;"
  , ""
  , "    add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';"
  , ""
  , "    ssl_prefer_server_ciphers on;"
  , "    ssl_ciphers 'kEECDH+ECDSA+AES128 kEECDH+ECDSA+AES256 kEECDH+AES128 kEECDH+AES256 kEDH+AES128 kEDH+AES256 DES-CBC3-SHA +SHA !aNULL !eNULL !LOW !MD5 !EXP !DSS !PSK !SRP !kECDH !CAMELLIA !RC4 !SEED';"
  , ""
  , "    ssl_protocols TLSv1.2 TLSv1.1 TLSv1;"
  , ""
  , "    # as recommended by http://nginx.org/en/docs/http/configuring_https_servers.html"
  , "    ssl_session_cache   shared:SSL:10m;"
  , "    ssl_session_timeout 10m;"
  , "    keepalive_timeout   70;"
  , ""
  , "    # nginx 1.5.9+ ONLY"
  , "    ssl_buffer_size 1400;"
  , ""
  , "    #"
  , "    # Generated by OpenSSL with the following command:"
  , "    #   openssl dhparam -outform pem -out dhparam2048.pem 2048"
  , "    ssl_dhparam /etc/ssl/certs/dhparam4096.pem;"
  , "}"
  ]


nfswDingySpaceNginx :: [String]
nfswDingySpaceNginx = [
  "# Adapted from https://gist.github.com/konklone/6532544"
  , ""
  , "server {"
  , "    listen 80;"
  , "    listen [::]:80;"
  , "    server_name dingy.space;"
  , "    return 301 https://$host$request_uri;"
  , "}"
  , ""
  , "server {"
  , "    listen 443 ssl http2;"
  , "    listen [::]:443 ssl http2;"
  , "    server_name nfsw.dingy.space;"
  , ""
  , "    access_log  /var/log/nginx/nfsw.dingy.space.log;"
  , "    error_log   /var/log/nginx/nfsw.dingy.space.log;"
  , ""
  , ""
  , "    root /var/www/nfsw.dingy.space;"
  , "    error_page 404 /404.html;"
  , "    error_page 403 /403.html;"
  , "    default_type text/plain;"
  , ""
  , "    # Path to certificate and private key."
  , "    # The .crt may omit the root CA cert, if it's a standard CA that ships with clients."
  , "    ssl_certificate /etc/ssl/certs/nfsw.ds.chained.le.pem;"
  , "    ssl_certificate_key /etc/ssl/private/nfsw.ds.d.le.key;"
  , ""
  , "    add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';"
  , ""
  , "    ssl_prefer_server_ciphers on;"
  , "    ssl_ciphers 'kEECDH+ECDSA+AES128 kEECDH+ECDSA+AES256 kEECDH+AES128 kEECDH+AES256 kEDH+AES128 kEDH+AES256 DES-CBC3-SHA +SHA !aNULL !eNULL !LOW !MD5 !EXP !DSS !PSK !SRP !kECDH !CAMELLIA !RC4 !SEED';"
  , ""
  , "    ssl_protocols TLSv1.2 TLSv1.1 TLSv1;"
  , ""
  , "    # as recommended by http://nginx.org/en/docs/http/configuring_https_servers.html"
  , "    ssl_session_cache   shared:SSL:10m;"
  , "    ssl_session_timeout 10m;"
  , "    keepalive_timeout   70;"
  , ""
  , "    # nginx 1.5.9+ ONLY"
  , "    ssl_buffer_size 1400;"
  , ""
  , "    #"
  , "    # Generated by OpenSSL with the following command:"
  , "    #   openssl dhparam -outform pem -out dhparam2048.pem 2048"
  , "    ssl_dhparam /etc/ssl/certs/dhparam4096.pem;"
  , "}"
  ]


--- certs
ricketyspaceNetDhparamPem :: Property (HasInfo + UnixLike)
ricketyspaceNetDhparamPem = File.hasPrivContent
  "/etc/ssl/certs/dhparam4096.pem" (Context "ricketyspace.net")

ricketyspaceNetCert :: Property (HasInfo + UnixLike)
ricketyspaceNetCert = File.hasPrivContent
   cert (Context "ricketyspace.net")
   `onChange` File.mode cert 0O0644
  where cert = "/etc/ssl/certs/rs.net.chained.le.pem"

ricketyspaceNetCertKey :: Property (HasInfo + UnixLike)
ricketyspaceNetCertKey = File.hasPrivContent
  "/etc/ssl/private/rs.net.d.le.key" (Context "ricketyspace.net")

ricketyspaceNetGitCert :: Property (HasInfo + UnixLike)
ricketyspaceNetGitCert = File.hasPrivContent
  "/etc/ssl/certs/git.rs.net.chained.le.pem" (Context "git.ricketyspace.net")

ricketyspaceNetGitCertKey :: Property (HasInfo + UnixLike)
ricketyspaceNetGitCertKey = File.hasPrivContent
  "/etc/ssl/private/git.rs.net.d.le.key" (Context "git.ricketyspace.net")

ricketyspaceNetLyraCert :: Property (HasInfo + UnixLike)
ricketyspaceNetLyraCert = File.hasPrivContent
   cert (Context "lyra.ricketyspace.net")
   `onChange` File.mode cert 0O0644
  where cert = "/etc/ssl/certs/lyra.rs.net.chained.le.pem"

ricketyspaceNetLyraCertKey :: Property (HasInfo + UnixLike)
ricketyspaceNetLyraCertKey = File.hasPrivContent
  "/etc/ssl/private/lyra.rs.net.d.le.key" (Context "lyra.ricketyspace.net")

dingySpaceCert :: Property (HasInfo + UnixLike)
dingySpaceCert = File.hasPrivContent
   cert (Context "dingy.space")
   `onChange` File.mode cert 0O0644
  where cert = "/etc/ssl/certs/ds.chained.le.pem"

dingySpaceCertKey :: Property (HasInfo + UnixLike)
dingySpaceCertKey = File.hasPrivContent
  "/etc/ssl/private/ds.d.le.key" (Context "dingy.space")

nfswDingySpaceCert :: Property (HasInfo + UnixLike)
nfswDingySpaceCert = File.hasPrivContent
   cert (Context "nfsw.dingy.space")
   `onChange` File.mode cert 0O0644
  where cert = "/etc/ssl/certs/nfsw.ds.chained.le.pem"

nfswDingySpaceCertKey :: Property (HasInfo + UnixLike)
nfswDingySpaceCertKey = File.hasPrivContent
  "/etc/ssl/private/nfsw.ds.d.le.key" (Context "nfsw.dingy.space")


--- dkim
ricketyspaceNetOpenDkimConf :: [File.Line]
ricketyspaceNetOpenDkimConf = [
  "KeyTable    /etc/opendkim/KeyTable"
  , "SigningTable    /etc/opendkim/SigningTable"
  , "ExternalIgnoreList    /etc/opendkim/TrustedHosts"
  , "InternalHosts    /etc/opendkim/TrustedHosts"
  , "LogWhy    yes"
  , "Socket    inet:12345@localhost"
  ]

ricketyspaceNetOpenDkimKeyTable :: [File.Line]
ricketyspaceNetOpenDkimKeyTable = [
  "mail._domainkey.ricketyspace.net ricketyspace.net:mail:/etc/dkimkeys/ricketyspace.net/mail.private"
  , "mail._domainkey.dingy.space dingy.space:mail:/etc/dkimkeys/dingy.space/mail.private"
  ]

ricketyspaceNetOpenDkimSigningTable :: [File.Line]
ricketyspaceNetOpenDkimSigningTable = [
  "ricketyspace.net mail._domainkey.ricketyspace.net"
  , "dingy.space mail._domainkey.dingy.space"
  ]

ricketyspaceNetOpenDkimTrustedHosts :: [File.Line]
ricketyspaceNetOpenDkimTrustedHosts = [
  "127.0.0.1"
  , "localhost"
  , "ricketyspace.net"
  , "dingy.space"
  ]


-- git-daemon
rsGitDaemonDefaults :: Property UnixLike
rsGitDaemonDefaults  = File.hasContent "/etc/default/git-daemon" [
  "# Defaults for git-daemon initscript"
  , "# sourced by /etc/init.d/git-daemon"
  , ""
  , "#"
  , "# This is a POSIX shell fragment"
  , "#"
  , ""
  , "GIT_DAEMON_ENABLE=true"
  , "GIT_DAEMON_USER=gitdaemon"
  , "GIT_DAEMON_BASE_PATH=/home/g/c"
  , "GIT_DAEMON_DIRECTORY=/home/g/c"
  , ""
  , "# Additional options that are passed to the Daemon."
  , "GIT_DAEMON_OPTIONS=\"\""
  ]

--- postfix
rsPostfixHeaderChecks :: Property DebianLike
rsPostfixHeaderChecks = Postfix.mappedFile "/etc/postfix/header_checks"
  (flip File.hasContent
    [
      "/^Received:.*with ESMTPSA/ IGNORE"
    , "/^X-Originating-IP:/ IGNORE"
    ]) `describe` "postfix header checks configured"
  `onChange` Postfix.reloaded

rsPostfixRBLOverride :: Property (HasInfo + UnixLike)
rsPostfixRBLOverride = File.hasPrivContent
  rblOFile (Context "ricketyspace.net")
  `onChange` na
  where
    na :: Property UnixLike
    na = scriptProperty ["postmap " ++ rblOFile]
         `assume` MadeChange
    rblOFile = "/etc/postfix/rbl_override"

rsPostfixMainCf :: [File.Line]
rsPostfixMainCf = [
  "smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)"
  , "biff = no"
  , ""
  , "append_dot_mydomain = no"
  , "compatibility_level = 2"
  , ""
  , "readme_directory = /usr/share/doc/postfix"
  , "html_directory = /usr/share/doc/postfix/html"
  , ""
  , "mime_header_checks = regexp:/etc/postfix/header_checks"
  , "header_checks = regexp:/etc/postfix/header_checks"
  , ""
  , "smtpd_tls_cert_file=/etc/ssl/certs/lyra.rs.net.chained.le.pem"
  , "smtpd_tls_key_file=/etc/ssl/private/lyra.rs.net.d.le.key"
  , "smtpd_use_tls=yes"
  , "smtpd_tls_auth_only=yes"
  , "smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache"
  , "smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache"
  , ""
  , "smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination"
  , "myhostname = lyra.ricketyspace.net"
  , "alias_maps = hash:/etc/aliases"
  , "alias_database = hash:/etc/aliases"
  , "myorigin = ricketyspace.net"
  , "mydestination = ricketyspace.net, localhost, /etc/postfix/virtual/domains"
  , "virtual_maps = hash:/etc/postfix/virtual/addresses"
  , "relayhost ="
  , "mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128"
  , "mailbox_command ="
  , "mailbox_size_limit = 0"
  , "recipient_delimiter = +"
  , "inet_interfaces = all"
  , "inet_protocols = all"
  , "relay_domains = ricketyspace.net"
  , ""
  , "smtpd_recipient_restrictions = permit_sasl_authenticated,"
  , "      reject_invalid_hostname,"
  , "      reject_unknown_recipient_domain,"
  , "      reject_unauth_destination,"
  , "      reject_rbl_client sbl.spamhaus.org,"
  , "      check_policy_service unix:private/policy-spf"
  , "      permit"
  , ""
  , "smtpd_helo_restrictions = check_helo_access hash:/etc/postfix/rbl_override,"
  , "        reject_invalid_helo_hostname,"
  , "        reject_non_fqdn_helo_hostname"
  , ""
  , "smtpd_client_restrictions = reject_rbl_client dnsbl.sorbs.net"
  , ""
  , "home_mailbox = Maildir/"
  , ""
  , "# dovecot"
  , "smtpd_sasl_type = dovecot"
  , "smtpd_sasl_path = private/auth"
  , "smtpd_sasl_auth_enable = yes"
  , ""
  , "# dkim"
  , "milter_default_action = accept"
  , "milter_protocol = 6"
  , "smtpd_milters = inet:localhost:12345"
  , "non_smtpd_milters = inet:localhost:12345"
  , ""
  , "# spf"
  , "policy-spf_time_limit = 3600s"
  , ""
  , "# map mail username/password to user's unix user/passwd."
  , "local_recipient_maps = proxy:unix:passwd.byname $alias_maps"
  ]

rsPostfixMasterCf :: [File.Line]
rsPostfixMasterCf = [
  "# Do not forget to execute `postfix reload` after editing this file."
  , "#"
  , "# =========================================================================="
  , "# service type  private unpriv  chroot  wakeup  maxproc command + args"
  , "#               (yes)   (yes)   (no)    (never) (100)"
  , "# =========================================================================="
  , "smtp      inet  n       -       y       -       -       smtpd"
  , "submission inet n       -       y       -       -       smtpd"
  , "  -o syslog_name=postfix/submission"
  , "  -o smtpd_tls_security_level=encrypt"
  , "  -o smtpd_sasl_auth_enable=yes"
  , "  -o smtpd_client_restrictions=permit_sasl_authenticated,reject"
  , "  -o milter_macro_daemon_name=ORIGINATING"
  , "smtps     inet  n       -       y       -       -       smtpd -v"
  , "  -o syslog_name=postfix/smtps"
  , "  -o smtpd_tls_wrappermode=yes"
  , "  -o smtpd_sasl_auth_enable=yes"
  , "  -o smtpd_client_restrictions=permit_sasl_authenticated,reject"
  , "  -o milter_macro_daemon_name=ORIGINATING"
  , "pickup    unix  n       -       y       60      1       pickup"
  , "cleanup   unix  n       -       y       -       0       cleanup"
  , "qmgr      unix  n       -       n       300     1       qmgr"
  , "tlsmgr    unix  -       -       y       1000?   1       tlsmgr"
  , "rewrite   unix  -       -       y       -       -       trivial-rewrite"
  , "bounce    unix  -       -       y       -       0       bounce"
  , "defer     unix  -       -       y       -       0       bounce"
  , "trace     unix  -       -       y       -       0       bounce"
  , "verify    unix  -       -       y       -       1       verify"
  , "flush     unix  n       -       y       1000?   0       flush"
  , "proxymap  unix  -       -       n       -       -       proxymap"
  , "proxywrite unix -       -       n       -       1       proxymap"
  , "smtp      unix  -       -       y       -       -       smtp"
  , "relay     unix  -       -       y       -       -       smtp"
  , "showq     unix  n       -       y       -       -       showq"
  , "error     unix  -       -       y       -       -       error"
  , "retry     unix  -       -       y       -       -       error"
  , "discard   unix  -       -       y       -       -       discard"
  , "local     unix  -       n       n       -       -       local"
  , "virtual   unix  -       n       n       -       -       virtual"
  , "lmtp      unix  -       -       y       -       -       lmtp"
  , "anvil     unix  -       -       y       -       1       anvil"
  , "scache    unix  -       -       y       -       1       scache"
  , "#"
  , "# ===================================================================="
  , "# Interfaces to non-Postfix software."
  , "#"
  , "maildrop  unix  -       n       n       -       -       pipe"
  , "  flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}"
  , "#"
  , "# See the Postfix UUCP_README file for configuration details."
  , "#"
  , "uucp      unix  -       n       n       -       -       pipe"
  , "  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)"
  , "#"
  , "# Other external delivery methods."
  , "#"
  , "ifmail    unix  -       n       n       -       -       pipe"
  , "  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)"
  , "bsmtp     unix  -       n       n       -       -       pipe"
  , "  flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient"
  , "scalemail-backend unix  -       n       n       -       2       pipe"
  , "  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}"
  , "mailman   unix  -       n       n       -       -       pipe"
  , "  flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py"
  , "  ${nexthop} ${user}"
  , ""
  , "# For Dovecot."
  , "dovecot   unix  -       n       n       -       -       pipe"
  , "  flags=DRhu user=email:email argv=/usr/lib/dovecot/deliver -f ${sender} -d ${recipient}"
  , ""
  , "# SPF snafu (from https://help.ubuntu.com/community/Postfix/SPF)"
  , "policy-spf  unix  -       n       n       -       -       spawn"
  , "     user=nobody argv=/usr/bin/policyd-spf"
  ]

rsPostfixVirtualAddresses :: Property (HasInfo + UnixLike)
rsPostfixVirtualAddresses = File.hasPrivContent
  vaFile (Context "ricketyspace.net")
  `onChange` na
  where
    na :: Property UnixLike
    na = scriptProperty ["postmap " ++ vaFile]
         `assume` MadeChange
    vaFile = "/etc/postfix/virtual/addresses"


rsPostfixVirtualDomains :: Property (HasInfo + UnixLike)
rsPostfixVirtualDomains = File.hasPrivContent
  vdFile (Context "ricketyspace.net")
  where
    vdFile = "/etc/postfix/virtual/domains"


rsDovecotConf :: [File.Line]
rsDovecotConf = [
  "mail_privileged_group = mail"
  , "namespace inbox {"
  , "  inbox = yes"
  , "  location ="
  , "  mailbox Drafts {"
  , "    special_use = \\Drafts"
  , "  }"
  , "  mailbox Junk {"
  , "    special_use = \\Junk"
  , "  }"
  , "  mailbox Sent {"
  , "    special_use = \\Sent"
  , "  }"
  , "  mailbox \"Sent Messages\" {"
  , "    special_use = \\Sent"
  , "  }"
  , "  mailbox Trash {"
  , "    special_use = \\Trash"
  , "  }"
  , "  prefix ="
  , "}"
  , "passdb {"
  , "  driver = pam"
  , "}"
  , "protocols = \" imap\""
  , "service auth {"
  , "  unix_listener /var/spool/postfix/private/auth {"
  , "    group = postfix"
  , "    mode = 0666"
  , "    user = postfix"
  , "  }"
  , "}"
  , "ssl = required"
  , "ssl_cert = </etc/ssl/certs/lyra.rs.net.chained.le.pem"
  , "ssl_key = </etc/ssl/private/lyra.rs.net.d.le.key"
  , "userdb {"
  , "  driver = passwd"
  , "}"
  ]

rsMailAliases :: Property (HasInfo + UnixLike)
rsMailAliases = File.hasPrivContent
  "/etc/aliases" (Context "ricketyspace.net")
  `onChange` na
  where
    na :: Property UnixLike
    na = scriptProperty ["cd /etc", "newaliases"]
         `assume` MadeChange

-- crons
stagitGenCron :: Property DebianLike
stagitGenCron = Cron.job "stagit-gen"
  (Cron.Times "* */1 * * *")
  (User "www-data") "/var/www/git.rs" cmd
  where
    cmd = "make"


-- common

--- tmux conf
tmuxConf :: [File.Line]
tmuxConf = [
  "set -g prefix C-a"
  , "unbind C-b"
  , "bind C-a send-prefix"
  , "set -g status-style bg=white"
  , "set -g status-right ''"
  ]

--- Docker CE
dockerCEInstalled :: Property DebianLike
dockerCEInstalled = Apt.installed ["docker-ce"]
  `requires` Apt.setSourcesListD [
  "deb [arch=amd64] https://download.docker.com/linux/debian buster stable"
  ] "docker-ce"
  `requires` Apt.trustsKey dockerCEKey

dockerCEKey :: Apt.AptKey
dockerCEKey = Apt.AptKey "docker-ce" $ unlines [
  "-----BEGIN PGP PUBLIC KEY BLOCK-----"
  , ""
  , "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"
  , "YT90qFF93M3v01BbxP+EIY2/9tiIPbrd"
  , "=0YYh"
  , "-----END PGP PUBLIC KEY BLOCK-----"
  ]


m31 :: [Host]  -- Andromeda. Computers outside s' galaxy.
m31 = [
  host "ns6.gandi.net" $ props
    & ipv4  "217.70.177.40"
      ]