propellor

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

config.hs (42779B)


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