@@ -65,6 +65,7 @@ import qualified OpenSSL.X509.Request as X509Request
6565import Reflex.Dom.Core
6666import Snap.Core (Snap)
6767import System.Environment
68+import System.FilePath ((</>))
6869import System.IO
6970import System.Process
7071import Text.URI (URI)
@@ -78,11 +79,13 @@ import qualified System.Which
7879
7980run
8081 :: Int -- ^ Port to run the backend
82+ -> Maybe FilePath -- ^ Optional directory in which to find "cert.pem", "chain.pem" and "privkey.pem" to be used for TLS.
83+ -- If this is Nothing and TLS is enabled, we'll generate a self-signed cert.
8184 -> ([Text] -> Snap ()) -- ^ Static asset handler
8285 -> Backend backendRoute frontendRoute -- ^ Backend
8386 -> Frontend (R frontendRoute) -- ^ Frontend
8487 -> IO ()
85−run port serveStaticAsset backend frontend = do
88+run port certDir serveStaticAsset backend frontend = do
8689 prettifyOutput
8790 let handleBackendErr (e :: IOException) = hPutStrLn stderr $ "backend stopped; make a change to your code to reload - error " <> show e
8891 --TODO: Use Obelisk.Backend.runBackend; this will require separating the checking and running phases
@@ -102,7 +105,7 @@ run port serveStaticAsset backend frontend = do
102105 appRouteToUrl (k :/ v) = renderObeliskRoute validFullEncoder (FullRoute_Frontend (ObeliskRoute_App k) :/ v)
103106 allJsUrl = renderAllJsPath validFullEncoder
104107
105− let conf = defRunConfig { _runConfig_redirectPort = port }
108+ let conf = defRunConfig { _runConfig_redirectPort = port, _runConfig_certDir = certDir }
106109 runWidget conf publicConfigs frontend validFullEncoder `finally` killThread backendTid
107110
108111-- Convenience wrapper to handle path segments for 'Snap.serveAsset'
@@ -135,24 +138,29 @@ runWidget conf configs frontend validFullEncoder = do
135138 -- Providing TLS here will also incidentally provide it to proxied requests to the backend.
136139 prepareRunner = case uri ^? uriScheme . _Just . unRText of
137140 Just "https" -> do
138− -- Generate a private key and self-signed certificate for TLS
139− privateKey <- RSA.generateRSAKey' 2048 3
141+ case _runConfig_certDir conf of
142+ Nothing -> do
143+ -- Generate a private key and self-signed certificate for TLS
144+ privateKey <- RSA.generateRSAKey' 2048 3
140145
141− certRequest <- X509Request.newX509Req
142− _ <- X509Request.setPublicKey certRequest privateKey
143− _ <- X509Request.signX509Req certRequest privateKey Nothing
146+ certRequest <- X509Request.newX509Req
147+ _ <- X509Request.setPublicKey certRequest privateKey
148+ _ <- X509Request.signX509Req certRequest privateKey Nothing
144149
145− cert <- X509.newX509 >>= X509Request.makeX509FromReq certRequest
146− _ <- X509.setPublicKey cert privateKey
147− now <- getCurrentTime
148− _ <- X509.setNotBefore cert $ addUTCTime (-1) now
149− _ <- X509.setNotAfter cert $ addUTCTime (365 * 24 * 60 * 60) now
150− _ <- X509.signX509 cert privateKey Nothing
150+ cert <- X509.newX509 >>= X509Request.makeX509FromReq certRequest
151+ _ <- X509.setPublicKey cert privateKey
152+ now <- getCurrentTime
153+ _ <- X509.setNotBefore cert $ addUTCTime (-1) now
154+ _ <- X509.setNotAfter cert $ addUTCTime (365 * 24 * 60 * 60) now
155+ _ <- X509.signX509 cert privateKey Nothing
151156
152− certByteString <- BSUTF8.fromString <$> PEM.writeX509 cert
153− privateKeyByteString <- BSUTF8.fromString <$> PEM.writePKCS8PrivateKey privateKey Nothing
157+ certByteString <- BSUTF8.fromString <$> PEM.writeX509 cert
158+ privateKeyByteString <- BSUTF8.fromString <$> PEM.writePKCS8PrivateKey privateKey Nothing
154159
155− return $ runTLSSocket (tlsSettingsMemory certByteString privateKeyByteString)
160+ return $ runTLSSocket (tlsSettingsMemory certByteString privateKeyByteString)
161+ Just certDir -> do
162+ putStrLn $ "Using certificate information from: " ++ certDir
163+ return $ runTLSSocket (tlsSettingsChain (certDir </> "cert.pem") [certDir </> "chain.pem"] (certDir </> "privkey.pem"))
156164 _ -> return runSettingsSocket
157165 runner <- prepareRunner
158166 bracket
@@ -257,6 +265,7 @@ data RunConfig = RunConfig
257265 , _runConfig_redirectHost :: ByteString
258266 , _runConfig_redirectPort :: Int
259267 , _runConfig_retryTimeout :: Int -- seconds
268+ , _runConfig_certDir :: Maybe FilePath
260269 }
261270
262271defRunConfig :: RunConfig
@@ -265,4 +274,5 @@ defRunConfig = RunConfig
265274 , _runConfig_redirectHost = "127.0.0.1"
266275 , _runConfig_redirectPort = 3001
267276 , _runConfig_retryTimeout = 1
277+ , _runConfig_certDir = Nothing
268278 }