From 293732fa1b9979b5c92b77edef11627f29477433 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sun, 7 Feb 2016 19:32:23 -0400 Subject: fix warning --- src/Propellor/Property/Obnam.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/Propellor/Property/Obnam.hs b/src/Propellor/Property/Obnam.hs index 9a391967..92c97f18 100644 --- a/src/Propellor/Property/Obnam.hs +++ b/src/Propellor/Property/Obnam.hs @@ -62,23 +62,23 @@ backup' dir crontimes params numclients = cronjob `describe` desc unwords $ catMaybes [ if numclients == OnlyClient -- forcelock fails if repo does not exist yet - then Just $ forcelock ++ " 2>/dev/null ;" + then Just $ forcelockcmd ++ " 2>/dev/null ;" else Nothing - , Just backup + , Just backupcmd , if any isKeepParam params - then Just $ "&& " ++ forget + then Just $ "&& " ++ forgetcmd else Nothing ] - forcelock = unwords $ + forcelockcmd = unwords $ [ "obnam" , "force-lock" ] ++ map shellEscape params - backup = unwords $ + backupcmd = unwords $ [ "obnam" , "backup" , shellEscape dir ] ++ map shellEscape params - forget = unwords $ + forgetcmd = unwords $ [ "obnam" , "forget" ] ++ map shellEscape params -- cgit v1.2.3 From 42042aefc13e235b36a28d5235a37ab4326a2400 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sun, 7 Feb 2016 19:33:57 -0400 Subject: propellor spin --- config-joey.hs | 6 ++++++ src/Propellor/Property/Apache.hs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/config-joey.hs b/config-joey.hs index 75333d78..5c3d376b 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -19,6 +19,7 @@ import qualified Propellor.Property.OpenId as OpenId import qualified Propellor.Property.Git as Git import qualified Propellor.Property.Postfix as Postfix import qualified Propellor.Property.Apache as Apache +import qualified Propellor.Property.LetsEncrypt as LetsEncrypt import qualified Propellor.Property.Grub as Grub import qualified Propellor.Property.Obnam as Obnam import qualified Propellor.Property.Gpg as Gpg @@ -332,6 +333,11 @@ kite = standardSystemUnhardened "kite.kitenet.net" Testing "amd64" & branchableSecondary & Dns.secondaryFor ["animx"] hosts "animx.eu.org" + -- testing + & Apache.httpsVirtualHost "letsencrypt.joeyh.name" "/var/www/html" + (LetsEncrypt.AgreeTOS (Just "id@joeyh.name")) + & alias "letsencrypt.joeyh.name" + elephant :: Host elephant = standardSystem "elephant.kitenet.net" Unstable "amd64" [ "Storage, big data, and backups, omnomnom!" diff --git a/src/Propellor/Property/Apache.hs b/src/Propellor/Property/Apache.hs index e841be9e..aba5770e 100644 --- a/src/Propellor/Property/Apache.hs +++ b/src/Propellor/Property/Apache.hs @@ -155,7 +155,7 @@ virtualHost' domain (Port p) docroot addedcfg = siteEnabled domain $ -- Example: -- -- > httpsVirtualHost "example.com" "/var/www" --- > (LetsEncrypt.AgreeTos (Just "me@my.domain")) +-- > (LetsEncrypt.AgreeTOS (Just "me@my.domain")) httpsVirtualHost :: Domain -> WebRoot -> LetsEncrypt.AgreeTOS -> Property NoInfo httpsVirtualHost domain docroot letos = httpsVirtualHost' domain docroot letos [] -- cgit v1.2.3 From 6e1f85f4d01de3b26cc09ff6380e51f82ce56bf4 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sun, 7 Feb 2016 19:39:22 -0400 Subject: typo --- src/Propellor/Property/Apache.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Propellor/Property/Apache.hs b/src/Propellor/Property/Apache.hs index aba5770e..38d47b94 100644 --- a/src/Propellor/Property/Apache.hs +++ b/src/Propellor/Property/Apache.hs @@ -185,7 +185,7 @@ httpsVirtualHost' domain docroot letos addedcfg = setup , File.hasContent cf $ vhost (Port 443) [ "SSLEngine on" , "SSLCertificateFile " ++ certfile - , "SSLCertificateKeyFile" ++ privkeyfile + , "SSLCertificateKeyFile " ++ privkeyfile , "SSLCertificateChainFile " ++ chainfile ] -- always reload; the cert has changed -- cgit v1.2.3 From e2bd3ece23976240c56dd522f04b5e6f4211828e Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sun, 7 Feb 2016 19:53:12 -0400 Subject: propellor spin --- src/Propellor/Property/Apache.hs | 21 ++++++++++++--------- src/Propellor/Property/LetsEncrypt.hs | 24 ++++++++++++++---------- 2 files changed, 26 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/Propellor/Property/Apache.hs b/src/Propellor/Property/Apache.hs index 38d47b94..c2c32a3b 100644 --- a/src/Propellor/Property/Apache.hs +++ b/src/Propellor/Property/Apache.hs @@ -179,20 +179,23 @@ httpsVirtualHost' domain docroot letos addedcfg = setup -- Everything else redirects to https , "RewriteRule ^/(.*) https://" ++ domain ++ "/$1 [L,R,NE]" ] - certinstaller _domain certfile privkeyfile chainfile _fullchainfile = + certinstaller :: LetsEncrypt.CertInstaller + certinstaller newcert _domain certfile privkeyfile chainfile _fullchainfile = 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 - , reloaded + , File.hasContent cf sslvhost + `onChange` reloaded + -- always reload when the cert has changed + , check (return newcert :: IO Bool) reloaded ] where cf = sslconffile "letsencrypt" + sslvhost = vhost (Port 443) + [ "SSLEngine on" + , "SSLCertificateFile " ++ certfile + , "SSLCertificateKeyFile " ++ privkeyfile + , "SSLCertificateChainFile " ++ chainfile + ] sslconffile s = "/etc/apache2/sites-available/ssl/" ++ domain ++ "/" ++ s ++ ".conf" vhost (Port p) ls = [ "" diff --git a/src/Propellor/Property/LetsEncrypt.hs b/src/Propellor/Property/LetsEncrypt.hs index 651cffd9..2df290be 100644 --- a/src/Propellor/Property/LetsEncrypt.hs +++ b/src/Propellor/Property/LetsEncrypt.hs @@ -26,11 +26,11 @@ type WebRoot = FilePath -- 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). -- +-- This also handles renewing the certificate. +-- 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 [] @@ -48,9 +48,8 @@ letsEncrypt' (AgreeTOS memail) domain domains webroot certinstaller = if ok then do endstats <- liftIO getstats - if startstats == endstats - then return NoChange - else ensureProperty certsinstalled + ensureProperty $ + certsinstalled (startstats /= endstats) else do liftIO $ hPutStr stderr transcript return FailedChange @@ -80,8 +79,9 @@ letsEncrypt' (AgreeTOS memail) domain domains webroot certinstaller = s <- getFileStatus f return (fileID s, deviceID s, fileMode s, fileSize s, modificationTime s) - certsinstalled = propertyList ("certs installed") $ - flip map alldomains $ \d -> certinstaller d + certsinstalled newcert = propertyList ("certs installed") $ + flip map alldomains $ \d -> certinstaller + newcert d (certFile d) (privKeyFile d) (chainFile d) @@ -91,7 +91,11 @@ letsEncrypt' (AgreeTOS memail) domain domains webroot certinstaller = -- -- 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 +-- +-- The Bool is True when a new cerficate was just obtained. +-- But, this is also run when the certificate has not changed, so that +-- any changes to the property will take effect. +type CertInstaller = Bool -> Domain -> CertFile -> PrivKeyFile -> ChainFile -> FullChainFile -> Property NoInfo -- | Locations of certificate files generated by lets encrypt. type CertFile = FilePath -- cgit v1.2.3 From efbb3d0e126721e0f9487f194379806c37f1988e Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sun, 7 Feb 2016 21:57:35 -0400 Subject: rethought how letsencrypt cert change is handled Simply use onChange to let any other property by run after letsencrypt gets/renews the cert. Much simpler and allows for revertable properties too! --- src/Propellor/Property/Apache.hs | 21 +++++++-------- src/Propellor/Property/LetsEncrypt.hs | 51 +++++++++++++---------------------- 2 files changed, 28 insertions(+), 44 deletions(-) (limited to 'src') diff --git a/src/Propellor/Property/Apache.hs b/src/Propellor/Property/Apache.hs index c2c32a3b..d0bcadfa 100644 --- a/src/Propellor/Property/Apache.hs +++ b/src/Propellor/Property/Apache.hs @@ -161,12 +161,12 @@ 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 +httpsVirtualHost' domain docroot letos addedcfg = setuphttp `requires` modEnabled "rewrite" `requires` modEnabled "ssl" - `before` LetsEncrypt.letsEncrypt letos domain docroot certinstaller + `before` setuphttps where - setup = siteEnabled' domain $ + setuphttp = 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. @@ -179,22 +179,21 @@ httpsVirtualHost' domain docroot letos addedcfg = setup -- Everything else redirects to https , "RewriteRule ^/(.*) https://" ++ domain ++ "/$1 [L,R,NE]" ] - certinstaller :: LetsEncrypt.CertInstaller - certinstaller newcert _domain certfile privkeyfile chainfile _fullchainfile = - combineProperties (domain ++ " ssl cert installed") + setuphttps = LetsEncrypt.letsEncrypt letos domain docroot + `onChange` combineProperties (domain ++ " ssl cert installed") [ File.dirExists (takeDirectory cf) , File.hasContent cf sslvhost `onChange` reloaded - -- always reload when the cert has changed - , check (return newcert :: IO Bool) reloaded + -- always reload since the cert has changed + , reloaded ] where cf = sslconffile "letsencrypt" sslvhost = vhost (Port 443) [ "SSLEngine on" - , "SSLCertificateFile " ++ certfile - , "SSLCertificateKeyFile " ++ privkeyfile - , "SSLCertificateChainFile " ++ chainfile + , "SSLCertificateFile " ++ LetsEncrypt.certFile domain + , "SSLCertificateKeyFile " ++ LetsEncrypt.privKeyFile domain + , "SSLCertificateChainFile " ++ LetsEncrypt.chainFile domain ] sslconffile s = "/etc/apache2/sites-available/ssl/" ++ domain ++ "/" ++ s ++ ".conf" vhost (Port p) ls = diff --git a/src/Propellor/Property/LetsEncrypt.hs b/src/Propellor/Property/LetsEncrypt.hs index 2df290be..d0dbc4e7 100644 --- a/src/Propellor/Property/LetsEncrypt.hs +++ b/src/Propellor/Property/LetsEncrypt.hs @@ -23,22 +23,29 @@ type WebRoot = FilePath -- -- 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. +-- not modify the web server's configuration in any way; this only obtains +-- the certificate it does not make the web server use it. -- -- This also handles renewing the certificate. -- 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 +-- This property returns `MadeChange` when the certificate is initially +-- obtained, and when it's renewed. So, it can be combined with a property +-- to make the webserver (or other server) use the certificate: +-- +-- > letsEncrypt (AgreeTOS (Just "me@example.com")) "example.com" "/var/www" +-- > `onChange` Apache.reload +-- +-- See `Propellor.Property.Apache.httpsVirtualHost` for a simpler way to +-- use letsencrypt, that is built on top of this. +letsEncrypt :: AgreeTOS -> Domain -> WebRoot -> 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 = +letsEncrypt' :: AgreeTOS -> Domain -> [Domain] -> WebRoot -> Property NoInfo +letsEncrypt' (AgreeTOS memail) domain domains webroot = prop `requires` installed where prop = property desc $ do @@ -48,8 +55,9 @@ letsEncrypt' (AgreeTOS memail) domain domains webroot certinstaller = if ok then do endstats <- liftIO getstats - ensureProperty $ - certsinstalled (startstats /= endstats) + if startstats /= endstats + then return MadeChange + else return NoChange else do liftIO $ hPutStr stderr transcript return FailedChange @@ -78,31 +86,8 @@ letsEncrypt' (AgreeTOS memail) domain domains webroot certinstaller = statfile f = catchMaybeIO $ do s <- getFileStatus f return (fileID s, deviceID s, fileMode s, fileSize s, modificationTime s) - - certsinstalled newcert = propertyList ("certs installed") $ - flip map alldomains $ \d -> certinstaller - newcert 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. --- --- The Bool is True when a new cerficate was just obtained. --- But, this is also run when the certificate has not changed, so that --- any changes to the property will take effect. -type CertInstaller = Bool -> 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 +-- | The cerificate files that letsencrypt will make available for a domain. liveCertDir :: Domain -> FilePath liveCertDir d = "/etc/letsencrypt/live" d -- cgit v1.2.3 From 97fdc43f8a49c87c730471442cf2117bf0a75d64 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sun, 7 Feb 2016 22:01:17 -0400 Subject: property is revertable --- config-joey.hs | 2 -- src/Propellor/Property/Apache.hs | 17 +++++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/config-joey.hs b/config-joey.hs index fc7bd681..5c3d376b 100644 --- a/config-joey.hs +++ b/config-joey.hs @@ -337,8 +337,6 @@ kite = standardSystemUnhardened "kite.kitenet.net" Testing "amd64" & Apache.httpsVirtualHost "letsencrypt.joeyh.name" "/var/www/html" (LetsEncrypt.AgreeTOS (Just "id@joeyh.name")) & alias "letsencrypt.joeyh.name" - -- to revert above, partially: - -- ! Apache.virtualHost "letsencrypt.joeyh.name" (Port 443) "/var/www/html" elephant :: Host elephant = standardSystem "elephant.kitenet.net" Unstable "amd64" diff --git a/src/Propellor/Property/Apache.hs b/src/Propellor/Property/Apache.hs index d0bcadfa..dee7a5fc 100644 --- a/src/Propellor/Property/Apache.hs +++ b/src/Propellor/Property/Apache.hs @@ -156,16 +156,21 @@ virtualHost' domain (Port p) docroot addedcfg = siteEnabled domain $ -- -- > httpsVirtualHost "example.com" "/var/www" -- > (LetsEncrypt.AgreeTOS (Just "me@my.domain")) -httpsVirtualHost :: Domain -> WebRoot -> LetsEncrypt.AgreeTOS -> Property NoInfo +-- +-- Note that reverting this property does not remove the certificate from +-- letsencrypt's cert store. +httpsVirtualHost :: Domain -> WebRoot -> LetsEncrypt.AgreeTOS -> RevertableProperty NoInfo 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 = setuphttp - `requires` modEnabled "rewrite" - `requires` modEnabled "ssl" - `before` setuphttps +httpsVirtualHost' :: Domain -> WebRoot -> LetsEncrypt.AgreeTOS -> [ConfigLine] -> RevertableProperty NoInfo +httpsVirtualHost' domain docroot letos addedcfg = setup teardown where + setup = setuphttp + `requires` modEnabled "rewrite" + `requires` modEnabled "ssl" + `before` setuphttps + teardown = siteDisabled domain setuphttp = siteEnabled' domain $ -- The sslconffile is only created after letsencrypt gets -- the cert. The "*" is needed to make apache not error -- cgit v1.2.3 From b493414d30c7aef37af904e55316436554fe54b2 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sun, 7 Feb 2016 22:17:33 -0400 Subject: wording --- src/Propellor/Property/LetsEncrypt.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/Propellor/Property/LetsEncrypt.hs b/src/Propellor/Property/LetsEncrypt.hs index d0dbc4e7..d5528c64 100644 --- a/src/Propellor/Property/LetsEncrypt.hs +++ b/src/Propellor/Property/LetsEncrypt.hs @@ -37,8 +37,8 @@ type WebRoot = FilePath -- > letsEncrypt (AgreeTOS (Just "me@example.com")) "example.com" "/var/www" -- > `onChange` Apache.reload -- --- See `Propellor.Property.Apache.httpsVirtualHost` for a simpler way to --- use letsencrypt, that is built on top of this. +-- See `Propellor.Property.Apache.httpsVirtualHost` for a more complete +-- integration of apache with letsencrypt, that's built on top of this. letsEncrypt :: AgreeTOS -> Domain -> WebRoot -> Property NoInfo letsEncrypt tos domain = letsEncrypt' tos domain [] -- cgit v1.2.3