diff options
| author | Joey Hess <joeyh@joeyh.name> | 2015-02-12 12:35:26 -0400 |
|---|---|---|
| committer | Joey Hess <joeyh@joeyh.name> | 2015-02-12 12:35:26 -0400 |
| commit | cb67eb1d08ec4cae991c2933624f12fbb68a7b03 (patch) | |
| tree | cb0dccfee84fa16ef2343487866d1ca08e146ec3 /src | |
| parent | 9a26a49f510f6880d1e19ad6e4393e8d54395240 (diff) | |
| parent | da77276378ecbed7d6434145793bfb209c731b76 (diff) | |
Merge branch 'joeyconfig'
Conflicts:
privdata.joey/privdata.gpg
Diffstat (limited to 'src')
| -rw-r--r-- | src/Propellor/PropAccum.hs | 15 | ||||
| -rw-r--r-- | src/Propellor/Property/Apache.hs | 16 | ||||
| -rw-r--r-- | src/Propellor/Property/Cron.hs | 38 | ||||
| -rw-r--r-- | src/Propellor/Property/File.hs | 2 | ||||
| -rw-r--r-- | src/Propellor/Property/Git.hs | 2 | ||||
| -rw-r--r-- | src/Propellor/Property/HostingProvider/Linode.hs | 9 | ||||
| -rw-r--r-- | src/Propellor/Property/Obnam.hs | 6 | ||||
| -rw-r--r-- | src/Propellor/Property/Postfix.hs | 35 | ||||
| -rw-r--r-- | src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs | 10 | ||||
| -rw-r--r-- | src/Propellor/Property/SiteSpecific/JoeySites.hs | 97 | ||||
| -rw-r--r-- | src/Propellor/Property/Ssh.hs | 39 | ||||
| -rw-r--r-- | src/Propellor/Property/Tor.hs | 80 |
12 files changed, 268 insertions, 81 deletions
diff --git a/src/Propellor/PropAccum.hs b/src/Propellor/PropAccum.hs index 139f1471..20d083cb 100644 --- a/src/Propellor/PropAccum.hs +++ b/src/Propellor/PropAccum.hs @@ -1,6 +1,13 @@ {-# LANGUAGE PackageImports #-} -module Propellor.PropAccum where +module Propellor.PropAccum + ( host + , props + , PropAccum(..) + , (!) + , PropList + , propigateContainer + ) where import Data.Monoid @@ -47,9 +54,9 @@ instance PropAccum Host where data PropList = PropList [Property HasInfo] instance PropAccum PropList where - PropList l & p = PropList (l ++ [toProp p]) - PropList l &^ p = PropList ([toProp p] ++ l) - getProperties (PropList l) = l + PropList l & p = PropList (toProp p : l) + PropList l &^ p = PropList (l ++ [toProp p]) + getProperties (PropList l) = reverse l -- | Adds a property in reverted form. (!) :: PropAccum h => h -> RevertableProperty -> h diff --git a/src/Propellor/Property/Apache.hs b/src/Propellor/Property/Apache.hs index e598de1f..a7c7e690 100644 --- a/src/Propellor/Property/Apache.hs +++ b/src/Propellor/Property/Apache.hs @@ -70,13 +70,17 @@ reloaded = Service.reloaded "apache2" -- | Configure apache to use SNI to differentiate between -- https hosts. +-- +-- This was off by default in apache 2.2.22. Newver versions enable +-- it by default. This property uses the filename used by the old version. multiSSL :: Property NoInfo -multiSSL = "/etc/apache2/conf.d/ssl" `File.hasContent` - [ "NameVirtualHost *:443" - , "SSLStrictSNIVHostCheck off" - ] - `describe` "apache SNI enabled" - `onChange` reloaded +multiSSL = check (doesDirectoryExist "/etc/apache2/conf.d") $ + "/etc/apache2/conf.d/ssl" `File.hasContent` + [ "NameVirtualHost *:443" + , "SSLStrictSNIVHostCheck off" + ] + `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. diff --git a/src/Propellor/Property/Cron.hs b/src/Propellor/Property/Cron.hs index 15cdd983..fd365c8f 100644 --- a/src/Propellor/Property/Cron.hs +++ b/src/Propellor/Property/Cron.hs @@ -8,18 +8,26 @@ import Utility.FileMode import Data.Char -type CronTimes = String +-- | When to run a cron job. +-- +-- The Daily, Monthly, and Weekly options allow the cron job to be run +-- by anacron, which is useful for non-servers. +data Times + = Times String -- ^ formatted as in crontab(5) + | Daily + | Weekly + | Monthly --- | Installs a cron job, run as a specified user, in a particular --- directory. Note that the Desc must be unique, as it is used for the --- cron.d/ filename. +-- | Installs a cron job, that will run as a specified user in a particular +-- directory. Note that the Desc must be unique, as it is used for the +-- cron job filename. -- -- Only one instance of the cron job is allowed to run at a time, no matter -- how long it runs. This is accomplished using flock locking of the cron -- job file. -- -- The cron job's output will only be emailed if it exits nonzero. -job :: Desc -> CronTimes -> UserName -> FilePath -> String -> Property NoInfo +job :: Desc -> Times -> UserName -> FilePath -> String -> Property NoInfo job desc times user cddir command = combineProperties ("cronned " ++ desc) [ cronjobfile `File.hasContent` [ "# Generated by propellor" @@ -27,8 +35,15 @@ job desc times user cddir command = combineProperties ("cronned " ++ desc) , "SHELL=/bin/sh" , "PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin" , "" - , times ++ "\t" ++ user ++ "\tchronic " ++ shellEscape scriptfile + , case times of + Times t -> t ++ "\t" ++ user ++ "\tchronic " ++ shellEscape scriptfile + _ -> case user of + "root" -> "chronic " ++ shellEscape scriptfile + _ -> "chronic su " ++ user ++ " -c " ++ shellEscape scriptfile ] + , case times of + Times _ -> doNothing + _ -> cronjobfile `File.mode` combineModes (readModes ++ executeModes) -- Use a separate script because it makes the cron job name -- prettier in emails, and also allows running the job manually. , scriptfile `File.hasContent` @@ -44,7 +59,12 @@ job desc times user cddir command = combineProperties ("cronned " ++ desc) `requires` Apt.installed ["util-linux", "moreutils"] where cmdline = "cd " ++ cddir ++ " && ( " ++ command ++ " )" - cronjobfile = "/etc/cron.d/" ++ name + cronjobfile = "/etc" </> cronjobdir </> name + cronjobdir = case times of + Times _ -> "cron.d" + Daily -> "cron.daily" + Weekly -> "cron.weekly" + Monthly -> "cron.monthly" scriptfile = "/usr/local/bin/" ++ name ++ "_cronjob" name = map sanitize desc sanitize c @@ -52,10 +72,10 @@ job desc times user cddir command = combineProperties ("cronned " ++ desc) | otherwise = '_' -- | Installs a cron job, and runs it niced and ioniced. -niceJob :: Desc -> CronTimes -> UserName -> FilePath -> String -> Property NoInfo +niceJob :: Desc -> Times -> UserName -> FilePath -> String -> Property NoInfo niceJob desc times user cddir command = job desc times user cddir ("nice ionice -c 3 sh -c " ++ shellEscape command) -- | Installs a cron job to run propellor. -runPropellor :: CronTimes -> Property NoInfo +runPropellor :: Times -> Property NoInfo runPropellor times = niceJob "propellor" times "root" localdir "./propellor" diff --git a/src/Propellor/Property/File.hs b/src/Propellor/Property/File.hs index 7167d61e..12d9202f 100644 --- a/src/Propellor/Property/File.hs +++ b/src/Propellor/Property/File.hs @@ -21,7 +21,7 @@ hasPrivContent :: IsContext c => FilePath -> c -> Property HasInfo hasPrivContent f = hasPrivContentFrom (PrivDataSourceFile (PrivFile f) f) f -- | Like hasPrivContent, but allows specifying a source --- for PrivData, rather than using PrivDataSourceFile. +-- for PrivData, rather than using PrivDataSourceFile . hasPrivContentFrom :: (IsContext c, IsPrivDataSource s) => s -> FilePath -> c -> Property HasInfo hasPrivContentFrom = hasPrivContent' writeFileProtected diff --git a/src/Propellor/Property/Git.hs b/src/Propellor/Property/Git.hs index c363d8c8..91f1e3ed 100644 --- a/src/Propellor/Property/Git.hs +++ b/src/Propellor/Property/Git.hs @@ -23,7 +23,7 @@ daemonRunning exportdir = setup <!> unsetup `requires` Apt.serviceInstalledRunning "openbsd-inetd" `onChange` - Service.running "openbsd-inetd" + Service.reloaded "openbsd-inetd" `describe` ("git-daemon exporting " ++ exportdir) unsetup = lacksLine conf (mkl "tcp4") `requires` diff --git a/src/Propellor/Property/HostingProvider/Linode.hs b/src/Propellor/Property/HostingProvider/Linode.hs index 90f41bf8..4dd66129 100644 --- a/src/Propellor/Property/HostingProvider/Linode.hs +++ b/src/Propellor/Property/HostingProvider/Linode.hs @@ -2,9 +2,18 @@ module Propellor.Property.HostingProvider.Linode where import Propellor import qualified Propellor.Property.Grub as Grub +import qualified Propellor.Property.File as File +import Utility.FileMode -- | Linode's pv-grub-x86_64 does not currently support booting recent -- Debian kernels compressed with xz. This sets up pv-grub chaing to enable -- it. chainPVGrub :: Grub.TimeoutSecs -> Property NoInfo chainPVGrub = Grub.chainPVGrub "hd0" "xen/xvda" + +-- | Linode disables mlocate's cron job's execute permissions, +-- presumably to avoid disk IO. This ensures it's executable. +mlocateEnabled :: Property NoInfo +mlocateEnabled = "/etc/cron.daily/mlocate" + `File.mode` combineModes (readModes ++ executeModes) + diff --git a/src/Propellor/Property/Obnam.hs b/src/Propellor/Property/Obnam.hs index adaf255c..c066d9f7 100644 --- a/src/Propellor/Property/Obnam.hs +++ b/src/Propellor/Property/Obnam.hs @@ -36,7 +36,7 @@ data NumClients = OnlyClient | MultipleClients -- > `requires` Ssh.keyImported SshRsa "root" (Context hostname) -- -- How awesome is that? -backup :: FilePath -> Cron.CronTimes -> [ObnamParam] -> NumClients -> Property NoInfo +backup :: FilePath -> Cron.Times -> [ObnamParam] -> NumClients -> Property NoInfo backup dir crontimes params numclients = backup' dir crontimes params numclients `requires` restored dir params @@ -46,7 +46,7 @@ backup dir crontimes params numclients = -- -- The gpg secret key will be automatically imported -- into root's keyring using Propellor.Property.Gpg.keyImported -backupEncrypted :: FilePath -> Cron.CronTimes -> [ObnamParam] -> NumClients -> Gpg.GpgKeyId -> Property HasInfo +backupEncrypted :: FilePath -> Cron.Times -> [ObnamParam] -> NumClients -> Gpg.GpgKeyId -> Property HasInfo backupEncrypted dir crontimes params numclients keyid = backup dir crontimes params' numclients `requires` Gpg.keyImported keyid "root" @@ -54,7 +54,7 @@ backupEncrypted dir crontimes params numclients keyid = params' = ("--encrypt-with=" ++ Gpg.getGpgKeyId keyid) : params -- | Does a backup, but does not automatically restore. -backup' :: FilePath -> Cron.CronTimes -> [ObnamParam] -> NumClients -> Property NoInfo +backup' :: FilePath -> Cron.Times -> [ObnamParam] -> NumClients -> Property NoInfo backup' dir crontimes params numclients = cronjob `describe` desc where desc = dir ++ " backed up by obnam" diff --git a/src/Propellor/Property/Postfix.hs b/src/Propellor/Property/Postfix.hs index fbb1ea51..0abd783e 100644 --- a/src/Propellor/Property/Postfix.hs +++ b/src/Propellor/Property/Postfix.hs @@ -4,8 +4,9 @@ module Propellor.Property.Postfix where import Propellor import qualified Propellor.Property.Apt as Apt -import Propellor.Property.File +import qualified Propellor.Property.File as File import qualified Propellor.Property.Service as Service +import qualified Propellor.Property.User as User import qualified Data.Map as M import Data.List @@ -103,7 +104,7 @@ mainCfIsSet name = do -- Note that multiline configurations that continue onto the next line -- are not currently supported. dedupMainCf :: Property NoInfo -dedupMainCf = fileProperty "postfix main.cf dedupped" dedupCf mainCfFile +dedupMainCf = File.fileProperty "postfix main.cf dedupped" dedupCf mainCfFile dedupCf :: [String] -> [String] dedupCf ls = @@ -125,3 +126,33 @@ dedupCf ls = 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 + +-- | Installs saslauthd and configures it for postfix, authenticating +-- against PAM. +-- +-- Does not configure postfix to use it; eg smtpd_sasl_auth_enable = yes +-- needs to be set to enable use. See +-- https://wiki.debian.org/PostfixAndSASL +saslAuthdInstalled :: Property NoInfo +saslAuthdInstalled = setupdaemon + `requires` Service.running "saslauthd" + `requires` postfixgroup + `requires` dirperm + `requires` Apt.installed ["sasl2-bin"] + `requires` smtpdconf + where + setupdaemon = "/etc/default/saslauthd" `File.containsLines` + [ "START=yes" + , "OPTIONS=\"-c -m " ++ dir ++ "\"" + ] + `onChange` Service.restarted "saslauthd" + smtpdconf = "/etc/postfix/sasl/smtpd.conf" `File.containsLines` + [ "pwcheck_method: saslauthd" + , "mech_list: PLAIN LOGIN" + ] + dirperm = check (not <$> doesDirectoryExist dir) $ + cmdProperty "dpkg-statoverride" + [ "--add", "root", "sasl", "710", dir ] + postfixgroup = "postfix" `User.hasGroup` "sasl" + `onChange` restarted + dir = "/var/spool/postfix/var/run/saslauthd" diff --git a/src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs b/src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs index 7fc523f9..102e6a1d 100644 --- a/src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs +++ b/src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs @@ -9,7 +9,7 @@ import qualified Propellor.Property.Cron as Cron import qualified Propellor.Property.Ssh as Ssh import qualified Propellor.Property.File as File import qualified Propellor.Property.Docker as Docker -import Propellor.Property.Cron (CronTimes) +import Propellor.Property.Cron (Times) builduser :: UserName builduser = "builder" @@ -25,7 +25,7 @@ builddir = gitbuilderdir </> "build" type TimeOut = String -- eg, 5h -autobuilder :: Architecture -> CronTimes -> TimeOut -> Property HasInfo +autobuilder :: Architecture -> Times -> TimeOut -> Property HasInfo autobuilder arch crontimes timeout = combineProperties "gitannexbuilder" $ props & Apt.serviceInstalledRunning "cron" & Cron.niceJob "gitannexbuilder" crontimes builduser gitbuilderdir @@ -102,10 +102,10 @@ standardAutoBuilderContainer dockerImage arch buildminute timeout = Docker.conta & User.accountFor builduser & tree arch & buildDepsApt - & autobuilder arch (show buildminute ++ " * * * *") timeout + & autobuilder arch (Cron.Times $ show buildminute ++ " * * * *") timeout & Docker.tweaked -androidAutoBuilderContainer :: (System -> Docker.Image) -> Cron.CronTimes -> TimeOut -> Docker.Container +androidAutoBuilderContainer :: (System -> Docker.Image) -> Times -> TimeOut -> Docker.Container androidAutoBuilderContainer dockerImage crontimes timeout = androidContainer dockerImage "android-git-annex-builder" (tree "android") builddir & Apt.unattendedUpgrades @@ -166,7 +166,7 @@ armelCompanionContainer dockerImage = Docker.container "armel-git-annex-builder- & Ssh.authorizedKeys builduser (Context "armel-git-annex-builder") & Docker.tweaked -armelAutoBuilderContainer :: (System -> Docker.Image) -> Cron.CronTimes -> TimeOut -> Docker.Container +armelAutoBuilderContainer :: (System -> Docker.Image) -> Times -> TimeOut -> Docker.Container armelAutoBuilderContainer dockerImage crontimes timeout = Docker.container "armel-git-annex-builder" (dockerImage $ System (Debian Unstable) "armel") & os (System (Debian Testing) "armel") diff --git a/src/Propellor/Property/SiteSpecific/JoeySites.hs b/src/Propellor/Property/SiteSpecific/JoeySites.hs index 34a5f02f..9644cb72 100644 --- a/src/Propellor/Property/SiteSpecific/JoeySites.hs +++ b/src/Propellor/Property/SiteSpecific/JoeySites.hs @@ -24,6 +24,7 @@ import Data.String.Utils oldUseNetServer :: [Host] -> Property HasInfo oldUseNetServer hosts = propertyList "olduse.net server" $ props + & Apt.installed ["leafnode"] & oldUseNetInstalled "oldusenet-server" & Obnam.latestVersion & oldUseNetBackup @@ -32,7 +33,6 @@ oldUseNetServer hosts = propertyList "olduse.net server" $ props removeDirectoryRecursive newsspool createSymbolicLink (datadir </> "news") newsspool ) - & Apt.installed ["leafnode"] & "/etc/news/leafnode/config" `File.hasContent` [ "# olduse.net configuration (deployed by propellor)" , "expire = 1000000" -- no expiry via texpire @@ -45,8 +45,8 @@ oldUseNetServer hosts = propertyList "olduse.net server" $ props & Apt.serviceInstalledRunning "openbsd-inetd" & File.notPresent "/etc/cron.daily/leafnode" & File.notPresent "/etc/cron.d/leafnode" - & Cron.niceJob "oldusenet-expire" "11 1 * * *" "news" newsspool expirecommand - & Cron.niceJob "oldusenet-uucp" "*/5 * * * *" "news" "/" uucpcommand + & Cron.niceJob "oldusenet-expire" (Cron.Times "11 1 * * *") "news" newsspool expirecommand + & Cron.niceJob "oldusenet-uucp" (Cron.Times "*/5 * * * *") "news" "/" uucpcommand & Apache.siteEnabled "nntp.olduse.net" nntpcfg where newsspool = "/var/spool/news" @@ -65,12 +65,14 @@ oldUseNetServer hosts = propertyList "olduse.net server" $ props , " </Directory>" ] - oldUseNetBackup = Obnam.backup datadir "33 4 * * *" + oldUseNetBackup = Obnam.backup datadir (Cron.Times "33 4 * * *") [ "--repository=sftp://2318@usw-s002.rsync.net/~/olduse.net" , "--client-name=spool" + , "--ssh-key=" ++ keyfile ] Obnam.OnlyClient - `requires` Ssh.keyImported SshRsa "root" (Context "olduse.net") + `requires` Ssh.keyImported' (Just keyfile) SshRsa "root" (Context "olduse.net") `requires` Ssh.knownHost hosts "usw-s002.rsync.net" "root" + keyfile = "/root/.ssh/olduse.net.key" oldUseNetShellBox :: Property HasInfo oldUseNetShellBox = propertyList "olduse.net shellbox" $ props @@ -113,12 +115,12 @@ mumbleServer :: [Host] -> Property HasInfo mumbleServer hosts = combineProperties hn $ props & Apt.serviceInstalledRunning "mumble-server" & Obnam.latestVersion - & Obnam.backup "/var/lib/mumble-server" "55 5 * * *" - [ "--repository=sftp://joey@usbackup.kitenet.net/~/lib/backup/" ++ hn ++ ".obnam" + & Obnam.backup "/var/lib/mumble-server" (Cron.Times "55 5 * * *") + [ "--repository=sftp://2318@usw-s002.rsync.net/~/" ++ hn ++ ".obnam" , "--client-name=mumble" ] Obnam.OnlyClient `requires` Ssh.keyImported SshRsa "root" (Context hn) - `requires` Ssh.knownHost hosts "usbackup.kitenet.net" "root" + `requires` Ssh.knownHost hosts "usw-s002.rsync.net" "root" & trivial (cmdProperty "chown" ["-R", "mumble-server:mumble-server", "/var/lib/mumble-server"]) where hn = "mumble.debian.net" @@ -129,8 +131,8 @@ obnamLowMem = combineProperties "obnam tuned for low memory use" , "/etc/obnam.conf" `File.containsLines` [ "[config]" , "# Suggested by liw to keep Obnam memory consumption down (at some speed cost)." - , "upload-queue-size = 128" - , "lru-size = 128" + , "upload-queue-size = 96" + , "lru-size = 96" ] ] @@ -138,20 +140,20 @@ obnamLowMem = combineProperties "obnam tuned for low memory use" gitServer :: [Host] -> Property HasInfo gitServer hosts = propertyList "git.kitenet.net setup" $ props & Obnam.latestVersion - & Obnam.backupEncrypted "/srv/git" "33 3 * * *" + & Obnam.backupEncrypted "/srv/git" (Cron.Times "33 3 * * *") [ "--repository=sftp://2318@usw-s002.rsync.net/~/git.kitenet.net" + , "--ssh-key=" ++ sshkey , "--client-name=wren" -- historical ] Obnam.OnlyClient (Gpg.GpgKeyId "1B169BE1") - `requires` Ssh.keyImported SshRsa "root" (Context "git.kitenet.net") + `requires` Ssh.keyImported' (Just sshkey) SshRsa "root" (Context "git.kitenet.net") `requires` Ssh.knownHost hosts "usw-s002.rsync.net" "root" `requires` Ssh.authorizedKeys "family" (Context "git.kitenet.net") `requires` User.accountFor "family" & Apt.installed ["git", "rsync", "gitweb"] - -- backport avoids channel flooding on branch merge - & Apt.installedBackport ["kgb-client"] - -- backport supports ssh event notification - & Apt.installedBackport ["git-annex"] + & Apt.installed ["git-annex"] + & Apt.installed ["kgb-client"] & File.hasPrivContentExposed "/etc/kgb-bot/kgb-client.conf" anyContext + `requires` File.dirExists "/etc/kgb-bot/" & Git.daemonRunning "/srv/git" & "/etc/gitweb.conf" `File.containsLines` [ "$projectroot = '/srv/git';" @@ -168,6 +170,7 @@ gitServer hosts = propertyList "git.kitenet.net setup" $ props & website "git.joeyh.name" & Apache.modEnabled "cgi" where + sshkey = "/root/.ssh/git.kitenet.net.key" website hn = apacheSite hn True [ " DocumentRoot /srv/web/git.kitenet.net/" , " <Directory /srv/web/git.kitenet.net/>" @@ -175,6 +178,7 @@ gitServer hosts = propertyList "git.kitenet.net setup" $ props , " AllowOverride None" , " AddHandler cgi-script .cgi" , " DirectoryIndex index.cgi" + , Apache.allowAll , " </Directory>" , "" , " ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/" @@ -204,7 +208,7 @@ annexWebSite origin hn uuid remotes = propertyList (hn ++" website using git-ann setup = userScriptProperty "joey" setupscript setupscript = [ "cd " ++ shellEscape dir - , "git config annex.uuid " ++ shellEscape uuid + , "git annex reinit " ++ shellEscape uuid ] ++ map addremote remotes ++ [ "git annex get" , "git update-server-info" @@ -217,14 +221,14 @@ annexWebSite origin hn uuid remotes = propertyList (hn ++" website using git-ann , " <Directory /srv/web/"++hn++">" , " Options FollowSymLinks" , " AllowOverride None" + , Apache.allowAll , " </Directory>" , " <Directory /srv/web/"++hn++">" , " Options Indexes FollowSymLinks ExecCGI" , " AllowOverride None" , " AddHandler cgi-script .cgi" , " DirectoryIndex index.html index.cgi" - , " Order allow,deny" - , " allow from all" + , Apache.allowAll , " </Directory>" ] @@ -252,8 +256,7 @@ apachecfg hn withssl middle , " <Directory \"/usr/share/apache2/icons\">" , " Options Indexes MultiViews" , " AllowOverride None" - , " Order allow,deny" - , " Allow from all" + , Apache.allowAll , " </Directory>" , "</VirtualHost>" ] @@ -288,6 +291,22 @@ gitAnnexDistributor = combineProperties "git-annex distributor, including rsync , File.ownerGroup d "joey" "joey" ] +downloads :: [Host] -> Property HasInfo +downloads hosts = annexWebSite "/srv/git/downloads.git" + "downloads.kitenet.net" + "840760dc-08f0-11e2-8c61-576b7e66acfd" + [("eubackup", "ssh://eubackup.kitenet.net/~/lib/downloads/")] + `requires` Ssh.knownHost hosts "eubackup.kitenet.net" "joey" + +tmp :: Property HasInfo +tmp = propertyList "tmp.kitenet.net" $ props + & annexWebSite "/srv/git/joey/tmp.git" + "tmp.kitenet.net" + "26fd6e38-1226-11e2-a75f-ff007033bdba" + [] + & twitRss + & pumpRss + -- Twitter, you kill us. twitRss :: Property HasInfo twitRss = combineProperties "twitter rss" $ props @@ -297,7 +316,7 @@ twitRss = combineProperties "twitter rss" $ props & feed "http://twitter.com/search/realtime?q=olduse+OR+git-annex+OR+debhelper+OR+etckeeper+OR+ikiwiki+-ashley_ikiwiki" "twittergrep" where dir = "/srv/web/tmp.kitenet.net/twitrss" - crontime = "15 * * * *" + crontime = Cron.Times "15 * * * *" feed url desc = Cron.job desc crontime "joey" dir $ "./twitRss " ++ shellEscape url ++ " > " ++ shellEscape ("../" ++ desc ++ ".rss") compiled = userScriptProperty "joey" @@ -311,9 +330,8 @@ twitRss = combineProperties "twitter rss" $ props ] -- Work around for expired ssl cert. --- (no longer expired, TODO remove this and change urls) pumpRss :: Property NoInfo -pumpRss = Cron.job "pump rss" "15 * * * *" "joey" "/srv/web/tmp.kitenet.net/" +pumpRss = Cron.job "pump rss" (Cron.Times "15 * * * *") "joey" "/srv/web/tmp.kitenet.net/" "wget https://pump2rss.com/feed/joeyh@identi.ca.atom -O pump.atom --no-check-certificate 2>/dev/null" ircBouncer :: Property HasInfo @@ -323,7 +341,7 @@ ircBouncer = propertyList "IRC bouncer" $ props & File.dirExists (takeDirectory conf) & File.hasPrivContent conf anyContext & File.ownerGroup conf "znc" "znc" - & Cron.job "znconboot" "@reboot" "znc" "~" "znc" + & Cron.job "znconboot" (Cron.Times "@reboot") "znc" "~" "znc" -- ensure running if it was not already & trivial (userScriptProperty "znc" ["znc || true"]) `describe` "znc running" @@ -347,9 +365,9 @@ githubBackup :: Property HasInfo githubBackup = propertyList "github-backup box" $ props & Apt.installed ["github-backup", "moreutils"] & githubKeys - & Cron.niceJob "github-backup run" "30 4 * * *" "joey" + & Cron.niceJob "github-backup run" (Cron.Times "30 4 * * *") "joey" "/home/joey/lib/backup" backupcmd - & Cron.niceJob "gitriddance" "30 4 * * *" "joey" + & Cron.niceJob "gitriddance" (Cron.Times "30 4 * * *") "joey" "/home/joey/lib/backup" gitriddancecmd where backupcmd = intercalate "&&" $ @@ -385,17 +403,17 @@ githubMirrors = plzuseurl u = "please submit changes to " ++ u ++ " instead of using github pull requests" rsyncNetBackup :: [Host] -> Property NoInfo -rsyncNetBackup hosts = Cron.niceJob "rsync.net copied in daily" "30 5 * * *" +rsyncNetBackup hosts = Cron.niceJob "rsync.net copied in daily" (Cron.Times "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 NoInfo -backupsBackedupTo hosts desthost destdir = Cron.niceJob desc - "1 1 * * 3" "joey" "/" cmd - `requires` Ssh.knownHost hosts desthost "joey" +backupsBackedupFrom :: [Host] -> HostName -> FilePath -> Property NoInfo +backupsBackedupFrom hosts srchost destdir = Cron.niceJob desc + (Cron.Times "@reboot") "joey" "/" cmd + `requires` Ssh.knownHost hosts srchost "joey" where - desc = "backups copied to " ++ desthost ++ " weekly" - cmd = "rsync -az --delete /home/joey/lib/backup " ++ desthost ++ ":" ++ destdir + desc = "backups copied from " ++ srchost ++ " on boot" + cmd = "rsync -az --bwlimit=300K --partial --delete " ++ srchost ++ ":lib/backup/ " ++ destdir </> srchost obnamRepos :: [String] -> Property NoInfo obnamRepos rs = propertyList ("obnam repos for " ++ unwords rs) @@ -408,7 +426,7 @@ obnamRepos rs = propertyList ("obnam repos for " ++ unwords rs) `before` File.ownerGroup d "joey" "joey" podcatcher :: Property NoInfo -podcatcher = Cron.niceJob "podcatcher run hourly" "55 * * * *" +podcatcher = Cron.niceJob "podcatcher run hourly" (Cron.Times "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"] @@ -450,6 +468,8 @@ kiteMailServer = propertyList "kitenet.net mail server" $ props & dkimInstalled + & Postfix.saslAuthdInstalled + & Apt.installed ["maildrop"] & "/etc/maildroprc" `File.hasContent` [ "# Global maildrop filter file (deployed with propellor)" @@ -514,8 +534,13 @@ kiteMailServer = propertyList "kitenet.net mail server" $ props , "# Filter out client relay lines from headers." , "header_checks = pcre:$config_directory/obscure_client_relay.pcre" + , "# Password auth for relaying (used by errol)" + , "smtpd_sasl_auth_enable = yes" + , "smtpd_sasl_security_options = noanonymous" + , "smtpd_sasl_local_domain = kitenet.net" + , "# Enable postgrey." - , "smtpd_recipient_restrictions = permit_tls_clientcerts,permit_mynetworks,reject_unauth_destination,check_policy_service inet:127.0.0.1:10023" + , "smtpd_recipient_restrictions = permit_tls_clientcerts,permit_sasl_authenticated,,permit_mynetworks,reject_unauth_destination,check_policy_service inet:127.0.0.1:10023" , "# Enable spamass-milter, amavis-milter, opendkim" , "smtpd_milters = unix:/spamass/spamass.sock unix:amavis/amavis.sock inet:localhost:8891" diff --git a/src/Propellor/Property/Ssh.hs b/src/Propellor/Property/Ssh.hs index 9290ea1e..f44688c1 100644 --- a/src/Propellor/Property/Ssh.hs +++ b/src/Propellor/Property/Ssh.hs @@ -12,6 +12,7 @@ module Propellor.Property.Ssh ( pubKey, getPubKey, keyImported, + keyImported', knownHost, authorizedKeys, listenPort @@ -147,13 +148,25 @@ getPubKey = asks (_sshPubKey . hostInfo) -- | Sets up a user with a ssh private key and public key pair from the -- PrivData. +-- +-- If the user already has a private/public key, it is left unchanged. keyImported :: IsContext c => SshKeyType -> UserName -> c -> Property HasInfo -keyImported keytype user context = combineProperties desc +keyImported = keyImported' Nothing + +-- | A file can be speficied to write the key to somewhere other than +-- usual. Allows a user to have multiple keys for different roles. +keyImported' :: IsContext c => Maybe FilePath -> SshKeyType -> UserName -> c -> Property HasInfo +keyImported' dest keytype user context = combineProperties desc [ installkey (SshPubKey keytype user) (install writeFile ".pub") , installkey (SshPrivKey keytype user) (install writeFileProtected "") ] where - desc = user ++ " has ssh key (" ++ fromKeyType keytype ++ ")" + desc = unwords $ catMaybes + [ Just user + , Just "has ssh key" + , dest + , Just $ "(" ++ fromKeyType keytype ++ ")" + ] installkey p a = withPrivData p context $ \getkey -> property desc $ getkey a install writer ext key = do @@ -168,9 +181,11 @@ keyImported keytype user context = combineProperties desc , File.ownerGroup (takeDirectory f) user user ] ) - keyfile ext = do - home <- homeDirectory <$> getUserEntryForName user - return $ home </> ".ssh" </> "id_" ++ fromKeyType keytype ++ ext + keyfile ext = case dest of + Nothing -> do + home <- homeDirectory <$> getUserEntryForName user + return $ home </> ".ssh" </> "id_" ++ fromKeyType keytype ++ ext + Just f -> return $ f ++ ext fromKeyType :: SshKeyType -> String fromKeyType SshRsa = "rsa" @@ -178,7 +193,7 @@ fromKeyType SshDsa = "dsa" fromKeyType SshEcdsa = "ecdsa" fromKeyType SshEd25519 = "ed25519" --- | Puts some host's ssh public key(s), as set using 'pubKey', +-- | Puts some host's ssh public key(s), as set using 'pubKey' or 'hostKey' -- into the known_hosts file for a user. knownHost :: [Host] -> HostName -> UserName -> Property NoInfo knownHost hosts hn user = property desc $ @@ -192,6 +207,7 @@ knownHost hosts hn user = property desc $ , f `File.containsLines` (map (\k -> hn ++ " " ++ k) (M.elems m)) , File.ownerGroup f user user + , File.ownerGroup (takeDirectory f) user user ] go _ = do warningMessage $ "no configred pubKey for " ++ hn @@ -215,12 +231,17 @@ authorizedKeys user context = withPrivData (SshAuthorizedKeys user) context $ \g -- | Ensures that a user's authorized_keys contains a line. -- Any other lines in the file are preserved as-is. authorizedKey :: UserName -> String -> Property NoInfo -authorizedKey user l = property (user ++ " has autorized_keys line " ++ l) $ do +authorizedKey user l = property desc $ do f <- liftIO $ dotFile "authorized_keys" user - ensureProperty $ - f `File.containsLine` l + ensureProperty $ combineProperties desc + [ f `File.containsLine` l `requires` File.dirExists (takeDirectory f) `onChange` File.mode f (combineModes [ownerWriteMode, ownerReadMode]) + , File.ownerGroup f user user + , File.ownerGroup (takeDirectory f) user user + ] + where + desc = user ++ " has autorized_keys line " ++ l -- | Makes the ssh server listen on a given port, in addition to any other -- ports it is configured to listen on. diff --git a/src/Propellor/Property/Tor.hs b/src/Propellor/Property/Tor.hs index 9a0fe477..8176e643 100644 --- a/src/Propellor/Property/Tor.hs +++ b/src/Propellor/Property/Tor.hs @@ -7,19 +7,78 @@ import qualified Propellor.Property.Service as Service import Utility.FileMode import System.Posix.Files +import Data.Char type HiddenServiceName = String +type NodeName = String + +-- | Sets up a tor bridge. (Not a relay or exit node.) +-- +-- Uses port 443 isBridge :: Property NoInfo -isBridge = setup `requires` Apt.installed ["tor"] +isBridge = isBridge' [] + +isBridge' :: [String] -> Property NoInfo +isBridge' extraconfig = server config `describe` "tor bridge" where - setup = mainConfig `File.hasContent` - [ "SocksPort 0" + config = + [ "BridgeRelay 1" + , "Exitpolicy reject *:*" , "ORPort 443" - , "BridgeRelay 1" + ] ++ extraconfig + +-- | Sets up a tor relay. +-- +-- Uses port 443 +isRelay :: Property NoInfo +isRelay = isRelay' [] + +isRelay' :: [String] -> Property NoInfo +isRelay' extraconfig = server config + `describe` "tor relay" + where + config = + [ "BridgeRelay 0" , "Exitpolicy reject *:*" - ] `onChange` restarted + , "ORPort 443" + ] ++ extraconfig + +-- | Converts a property like isBridge' or isRelay' to be a named +-- node, with a known private key. +-- +-- This can be moved to a different IP without needing to wait to +-- accumulate trust. +-- +-- The base property can be used to start out and then upgraded to +-- a named property later. +named :: NodeName -> ([String] -> Property NoInfo) -> Property HasInfo +named n basep = p `describe` (getDesc p ++ " " ++ n) + where + p = basep ["Nickname " ++ saneNickname n] + `requires` torPrivKey (Context ("tor " ++ n)) + +-- | A tor server (bridge, relay, or exit) +-- Don't use if you just want to run tor for personal use. +server :: [String] -> Property NoInfo +server extraconfig = setup + `requires` Apt.installed ["tor", "ntp"] + `describe` "tor server" + where + setup = mainConfig `File.hasContent` config + `onChange` restarted + config = + [ "SocksPort 0" + ] ++ extraconfig + +torPrivKey :: Context -> Property HasInfo +torPrivKey context = f `File.hasPrivContent` context + `onChange` File.ownerGroup f user user + -- install tor first, so the directory exists with right perms + `requires` Apt.installed ["tor"] + where + f = "/var/lib/tor/keys/secret_id_key" hiddenServiceAvailable :: HiddenServiceName -> Int -> Property NoInfo hiddenServiceAvailable hn port = hiddenServiceHostName prop @@ -80,3 +139,14 @@ varRun = "/var/run/tor" user :: UserName user = "debian-tor" + +type NickName = String + +-- | Convert String to a valid tor NickName. +saneNickname :: String -> NickName +saneNickname s + | null n = "unnamed" + | otherwise = n + where + legal c = isNumber c || isAsciiUpper c || isAsciiLower c + n = take 19 $ filter legal s |
