summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoey Hess2015-02-12 12:35:26 -0400
committerJoey Hess2015-02-12 12:35:26 -0400
commitcb67eb1d08ec4cae991c2933624f12fbb68a7b03 (patch)
treecb0dccfee84fa16ef2343487866d1ca08e146ec3 /src
parent9a26a49f510f6880d1e19ad6e4393e8d54395240 (diff)
parentda77276378ecbed7d6434145793bfb209c731b76 (diff)
Merge branch 'joeyconfig'
Conflicts: privdata.joey/privdata.gpg
Diffstat (limited to 'src')
-rw-r--r--src/Propellor/PropAccum.hs15
-rw-r--r--src/Propellor/Property/Apache.hs16
-rw-r--r--src/Propellor/Property/Cron.hs38
-rw-r--r--src/Propellor/Property/File.hs2
-rw-r--r--src/Propellor/Property/Git.hs2
-rw-r--r--src/Propellor/Property/HostingProvider/Linode.hs9
-rw-r--r--src/Propellor/Property/Obnam.hs6
-rw-r--r--src/Propellor/Property/Postfix.hs35
-rw-r--r--src/Propellor/Property/SiteSpecific/GitAnnexBuilder.hs10
-rw-r--r--src/Propellor/Property/SiteSpecific/JoeySites.hs97
-rw-r--r--src/Propellor/Property/Ssh.hs39
-rw-r--r--src/Propellor/Property/Tor.hs80
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