From 74a02add291f13b27fb6a1813b870a0730e1142b Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sat, 6 Feb 2016 20:04:00 -0400 Subject: Obnam: To cause old generations to be forgotten, keepParam can be passed to a backup property; this causes obnam forget to be run. --- src/Propellor/Property/Obnam.hs | 45 +++++++++++++++++++++--- src/Propellor/Property/SiteSpecific/JoeySites.hs | 3 ++ 2 files changed, 43 insertions(+), 5 deletions(-) (limited to 'src/Propellor') diff --git a/src/Propellor/Property/Obnam.hs b/src/Propellor/Property/Obnam.hs index 091a6d90..684c424e 100644 --- a/src/Propellor/Property/Obnam.hs +++ b/src/Propellor/Property/Obnam.hs @@ -25,9 +25,7 @@ data NumClients = OnlyClient | MultipleClients -- -- So, this property can be used to deploy a directory of content -- to a host, while also ensuring any changes made to it get backed up. --- And since Obnam encrypts, just make this property depend on a gpg --- key, and tell obnam to use the key, and your data will be backed --- up securely. For example: +-- For example: -- -- > & Obnam.backup "/srv/git" "33 3 * * *" -- > [ "--repository=sftp://2318@usw-s002.rsync.net/~/mygitrepos.obnam" @@ -35,13 +33,16 @@ data NumClients = OnlyClient | MultipleClients -- > `requires` Ssh.keyImported SshRsa "root" (Context hostname) -- -- How awesome is that? +-- +-- Note that this property does not make obnam encrypt the backup +-- repository. backup :: FilePath -> Cron.Times -> [ObnamParam] -> NumClients -> Property NoInfo backup dir crontimes params numclients = backup' dir crontimes params numclients `requires` restored dir params -- | Like backup, but the specified gpg key id is used to encrypt --- the repository. +-- the repository. -- -- The gpg secret key will be automatically imported -- into root's keyring using Propellor.Property.Gpg.keyImported @@ -58,7 +59,7 @@ backup' dir crontimes params numclients = cronjob `describe` desc where desc = dir ++ " backed up by obnam" cronjob = Cron.niceJob ("obnam_backup" ++ dir) crontimes (User "root") "/" $ - intercalate ";" $ catMaybes + intercalate "&&" $ catMaybes [ if numclients == OnlyClient then Just $ unwords $ [ "obnam" @@ -70,6 +71,12 @@ backup' dir crontimes params numclients = cronjob `describe` desc , "backup" , shellEscape dir ] ++ map shellEscape params + , if any isKeepParam params + then Just $ unwords $ + [ "obnam" + , "forget" + ] ++ map shellEscape params + else Nothing ] -- | Restores a directory from an obnam backup. @@ -107,5 +114,33 @@ restored dir params = property (dir ++ " restored by obnam") go , return FailedChange ) +-- | Policy for backup generations to keep. For example, KeepDays 30 will +-- keep the latest backup for each day when a backup was made, and keep the +-- last 30 such backups. When multiple KeepPolicies are combined together, +-- backups meeting any policy are kept. See obnam's man page for details. +data KeepPolicy + = KeepHours Int + | KeepDays Int + | KeepWeeks Int + | KeepMonths Int + | KeepYears Int + +-- | Constructs an ObnamParam that specifies which old backup generations +-- to keep. By default, all generations are kept. However, when this parameter +-- is passed to the `backup` or `backupEncrypted` properties, they will run +-- obnam forget to clean out generations not specified here. +keepParam :: [KeepPolicy] -> ObnamParam +keepParam ps = "--keep=" ++ intercalate "," (map go ps) + where + go (KeepHours n) = mk n 'h' + go (KeepDays n) = mk n 'd' + go (KeepWeeks n) = mk n 'w' + go (KeepMonths n) = mk n 'm' + go (KeepYears n) = mk n 'y' + mk n c = show n ++ [c] + +isKeepParam :: ObnamParam -> Bool +isKeepParam p = "--keep=" `isPrefixOf` p + installed :: Property NoInfo installed = Apt.installed ["obnam"] diff --git a/src/Propellor/Property/SiteSpecific/JoeySites.hs b/src/Propellor/Property/SiteSpecific/JoeySites.hs index 7e6d3f8c..03f2efcb 100644 --- a/src/Propellor/Property/SiteSpecific/JoeySites.hs +++ b/src/Propellor/Property/SiteSpecific/JoeySites.hs @@ -140,6 +140,7 @@ oldUseNetServer hosts = propertyList "olduse.net server" $ props [ "--repository=sftp://2318@usw-s002.rsync.net/~/olduse.net" , "--client-name=spool" , "--ssh-key=" ++ keyfile + , Obnam.keepParam [Obnam.KeepDays 30] ] Obnam.OnlyClient `requires` Ssh.userKeyAt (Just keyfile) (User "root") @@ -194,6 +195,7 @@ mumbleServer hosts = combineProperties hn $ props [ "--repository=sftp://2318@usw-s002.rsync.net/~/" ++ hn ++ ".obnam" , "--ssh-key=" ++ sshkey , "--client-name=mumble" + , Obnam.keepParam [Obnam.KeepDays 30] ] Obnam.OnlyClient `requires` Ssh.userKeyAt (Just sshkey) (User "root") @@ -213,6 +215,7 @@ gitServer hosts = propertyList "git.kitenet.net setup" $ props [ "--repository=sftp://2318@usw-s002.rsync.net/~/git.kitenet.net" , "--ssh-key=" ++ sshkey , "--client-name=wren" -- historical + , Obnam.keepParam [Obnam.KeepDays 30] ] Obnam.OnlyClient (Gpg.GpgKeyId "1B169BE1") `requires` Ssh.userKeyAt (Just sshkey) (User "root") -- cgit v1.2.3 From 5901b4ede5abc75aa17762d86aff80eb2de4960b Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sun, 7 Feb 2016 12:35:56 -0400 Subject: Delete /etc/apt/apt.conf.d/50unattended-upgrades.ucf-dist when unattended-upgrades is installed, to work around #812380 which results in many warnings from apt, including in cron mails. --- debian/changelog | 3 +++ src/Propellor/Property/Apt.hs | 2 ++ 2 files changed, 5 insertions(+) (limited to 'src/Propellor') diff --git a/debian/changelog b/debian/changelog index 2f2a74a8..59a4296d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -5,6 +5,9 @@ propellor (2.15.4) UNRELEASED; urgency=medium Thanks, Sean Whitton. * Obnam: To cause old generations to be forgotten, keepParam can be passed to a backup property; this causes obnam forget to be run. + * Delete /etc/apt/apt.conf.d/50unattended-upgrades.ucf-dist when + unattended-upgrades is installed, to work around #812380 which results + in many warnings from apt, including in cron mails. -- Joey Hess Mon, 18 Jan 2016 13:15:30 -0400 diff --git a/src/Propellor/Property/Apt.hs b/src/Propellor/Property/Apt.hs index d16c4855..f5d08c1d 100644 --- a/src/Propellor/Property/Apt.hs +++ b/src/Propellor/Property/Apt.hs @@ -223,6 +223,8 @@ unattendedUpgrades = enable disable enable = setup True `before` Service.running "cron" `before` configure + -- work around http://bugs.debian.org/812380 + `before` File.notPresent "/etc/apt/apt.conf.d/50unattended-upgrades.ucf-dist" disable = setup False setup enabled = (if enabled then installed else removed) ["unattended-upgrades"] -- cgit v1.2.3 From a4ce5d7aff72e874eda19a7ff9f1f59e91931457 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sun, 7 Feb 2016 13:01:43 -0400 Subject: force-lock fails when repo doesn't exist, so don't && it --- src/Propellor/Property/Obnam.hs | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) (limited to 'src/Propellor') diff --git a/src/Propellor/Property/Obnam.hs b/src/Propellor/Property/Obnam.hs index 684c424e..9a391967 100644 --- a/src/Propellor/Property/Obnam.hs +++ b/src/Propellor/Property/Obnam.hs @@ -59,25 +59,29 @@ backup' dir crontimes params numclients = cronjob `describe` desc where desc = dir ++ " backed up by obnam" cronjob = Cron.niceJob ("obnam_backup" ++ dir) crontimes (User "root") "/" $ - intercalate "&&" $ catMaybes + unwords $ catMaybes [ if numclients == OnlyClient - then Just $ unwords $ - [ "obnam" - , "force-lock" - ] ++ map shellEscape params + -- forcelock fails if repo does not exist yet + then Just $ forcelock ++ " 2>/dev/null ;" else Nothing - , Just $ unwords $ - [ "obnam" - , "backup" - , shellEscape dir - ] ++ map shellEscape params + , Just backup , if any isKeepParam params - then Just $ unwords $ - [ "obnam" - , "forget" - ] ++ map shellEscape params + then Just $ "&& " ++ forget else Nothing ] + forcelock = unwords $ + [ "obnam" + , "force-lock" + ] ++ map shellEscape params + backup = unwords $ + [ "obnam" + , "backup" + , shellEscape dir + ] ++ map shellEscape params + forget = unwords $ + [ "obnam" + , "forget" + ] ++ map shellEscape params -- | Restores a directory from an obnam backup. -- -- cgit v1.2.3 From b845b1c5efc1362dc78baf87747ba8b90fcd97dd Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sun, 7 Feb 2016 16:28:48 -0400 Subject: letsencrypt * Added Propellor.Property.LetsEncrypt * Apache.httpsVirtualHost: New property, setting up a https vhost with the certificate automatically obtained using letsencrypt. --- debian/changelog | 3 + propellor.cabal | 1 + src/Propellor/Property/Apache.hs | 134 ++++++++++++++++++++++++---------- src/Propellor/Property/LetsEncrypt.hs | 115 +++++++++++++++++++++++++++++ 4 files changed, 214 insertions(+), 39 deletions(-) create mode 100644 src/Propellor/Property/LetsEncrypt.hs (limited to 'src/Propellor') diff --git a/debian/changelog b/debian/changelog index 59a4296d..3d18b8fb 100644 --- a/debian/changelog +++ b/debian/changelog @@ -8,6 +8,9 @@ propellor (2.15.4) UNRELEASED; urgency=medium * Delete /etc/apt/apt.conf.d/50unattended-upgrades.ucf-dist when unattended-upgrades is installed, to work around #812380 which results in many warnings from apt, including in cron mails. + * Added Propellor.Property.LetsEncrypt + * Apache.httpsVirtualHost: New property, setting up a https vhost + with the certificate automatically obtained using letsencrypt. -- Joey Hess Mon, 18 Jan 2016 13:15:30 -0400 diff --git a/propellor.cabal b/propellor.cabal index 56b64ce7..fc2d3f1f 100644 --- a/propellor.cabal +++ b/propellor.cabal @@ -89,6 +89,7 @@ Library Propellor.Property.Grub Propellor.Property.Journald Propellor.Property.Kerberos + Propellor.Property.LetsEncrypt Propellor.Property.List Propellor.Property.LightDM Propellor.Property.Locale diff --git a/src/Propellor/Property/Apache.hs b/src/Propellor/Property/Apache.hs index 9e192e84..709c1753 100644 --- a/src/Propellor/Property/Apache.hs +++ b/src/Propellor/Property/Apache.hs @@ -4,6 +4,7 @@ import Propellor.Base import qualified Propellor.Property.File as File import qualified Propellor.Property.Apt as Apt import qualified Propellor.Property.Service as Service +import qualified Propellor.Property.LetsEncrypt as LetsEncrypt installed :: Property NoInfo installed = Apt.installed ["apache2"] @@ -14,48 +15,35 @@ restarted = Service.restarted "apache2" reloaded :: Property NoInfo reloaded = Service.reloaded "apache2" --- | A basic virtual host, publishing a directory, and logging to --- the combined apache log file. -virtualHost :: HostName -> Port -> FilePath -> RevertableProperty NoInfo -virtualHost hn (Port p) docroot = siteEnabled hn - [ "" - , "ServerName "++hn++":"++show p - , "DocumentRoot " ++ docroot - , "ErrorLog /var/log/apache2/error.log" - , "LogLevel warn" - , "CustomLog /var/log/apache2/access.log combined" - , "ServerSignature On" - , "" - ] - type ConfigFile = [String] -siteEnabled :: HostName -> ConfigFile -> RevertableProperty NoInfo -siteEnabled hn cf = enable disable - where - enable = combineProperties ("apache site enabled " ++ hn) - [ siteAvailable hn cf +siteEnabled :: Domain -> ConfigFile -> RevertableProperty NoInfo +siteEnabled domain cf = siteEnabled' domain cf siteDisabled domain + +siteEnabled' :: Domain -> ConfigFile -> Property NoInfo +siteEnabled' domain cf = combineProperties ("apache site enabled " ++ domain) + [ siteAvailable domain cf + `requires` installed + `onChange` reloaded + , check (not <$> isenabled) + (cmdProperty "a2ensite" ["--quiet", domain]) `requires` installed `onChange` reloaded - , check (not <$> isenabled) - (cmdProperty "a2ensite" ["--quiet", hn]) - `requires` installed - `onChange` reloaded - ] - disable = siteDisabled hn - isenabled = boolSystem "a2query" [Param "-q", Param "-s", Param hn] - -siteDisabled :: HostName -> Property NoInfo -siteDisabled hn = combineProperties - ("apache site disabled " ++ hn) - (map File.notPresent (siteCfg hn)) - `onChange` (cmdProperty "a2dissite" ["--quiet", hn] `assume` MadeChange) + ] + where + isenabled = boolSystem "a2query" [Param "-q", Param "-s", Param domain] + +siteDisabled :: Domain -> Property NoInfo +siteDisabled domain = combineProperties + ("apache site disabled " ++ domain) + (map File.notPresent (siteCfg domain)) + `onChange` (cmdProperty "a2dissite" ["--quiet", domain] `assume` MadeChange) `requires` installed `onChange` reloaded -siteAvailable :: HostName -> ConfigFile -> Property NoInfo -siteAvailable hn cf = combineProperties ("apache site available " ++ hn) $ - map (`File.hasContent` (comment:cf)) (siteCfg hn) +siteAvailable :: Domain -> ConfigFile -> Property NoInfo +siteAvailable domain cf = combineProperties ("apache site available " ++ domain) $ + map (`File.hasContent` (comment:cf)) (siteCfg domain) where comment = "# deployed with propellor, do not modify" @@ -86,12 +74,12 @@ listenPorts ps = "/etc/apache2/ports.conf" `File.hasContent` map portline ps -- This is a list of config files because different versions of apache -- use different filenames. Propellor simply writes them all. -siteCfg :: HostName -> [FilePath] -siteCfg hn = +siteCfg :: Domain -> [FilePath] +siteCfg domain = -- Debian pre-2.4 - [ "/etc/apache2/sites-available/" ++ hn + [ "/etc/apache2/sites-available/" ++ domain -- Debian 2.4+ - , "/etc/apache2/sites-available/" ++ hn ++ ".conf" + , "/etc/apache2/sites-available/" ++ domain ++ ".conf" ] -- | Configure apache to use SNI to differentiate between @@ -123,3 +111,71 @@ allowAll = unlines , "Require all granted" , "" ] + +type WebRoot = FilePath + +-- | A basic virtual host, publishing a directory, and logging to +-- the combined apache log file. Not https capable. +virtualHost :: Domain -> Port -> WebRoot -> RevertableProperty NoInfo +virtualHost domain (Port p) docroot = siteEnabled domain + [ "" + , "ServerName "++domain++":"++show p + , "DocumentRoot " ++ docroot + , "ErrorLog /var/log/apache2/error.log" + , "LogLevel warn" + , "CustomLog /var/log/apache2/access.log combined" + , "ServerSignature On" + , "" + ] + +-- | A virtual host using https, with the certificate obtained +-- using `Propellor.Property.LetsEncrypt.letsEncrypt`. +-- +-- http connections are redirected to https. +-- +-- Example: +-- +-- > httpsVirtualHost "example.com" "/var/www" +-- > (LetsEncrypt.AgreeTos (Just "me@my.domain")) +httpsVirtualHost :: Domain -> WebRoot -> LetsEncrypt.AgreeTOS -> Property NoInfo +httpsVirtualHost domain docroot letos = setup + `requires` modEnabled "rewrite" + `requires` modEnabled "ssl" + `before` LetsEncrypt.letsEncrypt letos domain docroot certinstaller + where + setup = siteEnabled' domain $ + -- The sslconffile is only created after letsencrypt gets + -- the cert. The "*" is needed to make apache not error + -- when the file doesn't exist. + ("IncludeOptional " ++ sslconffile "*") + : vhost (Port 80) + [ "RewriteEngine On" + -- Pass through .well-known directory on http for the + -- letsencrypt acme challenge. + , "RewriteRule ^/.well-known/(.*) - [L]" + -- Everything else redirects to https + , "RewriteRule ^/(.*) https://" ++ domain ++ "/$1 [L,R,NE]" + ] + certinstaller _domain certfile privkeyfile chainfile _fullchainfile = + File.hasContent (sslconffile "letsencrypt") + ( vhost (Port 443) + [ "SSLEngine on" + , "SSLCertificateFile " ++ certfile + , "SSLCertificateKeyFile" ++ privkeyfile + , "SSLCertificateChainFile " ++ chainfile + ] + ) + -- always reload; the cert has changed + `before` reloaded + sslconffile s = "/etc/apache2/sites-available/ssl/" ++ domain ++ "/" ++ s ++ ".conf" + vhost (Port p) ls = + [ "" + , "ServerName "++domain++":"++show p + , "DocumentRoot " ++ docroot + , "ErrorLog /var/log/apache2/error.log" + , "LogLevel warn" + , "CustomLog /var/log/apache2/access.log combined" + , "ServerSignature On" + ] ++ ls ++ + [ "" + ] diff --git a/src/Propellor/Property/LetsEncrypt.hs b/src/Propellor/Property/LetsEncrypt.hs new file mode 100644 index 00000000..651cffd9 --- /dev/null +++ b/src/Propellor/Property/LetsEncrypt.hs @@ -0,0 +1,115 @@ +-- | This module uses the letsencrypt reference client. + +module Propellor.Property.LetsEncrypt where + +import Propellor.Base +import qualified Propellor.Property.Apt as Apt + +import System.Posix.Files + +installed :: Property NoInfo +installed = Apt.installed ["letsencrypt"] + +-- | Tell the letsencrypt client that you agree with the Let's Encrypt +-- Subscriber Agreement. Providing an email address is recommended, +-- so that letcencrypt can contact you about problems. +data AgreeTOS = AgreeTOS (Maybe Email) + +type Email = String + +type WebRoot = FilePath + +-- | Uses letsencrypt to obtain a certificate for a domain. +-- +-- This should work with any web server, as long as letsencrypt can +-- write its temp files to the web root. The letsencrypt client does +-- not modify the web server's configuration in any way; instead the +-- `CertInstaller` is used once the client has successfully obtained the +-- certificate. +-- +-- This also handles renewing the certificate, and the `CertInstaller` is +-- also run after renewal. For renewel to work well, propellor needs to be +-- run periodically (at least a couple times per month). +-- +-- See `Propellor.Property.Apache.httpsVirtualHost` for a property built using this. +letsEncrypt :: AgreeTOS -> Domain -> WebRoot -> CertInstaller -> Property NoInfo +letsEncrypt tos domain = letsEncrypt' tos domain [] + +-- | Like `letsEncrypt`, but the certificate can be obtained for multiple +-- domains. +letsEncrypt' :: AgreeTOS -> Domain -> [Domain] -> WebRoot -> CertInstaller -> Property NoInfo +letsEncrypt' (AgreeTOS memail) domain domains webroot certinstaller = + prop `requires` installed + where + prop = property desc $ do + startstats <- liftIO getstats + (transcript, ok) <- liftIO $ + processTranscript "letsencrypt" params Nothing + if ok + then do + endstats <- liftIO getstats + if startstats == endstats + then return NoChange + else ensureProperty certsinstalled + else do + liftIO $ hPutStr stderr transcript + return FailedChange + + desc = "letsencrypt " ++ unwords alldomains + alldomains = domain : domains + params = + [ "certonly" + , "--agree-tos" + , case memail of + Just email -> "--email="++email + Nothing -> "--register-unsafely-without-email" + , "--webroot" + , "--webroot-path", webroot + , "--text" + , "--keep-until-expiring" + ] ++ map (\d -> "--domain="++d) alldomains + + getstats = mapM statcertfiles alldomains + statcertfiles d = mapM statfile + [ certFile d + , privKeyFile d + , chainFile d + , fullChainFile d + ] + statfile f = catchMaybeIO $ do + s <- getFileStatus f + return (fileID s, deviceID s, fileMode s, fileSize s, modificationTime s) + + certsinstalled = propertyList ("certs installed") $ + flip map alldomains $ \d -> certinstaller d + (certFile d) + (privKeyFile d) + (chainFile d) + (fullChainFile d) + +-- | A property that installs a certificate, once letsencrypt obtains it. +-- +-- For example, it could configure the web server to use the certificate +-- files, and restart the web server. +type CertInstaller = Domain -> CertFile -> PrivKeyFile -> ChainFile -> FullChainFile -> Property NoInfo + +-- | Locations of certificate files generated by lets encrypt. +type CertFile = FilePath +type PrivKeyFile = FilePath +type ChainFile = FilePath +type FullChainFile = FilePath + +liveCertDir :: Domain -> FilePath +liveCertDir d = "/etc/letsencrypt/live" d + +certFile :: Domain -> FilePath +certFile d = liveCertDir d "cert.pem" + +privKeyFile :: Domain -> FilePath +privKeyFile d = liveCertDir d "privkey.pem" + +chainFile :: Domain -> FilePath +chainFile d = liveCertDir d "chain.pem" + +fullChainFile :: Domain -> FilePath +fullChainFile d = liveCertDir d "fullchain.pem" -- cgit v1.2.3 From c90282fc7bb77bcba19cdd4adfe96af3fb1162f8 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sun, 7 Feb 2016 16:54:17 -0400 Subject: propellor spin --- src/Propellor/Property/Apache.hs | 35 ++++++++++++++++++++---- src/Propellor/Property/SiteSpecific/JoeySites.hs | 24 ++++++---------- 2 files changed, 38 insertions(+), 21 deletions(-) (limited to 'src/Propellor') diff --git a/src/Propellor/Property/Apache.hs b/src/Propellor/Property/Apache.hs index 709c1753..5b8128a4 100644 --- a/src/Propellor/Property/Apache.hs +++ b/src/Propellor/Property/Apache.hs @@ -15,7 +15,9 @@ restarted = Service.restarted "apache2" reloaded :: Property NoInfo reloaded = Service.reloaded "apache2" -type ConfigFile = [String] +type ConfigLine = String + +type ConfigFile = [ConfigLine] siteEnabled :: Domain -> ConfigFile -> RevertableProperty NoInfo siteEnabled domain cf = siteEnabled' domain cf siteDisabled domain @@ -101,7 +103,7 @@ multiSSL = check (doesDirectoryExist "/etc/apache2/conf.d") $ -- -- Works with multiple versions of apache that have different ways to do -- it. -allowAll :: String +allowAll :: ConfigLine allowAll = unlines [ "" , "Order allow,deny" @@ -112,12 +114,27 @@ allowAll = unlines , "" ] +-- | Config file fragment that can be inserted into a +-- stanza to allow apache to display directory index icons. +iconDir :: ConfigLine +iconDir = unlines + [ "" + , "Options Indexes MultiViews" + , "AllowOverride None" + , allowAll + , " " + ] + type WebRoot = FilePath -- | A basic virtual host, publishing a directory, and logging to -- the combined apache log file. Not https capable. virtualHost :: Domain -> Port -> WebRoot -> RevertableProperty NoInfo -virtualHost domain (Port p) docroot = siteEnabled domain +virtualHost domain (Port p) docroot = virtualHost' domain (Port p) docroot [] + +-- | Like `virtualHost` but with additional config lines added. +virtualHost' :: Domain -> Port -> WebRoot -> [ConfigLine] -> RevertableProperty NoInfo +virtualHost' domain (Port p) docroot addedcfg = siteEnabled domain $ [ "" , "ServerName "++domain++":"++show p , "DocumentRoot " ++ docroot @@ -125,7 +142,9 @@ virtualHost domain (Port p) docroot = siteEnabled domain , "LogLevel warn" , "CustomLog /var/log/apache2/access.log combined" , "ServerSignature On" - , "" + ] + ++ addedcfg ++ + [ "" ] -- | A virtual host using https, with the certificate obtained @@ -138,7 +157,11 @@ virtualHost domain (Port p) docroot = siteEnabled domain -- > httpsVirtualHost "example.com" "/var/www" -- > (LetsEncrypt.AgreeTos (Just "me@my.domain")) httpsVirtualHost :: Domain -> WebRoot -> LetsEncrypt.AgreeTOS -> Property NoInfo -httpsVirtualHost domain docroot letos = setup +httpsVirtualHost domain docroot letos = httpsVirtualHost' domain docroot letos [] + +-- | Like `httpsVirtualHost` but with additional config lines added. +httpsVirtualHost' :: Domain -> WebRoot -> LetsEncrypt.AgreeTOS -> [ConfigLine] -> Property NoInfo +httpsVirtualHost' domain docroot letos addedcfg = setup `requires` modEnabled "rewrite" `requires` modEnabled "ssl" `before` LetsEncrypt.letsEncrypt letos domain docroot certinstaller @@ -176,6 +199,6 @@ httpsVirtualHost domain docroot letos = setup , "LogLevel warn" , "CustomLog /var/log/apache2/access.log combined" , "ServerSignature On" - ] ++ ls ++ + ] ++ ls ++ addedcfg ++ [ "" ] diff --git a/src/Propellor/Property/SiteSpecific/JoeySites.hs b/src/Propellor/Property/SiteSpecific/JoeySites.hs index 03f2efcb..0bb98489 100644 --- a/src/Propellor/Property/SiteSpecific/JoeySites.hs +++ b/src/Propellor/Property/SiteSpecific/JoeySites.hs @@ -18,6 +18,7 @@ import qualified Propellor.Property.Apache as Apache import qualified Propellor.Property.Postfix as Postfix import qualified Propellor.Property.Systemd as Systemd import qualified Propellor.Property.Fail2Ban as Fail2Ban +import qualified Propellor.Property.LetsEncrypt as LetsEncrypt import Utility.FileMode import Data.List @@ -290,24 +291,21 @@ annexWebSite origin hn uuid remotes = propertyList (hn ++" website using git-ann , "git update-server-info" ] addremote (name, url) = "git remote add " ++ shellEscape name ++ " " ++ shellEscape url - setupapache = apacheSite hn True + setupapache = Apache.httpsVirtualHost' hn dir letos [ " ServerAlias www."++hn - , "" - , " DocumentRoot /srv/web/"++hn - , " " - , " Options FollowSymLinks" - , " AllowOverride None" - , Apache.allowAll - , " " - , " " + , Apache.iconDir + , " " , " Options Indexes FollowSymLinks ExecCGI" , " AllowOverride None" , " AddHandler cgi-script .cgi" , " DirectoryIndex index.html index.cgi" - , Apache.allowAll + , Apache.allowAll , " " ] +letos :: LetsEncrypt.AgreeTOS +letos = LetsEncrypt.AgreeTOS (Just "id@joeyh.name") + apacheSite :: HostName -> Bool -> Apache.ConfigFile -> RevertableProperty NoInfo apacheSite hn withssl middle = Apache.siteEnabled hn $ apachecfg hn withssl middle @@ -329,11 +327,7 @@ apachecfg hn withssl middle , " CustomLog /var/log/apache2/access.log combined" , " ServerSignature On" , " " - , " " - , " Options Indexes MultiViews" - , " AllowOverride None" - , Apache.allowAll - , " " + , Apache.iconDir , "" ] where -- cgit v1.2.3 From 648e1e50a015636d129bd4d7357c779688685cc0 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sun, 7 Feb 2016 17:04:34 -0400 Subject: propellor spin --- src/Propellor/Property/Apache.hs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/Propellor') diff --git a/src/Propellor/Property/Apache.hs b/src/Propellor/Property/Apache.hs index 5b8128a4..e841be9e 100644 --- a/src/Propellor/Property/Apache.hs +++ b/src/Propellor/Property/Apache.hs @@ -180,16 +180,19 @@ httpsVirtualHost' domain docroot letos addedcfg = setup , "RewriteRule ^/(.*) https://" ++ domain ++ "/$1 [L,R,NE]" ] certinstaller _domain certfile privkeyfile chainfile _fullchainfile = - File.hasContent (sslconffile "letsencrypt") - ( vhost (Port 443) + combineProperties (domain ++ " ssl cert installed") + [ File.dirExists (takeDirectory cf) + , File.hasContent cf $ vhost (Port 443) [ "SSLEngine on" , "SSLCertificateFile " ++ certfile , "SSLCertificateKeyFile" ++ privkeyfile , "SSLCertificateChainFile " ++ chainfile ] - ) -- always reload; the cert has changed - `before` reloaded + , reloaded + ] + where + cf = sslconffile "letsencrypt" sslconffile s = "/etc/apache2/sites-available/ssl/" ++ domain ++ "/" ++ s ++ ".conf" vhost (Port p) ls = [ "" -- cgit v1.2.3 From 12ff3991270b1aeb9a3822ebbcbee9037cc7f401 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sun, 7 Feb 2016 17:08:06 -0400 Subject: temporarily revert using letsencrypt It's currently not letting me make a cert for kitenet.net as I made too many during testing. --- src/Propellor/Property/SiteSpecific/JoeySites.hs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'src/Propellor') diff --git a/src/Propellor/Property/SiteSpecific/JoeySites.hs b/src/Propellor/Property/SiteSpecific/JoeySites.hs index 0bb98489..03f2efcb 100644 --- a/src/Propellor/Property/SiteSpecific/JoeySites.hs +++ b/src/Propellor/Property/SiteSpecific/JoeySites.hs @@ -18,7 +18,6 @@ import qualified Propellor.Property.Apache as Apache import qualified Propellor.Property.Postfix as Postfix import qualified Propellor.Property.Systemd as Systemd import qualified Propellor.Property.Fail2Ban as Fail2Ban -import qualified Propellor.Property.LetsEncrypt as LetsEncrypt import Utility.FileMode import Data.List @@ -291,21 +290,24 @@ annexWebSite origin hn uuid remotes = propertyList (hn ++" website using git-ann , "git update-server-info" ] addremote (name, url) = "git remote add " ++ shellEscape name ++ " " ++ shellEscape url - setupapache = Apache.httpsVirtualHost' hn dir letos + setupapache = apacheSite hn True [ " ServerAlias www."++hn - , Apache.iconDir - , " " + , "" + , " DocumentRoot /srv/web/"++hn + , " " + , " Options FollowSymLinks" + , " AllowOverride None" + , Apache.allowAll + , " " + , " " , " Options Indexes FollowSymLinks ExecCGI" , " AllowOverride None" , " AddHandler cgi-script .cgi" , " DirectoryIndex index.html index.cgi" - , Apache.allowAll + , Apache.allowAll , " " ] -letos :: LetsEncrypt.AgreeTOS -letos = LetsEncrypt.AgreeTOS (Just "id@joeyh.name") - apacheSite :: HostName -> Bool -> Apache.ConfigFile -> RevertableProperty NoInfo apacheSite hn withssl middle = Apache.siteEnabled hn $ apachecfg hn withssl middle @@ -327,7 +329,11 @@ apachecfg hn withssl middle , " CustomLog /var/log/apache2/access.log combined" , " ServerSignature On" , " " - , Apache.iconDir + , " " + , " Options Indexes MultiViews" + , " AllowOverride None" + , Apache.allowAll + , " " , "" ] where -- cgit v1.2.3