Merge branch 'joeyconfig'
This commit is contained in:
commit
81f370b9da
155
config-joey.hs
155
config-joey.hs
|
@ -8,6 +8,7 @@ import qualified Propellor.Property.File as File
|
|||
import qualified Propellor.Property.Apt as Apt
|
||||
import qualified Propellor.Property.Network as Network
|
||||
import qualified Propellor.Property.Ssh as Ssh
|
||||
import qualified Propellor.Property.Gpg as Gpg
|
||||
import qualified Propellor.Property.Cron as Cron
|
||||
import qualified Propellor.Property.Sudo as Sudo
|
||||
import qualified Propellor.Property.User as User
|
||||
|
@ -22,6 +23,7 @@ import qualified Propellor.Property.Apache as Apache
|
|||
import qualified Propellor.Property.Postfix as Postfix
|
||||
import qualified Propellor.Property.Service as Service
|
||||
import qualified Propellor.Property.Grub as Grub
|
||||
import qualified Propellor.Property.Obnam as Obnam
|
||||
import qualified Propellor.Property.HostingProvider.DigitalOcean as DigitalOcean
|
||||
import qualified Propellor.Property.HostingProvider.CloudAtCost as CloudAtCost
|
||||
import qualified Propellor.Property.HostingProvider.Linode as Linode
|
||||
|
@ -41,7 +43,7 @@ hosts = -- (o) `
|
|||
|
||||
& Apt.buildDep ["git-annex"] `period` Daily
|
||||
& Docker.configured
|
||||
& Docker.docked hosts "android-git-annex"
|
||||
! Docker.docked hosts "android-git-annex"
|
||||
|
||||
, standardSystem "clam.kitenet.net" Unstable "amd64"
|
||||
[ "Unreliable server. Anything here may be lost at any time!" ]
|
||||
|
@ -61,38 +63,70 @@ hosts = -- (o) `
|
|||
[ "Main git-annex build box." ]
|
||||
& ipv4 "138.38.108.179"
|
||||
|
||||
& Hostname.sane
|
||||
& Apt.unattendedUpgrades
|
||||
& Postfix.satellite
|
||||
& Docker.configured
|
||||
& Docker.docked hosts "amd64-git-annex-builder"
|
||||
& Docker.docked hosts "i386-git-annex-builder"
|
||||
& Docker.docked hosts "armel-git-annex-builder-companion"
|
||||
& Docker.docked hosts "armel-git-annex-builder"
|
||||
& Docker.docked hosts "android-git-annex-builder"
|
||||
-- not currently working
|
||||
! Docker.docked hosts "armel-git-annex-builder-companion"
|
||||
! Docker.docked hosts "armel-git-annex-builder"
|
||||
& Docker.garbageCollected `period` Daily
|
||||
& Apt.buildDep ["git-annex"] `period` Daily
|
||||
|
||||
, standardSystem "kite.kitenet.net" Unstable "amd64"
|
||||
-- This is not a complete description of kite, since it's a
|
||||
-- multiuser system with eg, user passwords that are not deployed
|
||||
-- with propellor.
|
||||
, standardSystemUnhardened "kite.kitenet.net" Unstable "amd64"
|
||||
[ "Welcome to the new kitenet.net server!"
|
||||
, "This is still under construction and not yet live.."
|
||||
]
|
||||
& ipv4 "66.228.36.95"
|
||||
& ipv6 "2600:3c03::f03c:91ff:fe73:b0d2"
|
||||
-- & alias "kitenet.net" -- not yet live!
|
||||
|
||||
& Apt.installed ["linux-image-amd64"]
|
||||
& Linode.chainPVGrub 5
|
||||
& Hostname.sane
|
||||
& Apt.unattendedUpgrades
|
||||
& Apt.installed ["systemd"]
|
||||
& Ssh.hostKeys (Context "kitenet.net")
|
||||
|
||||
-- Since ssh password authentication is allowed:
|
||||
& Apt.serviceInstalledRunning "fail2ban"
|
||||
& Obnam.backup "/" "33 1 * * *"
|
||||
[ "--repository=sftp://joey@eubackup.kitenet.net/~/lib/backup/kite.obnam"
|
||||
, "--client-name=kitenet.net"
|
||||
, "--encrypt-with="
|
||||
, "--exclude=/var/cache"
|
||||
, "--exclude=/var/tmp"
|
||||
, "--exclude=/home/joey/lib"
|
||||
, "--exclude=.*/tmp/"
|
||||
, "--one-file-system"
|
||||
] Obnam.OnlyClient
|
||||
`requires` Gpg.keyImported "98147487" "root"
|
||||
`requires` Ssh.keyImported SshRsa "root"
|
||||
(Context "kite.kitenet.net")
|
||||
`requires` Ssh.knownHost hosts "eubackup.kitenet.net" "root"
|
||||
|
||||
-- & alias "smtp.kitenet.net" -- not yet live!
|
||||
-- & alias "imap.kitenet.net" -- not yet live!
|
||||
-- & alias "mail.kitenet.net" -- not yet live!
|
||||
& JoeySites.kiteMailServer
|
||||
|
||||
& JoeySites.legacyWebSites
|
||||
|
||||
& Apt.installed
|
||||
["git-annex", "myrepos"
|
||||
, "build-essential", "make"
|
||||
-- Some users have zsh as their login shell.
|
||||
, "zsh"
|
||||
]
|
||||
|
||||
, standardSystem "diatom.kitenet.net" Stable "amd64"
|
||||
[ "Important stuff that needs not too much memory or CPU." ]
|
||||
& ipv4 "107.170.31.195"
|
||||
|
||||
& DigitalOcean.distroKernel
|
||||
& Hostname.sane
|
||||
& Ssh.hostKeys (Context "diatom.kitenet.net")
|
||||
& Apt.unattendedUpgrades
|
||||
& Apt.serviceInstalledRunning "ntp"
|
||||
|
@ -103,10 +137,7 @@ hosts = -- (o) `
|
|||
& Apt.serviceInstalledRunning "swapspace"
|
||||
|
||||
& Apt.serviceInstalledRunning "apache2"
|
||||
& File.hasPrivContent "/etc/ssl/certs/web.pem" (Context "kitenet.net")
|
||||
& File.hasPrivContent "/etc/ssl/private/web.pem" (Context "kitenet.net")
|
||||
& File.hasPrivContent "/etc/ssl/certs/startssl.pem" (Context "kitenet.net")
|
||||
& Apache.modEnabled "ssl"
|
||||
& JoeySites.kitenetHttps
|
||||
& Apache.multiSSL
|
||||
& File.ownerGroup "/srv/web" "joey" "joey"
|
||||
& Apt.installed ["analog"]
|
||||
|
@ -116,14 +147,16 @@ hosts = -- (o) `
|
|||
& JoeySites.gitServer hosts
|
||||
|
||||
& alias "downloads.kitenet.net"
|
||||
& JoeySites.annexWebSite hosts "/srv/git/downloads.git"
|
||||
& JoeySites.annexWebSite "/srv/git/downloads.git"
|
||||
"downloads.kitenet.net"
|
||||
"840760dc-08f0-11e2-8c61-576b7e66acfd"
|
||||
[("turtle", "ssh://turtle.kitenet.net/~/lib/downloads/")]
|
||||
[("usbackup", "ssh://usbackup.kitenet.net/~/lib/downloads/")]
|
||||
`requires` Ssh.keyImported SshRsa "joey" (Context "downloads.kitenet.net")
|
||||
`requires` Ssh.knownHost hosts "usbackup.kitenet.net" "joey"
|
||||
& JoeySites.gitAnnexDistributor
|
||||
|
||||
& alias "tmp.kitenet.net"
|
||||
& JoeySites.annexWebSite hosts "/srv/git/joey/tmp.git"
|
||||
& JoeySites.annexWebSite "/srv/git/joey/tmp.git"
|
||||
"tmp.kitenet.net"
|
||||
"26fd6e38-1226-11e2-a75f-ff007033bdba"
|
||||
[]
|
||||
|
@ -148,25 +181,28 @@ hosts = -- (o) `
|
|||
|
||||
, let ctx = Context "elephant.kitenet.net"
|
||||
in standardSystem "elephant.kitenet.net" Unstable "amd64"
|
||||
[ "Storage, big data, and backups, omnomnom!" ]
|
||||
[ "Storage, big data, and backups, omnomnom!"
|
||||
, "(Encrypt all data stored here.)"
|
||||
]
|
||||
& ipv4 "193.234.225.114"
|
||||
|
||||
& Grub.chainPVGrub "hd0,0" "xen/xvda1" 30
|
||||
& Hostname.sane
|
||||
& Postfix.satellite
|
||||
& Apt.unattendedUpgrades
|
||||
& Ssh.hostKeys ctx
|
||||
& sshPubKey "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAJkoPRhUGT8EId6m37uBdYEtq42VNwslKnc9mmO+89ody066q6seHKeFY6ImfwjcyIjM30RTzEwftuVNQnbEB0="
|
||||
& Ssh.keyImported SshRsa "joey" ctx
|
||||
& Apt.serviceInstalledRunning "swapspace"
|
||||
|
||||
& alias "eubackup.kitenet.net"
|
||||
& Apt.installed ["obnam", "sshfs", "rsync"]
|
||||
& JoeySites.obnamRepos ["wren", "pell", "kite"]
|
||||
& JoeySites.githubBackup
|
||||
& JoeySites.obnamRepos ["wren", "pell"]
|
||||
& Ssh.knownHost hosts "usw-s002.rsync.net" "joey"
|
||||
& JoeySites.rsyncNetBackup hosts
|
||||
& JoeySites.backupsBackedupTo hosts "usbackup.kitenet.net" "lib/backup/eubackup"
|
||||
|
||||
& alias "podcatcher.kitenet.net"
|
||||
& Apt.installed ["git-annex"]
|
||||
& JoeySites.podcatcher
|
||||
|
||||
& alias "znc.kitenet.net"
|
||||
& JoeySites.ircBouncer
|
||||
|
@ -201,9 +237,9 @@ hosts = -- (o) `
|
|||
`onChange` Service.restarted "ssh"
|
||||
|
||||
-- temp
|
||||
& Docker.docked hosts "amd64-git-annex-builder"
|
||||
& Docker.docked hosts "i386-git-annex-builder"
|
||||
& Docker.docked hosts "android-git-annex-builder"
|
||||
! Docker.docked hosts "amd64-git-annex-builder"
|
||||
! Docker.docked hosts "i386-git-annex-builder"
|
||||
! Docker.docked hosts "android-git-annex-builder"
|
||||
|
||||
|
||||
--' __|II| ,.
|
||||
|
@ -261,8 +297,17 @@ type Motd = [String]
|
|||
|
||||
-- This is my standard system setup.
|
||||
standardSystem :: HostName -> DebianSuite -> Architecture -> Motd -> Host
|
||||
standardSystem hn suite arch motd = host hn
|
||||
standardSystem hn suite arch motd = standardSystemUnhardened hn suite arch motd
|
||||
-- Harden the system, but only once root's authorized_keys
|
||||
-- is safely in place.
|
||||
& check (Ssh.hasAuthorizedKeys "root")
|
||||
(Ssh.passwordAuthentication False)
|
||||
|
||||
standardSystemUnhardened :: HostName -> DebianSuite -> Architecture -> Motd -> Host
|
||||
standardSystemUnhardened hn suite arch motd = host hn
|
||||
& os (System (Debian suite) arch)
|
||||
& Hostname.sane
|
||||
& Hostname.searchDomain
|
||||
& File.hasContent "/etc/motd" ("":motd++[""])
|
||||
& Apt.stdSourcesList `onChange` Apt.upgrade
|
||||
& Apt.cacheCleaned
|
||||
|
@ -270,10 +315,6 @@ standardSystem hn suite arch motd = host hn
|
|||
& Apt.installed ["ssh"]
|
||||
& GitHome.installedFor "root"
|
||||
& User.hasSomePassword "root" (Context hn)
|
||||
-- Harden the system, but only once root's authorized_keys
|
||||
-- is safely in place.
|
||||
& check (Ssh.hasAuthorizedKeys "root")
|
||||
(Ssh.passwordAuthentication False)
|
||||
& User.accountFor "joey"
|
||||
& User.hasSomePassword "joey" (Context hn)
|
||||
& Sudo.enabledFor "joey"
|
||||
|
@ -350,6 +391,7 @@ monsters = -- but do want to track their public keys etc.
|
|||
& ipv4 "67.223.19.96"
|
||||
& ipv6 "2001:4978:f:2d9::2"
|
||||
& alias "backup.kitenet.net"
|
||||
& alias "usbackup.kitenet.net"
|
||||
& sshPubKey "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAokMXQiX/NZjA1UbhMdgAscnS5dsmy+Q7bWrQ6tsTZ/o+6N/T5cbjoBHOdpypXJI3y/PiJTDJaQtXIhLa8gFg/EvxMnMz/KG9skADW1361JmfCc4BxicQIO2IOOe6eilPr+YsnOwiHwL0vpUnuty39cppuMWVD25GzxXlS6KQsLCvXLzxLLuNnGC43UAM0q4UwQxDtAZEK1dH2o3HMWhgMP2qEQupc24dbhpO3ecxh2C9678a3oGDuDuNf7mLp3s7ptj5qF3onitpJ82U5o7VajaHoygMaSRFeWxP2c13eM57j3bLdLwxVXFhePcKXARu1iuFTLS5uUf3hN6MkQcOGw=="
|
||||
, host "wren.kitenet.net"
|
||||
& ipv4 "80.68.85.49"
|
||||
|
@ -359,37 +401,46 @@ monsters = -- but do want to track their public keys etc.
|
|||
& alias "ftp.kitenet.net"
|
||||
& alias "mail.kitenet.net"
|
||||
& alias "smtp.kitenet.net"
|
||||
& alias "sows-ear.kitenet.net"
|
||||
& alias "www.sows-ear.kitenet.net"
|
||||
& alias "wortroot.kitenet.net"
|
||||
& alias "www.wortroot.kitenet.net"
|
||||
& alias "joey.kitenet.net"
|
||||
& alias "anna.kitenet.net"
|
||||
& alias "bitlbee.kitenet.net"
|
||||
{- Remaining services on kite:
|
||||
-
|
||||
- / = ready to go on kite.kitenet.net
|
||||
-
|
||||
- mail
|
||||
- postfix
|
||||
- postgrey
|
||||
- /postfix
|
||||
- /postgrey
|
||||
- mailman
|
||||
- spamassassin
|
||||
- sqwebmail
|
||||
- courier
|
||||
- imap
|
||||
- tls
|
||||
- apache
|
||||
- some static websites
|
||||
- bitlbee
|
||||
- prosody
|
||||
- (used by daddy's git-annex)
|
||||
- named
|
||||
- (branchable is still pushing to here
|
||||
- (thinking it's ns2.branchable.com), but it's no
|
||||
- longer a primary or secondary for anything)
|
||||
- /spamassassin
|
||||
- sqwebmail (cannot use this with dovecot, alternatives?)
|
||||
- /imap server
|
||||
- /pop server
|
||||
- /apache
|
||||
- (need to re-rsync /srv/web to new kite.kitenet.net
|
||||
- server before decommissioning)
|
||||
- bitlbee (EOL?)
|
||||
- prosody (EOL?)
|
||||
- ftpd (EOL)
|
||||
-
|
||||
- user shell stuff:
|
||||
- pine, zsh, make, git-annex, myrepos, ...
|
||||
- Pre-transition:
|
||||
- - re-rsync /home (skip ~joey and .pine*)
|
||||
-
|
||||
- Transition plan:
|
||||
- - on darkstar: offlineimap run & disable cron job
|
||||
- & move offlineimap files to tmp
|
||||
- - take down wren pstfix, imap, pop servers
|
||||
- - log all users out of wren
|
||||
- - final /home rsync (skip ~joey and .pine*)
|
||||
- - rsync /var/mail
|
||||
- - rsync mailman and mailman list archives dirs
|
||||
- - switch kitenet.net dns and enable pop.kitenet.net etc aliass
|
||||
- - point wren.kitenet.net at kite.kitenet.net temporarily
|
||||
- (make real-wren.kitenet.net alias)
|
||||
- - reconfigure errol's email client to use new server
|
||||
- - re-run offlinimap against new server
|
||||
- - test mail
|
||||
- - test virus filtering
|
||||
- - test http://kitenet.net/~kyle/ (user home dirs)
|
||||
- - migrate user cron jobs
|
||||
-}
|
||||
, host "mouse.kitenet.net"
|
||||
& ipv6 "2001:4830:1600:492::2"
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
propellor (0.8.2) unstable; urgency=medium
|
||||
|
||||
* Fix bug in File.containsLines that caused lines that were already in the
|
||||
file to sometimes be appended to the end.
|
||||
* Hostname.sane also configures /etc/mailname.
|
||||
* Fixed Postfix.satellite to really configure relayhost = smtp.domain.
|
||||
* Avoid reconfiguring postfix unncessarily when it already has a relayhost.
|
||||
* Deal with apache 2.4's change in the name of site-available config files.
|
||||
* Hostname aliases can now be used in several places, including --spin
|
||||
and Ssh.knownHost.
|
||||
|
||||
-- Joey Hess <joeyh@debian.org> Mon, 04 Aug 2014 01:12:19 -0400
|
||||
|
||||
propellor (0.8.1) unstable; urgency=medium
|
||||
|
||||
* Run apt-get update in initial bootstrap.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
|||
Name: propellor
|
||||
Version: 0.8.1
|
||||
Version: 0.8.2
|
||||
Cabal-Version: >= 1.6
|
||||
License: BSD3
|
||||
Maintainer: Joey Hess <joey@kitenet.net>
|
||||
|
|
|
@ -43,11 +43,15 @@ ipv6 = addDNS . Address . IPv6
|
|||
-- problems with CNAMEs, and also means that when multiple hosts have the
|
||||
-- same alias, a DNS round-robin is automatically set up.
|
||||
alias :: Domain -> Property
|
||||
alias = addDNS . CNAME . AbsDomain
|
||||
alias d = pureInfoProperty ("alias " ++ d) $ mempty
|
||||
{ _aliases = S.singleton d
|
||||
-- A CNAME is added here, but the DNS setup code converts it to an
|
||||
-- IP address when that makes sense.
|
||||
, _dns = S.singleton $ CNAME $ AbsDomain d
|
||||
}
|
||||
|
||||
addDNS :: Record -> Property
|
||||
addDNS r = pureInfoProperty (rdesc r) $
|
||||
mempty { _dns = S.singleton r }
|
||||
addDNS r = pureInfoProperty (rdesc r) $ mempty { _dns = S.singleton r }
|
||||
where
|
||||
rdesc (CNAME d) = unwords ["alias", ddesc d]
|
||||
rdesc (Address (IPv4 addr)) = unwords ["ipv4", addr]
|
||||
|
@ -71,8 +75,15 @@ getSshPubKey = askInfo _sshPubKey
|
|||
hostMap :: [Host] -> M.Map HostName Host
|
||||
hostMap l = M.fromList $ zip (map hostName l) l
|
||||
|
||||
aliasMap :: [Host] -> M.Map HostName Host
|
||||
aliasMap = M.fromList . concat .
|
||||
map (\h -> map (\aka -> (aka, h)) $ S.toList $ _aliases $ hostInfo h)
|
||||
|
||||
findHost :: [Host] -> HostName -> Maybe Host
|
||||
findHost l hn = M.lookup hn (hostMap l)
|
||||
findHost l hn = maybe (findAlias l hn) Just (M.lookup hn (hostMap l))
|
||||
|
||||
findAlias :: [Host] -> HostName -> Maybe Host
|
||||
findAlias l hn = M.lookup hn (aliasMap l)
|
||||
|
||||
getAddresses :: Info -> [IPAddr]
|
||||
getAddresses = mapMaybe getIPAddr . S.toList . _dns
|
||||
|
|
|
@ -10,20 +10,21 @@ type ConfigFile = [String]
|
|||
siteEnabled :: HostName -> ConfigFile -> RevertableProperty
|
||||
siteEnabled hn cf = RevertableProperty enable disable
|
||||
where
|
||||
enable = trivial $ cmdProperty "a2ensite" ["--quiet", hn]
|
||||
enable = trivial (cmdProperty "a2ensite" ["--quiet", hn])
|
||||
`describe` ("apache site enabled " ++ hn)
|
||||
`requires` siteAvailable hn cf
|
||||
`requires` installed
|
||||
`onChange` reloaded
|
||||
disable = trivial $ File.notPresent (siteCfg hn)
|
||||
`describe` ("apache site disabled " ++ hn)
|
||||
disable = trivial $ combineProperties
|
||||
("apache site disabled " ++ hn)
|
||||
(map File.notPresent (siteCfg hn))
|
||||
`onChange` cmdProperty "a2dissite" ["--quiet", hn]
|
||||
`requires` installed
|
||||
`onChange` reloaded
|
||||
|
||||
siteAvailable :: HostName -> ConfigFile -> Property
|
||||
siteAvailable hn cf = siteCfg hn `File.hasContent` (comment:cf)
|
||||
`describe` ("apache site available " ++ hn)
|
||||
siteAvailable hn cf = combineProperties ("apache site available " ++ hn) $
|
||||
map (`File.hasContent` (comment:cf)) (siteCfg hn)
|
||||
where
|
||||
comment = "# deployed with propellor, do not modify"
|
||||
|
||||
|
@ -39,8 +40,15 @@ modEnabled modname = RevertableProperty enable disable
|
|||
`requires` installed
|
||||
`onChange` reloaded
|
||||
|
||||
siteCfg :: HostName -> FilePath
|
||||
siteCfg hn = "/etc/apache2/sites-available/" ++ hn
|
||||
-- This is a list of config files because different versions of apache
|
||||
-- use different filenames. Propellor simply writen them all.
|
||||
siteCfg :: HostName -> [FilePath]
|
||||
siteCfg hn =
|
||||
-- Debian pre-2.4
|
||||
[ "/etc/apache2/sites-available/" ++ hn
|
||||
-- Debian 2.4+
|
||||
, "/etc/apache2/sites-available/" ++ hn ++ ".conf"
|
||||
]
|
||||
|
||||
installed :: Property
|
||||
installed = Apt.installed ["apache2"]
|
||||
|
@ -60,3 +68,19 @@ multiSSL = "/etc/apache2/conf.d/ssl" `File.hasContent`
|
|||
]
|
||||
`describe` "apache SNI enabled"
|
||||
`onChange` reloaded
|
||||
|
||||
-- | Config file fragment that can be inserted into a <Directory>
|
||||
-- stanza to allow global read access to the directory.
|
||||
--
|
||||
-- Works with multiple versions of apache that have different ways to do
|
||||
-- it.
|
||||
allowAll :: String
|
||||
allowAll = unlines
|
||||
[ "<IfVersion < 2.4>"
|
||||
, "Order allow,deny"
|
||||
, "allow from all"
|
||||
, "</IfVersion>"
|
||||
, "<IfVersion >= 2.4>"
|
||||
, "Require all granted"
|
||||
, "</IfVersion>"
|
||||
]
|
||||
|
|
|
@ -4,6 +4,7 @@ import Propellor
|
|||
import qualified Propellor.Property.File as File
|
||||
import qualified Propellor.Property.Apt as Apt
|
||||
import Utility.SafeCommand
|
||||
import Utility.FileMode
|
||||
|
||||
import Data.Char
|
||||
|
||||
|
@ -19,22 +20,33 @@ type CronTimes = String
|
|||
--
|
||||
-- The cron job's output will only be emailed if it exits nonzero.
|
||||
job :: Desc -> CronTimes -> UserName -> FilePath -> String -> Property
|
||||
job desc times user cddir command = cronjobfile `File.hasContent`
|
||||
[ "# Generated by propellor"
|
||||
, ""
|
||||
, "SHELL=/bin/sh"
|
||||
, "PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
|
||||
, ""
|
||||
, times ++ "\t" ++ user ++ "\t"
|
||||
++ "chronic flock -n " ++ shellEscape cronjobfile
|
||||
++ " sh -c " ++ shellEscape cmdline
|
||||
job desc times user cddir command = combineProperties ("cronned " ++ desc)
|
||||
[ cronjobfile `File.hasContent`
|
||||
[ "# Generated by propellor"
|
||||
, ""
|
||||
, "SHELL=/bin/sh"
|
||||
, "PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
|
||||
, ""
|
||||
, times ++ "\t" ++ user ++ "\tchronic " ++ shellEscape scriptfile
|
||||
]
|
||||
-- Use a separate script because it makes the cron job name
|
||||
-- prettier in emails, and also allows running the job manually.
|
||||
, scriptfile `File.hasContent`
|
||||
[ "#!/bin/sh"
|
||||
, "# Generated by propellor"
|
||||
, "set -e"
|
||||
, "flock -n " ++ shellEscape cronjobfile
|
||||
++ " sh -c " ++ shellEscape cmdline
|
||||
]
|
||||
, scriptfile `File.mode` combineModes (readModes ++ executeModes)
|
||||
]
|
||||
`requires` Apt.serviceInstalledRunning "cron"
|
||||
`requires` Apt.installed ["util-linux", "moreutils"]
|
||||
`describe` ("cronned " ++ desc)
|
||||
where
|
||||
cmdline = "cd " ++ cddir ++ " && ( " ++ command ++ " )"
|
||||
cronjobfile = "/etc/cron.d/" ++ map sanitize desc
|
||||
cronjobfile = "/etc/cron.d/" ++ name
|
||||
scriptfile = "/usr/local/bin/" ++ name ++ "_cronjob"
|
||||
name = map sanitize desc
|
||||
sanitize c
|
||||
| isAlphaNum c = c
|
||||
| otherwise = '_'
|
||||
|
@ -42,7 +54,7 @@ job desc times user cddir command = cronjobfile `File.hasContent`
|
|||
-- | Installs a cron job, and runs it niced and ioniced.
|
||||
niceJob :: Desc -> CronTimes -> UserName -> FilePath -> String -> Property
|
||||
niceJob desc times user cddir command = job desc times user cddir
|
||||
("nice ionice -c 3 " ++ command)
|
||||
("nice ionice -c 3 sh -c " ++ shellEscape command)
|
||||
|
||||
-- | Installs a cron job to run propellor.
|
||||
runPropellor :: CronTimes -> Property
|
||||
|
|
|
@ -18,28 +18,32 @@ f `hasContent` newcontent = fileProperty ("replace " ++ f)
|
|||
-- The file's permissions are preserved if the file already existed.
|
||||
-- Otherwise, they're set to 600.
|
||||
hasPrivContent :: FilePath -> Context -> Property
|
||||
hasPrivContent f context = withPrivData (PrivFile f) context $ \getcontent ->
|
||||
property desc $ getcontent $ \privcontent ->
|
||||
ensureProperty $ fileProperty' writeFileProtected desc
|
||||
(\_oldcontent -> lines privcontent) f
|
||||
hasPrivContent = hasPrivContent' writeFileProtected
|
||||
|
||||
-- | Leaves the file at its default or current mode,
|
||||
-- allowing "private" data to be read.
|
||||
--
|
||||
-- Use with caution!
|
||||
hasPrivContentExposed :: FilePath -> Context -> Property
|
||||
hasPrivContentExposed = hasPrivContent' writeFile
|
||||
|
||||
hasPrivContent' :: (String -> FilePath -> IO ()) -> FilePath -> Context -> Property
|
||||
hasPrivContent' writer f context =
|
||||
withPrivData (PrivFile f) context $ \getcontent ->
|
||||
property desc $ getcontent $ \privcontent ->
|
||||
ensureProperty $ fileProperty' writer desc
|
||||
(\_oldcontent -> lines privcontent) f
|
||||
where
|
||||
desc = "privcontent " ++ f
|
||||
|
||||
-- | Leaves the file world-readable.
|
||||
hasPrivContentExposed :: FilePath -> Context -> Property
|
||||
hasPrivContentExposed f context = hasPrivContent f context `onChange`
|
||||
mode f (combineModes (ownerWriteMode:readModes))
|
||||
|
||||
-- | Ensures that a line is present in a file, adding it to the end if not.
|
||||
containsLine :: FilePath -> Line -> Property
|
||||
f `containsLine` l = f `containsLines` [l]
|
||||
|
||||
containsLines :: FilePath -> [Line] -> Property
|
||||
f `containsLines` l = fileProperty (f ++ " contains:" ++ show l) go f
|
||||
f `containsLines` ls = fileProperty (f ++ " contains:" ++ show ls) go f
|
||||
where
|
||||
go ls
|
||||
| all (`elem` ls) l = ls
|
||||
| otherwise = ls++l
|
||||
go content = content ++ filter (`notElem` content) ls
|
||||
|
||||
-- | Ensures that a line is not present in a file.
|
||||
-- Note that the file is ensured to exist, so if it doesn't, an empty
|
||||
|
|
|
@ -3,10 +3,14 @@ module Propellor.Property.Hostname where
|
|||
import Propellor
|
||||
import qualified Propellor.Property.File as File
|
||||
|
||||
import Data.List
|
||||
|
||||
-- | Ensures that the hostname is set using best practices.
|
||||
--
|
||||
-- Configures /etc/hostname and the current hostname.
|
||||
--
|
||||
-- Configures /etc/mailname with the domain part of the hostname.
|
||||
--
|
||||
-- /etc/hosts is also configured, with an entry for 127.0.1.1, which is
|
||||
-- standard at least on Debian to set the FDQN.
|
||||
--
|
||||
|
@ -29,6 +33,8 @@ setTo hn = combineProperties desc go
|
|||
else Just $ trivial $ hostsline "127.0.1.1" [hn, basehost]
|
||||
, Just $ trivial $ hostsline "127.0.0.1" ["localhost"]
|
||||
, Just $ trivial $ cmdProperty "hostname" [basehost]
|
||||
, Just $ "/etc/mailname" `File.hasContent`
|
||||
[if null domain then hn else domain]
|
||||
]
|
||||
|
||||
hostsline ip names = File.fileProperty desc
|
||||
|
@ -37,3 +43,21 @@ setTo hn = combineProperties desc go
|
|||
addhostsline ip names ls =
|
||||
(ip ++ "\t" ++ (unwords names)) : filter (not . hasip ip) ls
|
||||
hasip ip l = headMaybe (words l) == Just ip
|
||||
|
||||
-- | Makes /etc/resolv.conf contain search and domain lines for
|
||||
-- the domain that the hostname is in.
|
||||
searchDomain :: Property
|
||||
searchDomain = property desc (ensureProperty . go =<< asks hostName)
|
||||
where
|
||||
desc = "resolv.conf search and domain configured"
|
||||
go hn =
|
||||
let (_basehost, domain) = separate (== '.') hn
|
||||
in File.fileProperty desc (use domain) "/etc/resolv.conf"
|
||||
use domain ls = filter wanted $ nub (ls ++ cfgs)
|
||||
where
|
||||
cfgs = ["domain " ++ domain, "search " ++ domain]
|
||||
wanted l
|
||||
| l `elem` cfgs = True
|
||||
| "domain " `isPrefixOf` l = False
|
||||
| "search " `isPrefixOf` l = False
|
||||
| otherwise = True
|
||||
|
|
|
@ -33,8 +33,8 @@ data NumClients = OnlyClient | MultipleClients
|
|||
-- > [ "--repository=sftp://2318@usw-s002.rsync.net/~/mygitrepos.obnam"
|
||||
-- > , "--encrypt-with=1B169BE1"
|
||||
-- > ] Obnam.OnlyClient
|
||||
-- > `requires` Gpg.keyImported "1B169BE1" "root"
|
||||
-- > `requires` Ssh.keyImported SshRsa "root"
|
||||
-- > `requires` Gpg.keyImported "1B169BE1" "root"
|
||||
-- > `requires` Ssh.keyImported SshRsa "root" (Context hostname)
|
||||
--
|
||||
-- How awesome is that?
|
||||
backup :: FilePath -> Cron.CronTimes -> [ObnamParam] -> NumClients -> Property
|
||||
|
|
|
@ -2,24 +2,120 @@ module Propellor.Property.Postfix where
|
|||
|
||||
import Propellor
|
||||
import qualified Propellor.Property.Apt as Apt
|
||||
import Propellor.Property.File
|
||||
import qualified Propellor.Property.Service as Service
|
||||
|
||||
import qualified Data.Map as M
|
||||
import Data.List
|
||||
import Data.Char
|
||||
|
||||
installed :: Property
|
||||
installed = Apt.serviceInstalledRunning "postfix"
|
||||
|
||||
restarted :: Property
|
||||
restarted = Service.restarted "postfix"
|
||||
|
||||
reloaded :: Property
|
||||
reloaded = Service.reloaded "postfix"
|
||||
|
||||
-- | Configures postfix as a satellite system, which
|
||||
-- relats all mail through a relay host, which defaults to smtp.domain.
|
||||
-- relays all mail through a relay host, which defaults to smtp.domain.
|
||||
--
|
||||
-- The smarthost may refuse to relay mail on to other domains, without
|
||||
-- futher coniguration/keys. But this should be enough to get cron job
|
||||
-- mail flowing to a place where it will be seen.
|
||||
satellite :: Property
|
||||
satellite = setup `requires` installed
|
||||
satellite = check (not <$> mainCfIsSet "relayhost") setup
|
||||
`requires` installed
|
||||
where
|
||||
setup = trivial $ property "postfix satellite system" $ do
|
||||
hn <- asks hostName
|
||||
ensureProperty $ Apt.reConfigure "postfix"
|
||||
[ ("postfix/main_mailer_type", "select", "Satellite system")
|
||||
, ("postfix/root_address", "string", "root")
|
||||
, ("postfix/destinations", "string", " ")
|
||||
, ("postfix/mailname", "string", hn)
|
||||
let (_, domain) = separate (== '.') hn
|
||||
ensureProperties
|
||||
[ Apt.reConfigure "postfix"
|
||||
[ ("postfix/main_mailer_type", "select", "Satellite system")
|
||||
, ("postfix/root_address", "string", "root")
|
||||
, ("postfix/destinations", "string", " ")
|
||||
, ("postfix/mailname", "string", hn)
|
||||
]
|
||||
, mainCf ("relayhost", domain)
|
||||
`onChange` reloaded
|
||||
]
|
||||
|
||||
-- | Sets up a file by running a property (which the filename is passed
|
||||
-- to). If the setup property makes a change, postmap will be run on the
|
||||
-- file, and postfix will be reloaded.
|
||||
mappedFile :: FilePath -> (FilePath -> Property) -> Property
|
||||
mappedFile f setup = setup f
|
||||
`onChange` cmdProperty "postmap" [f]
|
||||
|
||||
-- | Run newaliases command, which should be done after changing
|
||||
-- /etc/aliases.
|
||||
newaliases :: Property
|
||||
newaliases = trivial $ cmdProperty "newaliases" []
|
||||
|
||||
-- | The main config file for postfix.
|
||||
mainCfFile :: FilePath
|
||||
mainCfFile = "/etc/postfix/main.cf"
|
||||
|
||||
-- | Sets a main.cf name=value pair. Does not reload postfix immediately.
|
||||
mainCf :: (String, String) -> Property
|
||||
mainCf (name, value) = check notset set
|
||||
`describe` ("postfix main.cf " ++ setting)
|
||||
where
|
||||
setting = name ++ "=" ++ value
|
||||
notset = (/= Just value) <$> getMainCf name
|
||||
set = cmdProperty "postconf" ["-e", setting]
|
||||
|
||||
-- | Gets a man.cf setting.
|
||||
getMainCf :: String -> IO (Maybe String)
|
||||
getMainCf name = parse . lines <$> readProcess "postconf" [name]
|
||||
where
|
||||
parse (l:_) = Just $
|
||||
case separate (== '=') l of
|
||||
(_, (' ':v)) -> v
|
||||
(_, v) -> v
|
||||
parse [] = Nothing
|
||||
|
||||
-- | Checks if a main.cf field is set. A field that is set to ""
|
||||
-- is considered not set.
|
||||
mainCfIsSet :: String -> IO Bool
|
||||
mainCfIsSet name = do
|
||||
v <- getMainCf name
|
||||
return $ v /= Nothing && v /= Just ""
|
||||
|
||||
-- | Parses main.cf, and removes any initial configuration lines that are
|
||||
-- overridden to other values later in the file.
|
||||
--
|
||||
-- For example, to add some settings, removing any old settings:
|
||||
--
|
||||
-- > mainCf `File.containsLines`
|
||||
-- > [ "# I like bars."
|
||||
-- > , "foo = bar"
|
||||
-- > ] `onChange` dedupMainCf
|
||||
--
|
||||
-- Note that multiline configurations that continue onto the next line
|
||||
-- are not currently supported.
|
||||
dedupMainCf :: Property
|
||||
dedupMainCf = fileProperty "postfix main.cf dedupped" dedupCf mainCfFile
|
||||
|
||||
dedupCf :: [String] -> [String]
|
||||
dedupCf ls =
|
||||
let parsed = map parse ls
|
||||
in dedup [] (keycounts $ rights parsed) parsed
|
||||
where
|
||||
parse l
|
||||
| "#" `isPrefixOf` l = Left l
|
||||
| "=" `isInfixOf` l =
|
||||
let (k, v) = separate (== '=') l
|
||||
in Right ((filter (not . isSpace) k), v)
|
||||
| otherwise = Left l
|
||||
fmt k v = k ++ " =" ++ v
|
||||
|
||||
keycounts = M.fromListWith (+) . map (\(k, _v) -> (k, (1 :: Integer)))
|
||||
|
||||
dedup c _ [] = reverse c
|
||||
dedup c kc ((Left v):rest) = dedup (v:c) kc rest
|
||||
dedup c kc ((Right (k, v)):rest) = case M.lookup k kc of
|
||||
Just n | n > 1 -> dedup c (M.insert k (n - 1) kc) rest
|
||||
_ -> dedup (fmt k v:c) kc rest
|
||||
|
|
|
@ -14,12 +14,14 @@ import qualified Propellor.Property.Service as Service
|
|||
import qualified Propellor.Property.User as User
|
||||
import qualified Propellor.Property.Obnam as Obnam
|
||||
import qualified Propellor.Property.Apache as Apache
|
||||
import qualified Propellor.Property.Postfix as Postfix
|
||||
import Utility.SafeCommand
|
||||
import Utility.FileMode
|
||||
import Utility.Path
|
||||
|
||||
import Data.List
|
||||
import System.Posix.Files
|
||||
import Data.String.Utils
|
||||
|
||||
oldUseNetServer :: [Host] -> Property
|
||||
oldUseNetServer hosts = propertyList ("olduse.net server")
|
||||
|
@ -59,9 +61,7 @@ oldUseNetServer hosts = propertyList ("olduse.net server")
|
|||
, " <Directory " ++ datadir ++ "/>"
|
||||
, " Options Indexes FollowSymlinks"
|
||||
, " AllowOverride None"
|
||||
-- I had this in the file before.
|
||||
-- This may be needed by a newer version of apache?
|
||||
--, " Require all granted"
|
||||
, Apache.allowAll
|
||||
, " </Directory>"
|
||||
]
|
||||
]
|
||||
|
@ -114,11 +114,11 @@ mumbleServer hosts = combineProperties hn
|
|||
[ Apt.serviceInstalledRunning "mumble-server"
|
||||
, Obnam.latestVersion
|
||||
, Obnam.backup "/var/lib/mumble-server" "55 5 * * *"
|
||||
[ "--repository=sftp://joey@turtle.kitenet.net/~/lib/backup/" ++ hn ++ ".obnam"
|
||||
[ "--repository=sftp://joey@usbackup.kitenet.net/~/lib/backup/" ++ hn ++ ".obnam"
|
||||
, "--client-name=mumble"
|
||||
] Obnam.OnlyClient
|
||||
`requires` Ssh.keyImported SshRsa "root" (Context hn)
|
||||
`requires` Ssh.knownHost hosts "turtle.kitenet.net" "root"
|
||||
`requires` Ssh.knownHost hosts "usbackup.kitenet.net" "root"
|
||||
, trivial $ cmdProperty "chown" ["-R", "mumble-server:mumble-server", "/var/lib/mumble-server"]
|
||||
]
|
||||
where
|
||||
|
@ -142,7 +142,7 @@ gitServer hosts = propertyList "git.kitenet.net setup"
|
|||
, Obnam.backup "/srv/git" "33 3 * * *"
|
||||
[ "--repository=sftp://2318@usw-s002.rsync.net/~/git.kitenet.net"
|
||||
, "--encrypt-with=1B169BE1"
|
||||
, "--client-name=wren"
|
||||
, "--client-name=wren" -- historical
|
||||
] Obnam.OnlyClient
|
||||
`requires` Gpg.keyImported "1B169BE1" "root"
|
||||
`requires` Ssh.keyImported SshRsa "root" (Context "git.kitenet.net")
|
||||
|
@ -191,8 +191,8 @@ gitServer hosts = propertyList "git.kitenet.net setup"
|
|||
type AnnexUUID = String
|
||||
|
||||
-- | A website, with files coming from a git-annex repository.
|
||||
annexWebSite :: [Host] -> Git.RepoUrl -> HostName -> AnnexUUID -> [(String, Git.RepoUrl)] -> Property
|
||||
annexWebSite hosts origin hn uuid remotes = propertyList (hn ++" website using git-annex")
|
||||
annexWebSite :: Git.RepoUrl -> HostName -> AnnexUUID -> [(String, Git.RepoUrl)] -> Property
|
||||
annexWebSite origin hn uuid remotes = propertyList (hn ++" website using git-annex")
|
||||
[ Git.cloned "joey" origin dir Nothing
|
||||
`onChange` setup
|
||||
, postupdatehook `File.hasContent`
|
||||
|
@ -206,8 +206,6 @@ annexWebSite hosts origin hn uuid remotes = propertyList (hn ++" website using g
|
|||
dir = "/srv/web/" ++ hn
|
||||
postupdatehook = dir </> ".git/hooks/post-update"
|
||||
setup = userScriptProperty "joey" setupscript
|
||||
`requires` Ssh.keyImported SshRsa "joey" (Context hn)
|
||||
`requires` Ssh.knownHost hosts "turtle.kitenet.net" "joey"
|
||||
setupscript =
|
||||
[ "cd " ++ shellEscape dir
|
||||
, "git config annex.uuid " ++ shellEscape uuid
|
||||
|
@ -348,8 +346,27 @@ githubBackup = propertyList "github-backup box"
|
|||
, let f = "/home/joey/.github-keys"
|
||||
in File.hasPrivContent f anyContext
|
||||
`onChange` File.ownerGroup f "joey" "joey"
|
||||
, Cron.niceJob "github-backup run" "30 4 * * *" "joey"
|
||||
"/home/joey/lib/backup" $ intercalate "&&"
|
||||
[ "mkdir -p github"
|
||||
, "cd github"
|
||||
, ". $HOME/.github-keys && github-backup joeyh"
|
||||
]
|
||||
]
|
||||
|
||||
rsyncNetBackup :: [Host] -> Property
|
||||
rsyncNetBackup hosts = Cron.niceJob "rsync.net copied in daily" "30 5 * * *"
|
||||
"joey" "/home/joey/lib/backup" "mkdir -p rsync.net && rsync --delete -az 2318@usw-s002.rsync.net: rsync.net"
|
||||
`requires` Ssh.knownHost hosts "usw-s002.rsync.net" "joey"
|
||||
|
||||
backupsBackedupTo :: [Host] -> HostName -> FilePath -> Property
|
||||
backupsBackedupTo hosts desthost destdir = Cron.niceJob desc
|
||||
"1 1 * * 3" "joey" "/" cmd
|
||||
`requires` Ssh.knownHost hosts desthost "joey"
|
||||
where
|
||||
desc = "backups copied to " ++ desthost ++ " weekly"
|
||||
cmd = "rsync -az --delete /home/joey/lib/backup " ++ desthost ++ ":" ++ destdir
|
||||
|
||||
obnamRepos :: [String] -> Property
|
||||
obnamRepos rs = propertyList ("obnam repos for " ++ unwords rs)
|
||||
(mkbase : map mkrepo rs)
|
||||
|
@ -360,3 +377,354 @@ obnamRepos rs = propertyList ("obnam repos for " ++ unwords rs)
|
|||
mkdir d = File.dirExists d
|
||||
`before` File.ownerGroup d "joey" "joey"
|
||||
|
||||
podcatcher :: Property
|
||||
podcatcher = Cron.niceJob "podcatcher run hourly" "55 * * * *"
|
||||
"joey" "/home/joey/lib/sound/podcasts"
|
||||
"xargs git-annex importfeed -c annex.genmetadata=true < feeds; mr --quiet update"
|
||||
`requires` Apt.installed ["git-annex", "myrepos"]
|
||||
|
||||
kiteMailServer :: Property
|
||||
kiteMailServer = propertyList "kitenet.net mail server"
|
||||
[ Postfix.installed
|
||||
, Apt.installed ["postfix-pcre"]
|
||||
, Apt.serviceInstalledRunning "postgrey"
|
||||
|
||||
, Apt.serviceInstalledRunning "spamassassin"
|
||||
, "/etc/default/spamassassin" `File.containsLines`
|
||||
[ "# Propellor deployed"
|
||||
, "ENABLED=1"
|
||||
, "CRON=1"
|
||||
, "OPTIONS=\"--create-prefs --max-children 5 --helper-home-dir\""
|
||||
, "CRON=1"
|
||||
, "NICE=\"--nicelevel 15\""
|
||||
] `onChange` Service.restarted "spamassassin"
|
||||
`describe` "spamd enabled"
|
||||
`requires` Apt.serviceInstalledRunning "cron"
|
||||
|
||||
, Apt.serviceInstalledRunning "spamass-milter"
|
||||
-- Add -m to prevent modifying messages Subject or body.
|
||||
, "/etc/default/spamass-milter" `File.containsLine`
|
||||
"OPTIONS=\"-m -u spamass-milter -i 127.0.0.1\""
|
||||
`onChange` Service.restarted "spamass-milter"
|
||||
`describe` "spamass-milter configured"
|
||||
|
||||
, Apt.serviceInstalledRunning "amavisd-milter"
|
||||
, "/etc/default/amavisd-milter" `File.containsLines`
|
||||
[ "# Propellor deployed"
|
||||
, "MILTERSOCKET=/var/spool/postfix/amavis/amavis.sock"
|
||||
, "MILTERSOCKETOWNER=\"postfix:postfix\""
|
||||
, "MILTERSOCKETMODE=\"0660\""
|
||||
]
|
||||
`onChange` Service.restarted "amavisd-milter"
|
||||
`describe` "amavisd-milter configured for postfix"
|
||||
, Apt.serviceInstalledRunning "clamav-freshclam"
|
||||
|
||||
, Apt.installed ["maildrop"]
|
||||
, "/etc/maildroprc" `File.hasContent`
|
||||
[ "# Global maildrop filter file (deployed with propellor)"
|
||||
, "DEFAULT=\"$HOME/Maildir\""
|
||||
, "MAILBOX=\"$DEFAULT/.\""
|
||||
, "# Filter spam to a spam folder, unless .keepspam exists"
|
||||
, "if (/^X-Spam-Status: Yes/)"
|
||||
, "{"
|
||||
, " `test -e \"$HOME/.keepspam\"`"
|
||||
, " if ( $RETURNCODE != 0 )"
|
||||
, " to ${MAILBOX}spam"
|
||||
, "}"
|
||||
]
|
||||
`describe` "maildrop configured"
|
||||
|
||||
, "/etc/aliases" `File.hasPrivContentExposed` ctx
|
||||
`onChange` Postfix.newaliases
|
||||
, hasJoeyCAChain
|
||||
, "/etc/ssl/certs/postfix.pem" `File.hasPrivContentExposed` ctx
|
||||
, "/etc/ssl/private/postfix.pem" `File.hasPrivContent` ctx
|
||||
|
||||
, "/etc/postfix/mydomain" `File.containsLines`
|
||||
[ "/.*\\.kitenet\\.net/\tOK"
|
||||
, "/ikiwiki\\.info/\tOK"
|
||||
, "/joeyh\\.name/\tOK"
|
||||
]
|
||||
`onChange` Postfix.reloaded
|
||||
`describe` "postfix mydomain file configured"
|
||||
, "/etc/postfix/obscure_client_relay.pcre" `File.containsLine`
|
||||
"/^Received: from ([^.]+)\\.kitenet\\.net.*using TLS.*by kitenet\\.net \\(([^)]+)\\) with (E?SMTPS?A?) id ([A-F[:digit:]]+)(.*)/ IGNORE"
|
||||
`onChange` Postfix.reloaded
|
||||
`describe` "postfix obscure_client_relay file configured"
|
||||
, Postfix.mappedFile "/etc/postfix/virtual"
|
||||
(flip File.containsLines
|
||||
[ "# *@joeyh.name to joey"
|
||||
, "@joeyh.name\tjoey"
|
||||
]
|
||||
) `describe` "postfix virtual file configured"
|
||||
`onChange` Postfix.reloaded
|
||||
, Postfix.mappedFile "/etc/postfix/relay_clientcerts" $
|
||||
flip File.hasPrivContentExposed ctx
|
||||
, Postfix.mainCfFile `File.containsLines`
|
||||
[ "myhostname = kitenet.net"
|
||||
, "mydomain = $myhostname"
|
||||
, "append_dot_mydomain = no"
|
||||
, "myorigin = kitenet.net"
|
||||
, "mydestination = $myhostname, localhost.$mydomain, $mydomain, kite.$mydomain., localhost, regexp:$config_directory/mydomain"
|
||||
, "mailbox_command = maildrop"
|
||||
, "virtual_alias_maps = hash:/etc/postfix/virtual"
|
||||
|
||||
, "# Allow clients with trusted certs to relay mail through."
|
||||
, "relay_clientcerts = hash:/etc/postfix/relay_clientcerts"
|
||||
, "smtpd_relay_restrictions = permit_mynetworks,permit_tls_clientcerts,permit_sasl_authenticated,reject_unauth_destination"
|
||||
|
||||
, "# Filter out client relay lines from headers."
|
||||
, "header_checks = pcre:$config_directory/obscure_client_relay.pcre"
|
||||
|
||||
, "# Enable postgrey."
|
||||
, "smtpd_recipient_restrictions = permit_mynetworks,reject_unauth_destination,check_policy_service inet:127.0.0.1:10023"
|
||||
|
||||
, "# Enable spamass-milter and amavis-milter."
|
||||
, "smtpd_milters = unix:/spamass/spamass.sock unix:amavis/amavis.sock"
|
||||
, "milter_connect_macros = j {daemon_name} v {if_name} _"
|
||||
|
||||
, "# TLS setup -- server"
|
||||
, "smtpd_tls_CAfile = /etc/ssl/certs/joeyca.pem"
|
||||
, "smtpd_tls_cert_file = /etc/ssl/certs/postfix.pem"
|
||||
, "smtpd_tls_key_file = /etc/ssl/private/postfix.pem"
|
||||
, "smtpd_tls_loglevel = 1"
|
||||
, "smtpd_tls_received_header = yes"
|
||||
, "smtpd_use_tls = yes"
|
||||
, "smtpd_tls_ask_ccert = yes"
|
||||
, "smtpd_tls_session_cache_database = sdbm:/etc/postfix/smtpd_scache"
|
||||
|
||||
, "# TLS setup -- client"
|
||||
, "smtp_tls_CAfile = /etc/ssl/certs/joeyca.pem"
|
||||
, "smtp_tls_cert_file = /etc/ssl/certs/postfix.pem"
|
||||
, "smtp_tls_key_file = /etc/ssl/private/postfix.pem"
|
||||
, "smtp_tls_loglevel = 1"
|
||||
, "smtp_use_tls = yes"
|
||||
, "smtp_tls_session_cache_database = sdbm:/etc/postfix/smtp_scache"
|
||||
]
|
||||
`onChange` Postfix.dedupMainCf
|
||||
`onChange` Postfix.reloaded
|
||||
`describe` "postfix configured"
|
||||
|
||||
, Apt.serviceInstalledRunning "dovecot-imapd"
|
||||
, Apt.serviceInstalledRunning "dovecot-pop3d"
|
||||
, "/etc/dovecot/conf.d/10-mail.conf" `File.containsLine`
|
||||
"mail_location = maildir:~/Maildir"
|
||||
`onChange` Service.reloaded "dovecot"
|
||||
`describe` "dovecot mail.conf"
|
||||
, "/etc/dovecot/conf.d/10-auth.conf" `File.containsLine`
|
||||
"!include auth-passwdfile.conf.ext"
|
||||
`onChange` Service.restarted "dovecot"
|
||||
`describe` "dovecot auth.conf"
|
||||
, File.hasPrivContent dovecotusers ctx
|
||||
`onChange` (dovecotusers `File.mode`
|
||||
combineModes [ownerReadMode, groupReadMode])
|
||||
, File.ownerGroup dovecotusers "root" "dovecot"
|
||||
|
||||
, Apt.installed ["mutt", "bsd-mailx", "alpine"]
|
||||
|
||||
, pinescript `File.hasContent`
|
||||
[ "#!/bin/sh"
|
||||
, "# deployed with propellor"
|
||||
, "set -e"
|
||||
, "pass=$HOME/.pine-password"
|
||||
, "if [ ! -e $pass ]; then"
|
||||
, "\ttouch $pass"
|
||||
, "fi"
|
||||
, "chmod 600 $pass"
|
||||
, "exec alpine -passfile $pass \"$@\""
|
||||
]
|
||||
`onChange` (pinescript `File.mode`
|
||||
combineModes (readModes ++ executeModes))
|
||||
`describe` "pine wrapper script"
|
||||
, "/etc/pine.conf" `File.containsLines`
|
||||
[ "inbox-path={localhost/novalidate-cert}inbox"
|
||||
]
|
||||
`describe` "pine configured to use local imap server"
|
||||
]
|
||||
where
|
||||
ctx = Context "kitenet.net"
|
||||
pinescript = "/usr/local/bin/pine"
|
||||
dovecotusers = "/etc/dovecot/users"
|
||||
|
||||
hasJoeyCAChain :: Property
|
||||
hasJoeyCAChain = "/etc/ssl/certs/joeyca.pem" `File.hasPrivContentExposed`
|
||||
Context "joeyca.pem"
|
||||
|
||||
kitenetHttps :: Property
|
||||
kitenetHttps = propertyList "kitenet.net https certs"
|
||||
[ File.hasPrivContent "/etc/ssl/certs/web.pem" ctx
|
||||
, File.hasPrivContent "/etc/ssl/private/web.pem" ctx
|
||||
, File.hasPrivContent "/etc/ssl/certs/startssl.pem" ctx
|
||||
, toProp $ Apache.modEnabled "ssl"
|
||||
]
|
||||
where
|
||||
ctx = Context "kitenet.net"
|
||||
|
||||
-- Legacy static web sites and redirections from kitenet.net to newer
|
||||
-- sites.
|
||||
legacyWebSites :: Property
|
||||
legacyWebSites = propertyList "legacy web sites"
|
||||
[ Apt.serviceInstalledRunning "apache2"
|
||||
, toProp $ Apache.modEnabled "rewrite"
|
||||
, toProp $ Apache.modEnabled "cgi"
|
||||
, toProp $ Apache.modEnabled "speling"
|
||||
, userDirHtml
|
||||
, kitenetHttps
|
||||
, toProp $ Apache.siteEnabled "kitenet.net" $ apachecfg "kitenet.net" True
|
||||
-- /var/www is empty
|
||||
[ "DocumentRoot /var/www"
|
||||
, "<Directory /var/www>"
|
||||
, " Options Indexes FollowSymLinks MultiViews ExecCGI Includes"
|
||||
, " AllowOverride None"
|
||||
, Apache.allowAll
|
||||
, "</Directory>"
|
||||
, "ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/"
|
||||
|
||||
-- for mailman cgi scripts
|
||||
, "<Directory /usr/lib/cgi-bin>"
|
||||
, " AllowOverride None"
|
||||
, " Options ExecCGI"
|
||||
, Apache.allowAll
|
||||
, "</Directory>"
|
||||
, "Alias /pipermail/ /var/lib/mailman/archives/public/"
|
||||
, "<Directory /var/lib/mailman/archives/public/>"
|
||||
, " Options Indexes MultiViews FollowSymlinks"
|
||||
, " AllowOverride None"
|
||||
, Apache.allowAll
|
||||
, "</Directory>"
|
||||
, "Alias /images/ /usr/share/images/"
|
||||
, "<Directory /usr/share/images/>"
|
||||
, " Options Indexes MultiViews"
|
||||
, " AllowOverride None"
|
||||
, Apache.allowAll
|
||||
, "</Directory>"
|
||||
|
||||
, "RewriteEngine On"
|
||||
, "# Force hostname to kitenet.net"
|
||||
, "RewriteCond %{HTTP_HOST} !^kitenet\\.net [NC]"
|
||||
, "RewriteCond %{HTTP_HOST} !^$"
|
||||
, "RewriteRule ^/(.*) http://kitenet\\.net/$1 [L,R]"
|
||||
|
||||
, "# Moved pages"
|
||||
, "RewriteRule /programs/debhelper http://joeyh.name/code/debhelper/ [L]"
|
||||
, "RewriteRule /programs/satutils http://joeyh.name/code/satutils/ [L]"
|
||||
, "RewriteRule /programs/filters http://joeyh.name/code/filters/ [L]"
|
||||
, "RewriteRule /programs/ticker http://joeyh.name/code/ticker/ [L]"
|
||||
, "RewriteRule /programs/pdmenu http://joeyh.name/code/pdmenu/ [L]"
|
||||
, "RewriteRule /programs/sleepd http://joeyh.name/code/sleepd/ [L]"
|
||||
, "RewriteRule /programs/Lingua::EN::Words2Nums http://joeyh.name/code/Words2Nums/ [L]"
|
||||
, "RewriteRule /programs/wmbattery http://joeyh.name/code/wmbattery/ [L]"
|
||||
, "RewriteRule /programs/dpkg-repack http://joeyh.name/code/dpkg-repack/ [L]"
|
||||
, "RewriteRule /programs/debconf http://joeyh.name/code/debconf/ [L]"
|
||||
, "RewriteRule /programs/perlmoo http://joeyh.name/code/perlmoo/ [L]"
|
||||
, "RewriteRule /programs/alien http://joeyh.name/code/alien/ [L]"
|
||||
, "RewriteRule /~joey/blog/entry/(.+)-[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9].html http://joeyh.name/blog/entry/$1/ [L]"
|
||||
, "RewriteRule /~anna/.* http://waldeneffect\\.org/ [R]"
|
||||
, "RewriteRule /~anna/.* http://waldeneffect\\.org/ [R]"
|
||||
, "RewriteRule /~anna http://waldeneffect\\.org/ [R]"
|
||||
, "RewriteRule /simpleid/ http://openid.kitenet.net:8081/simpleid/"
|
||||
, "# Even the kite home page is not here any more!"
|
||||
, "RewriteRule ^/$ http://www.kitenet.net/ [R]"
|
||||
, "RewriteRule ^/index.html http://www.kitenet.net/ [R]"
|
||||
, "RewriteRule ^/joey http://www.kitenet.net/joey/ [R]"
|
||||
, "RewriteRule ^/joey/index.html http://www.kitenet.net/joey/ [R]"
|
||||
, "RewriteRule ^/wifi http://www.kitenet.net/wifi/ [R]"
|
||||
, "RewriteRule ^/wifi/index.html http://www.kitenet.net/wifi/ [R]"
|
||||
|
||||
, "# Old ikiwiki filenames for kitenet.net wiki."
|
||||
, "rewritecond $1 !^/~"
|
||||
, "rewritecond $1 !^/doc/"
|
||||
, "rewritecond $1 !^/pipermail/"
|
||||
, "rewritecond $1 !^/cgi-bin/"
|
||||
, "rewritecond $1 !.*/index$"
|
||||
, "rewriterule (.+).html$ $1/ [r]"
|
||||
|
||||
, "# Old ikiwiki filenames for joey's wiki."
|
||||
, "rewritecond $1 ^/~joey/"
|
||||
, "rewritecond $1 !.*/index$"
|
||||
, "rewriterule (.+).html$ http://kitenet.net/$1/ [L,R]"
|
||||
|
||||
, "# ~joey to joeyh.name"
|
||||
, "rewriterule /~joey/(.*) http://joeyh.name/$1 [L]"
|
||||
|
||||
, "# Old familywiki location."
|
||||
, "rewriterule /~family/(.*).html http://family.kitenet.net/$1 [L]"
|
||||
, "rewriterule /~family/(.*).rss http://family.kitenet.net/$1/index.rss [L]"
|
||||
, "rewriterule /~family(.*) http://family.kitenet.net$1 [L]"
|
||||
|
||||
, "rewriterule /~kyle/bywayofscience(.*) http://bywayofscience.branchable.com$1 [L]"
|
||||
, "rewriterule /~kyle/family/wiki/(.*).html http://macleawiki.branchable.com/$1 [L]"
|
||||
, "rewriterule /~kyle/family/wiki/(.*).rss http://macleawiki.branchable.com/$1/index.rss [L]"
|
||||
, "rewriterule /~kyle/family/wiki(.*) http://macleawiki.branchable.com$1 [L]"
|
||||
]
|
||||
, alias "anna.kitenet.net"
|
||||
, toProp $ Apache.siteEnabled "anna.kitenet.net" $ apachecfg "anna.kitenet.net" False
|
||||
[ "DocumentRoot /home/anna/html"
|
||||
, "<Directory /home/anna/html/>"
|
||||
, " Options Indexes ExecCGI"
|
||||
, " AllowOverride None"
|
||||
, Apache.allowAll
|
||||
, "</Directory>"
|
||||
]
|
||||
, alias "sows-ear.kitenet.net"
|
||||
, alias "www.sows-ear.kitenet.net"
|
||||
, toProp $ Apache.siteEnabled "sows-ear.kitenet.net" $ apachecfg "sows-ear.kitenet.net" False
|
||||
[ "ServerAlias www.sows-ear.kitenet.net"
|
||||
, "DocumentRoot /srv/web/sows-ear.kitenet.net"
|
||||
, "<Directory /srv/web/sows-ear.kitenet.net>"
|
||||
, " Options FollowSymLinks"
|
||||
, " AllowOverride None"
|
||||
, Apache.allowAll
|
||||
, "</Directory>"
|
||||
]
|
||||
, alias "wortroot.kitenet.net"
|
||||
, alias "www.wortroot.kitenet.net"
|
||||
, toProp $ Apache.siteEnabled "wortroot.kitenet.net" $ apachecfg "wortroot.kitenet.net" False
|
||||
[ "ServerAlias www.wortroot.kitenet.net"
|
||||
, "DocumentRoot /srv/web/wortroot.kitenet.net"
|
||||
, "<Directory /srv/web/wortroot.kitenet.net>"
|
||||
, " Options FollowSymLinks"
|
||||
, " AllowOverride None"
|
||||
, Apache.allowAll
|
||||
, "</Directory>"
|
||||
]
|
||||
, alias "creeksidepress.com"
|
||||
, toProp $ Apache.siteEnabled "creeksidepress.com" $ apachecfg "creeksidepress.com" False
|
||||
[ "ServerAlias www.creeksidepress.com"
|
||||
, "DocumentRoot /srv/web/www.creeksidepress.com"
|
||||
, "<Directory /srv/web/www.creeksidepress.com>"
|
||||
, " Options FollowSymLinks"
|
||||
, " AllowOverride None"
|
||||
, Apache.allowAll
|
||||
, "</Directory>"
|
||||
]
|
||||
, alias "joey.kitenet.net"
|
||||
, toProp $ Apache.siteEnabled "joey.kitenet.net" $ apachecfg "joey.kitenet.net" False
|
||||
[ "DocumentRoot /home/joey/html"
|
||||
, "<Directory /home/joey/html/>"
|
||||
, " Options Indexes ExecCGI"
|
||||
, " AllowOverride None"
|
||||
, Apache.allowAll
|
||||
, "</Directory>"
|
||||
|
||||
, "RewriteEngine On"
|
||||
|
||||
, "# Old ikiwiki filenames for joey's wiki."
|
||||
, "rewritecond $1 !.*/index$"
|
||||
, "rewriterule (.+).html$ http://joeyh.name/$1/ [l]"
|
||||
|
||||
, "rewritecond $1 !.*/index$"
|
||||
, "rewriterule (.+).rss$ http://joeyh.name/$1/index.rss [l]"
|
||||
|
||||
, "# Redirect all to joeyh.name."
|
||||
, "rewriterule (.*) http://joeyh.name$1 [r]"
|
||||
]
|
||||
]
|
||||
|
||||
userDirHtml :: Property
|
||||
userDirHtml = File.fileProperty "apache userdir is html" (map munge) conf
|
||||
`onChange` Apache.reloaded
|
||||
`requires` (toProp $ Apache.modEnabled "userdir")
|
||||
where
|
||||
munge = replace "public_html" "html"
|
||||
conf = "/etc/apache2/mods-available/userdir.conf"
|
||||
|
|
|
@ -12,6 +12,7 @@ data Info = Info
|
|||
{ _os :: Val System
|
||||
, _privDataFields :: S.Set (PrivDataField, Context)
|
||||
, _sshPubKey :: Val String
|
||||
, _aliases :: S.Set HostName
|
||||
, _dns :: S.Set Dns.Record
|
||||
, _namedconf :: Dns.NamedConfMap
|
||||
, _dockerinfo :: DockerInfo
|
||||
|
@ -19,11 +20,12 @@ data Info = Info
|
|||
deriving (Eq, Show)
|
||||
|
||||
instance Monoid Info where
|
||||
mempty = Info mempty mempty mempty mempty mempty mempty
|
||||
mempty = Info mempty mempty mempty mempty mempty mempty mempty
|
||||
mappend old new = Info
|
||||
{ _os = _os old <> _os new
|
||||
, _privDataFields = _privDataFields old <> _privDataFields new
|
||||
, _sshPubKey = _sshPubKey old <> _sshPubKey new
|
||||
, _aliases = _aliases old <> _aliases new
|
||||
, _dns = _dns old <> _dns new
|
||||
, _namedconf = _namedconf old <> _namedconf new
|
||||
, _dockerinfo = _dockerinfo old <> _dockerinfo new
|
||||
|
|
Loading…
Reference in New Issue