Merge branch 'joeyconfig'

This commit is contained in:
Joey Hess 2014-08-04 01:12:39 -04:00
commit 81f370b9da
13 changed files with 1459 additions and 544 deletions

View File

@ -8,6 +8,7 @@ import qualified Propellor.Property.File as File
import qualified Propellor.Property.Apt as Apt import qualified Propellor.Property.Apt as Apt
import qualified Propellor.Property.Network as Network import qualified Propellor.Property.Network as Network
import qualified Propellor.Property.Ssh as Ssh 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.Cron as Cron
import qualified Propellor.Property.Sudo as Sudo import qualified Propellor.Property.Sudo as Sudo
import qualified Propellor.Property.User as User 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.Postfix as Postfix
import qualified Propellor.Property.Service as Service import qualified Propellor.Property.Service as Service
import qualified Propellor.Property.Grub as Grub 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.DigitalOcean as DigitalOcean
import qualified Propellor.Property.HostingProvider.CloudAtCost as CloudAtCost import qualified Propellor.Property.HostingProvider.CloudAtCost as CloudAtCost
import qualified Propellor.Property.HostingProvider.Linode as Linode import qualified Propellor.Property.HostingProvider.Linode as Linode
@ -41,7 +43,7 @@ hosts = -- (o) `
& Apt.buildDep ["git-annex"] `period` Daily & Apt.buildDep ["git-annex"] `period` Daily
& Docker.configured & Docker.configured
& Docker.docked hosts "android-git-annex" ! Docker.docked hosts "android-git-annex"
, standardSystem "clam.kitenet.net" Unstable "amd64" , standardSystem "clam.kitenet.net" Unstable "amd64"
[ "Unreliable server. Anything here may be lost at any time!" ] [ "Unreliable server. Anything here may be lost at any time!" ]
@ -61,38 +63,70 @@ hosts = -- (o) `
[ "Main git-annex build box." ] [ "Main git-annex build box." ]
& ipv4 "138.38.108.179" & ipv4 "138.38.108.179"
& Hostname.sane
& Apt.unattendedUpgrades & Apt.unattendedUpgrades
& Postfix.satellite & Postfix.satellite
& Docker.configured & Docker.configured
& Docker.docked hosts "amd64-git-annex-builder" & Docker.docked hosts "amd64-git-annex-builder"
& Docker.docked hosts "i386-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" & 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 & Docker.garbageCollected `period` Daily
& Apt.buildDep ["git-annex"] `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!" [ "Welcome to the new kitenet.net server!"
, "This is still under construction and not yet live.." , "This is still under construction and not yet live.."
] ]
& ipv4 "66.228.36.95" & ipv4 "66.228.36.95"
& ipv6 "2600:3c03::f03c:91ff:fe73:b0d2" & ipv6 "2600:3c03::f03c:91ff:fe73:b0d2"
-- & alias "kitenet.net" -- not yet live!
& Apt.installed ["linux-image-amd64"] & Apt.installed ["linux-image-amd64"]
& Linode.chainPVGrub 5 & Linode.chainPVGrub 5
& Hostname.sane
& Apt.unattendedUpgrades & Apt.unattendedUpgrades
& Apt.installed ["systemd"] & Apt.installed ["systemd"]
& Ssh.hostKeys (Context "kitenet.net") & 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" , standardSystem "diatom.kitenet.net" Stable "amd64"
[ "Important stuff that needs not too much memory or CPU." ] [ "Important stuff that needs not too much memory or CPU." ]
& ipv4 "107.170.31.195" & ipv4 "107.170.31.195"
& DigitalOcean.distroKernel & DigitalOcean.distroKernel
& Hostname.sane
& Ssh.hostKeys (Context "diatom.kitenet.net") & Ssh.hostKeys (Context "diatom.kitenet.net")
& Apt.unattendedUpgrades & Apt.unattendedUpgrades
& Apt.serviceInstalledRunning "ntp" & Apt.serviceInstalledRunning "ntp"
@ -103,10 +137,7 @@ hosts = -- (o) `
& Apt.serviceInstalledRunning "swapspace" & Apt.serviceInstalledRunning "swapspace"
& Apt.serviceInstalledRunning "apache2" & Apt.serviceInstalledRunning "apache2"
& File.hasPrivContent "/etc/ssl/certs/web.pem" (Context "kitenet.net") & JoeySites.kitenetHttps
& File.hasPrivContent "/etc/ssl/private/web.pem" (Context "kitenet.net")
& File.hasPrivContent "/etc/ssl/certs/startssl.pem" (Context "kitenet.net")
& Apache.modEnabled "ssl"
& Apache.multiSSL & Apache.multiSSL
& File.ownerGroup "/srv/web" "joey" "joey" & File.ownerGroup "/srv/web" "joey" "joey"
& Apt.installed ["analog"] & Apt.installed ["analog"]
@ -116,14 +147,16 @@ hosts = -- (o) `
& JoeySites.gitServer hosts & JoeySites.gitServer hosts
& alias "downloads.kitenet.net" & alias "downloads.kitenet.net"
& JoeySites.annexWebSite hosts "/srv/git/downloads.git" & JoeySites.annexWebSite "/srv/git/downloads.git"
"downloads.kitenet.net" "downloads.kitenet.net"
"840760dc-08f0-11e2-8c61-576b7e66acfd" "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 & JoeySites.gitAnnexDistributor
& alias "tmp.kitenet.net" & alias "tmp.kitenet.net"
& JoeySites.annexWebSite hosts "/srv/git/joey/tmp.git" & JoeySites.annexWebSite "/srv/git/joey/tmp.git"
"tmp.kitenet.net" "tmp.kitenet.net"
"26fd6e38-1226-11e2-a75f-ff007033bdba" "26fd6e38-1226-11e2-a75f-ff007033bdba"
[] []
@ -148,25 +181,28 @@ hosts = -- (o) `
, let ctx = Context "elephant.kitenet.net" , let ctx = Context "elephant.kitenet.net"
in standardSystem "elephant.kitenet.net" Unstable "amd64" 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" & ipv4 "193.234.225.114"
& Grub.chainPVGrub "hd0,0" "xen/xvda1" 30 & Grub.chainPVGrub "hd0,0" "xen/xvda1" 30
& Hostname.sane
& Postfix.satellite & Postfix.satellite
& Apt.unattendedUpgrades & Apt.unattendedUpgrades
& Ssh.hostKeys ctx & Ssh.hostKeys ctx
& sshPubKey "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAJkoPRhUGT8EId6m37uBdYEtq42VNwslKnc9mmO+89ody066q6seHKeFY6ImfwjcyIjM30RTzEwftuVNQnbEB0="
& Ssh.keyImported SshRsa "joey" ctx & Ssh.keyImported SshRsa "joey" ctx
& Apt.serviceInstalledRunning "swapspace" & Apt.serviceInstalledRunning "swapspace"
& alias "eubackup.kitenet.net" & alias "eubackup.kitenet.net"
& Apt.installed ["obnam", "sshfs", "rsync"] & Apt.installed ["obnam", "sshfs", "rsync"]
& JoeySites.obnamRepos ["wren", "pell", "kite"]
& JoeySites.githubBackup & JoeySites.githubBackup
& JoeySites.obnamRepos ["wren", "pell"] & JoeySites.rsyncNetBackup hosts
& Ssh.knownHost hosts "usw-s002.rsync.net" "joey" & JoeySites.backupsBackedupTo hosts "usbackup.kitenet.net" "lib/backup/eubackup"
& alias "podcatcher.kitenet.net" & alias "podcatcher.kitenet.net"
& Apt.installed ["git-annex"] & JoeySites.podcatcher
& alias "znc.kitenet.net" & alias "znc.kitenet.net"
& JoeySites.ircBouncer & JoeySites.ircBouncer
@ -201,9 +237,9 @@ hosts = -- (o) `
`onChange` Service.restarted "ssh" `onChange` Service.restarted "ssh"
-- temp -- temp
& Docker.docked hosts "amd64-git-annex-builder" ! Docker.docked hosts "amd64-git-annex-builder"
& Docker.docked hosts "i386-git-annex-builder" ! Docker.docked hosts "i386-git-annex-builder"
& Docker.docked hosts "android-git-annex-builder" ! Docker.docked hosts "android-git-annex-builder"
--' __|II| ,. --' __|II| ,.
@ -261,8 +297,17 @@ type Motd = [String]
-- This is my standard system setup. -- This is my standard system setup.
standardSystem :: HostName -> DebianSuite -> Architecture -> Motd -> Host 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) & os (System (Debian suite) arch)
& Hostname.sane
& Hostname.searchDomain
& File.hasContent "/etc/motd" ("":motd++[""]) & File.hasContent "/etc/motd" ("":motd++[""])
& Apt.stdSourcesList `onChange` Apt.upgrade & Apt.stdSourcesList `onChange` Apt.upgrade
& Apt.cacheCleaned & Apt.cacheCleaned
@ -270,10 +315,6 @@ standardSystem hn suite arch motd = host hn
& Apt.installed ["ssh"] & Apt.installed ["ssh"]
& GitHome.installedFor "root" & GitHome.installedFor "root"
& User.hasSomePassword "root" (Context hn) & 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.accountFor "joey"
& User.hasSomePassword "joey" (Context hn) & User.hasSomePassword "joey" (Context hn)
& Sudo.enabledFor "joey" & Sudo.enabledFor "joey"
@ -350,6 +391,7 @@ monsters = -- but do want to track their public keys etc.
& ipv4 "67.223.19.96" & ipv4 "67.223.19.96"
& ipv6 "2001:4978:f:2d9::2" & ipv6 "2001:4978:f:2d9::2"
& alias "backup.kitenet.net" & alias "backup.kitenet.net"
& alias "usbackup.kitenet.net"
& sshPubKey "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAokMXQiX/NZjA1UbhMdgAscnS5dsmy+Q7bWrQ6tsTZ/o+6N/T5cbjoBHOdpypXJI3y/PiJTDJaQtXIhLa8gFg/EvxMnMz/KG9skADW1361JmfCc4BxicQIO2IOOe6eilPr+YsnOwiHwL0vpUnuty39cppuMWVD25GzxXlS6KQsLCvXLzxLLuNnGC43UAM0q4UwQxDtAZEK1dH2o3HMWhgMP2qEQupc24dbhpO3ecxh2C9678a3oGDuDuNf7mLp3s7ptj5qF3onitpJ82U5o7VajaHoygMaSRFeWxP2c13eM57j3bLdLwxVXFhePcKXARu1iuFTLS5uUf3hN6MkQcOGw==" & sshPubKey "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAokMXQiX/NZjA1UbhMdgAscnS5dsmy+Q7bWrQ6tsTZ/o+6N/T5cbjoBHOdpypXJI3y/PiJTDJaQtXIhLa8gFg/EvxMnMz/KG9skADW1361JmfCc4BxicQIO2IOOe6eilPr+YsnOwiHwL0vpUnuty39cppuMWVD25GzxXlS6KQsLCvXLzxLLuNnGC43UAM0q4UwQxDtAZEK1dH2o3HMWhgMP2qEQupc24dbhpO3ecxh2C9678a3oGDuDuNf7mLp3s7ptj5qF3onitpJ82U5o7VajaHoygMaSRFeWxP2c13eM57j3bLdLwxVXFhePcKXARu1iuFTLS5uUf3hN6MkQcOGw=="
, host "wren.kitenet.net" , host "wren.kitenet.net"
& ipv4 "80.68.85.49" & ipv4 "80.68.85.49"
@ -359,37 +401,46 @@ monsters = -- but do want to track their public keys etc.
& alias "ftp.kitenet.net" & alias "ftp.kitenet.net"
& alias "mail.kitenet.net" & alias "mail.kitenet.net"
& alias "smtp.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" & alias "bitlbee.kitenet.net"
{- Remaining services on kite: {- Remaining services on kite:
-
- / = ready to go on kite.kitenet.net
- -
- mail - mail
- postfix - /postfix
- postgrey - /postgrey
- mailman - mailman
- spamassassin - /spamassassin
- sqwebmail - sqwebmail (cannot use this with dovecot, alternatives?)
- courier - /imap server
- imap - /pop server
- tls - /apache
- apache - (need to re-rsync /srv/web to new kite.kitenet.net
- some static websites - server before decommissioning)
- bitlbee - bitlbee (EOL?)
- prosody - prosody (EOL?)
- (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)
- ftpd (EOL) - ftpd (EOL)
- -
- user shell stuff: - Pre-transition:
- pine, zsh, make, git-annex, myrepos, ... - - 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" , host "mouse.kitenet.net"
& ipv6 "2001:4830:1600:492::2" & ipv6 "2001:4830:1600:492::2"

13
debian/changelog vendored
View File

@ -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 propellor (0.8.1) unstable; urgency=medium
* Run apt-get update in initial bootstrap. * Run apt-get update in initial bootstrap.

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
Name: propellor Name: propellor
Version: 0.8.1 Version: 0.8.2
Cabal-Version: >= 1.6 Cabal-Version: >= 1.6
License: BSD3 License: BSD3
Maintainer: Joey Hess <joey@kitenet.net> Maintainer: Joey Hess <joey@kitenet.net>

View File

@ -43,11 +43,15 @@ ipv6 = addDNS . Address . IPv6
-- problems with CNAMEs, and also means that when multiple hosts have the -- problems with CNAMEs, and also means that when multiple hosts have the
-- same alias, a DNS round-robin is automatically set up. -- same alias, a DNS round-robin is automatically set up.
alias :: Domain -> Property 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 :: Record -> Property
addDNS r = pureInfoProperty (rdesc r) $ addDNS r = pureInfoProperty (rdesc r) $ mempty { _dns = S.singleton r }
mempty { _dns = S.singleton r }
where where
rdesc (CNAME d) = unwords ["alias", ddesc d] rdesc (CNAME d) = unwords ["alias", ddesc d]
rdesc (Address (IPv4 addr)) = unwords ["ipv4", addr] rdesc (Address (IPv4 addr)) = unwords ["ipv4", addr]
@ -71,8 +75,15 @@ getSshPubKey = askInfo _sshPubKey
hostMap :: [Host] -> M.Map HostName Host hostMap :: [Host] -> M.Map HostName Host
hostMap l = M.fromList $ zip (map hostName l) l 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 :: [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 :: Info -> [IPAddr]
getAddresses = mapMaybe getIPAddr . S.toList . _dns getAddresses = mapMaybe getIPAddr . S.toList . _dns

View File

@ -10,20 +10,21 @@ type ConfigFile = [String]
siteEnabled :: HostName -> ConfigFile -> RevertableProperty siteEnabled :: HostName -> ConfigFile -> RevertableProperty
siteEnabled hn cf = RevertableProperty enable disable siteEnabled hn cf = RevertableProperty enable disable
where where
enable = trivial $ cmdProperty "a2ensite" ["--quiet", hn] enable = trivial (cmdProperty "a2ensite" ["--quiet", hn])
`describe` ("apache site enabled " ++ hn) `describe` ("apache site enabled " ++ hn)
`requires` siteAvailable hn cf `requires` siteAvailable hn cf
`requires` installed `requires` installed
`onChange` reloaded `onChange` reloaded
disable = trivial $ File.notPresent (siteCfg hn) disable = trivial $ combineProperties
`describe` ("apache site disabled " ++ hn) ("apache site disabled " ++ hn)
(map File.notPresent (siteCfg hn))
`onChange` cmdProperty "a2dissite" ["--quiet", hn] `onChange` cmdProperty "a2dissite" ["--quiet", hn]
`requires` installed `requires` installed
`onChange` reloaded `onChange` reloaded
siteAvailable :: HostName -> ConfigFile -> Property siteAvailable :: HostName -> ConfigFile -> Property
siteAvailable hn cf = siteCfg hn `File.hasContent` (comment:cf) siteAvailable hn cf = combineProperties ("apache site available " ++ hn) $
`describe` ("apache site available " ++ hn) map (`File.hasContent` (comment:cf)) (siteCfg hn)
where where
comment = "# deployed with propellor, do not modify" comment = "# deployed with propellor, do not modify"
@ -39,8 +40,15 @@ modEnabled modname = RevertableProperty enable disable
`requires` installed `requires` installed
`onChange` reloaded `onChange` reloaded
siteCfg :: HostName -> FilePath -- This is a list of config files because different versions of apache
siteCfg hn = "/etc/apache2/sites-available/" ++ hn -- 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 :: Property
installed = Apt.installed ["apache2"] installed = Apt.installed ["apache2"]
@ -60,3 +68,19 @@ multiSSL = "/etc/apache2/conf.d/ssl" `File.hasContent`
] ]
`describe` "apache SNI enabled" `describe` "apache SNI enabled"
`onChange` reloaded `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>"
]

