propellor

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

config.hs (52375B)


      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 © 2017, 2018 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.Debootstrap as Debootstrap
     46 import qualified Propellor.Property.Chroot as Chroot
     47 import qualified Propellor.Property.Cron as Cron
     48 import qualified Propellor.Property.Dns as Dns
     49 import qualified Propellor.Property.Fail2Ban as Fail2Ban
     50 import qualified Propellor.Property.File as File
     51 import qualified Propellor.Property.Group as Group
     52 import qualified Propellor.Property.Locale as Locale
     53 import qualified Propellor.Property.Nginx as Nginx
     54 import qualified Propellor.Property.Postfix as Postfix
     55 import qualified Propellor.Property.Service as Service
     56 import qualified Propellor.Property.Ssh as Ssh
     57 import qualified Propellor.Property.Sudo as Sudo
     58 import qualified Propellor.Property.Systemd as Systemd
     59 import qualified Propellor.Property.User as User
     60 
     61 
     62 
     63 main :: IO ()
     64 main = defaultMain hosts
     65 
     66 -- The hosts propellor knows about.
     67 hosts :: [Host]
     68 hosts = privateHosts ++ publicHosts
     69 
     70 privateHosts :: [Host]
     71 privateHosts = [ cygnus ]
     72 
     73 publicHosts :: [Host]
     74 publicHosts = [ crux, ara, lyra ] ++ m31
     75 
     76 -- configure cygnus.
     77 cygnus :: Host
     78 cygnus = host "cygnus.ricketyspace.net" $ props
     79         & osDebian Unstable X86_64
     80 	& Locale.available "en_US.UTF-8"
     81         & File.hasContent "/etc/motd" (["At cygnus."])
     82         -- apt config.
     83         & Apt.stdSourcesList
     84         & Apt.unattendedUpgrades
     85         & Apt.safeUpgrade
     86         & Apt.installed ["tor", "torsocks"
     87                          , "curl"
     88                          , "zsh", "rxvt-unicode", "tmux"
     89                          , "cvs", "git", "git-doc", "git-ftp", "git-email"
     90                          , "git-annex", "git-remote-gcrypt", "myrepos"
     91                          , "xinit", "x11-xserver-utils", "libxrandr-dev"
     92                          , "stumpwm"
     93                          , "xfonts-terminus", "fonts-noto", "unifont"
     94                          , "imagemagick", "inkscape", "gimp", "sane"
     95                          , "parcimonie", "pass", "pinentry-gtk2"
     96                          , "tomb", "wipe", "steghide"
     97                          , "python-virtualenv", "python-pip"
     98                          , "make", "make-doc", "gcc", "gcc-doc", "gcc-doc-base"
     99                          , "golang-go"
    100                          , "racket", "racket-doc"
    101                          , "glibc-doc", "glibc-doc-reference"
    102                          , "gdb", "valgrind", "exuberant-ctags"
    103                          , "mdk", "mdk-doc"
    104                          , "udisks2", "etckeeper", "acpi-support"
    105                          , "ntp", "ntp-doc", "mosh", "dnsutils", "nscd"
    106                          , "nginx-full", "network-manager", "mailutils"
    107                          , "postgresql-doc", "postgresql-client"
    108                          , "alsa-utils", "pulseaudio"
    109                          , "mpd", "mpc", "ncmpcpp", "mplayer"
    110                          , "mupdf"
    111                          , "htop"
    112                          , "chromium", "w3m", "lynx", "torbrowser-launcher"
    113                          , "redshift"
    114                          , "unzip", "zip"
    115                          , "silversearcher-ag"
    116                          , "weather-util", "weather-util-data"
    117                          , "sudo", "debian-goodies", "debootstrap"
    118                          , "aptitude"
    119 			 , "systemd-container", "docker-compose"
    120                          , "anarchism"
    121                         ]
    122         -- docker-ce
    123         & dockerCEInstalled
    124         -- s config.
    125 	& User.accountFor (User "s")
    126 	& User.hasLoginShell (User "s") "/usr/bin/zsh"
    127 	& User.hasSomePassword (User "s")
    128         & Ssh.userKeyAt (Just sCanonicalSshKeyPath)
    129         (User "s") hostContext (SshRsa, sCanonicalSshPubKey)
    130         & User.hasGroup (User "s") (Group "docker")
    131         --- crons.
    132         & rsyncToMollisol
    133         & rsyncFromCruxToMollisol
    134         & annexToArdisol
    135         & annexSyncFromHome
    136         & getCruxEtc
    137         & autoCommitCygnusRepos
    138         & removeEmptyHomeDirs
    139         & buildEmacs
    140         & installEmacs
    141         & cygnusPgDump
    142         -- chroots.
    143         & Chroot.provisioned cygnusEmacsBuilder
    144         -- containers.
    145         & Systemd.nspawned cygnusPostgresContainer
    146         -- root config.
    147         & User.hasSomePassword (User "root")
    148         & Ssh.authorizedKey (User "root") sCanonicalSshPubKey
    149         & Cron.job "etc-push" (Cron.Times "15 03 *   * *") (User "root")
    150         "/etc" "git push"
    151         -- etc config.
    152         & File.containsLines "/etc/hosts" cygnusHosts
    153         & File.hasContent "/etc/mpd.conf" cygnusMpd
    154         & File.hasContent "/etc/sudoers.d/s" cygnusSudoers
    155         & File.containsLines "/etc/NetworkManager/NetworkManager.conf"
    156         cygnusNetworkManager
    157         --- nginx.
    158         & Nginx.siteEnabled "cygnus" cygnusNginx
    159         -- propellor.
    160         & Cron.runPropellor (Cron.Times "30 22 * * *")
    161 
    162 -- keys.
    163 sCanonicalSshKeyPath :: [Char]
    164 sCanonicalSshKeyPath = "/home/s/.ssh/id_the_rsa"
    165 
    166 sCanonicalSshPubKey :: [Char]
    167 sCanonicalSshPubKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDd7kT6tpH4zZ/hFlRmSVH1oJZZJJUvoMd89AiskXAq5rBrvZC90WVOF12OTQVQqslUVV2ze7BCC13UEfK5F2xP+7F6FDqSFApV4lBsJWLNbtDlZY23lTYqi/L6muq3x5tbqJLQjUK5ItORe0Ecqqz1ymSy+Zk+kHmHfnqyoWAQ7Z5GJkRu1B4J9uT3LJDIgLE8m4nJEOoCJ5vnycJfs0LCwHNZ67H38FV3Uw/sGibCNyCSJHQcG+nkKGYzABDcbXmWXedUq0MlRY2TjU22cOzjaAH0mf6M7m6KQCQeXjdxyyLaI3lNOzhBOU7j8/H9GqdRDH8pZ5e4xp+AG3tcrWi2E+47Qp4J9qv0YPgfj7JZ4oJhvCKGkgldOQZ8mkvjDLvAMGte0zpk2SPDlfJeFgfDHMre3nxAAzIfmhaIX2j86LdUh717BmcXDYD//9SubRLdAZrOKh4Iapcotm0STOFUlDa6nvh27DuKIIvq7v/+ID6P4fSNb4h1ktC/3lrhI21ei8ZjBulonJ9XV+BPGlnIzmWL7g5j4dMm90AqZXRRKQMAX9UIizLoCh58KR5gESszQ/8MSrELclI1fUwiY4Wxlvf6ZsGsg3c22xumxlnc85eiAYQ1LEPWNghcyqE96yrIhXrph3unLRsEbZcGxgXw/dcUtjIL9tjDO9zSN6ZuOQ== rsd@grus"
    168 
    169 sAraSshPubKey :: [Char]
    170 sAraSshPubKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDTn+zicq75blG6yWY0GIkba93FDbWHOo95k2oqrUVr3llmpuRnYCdp6F7LIiRF4NxDA6hBL5JAlxwS0mKgpbzJjbav7S7XxV1HxrtbO/bQqbcH3pvGCuSvltpZg072pB/g1Uhcv9S2FiOHJsedqlGiqUfucuFdRINYk3a8cQVTZ6XB7AxpqGLova3b+1rdhF6c5DSxgMscvZ+zGANN8tmv/0iGERRRdpvePR7iKZp7ZpqrsS4zXHJO537T6Z05lhaD+Fk/sgz5AUT/fkxIVvsNYL23mVS2fQg1htBAZi4vZ7joZn643bc1w6B7oa8nKEagRu4EWw+LbMeKzecIv0pB s@cygnus.ricketyspace.net"
    171 
    172 sLyraSshPubKey :: [Char]
    173 sLyraSshPubKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0UdjqvAo5lICg0BwGv6aQVSO+Ew6ZXFwbwpZiXhZ03+P/Wk98mJJUln+NaKC9lbjQnAShviBy5BIPbexQJ21vvP6mh13Z6xPPtHWRYUy+Y0GYvnc8yKfsnJ2bDaXobpAprfwWpwhW01ZC2xehaZxE+w8dwdAP4v53w5XlLU733f19vRqis4Y0/jyse2BMpsVGzWiLRLprjeLOgdcDB+yLP+EnM46Yj5z0xchbO2uCozloX1yhExDJ2Z4MITx397+/3GYaPEytcMVD/YmIScU4y4nqwa8O/sg5miamD6HUzAWKOHkHgf1HosDbz4JHcEwJqgSvqTeVJ5UWWuPK7YfJ s@cygnus.ricketyspace.net"
    174 
    175 -- start cygnus snafu.
    176 sourceCygnusEnv :: [Char] -> [Char]
    177 sourceCygnusEnv cmd = ". $HOME/.env; " ++ cmd
    178 
    179 --- backup paths.
    180 ---- entisol.
    181 mollisolFilter :: [Char]
    182 mollisolFilter = "/home/s/v/cygnus.ricketyspace.net/"
    183                  ++ "dp/rsync-backup/filter-mollisol"
    184 
    185 mollisolExcludeFilter :: [Char]
    186 mollisolExcludeFilter = "/home/s/v/cygnus.ricketyspace.net/"
    187                         ++ "dp/rsync-backup/filter-mollisol-exclude"
    188 
    189 mollisolBackupPath :: [Char]
    190 mollisolBackupPath = "/media/mollisol/box/cygnus/latest/"
    191 
    192 ardisolAnnexPath :: [Char]
    193 ardisolAnnexPath = "/media/ardisol/annex"
    194 
    195 pgDumpsDir :: [Char]
    196 pgDumpsDir = "/home/s/.pgdumps"
    197 
    198 ---- crux.
    199 cruxFilter :: [Char]
    200 cruxFilter = "/home/s/v/cygnus.ricketyspace.net/dp/rsync-backup/filter-crux"
    201 
    202 cruxBackupPath :: [Char]
    203 cruxBackupPath = "/media/mollisol/box/crux/latest/"
    204 
    205 ---- scripts.
    206 rsyncBuTo :: [Char] -> [Char] -> [Char] -> [Char] -> [Char]
    207 rsyncBuTo src dest rbtFilter rbtExcludeFilter = concat [
    208   "./rsync-bu-to "
    209   , src, " "
    210   , dest, " "
    211   , rbtFilter, " "
    212   , rbtExcludeFilter
    213   ]
    214 
    215 annexTo :: [Char] -> [Char] -> [Char]
    216 annexTo annexPath annexGetPath = "./annex-to "
    217                          ++ annexPath ++ " " ++ annexGetPath
    218 
    219 ---- s's crons on cygnus.
    220 rsyncToMollisol :: Property DebianLike
    221 rsyncToMollisol = Cron.job "rsync-to-mollisol" (Cron.Times "00 03 * * *")
    222   (User "s") "/home/s/.bin" rsyncCmd
    223   where
    224     rsyncCmd = rsyncBuTo "/home/s" mollisolBackupPath
    225                mollisolFilter mollisolExcludeFilter
    226 
    227 rsyncFromCruxToMollisol :: Property DebianLike
    228 rsyncFromCruxToMollisol = Cron.job "rsync-from-crux-to-mollisol"
    229   (Cron.Times "30 03 * * *")
    230   (User "s") "/home/s/.bin" rsyncCmd
    231   where
    232     rsyncCmd = sourceCygnusEnv (rsyncBuTo "s@crux:~/" cruxBackupPath
    233                                  cruxFilter "")
    234 
    235 annexToArdisol :: Property DebianLike
    236 annexToArdisol = Cron.job "annex-to-ardisol" (Cron.Times "45 03 * * *")
    237   (User "s") "/home/s/.bin" annexCmd
    238   where
    239     annexCmd = annexTo ardisolAnnexPath "."
    240 
    241 annexSyncFromHome :: Property DebianLike
    242 annexSyncFromHome = Cron.job "annex-sync-from-home"
    243   (Cron.Times "00 03 * * *")
    244   (User "s") "/home/s/annex" annexCmd
    245   where
    246     annexCmd = "git annex sync"
    247 
    248 getCruxEtc :: Property DebianLike
    249 getCruxEtc = Cron.job "get-crux-etc" (Cron.Times "20 22 * * *")
    250   (User "s") "/home/s/v/cygnus.ricketyspace.net/crux-etc" gitCmd
    251   where
    252     gitCmd = sourceCygnusEnv "git pull origin"
    253 
    254 autoCommitCygnusRepos :: Property DebianLike
    255 autoCommitCygnusRepos = Cron.job "auto-commit-repos"
    256   (Cron.Times "20 00 * * *")
    257   (User "s") "/home/s/.bin/" autoCommitCmd
    258   where
    259    autoCommitCmd = sourceCygnusEnv "./git-difme"
    260 
    261 removeEmptyHomeDirs :: Property DebianLike
    262 removeEmptyHomeDirs = Cron.job "remove-empty-home-dirs"
    263   (Cron.Times "*/10 * * * *")
    264   (User "s") "/home/s/" cmd
    265   where
    266     cmd = "rm -rf Documents Public Desktop Downloads" ++
    267           " Music Pictures Templates Videos"
    268 
    269 buildEmacs :: Property DebianLike
    270 buildEmacs = Cron.job "build-emacs"
    271   (Cron.Times "00 12 * * 6")
    272   (User "s") "/home/s/.bin/" cmd
    273   where
    274     cmd = "/home/s/.bin/emacs-build start-make"
    275 
    276 installEmacs :: Property DebianLike
    277 installEmacs = Cron.job "install-emacs"
    278   (Cron.Times "00 14 * * 6")
    279   (User "root") "/home/s/v/git.sv.gnu.org/emacs" cmd
    280   where
    281     cmd = "make install"
    282 
    283 cygnusPgDump :: Property DebianLike
    284 cygnusPgDump = Cron.job "cygnusPgDump"
    285   (Cron.Times "30 02 * * *")
    286   (User "s") "/home/s/.bin/" cmd
    287   where
    288     cmd = "./pgdump " ++ "s" ++ " " ++ pgDumpsDir
    289 
    290 ---- containers / chroots
    291 
    292 cygnusPostgresContainer :: Systemd.Container
    293 cygnusPostgresContainer = Systemd.debContainer "cygnus-postgres" $ props
    294   & osDebian (Stable "stretch") X86_64
    295   & Locale.available "en_US.UTF-8"
    296   & Apt.stdSourcesList `onChange` Apt.upgrade
    297   & Apt.cacheCleaned
    298   & Apt.installed ["postgresql", "postgresql-plpython3"]
    299 
    300 cygnusEmacsBuilder :: Chroot.Chroot
    301 cygnusEmacsBuilder = Chroot.debootstrapped Debootstrap.BuilddD dir $ props
    302   & osDebian Unstable X86_64
    303   & Locale.available "en_US.UTF-8"
    304   & Apt.stdSourcesList `onChange` Apt.upgrade
    305   & Apt.cacheCleaned
    306   & Apt.installed ["locales", "libtiff5-dev", "libgtk-3-dev"
    307                   , "libxpm-dev", "libgif-dev", "libgnutls28-dev"
    308                   , "libmagickwand-dev", "libncurses-dev", "git"
    309                   , "mailutils", "texinfo", "libacl1-dev"
    310                   ]
    311   & Apt.buildDep ["emacs"]
    312   where
    313     dir = "/var/lib/container/emacs-builder"
    314 
    315 ---- etc
    316 cygnusHosts :: [File.Line]
    317 cygnusHosts = [
    318   "127.0.0.2    taoup.web"
    319   , "127.0.0.3    gnu.web"
    320   , "127.0.0.4    rsd.web"
    321   , "127.0.0.5    sicp.web"
    322   , "127.0.0.6    tmp.web"
    323   , "127.0.0.8    lp.web"
    324   , "127.0.0.9    vm.web"
    325   , "127.0.0.10    home.web"
    326   , "127.0.0.11    rs.web"
    327   , "127.0.0.12    lpsg.web"
    328   , "127.0.0.13    cached.web"
    329   ]
    330 
    331 cygnusMpd :: [File.Line]
    332 cygnusMpd = [
    333   "music_directory \"/home/s/annex/music/\""
    334   , "playlist_directory \"/var/lib/mpd/playlists\""
    335   , "db_file \"/var/lib/mpd/tag_cache\""
    336   , "log_file \"/var/log/mpd/mpd.log\""
    337   , "state_file \"/var/lib/mpd/state\""
    338   , "sticker_file \"/var/lib/mpd/sticker.sql\""
    339   , "user \"mpd\""
    340   , "bind_to_address \"localhost\""
    341   , "port \"6600\""
    342   , "log_level \"verbose\""
    343   , "save_absolute_paths_in_playlists \"yes\""
    344   , "metadata_to_use \"artist,album,title,track,name,genre,date,composer,"
    345     ++ "performer,disc\""
    346   , "auto_update \"yes\""
    347   , "input {"
    348   , "        plugin \"curl\""
    349   , "}"
    350   , "audio_output {"
    351   , "        type            \"alsa\""
    352   , "        name            \"My ALSA Device\""
    353   , "        mixer_type      \"software\""
    354   , "}"
    355   , "audio_output {"
    356   , "type    \"httpd\""
    357   , "name    \"My HTTP Stream\""
    358   , "bind_to_address    \"0.0.0.0\""
    359   , "quality    \"10.0\""
    360   , "port    \"8426\""
    361   , "format    \"44100:16:1\""
    362   , "max_clients   \"0\""
    363   , "}"
    364   , "filesystem_charset \"UTF-8\""
    365   , "id3v1_encoding \"UTF-8\""
    366   ]
    367 
    368 cygnusNginx :: [String]
    369 cygnusNginx = [
    370   "### vm server"
    371   ,"server {"
    372   ,"    ssi on;"
    373   ,"    server_name vm.web;"
    374   ,"    root /home/s/v/git/s/v-page;"
    375   ,"    index index.php index.html;"
    376   , ""
    377   ,"    location / {"
    378   ,"       autoindex on;"
    379   ,"       include /etc/nginx/mime.types;"
    380   ,"       rewrite ^/(land|macro|wp|wild|people|fashion).*$ "
    381     ++ "/gallery/$1/ permanent;"
    382   ,"       rewrite ^/as/photography-classes/?$ /as permanent;"
    383   ,"    }"
    384   , ""
    385   ,"    location ~ \\.php$ {"
    386   ,"        include snippets/fastcgi-php.conf;"
    387   ,"        fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;"
    388   ,"    }"
    389   ,"}"
    390 
    391   ,"### rsd server"
    392   ,"server {"
    393   ,"    server_name rsd.web;"
    394   ,"    root /home/s/v/ninthfloor.org/rsd-w3;"
    395   ,"    index index.html;"
    396   , ""
    397   ,"    location / {"
    398   ,"       autoindex on;"
    399   ,"    }"
    400   ,"}"
    401 
    402   ,"### home server"
    403   ,"server {"
    404   ,"    server_name home.web;"
    405   ,"    root /home/s/;"
    406   ,"    index index.html;"
    407   , ""
    408   ,"    location / {"
    409   ,"       autoindex on;"
    410   ,"    }"
    411   ,"}"
    412 
    413   ,"### ricketyspace server"
    414   ,"server {"
    415   ,"    server_name rs.web;"
    416   ,"    root /home/s/v/crux.ricketyspace.net/rs;"
    417   ,"    index index.html;"
    418   , ""
    419   ,"    location / {"
    420   ,"       autoindex on;"
    421   ,"    }"
    422   ,"}"
    423 
    424   ,"### gnu server"
    425   ,"server {"
    426   ,"        ssi on;"
    427   ,"    server_name gnu.web;"
    428   ,"    root /home/s/v/cvs.sv.gnu.org/www-gnu/;"
    429   ,"    index index.html, home.html;"
    430   , ""
    431   ,"    location /software/ {"
    432   ,"        alias /home/s/v/cvs/gnu/software/;"
    433   ,"        autoindex on;"
    434   ,"    }"
    435   , ""
    436   ,"    location / {"
    437   ,"       autoindex on;"
    438   ,"    }"
    439   ,"}"
    440 
    441   ,"### lp server"
    442   ,"server {"
    443   ,"        ssi on;"
    444   ,"    server_name lp.web;"
    445   ,"    root /home/s/v/git/fsf/lp-static;"
    446   ,"    index index.html;"
    447   ,""
    448   ,"    location / {"
    449   ,"       autoindex on;"
    450   ,"    }"
    451   ,"}"
    452   , ""
    453   ,"### sicp server"
    454   ,"server {"
    455   ,"        ssi on;"
    456   ,"    server_name sicp.web;"
    457   ,"    root /home/s/annex/eat/sicp/mitpress.mit.edu/sicp;"
    458   ,"    index index.html;"
    459   ,""
    460   ,"    location / {"
    461   ,"       autoindex on;"
    462   ,"    }"
    463   ,"}"
    464   , ""
    465   ,"### taoup server"
    466   ,"server {"
    467   ,"        ssi on;"
    468   ,"    server_name taoup.web;"
    469   ,"    root /home/s/annex/eat/taoup/www.catb.org/~esr/writings/taoup/html;"
    470   ,"    index index.html;"
    471   ,""
    472   ,"    location / {"
    473   ,"       autoindex on;"
    474   ,"    }"
    475   ,"}"
    476   , ""
    477   ,"### combox server"
    478   ,"server {"
    479   ,"        ssi on;"
    480   ,"    server_name combox.web;"
    481   ,"    root /home/s/v/git/bgc/combox/docs/_build/html;"
    482   ,"    index index.html;"
    483 
    484   ,"    location / {"
    485   ,"       autoindex on;"
    486   ,"    }"
    487   ,"}"
    488   , ""
    489   ,"### lpschedule-generator server"
    490   ,"server {"
    491   ,"        ssi on;"
    492   ,"    server_name lpsg.web;"
    493   ,"    root /home/s/v/git/fsf/lpschedule-generator/docs/_build/html;"
    494   ,"    index index.html;"
    495   , ""
    496   ,"    location / {"
    497 
    498   ,"       autoindex on;"
    499   ,"    }"
    500   ,"}"
    501   , ""
    502   ,"### tmp server"
    503   ,"server {"
    504   ,"    ssi on;"
    505   ,"    server_name tmp.web;"
    506   ,"    root /tmp;"
    507   ,"    index index.html;"
    508   , ""
    509   ,"    location / {"
    510   ,"       autoindex on;"
    511   ,"    }"
    512   ,"}"
    513   ,"### cached web server"
    514   ,"server {"
    515   ,"    ssi on;"
    516   ,"    server_name cached.web;"
    517   ,"    root /home/s/.cached-web;"
    518   ,"    index index.html;"
    519   , ""
    520   ,"    location / {"
    521   ,"       autoindex on;"
    522   ,"    }"
    523   ,"}"
    524   ]
    525 
    526 cygnusSudoers :: [File.Line]
    527 cygnusSudoers = [
    528   "# Host alias specification"
    529   , ""
    530   ,"# User alias specification"
    531   ,"User_Alias    CRYPTERS = s"
    532   ,"User_Alias    SANDBOXERS = s"
    533   ,"User_Alias    SUPER_COWS = s"
    534   , ""
    535   ,"# Cmnd alias specification"
    536   ,"Cmnd_Alias    CRYPT = /sbin/losetup, /sbin/cryptsetup, \\"
    537   ,"                      /sbin/mkfs.ext4, /bin/mount, /bin/umount, \\"
    538   ,"                      /usr/bin/tomb"
    539   ,"Cmnd_Alias    SANDBOX = /usr/sbin/debootstrap, /usr/bin/systemd-nspawn, \\"
    540   ,"                        /bin/machinectl, /usr/bin/docker"
    541   , ""
    542   ,"# User privilege specification"
    543   ,"root    ALL=(ALL:ALL) ALL"
    544   , ""
    545   ,"# Allow super cows to execute any command"
    546   ,"SUPER_COWS ALL=(ALL:ALL) ALL"
    547 
    548   , ""
    549   ,"CRYPTERS  ALL = NOPASSWD: CRYPT "
    550   ,"SANDBOXERS ALL = NOPASSWD: SANDBOX"
    551   ]
    552 
    553 cygnusNetworkManager :: [File.Line]
    554 cygnusNetworkManager = [
    555   "[device]"
    556   , "wifi.scan-rand-mac-address=no"
    557   ]
    558 -- end cygnus snafu.
    559 
    560 -- configure crux
    561 crux :: Host
    562 crux = host "crux.ricketyspace.net" $props
    563        & osDebian (Stable "stretch") X86_64
    564        & ipv4 "45.55.79.15"
    565        & ipv6 "2604:a880:800:10::a12:5001"
    566        & File.hasContent "/etc/motd" (["At crux."])
    567        -- apt
    568        & Apt.stdSourcesList
    569        & Apt.unattendedUpgrades
    570        & Apt.safeUpgrade
    571        -- s config.
    572        & User.accountFor (User "s")
    573        & User.hasLoginShell (User "s") "/bin/bash"
    574        & User.hasSomePassword (User "s")
    575        & Ssh.userKeyAt (Just sCruxSshKeyPath) (User "s")
    576             hostContext (SshRsa, sCruxSshKeyPub)
    577        -- system config.
    578        & Cron.job "etc-push" (Cron.Times "15 00 *   * *") (User "root")
    579         "/etc" "git push"
    580        & Fail2Ban.installed
    581 
    582 sCruxSshKeyPath :: [Char]
    583 sCruxSshKeyPath = "/home/s/.ssh/id_rsa"
    584 
    585 sCruxSshKeyPub :: [Char]
    586 sCruxSshKeyPub = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC7uhKZgiMr0G0NDUQBz/3AOJNzPeq3EZS8y62rvAHt8Mc7qvjjLjW/rNmPRDMU+6y0RcbKiZfzyUdPHYddD73iftFQs8PkAkKbgjCKZus+Id+/Q6gjSmn9LMHDbyAcy6QpADIfZDm2k0cKuNNlnj4xGtSkxnzGnw+bR0oFmvCM+bDEq3Vz7b0hJYCQqPsUVEOw0SNHGtMa9y6iqpuuN/HSYainsjviDvReuo5qUOY7tmygx/6uge13BjKhhsTvg5KQkgtTYvSo+pxdO4Jgk60wnmFGK93+WoJBD2A0KRhOaelQ5SkgDv9sKf4Uhtb8ANCuneMBRBPHjlaMk59JzVUCHS8qSXYvklFkwUahqwUsd3TOfc93tNAnv90hJkzm4PtkjmC4iWMDNmjIflulKh2v6ul7ZJ/appHUrqM3qJVYTV+MXB1jduw90dR+K9ThroeSSw1aKUW1ijkgjFEt+l6R4CLHvBbfdYt4EbKU/v+GJlP5aQbUT0PGAotqeyX58L6iOCTlRRUm94uVjtakoiBAAZi1m0z0/AEMRxtc8Eym8vXiNcULeConcn+rMm5fYdC9NysPJcEu6P+ycx74Klw82sBvWMS/oek/Tn/B2WRwRbPVl3rxz1gwDDA/sE3UKnj04LCQXRdYMKEndPqsA8eD/WgWXBubxj6ThoAtSV7Czw== s@cygnus"
    587 
    588 
    589 -- configure ara
    590 ara :: Host
    591 ara = host "ara.ricketyspace.net" $props
    592        & osDebian Testing X86_64
    593        & ipv4 "68.183.110.18"
    594        & Locale.available "en_US.UTF-8"
    595        & File.hasContent "/etc/motd" (["At ara."])
    596        -- apt
    597        & Apt.stdSourcesList
    598        & Apt.unattendedUpgrades
    599        & Apt.safeUpgrade
    600        & Apt.installed ["nginx-full", "htop"
    601                         , "git",  "tmux"
    602                         , "python-virtualenv", "man-db"
    603                         , "gcc", "gdb", "pkg-config"
    604                         , "exuberant-ctags", "make"
    605                         , "wget", "xz-utils", "netbase"
    606                         , "gnupg2"
    607                        ]
    608        -- sshd
    609        & Ssh.passwordAuthentication False
    610        -- system
    611        & Fail2Ban.installed
    612        -- root config
    613        & File.hasContent "/root/.tmux.conf" tmuxConf
    614        & Ssh.authorizedKey (User "root") sCanonicalSshPubKey
    615        & Ssh.userKeyAt (Just "/root/.ssh/id_rsa")
    616         (User "root") hostContext (SshRsa, sAraSshPubKey)
    617        -- s config
    618        & User.accountFor(User "s")
    619        & User.hasPassword (User "s")
    620        & User.hasLoginShell (User "s") "/bin/bash"
    621        & Sudo.enabledFor (User "s")
    622        & Ssh.authorizedKey (User "s") sCanonicalSshPubKey
    623        & Ssh.userKeyAt (Just "/home/s/.ssh/id_rsa")
    624         (User "s") hostContext (SshRsa, sAraSshPubKey)
    625       & File.hasContent "/home/s/.tmux.conf" tmuxConf
    626       & File.ownerGroup "/home/s" (User "s") (Group "s")
    627       & File.containsLines "/home/s/.bashrc" [
    628          "export GUIX_LOCPATH=$HOME/.guix-profile/lib/locale"
    629          , "export PATH=\"/home/s/.guix-profile/bin${PATH:+:}$PATH\""
    630          ]
    631        -- nginx
    632       & Nginx.siteEnabled "ara.ricketyspace.net" araNginx
    633       & File.dirExists "/home/s/pub"
    634       & File.ownerGroup "/home/s/pub" (User "s") (Group "s")
    635       & File.hasContent "/home/s/pub/robots.txt" araWebRobotsTxt
    636       & File.hasContent "/home/s/pub/index.html" araWebIndex
    637       -- docker-ce
    638       & dockerCEInstalled
    639       & User.hasGroup (User "s") (Group "docker")
    640       -- guix
    641       & File.hasContent "/usr/local/bin/install-guix" installGuix
    642       & File.mode "/usr/local/bin/install-guix" 0O0755
    643       & scriptProperty [ "install-guix" ] `assume` MadeChange
    644 
    645 
    646 installGuix :: [File.Line]
    647 installGuix = [
    648   "#!/bin/sh"
    649   , "[ -d \"/gnu\" ] && exit 0"
    650   , ""
    651   , "gpg --keyserver pool.sks-keyservers.net --recv-keys 3CE464558A84FDC69DB40CFB090B11993D9AEBB5 &&"
    652   , "   wget https://git.savannah.gnu.org/cgit/guix.git/plain/etc/guix-install.sh \\"
    653   , "        -O /tmp/guix-install.sh  && \\"
    654   , "        chmod +x /tmp/guix-install.sh && \\"
    655   , "        sed -i -e 's/read -r  ANSWER/read -N 1 -r  ANSWER/g' /tmp/guix-install.sh && \\"
    656   , "        echo \"yy\" | /tmp/guix-install.sh && \\"
    657   ,         "rm -rf /tmp/guix-install.sh"
    658   ]
    659 
    660 
    661 araNginx :: [String]
    662 araNginx = [
    663   "server {"
    664   , "    listen 80;"
    665   , "    listen [::]:80;"
    666   , "    server_name ara.ricketyspace.net;"
    667   , "    root /home/s/pub;"
    668   , "    error_page 404 /404.html;"
    669   , "    error_page 403 /403.html;"
    670   , "    default_type text/plain;"
    671   , "}"
    672   ]
    673 
    674 araWebRobotsTxt :: [File.Line]
    675 araWebRobotsTxt = [
    676   "User-agent: *"
    677   , "Disallow: /"
    678   ]
    679 
    680 araWebIndex :: [File.Line]
    681 araWebIndex = [""]
    682 
    683 
    684 -- configure lyra
    685 lyra :: Host
    686 lyra = host "lyra.ricketyspace.net" $props
    687        & osDebian (Stable "stretch") X86_64
    688        & Locale.available "en_US.UTF-8"
    689        & ipv4 "159.89.229.91"
    690        & ipv6 "2604:a880:400:d1::77f:8001"
    691        & alias "ricketyspace.net"
    692        & alias "git.ricketyspace.net"
    693        & File.hasContent "/etc/motd" (["At lyra."])
    694        -- apt
    695        & Apt.stdSourcesList
    696        & Apt.unattendedUpgrades
    697        & Apt.safeUpgrade
    698        & Apt.installed ["git", "etckeeper"
    699                         , "htop", "sudo", "zsh", "tmux"
    700                         , "emacs", "rsync", "git-annex"
    701                         , "cgit", "fcgiwrap", "git-daemon-sysvinit"
    702                         , "bind9", "bind9-doc"
    703                         , "opendkim", "opendkim-tools",
    704                           "postfix", "postfix-doc"
    705                         , "postfix-policyd-spf-python", "postfix-pcre"
    706                         , "dovecot-imapd", "python3-markdown", "python3-docutils"
    707                         , "pandoc", "cvs"
    708                         , "libgit2-dev", "musl-dev", "make"
    709                        ]
    710        -- sshd
    711        & Ssh.passwordAuthentication False
    712        -- system
    713        & Fail2Ban.installed
    714        --- nginx
    715        & Apt.removed ["apache2"]
    716        & Nginx.installed
    717        & Nginx.siteEnabled "ricketyspace.net" ricketyspaceNetNginx
    718        & Nginx.siteEnabled "git.ricketyspace.net" ricketyspaceNetGitNginx
    719        & ricketyspaceNetDhparamPem
    720        & ricketyspaceNetCert `onChange` Nginx.restarted
    721        & ricketyspaceNetCertKey
    722        & ricketyspaceNetGitCert
    723        & ricketyspaceNetGitCertKey
    724        & ricketyspaceNetLyraCert
    725        & ricketyspaceNetLyraCertKey
    726        -- cgit
    727        & rsCgitRC
    728        -- git-daemon
    729        & rsGitDaemonDefaults `onChange` Service.restarted "git-daemon"
    730        -- opendkim
    731        & File.dirExists "/etc/opendkim"
    732        & File.containsLines "/etc/opendkim.conf" ricketyspaceNetOpenDkimConf
    733        & File.containsLines "/etc/opendkim/KeyTable" ricketyspaceNetOpenDkimKeyTable
    734        & File.containsLines "/etc/opendkim/SigningTable" ricketyspaceNetOpenDkimSigningTable
    735        & File.containsLines "/etc/opendkim/TrustedHosts" ricketyspaceNetOpenDkimTrustedHosts
    736        & File.dirExists "/etc/dkimkeys/ricketyspace.net"
    737        & File.ownerGroup "/etc/dkimkeys/ricketyspace.net" (User "opendkim") (Group "opendkim")
    738        & File.hasPrivContent "/etc/dkimkeys/ricketyspace.net/mail.private" (Context "ricketyspace.net")
    739        & File.ownerGroup "/etc/dkimkeys/ricketyspace.net/mail.private"
    740             (User "opendkim") (Group "opendkim")
    741        -- postfix
    742        & rsMailAliases
    743        & rsPostfixHeaderChecks
    744        & rsPostfixRBLOverride
    745        & File.hasContent "/etc/postfix/main.cf" rsPostfixMainCf
    746        & File.hasContent "/etc/postfix/master.cf" rsPostfixMasterCf `onChange` Postfix.reloaded
    747        -- dovecot
    748        & File.hasContent "/etc/dovecot/dovecot.conf" rsDovecotConf
    749              `onChange` Service.restarted "dovecot"
    750        -- bind
    751        & Dns.primary publicHosts "ricketyspace.net"
    752        (Dns.mkSOA "lyra.ricketyspace.net" 20180129)
    753        [
    754          (RootDomain, NS $ RelDomain "lyra")
    755        , (RootDomain, NS $ AbsDomain "ns6.gandi.net")
    756        , (RootDomain, MX 0 $ AbsDomain "lyra.ricketyspace.net")
    757        , (RootDomain, TXT "v=spf1 mx -all")
    758        , (RelDomain "_dmarc", TXT "v=DMARC1; p=none; rua=mailto:root@ricketyspace.net")
    759        , (RelDomain "mail._domainkey", TXT "v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6ClUrJTSt/UISOTEoZy36SfCjuyuajJVGEzYrhuysn2CA3MNt6y3dsHrjalA04Bix02KySMVEHcbScsd54MHhvk364pOkapuAEFIAmvY9SiZGRWsKMK5tgq/aSgH6xFg6M1CupV4QHWFgk193juiEdnB8uwXKTxOdKP/P/xbU3h98MFvXmNzT8sEr5VMZHiTdzqcxCLLfhx27iwjFJh4td6y+0n5YO/M2zf3n9ikXIof/dw4lA5Xo2icI3G88LMl9Tk4vcY0UVXXXulKmrnrn96Nyow0zU31kB/NUb1HbOxaVLz7KJThS+U9NV/66vZ5blwg7aExJXkPfVEtLTca+wIDAQAB")
    760        ]
    761        -- crons
    762        & stagitGenCron
    763        --
    764        & Group.exists (Group "pubs") Nothing
    765        -- root config
    766        & Ssh.authorizedKey (User "root") sCanonicalSshPubKey
    767        & File.hasContent "/root/.tmux.conf" tmuxConf
    768        -- www-data config
    769        & User.hasGroup (User "www-data") (Group "pubs")
    770        -- gitdaemon config
    771        & User.hasGroup (User "gitdaemon") (Group "pubs")
    772        -- w config
    773        & User.accountFor (User "w")
    774        & User.hasGroup (User "w") (Group "pubs")
    775        & Ssh.authorizedKey (User "w") sCanonicalSshPubKey
    776        & Ssh.authorizedKey (User "w") sCruxSshKeyPub
    777        --- pub dir config
    778        & File.dirExists "/home/w/pub"
    779        & File.ownerGroup "/home/w/pub" (User "w") (Group "pubs")
    780        & File.mode "/home/w/pub" 0O0770
    781        --- gpub dir config
    782        & File.dirExists "/home/w/gpub"
    783        & File.ownerGroup "/home/w/gpub" (User "w") (Group "pubs")
    784        & File.mode "/home/w/gpub" 0O0770
    785        -- g config
    786        & User.accountFor (User "g")
    787        & User.hasGroup (User "g") (Group "pubs")
    788        & Ssh.authorizedKey (User "g") sCanonicalSshPubKey
    789        & Ssh.authorizedKey (User "g") sAraSshPubKey
    790        -- c dir config
    791        & File.dirExists "/home/g/c"
    792        & File.ownerGroup "/home/g/c" (User "g") (Group "pubs")
    793        & File.mode "/home/g/c" 0O0770
    794        -- s config
    795        & User.accountFor (User "s")
    796        & User.hasPassword (User "s")
    797        & Sudo.enabledFor (User "s")
    798        & Ssh.authorizedKey (User "s") sCanonicalSshPubKey
    799        & Ssh.userKeyAt (Just "/home/s/.ssh/id_rsa")
    800         (User "s") hostContext (SshRsa, sLyraSshPubKey)
    801 
    802 
    803 --- nginx
    804 ricketyspaceNetNginx :: [String]
    805 ricketyspaceNetNginx = [
    806   "# Adapted from https://gist.github.com/konklone/6532544"
    807   , ""
    808   , "server {"
    809   , "    listen 80;"
    810   , "    listen [::]:80;"
    811   , "    listen 443 ssl http2;"
    812   , "    listen [::]:443 ssl http2;"
    813   , "    server_name lyra.ricketyspace.net;"
    814   , "    return 301 https://ricketyspace.net;"
    815   , ""
    816   , "    ssl_certificate /etc/ssl/certs/lyra.rs.net.chained.le.pem;"
    817   , "    ssl_certificate_key /etc/ssl/private/lyra.rs.net.d.le.key;"
    818   , "}"
    819   , ""
    820   , "server {"
    821   , "    listen 80;"
    822   , "    listen [::]:80;"
    823   , "    server_name ricketyspace.net;"
    824   , "    return 301 https://$host$request_uri;"
    825   , "}"
    826   , ""
    827   , "# The 'spdy' at the end of the listen command below turns on SPDY support."
    828   , ""
    829   , "server {"
    830   , "    listen 443 ssl http2;"
    831   , "    listen [::]:443 ssl http2;"
    832   , "    server_name ricketyspace.net;"
    833   , ""
    834   , "    root /home/w/pub;"
    835   , "    error_page 404 /404.html;"
    836   , "    error_page 403 /403.html;"
    837   , "    default_type text/plain;"
    838   , ""
    839   , "    location /git-difme/releases/ {"
    840   , "         autoindex on;"
    841   , "    }"
    842   , ""
    843   , "    location /nsfw/ {"
    844   , "        autoindex on;"
    845   , "    }"
    846   , ""
    847   , "    # Path to certificate and private key."
    848   , "    # The .crt may omit the root CA cert, if it's a standard CA that ships with clients."
    849   , "    ssl_certificate /etc/ssl/certs/rs.net.chained.le.pem;"
    850   , "    ssl_certificate_key /etc/ssl/private/rs.net.d.le.key;"
    851   , ""
    852   , "    add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';"
    853   , ""
    854   , "    ssl_prefer_server_ciphers on;"
    855   , "    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';"
    856   , ""
    857   , "    ssl_protocols TLSv1.2 TLSv1.1 TLSv1;"
    858   , ""
    859   , "    # as recommended by http://nginx.org/en/docs/http/configuring_https_servers.html"
    860   , "    ssl_session_cache   shared:SSL:10m;"
    861   , "    ssl_session_timeout 10m;"
    862   , "    keepalive_timeout   70;"
    863   , ""
    864   , "    # nginx 1.5.9+ ONLY"
    865   , "    ssl_buffer_size 1400; "
    866   , ""
    867   , "    #"
    868   , "    # Generated by OpenSSL with the following command:"
    869   , "    #   openssl dhparam -outform pem -out dhparam2048.pem 2048"
    870   , "    ssl_dhparam /etc/ssl/certs/dhparam4096.pem;"
    871   , "}"
    872   ]
    873 
    874 ricketyspaceNetGitNginx :: [String]
    875 ricketyspaceNetGitNginx = [
    876   "# Adapted from https://gist.github.com/konklone/6532544"
    877   , ""
    878   , "server {"
    879   , "    listen 80;"
    880   , "    listen [::]:80;"
    881   , "    server_name git.ricketyspace.net;"
    882   , "    return 301 https://$host$request_uri;"
    883   , "}"
    884   , ""
    885   , "server {"
    886   , "    listen 443 ssl http2;"
    887   , "    listen [::]:443 ssl http2;"
    888   , "    server_name git.ricketyspace.net;"
    889   , ""
    890   , "    access_log  /var/log/nginx/cgit-access.log;"
    891   , "    error_log   /var/log/nginx/cgit-error.log;"
    892   , ""
    893   , ""
    894   , "    root /home/g/git.rs/html;"
    895   , "    error_page 404 /404.html;"
    896   , "    error_page 403 /403.html;"
    897   , "    default_type text/plain;"
    898   , ""
    899   , "    location ~ /((style|logo|favicon)\\.(css|png|ico))$ {"
    900   , "         alias /home/g/git.rs/$1;"
    901   , "    }"
    902   , ""
    903   , "    rewrite ^(.*)(tree|plain)/(.*)$ $1file/$3.html permanent;"
    904   , ""
    905   , "    # Path to certificate and private key."
    906   , "    # The .crt may omit the root CA cert, if it's a standard CA that ships with clients."
    907   , "    ssl_certificate /etc/ssl/certs/git.rs.net.chained.le.pem;"
    908   , "    ssl_certificate_key /etc/ssl/private/git.rs.net.d.le.key;"
    909   , ""
    910   , "    add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';"
    911   , ""
    912   , "    ssl_prefer_server_ciphers on;"
    913   , "    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';"
    914   , ""
    915   , "    ssl_protocols TLSv1.2 TLSv1.1 TLSv1;"
    916   , ""
    917   , "    # as recommended by http://nginx.org/en/docs/http/configuring_https_servers.html"
    918   , "    ssl_session_cache   shared:SSL:10m;"
    919   , "    ssl_session_timeout 10m;"
    920   , "    keepalive_timeout   70;"
    921   , ""
    922   , "    # nginx 1.5.9+ ONLY"
    923   , "    ssl_buffer_size 1400;"
    924   , ""
    925   , "    #"
    926   , "    # Generated by OpenSSL with the following command:"
    927   , "    #   openssl dhparam -outform pem -out dhparam2048.pem 2048"
    928   , "    ssl_dhparam /etc/ssl/certs/dhparam4096.pem;"
    929   , "}"
    930   ]
    931 
    932 --- certs
    933 ricketyspaceNetDhparamPem :: Property (HasInfo + UnixLike)
    934 ricketyspaceNetDhparamPem = File.hasPrivContent
    935   "/etc/ssl/certs/dhparam4096.pem" (Context "ricketyspace.net")
    936 
    937 ricketyspaceNetCert :: Property (HasInfo + UnixLike)
    938 ricketyspaceNetCert = File.hasPrivContent
    939    cert (Context "ricketyspace.net")
    940    `onChange` File.mode cert 0O0644
    941   where cert = "/etc/ssl/certs/rs.net.chained.le.pem"
    942 
    943 ricketyspaceNetCertKey :: Property (HasInfo + UnixLike)
    944 ricketyspaceNetCertKey = File.hasPrivContent
    945   "/etc/ssl/private/rs.net.d.le.key" (Context "ricketyspace.net")
    946 
    947 ricketyspaceNetGitCert :: Property (HasInfo + UnixLike)
    948 ricketyspaceNetGitCert = File.hasPrivContent
    949   "/etc/ssl/certs/git.rs.net.chained.le.pem" (Context "git.ricketyspace.net")
    950 
    951 ricketyspaceNetGitCertKey :: Property (HasInfo + UnixLike)
    952 ricketyspaceNetGitCertKey = File.hasPrivContent
    953   "/etc/ssl/private/git.rs.net.d.le.key" (Context "git.ricketyspace.net")
    954 
    955 ricketyspaceNetLyraCert :: Property (HasInfo + UnixLike)
    956 ricketyspaceNetLyraCert = File.hasPrivContent
    957    cert (Context "lyra.ricketyspace.net")
    958    `onChange` File.mode cert 0O0644
    959   where cert = "/etc/ssl/certs/lyra.rs.net.chained.le.pem"
    960 
    961 ricketyspaceNetLyraCertKey :: Property (HasInfo + UnixLike)
    962 ricketyspaceNetLyraCertKey = File.hasPrivContent
    963   "/etc/ssl/private/lyra.rs.net.d.le.key" (Context "lyra.ricketyspace.net")
    964 
    965 --- dkim
    966 ricketyspaceNetOpenDkimConf :: [File.Line]
    967 ricketyspaceNetOpenDkimConf = [
    968   "KeyTable    /etc/opendkim/KeyTable"
    969   , "SigningTable    /etc/opendkim/SigningTable"
    970   , "ExternalIgnoreList    /etc/opendkim/TrustedHosts"
    971   , "InternalHosts    /etc/opendkim/TrustedHosts"
    972   , "LogWhy    yes"
    973   , "Socket    inet:12345@localhost"
    974   ]
    975 
    976 ricketyspaceNetOpenDkimKeyTable :: [File.Line]
    977 ricketyspaceNetOpenDkimKeyTable = [
    978   "mail._domainkey.ricketyspace.net ricketyspace.net:mail:/etc/dkimkeys/ricketyspace.net/mail.private"
    979   ]
    980 
    981 ricketyspaceNetOpenDkimSigningTable :: [File.Line]
    982 ricketyspaceNetOpenDkimSigningTable = [
    983   "ricketyspace.net mail._domainkey.ricketyspace.net"
    984   ]
    985 
    986 ricketyspaceNetOpenDkimTrustedHosts :: [File.Line]
    987 ricketyspaceNetOpenDkimTrustedHosts = [
    988   "127.0.0.1"
    989   , "localhost"
    990   , "ricketyspace.net"
    991   ]
    992 
    993 -- cgit
    994 rsCgitRC :: Property UnixLike
    995 rsCgitRC = File.hasContent "/etc/cgitrc" ["#"
    996   , "# cgit config"
    997   , "# see cgitrc(5) for details"
    998   , "virtual-root=/"
    999   , ""
   1000   , "# Specify some default clone urls using macro expansion"
   1001   , "clone-url=git://git.ricketyspace.net/$CGIT_REPO_URL.git"
   1002   , ""
   1003   , "# Root title"
   1004   , "root-title=git.ricketyspace.net"
   1005   , ""
   1006   , "# Root description"
   1007   , "root-desc=git space"
   1008   , ""
   1009   , "# CSS"
   1010   , "css=/cgit.css"
   1011   , ""
   1012   , "# Logo at the top of the page"
   1013   , "logo=/cgit.png"
   1014   , ""
   1015   , "# Favicon"
   1016   , "favicon=/favicon.ico"
   1017   , ""
   1018   , "# Disable owner on index page"
   1019   , "enable-index-owner=0"
   1020   , ""
   1021   , "# Source gitweb.description, gitweb.owner from each project config"
   1022   , "enable-git-config=1"
   1023   , ""
   1024   , "# Remove .git suffix from project display"
   1025   , "remove-suffix=1"
   1026   , ""
   1027   , "# Enable ASCII art commit history graph on the log pages"
   1028   , "enable-commit-graph=1"
   1029   , ""
   1030   , "# Show number of affected files per commit on the log pages"
   1031   , "enable-log-filecount=1"
   1032   , ""
   1033   , "# Show number of added/removed lines per commit on the log pages"
   1034   , "enable-log-linecount=1"
   1035   , ""
   1036   , "# Sort branches by date"
   1037   , "branch-sort=age"
   1038   , ""
   1039   , "# Enable statistics per week, month and quarter"
   1040   , "max-stats=quarter"
   1041   , ""
   1042   , "# Allow download of tar.gz, tar.bz2 and zip-files"
   1043   , "snapshots=tar.bz2 tar.xz zip"
   1044   , ""
   1045   , "##"
   1046   , "## List of common mimetypes"
   1047   , "##"
   1048   , ""
   1049   , "mimetype.gif=image/gif"
   1050   , "mimetype.html=text/html"
   1051   , "mimetype.jpg=image/jpeg"
   1052   , "mimetype.jpeg=image/jpeg"
   1053   , "mimetype.pdf=application/pdf"
   1054   , "mimetype.png=image/png"
   1055   , "mimetype.svg=image/svg+xml"
   1056   , ""
   1057   , "# Highlight source code with python pygments-based highligher"
   1058   , "source-filter=/usr/lib/cgit/filters/syntax-highlighting.py"
   1059   , ""
   1060   , "# Format markdown, restructuredtext, manpages, text files, and html files"
   1061   , "# through the right converters"
   1062   , "about-filter=/usr/lib/cgit/filters/about-formatting.sh"
   1063   , ""
   1064   , ""
   1065   , "##"
   1066   , "## Search for these files in the root of the default branch of repositories"
   1067   , "## for coming up with the about page:"
   1068   , "##"
   1069   , "readme=:README.rst"
   1070   , "readme=:readme.rst"
   1071   , "readme=:README.md"
   1072   , "readme=:readme.md"
   1073   , "readme=:README.mkd"
   1074   , "readme=:readme.mkd"
   1075   , "readme=:README.html"
   1076   , "readme=:readme.html"
   1077   , "readme=:README.htm"
   1078   , "readme=:readme.htm"
   1079   , "readme=:README.txt"
   1080   , "readme=:readme.txt"
   1081   , "readme=:README"
   1082   , "readme=:readme"
   1083   , "readme=:INSTALL.md"
   1084   , "readme=:install.md"
   1085   , "readme=:INSTALL.mkd"
   1086   , "readme=:install.mkd"
   1087   , "readme=:INSTALL.rst"
   1088   , "readme=:install.rst"
   1089   , "readme=:INSTALL.html"
   1090   , "readme=:install.html"
   1091   , "readme=:INSTALL.htm"
   1092   , "readme=:install.htm"
   1093   , "readme=:INSTALL.txt"
   1094   , "readme=:install.txt"
   1095   , "readme=:INSTALL"
   1096   , "readme=:install"
   1097   , "readme=:README.org"
   1098   , ""
   1099   , "# prevent private repos from being listed"
   1100   , "strict-export=git-daemon-export-ok"
   1101   , ""
   1102   , "scan-path=/home/g/c"
   1103   ]
   1104 
   1105 -- git-daemon
   1106 rsGitDaemonDefaults :: Property UnixLike
   1107 rsGitDaemonDefaults  = File.hasContent "/etc/default/git-daemon" [
   1108   "# Defaults for git-daemon initscript"
   1109   , "# sourced by /etc/init.d/git-daemon"
   1110   , ""
   1111   , "#"
   1112   , "# This is a POSIX shell fragment"
   1113   , "#"
   1114   , ""
   1115   , "GIT_DAEMON_ENABLE=true"
   1116   , "GIT_DAEMON_USER=gitdaemon"
   1117   , "GIT_DAEMON_BASE_PATH=/home/g/c"
   1118   , "GIT_DAEMON_DIRECTORY=/home/g/c"
   1119   , ""
   1120   , "# Additional options that are passed to the Daemon."
   1121   , "GIT_DAEMON_OPTIONS=\"\""
   1122   ]
   1123 
   1124 --- postfix
   1125 rsPostfixHeaderChecks :: Property DebianLike
   1126 rsPostfixHeaderChecks = Postfix.mappedFile "/etc/postfix/header_checks"
   1127   (flip File.hasContent
   1128     [
   1129       "/^Received:.*with ESMTPSA/ IGNORE"
   1130     , "/^X-Originating-IP:/ IGNORE"
   1131     ]) `describe` "postfix header checks configured"
   1132   `onChange` Postfix.reloaded
   1133 
   1134 rsPostfixRBLOverride :: Property (HasInfo + UnixLike)
   1135 rsPostfixRBLOverride = File.hasPrivContent
   1136   rblOFile (Context "ricketyspace.net")
   1137   `onChange` na
   1138   where
   1139     na :: Property UnixLike
   1140     na = scriptProperty ["postmap " ++ rblOFile]
   1141          `assume` MadeChange
   1142     rblOFile = "/etc/postfix/rbl_override"
   1143 
   1144 rsPostfixMainCf :: [File.Line]
   1145 rsPostfixMainCf = [
   1146   "smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)"
   1147   , "biff = no"
   1148   , ""
   1149   , "append_dot_mydomain = no"
   1150   , "compatibility_level = 2"
   1151   , ""
   1152   , "readme_directory = /usr/share/doc/postfix"
   1153   , "html_directory = /usr/share/doc/postfix/html"
   1154   , ""
   1155   , "mime_header_checks = regexp:/etc/postfix/header_checks"
   1156   , "header_checks = regexp:/etc/postfix/header_checks"
   1157   , ""
   1158   , "smtpd_tls_cert_file=/etc/ssl/certs/lyra.rs.net.chained.le.pem"
   1159   , "smtpd_tls_key_file=/etc/ssl/private/lyra.rs.net.d.le.key"
   1160   , "smtpd_use_tls=yes"
   1161   , "smtpd_tls_auth_only=yes"
   1162   , "smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache"
   1163   , "smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache"
   1164   , ""
   1165   , "smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination"
   1166   , "myhostname = lyra.ricketyspace.net"
   1167   , "alias_maps = hash:/etc/aliases"
   1168   , "alias_database = hash:/etc/aliases"
   1169   , "myorigin = ricketyspace.net"
   1170   , "mydestination = ricketyspace.net, localhost"
   1171   , "relayhost ="
   1172   , "mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128"
   1173   , "mailbox_command ="
   1174   , "mailbox_size_limit = 0"
   1175   , "recipient_delimiter = +"
   1176   , "inet_interfaces = all"
   1177   , "inet_protocols = all"
   1178   , "relay_domains = ricketyspace.net"
   1179   , ""
   1180   , "smtpd_recipient_restrictions = permit_sasl_authenticated,"
   1181   , "      reject_invalid_hostname,"
   1182   , "      reject_unknown_recipient_domain,"
   1183   , "      reject_unauth_destination,"
   1184   , "      reject_rbl_client sbl.spamhaus.org,"
   1185   , "      check_policy_service unix:private/policy-spf"
   1186   , "      permit"
   1187   , ""
   1188   , "smtpd_helo_restrictions = check_helo_access hash:/etc/postfix/rbl_override,"
   1189   , "        reject_invalid_helo_hostname,"
   1190   , "        reject_non_fqdn_helo_hostname"
   1191   , ""
   1192   , "smtpd_client_restrictions = reject_rbl_client dnsbl.sorbs.net"
   1193   , ""
   1194   , "home_mailbox = Maildir/"
   1195   , ""
   1196   , "# dovecot"
   1197   , "smtpd_sasl_type = dovecot"
   1198   , "smtpd_sasl_path = private/auth"
   1199   , "smtpd_sasl_auth_enable = yes"
   1200   , ""
   1201   , "# dkim"
   1202   , "milter_default_action = accept"
   1203   , "milter_protocol = 6"
   1204   , "smtpd_milters = inet:localhost:12345"
   1205   , "non_smtpd_milters = inet:localhost:12345"
   1206   , ""
   1207   , "# spf"
   1208   , "policy-spf_time_limit = 3600s"
   1209   , ""
   1210   , "# map mail username/password to user's unix user/passwd."
   1211   , "local_recipient_maps = proxy:unix:passwd.byname $alias_maps"
   1212   ]
   1213 
   1214 rsPostfixMasterCf :: [File.Line]
   1215 rsPostfixMasterCf = [
   1216   "# Do not forget to execute `postfix reload` after editing this file."
   1217   , "#"
   1218   , "# =========================================================================="
   1219   , "# service type  private unpriv  chroot  wakeup  maxproc command + args"
   1220   , "#               (yes)   (yes)   (no)    (never) (100)"
   1221   , "# =========================================================================="
   1222   , "smtp      inet  n       -       y       -       -       smtpd"
   1223   , "submission inet n       -       y       -       -       smtpd"
   1224   , "  -o syslog_name=postfix/submission"
   1225   , "  -o smtpd_tls_security_level=encrypt"
   1226   , "  -o smtpd_sasl_auth_enable=yes"
   1227   , "  -o smtpd_client_restrictions=permit_sasl_authenticated,reject"
   1228   , "  -o milter_macro_daemon_name=ORIGINATING"
   1229   , "smtps     inet  n       -       y       -       -       smtpd -v"
   1230   , "  -o syslog_name=postfix/smtps"
   1231   , "  -o smtpd_tls_wrappermode=yes"
   1232   , "  -o smtpd_sasl_auth_enable=yes"
   1233   , "  -o smtpd_client_restrictions=permit_sasl_authenticated,reject"
   1234   , "  -o milter_macro_daemon_name=ORIGINATING"
   1235   , "pickup    unix  n       -       y       60      1       pickup"
   1236   , "cleanup   unix  n       -       y       -       0       cleanup"
   1237   , "qmgr      unix  n       -       n       300     1       qmgr"
   1238   , "tlsmgr    unix  -       -       y       1000?   1       tlsmgr"
   1239   , "rewrite   unix  -       -       y       -       -       trivial-rewrite"
   1240   , "bounce    unix  -       -       y       -       0       bounce"
   1241   , "defer     unix  -       -       y       -       0       bounce"
   1242   , "trace     unix  -       -       y       -       0       bounce"
   1243   , "verify    unix  -       -       y       -       1       verify"
   1244   , "flush     unix  n       -       y       1000?   0       flush"
   1245   , "proxymap  unix  -       -       n       -       -       proxymap"
   1246   , "proxywrite unix -       -       n       -       1       proxymap"
   1247   , "smtp      unix  -       -       y       -       -       smtp"
   1248   , "relay     unix  -       -       y       -       -       smtp"
   1249   , "showq     unix  n       -       y       -       -       showq"
   1250   , "error     unix  -       -       y       -       -       error"
   1251   , "retry     unix  -       -       y       -       -       error"
   1252   , "discard   unix  -       -       y       -       -       discard"
   1253   , "local     unix  -       n       n       -       -       local"
   1254   , "virtual   unix  -       n       n       -       -       virtual"
   1255   , "lmtp      unix  -       -       y       -       -       lmtp"
   1256   , "anvil     unix  -       -       y       -       1       anvil"
   1257   , "scache    unix  -       -       y       -       1       scache"
   1258   , "#"
   1259   , "# ===================================================================="
   1260   , "# Interfaces to non-Postfix software."
   1261   , "#"
   1262   , "maildrop  unix  -       n       n       -       -       pipe"
   1263   , "  flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}"
   1264   , "#"
   1265   , "# See the Postfix UUCP_README file for configuration details."
   1266   , "#"
   1267   , "uucp      unix  -       n       n       -       -       pipe"
   1268   , "  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)"
   1269   , "#"
   1270   , "# Other external delivery methods."
   1271   , "#"
   1272   , "ifmail    unix  -       n       n       -       -       pipe"
   1273   , "  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)"
   1274   , "bsmtp     unix  -       n       n       -       -       pipe"
   1275   , "  flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient"
   1276   , "scalemail-backend unix  -       n       n       -       2       pipe"
   1277   , "  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}"
   1278   , "mailman   unix  -       n       n       -       -       pipe"
   1279   , "  flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py"
   1280   , "  ${nexthop} ${user}"
   1281   , ""
   1282   , "# For Dovecot."
   1283   , "dovecot   unix  -       n       n       -       -       pipe"
   1284   , "  flags=DRhu user=email:email argv=/usr/lib/dovecot/deliver -f ${sender} -d ${recipient}"
   1285   , ""
   1286   , "# SPF snafu (from https://help.ubuntu.com/community/Postfix/SPF)"
   1287   , "policy-spf  unix  -       n       n       -       -       spawn"
   1288   , "     user=nobody argv=/usr/bin/policyd-spf"
   1289   ]
   1290 
   1291 rsDovecotConf :: [File.Line]
   1292 rsDovecotConf = [
   1293   "mail_privileged_group = mail"
   1294   , "namespace inbox {"
   1295   , "  inbox = yes"
   1296   , "  location ="
   1297   , "  mailbox Drafts {"
   1298   , "    special_use = \\Drafts"
   1299   , "  }"
   1300   , "  mailbox Junk {"
   1301   , "    special_use = \\Junk"
   1302   , "  }"
   1303   , "  mailbox Sent {"
   1304   , "    special_use = \\Sent"
   1305   , "  }"
   1306   , "  mailbox \"Sent Messages\" {"
   1307   , "    special_use = \\Sent"
   1308   , "  }"
   1309   , "  mailbox Trash {"
   1310   , "    special_use = \\Trash"
   1311   , "  }"
   1312   , "  prefix ="
   1313   , "}"
   1314   , "passdb {"
   1315   , "  driver = pam"
   1316   , "}"
   1317   , "protocols = \" imap\""
   1318   , "service auth {"
   1319   , "  unix_listener /var/spool/postfix/private/auth {"
   1320   , "    group = postfix"
   1321   , "    mode = 0666"
   1322   , "    user = postfix"
   1323   , "  }"
   1324   , "}"
   1325   , "ssl = required"
   1326   , "ssl_cert = </etc/ssl/certs/lyra.rs.net.chained.le.pem"
   1327   , "ssl_key = </etc/ssl/private/lyra.rs.net.d.le.key"
   1328   , "userdb {"
   1329   , "  driver = passwd"
   1330   , "}"
   1331   ]
   1332 
   1333 rsMailAliases :: Property (HasInfo + UnixLike)
   1334 rsMailAliases = File.hasPrivContent
   1335   "/etc/aliases" (Context "ricketyspace.net")
   1336   `onChange` na
   1337   where
   1338     na :: Property UnixLike
   1339     na = scriptProperty ["cd /etc", "newaliases"]
   1340          `assume` MadeChange
   1341 
   1342 -- crons
   1343 stagitGenCron :: Property DebianLike
   1344 stagitGenCron = Cron.job "stagit-gen"
   1345   (Cron.Times "* */1 * * *")
   1346   (User "g") "/home/g/git.rs" cmd
   1347   where
   1348     cmd = "make"
   1349 
   1350 -- common
   1351 
   1352 --- tmux conf
   1353 tmuxConf :: [File.Line]
   1354 tmuxConf = [
   1355   "set -g prefix C-a"
   1356   , "unbind C-b"
   1357   , "bind C-a send-prefix"
   1358   , "set -g status-style bg=white"
   1359   , "set -g status-right ''"
   1360   ]
   1361 
   1362 --- Docker CE
   1363 dockerCEInstalled :: Property DebianLike
   1364 dockerCEInstalled = Apt.installed ["docker-ce"]
   1365   `requires` Apt.setSourcesListD [
   1366   "deb [arch=amd64] https://download.docker.com/linux/debian buster stable"
   1367   ] "docker-ce"
   1368   `requires` Apt.trustsKey dockerCEKey
   1369 
   1370 dockerCEKey :: Apt.AptKey
   1371 dockerCEKey = Apt.AptKey "docker-ce" $ unlines [
   1372   "-----BEGIN PGP PUBLIC KEY BLOCK-----"
   1373   , ""
   1374   , "mQINBFit2ioBEADhWpZ8/wvZ6hUTiXOwQHXMAlaFHcPH9hAtr4F1y2+OYdbtMuth"
   1375   , "lqqwp028AqyY+PRfVMtSYMbjuQuu5byyKR01BbqYhuS3jtqQmljZ/bJvXqnmiVXh"
   1376   , "38UuLa+z077PxyxQhu5BbqntTPQMfiyqEiU+BKbq2WmANUKQf+1AmZY/IruOXbnq"
   1377   , "L4C1+gJ8vfmXQt99npCaxEjaNRVYfOS8QcixNzHUYnb6emjlANyEVlZzeqo7XKl7"
   1378   , "UrwV5inawTSzWNvtjEjj4nJL8NsLwscpLPQUhTQ+7BbQXAwAmeHCUTQIvvWXqw0N"
   1379   , "cmhh4HgeQscQHYgOJjjDVfoY5MucvglbIgCqfzAHW9jxmRL4qbMZj+b1XoePEtht"
   1380   , "ku4bIQN1X5P07fNWzlgaRL5Z4POXDDZTlIQ/El58j9kp4bnWRCJW0lya+f8ocodo"
   1381   , "vZZ+Doi+fy4D5ZGrL4XEcIQP/Lv5uFyf+kQtl/94VFYVJOleAv8W92KdgDkhTcTD"
   1382   , "G7c0tIkVEKNUq48b3aQ64NOZQW7fVjfoKwEZdOqPE72Pa45jrZzvUFxSpdiNk2tZ"
   1383   , "XYukHjlxxEgBdC/J3cMMNRE1F4NCA3ApfV1Y7/hTeOnmDuDYwr9/obA8t016Yljj"
   1384   , "q5rdkywPf4JF8mXUW5eCN1vAFHxeg9ZWemhBtQmGxXnw9M+z6hWwc6ahmwARAQAB"
   1385   , "tCtEb2NrZXIgUmVsZWFzZSAoQ0UgZGViKSA8ZG9ja2VyQGRvY2tlci5jb20+iQI3"
   1386   , "BBMBCgAhBQJYrefAAhsvBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEI2BgDwO"
   1387   , "v82IsskP/iQZo68flDQmNvn8X5XTd6RRaUH33kXYXquT6NkHJciS7E2gTJmqvMqd"
   1388   , "tI4mNYHCSEYxI5qrcYV5YqX9P6+Ko+vozo4nseUQLPH/ATQ4qL0Zok+1jkag3Lgk"
   1389   , "jonyUf9bwtWxFp05HC3GMHPhhcUSexCxQLQvnFWXD2sWLKivHp2fT8QbRGeZ+d3m"
   1390   , "6fqcd5Fu7pxsqm0EUDK5NL+nPIgYhN+auTrhgzhK1CShfGccM/wfRlei9Utz6p9P"
   1391   , "XRKIlWnXtT4qNGZNTN0tR+NLG/6Bqd8OYBaFAUcue/w1VW6JQ2VGYZHnZu9S8LMc"
   1392   , "FYBa5Ig9PxwGQOgq6RDKDbV+PqTQT5EFMeR1mrjckk4DQJjbxeMZbiNMG5kGECA8"
   1393   , "g383P3elhn03WGbEEa4MNc3Z4+7c236QI3xWJfNPdUbXRaAwhy/6rTSFbzwKB0Jm"
   1394   , "ebwzQfwjQY6f55MiI/RqDCyuPj3r3jyVRkK86pQKBAJwFHyqj9KaKXMZjfVnowLh"
   1395   , "9svIGfNbGHpucATqREvUHuQbNnqkCx8VVhtYkhDb9fEP2xBu5VvHbR+3nfVhMut5"
   1396   , "G34Ct5RS7Jt6LIfFdtcn8CaSas/l1HbiGeRgc70X/9aYx/V/CEJv0lIe8gP6uDoW"
   1397   , "FPIZ7d6vH+Vro6xuWEGiuMaiznap2KhZmpkgfupyFmplh0s6knymuQINBFit2ioB"
   1398   , "EADneL9S9m4vhU3blaRjVUUyJ7b/qTjcSylvCH5XUE6R2k+ckEZjfAMZPLpO+/tF"
   1399   , "M2JIJMD4SifKuS3xck9KtZGCufGmcwiLQRzeHF7vJUKrLD5RTkNi23ydvWZgPjtx"
   1400   , "Q+DTT1Zcn7BrQFY6FgnRoUVIxwtdw1bMY/89rsFgS5wwuMESd3Q2RYgb7EOFOpnu"
   1401   , "w6da7WakWf4IhnF5nsNYGDVaIHzpiqCl+uTbf1epCjrOlIzkZ3Z3Yk5CM/TiFzPk"
   1402   , "z2lLz89cpD8U+NtCsfagWWfjd2U3jDapgH+7nQnCEWpROtzaKHG6lA3pXdix5zG8"
   1403   , "eRc6/0IbUSWvfjKxLLPfNeCS2pCL3IeEI5nothEEYdQH6szpLog79xB9dVnJyKJb"
   1404   , "VfxXnseoYqVrRz2VVbUI5Blwm6B40E3eGVfUQWiux54DspyVMMk41Mx7QJ3iynIa"
   1405   , "1N4ZAqVMAEruyXTRTxc9XW0tYhDMA/1GYvz0EmFpm8LzTHA6sFVtPm/ZlNCX6P1X"
   1406   , "zJwrv7DSQKD6GGlBQUX+OeEJ8tTkkf8QTJSPUdh8P8YxDFS5EOGAvhhpMBYD42kQ"
   1407   , "pqXjEC+XcycTvGI7impgv9PDY1RCC1zkBjKPa120rNhv/hkVk/YhuGoajoHyy4h7"
   1408   , "ZQopdcMtpN2dgmhEegny9JCSwxfQmQ0zK0g7m6SHiKMwjwARAQABiQQ+BBgBCAAJ"
   1409   , "BQJYrdoqAhsCAikJEI2BgDwOv82IwV0gBBkBCAAGBQJYrdoqAAoJEH6gqcPyc/zY"
   1410   , "1WAP/2wJ+R0gE6qsce3rjaIz58PJmc8goKrir5hnElWhPgbq7cYIsW5qiFyLhkdp"
   1411   , "YcMmhD9mRiPpQn6Ya2w3e3B8zfIVKipbMBnke/ytZ9M7qHmDCcjoiSmwEXN3wKYI"
   1412   , "mD9VHONsl/CG1rU9Isw1jtB5g1YxuBA7M/m36XN6x2u+NtNMDB9P56yc4gfsZVES"
   1413   , "KA9v+yY2/l45L8d/WUkUi0YXomn6hyBGI7JrBLq0CX37GEYP6O9rrKipfz73XfO7"
   1414   , "JIGzOKZlljb/D9RX/g7nRbCn+3EtH7xnk+TK/50euEKw8SMUg147sJTcpQmv6UzZ"
   1415   , "cM4JgL0HbHVCojV4C/plELwMddALOFeYQzTif6sMRPf+3DSj8frbInjChC3yOLy0"
   1416   , "6br92KFom17EIj2CAcoeq7UPhi2oouYBwPxh5ytdehJkoo+sN7RIWua6P2WSmon5"
   1417   , "U888cSylXC0+ADFdgLX9K2zrDVYUG1vo8CX0vzxFBaHwN6Px26fhIT1/hYUHQR1z"
   1418   , "VfNDcyQmXqkOnZvvoMfz/Q0s9BhFJ/zU6AgQbIZE/hm1spsfgvtsD1frZfygXJ9f"
   1419   , "irP+MSAI80xHSf91qSRZOj4Pl3ZJNbq4yYxv0b1pkMqeGdjdCYhLU+LZ4wbQmpCk"
   1420   , "SVe2prlLureigXtmZfkqevRz7FrIZiu9ky8wnCAPwC7/zmS18rgP/17bOtL4/iIz"
   1421   , "QhxAAoAMWVrGyJivSkjhSGx1uCojsWfsTAm11P7jsruIL61ZzMUVE2aM3Pmj5G+W"
   1422   , "9AcZ58Em+1WsVnAXdUR//bMmhyr8wL/G1YO1V3JEJTRdxsSxdYa4deGBBY/Adpsw"
   1423   , "24jxhOJR+lsJpqIUeb999+R8euDhRHG9eFO7DRu6weatUJ6suupoDTRWtr/4yGqe"
   1424   , "dKxV3qQhNLSnaAzqW/1nA3iUB4k7kCaKZxhdhDbClf9P37qaRW467BLCVO/coL3y"
   1425   , "Vm50dwdrNtKpMBh3ZpbB1uJvgi9mXtyBOMJ3v8RZeDzFiG8HdCtg9RvIt/AIFoHR"
   1426   , "H3S+U79NT6i0KPzLImDfs8T7RlpyuMc4Ufs8ggyg9v3Ae6cN3eQyxcK3w0cbBwsh"
   1427   , "/nQNfsA6uu+9H7NhbehBMhYnpNZyrHzCmzyXkauwRAqoCbGCNykTRwsur9gS41TQ"
   1428   , "M8ssD1jFheOJf3hODnkKU+HKjvMROl1DK7zdmLdNzA1cvtZH/nCC9KPj1z8QC47S"
   1429   , "xx+dTZSx4ONAhwbS/LN3PoKtn8LPjY9NP9uDWI+TWYquS2U+KHDrBDlsgozDbs/O"
   1430   , "jCxcpDzNmXpWQHEtHU7649OXHP7UeNST1mCUCH5qdank0V1iejF6/CfTFU4MfcrG"
   1431   , "YT90qFF93M3v01BbxP+EIY2/9tiIPbrd"
   1432   , "=0YYh"
   1433   , "-----END PGP PUBLIC KEY BLOCK-----"
   1434   ]
   1435 
   1436 
   1437 m31 :: [Host]  -- Andromeda. Computers outside s' galaxy.
   1438 m31 = [
   1439   host "ns6.gandi.net" $ props
   1440     & ipv4  "217.70.177.40"
   1441       ]