sproxy2 1.92.0 → 1.93.0
raw patch · 9 files changed
+236/−243 lines, 9 files
Files
- ChangeLog.md +11/−0
- README.md +13/−13
- datafile.example.yml +29/−0
- datafile.yml.example +0/−34
- sproxy.example.yml +172/−0
- sproxy.yml.example +0/−176
- sproxy2.cabal +3/−3
- src/Sproxy/Config.hs +2/−2
- src/Sproxy/Server.hs +6/−15
ChangeLog.md view
@@ -1,5 +1,16 @@ For differences with the original Sproxy scroll down. +1.93.0+======++ * BREAKING: Allow `!include` in config file.+ This changes semantics of options `key` and `oauth2.<provider>.client_secret`.+ They are no longer files, but strings. To read content from files, use+ !include. The point of being files or read from files is to segregate secrets+ from non-sensitive easily discoverable settings. With `!include` it is much more+ simple and flexible.++ 1.92.0 ======
README.md view
@@ -57,7 +57,7 @@ ------------------ Permissions are stored in internal SQLite3 database and imported from data sources, which can be a PostgreSQL database or a file. See-[sproxy.sql](./sproxy.sql) and [datafile.yml.example](./datafile.yml.example)+[sproxy.sql](./sproxy.sql) and [datafile.example.yml](./datafile.example.yml) for details. Do note that Sproxy2 fetches only `group_member`, `group_privilege`@@ -91,17 +91,6 @@ surprising, please see the following example: -Keep in mind that:--- Domains are converted into lower case (coming from a data source or HTTP requests).-- Emails are converted into lower case (coming from a data source or OAuth2 providers).-- Groups are case-sensitive and treated as is.-- HTTP methods are *case-sensitive*.-- HTTP query parameters are ignored when matching a request against the rules.-- Privileges are case-sensitive and treated as is.-- SQL wildcards (`_` and `%`) are supported for emails, domains, paths.-- Privileges example ------------------ @@ -138,6 +127,17 @@ Likewise `readers` have no access to e.g. `/wiki/edit/delete_everything.php`. +Keep in mind that:++- Domains are converted into lower case (coming from a data source or HTTP requests).+- Emails are converted into lower case (coming from a data source or OAuth2 providers).+- Groups are case-sensitive and treated as is.+- HTTP methods are *case-sensitive*.+- HTTP query parameters are ignored when matching a request against the rules.+- Privileges are case-sensitive and treated as is.+- SQL wildcards (`_` and `%`) are supported for emails, domains, paths.++ HTTP headers passed to the back-end server ------------------------------------------ @@ -198,7 +198,7 @@ ============= By default `sproxy2` will read its configuration from `sproxy.yml`. There is-example file with documentation [sproxy.yml.example](sproxy.yml.example). You+example file with documentation [sproxy.example.yml](sproxy.example.yml). You can specify a custom path with: ```
+ datafile.example.yml view
@@ -0,0 +1,29 @@+group_member:+ - group: "devops"+ email: "%"++ - group: "foo"+ email: "%"+++group_privilege:+ - group: "foo"+ domain: "example.com"+ privilege: "full"++ - group: "devops"+ domain: "example.com"+ privilege: "full"+++privilege_rule:+ - domain: "example.com"+ privilege: "full"+ path: "%"+ method: "GET"++ - domain: "example.com"+ privilege: "full"+ path: "%"+ method: "POST"+
− datafile.yml.example
@@ -1,34 +0,0 @@---- # Data file. Don't remove this line. This is YAML: https://en.wikipedia.org/wiki/YAML--group_member:- - group: "devops"- email: "%"-- - group: "foo"- email: "%"---group_privilege:- - group: "foo"- domain: "example.com"- privilege: "full"-- - group: "devops"- domain: "example.com"- privilege: "full"---privilege_rule:- - domain: "example.com"- privilege: "full"- path: "%"- method: "GET"-- - domain: "example.com"- privilege: "full"- path: "%"- method: "POST"---... # End of data file. Don't remove this line. This is YAML: https://en.wikipedia.org/wiki/YAML-
+ sproxy.example.yml view
@@ -0,0 +1,172 @@+# NOTE: You can use the !include directive to import parts of this file.++# Logging level: debug, info, warn, error.+# Optional. Default is debug.+#+# log_level: debug++# The port Sproxy listens on (HTTPS).+# Optional. Default is 443.+#+# listen: 443++# 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++# 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 dumb 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++# The system user Sproxy switches to if launched as root (after opening the ports).+# Optional. Default is sproxy.+#+# user: sproxy++# Home directory for various files including SQLite3 authorization database.+# Optional. Default is current directory.+#+# home: "."+++# 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 if `ssl` = true.+ssl_key: /path/key.pem++# Chain SSL certificate files.+# Optional. Default is an empty list+# Example:+# ssl_cert_chain:+# - /path/foo.pem+# - /path/bar.pem+#+# ssl_cert_chain: []+++# 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.+# Example:+# database: "user=sproxy-readonly dbname=sproxy port=6001"+#+# database:++# PostgreSQL password file.+# Optional. If specified, sproxy will set PGPASSFILE environment variable pointing to this file+# Example:+# pgpassfile: /run/keys/sproxy.pgpass+#+# pgpassfile:+++# YAML file used to fill internal SQLite3 database.+# 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.+# For example see the datafile.yml.example+#+# datafile: /path/data.yml+++# Arbitrary string used to sign sproxy cookie and other things (secret!).+# Optional. If not specified, a random key is generated on startup, and+# as a consequence, restaring sproxy will invalidate existing user sessions.+# This option could be useful for load-balancing with multiple sproxy instances,+# when all instances must understand cookies created by each other.+# This should not be very large, a few random bytes are fine.+# +# key: !include /run/keys/sproxy.secret+++# Credentials for supported OAuth2 providers.+# Currently supported: "google", "linkedin"+# At least one provider is required.+# Attributes:+# client_id - OAuth2 client ID.+# client_secret - OAuth2 client secret.+#+# Example:+# oauth2:+# google:+# client_id: "XXXXXXXXXXXX-YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY.apps.googleusercontent.com"+# client_secret: !include /run/keys/XXXXXXXXXXXX-YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY.apps.googleusercontent.com+#+# linkedin:+# client_id: "xxxxxxxxxxxxxx"+# client_secret: !include "/run/keys/xxxxxxxxxxxxxx"+#+#+# oauth2:+# google:+# client_id:+# client_secret:+++# Backend servers. At least one is required.+# NOTE: backends at TCP port are not secure, even on localhost,+# because any local user can connect to the backend bypassing sproxy+# authentication and authorization.+#+# It is recommended to communicate with backends via unix sockets only.+# Unix sockets should be secured with proper unix file permissions.+#+# Backend attributes:+# name - the host name as in the Host HTTP header.+# May include wildcards * and ?. The first matching+# backend will be used. Examples: "*.example.com", "wiki.corp.com".+# Optional. Default is "*". Note, that the name must include+# port number if non-standard.+# address - backend IP address. Optional. Default is 127.0.0.1.+# port - backend TCP port. Required unless unix socket is defined.+# socket - unix socket. Highly recommended for security reasons.+# If defined, IP address and TCP port are ignored.+#+# cookie_name - sproxy cookie name. Optional. Default is "sproxy".+# cookie_domain - sproxy cookie domain. Optional. Default is the request host name as per RFC2109.+# cookie_max_age - sproxy cookie shelflife in seconds. Optional. Default is 604800 (7 days).+# conn_count - number of connections to keep alive. Optional. Default is 32.+# This is specific to Haskell HTTP Client library, and is per host name,+# not per backend. HTTP Client's default is 10.+#+# backends:+# - name: wiki.example.com+# port: 9090+# cookie_name: sproxy_example+# cookie_max_age: 86400+#+backends:+ - port: 8080+
− sproxy.yml.example
@@ -1,176 +0,0 @@---- # Sproxy configuration. Don't remove this line. This is YAML: https://en.wikipedia.org/wiki/YAML--# Logging level: debug, info, warn, error.-# Optional. Default is debug.-#-# log_level: debug--# The port Sproxy listens on (HTTPS).-# Optional. Default is 443.-#-# listen: 443--# 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--# 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--# The system user Sproxy switches to if launched as root (after opening the ports).-# Optional. Default is sproxy.-#-# user: sproxy--# Home directory for various files including SQLite3 authorization database.-# Optional. Default is current directory.-#-# home: "."---# 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 if `ssl` = true.-ssl_key: /path/key.pem--# Chain SSL certificate files.-# Optional. Default is an empty list-# Example:-# ssl_cert_chain:-# - /path/foo.pem-# - /path/bar.pem-#-# ssl_cert_chain: []---# 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.-# Example:-# database: "user=sproxy-readonly dbname=sproxy port=6001"-#-# database:--# PostgreSQL password file.-# Optional. If specified, sproxy will set PGPASSFILE environment variable pointing to this file-# Example:-# pgpassfile: /run/keys/sproxy.pgpass-#-# pgpassfile:---# YAML file used to fill internal SQLite3 database.-# 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.-# For example see the datafile.yml.example-#-# datafile: /path/data.yml---# A file with arbitrary content used to sign sproxy cookie and other things (secret!).-# Optional. If not specified, a random key is generated on startup, and-# as a consequence, restaring sproxy will invalidate existing user sessions.-# This option could be useful for load-balancing with multiple sproxy instances,-# when all instances must understand cookies created by each other.-# This should not be very large, a few random bytes are fine.-# -# key: /run/keys/sproxy.secret---# Credentials for supported OAuth2 providers.-# Currently supported: "google", "linkedin"-# At least one provider is required.-# Attributes:-# client_id - OAuth2 client ID (string)-# client_secret - OAuth2 client secret. Regardless of its name, this is a file.-# The secret is read from the file which you should keep secret.-# Only the first line of this file is read.-#-# Example:-# oauth2:-# google:-# client_id: "XXXXXXXXXXXX-YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY.apps.googleusercontent.com"-# client_secret: "/run/keys/XXXXXXXXXXXX-YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY.apps.googleusercontent.com"-#-# linkedin:-# client_id: "xxxxxxxxxxxxxx"-# client_secret: "/run/keys/xxxxxxxxxxxxxx"-#-#-# oauth2:-# google:-# client_id:-# client_secret:---# Backend servers. At least one is required.-# NOTE: backends at TCP port are not secure, even on localhost,-# because any local user can connect to the backend bypassing sproxy-# authentication and authorization.-#-# It is recommended to communicate with backends via unix sockets only.-# Unix sockets should be secured with proper unix file permissions.-#-# Backend attributes:-# name - the host name as in the Host HTTP header.-# May include wildcards * and ?. The first matching-# backend will be used. Examples: "*.example.com", "wiki.corp.com".-# Optional. Default is "*". Note, that the name must include-# port number if non-standard.-# address - backend IP address. Optional. Default is 127.0.0.1.-# port - backend TCP port. Required unless unix socket is defined.-# socket - unix socket. Highly recommended for security reasons.-# If defined, IP address and TCP port are ignored.-#-# cookie_name - sproxy cookie name. Optional. Default is "sproxy".-# cookie_domain - sproxy cookie domain. Optional. Default is the request host name as per RFC2109.-# cookie_max_age - sproxy cookie shelflife in seconds. Optional. Default is 604800 (7 days).-# conn_count - number of connections to keep alive. Optional. Default is 32.-# This is specific to Haskell HTTP Client library, and is per host name,-# not per backend. HTTP Client's default is 10.-#-# backends:-# - name: wiki.example.com-# port: 9090-# cookie_name: sproxy_example-# cookie_max_age: 86400-#-backends:- - port: 8080--... # End of configuration. Don't remove this line. This is YAML: https://en.wikipedia.org/wiki/YAML-
sproxy2.cabal view
@@ -1,5 +1,5 @@ name: sproxy2-version: 1.92.0+version: 1.93.0 synopsis: Secure HTTP proxy for authenticating users via OAuth2 description: Sproxy is secure by default. No requests makes it to the backend@@ -17,9 +17,9 @@ extra-source-files: ChangeLog.md README.md- datafile.yml.example+ datafile.example.yml+ sproxy.example.yml sproxy.sql- sproxy.yml.example source-repository head type: git
src/Sproxy/Config.hs view
@@ -24,7 +24,7 @@ , cfSslCert :: Maybe FilePath , cfSslKey :: Maybe FilePath , cfSslCertChain :: [FilePath]-, cfKey :: Maybe FilePath+, cfKey :: Maybe String , cfListen80 :: Maybe Bool , cfHttpsPort :: Maybe Word16 , cfBackends :: [BackendConf]@@ -83,7 +83,7 @@ data OAuth2Conf = OAuth2Conf { oa2ClientId :: String-, oa2ClientSecret :: FilePath+, oa2ClientSecret :: String } deriving (Show) instance FromJSON OAuth2Conf where
src/Sproxy/Server.hs view
@@ -5,13 +5,12 @@ import Control.Concurrent (forkIO) import Control.Exception (bracketOnError) import Control.Monad (void, when)-import Data.ByteString as BS (hGetLine, readFile) import Data.ByteString.Char8 (pack) import Data.HashMap.Strict as HM (fromList, lookup, toList) import Data.Maybe (fromMaybe) import Data.Text (Text) import Data.Word (Word16)-import Data.Yaml (decodeFileEither)+import Data.Yaml.Include (decodeFileEither) import Network.HTTP.Client (Manager, ManagerSettings(..), defaultManagerSettings, newManager, socketConnection) import Network.HTTP.Client.Internal (Connection) import Network.Socket ( Socket, Family(AF_INET, AF_UNIX), SockAddr(SockAddrInet, SockAddrUnix),@@ -25,7 +24,7 @@ import System.Environment (setEnv) import System.Exit (exitFailure) import System.FilePath.Glob (compile)-import System.IO (IOMode(ReadMode), hIsEOF, hPutStrLn, stderr, withFile)+import System.IO (hPutStrLn, stderr) import System.Posix.User ( GroupEntry(..), UserEntry(..), getAllGroupEntries, getRealUserID, getUserEntryForName, setGroupID, setGroups, setUserID )@@ -47,7 +46,6 @@ server configFile = do cf <- readConfigFile configFile Log.start $ cfLogLevel cf- Log.debug $ show cf sock <- socket AF_INET Stream 0 setSocketOption sock ReuseAddr 1@@ -78,7 +76,7 @@ key <- maybe (Log.info "using new random key" >> getEntropy 32)- (\f -> Log.info ("reading key from " ++ f) >> BS.readFile f)+ (return . pack) (cfKey cf) let@@ -139,16 +137,9 @@ exitFailure Just provider -> do Log.info $ "oauth2: adding " ++ show name- client_secret <- withFile secret_file ReadMode $ \h -> do- empty <- hIsEOF h- if empty then do- Log.error $ "oauth2: empty secret file for "- ++ show name ++ ": " ++ show secret_file- return $ pack ""- else BS.hGetLine h- return (name, provider (pack client_id, client_secret))- where client_id = oa2ClientId cfg- secret_file = oa2ClientSecret cfg+ return (name, provider (client_id, client_secret))+ where client_id = pack $ oa2ClientId cfg+ client_secret = pack $ oa2ClientSecret cfg newBackendManager :: BackendConf -> IO Manager