View File

@ -4,6 +4,7 @@ import Propellor
import qualified Propellor.Property.File as File import qualified Propellor.Property.File as File
import qualified Propellor.Property.Apt as Apt import qualified Propellor.Property.Apt as Apt
import Utility.SafeCommand import Utility.SafeCommand
import Utility.FileMode
import Data.Char import Data.Char
@ -19,22 +20,33 @@ type CronTimes = String
-- --
-- The cron job's output will only be emailed if it exits nonzero. -- The cron job's output will only be emailed if it exits nonzero.
job :: Desc -> CronTimes -> UserName -> FilePath -> String -> Property job :: Desc -> CronTimes -> UserName -> FilePath -> String -> Property
job desc times user cddir command = cronjobfile `File.hasContent` job desc times user cddir command = combineProperties ("cronned " ++ desc)
[ cronjobfile `File.hasContent`
[ "# Generated by propellor" [ "# Generated by propellor"
, "" , ""
, "SHELL=/bin/sh" , "SHELL=/bin/sh"
, "PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin" , "PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
, "" , ""
, times ++ "\t" ++ user ++ "\t" , times ++ "\t" ++ user ++ "\tchronic " ++ shellEscape scriptfile
++ "chronic flock -n " ++ shellEscape cronjobfile ]
-- 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 ++ " sh -c " ++ shellEscape cmdline
] ]
, scriptfile `File.mode` combineModes (readModes ++ executeModes)
]
`requires` Apt.serviceInstalledRunning "cron" `requires` Apt.serviceInstalledRunning "cron"
`requires` Apt.installed ["util-linux", "moreutils"] `requires` Apt.installed ["util-linux", "moreutils"]
`describe` ("cronned " ++ desc)
where where
cmdline = "cd " ++ cddir ++ " && ( " ++ command ++ " )" 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 sanitize c
| isAlphaNum c = c | isAlphaNum c = c
| otherwise = '_' | otherwise = '_'
@ -42,7 +54,7 @@ job desc times user cddir command = cronjobfile `File.hasContent`
-- | Installs a cron job, and runs it niced and ioniced. -- | Installs a cron job, and runs it niced and ioniced.
niceJob :: Desc -> CronTimes -> UserName -> FilePath -> String -> Property niceJob :: Desc -> CronTimes -> UserName -> FilePath -> String -> Property
niceJob desc times user cddir command = job desc times user cddir 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. -- | Installs a cron job to run propellor.
runPropellor :: CronTimes -> Property runPropellor :: CronTimes -> Property

