summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Cady <d@jerkface.net>2016-01-26 22:26:22 -0500
committerAndrew Cady <d@jerkface.net>2016-01-27 07:26:34 -0500
commiteaaebe924ba1cebc40a160a28f3779fd08c2181b (patch)
treef3c23824da3985e46ebf01f81422140611360e95
parentf48e818311f9ee8cf5437b8f2080713fb0203688 (diff)
clean up http-served files after challenges complete
-rw-r--r--acme-certify.cabal3
-rw-r--r--src/Network/ACME.hs43
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
23executable acme-certify 24executable 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)
43import Data.List 43import Data.List
44import Control.Error 44import Control.Error
45import Control.Arrow 45import Control.Arrow
46 46import Control.Monad.Trans.Resource hiding (register)
47type HttpProvisioner = URI -> ByteString -> IO ()
48 47
49genReq :: Keys -> [DomainName] -> IO CSR 48genReq :: Keys -> [DomainName] -> IO CSR
50genReq _ [] = error "genReq called with zero domains" 49genReq _ [] = 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
62type HttpProvisioner = URI -> ByteString -> ResIO ()
63fileProvisioner :: WritableDir -> HttpProvisioner 63fileProvisioner :: WritableDir -> HttpProvisioner
64fileProvisioner challengeDir = BC.writeFile . uToF 64fileProvisioner 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
69acmeChallengeURI :: DomainName -> BC.ByteString -> URI 72acmeChallengeURI :: DomainName -> BC.ByteString -> URI
70acmeChallengeURI dom tok = URI 73acmeChallengeURI dom tok = URI
@@ -76,23 +79,20 @@ acmeChallengeURI dom tok = URI
76 79
77certify :: URI -> Keys -> Maybe (URI, EmailAddress) -> HttpProvisioner -> CSR -> IO (Either String X509) 80certify :: URI -> Keys -> Maybe (URI, EmailAddress) -> HttpProvisioner -> CSR -> IO (Either String X509)
78certify directoryUrl keys reg provision certReq = 81certify 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
97pollResults :: [Response LC.ByteString] -> ACME (Either String ()) 97pollResults :: [Response LC.ByteString] -> ACME (Either String ())
98pollResults [] = return $ Right () 98pollResults [] = return $ Right ()
@@ -127,15 +127,14 @@ domainToString = T.unpack . decodeUtf8 . Text.Domain.Validate.toByteString
127 127
128canProvision :: WritableDir -> DomainName -> IO Bool 128canProvision :: WritableDir -> DomainName -> IO Bool
129canProvision challengeDir domain = do 129canProvision 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
141extractCR :: MonadReader Env m => Response LC.ByteString -> m ChallengeRequest 140extractCR :: MonadReader Env m => Response LC.ByteString -> m ChallengeRequest