aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sproxy.yml.example39
-rw-r--r--src/Sproxy/Config.hs12
-rw-r--r--src/Sproxy/Server.hs49
3 files changed, 73 insertions, 27 deletions
diff --git a/sproxy.yml.example b/sproxy.yml.example
index 9fba77b..de5f434 100644
--- a/sproxy.yml.example
+++ b/sproxy.yml.example
@@ -10,12 +10,35 @@
#
# listen: 443
-# Listen on port 80 and redirect HTTP requests to HTTPS.
-# Optional. Default is true when listen == 443, otherwise false.
+# Whether SSL is used on port defined by `listen`.
+# You should only set it to false iff you intent to do SSL-termination
+# somewhere else, e. g. at a load-balancer in a local network.
+# If true, you also have to specify `ssl_key` and `ssl_cert`.
+# Note that there is no way Sproxy can be usable without HTTPS/SSL at the user side,
+# because Sproxy sets cookie for HTTPS only.
+# Optional. Default is true.
+# ssl: true
+
+# Listen on port 80 and redirect HTTP requests to HTTPS (see `https_port`).
+# Optional. Default is true when `listen` == 443, otherwise false.
#
# listen80: true
-# Whether HTTP2 is enabled. Optional. Default is "true"
+# Port used in redirection of HTTP requests to HTTPS.
+# I. e., http://example.com -> https://example.com[:https_port],
+# If `http_port` == 443, the port part if omitted.
+# This is useful when behind a dump proxy or load-balancer, like Amazon ELB,
+# (and`ssl` == false). It's unlikely that something other than 443
+# is exposed to users, but if you are behind a proxy
+# you can't really know the correct https port.
+# Optional. Default is as `listen`.
+#
+# Example:
+# https_port: 4040
+#
+# https_port:
+
+# Whether HTTP2 is enabled. Optional. Default is true.
#
# http2: true
@@ -30,14 +53,14 @@
# home: "."
-# File with SSL certificate. Required.
+# File with SSL certificate. Required if `ssl` == true.
# It can be a bundle with the server certificate coming first:
# cat me-cert.pem CA-cert.pem > cert.pem
# Once again: most wanted certs go first ;-)
# Or you can opt in using of `ssl_cert_chain`
ssl_cert: /path/cert.pem
-# File with SSL key (secret!). Required.
+# File with SSL key (secret!). Required if `ssl` = true.
ssl_key: /path/key.pem
# Chain SSL certificate files.
@@ -53,8 +76,8 @@ ssl_key: /path/key.pem
# PostgreSQL database connection string.
# Optional. If specified, sproxy will periodically pull the data from this
# database into internal SQLite3 database. Define password in a file
-# referenced by the PGPASSFILE environment variable. Or use the "pgpassfile" option.
-# Cannot be used with the "datafile" option.
+# referenced by the PGPASSFILE environment variable. Or use the `pgpassfile` option.
+# Cannot be used with the `datafile` option.
# Example:
# database: "user=sproxy-readonly dbname=sproxy port=6001"
#
@@ -72,7 +95,7 @@ ssl_key: /path/key.pem
# Optional. If specified, Sproxy will import it on start overwriting
# and existing data in the internal database.
# Useful for development or some simple deployments.
-# Cannot be used with the "database" option.
+# Cannot be used with the `database` option.
# For example see the datafile.yml.example
#
# datafile: /path/data.yml
diff --git a/src/Sproxy/Config.hs b/src/Sproxy/Config.hs
index e76b436..4cae025 100644
--- a/src/Sproxy/Config.hs
+++ b/src/Sproxy/Config.hs
@@ -17,14 +17,16 @@ import Sproxy.Logging (LogLevel(Debug))
data ConfigFile = ConfigFile {
cfListen :: Word16
+, cfSsl :: Bool
, cfUser :: String
, cfHome :: FilePath
, cfLogLevel :: LogLevel
-, cfSslCert :: FilePath
-, cfSslKey :: FilePath
+, cfSslCert :: Maybe FilePath
+, cfSslKey :: Maybe FilePath
, cfSslCertChain :: [FilePath]
, cfKey :: Maybe FilePath
, cfListen80 :: Maybe Bool
+, cfHttpsPort :: Maybe Word16
, cfBackends :: [BackendConf]
, cfOAuth2 :: HashMap Text OAuth2Conf
, cfDataFile :: Maybe FilePath
@@ -36,14 +38,16 @@ data ConfigFile = ConfigFile {
instance FromJSON ConfigFile where
parseJSON (Object m) = ConfigFile <$>
m .:? "listen" .!= 443
+ <*> m .:? "ssl" .!= True
<*> m .:? "user" .!= "sproxy"
<*> m .:? "home" .!= "."
<*> m .:? "log_level" .!= Debug
- <*> m .: "ssl_cert"
- <*> m .: "ssl_key"
+ <*> m .:? "ssl_cert"
+ <*> m .:? "ssl_key"
<*> m .:? "ssl_cert_chain" .!= []
<*> m .:? "key"
<*> m .:? "listen80"
+ <*> m .:? "https_port"
<*> m .: "backends"
<*> m .: "oauth2"
<*> m .:? "datafile"
diff --git a/src/Sproxy/Server.hs b/src/Sproxy/Server.hs
index 3c34b0c..6e24bfd 100644
--- a/src/Sproxy/Server.hs
+++ b/src/Sproxy/Server.hs
@@ -14,11 +14,12 @@ import Data.Word (Word16)
import Data.Yaml (decodeFileEither)
import Network.HTTP.Client (Manager, ManagerSettings(..), defaultManagerSettings, newManager, socketConnection)
import Network.HTTP.Client.Internal (Connection)
-import Network.Socket ( Family(AF_INET, AF_UNIX), SockAddr(SockAddrInet, SockAddrUnix),
+import Network.Socket ( Socket, Family(AF_INET, AF_UNIX), SockAddr(SockAddrInet, SockAddrUnix),
SocketOption(ReuseAddr), SocketType(Stream), bind, close, connect, inet_addr,
listen, maxListenQueue, setSocketOption, socket )
+import Network.Wai (Application)
import Network.Wai.Handler.WarpTLS (tlsSettingsChain, runTLSSocket)
-import Network.Wai.Handler.Warp ( defaultSettings, runSettingsSocket,
+import Network.Wai.Handler.Warp ( Settings, defaultSettings, runSettingsSocket,
setHTTP2Disabled, setOnException )
import System.Entropy (getEntropy)
import System.Environment (setEnv)
@@ -37,6 +38,11 @@ import qualified Sproxy.Logging as Log
import qualified Sproxy.Server.DB as DB
+{- TODO:
+ - Log.error && exitFailure should be replaced
+ - by Log.fatal && wait for logger thread to print && exitFailure
+-}
+
server :: FilePath -> IO ()
server configFile = do
cf <- readConfigFile configFile
@@ -81,13 +87,6 @@ server configFile = do
setOnException (\_ _ -> return ())
defaultSettings
- case maybe80 of
- Nothing -> return ()
- Just sock80 -> do
- Log.info "listening on port 80 (HTTP redirect)"
- listen sock80 maxListenQueue
- void . forkIO $ runSettingsSocket settings sock80 (redirect $ cfListen cf)
-
oauth2clients <- HM.fromList <$> mapM newOAuth2Client (HM.toList (cfOAuth2 cf))
backends <-
@@ -96,15 +95,22 @@ server configFile = do
return (compile $ beName be, be, m)
) $ cfBackends cf
+
+ warpServer <- newServer cf
+
+ case maybe80 of
+ Nothing -> return ()
+ Just sock80 -> do
+ let httpsPort = fromMaybe (cfListen cf) (cfHttpsPort cf)
+ Log.info "listening on port 80 (HTTP redirect)"
+ listen sock80 maxListenQueue
+ void . forkIO $ runSettingsSocket settings sock80 (redirect httpsPort)
+
-- XXX 2048 is from bindPortTCP from streaming-commons used internally by runTLS.
-- XXX Since we don't call runTLS, we listen socket here with the same options.
- Log.info $ "listening on port " ++ show (cfListen cf) ++ " (HTTPS)"
+ Log.info $ "proxy listening on port " ++ show (cfListen cf)
listen sock (max 2048 maxListenQueue)
- runTLSSocket
- (tlsSettingsChain (cfSslCert cf) (cfSslCertChain cf) (cfSslKey cf))
- settings
- sock
- (sproxy key db oauth2clients backends)
+ warpServer settings sock (sproxy key db oauth2clients backends)
newDataSource :: ConfigFile -> IO (Maybe DB.DataSource)
@@ -167,6 +173,19 @@ newBackendManager be = do
}
+newServer :: ConfigFile -> IO (Settings -> Socket -> Application -> IO ())
+newServer cf
+ | cfSsl cf =
+ case (cfSslKey cf, cfSslCert cf) of
+ (Just k, Just c) ->
+ return $ runTLSSocket (tlsSettingsChain c (cfSslCertChain cf) k)
+ _ -> do Log.error "missings SSL certificate"
+ exitFailure
+ | otherwise = do
+ Log.warn "not using SSL!"
+ return runSettingsSocket
+
+
openUnixSocketConnection :: FilePath -> IO Connection
openUnixSocketConnection f =
bracketOnError