View File

@ -18,28 +18,32 @@ f `hasContent` newcontent = fileProperty ("replace " ++ f)
-- The file's permissions are preserved if the file already existed. -- The file's permissions are preserved if the file already existed.
-- Otherwise, they're set to 600. -- Otherwise, they're set to 600.
hasPrivContent :: FilePath -> Context -> Property hasPrivContent :: FilePath -> Context -> Property
hasPrivContent f context = withPrivData (PrivFile f) context $ \getcontent -> 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 -> property desc $ getcontent $ \privcontent ->
ensureProperty $ fileProperty' writeFileProtected desc ensureProperty $ fileProperty' writer desc
(\_oldcontent -> lines privcontent) f (\_oldcontent -> lines privcontent) f
where where
desc = "privcontent " ++ f 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. -- | Ensures that a line is present in a file, adding it to the end if not.
containsLine :: FilePath -> Line -> Property containsLine :: FilePath -> Line -> Property
f `containsLine` l = f `containsLines` [l] f `containsLine` l = f `containsLines` [l]
containsLines :: FilePath -> [Line] -> Property 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 where
go ls go content = content ++ filter (`notElem` content) ls
| all (`elem` ls) l = ls
| otherwise = ls++l
-- | Ensures that a line is not present in a file. -- | 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 -- Note that the file is ensured to exist, so if it doesn't, an empty

