diff options
author | Andrew Cady <d@jerkface.net> | 2016-01-26 22:26:22 -0500 |
---|---|---|
committer | Andrew Cady <d@jerkface.net> | 2016-01-27 07:26:34 -0500 |
commit | eaaebe924ba1cebc40a160a28f3779fd08c2181b (patch) | |
tree | f3c23824da3985e46ebf01f81422140611360e95 | |
parent | f48e818311f9ee8cf5437b8f2080713fb0203688 (diff) |
clean up http-served files after challenges complete
-rw-r--r-- | acme-certify.cabal | 3 | ||||
-rw-r--r-- | src/Network/ACME.hs | 43 |
2 files changed, 23 insertions, 23 deletions
diff --git a/acme-certify.cabal b/acme-certify.cabal index b9e9818..01b0e7d 100644 --- a/acme-certify.cabal +++ b/acme-certify.cabal | |||
@@ -17,7 +17,8 @@ library | |||
17 | build-depends: base >= 4.7 && < 5, | 17 | build-depends: base >= 4.7 && < 5, |
18 | cryptonite, aeson, bytestring, base64-bytestring, SHA, | 18 | cryptonite, aeson, bytestring, base64-bytestring, SHA, |
19 | mtl, text, HsOpenSSL, wreq, lens, lens-aeson, time, | 19 | mtl, text, HsOpenSSL, wreq, lens, lens-aeson, time, |
20 | email-validate, pipes, directory, network-uri, errors | 20 | email-validate, pipes, directory, network-uri, errors, |
21 | resourcet | ||
21 | default-language: Haskell2010 | 22 | default-language: Haskell2010 |
22 | 23 | ||
23 | executable acme-certify | 24 | executable acme-certify |
diff --git a/src/Network/ACME.hs b/src/Network/ACME.hs index e08d5b9..85a27e4 100644 --- a/src/Network/ACME.hs +++ b/src/Network/ACME.hs | |||
@@ -43,8 +43,7 @@ import OpenSSL.X509 (readDerX509, X509) | |||
43 | import Data.List | 43 | import Data.List |
44 | import Control.Error | 44 | import Control.Error |
45 | import Control.Arrow | 45 | import Control.Arrow |
46 | 46 | import Control.Monad.Trans.Resource hiding (register) | |
47 | type HttpProvisioner = URI -> ByteString -> IO () | ||
48 | 47 | ||
49 | genReq :: Keys -> [DomainName] -> IO CSR | 48 | genReq :: Keys -> [DomainName] -> IO CSR |
50 | genReq _ [] = error "genReq called with zero domains" | 49 | genReq _ [] = error "genReq called with zero domains" |
@@ -60,11 +59,15 @@ genReq (Keys priv pub) domains@(domain:_) = withOpenSSL $ do | |||
60 | where | 59 | where |
61 | nidSubjectAltName = 85 | 60 | nidSubjectAltName = 85 |
62 | 61 | ||
62 | type HttpProvisioner = URI -> ByteString -> ResIO () | ||
63 | fileProvisioner :: WritableDir -> HttpProvisioner | 63 | fileProvisioner :: WritableDir -> HttpProvisioner |
64 | fileProvisioner challengeDir = BC.writeFile . uToF | 64 | fileProvisioner challengeDir uri thumbtoken = do |
65 | void $ allocate (return f) removeFile | ||
66 | liftIO $ BC.writeFile f thumbtoken | ||
67 | |||
65 | where | 68 | where |
66 | uToF = (coerce challengeDir </>) . takeWhileEnd (/= '/') . uriPath | 69 | f = (coerce challengeDir </>) . takeWhileEnd (/= '/') . uriPath $ uri |
67 | takeWhileEnd f = reverse . takeWhile f . reverse | 70 | takeWhileEnd s = reverse . takeWhile s . reverse |
68 | 71 | ||
69 | acmeChallengeURI :: DomainName -> BC.ByteString -> URI | 72 | acmeChallengeURI :: DomainName -> BC.ByteString -> URI |
70 | acmeChallengeURI dom tok = URI | 73 | acmeChallengeURI dom tok = URI |
@@ -76,23 +79,20 @@ acmeChallengeURI dom tok = URI | |||
76 | 79 | ||
77 | certify :: URI -> Keys -> Maybe (URI, EmailAddress) -> HttpProvisioner -> CSR -> IO (Either String X509) | 80 | certify :: URI -> Keys -> Maybe (URI, EmailAddress) -> HttpProvisioner -> CSR -> IO (Either String X509) |
78 | certify directoryUrl keys reg provision certReq = | 81 | certify directoryUrl keys reg provision certReq = |
79 | |||
80 | (mapM readDerX509 =<<) $ runACME directoryUrl keys $ do | 82 | (mapM readDerX509 =<<) $ runACME directoryUrl keys $ do |
81 | |||
82 | forM_ reg $ uncurry register >=> statusReport | 83 | forM_ reg $ uncurry register >=> statusReport |
83 | 84 | ||
84 | let performChallenge domain (ChallengeRequest nextUri token thumbtoken) = do | 85 | let performChallenge domain (ChallengeRequest nextUri token thumbtoken) = do |
85 | liftIO $ provision (acmeChallengeURI domain token) thumbtoken | 86 | liftResourceT $ provision (acmeChallengeURI domain token) thumbtoken |
86 | notifyChallenge nextUri thumbtoken >>= statusReport >>= ncErrorReport | 87 | notifyChallenge nextUri thumbtoken >>= statusReport >>= ncErrorReport |
87 | 88 | ||
88 | challengeResultLinks <- forM (csrDomains certReq) $ \dom -> challengeRequest dom >>= | 89 | cr dom = challengeRequest dom >>= statusReport >>= extractCR >>= performChallenge dom |
89 | statusReport >>= | ||
90 | extractCR >>= | ||
91 | performChallenge dom | ||
92 | 90 | ||
93 | runExceptT $ do | 91 | runResourceT $ do |
94 | ExceptT $ pollResults challengeResultLinks <&> left ("certificate receipt was not attempted because a challenge failed: " ++) | 92 | challengeResultLinks <- forM (csrDomains certReq) cr |
95 | ExceptT $ retrieveCert certReq >>= statusReport <&> checkCertResponse | 93 | lift . runExceptT $ do |
94 | ExceptT $ pollResults challengeResultLinks <&> left ("certificate receipt was not attempted because a challenge failed: " ++) | ||
95 | ExceptT $ retrieveCert certReq >>= statusReport <&> checkCertResponse | ||
96 | 96 | ||
97 | pollResults :: [Response LC.ByteString] -> ACME (Either String ()) | 97 | pollResults :: [Response LC.ByteString] -> ACME (Either String ()) |
98 | pollResults [] = return $ Right () | 98 | pollResults [] = return $ Right () |
@@ -127,15 +127,14 @@ domainToString = T.unpack . decodeUtf8 . Text.Domain.Validate.toByteString | |||
127 | 127 | ||
128 | canProvision :: WritableDir -> DomainName -> IO Bool | 128 | canProvision :: WritableDir -> DomainName -> IO Bool |
129 | canProvision challengeDir domain = do | 129 | canProvision challengeDir domain = do |
130 | randomish <- fromString . show <$> getPOSIXTime | 130 | token <- (".test." ++) . show <$> getPOSIXTime |
131 | 131 | ||
132 | let absFile = coerce challengeDir </> relFile | 132 | let uri = acmeChallengeURI domain (fromString token) |
133 | relFile = ".test." ++ show randomish | ||
134 | 133 | ||
135 | LC.writeFile absFile randomish | 134 | r <- runResourceT $ do |
136 | r <- W.get $ "http://" ++ domainToString domain </> ".well-known/acme-challenge" </> relFile | 135 | fileProvisioner challengeDir uri (fromString token) |
137 | removeFile absFile | 136 | liftIO $ W.get (show uri) |
138 | return $ r ^. responseBody == randomish | 137 | return $ r ^. responseBody == fromString token |
139 | 138 | ||
140 | 139 | ||
141 | extractCR :: MonadReader Env m => Response LC.ByteString -> m ChallengeRequest | 140 | extractCR :: MonadReader Env m => Response LC.ByteString -> m ChallengeRequest |