propellor

propellor config for hosts.
git clone git://git.ricketyspace.net/propellor.git
Log | Files | Refs | LICENSE

config.hs (42509B)


      1 -- This is the main configuration file for Propellor, and is used to build
      2 -- the propellor program.    https://propellor.branchable.com/
      3 
      4 -- Copyright © 2019 rsiddharth <s@ricketyspace.net>
      5 -- License: BSD2
      6 --
      7 --  _________
      8 -- < s' lazy >
      9 --  ---------
     10 --  \                   .,
     11 --    \         .      .TR   d'
     12 --      \      k,l    .R.b  .t .Je
     13 --        \   .P q.   a|.b .f .Z%
     14 --            .b .h  .E` # J: 2`     .
     15 --       .,.a .E  ,L.M'  ?:b `| ..J9!`.,
     16 --        q,.h.M`   `..,   ..,""` ..2"`
     17 --        .M, J8`   `:       `   3;
     18 --    .    Jk              ...,   `^7"90c.
     19 --     j,  ,!     .7"'`j,.|   .n.   ...
     20 --    j, 7'     .r`     4:      L   `...
     21 --   ..,m.      J`    ..,|..    J`  7TWi
     22 --   ..JJ,.:    %    oo      ,. ....,
     23 --     .,E      3     7`g.M:    P  41
     24 --    JT7"'      O.   .J,;     ``  V"7N.
     25 --    G.           ""Q+  .Zu.,!`      Z`
     26 --    .9.. .         J&..J!       .  ,:
     27 --       7"9a                    JM"!
     28 --          .5J.     ..        ..F`
     29 --             78a..   `    ..2'
     30 --                 J9Ksaw0"'
     31 --                .EJ?A...a.
     32 --                q...g...gi
     33 --               .m...qa..,y:
     34 --               .HQFNB&...mm
     35 --                ,Z|,m.a.,dp
     36 --             .,?f` ,E?:"^7b
     37 --             `A| . .F^^7'^4,
     38 --              .MMMMMMMMMMMQzna,
     39 --          ...f"A.JdT     J:    Jp,
     40 --           `JNa..........A....af`
     41 --                `^^^^^'`
     42 
     43 import Propellor
     44 import qualified Propellor.Property.Apt as Apt
     45 import qualified Propellor.Property.Cron as Cron
     46 import qualified Propellor.Property.Dns as Dns
     47 import qualified Propellor.Property.Fail2Ban as Fail2Ban
     48 import qualified Propellor.Property.File as File
     49 import qualified Propellor.Property.Group as Group
     50 import qualified Propellor.Property.Locale as Locale
     51 import qualified Propellor.Property.Nginx as Nginx
     52 import qualified Propellor.Property.Postfix as Postfix
     53 import qualified Propellor.Property.Service as Service
     54 import qualified Propellor.Property.Ssh as Ssh
     55 import qualified Propellor.Property.Sudo as Sudo
     56 import qualified Propellor.Property.User as User
     57 
     58 
     59 
     60 main :: IO ()
     61 main = defaultMain hosts
     62 
     63 -- The hosts propellor knows about.
     64 hosts :: [Host]
     65 hosts = publicHosts ++ [ grus ]
     66 
     67 publicHosts :: [Host]
     68 publicHosts = [ lyra, virgo, ara ] ++ [ vela ] ++ m31
     69 
     70 
     71 -- keys.
     72 sCanonicalSshPubKey :: [Char]
     73 sCanonicalSshPubKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDd7kT6tpH4zZ/hFlRmSVH1oJZZJJUvoMd89AiskXAq5rBrvZC90WVOF12OTQVQqslUVV2ze7BCC13UEfK5F2xP+7F6FDqSFApV4lBsJWLNbtDlZY23lTYqi/L6muq3x5tbqJLQjUK5ItORe0Ecqqz1ymSy+Zk+kHmHfnqyoWAQ7Z5GJkRu1B4J9uT3LJDIgLE8m4nJEOoCJ5vnycJfs0LCwHNZ67H38FV3Uw/sGibCNyCSJHQcG+nkKGYzABDcbXmWXedUq0MlRY2TjU22cOzjaAH0mf6M7m6KQCQeXjdxyyLaI3lNOzhBOU7j8/H9GqdRDH8pZ5e4xp+AG3tcrWi2E+47Qp4J9qv0YPgfj7JZ4oJhvCKGkgldOQZ8mkvjDLvAMGte0zpk2SPDlfJeFgfDHMre3nxAAzIfmhaIX2j86LdUh717BmcXDYD//9SubRLdAZrOKh4Iapcotm0STOFUlDa6nvh27DuKIIvq7v/+ID6P4fSNb4h1ktC/3lrhI21ei8ZjBulonJ9XV+BPGlnIzmWL7g5j4dMm90AqZXRRKQMAX9UIizLoCh58KR5gESszQ/8MSrELclI1fUwiY4Wxlvf6ZsGsg3c22xumxlnc85eiAYQ1LEPWNghcyqE96yrIhXrph3unLRsEbZcGxgXw/dcUtjIL9tjDO9zSN6ZuOQ== rsd@grus"
     74 
     75 sAraSshPubKey :: [Char]
     76 sAraSshPubKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDTn+zicq75blG6yWY0GIkba93FDbWHOo95k2oqrUVr3llmpuRnYCdp6F7LIiRF4NxDA6hBL5JAlxwS0mKgpbzJjbav7S7XxV1HxrtbO/bQqbcH3pvGCuSvltpZg072pB/g1Uhcv9S2FiOHJsedqlGiqUfucuFdRINYk3a8cQVTZ6XB7AxpqGLova3b+1rdhF6c5DSxgMscvZ+zGANN8tmv/0iGERRRdpvePR7iKZp7ZpqrsS4zXHJO537T6Z05lhaD+Fk/sgz5AUT/fkxIVvsNYL23mVS2fQg1htBAZi4vZ7joZn643bc1w6B7oa8nKEagRu4EWw+LbMeKzecIv0pB s@cygnus.ricketyspace.net"
     77 
     78 sLyraSshPubKey :: [Char]
     79 sLyraSshPubKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0UdjqvAo5lICg0BwGv6aQVSO+Ew6ZXFwbwpZiXhZ03+P/Wk98mJJUln+NaKC9lbjQnAShviBy5BIPbexQJ21vvP6mh13Z6xPPtHWRYUy+Y0GYvnc8yKfsnJ2bDaXobpAprfwWpwhW01ZC2xehaZxE+w8dwdAP4v53w5XlLU733f19vRqis4Y0/jyse2BMpsVGzWiLRLprjeLOgdcDB+yLP+EnM46Yj5z0xchbO2uCozloX1yhExDJ2Z4MITx397+/3GYaPEytcMVD/YmIScU4y4nqwa8O/sg5miamD6HUzAWKOHkHgf1HosDbz4JHcEwJqgSvqTeVJ5UWWuPK7YfJ s@cygnus.ricketyspace.net"
     80 
     81 -- configure ara
     82 grus :: Host
     83 grus = host "grus.ricketyspace.net" $props
     84        & osDebian Unstable X86_64
     85        & Locale.available "en_US.UTF-8"
     86        & File.hasContent "/etc/motd" (["At grus."])
     87        & dockerCEInstalled
     88 
     89 -- configure ara
     90 ara :: Host
     91 ara = host "ara.ricketyspace.net" $props
     92        & osDebian (Stable "buster") X86_64
     93        & Locale.available "en_US.UTF-8"
     94        & ipv4 "207.246.85.24"
     95        & File.hasContent "/etc/motd" (["At ara."])
     96        -- apt
     97        & Apt.stdSourcesList
     98        & Apt.unattendedUpgrades
     99        & Apt.safeUpgrade
    100        & Apt.installed ["htop", "tmux", "git"]
    101        -- sshd
    102        & Ssh.passwordAuthentication False
    103        -- system
    104        -- & Fail2Ban.installed
    105        -- root config
    106        & File.hasContent "/root/.tmux.conf" tmuxConf
    107        & Ssh.authorizedKey (User "root") sCanonicalSshPubKey
    108        & Ssh.authorizedKey (User "root") sAraSshPubKey
    109        & Ssh.userKeyAt (Just "/root/.ssh/id_rsa")
    110         (User "root") hostContext (SshRsa, sAraSshPubKey)
    111        & File.hasPrivContent "/root/switchblade.ovpn"
    112        (Context "ara.ricketyspace.net")
    113        -- s config
    114        & User.accountFor(User "s")
    115        & User.hasPassword (User "s")
    116        & User.hasLoginShell (User "s") "/bin/bash"
    117        & Sudo.enabledFor (User "s")
    118        & Ssh.authorizedKey (User "s") sCanonicalSshPubKey
    119        & Ssh.userKeyAt (Just "/home/s/.ssh/id_rsa")
    120         (User "s") hostContext (SshRsa, sAraSshPubKey)
    121       & File.hasContent "/home/s/.tmux.conf" tmuxConf
    122       & File.ownerGroup "/home/s" (User "s") (Group "s")
    123 
    124 
    125 -- configure virgo
    126 virgo :: Host
    127 virgo = host "virgo.ricketyspace.net" $props
    128        & osDebian (Stable "buster") X86_64
    129        & ipv4 "45.76.0.41"
    130        & ipv6 "2001:19f0:5:512b:5400:02ff:fe50:0fd1"
    131        & Locale.available "en_US.UTF-8"
    132        & File.hasContent "/etc/motd" (["At virgo."])
    133        -- apt
    134        & Apt.stdSourcesList
    135        & Apt.unattendedUpgrades
    136        & Apt.safeUpgrade
    137        & Apt.installed ["htop", "etckeeper", "opendkim"
    138                        , "git-daemon-sysvinit", "postfix"]
    139        -- sshd
    140        & Ssh.passwordAuthentication False
    141        -- system
    142        & Fail2Ban.installed
    143        --- nginx
    144        & Apt.removed ["apache2"]
    145        & Nginx.installed
    146        & ricketyspaceNetDhparamPem
    147        & ricketyspaceNetCert `onChange` Nginx.restarted
    148        & ricketyspaceNetCertKey
    149        & ricketyspaceNetGitCert
    150        & ricketyspaceNetGitCertKey
    151        & ricketyspaceNetLyraCert
    152        & ricketyspaceNetLyraCertKey
    153        & Nginx.siteEnabled "ricketyspace.net" ricketyspaceNetNginx
    154        & Nginx.siteEnabled "git.ricketyspace.net" ricketyspaceNetGitNginx
    155        -- crons
    156        & stagitGenCron
    157        -- web
    158        & Group.exists (Group "pubs") Nothing
    159        -- web - root
    160        & File.dirExists "/var/www/root"
    161        & File.ownerGroup "/var/www/root" (User "www-data") (Group "pubs")
    162        & File.mode "/var/www/root/" 0O0770
    163        -- web - git
    164        & File.dirExists "/var/www/git.rs"
    165        & File.ownerGroup "/var/www/git.rs" (User "www-data") (Group "pubs")
    166        & File.mode "/var/www/git.rs/" 0O0770
    167        -- www-data config
    168        & User.hasGroup (User "www-data") (Group "pubs")
    169        -- root config
    170        & File.hasContent "/root/.tmux.conf" tmuxConf
    171        & Ssh.authorizedKey (User "root") sCanonicalSshPubKey
    172        -- g config
    173        & User.accountFor (User "g")
    174        & User.hasGroup (User "g") (Group "pubs")
    175        & Ssh.authorizedKey (User "g") sCanonicalSshPubKey
    176        & Ssh.authorizedKey (User "g") sAraSshPubKey
    177        --- c dir config
    178        & File.dirExists "/home/g/c"
    179        & File.ownerGroup "/home/g/c" (User "g") (Group "pubs")
    180        & File.mode "/home/g/c" 0O0770
    181        -- s config
    182        & User.accountFor (User "s")
    183        & User.hasPassword (User "s")
    184        & Sudo.enabledFor (User "s")
    185        & Ssh.authorizedKey (User "s") sCanonicalSshPubKey
    186        -- git-daemon
    187        & rsGitDaemonDefaults `onChange` Service.restarted "git-daemon"
    188        -- opendkim
    189        & File.dirExists "/etc/opendkim"
    190        & File.containsLines "/etc/opendkim.conf" ricketyspaceNetOpenDkimConf
    191        & File.containsLines "/etc/opendkim/KeyTable" ricketyspaceNetOpenDkimKeyTable
    192        & File.containsLines "/etc/opendkim/SigningTable" ricketyspaceNetOpenDkimSigningTable
    193        & File.containsLines "/etc/opendkim/TrustedHosts" ricketyspaceNetOpenDkimTrustedHosts
    194        & File.dirExists "/etc/dkimkeys/ricketyspace.net"
    195        & File.ownerGroup "/etc/dkimkeys/ricketyspace.net" (User "opendkim") (Group "opendkim")
    196        & File.hasPrivContent "/etc/dkimkeys/ricketyspace.net/mail.private" (Context "ricketyspace.net")
    197        & File.ownerGroup "/etc/dkimkeys/ricketyspace.net/mail.private"
    198             (User "opendkim") (Group "opendkim")
    199        -- postfix
    200        & rsMailAliases
    201        & rsPostfixHeaderChecks
    202        & rsPostfixRBLOverride
    203        & File.hasContent "/etc/postfix/main.cf" rsPostfixMainCf
    204        & File.hasContent "/etc/postfix/master.cf" rsPostfixMasterCf `onChange` Postfix.reloaded
    205 
    206 
    207 
    208 -- configure lyra
    209 lyra :: Host
    210 lyra = host "lyra.ricketyspace.net" $props
    211        & osDebian (Stable "stretch") X86_64
    212        & Locale.available "en_US.UTF-8"
    213        & ipv4 "159.89.229.91"
    214        & ipv6 "2604:a880:400:d1::77f:8001"
    215        & alias "ricketyspace.net"
    216        & alias "git.ricketyspace.net"
    217        & alias "dingy.space"
    218        & alias "nfsw.dingy.space"
    219        & File.hasContent "/etc/motd" (["At lyra."])
    220        -- apt
    221        & Apt.stdSourcesList
    222        & Apt.unattendedUpgrades
    223        & Apt.safeUpgrade
    224        & Apt.installed ["git", "etckeeper"
    225                         , "htop", "sudo", "zsh", "tmux"
    226                         , "emacs", "rsync", "git-annex"
    227                         , "cgit", "fcgiwrap", "git-daemon-sysvinit"
    228                         , "bind9", "bind9-doc"
    229                         , "opendkim", "opendkim-tools",
    230                           "postfix", "postfix-doc"
    231                         , "postfix-policyd-spf-python", "postfix-pcre"
    232                         , "dovecot-imapd", "python3-markdown", "python3-docutils"
    233                         , "pandoc", "cvs"
    234                         , "libgit2-dev", "musl-dev", "make"
    235                        ]
    236        -- sshd
    237        & Ssh.passwordAuthentication False
    238        -- system
    239        & Fail2Ban.installed
    240        --- nginx
    241        & Apt.removed ["apache2"]
    242        & Nginx.installed
    243        & Nginx.siteEnabled "ricketyspace.net" ricketyspaceNetNginx
    244        & Nginx.siteEnabled "git.ricketyspace.net" ricketyspaceNetGitNginx
    245        & Nginx.siteEnabled "dingy.space" dingySpaceNginx
    246        & Nginx.siteEnabled "nfsw.dingy.space" nfswDingySpaceNginx
    247        & ricketyspaceNetDhparamPem
    248        & ricketyspaceNetCert `onChange` Nginx.restarted
    249        & ricketyspaceNetCertKey
    250        & ricketyspaceNetGitCert
    251        & ricketyspaceNetGitCertKey
    252        & ricketyspaceNetLyraCert
    253        & ricketyspaceNetLyraCertKey
    254        & dingySpaceCert
    255        & dingySpaceCertKey
    256        & nfswDingySpaceCert
    257        & nfswDingySpaceCertKey
    258        -- git-daemon
    259        & rsGitDaemonDefaults `onChange` Service.restarted "git-daemon"
    260        -- opendkim
    261        & File.dirExists "/etc/opendkim"
    262        & File.containsLines "/etc/opendkim.conf" ricketyspaceNetOpenDkimConf
    263        & File.containsLines "/etc/opendkim/KeyTable" ricketyspaceNetOpenDkimKeyTable
    264        & File.containsLines "/etc/opendkim/SigningTable" ricketyspaceNetOpenDkimSigningTable
    265        & File.containsLines "/etc/opendkim/TrustedHosts" ricketyspaceNetOpenDkimTrustedHosts
    266        --- opendkim for ricketyspace.net
    267        & File.dirExists "/etc/dkimkeys/ricketyspace.net"
    268        & File.ownerGroup "/etc/dkimkeys/ricketyspace.net" (User "opendkim") (Group "opendkim")
    269        & File.hasPrivContent "/etc/dkimkeys/ricketyspace.net/mail.private" (Context "ricketyspace.net")
    270        & File.ownerGroup "/etc/dkimkeys/ricketyspace.net/mail.private"
    271             (User "opendkim") (Group "opendkim")
    272        --- opendkim for dingy.space
    273        & File.dirExists "/etc/dkimkeys/dingy.space"
    274        & File.ownerGroup "/etc/dkimkeys/dingy.space" (User "opendkim") (Group "opendkim")
    275        & File.hasPrivContent "/etc/dkimkeys/dingy.space/mail.private" (Context "dingy.space")
    276        & File.ownerGroup "/etc/dkimkeys/dingy.space/mail.private"
    277             (User "opendkim") (Group "opendkim")
    278        -- postfix
    279        & rsMailAliases
    280        & rsPostfixHeaderChecks
    281        & rsPostfixRBLOverride
    282        & File.hasContent "/etc/postfix/main.cf" rsPostfixMainCf
    283        & File.hasContent "/etc/postfix/master.cf" rsPostfixMasterCf `onChange` Postfix.reloaded
    284        & File.dirExists "/etc/postfix/virtual"
    285        & rsPostfixVirtualAddresses
    286        & rsPostfixVirtualDomains
    287        -- dovecot
    288        & File.hasContent "/etc/dovecot/dovecot.conf" rsDovecotConf
    289              `onChange` Service.restarted "dovecot"
    290        -- bind
    291        & Dns.primary publicHosts "ricketyspace.net"
    292        (Dns.mkSOA "lyra.ricketyspace.net" 20180129)
    293        [
    294          (RootDomain, NS $ RelDomain "lyra")
    295        , (RootDomain, NS $ AbsDomain "ns6.gandi.net")
    296        , (RootDomain, MX 0 $ AbsDomain "lyra.ricketyspace.net")
    297        , (RootDomain, TXT "v=spf1 mx -all")
    298        , (RelDomain "_dmarc", TXT "v=DMARC1; p=none; rua=mailto:root@ricketyspace.net")
    299        , (RelDomain "mail._domainkey", TXT "v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6ClUrJTSt/UISOTEoZy36SfCjuyuajJVGEzYrhuysn2CA3MNt6y3dsHrjalA04Bix02KySMVEHcbScsd54MHhvk364pOkapuAEFIAmvY9SiZGRWsKMK5tgq/aSgH6xFg6M1CupV4QHWFgk193juiEdnB8uwXKTxOdKP/P/xbU3h98MFvXmNzT8sEr5VMZHiTdzqcxCLLfhx27iwjFJh4td6y+0n5YO/M2zf3n9ikXIof/dw4lA5Xo2icI3G88LMl9Tk4vcY0UVXXXulKmrnrn96Nyow0zU31kB/NUb1HbOxaVLz7KJThS+U9NV/66vZ5blwg7aExJXkPfVEtLTca+wIDAQAB")
    300        ]
    301        & Dns.primary publicHosts "dingy.space"
    302        (Dns.mkSOA "lyra.ricketyspace.net" 20191028)
    303        [
    304          (RootDomain, NS $ AbsDomain "lyra.ricketyspace.net")
    305        , (RootDomain, NS $ AbsDomain "ns6.gandi.net")
    306        , (RootDomain, MX 0 $ AbsDomain "lyra.ricketyspace.net")
    307        , (RootDomain, TXT "v=spf1 mx -all")
    308        , (RelDomain "_dmarc", TXT "v=DMARC1; p=reject; rua=mailto:root@dingy.space")
    309        , (RelDomain "mail._domainkey", TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyxnjDmXyu4Z+RKCDobnebr9Sp+w20Dmn7KFhjgVtKGirQGnemMZTxlXrO+5Ro8T0Ecq+D472EKxQbXPUr5GG7zxIeqldjU3kOChjAoOE8TUsR9pxShdp2qJylGdqvZqPTMApbzKJPXJC8j7yBA9DspLyaPluj50o8XqmbrWkZaZ4/+05E3MfjqbTJsqr0yDl/3/KoB4Qut3LqTCWrpzvGG4Ixu0wUz5FLPq6U6m7xiEnDMtTMnRDuz2XLcNtQXwHtxrkB9TdViDPA//FQONTtnXtaQjwuEh3FMScElkcYAl3tZr+ZRfgzZanH2YIDf80Og0ZQZkD06i1HQxWrM4SQwIDAQAB")
    310        ]
    311        -- crons
    312        & stagitGenCron
    313        -- web
    314        & Group.exists (Group "pubs") Nothing
    315        -- web - root
    316        & File.dirExists "/var/www/root"
    317        & File.ownerGroup "/var/www/root" (User "www-data") (Group "pubs")
    318        & File.mode "/var/www/root/" 0O0770
    319        -- web - git
    320        & File.dirExists "/var/www/git.rs"
    321        & File.ownerGroup "/var/www/git.rs" (User "www-data") (Group "pubs")
    322        & File.mode "/var/www/git.rs/" 0O0770
    323        -- root config
    324        & Ssh.authorizedKey (User "root") sCanonicalSshPubKey
    325        & File.hasContent "/root/.tmux.conf" tmuxConf
    326        -- www-data config
    327        & User.hasGroup (User "www-data") (Group "pubs")
    328        -- gitdaemon config
    329        & User.hasGroup (User "gitdaemon") (Group "pubs")
    330        -- w config
    331        & User.accountFor (User "w")
    332        & User.hasGroup (User "w") (Group "pubs")
    333        & Ssh.authorizedKey (User "w") sCanonicalSshPubKey
    334        -- g config
    335        & User.accountFor (User "g")
    336        & User.hasGroup (User "g") (Group "pubs")
    337        & Ssh.authorizedKey (User "g") sCanonicalSshPubKey
    338        & Ssh.authorizedKey (User "g") sAraSshPubKey
    339        -- c dir config
    340        & File.dirExists "/home/g/c"
    341        & File.ownerGroup "/home/g/c" (User "g") (Group "pubs")
    342        & File.mode "/home/g/c" 0O0770
    343        -- s config
    344        & User.accountFor (User "s")
    345        & User.hasPassword (User "s")
    346        & Sudo.enabledFor (User "s")
    347        & Ssh.authorizedKey (User "s") sCanonicalSshPubKey
    348        & Ssh.authorizedKey (User "s") sAraSshPubKey
    349        & Ssh.userKeyAt (Just "/home/s/.ssh/id_rsa")
    350         (User "s") hostContext (SshRsa, sLyraSshPubKey)
    351 
    352 vela :: Host
    353 vela = host "vela.dingy.space" $props
    354        & ipv4 "45.77.145.168"
    355 
    356 
    357 --- nginx
    358 ricketyspaceNetNginx :: [String]
    359 ricketyspaceNetNginx = [
    360   "# Adapted from https://gist.github.com/konklone/6532544"
    361   , ""
    362   , "server {"
    363   , "    listen 80;"
    364   , "    listen [::]:80;"
    365   , "    listen 443 ssl http2;"
    366   , "    listen [::]:443 ssl http2;"
    367   , "    server_name lyra.ricketyspace.net;"
    368   , "    return 301 https://ricketyspace.net;"
    369   , ""
    370   , "    ssl_certificate /etc/ssl/certs/lyra.rs.net.chained.le.pem;"
    371   , "    ssl_certificate_key /etc/ssl/private/lyra.rs.net.d.le.key;"
    372   , "}"
    373   , ""
    374   , "server {"
    375   , "    listen 80;"
    376   , "    listen [::]:80;"
    377   , "    server_name ricketyspace.net;"
    378   , "    return 301 https://$host$request_uri;"
    379   , "}"
    380   , ""
    381   , "# The 'spdy' at the end of the listen command below turns on SPDY support."
    382   , ""
    383   , "server {"
    384   , "    listen 443 ssl http2;"
    385   , "    listen [::]:443 ssl http2;"
    386   , "    server_name ricketyspace.net;"
    387   , ""
    388   , "    root /var/www/root;"
    389   , "    error_page 404 /404.html;"
    390   , "    error_page 403 /403.html;"
    391   , "    default_type text/plain;"
    392   , ""
    393   , "    location /git-difme/releases/ {"
    394   , "         autoindex on;"
    395   , "    }"
    396   , ""
    397   , "    location /nsfw/ {"
    398   , "        autoindex on;"
    399   , "    }"
    400   , ""
    401   , "    location /nfsw/ {"
    402   , "        autoindex on;"
    403   , "    }"
    404   , ""
    405   , "    location /tmp/x200/ {"
    406   , "        autoindex on;"
    407   , "    }"
    408   , ""
    409   , "    # Path to certificate and private key."
    410   , "    # The .crt may omit the root CA cert, if it's a standard CA that ships with clients."
    411   , "    ssl_certificate /etc/ssl/certs/rs.net.chained.le.pem;"
    412   , "    ssl_certificate_key /etc/ssl/private/rs.net.d.le.key;"
    413   , ""
    414   , "    add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';"
    415   , ""
    416   , "    ssl_prefer_server_ciphers on;"
    417   , "    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';"
    418   , ""
    419   , "    ssl_protocols TLSv1.2 TLSv1.1 TLSv1;"
    420   , ""
    421   , "    # as recommended by http://nginx.org/en/docs/http/configuring_https_servers.html"
    422   , "    ssl_session_cache   shared:SSL:10m;"
    423   , "    ssl_session_timeout 10m;"
    424   , "    keepalive_timeout   70;"
    425   , ""
    426   , "    # nginx 1.5.9+ ONLY"
    427   , "    ssl_buffer_size 1400; "
    428   , ""
    429   , "    #"
    430   , "    # Generated by OpenSSL with the following command:"
    431   , "    #   openssl dhparam -outform pem -out dhparam2048.pem 2048"
    432   , "    ssl_dhparam /etc/ssl/certs/dhparam4096.pem;"
    433   , "}"
    434   ]
    435 
    436 ricketyspaceNetGitNginx :: [String]
    437 ricketyspaceNetGitNginx = [
    438   "# Adapted from https://gist.github.com/konklone/6532544"
    439   , ""
    440   , "server {"
    441   , "    listen 80;"
    442   , "    listen [::]:80;"
    443   , "    server_name git.ricketyspace.net;"
    444   , "    return 301 https://$host$request_uri;"
    445   , "}"
    446   , ""
    447   , "server {"
    448   , "    listen 443 ssl http2;"
    449   , "    listen [::]:443 ssl http2;"
    450   , "    server_name git.ricketyspace.net;"
    451   , ""
    452   , "    access_log  /var/log/nginx/git-access.log;"
    453   , "    error_log   /var/log/nginx/git-error.log;"
    454   , ""
    455   , ""
    456   , "    root /var/www/git.rs/html;"
    457   , "    error_page 404 /404.html;"
    458   , "    error_page 403 /403.html;"
    459   , "    default_type text/plain;"
    460   , ""
    461   , "    location ~ /((style|logo|favicon)\\.(css|png|ico))$ {"
    462   , "         alias /var/www/git.rs/$1;"
    463   , "    }"
    464   , ""
    465   , "    rewrite ^(.*)(tree|plain)/(.*)$ $1file/$3.html permanent;"
    466   , ""
    467   , "    # Path to certificate and private key."
    468   , "    # The .crt may omit the root CA cert, if it's a standard CA that ships with clients."
    469   , "    ssl_certificate /etc/ssl/certs/git.rs.net.chained.le.pem;"
    470   , "    ssl_certificate_key /etc/ssl/private/git.rs.net.d.le.key;"
    471   , ""
    472   , "    add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';"
    473   , ""
    474   , "    ssl_prefer_server_ciphers on;"
    475   , "    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';"
    476   , ""
    477   , "    ssl_protocols TLSv1.2 TLSv1.1 TLSv1;"
    478   , ""
    479   , "    # as recommended by http://nginx.org/en/docs/http/configuring_https_servers.html"
    480   , "    ssl_session_cache   shared:SSL:10m;"
    481   , "    ssl_session_timeout 10m;"
    482   , "    keepalive_timeout   70;"
    483   , ""
    484   , "    # nginx 1.5.9+ ONLY"
    485   , "    ssl_buffer_size 1400;"
    486   , ""
    487   , "    #"
    488   , "    # Generated by OpenSSL with the following command:"
    489   , "    #   openssl dhparam -outform pem -out dhparam2048.pem 2048"
    490   , "    ssl_dhparam /etc/ssl/certs/dhparam4096.pem;"
    491   , "}"
    492   ]
    493 
    494 
    495 dingySpaceNginx :: [String]
    496 dingySpaceNginx = [
    497   "# Adapted from https://gist.github.com/konklone/6532544"
    498   , ""
    499   , "server {"
    500   , "    listen 80;"
    501   , "    listen [::]:80;"
    502   , "    server_name dingy.space;"
    503   , "    return 301 https://$host$request_uri;"
    504   , "}"
    505   , ""
    506   , "server {"
    507   , "    listen 443 ssl http2;"
    508   , "    listen [::]:443 ssl http2;"
    509   , "    server_name dingy.space;"
    510   , ""
    511   , "    access_log  /var/log/nginx/dingy.space.log;"
    512   , "    error_log   /var/log/nginx/dingy.space.log;"
    513   , ""
    514   , ""
    515   , "    root /var/www/dingy.space;"
    516   , "    error_page 404 /404.html;"
    517   , "    error_page 403 /403.html;"
    518   , "    default_type text/plain;"
    519   , ""
    520   , "    # Path to certificate and private key."
    521   , "    # The .crt may omit the root CA cert, if it's a standard CA that ships with clients."
    522   , "    ssl_certificate /etc/ssl/certs/ds.chained.le.pem;"
    523   , "    ssl_certificate_key /etc/ssl/private/ds.d.le.key;"
    524   , ""
    525   , "    add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';"
    526   , ""
    527   , "    ssl_prefer_server_ciphers on;"
    528   , "    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';"
    529   , ""
    530   , "    ssl_protocols TLSv1.2 TLSv1.1 TLSv1;"
    531   , ""
    532   , "    # as recommended by http://nginx.org/en/docs/http/configuring_https_servers.html"
    533   , "    ssl_session_cache   shared:SSL:10m;"
    534   , "    ssl_session_timeout 10m;"
    535   , "    keepalive_timeout   70;"
    536   , ""
    537   , "    # nginx 1.5.9+ ONLY"
    538   , "    ssl_buffer_size 1400;"
    539   , ""
    540   , "    #"
    541   , "    # Generated by OpenSSL with the following command:"
    542   , "    #   openssl dhparam -outform pem -out dhparam2048.pem 2048"
    543   , "    ssl_dhparam /etc/ssl/certs/dhparam4096.pem;"
    544   , "}"
    545   ]
    546 
    547 
    548 nfswDingySpaceNginx :: [String]
    549 nfswDingySpaceNginx = [
    550   "# Adapted from https://gist.github.com/konklone/6532544"
    551   , ""
    552   , "server {"
    553   , "    listen 80;"
    554   , "    listen [::]:80;"
    555   , "    server_name dingy.space;"
    556   , "    return 301 https://$host$request_uri;"
    557   , "}"
    558   , ""
    559   , "server {"
    560   , "    listen 443 ssl http2;"
    561   , "    listen [::]:443 ssl http2;"
    562   , "    server_name nfsw.dingy.space;"
    563   , ""
    564   , "    access_log  /var/log/nginx/nfsw.dingy.space.log;"
    565   , "    error_log   /var/log/nginx/nfsw.dingy.space.log;"
    566   , ""
    567   , ""
    568   , "    root /var/www/nfsw.dingy.space;"
    569   , "    error_page 404 /404.html;"
    570   , "    error_page 403 /403.html;"
    571   , "    default_type text/plain;"
    572   , ""
    573   , "    # Path to certificate and private key."
    574   , "    # The .crt may omit the root CA cert, if it's a standard CA that ships with clients."
    575   , "    ssl_certificate /etc/ssl/certs/nfsw.ds.chained.le.pem;"
    576   , "    ssl_certificate_key /etc/ssl/private/nfsw.ds.d.le.key;"
    577   , ""
    578   , "    add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';"
    579   , ""
    580   , "    ssl_prefer_server_ciphers on;"
    581   , "    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';"
    582   , ""
    583   , "    ssl_protocols TLSv1.2 TLSv1.1 TLSv1;"
    584   , ""
    585   , "    # as recommended by http://nginx.org/en/docs/http/configuring_https_servers.html"
    586   , "    ssl_session_cache   shared:SSL:10m;"
    587   , "    ssl_session_timeout 10m;"
    588   , "    keepalive_timeout   70;"
    589   , ""
    590   , "    # nginx 1.5.9+ ONLY"
    591   , "    ssl_buffer_size 1400;"
    592   , ""
    593   , "    #"
    594   , "    # Generated by OpenSSL with the following command:"
    595   , "    #   openssl dhparam -outform pem -out dhparam2048.pem 2048"
    596   , "    ssl_dhparam /etc/ssl/certs/dhparam4096.pem;"
    597   , "}"
    598   ]
    599 
    600 
    601 --- certs
    602 ricketyspaceNetDhparamPem :: Property (HasInfo + UnixLike)
    603 ricketyspaceNetDhparamPem = File.hasPrivContent
    604   "/etc/ssl/certs/dhparam4096.pem" (Context "ricketyspace.net")
    605 
    606 ricketyspaceNetCert :: Property (HasInfo + UnixLike)
    607 ricketyspaceNetCert = File.hasPrivContent
    608    cert (Context "ricketyspace.net")
    609    `onChange` File.mode cert 0O0644
    610   where cert = "/etc/ssl/certs/rs.net.chained.le.pem"
    611 
    612 ricketyspaceNetCertKey :: Property (HasInfo + UnixLike)
    613 ricketyspaceNetCertKey = File.hasPrivContent
    614   "/etc/ssl/private/rs.net.d.le.key" (Context "ricketyspace.net")
    615 
    616 ricketyspaceNetGitCert :: Property (HasInfo + UnixLike)
    617 ricketyspaceNetGitCert = File.hasPrivContent
    618   "/etc/ssl/certs/git.rs.net.chained.le.pem" (Context "git.ricketyspace.net")
    619 
    620 ricketyspaceNetGitCertKey :: Property (HasInfo + UnixLike)
    621 ricketyspaceNetGitCertKey = File.hasPrivContent
    622   "/etc/ssl/private/git.rs.net.d.le.key" (Context "git.ricketyspace.net")
    623 
    624 ricketyspaceNetLyraCert :: Property (HasInfo + UnixLike)
    625 ricketyspaceNetLyraCert = File.hasPrivContent
    626    cert (Context "lyra.ricketyspace.net")
    627    `onChange` File.mode cert 0O0644
    628   where cert = "/etc/ssl/certs/lyra.rs.net.chained.le.pem"
    629 
    630 ricketyspaceNetLyraCertKey :: Property (HasInfo + UnixLike)
    631 ricketyspaceNetLyraCertKey = File.hasPrivContent
    632   "/etc/ssl/private/lyra.rs.net.d.le.key" (Context "lyra.ricketyspace.net")
    633 
    634 dingySpaceCert :: Property (HasInfo + UnixLike)
    635 dingySpaceCert = File.hasPrivContent
    636    cert (Context "dingy.space")
    637    `onChange` File.mode cert 0O0644
    638   where cert = "/etc/ssl/certs/ds.chained.le.pem"
    639 
    640 dingySpaceCertKey :: Property (HasInfo + UnixLike)
    641 dingySpaceCertKey = File.hasPrivContent
    642   "/etc/ssl/private/ds.d.le.key" (Context "dingy.space")
    643 
    644 nfswDingySpaceCert :: Property (HasInfo + UnixLike)
    645 nfswDingySpaceCert = File.hasPrivContent
    646    cert (Context "nfsw.dingy.space")
    647    `onChange` File.mode cert 0O0644
    648   where cert = "/etc/ssl/certs/nfsw.ds.chained.le.pem"
    649 
    650 nfswDingySpaceCertKey :: Property (HasInfo + UnixLike)
    651 nfswDingySpaceCertKey = File.hasPrivContent
    652   "/etc/ssl/private/nfsw.ds.d.le.key" (Context "nfsw.dingy.space")
    653 
    654 
    655 --- dkim
    656 ricketyspaceNetOpenDkimConf :: [File.Line]
    657 ricketyspaceNetOpenDkimConf = [
    658   "KeyTable    /etc/opendkim/KeyTable"
    659   , "SigningTable    /etc/opendkim/SigningTable"
    660   , "ExternalIgnoreList    /etc/opendkim/TrustedHosts"
    661   , "InternalHosts    /etc/opendkim/TrustedHosts"
    662   , "LogWhy    yes"
    663   , "Socket    inet:12345@localhost"
    664   ]
    665 
    666 ricketyspaceNetOpenDkimKeyTable :: [File.Line]
    667 ricketyspaceNetOpenDkimKeyTable = [
    668   "mail._domainkey.ricketyspace.net ricketyspace.net:mail:/etc/dkimkeys/ricketyspace.net/mail.private"
    669   , "mail._domainkey.dingy.space dingy.space:mail:/etc/dkimkeys/dingy.space/mail.private"
    670   ]
    671 
    672 ricketyspaceNetOpenDkimSigningTable :: [File.Line]
    673 ricketyspaceNetOpenDkimSigningTable = [
    674   "ricketyspace.net mail._domainkey.ricketyspace.net"
    675   , "dingy.space mail._domainkey.dingy.space"
    676   ]
    677 
    678 ricketyspaceNetOpenDkimTrustedHosts :: [File.Line]
    679 ricketyspaceNetOpenDkimTrustedHosts = [
    680   "127.0.0.1"
    681   , "localhost"
    682   , "ricketyspace.net"
    683   , "dingy.space"
    684   ]
    685 
    686 
    687 -- git-daemon
    688 rsGitDaemonDefaults :: Property UnixLike
    689 rsGitDaemonDefaults  = File.hasContent "/etc/default/git-daemon" [
    690   "# Defaults for git-daemon initscript"
    691   , "# sourced by /etc/init.d/git-daemon"
    692   , ""
    693   , "#"
    694   , "# This is a POSIX shell fragment"
    695   , "#"
    696   , ""
    697   , "GIT_DAEMON_ENABLE=true"
    698   , "GIT_DAEMON_USER=gitdaemon"
    699   , "GIT_DAEMON_BASE_PATH=/home/g/c"
    700   , "GIT_DAEMON_DIRECTORY=/home/g/c"
    701   , ""
    702   , "# Additional options that are passed to the Daemon."
    703   , "GIT_DAEMON_OPTIONS=\"\""
    704   ]
    705 
    706 --- postfix
    707 rsPostfixHeaderChecks :: Property DebianLike
    708 rsPostfixHeaderChecks = Postfix.mappedFile "/etc/postfix/header_checks"
    709   (flip File.hasContent
    710     [
    711       "/^Received:.*with ESMTPSA/ IGNORE"
    712     , "/^X-Originating-IP:/ IGNORE"
    713     ]) `describe` "postfix header checks configured"
    714   `onChange` Postfix.reloaded
    715 
    716 rsPostfixRBLOverride :: Property (HasInfo + UnixLike)
    717 rsPostfixRBLOverride = File.hasPrivContent
    718   rblOFile (Context "ricketyspace.net")
    719   `onChange` na
    720   where
    721     na :: Property UnixLike
    722     na = scriptProperty ["postmap " ++ rblOFile]
    723          `assume` MadeChange
    724     rblOFile = "/etc/postfix/rbl_override"
    725 
    726 rsPostfixMainCf :: [File.Line]
    727 rsPostfixMainCf = [
    728   "smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)"
    729   , "biff = no"
    730   , ""
    731   , "append_dot_mydomain = no"
    732   , "compatibility_level = 2"
    733   , ""
    734   , "readme_directory = /usr/share/doc/postfix"
    735   , "html_directory = /usr/share/doc/postfix/html"
    736   , ""
    737   , "mime_header_checks = regexp:/etc/postfix/header_checks"
    738   , "header_checks = regexp:/etc/postfix/header_checks"
    739   , ""
    740   , "smtpd_tls_cert_file=/etc/ssl/certs/lyra.rs.net.chained.le.pem"
    741   , "smtpd_tls_key_file=/etc/ssl/private/lyra.rs.net.d.le.key"
    742   , "smtpd_use_tls=yes"
    743   , "smtpd_tls_auth_only=yes"
    744   , "smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache"
    745   , "smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache"
    746   , ""
    747   , "smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination"
    748   , "myhostname = lyra.ricketyspace.net"
    749   , "alias_maps = hash:/etc/aliases"
    750   , "alias_database = hash:/etc/aliases"
    751   , "myorigin = ricketyspace.net"
    752   , "mydestination = ricketyspace.net, localhost, /etc/postfix/virtual/domains"
    753   , "virtual_maps = hash:/etc/postfix/virtual/addresses"
    754   , "relayhost ="
    755   , "mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128"
    756   , "mailbox_command ="
    757   , "mailbox_size_limit = 0"
    758   , "recipient_delimiter = +"
    759   , "inet_interfaces = all"
    760   , "inet_protocols = all"
    761   , "relay_domains = ricketyspace.net"
    762   , ""
    763   , "smtpd_recipient_restrictions = permit_sasl_authenticated,"
    764   , "      reject_invalid_hostname,"
    765   , "      reject_unknown_recipient_domain,"
    766   , "      reject_unauth_destination,"
    767   , "      reject_rbl_client sbl.spamhaus.org,"
    768   , "      check_policy_service unix:private/policy-spf"
    769   , "      permit"
    770   , ""
    771   , "smtpd_helo_restrictions = check_helo_access hash:/etc/postfix/rbl_override,"
    772   , "        reject_invalid_helo_hostname,"
    773   , "        reject_non_fqdn_helo_hostname"
    774   , ""
    775   , "smtpd_client_restrictions = reject_rbl_client dnsbl.sorbs.net"
    776   , ""
    777   , "home_mailbox = Maildir/"
    778   , ""
    779   , "# dovecot"
    780   , "smtpd_sasl_type = dovecot"
    781   , "smtpd_sasl_path = private/auth"
    782   , "smtpd_sasl_auth_enable = yes"
    783   , ""
    784   , "# dkim"
    785   , "milter_default_action = accept"
    786   , "milter_protocol = 6"
    787   , "smtpd_milters = inet:localhost:12345"
    788   , "non_smtpd_milters = inet:localhost:12345"
    789   , ""
    790   , "# spf"
    791   , "policy-spf_time_limit = 3600s"
    792   , ""
    793   , "# map mail username/password to user's unix user/passwd."
    794   , "local_recipient_maps = proxy:unix:passwd.byname $alias_maps"
    795   ]
    796 
    797 rsPostfixMasterCf :: [File.Line]
    798 rsPostfixMasterCf = [
    799   "# Do not forget to execute `postfix reload` after editing this file."
    800   , "#"
    801   , "# =========================================================================="
    802   , "# service type  private unpriv  chroot  wakeup  maxproc command + args"
    803   , "#               (yes)   (yes)   (no)    (never) (100)"
    804   , "# =========================================================================="
    805   , "smtp      inet  n       -       y       -       -       smtpd"
    806   , "submission inet n       -       y       -       -       smtpd"
    807   , "  -o syslog_name=postfix/submission"
    808   , "  -o smtpd_tls_security_level=encrypt"
    809   , "  -o smtpd_sasl_auth_enable=yes"
    810   , "  -o smtpd_client_restrictions=permit_sasl_authenticated,reject"
    811   , "  -o milter_macro_daemon_name=ORIGINATING"
    812   , "smtps     inet  n       -       y       -       -       smtpd -v"
    813   , "  -o syslog_name=postfix/smtps"
    814   , "  -o smtpd_tls_wrappermode=yes"
    815   , "  -o smtpd_sasl_auth_enable=yes"
    816   , "  -o smtpd_client_restrictions=permit_sasl_authenticated,reject"
    817   , "  -o milter_macro_daemon_name=ORIGINATING"
    818   , "pickup    unix  n       -       y       60      1       pickup"
    819   , "cleanup   unix  n       -       y       -       0       cleanup"
    820   , "qmgr      unix  n       -       n       300     1       qmgr"
    821   , "tlsmgr    unix  -       -       y       1000?   1       tlsmgr"
    822   , "rewrite   unix  -       -       y       -       -       trivial-rewrite"
    823   , "bounce    unix  -       -       y       -       0       bounce"
    824   , "defer     unix  -       -       y       -       0       bounce"
    825   , "trace     unix  -       -       y       -       0       bounce"
    826   , "verify    unix  -       -       y       -       1       verify"
    827   , "flush     unix  n       -       y       1000?   0       flush"
    828   , "proxymap  unix  -       -       n       -       -       proxymap"
    829   , "proxywrite unix -       -       n       -       1       proxymap"
    830   , "smtp      unix  -       -       y       -       -       smtp"
    831   , "relay     unix  -       -       y       -       -       smtp"
    832   , "showq     unix  n       -       y       -       -       showq"
    833   , "error     unix  -       -       y       -       -       error"
    834   , "retry     unix  -       -       y       -       -       error"
    835   , "discard   unix  -       -       y       -       -       discard"
    836   , "local     unix  -       n       n       -       -       local"
    837   , "virtual   unix  -       n       n       -       -       virtual"
    838   , "lmtp      unix  -       -       y       -       -       lmtp"
    839   , "anvil     unix  -       -       y       -       1       anvil"
    840   , "scache    unix  -       -       y       -       1       scache"
    841   , "#"
    842   , "# ===================================================================="
    843   , "# Interfaces to non-Postfix software."
    844   , "#"
    845   , "maildrop  unix  -       n       n       -       -       pipe"
    846   , "  flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}"
    847   , "#"
    848   , "# See the Postfix UUCP_README file for configuration details."
    849   , "#"
    850   , "uucp      unix  -       n       n       -       -       pipe"
    851   , "  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)"
    852   , "#"
    853   , "# Other external delivery methods."
    854   , "#"
    855   , "ifmail    unix  -       n       n       -       -       pipe"
    856   , "  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)"
    857   , "bsmtp     unix  -       n       n       -       -       pipe"
    858   , "  flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient"
    859   , "scalemail-backend unix  -       n       n       -       2       pipe"
    860   , "  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}"
    861   , "mailman   unix  -       n       n       -       -       pipe"
    862   , "  flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py"
    863   , "  ${nexthop} ${user}"
    864   , ""
    865   , "# For Dovecot."
    866   , "dovecot   unix  -       n       n       -       -       pipe"
    867   , "  flags=DRhu user=email:email argv=/usr/lib/dovecot/deliver -f ${sender} -d ${recipient}"
    868   , ""
    869   , "# SPF snafu (from https://help.ubuntu.com/community/Postfix/SPF)"
    870   , "policy-spf  unix  -       n       n       -       -       spawn"
    871   , "     user=nobody argv=/usr/bin/policyd-spf"
    872   ]
    873 
    874 rsPostfixVirtualAddresses :: Property (HasInfo + UnixLike)
    875 rsPostfixVirtualAddresses = File.hasPrivContent
    876   vaFile (Context "ricketyspace.net")
    877   `onChange` na
    878   where
    879     na :: Property UnixLike
    880     na = scriptProperty ["postmap " ++ vaFile]
    881          `assume` MadeChange
    882     vaFile = "/etc/postfix/virtual/addresses"
    883 
    884 
    885 rsPostfixVirtualDomains :: Property (HasInfo + UnixLike)
    886 rsPostfixVirtualDomains = File.hasPrivContent
    887   vdFile (Context "ricketyspace.net")
    888   where
    889     vdFile = "/etc/postfix/virtual/domains"
    890 
    891 
    892 rsDovecotConf :: [File.Line]
    893 rsDovecotConf = [
    894   "mail_privileged_group = mail"
    895   , "namespace inbox {"
    896   , "  inbox = yes"
    897   , "  location ="
    898   , "  mailbox Drafts {"
    899   , "    special_use = \\Drafts"
    900   , "  }"
    901   , "  mailbox Junk {"
    902   , "    special_use = \\Junk"
    903   , "  }"
    904   , "  mailbox Sent {"
    905   , "    special_use = \\Sent"
    906   , "  }"
    907   , "  mailbox \"Sent Messages\" {"
    908   , "    special_use = \\Sent"
    909   , "  }"
    910   , "  mailbox Trash {"
    911   , "    special_use = \\Trash"
    912   , "  }"
    913   , "  prefix ="
    914   , "}"
    915   , "passdb {"
    916   , "  driver = pam"
    917   , "}"
    918   , "protocols = \" imap\""
    919   , "service auth {"
    920   , "  unix_listener /var/spool/postfix/private/auth {"
    921   , "    group = postfix"
    922   , "    mode = 0666"
    923   , "    user = postfix"
    924   , "  }"
    925   , "}"
    926   , "ssl = required"
    927   , "ssl_cert = </etc/ssl/certs/lyra.rs.net.chained.le.pem"
    928   , "ssl_key = </etc/ssl/private/lyra.rs.net.d.le.key"
    929   , "userdb {"
    930   , "  driver = passwd"
    931   , "}"
    932   ]
    933 
    934 rsMailAliases :: Property (HasInfo + UnixLike)
    935 rsMailAliases = File.hasPrivContent
    936   "/etc/aliases" (Context "ricketyspace.net")
    937   `onChange` na
    938   where
    939     na :: Property UnixLike
    940     na = scriptProperty ["cd /etc", "newaliases"]
    941          `assume` MadeChange
    942 
    943 -- crons
    944 stagitGenCron :: Property DebianLike
    945 stagitGenCron = Cron.job "stagit-gen"
    946   (Cron.Times "* */1 * * *")
    947   (User "www-data") "/var/www/git.rs" cmd
    948   where
    949     cmd = "make"
    950 
    951 
    952 -- common
    953 
    954 --- tmux conf
    955 tmuxConf :: [File.Line]
    956 tmuxConf = [
    957   "set -g prefix C-a"
    958   , "unbind C-b"
    959   , "bind C-a send-prefix"
    960   , "set -g status-style bg=white"
    961   , "set -g status-right ''"
    962   ]
    963 
    964 --- Docker CE
    965 dockerCEInstalled :: Property DebianLike
    966 dockerCEInstalled = Apt.installed ["docker-ce"]
    967   `requires` Apt.setSourcesListD [
    968   "deb [arch=amd64] https://download.docker.com/linux/debian buster stable"
    969   ] "docker-ce"
    970   `requires` Apt.trustsKey dockerCEKey
    971 
    972 dockerCEKey :: Apt.AptKey
    973 dockerCEKey = Apt.AptKey "docker-ce" $ unlines [
    974   "-----BEGIN PGP PUBLIC KEY BLOCK-----"
    975   , ""
    976   , "mQINBFit2ioBEADhWpZ8/wvZ6hUTiXOwQHXMAlaFHcPH9hAtr4F1y2+OYdbtMuth"
    977   , "lqqwp028AqyY+PRfVMtSYMbjuQuu5byyKR01BbqYhuS3jtqQmljZ/bJvXqnmiVXh"
    978   , "38UuLa+z077PxyxQhu5BbqntTPQMfiyqEiU+BKbq2WmANUKQf+1AmZY/IruOXbnq"
    979   , "L4C1+gJ8vfmXQt99npCaxEjaNRVYfOS8QcixNzHUYnb6emjlANyEVlZzeqo7XKl7"
    980   , "UrwV5inawTSzWNvtjEjj4nJL8NsLwscpLPQUhTQ+7BbQXAwAmeHCUTQIvvWXqw0N"
    981   , "cmhh4HgeQscQHYgOJjjDVfoY5MucvglbIgCqfzAHW9jxmRL4qbMZj+b1XoePEtht"
    982   , "ku4bIQN1X5P07fNWzlgaRL5Z4POXDDZTlIQ/El58j9kp4bnWRCJW0lya+f8ocodo"
    983   , "vZZ+Doi+fy4D5ZGrL4XEcIQP/Lv5uFyf+kQtl/94VFYVJOleAv8W92KdgDkhTcTD"
    984   , "G7c0tIkVEKNUq48b3aQ64NOZQW7fVjfoKwEZdOqPE72Pa45jrZzvUFxSpdiNk2tZ"
    985   , "XYukHjlxxEgBdC/J3cMMNRE1F4NCA3ApfV1Y7/hTeOnmDuDYwr9/obA8t016Yljj"
    986   , "q5rdkywPf4JF8mXUW5eCN1vAFHxeg9ZWemhBtQmGxXnw9M+z6hWwc6ahmwARAQAB"
    987   , "tCtEb2NrZXIgUmVsZWFzZSAoQ0UgZGViKSA8ZG9ja2VyQGRvY2tlci5jb20+iQI3"
    988   , "BBMBCgAhBQJYrefAAhsvBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEI2BgDwO"
    989   , "v82IsskP/iQZo68flDQmNvn8X5XTd6RRaUH33kXYXquT6NkHJciS7E2gTJmqvMqd"
    990   , "tI4mNYHCSEYxI5qrcYV5YqX9P6+Ko+vozo4nseUQLPH/ATQ4qL0Zok+1jkag3Lgk"
    991   , "jonyUf9bwtWxFp05HC3GMHPhhcUSexCxQLQvnFWXD2sWLKivHp2fT8QbRGeZ+d3m"
    992   , "6fqcd5Fu7pxsqm0EUDK5NL+nPIgYhN+auTrhgzhK1CShfGccM/wfRlei9Utz6p9P"
    993   , "XRKIlWnXtT4qNGZNTN0tR+NLG/6Bqd8OYBaFAUcue/w1VW6JQ2VGYZHnZu9S8LMc"
    994   , "FYBa5Ig9PxwGQOgq6RDKDbV+PqTQT5EFMeR1mrjckk4DQJjbxeMZbiNMG5kGECA8"
    995   , "g383P3elhn03WGbEEa4MNc3Z4+7c236QI3xWJfNPdUbXRaAwhy/6rTSFbzwKB0Jm"
    996   , "ebwzQfwjQY6f55MiI/RqDCyuPj3r3jyVRkK86pQKBAJwFHyqj9KaKXMZjfVnowLh"
    997   , "9svIGfNbGHpucATqREvUHuQbNnqkCx8VVhtYkhDb9fEP2xBu5VvHbR+3nfVhMut5"
    998   , "G34Ct5RS7Jt6LIfFdtcn8CaSas/l1HbiGeRgc70X/9aYx/V/CEJv0lIe8gP6uDoW"
    999   , "FPIZ7d6vH+Vro6xuWEGiuMaiznap2KhZmpkgfupyFmplh0s6knymuQINBFit2ioB"
   1000   , "EADneL9S9m4vhU3blaRjVUUyJ7b/qTjcSylvCH5XUE6R2k+ckEZjfAMZPLpO+/tF"
   1001   , "M2JIJMD4SifKuS3xck9KtZGCufGmcwiLQRzeHF7vJUKrLD5RTkNi23ydvWZgPjtx"
   1002   , "Q+DTT1Zcn7BrQFY6FgnRoUVIxwtdw1bMY/89rsFgS5wwuMESd3Q2RYgb7EOFOpnu"
   1003   , "w6da7WakWf4IhnF5nsNYGDVaIHzpiqCl+uTbf1epCjrOlIzkZ3Z3Yk5CM/TiFzPk"
   1004   , "z2lLz89cpD8U+NtCsfagWWfjd2U3jDapgH+7nQnCEWpROtzaKHG6lA3pXdix5zG8"
   1005   , "eRc6/0IbUSWvfjKxLLPfNeCS2pCL3IeEI5nothEEYdQH6szpLog79xB9dVnJyKJb"
   1006   , "VfxXnseoYqVrRz2VVbUI5Blwm6B40E3eGVfUQWiux54DspyVMMk41Mx7QJ3iynIa"
   1007   , "1N4ZAqVMAEruyXTRTxc9XW0tYhDMA/1GYvz0EmFpm8LzTHA6sFVtPm/ZlNCX6P1X"
   1008   , "zJwrv7DSQKD6GGlBQUX+OeEJ8tTkkf8QTJSPUdh8P8YxDFS5EOGAvhhpMBYD42kQ"
   1009   , "pqXjEC+XcycTvGI7impgv9PDY1RCC1zkBjKPa120rNhv/hkVk/YhuGoajoHyy4h7"
   1010   , "ZQopdcMtpN2dgmhEegny9JCSwxfQmQ0zK0g7m6SHiKMwjwARAQABiQQ+BBgBCAAJ"
   1011   , "BQJYrdoqAhsCAikJEI2BgDwOv82IwV0gBBkBCAAGBQJYrdoqAAoJEH6gqcPyc/zY"
   1012   , "1WAP/2wJ+R0gE6qsce3rjaIz58PJmc8goKrir5hnElWhPgbq7cYIsW5qiFyLhkdp"
   1013   , "YcMmhD9mRiPpQn6Ya2w3e3B8zfIVKipbMBnke/ytZ9M7qHmDCcjoiSmwEXN3wKYI"
   1014   , "mD9VHONsl/CG1rU9Isw1jtB5g1YxuBA7M/m36XN6x2u+NtNMDB9P56yc4gfsZVES"
   1015   , "KA9v+yY2/l45L8d/WUkUi0YXomn6hyBGI7JrBLq0CX37GEYP6O9rrKipfz73XfO7"
   1016   , "JIGzOKZlljb/D9RX/g7nRbCn+3EtH7xnk+TK/50euEKw8SMUg147sJTcpQmv6UzZ"
   1017   , "cM4JgL0HbHVCojV4C/plELwMddALOFeYQzTif6sMRPf+3DSj8frbInjChC3yOLy0"
   1018   , "6br92KFom17EIj2CAcoeq7UPhi2oouYBwPxh5ytdehJkoo+sN7RIWua6P2WSmon5"
   1019   , "U888cSylXC0+ADFdgLX9K2zrDVYUG1vo8CX0vzxFBaHwN6Px26fhIT1/hYUHQR1z"
   1020   , "VfNDcyQmXqkOnZvvoMfz/Q0s9BhFJ/zU6AgQbIZE/hm1spsfgvtsD1frZfygXJ9f"
   1021   , "irP+MSAI80xHSf91qSRZOj4Pl3ZJNbq4yYxv0b1pkMqeGdjdCYhLU+LZ4wbQmpCk"
   1022   , "SVe2prlLureigXtmZfkqevRz7FrIZiu9ky8wnCAPwC7/zmS18rgP/17bOtL4/iIz"
   1023   , "QhxAAoAMWVrGyJivSkjhSGx1uCojsWfsTAm11P7jsruIL61ZzMUVE2aM3Pmj5G+W"
   1024   , "9AcZ58Em+1WsVnAXdUR//bMmhyr8wL/G1YO1V3JEJTRdxsSxdYa4deGBBY/Adpsw"
   1025   , "24jxhOJR+lsJpqIUeb999+R8euDhRHG9eFO7DRu6weatUJ6suupoDTRWtr/4yGqe"
   1026   , "dKxV3qQhNLSnaAzqW/1nA3iUB4k7kCaKZxhdhDbClf9P37qaRW467BLCVO/coL3y"
   1027   , "Vm50dwdrNtKpMBh3ZpbB1uJvgi9mXtyBOMJ3v8RZeDzFiG8HdCtg9RvIt/AIFoHR"
   1028   , "H3S+U79NT6i0KPzLImDfs8T7RlpyuMc4Ufs8ggyg9v3Ae6cN3eQyxcK3w0cbBwsh"
   1029   , "/nQNfsA6uu+9H7NhbehBMhYnpNZyrHzCmzyXkauwRAqoCbGCNykTRwsur9gS41TQ"
   1030   , "M8ssD1jFheOJf3hODnkKU+HKjvMROl1DK7zdmLdNzA1cvtZH/nCC9KPj1z8QC47S"
   1031   , "xx+dTZSx4ONAhwbS/LN3PoKtn8LPjY9NP9uDWI+TWYquS2U+KHDrBDlsgozDbs/O"
   1032   , "jCxcpDzNmXpWQHEtHU7649OXHP7UeNST1mCUCH5qdank0V1iejF6/CfTFU4MfcrG"
   1033   , "YT90qFF93M3v01BbxP+EIY2/9tiIPbrd"
   1034   , "=0YYh"
   1035   , "-----END PGP PUBLIC KEY BLOCK-----"
   1036   ]
   1037 
   1038 
   1039 m31 :: [Host]  -- Andromeda. Computers outside s' galaxy.
   1040 m31 = [
   1041   host "ns6.gandi.net" $ props
   1042     & ipv4  "217.70.177.40"
   1043       ]