View File

@ -3,10 +3,14 @@ module Propellor.Property.Hostname where
import Propellor import Propellor
import qualified Propellor.Property.File as File import qualified Propellor.Property.File as File
import Data.List
-- | Ensures that the hostname is set using best practices. -- | Ensures that the hostname is set using best practices.
-- --
-- Configures /etc/hostname and the current hostname. -- 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 -- /etc/hosts is also configured, with an entry for 127.0.1.1, which is
-- standard at least on Debian to set the FDQN. -- 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] else Just $ trivial $ hostsline "127.0.1.1" [hn, basehost]
, Just $ trivial $ hostsline "127.0.0.1" ["localhost"] , Just $ trivial $ hostsline "127.0.0.1" ["localhost"]
, Just $ trivial $ cmdProperty "hostname" [basehost] , Just $ trivial $ cmdProperty "hostname" [basehost]
, Just $ "/etc/mailname" `File.hasContent`
[if null domain then hn else domain]
] ]
hostsline ip names = File.fileProperty desc hostsline ip names = File.fileProperty desc
@ -37,3 +43,21 @@ setTo hn = combineProperties desc go
addhostsline ip names ls = addhostsline ip names ls =
(ip ++ "\t" ++ (unwords names)) : filter (not . hasip ip) ls (ip ++ "\t" ++ (unwords names)) : filter (not . hasip ip) ls
hasip ip l = headMaybe (words l) == Just ip 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

