propellor 0.8.1 → 0.8.2
raw patch · 13 files changed
+727/−109 lines, 13 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ Propellor.Info: aliasMap :: [Host] -> Map HostName Host
+ Propellor.Info: findAlias :: [Host] -> HostName -> Maybe Host
+ Propellor.Property.Apache: allowAll :: String
+ Propellor.Property.File: hasPrivContent' :: (String -> FilePath -> IO ()) -> FilePath -> Context -> Property
+ Propellor.Property.Hostname: searchDomain :: Property
+ Propellor.Property.Postfix: dedupCf :: [String] -> [String]
+ Propellor.Property.Postfix: dedupMainCf :: Property
+ Propellor.Property.Postfix: getMainCf :: String -> IO (Maybe String)
+ Propellor.Property.Postfix: mainCf :: (String, String) -> Property
+ Propellor.Property.Postfix: mainCfFile :: FilePath
+ Propellor.Property.Postfix: mainCfIsSet :: String -> IO Bool
+ Propellor.Property.Postfix: mappedFile :: FilePath -> (FilePath -> Property) -> Property
+ Propellor.Property.Postfix: newaliases :: Property
+ Propellor.Property.Postfix: reloaded :: Property
+ Propellor.Property.Postfix: restarted :: Property
+ Propellor.Property.SiteSpecific.JoeySites: backupsBackedupTo :: [Host] -> HostName -> FilePath -> Property
+ Propellor.Property.SiteSpecific.JoeySites: hasJoeyCAChain :: Property
+ Propellor.Property.SiteSpecific.JoeySites: kiteMailServer :: Property
+ Propellor.Property.SiteSpecific.JoeySites: kitenetHttps :: Property
+ Propellor.Property.SiteSpecific.JoeySites: legacyWebSites :: Property
+ Propellor.Property.SiteSpecific.JoeySites: podcatcher :: Property
+ Propellor.Property.SiteSpecific.JoeySites: rsyncNetBackup :: [Host] -> Property
+ Propellor.Property.SiteSpecific.JoeySites: userDirHtml :: Property
- Propellor.Property.Apache: siteCfg :: HostName -> FilePath
+ Propellor.Property.Apache: siteCfg :: HostName -> [FilePath]
- Propellor.Property.SiteSpecific.JoeySites: annexWebSite :: [Host] -> RepoUrl -> HostName -> AnnexUUID -> [(String, RepoUrl)] -> Property
+ Propellor.Property.SiteSpecific.JoeySites: annexWebSite :: RepoUrl -> HostName -> AnnexUUID -> [(String, RepoUrl)] -> Property
Files
- CHANGELOG +13/−0
- config-joey.hs +103/−52
- debian/changelog +13/−0
- propellor.cabal +1/−1
- src/Propellor/Info.hs +15/−4
- src/Propellor/Property/Apache.hs +31/−7
- src/Propellor/Property/Cron.hs +24/−12
- src/Propellor/Property/File.hs +17/−13
- src/Propellor/Property/Hostname.hs +24/−0
- src/Propellor/Property/Obnam.hs +2/−2
- src/Propellor/Property/Postfix.hs +103/−7
- src/Propellor/Property/SiteSpecific/JoeySites.hs +378/−10
- src/Propellor/Types/Info.hs +3/−1
CHANGELOG view
@@ -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.
config-joey.hs view
@@ -8,6 +8,7 @@ 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.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 @@ & 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 @@ [ "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 @@ & 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 @@ & 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 @@ , 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 @@ `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 @@ -- 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 @@ & 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 @@ & 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 @@ & 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"
debian/changelog view
@@ -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.
propellor.cabal view
@@ -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>
src/Propellor/Info.hs view
@@ -43,11 +43,15 @@ -- 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 @@ 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
src/Propellor/Property/Apache.hs view
@@ -10,20 +10,21 @@ 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 @@ `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 @@ ] `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>"+ ]
src/Propellor/Property/Cron.hs view
@@ -4,6 +4,7 @@ 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 @@ -- -- 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 @@ -- | 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
src/Propellor/Property/File.hs view
@@ -18,28 +18,32 @@ -- 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- where- desc = "privcontent " ++ f+hasPrivContent = hasPrivContent' writeFileProtected --- | Leaves the file world-readable.+-- | Leaves the file at its default or current mode,+-- allowing "private" data to be read.+--+-- Use with caution! hasPrivContentExposed :: FilePath -> Context -> Property-hasPrivContentExposed f context = hasPrivContent f context `onChange`- mode f (combineModes (ownerWriteMode:readModes))+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+ -- | 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
src/Propellor/Property/Hostname.hs view
@@ -3,10 +3,14 @@ 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 @@ 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 @@ 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
src/Propellor/Property/Obnam.hs view
@@ -33,8 +33,8 @@ -- > [ "--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
src/Propellor/Property/Postfix.hs view
@@ -2,24 +2,120 @@ 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
src/Propellor/Property/SiteSpecific/JoeySites.hs view
@@ -14,12 +14,14 @@ 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 @@ , " <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 @@ [ 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 @@ , 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 @@ 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 @@ 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 @@ , 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 @@ 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"
src/Propellor/Types/Info.hs view
@@ -12,6 +12,7 @@ { _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 @@ 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