keter 1.8.4 → 1.9
raw patch · 6 files changed
+55/−20 lines, 6 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Keter.Proxy: [psLogException] :: ProxySettings -> Request -> SomeException -> IO ()
+ Keter.Proxy: [psProxyException] :: ProxySettings -> ByteString
+ Keter.Types: ProxyException :: Request -> SomeException -> LogMessage
+ Keter.Types: [kconfigProxyException] :: KeterConfig -> !Maybe FilePath
+ Keter.Types.Common: ProxyException :: Request -> SomeException -> LogMessage
+ Keter.Types.V10: [kconfigProxyException] :: KeterConfig -> !Maybe FilePath
- Keter.Proxy: MkProxySettings :: (ByteString -> IO (Maybe (ProxyAction, Credentials))) -> !Manager -> !KeterConfig -> (ByteString -> ByteString) -> ByteString -> ProxySettings
+ Keter.Proxy: MkProxySettings :: (ByteString -> IO (Maybe (ProxyAction, Credentials))) -> !Manager -> !KeterConfig -> (ByteString -> ByteString) -> ByteString -> ByteString -> (Request -> SomeException -> IO ()) -> ProxySettings
- Keter.Proxy: makeSettings :: KeterConfig -> HostManager -> IO ProxySettings
+ Keter.Proxy: makeSettings :: (LogMessage -> IO ()) -> KeterConfig -> HostManager -> IO ProxySettings
- Keter.Types: KeterConfig :: FilePath -> PortSettings -> !NonEmptyVector ListeningPort -> Maybe Text -> !Vector (Stanza ()) -> Bool -> !Int -> !Int -> !Map Text Text -> !Int -> !Maybe Port -> !Maybe FilePath -> !Maybe FilePath -> KeterConfig
+ Keter.Types: KeterConfig :: FilePath -> PortSettings -> !NonEmptyVector ListeningPort -> Maybe Text -> !Vector (Stanza ()) -> Bool -> !Int -> !Int -> !Map Text Text -> !Int -> !Maybe Port -> !Maybe FilePath -> !Maybe FilePath -> !Maybe FilePath -> KeterConfig
- Keter.Types.V10: KeterConfig :: FilePath -> PortSettings -> !NonEmptyVector ListeningPort -> Maybe Text -> !Vector (Stanza ()) -> Bool -> !Int -> !Int -> !Map Text Text -> !Int -> !Maybe Port -> !Maybe FilePath -> !Maybe FilePath -> KeterConfig
+ Keter.Types.V10: KeterConfig :: FilePath -> PortSettings -> !NonEmptyVector ListeningPort -> Maybe Text -> !Vector (Stanza ()) -> Bool -> !Int -> !Int -> !Map Text Text -> !Int -> !Maybe Port -> !Maybe FilePath -> !Maybe FilePath -> !Maybe FilePath -> KeterConfig
Files
- ChangeLog.md +13/−1
- Keter/Main.hs +4/−4
- Keter/Proxy.hs +28/−10
- Keter/Types/Common.hs +3/−0
- Keter/Types/V10.hs +5/−1
- keter.cabal +2/−4
ChangeLog.md view
@@ -1,6 +1,18 @@+## 1.9+++ Update status code of missing host responses.+ They now emit a 502 on missing host, and 404 on host not found++ Always restart keter in the nix config for systemd.+ It turns out that keter may exit with exit code 0 under load testing.+ Changing from on-failure to always in systemd should bring it back up.++ Squash proxy exceptions if they occur and serve a default or custom error+ response.+ Emits the exception to the log.++ Add incoming folder to the CI build.+ ## 1.8.4 -+ Get rid of ominious warning at the top, "Hackage documentation generation is not reliable.."++ Get rid of ominious warning at the top. Thanks to /u/Opposite-Platypus-99 for pointing this out.
Keter/Main.hs view
@@ -70,7 +70,7 @@ log StartWatching startWatching kc appMan log log StartListening- startListening kc hostman+ startListening log kc hostman -- | Load up Keter config. withConfig :: FilePath@@ -217,9 +217,9 @@ return [fp1] ) (filter (\x -> x /= "." && x /= "..") dir) -startListening :: KeterConfig -> HostMan.HostManager -> IO ()-startListening config hostman = do- settings <- Proxy.makeSettings config hostman+startListening :: (LogMessage -> IO ()) -> KeterConfig -> HostMan.HostManager -> IO ()+startListening log config hostman = do+ settings <- Proxy.makeSettings log config hostman runAndBlock (kconfigListeners config) $ Proxy.reverseProxy settings runAndBlock :: NonEmptyVector a
Keter/Proxy.hs view
@@ -48,12 +48,13 @@ setLpsTimeBound, waiProxyToSettings, wpsSetIpHeader,+ wpsOnExc, wpsGetDest) import qualified Network.HTTP.ReverseProxy.Rewrite as Rewrite import Network.HTTP.Types (mkStatus, status200, status301, status302, status303, status307,- status404)+ status404, status502) import qualified Network.Wai as Wai import Network.Wai.Application.Static (defaultFileServerSettings, ssListing, staticApp)@@ -77,15 +78,17 @@ data ProxySettings = MkProxySettings { -- | Mapping from virtual hostname to port number.- psHostLookup :: ByteString -> IO (Maybe (ProxyAction, TLS.Credentials))- , psManager :: !Manager- , psConfig :: !KeterConfig- , psUnkownHost :: ByteString -> ByteString- , psMissingHost :: ByteString+ psHostLookup :: ByteString -> IO (Maybe (ProxyAction, TLS.Credentials))+ , psManager :: !Manager+ , psConfig :: !KeterConfig+ , psUnkownHost :: ByteString -> ByteString+ , psMissingHost :: ByteString+ , psProxyException :: ByteString+ , psLogException :: Wai.Request -> SomeException -> IO () } -makeSettings :: KeterConfig -> HostMan.HostManager -> IO ProxySettings-makeSettings psConfig@KeterConfig {..} hostman = do+makeSettings :: (LogMessage -> IO ()) -> KeterConfig -> HostMan.HostManager -> IO ProxySettings+makeSettings log psConfig@KeterConfig {..} hostman = do psManager <- HTTP.newManager HTTP.tlsManagerSettings psMissingHost <- case kconfigMissingHostResponse of Nothing -> pure defaultMissingHostBody@@ -93,8 +96,12 @@ psUnkownHost <- case kconfigUnknownHostResponse of Nothing -> pure defaultUnknownHostBody Just x -> const <$> taggedReadFile "missing-host-response-file" x+ psProxyException <- case kconfigProxyException of+ Nothing -> pure defaultProxyException+ Just x -> taggedReadFile "proxy-exception-response-file" x pure $ MkProxySettings{..} where+ psLogException a b = log $ ProxyException a b psHostLookup = HostMan.lookupAction hostman . CI.mk taggedReadFile :: String -> FilePath -> IO ByteString@@ -132,6 +139,8 @@ s { WarpTLS.tlsServerHooks = newHooks{TLS.onServerNameIndication = newOnServerNameIndication} , WarpTLS.tlsSessionManagerConfig = if session then (Just TLSSession.defaultConfig) else Nothing } ++ withClient :: Bool -- ^ is secure? -> ProxySettings -> Wai.Application@@ -144,6 +153,7 @@ then SIHFromHeader else SIHFromSocket , wpsGetDest = Just getDest+ , wpsOnExc = handleProxyException psLogException psProxyException } psManager where useHeader :: Bool@@ -273,12 +283,20 @@ , Wai.rawQueryString req ] +handleProxyException :: (Wai.Request -> SomeException -> IO ()) -> ByteString -> SomeException -> Wai.Application+handleProxyException handleException onexceptBody except req respond = do+ handleException req except+ respond $ missingHostResponse onexceptBody++defaultProxyException :: ByteString+defaultProxyException = "<!DOCTYPE html>\n<html><head><title>Welcome to Keter</title></head><body><h1>Welcome to Keter</h1><p>There was a proxy error, check the keter logs for details.</p></body></html>"+ defaultMissingHostBody :: ByteString defaultMissingHostBody = "<!DOCTYPE html>\n<html><head><title>Welcome to Keter</title></head><body><h1>Welcome to Keter</h1><p>You did not provide a virtual hostname for this request.</p></body></html>" missingHostResponse :: ByteString -> Wai.Response missingHostResponse missingHost = Wai.responseBuilder- status200+ status502 [("Content-Type", "text/html; charset=utf-8")] $ copyByteString missingHost @@ -289,7 +307,7 @@ unknownHostResponse :: ByteString -> ByteString -> Wai.Response unknownHostResponse host body = Wai.responseBuilder- status200+ status404 [("Content-Type", "text/html; charset=utf-8"), ("X-Forwarded-Host", -- if an attacker manages to insert line breaks somehow,
Keter/Types/Common.hs view
@@ -14,6 +14,7 @@ , SomeException ) where +import qualified Network.Wai as Wai import Control.Exception (Exception, SomeException) import Data.Aeson (FromJSON, Object, ToJSON, Value (Bool), object, withBool,@@ -94,6 +95,7 @@ | BindCli AddrInfo | ReceivedCliConnection SockAddr | KillingApp Port Text+ | ProxyException Wai.Request SomeException instance Show LogMessage where show (ProcessCreated f) = "Created process: " ++ f@@ -161,6 +163,7 @@ show StartListening = "Started listening" show (BindCli addr) = "Bound cli to " <> show addr show (ReceivedCliConnection peer) = "CLI Connection from " <> show peer+ show (ProxyException req except) = "Got a proxy exception on request " <> show req <> " with exception " <> show except data KeterException = CannotParsePostgres FilePath | ExitCodeFailure FilePath ExitCode
Keter/Types/V10.hs view
@@ -107,7 +107,8 @@ -- ^ Port for the cli to listen on , kconfigUnknownHostResponse :: !(Maybe F.FilePath)- , kconfigMissingHostResponse :: !(Maybe F.FilePath)+ , kconfigMissingHostResponse :: !(Maybe F.FilePath)+ , kconfigProxyException :: !(Maybe F.FilePath) } instance ToCurrent KeterConfig where@@ -126,6 +127,7 @@ , kconfigCliPort = Nothing , kconfigUnknownHostResponse = Nothing , kconfigMissingHostResponse = Nothing+ , kconfigProxyException = Nothing } where getSSL Nothing = V.empty@@ -152,6 +154,7 @@ , kconfigCliPort = Nothing , kconfigUnknownHostResponse = Nothing , kconfigMissingHostResponse = Nothing+ , kconfigProxyException = Nothing } instance ParseYamlFile KeterConfig where@@ -175,6 +178,7 @@ <*> o .:? "cli-port" <*> o .:? "missing-host-response-file" <*> o .:? "unknown-host-response-file"+ <*> o .:? "proxy-exception-response-file" -- | Whether we should force redirect to HTTPS routes. type RequiresSecure = Bool
keter.cabal view
@@ -1,6 +1,6 @@ Cabal-version: >=1.10 Name: keter-Version: 1.8.4+Version: 1.9 Synopsis: Web application deployment manager, focusing on Haskell web frameworks Description: Deployment system for web applications, originally intended for hosting Yesod@@ -9,12 +9,10 @@ * Binds to the main port (usually port 80) and reverse proxies requests to your application based on virtual hostnames. * Provides SSL support if requested. * Automatically launches applications, monitors processes, and relaunches any processes which die.- * Provides graceful redeployment support, by launching a second copy of your application, performing a health check[1], and then switching reverse proxying to the new process.+ * Provides graceful redeployment support, by launching a second copy of your application, performing a health check, and then switching reverse proxying to the new process. * Management of log files. . Keter provides many more advanced features and extension points. It allows configuration of static hosts, redirect rules, management of PostgreSQL databases, and more. It supports a simple bundle format for applications which allows for easy management of your web apps.- .- 1: The health check happens trough checking if a port is opened. If your app doesn't open a port after 30 seconds it's presumed not healthy and gets a term signal. Homepage: http://www.yesodweb.com/ License: MIT