View File

@ -34,7 +34,7 @@ data NumClients = OnlyClient | MultipleClients
-- > , "--encrypt-with=1B169BE1" -- > , "--encrypt-with=1B169BE1"
-- > ] Obnam.OnlyClient -- > ] Obnam.OnlyClient
-- > `requires` Gpg.keyImported "1B169BE1" "root" -- > `requires` Gpg.keyImported "1B169BE1" "root"
-- > `requires` Ssh.keyImported SshRsa "root" -- > `requires` Ssh.keyImported SshRsa "root" (Context hostname)
-- --
-- How awesome is that? -- How awesome is that?
backup :: FilePath -> Cron.CronTimes -> [ObnamParam] -> NumClients -> Property backup :: FilePath -> Cron.CronTimes -> [ObnamParam] -> NumClients -> Property

View File

@ -2,24 +2,120 @@ module Propellor.Property.Postfix where
import Propellor import Propellor
import qualified Propellor.Property.Apt as Apt 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 :: Property
installed = Apt.serviceInstalledRunning "postfix" installed = Apt.serviceInstalledRunning "postfix"
restarted :: Property
restarted = Service.restarted "postfix"
reloaded :: Property
reloaded = Service.reloaded "postfix"
-- | Configures postfix as a satellite system, which -- | 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 -- The smarthost may refuse to relay mail on to other domains, without
-- futher coniguration/keys. But this should be enough to get cron job -- futher coniguration/keys. But this should be enough to get cron job
-- mail flowing to a place where it will be seen. -- mail flowing to a place where it will be seen.
satellite :: Property satellite :: Property
satellite = setup `requires` installed satellite = check (not <$> mainCfIsSet "relayhost") setup
`requires` installed
where where
setup = trivial $ property "postfix satellite system" $ do setup = trivial $ property "postfix satellite system" $ do
hn <- asks hostName hn <- asks hostName
ensureProperty $ Apt.reConfigure "postfix" let (_, domain) = separate (== '.') hn
ensureProperties
[ Apt.reConfigure "postfix"
[ ("postfix/main_mailer_type", "select", "Satellite system") [ ("postfix/main_mailer_type", "select", "Satellite system")
, ("postfix/root_address", "string", "root") , ("postfix/root_address", "string", "root")
, ("postfix/destinations", "string", " ") , ("postfix/destinations", "string", " ")
, ("postfix/mailname", "string", hn) , ("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

View File

@ -14,12 +14,14 @@ import qualified Propellor.Property.Service as Service
import qualified Propellor.Property.User as User import qualified Propellor.Property.User as User
import qualified Propellor.Property.Obnam as Obnam import qualified Propellor.Property.Obnam as Obnam
import qualified Propellor.Property.Apache as Apache import qualified Propellor.Property.Apache as Apache
import qualified Propellor.Property.Postfix as Postfix
import Utility.SafeCommand import Utility.SafeCommand
import Utility.FileMode import Utility.FileMode
import Utility.Path import Utility.Path
import Data.List import Data.List
import System.Posix.Files import System.Posix.Files
import Data.String.Utils
oldUseNetServer :: [Host] -> Property oldUseNetServer :: [Host] -> Property
oldUseNetServer hosts = propertyList ("olduse.net server") oldUseNetServer hosts = propertyList ("olduse.net server")
@ -59,9 +61,7 @@ oldUseNetServer hosts = propertyList ("olduse.net server")
, " <Directory " ++ datadir ++ "/>" , " <Directory " ++ datadir ++ "/>"
, " Options Indexes FollowSymlinks" , " Options Indexes FollowSymlinks"
, " AllowOverride None" , " AllowOverride None"
-- I had this in the file before. , Apache.allowAll
-- This may be needed by a newer version of apache?
--, " Require all granted"
, " </Directory>" , " </Directory>"
] ]
] ]
@ -114,11 +114,11 @@ mumbleServer hosts = combineProperties hn
[ Apt.serviceInstalledRunning "mumble-server" [ Apt.serviceInstalledRunning "mumble-server"
, Obnam.latestVersion , Obnam.latestVersion
, Obnam.backup "/var/lib/mumble-server" "55 5 * * *" , 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" , "--client-name=mumble"
] Obnam.OnlyClient ] Obnam.OnlyClient
`requires` Ssh.keyImported SshRsa "root" (Context hn) `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"] , trivial $ cmdProperty "chown" ["-R", "mumble-server:mumble-server", "/var/lib/mumble-server"]
] ]
where where
@ -142,7 +142,7 @@ gitServer hosts = propertyList "git.kitenet.net setup"
, Obnam.backup "/srv/git" "33 3 * * *" , Obnam.backup "/srv/git" "33 3 * * *"
[ "--repository=sftp://2318@usw-s002.rsync.net/~/git.kitenet.net" [ "--repository=sftp://2318@usw-s002.rsync.net/~/git.kitenet.net"
, "--encrypt-with=1B169BE1" , "--encrypt-with=1B169BE1"
, "--client-name=wren" , "--client-name=wren" -- historical
] Obnam.OnlyClient ] Obnam.OnlyClient
`requires` Gpg.keyImported "1B169BE1" "root" `requires` Gpg.keyImported "1B169BE1" "root"
`requires` Ssh.keyImported SshRsa "root" (Context "git.kitenet.net") `requires` Ssh.keyImported SshRsa "root" (Context "git.kitenet.net")
@ -191,8 +191,8 @@ gitServer hosts = propertyList "git.kitenet.net setup"
type AnnexUUID = String type AnnexUUID = String
-- | A website, with files coming from a git-annex repository. -- | A website, with files coming from a git-annex repository.
annexWebSite :: [Host] -> Git.RepoUrl -> HostName -> AnnexUUID -> [(String, Git.RepoUrl)] -> Property annexWebSite :: Git.RepoUrl -> HostName -> AnnexUUID -> [(String, Git.RepoUrl)] -> Property
annexWebSite hosts origin hn uuid remotes = propertyList (hn ++" website using git-annex") annexWebSite origin hn uuid remotes = propertyList (hn ++" website using git-annex")
[ Git.cloned "joey" origin dir Nothing [ Git.cloned "joey" origin dir Nothing
`onChange` setup `onChange` setup
, postupdatehook `File.hasContent` , postupdatehook `File.hasContent`
@ -206,8 +206,6 @@ annexWebSite hosts origin hn uuid remotes = propertyList (hn ++" website using g
dir = "/srv/web/" ++ hn dir = "/srv/web/" ++ hn
postupdatehook = dir </> ".git/hooks/post-update" postupdatehook = dir </> ".git/hooks/post-update"
setup = userScriptProperty "joey" setupscript setup = userScriptProperty "joey" setupscript
`requires` Ssh.keyImported SshRsa "joey" (Context hn)
`requires` Ssh.knownHost hosts "turtle.kitenet.net" "joey"
setupscript = setupscript =
[ "cd " ++ shellEscape dir [ "cd " ++ shellEscape dir
, "git config annex.uuid " ++ shellEscape uuid , "git config annex.uuid " ++ shellEscape uuid
@ -348,7 +346,26 @@ githubBackup = propertyList "github-backup box"
, let f = "/home/joey/.github-keys" , let f = "/home/joey/.github-keys"
in File.hasPrivContent f anyContext in File.hasPrivContent f anyContext
`onChange` File.ownerGroup f "joey" "joey" `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 :: [String] -> Property
obnamRepos rs = propertyList ("obnam repos for " ++ unwords rs) obnamRepos rs = propertyList ("obnam repos for " ++ unwords rs)
@ -360,3 +377,354 @@ obnamRepos rs = propertyList ("obnam repos for " ++ unwords rs)
mkdir d = File.dirExists d mkdir d = File.dirExists d
`before` File.ownerGroup d "joey" "joey" `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"

View File

@ -12,6 +12,7 @@ data Info = Info
{ _os :: Val System { _os :: Val System
, _privDataFields :: S.Set (PrivDataField, Context) , _privDataFields :: S.Set (PrivDataField, Context)
, _sshPubKey :: Val String , _sshPubKey :: Val String
, _aliases :: S.Set HostName
, _dns :: S.Set Dns.Record , _dns :: S.Set Dns.Record
, _namedconf :: Dns.NamedConfMap , _namedconf :: Dns.NamedConfMap
, _dockerinfo :: DockerInfo , _dockerinfo :: DockerInfo
@ -19,11 +20,12 @@ data Info = Info
deriving (Eq, Show) deriving (Eq, Show)
instance Monoid Info where 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 mappend old new = Info
{ _os = _os old <> _os new { _os = _os old <> _os new
, _privDataFields = _privDataFields old <> _privDataFields new , _privDataFields = _privDataFields old <> _privDataFields new
, _sshPubKey = _sshPubKey old <> _sshPubKey new , _sshPubKey = _sshPubKey old <> _sshPubKey new
, _aliases = _aliases old <> _aliases new
, _dns = _dns old <> _dns new , _dns = _dns old <> _dns new
, _namedconf = _namedconf old <> _namedconf new , _namedconf = _namedconf old <> _namedconf new
, _dockerinfo = _dockerinfo old <> _dockerinfo new , _dockerinfo = _dockerinfo old <> _dockerinfo new