sproxy2 1.93.0 → 1.94.0
raw patch · 7 files changed
+48/−22 lines, 7 files
Files
- ChangeLog.md +16/−1
- README.md +4/−2
- sproxy.example.yml +7/−5
- sproxy.sql +4/−1
- sproxy2.cabal +1/−1
- src/Sproxy/Application.hs +14/−10
- src/Sproxy/Server/DB.hs +2/−2
ChangeLog.md view
@@ -1,5 +1,20 @@ For differences with the original Sproxy scroll down. +1.94.0+======++ * BREAKING: Disregard possible port in the Host HTTP header.+ Previously, Sproxy took possible port number into account when+ looking for backend and privileges. Now it ignores port and considers+ domain name only. This also gets Sproxy in line with browsers and SSL+ certificates: certificates do not include port numbers, browsers ignore+ ports when sending cookies.++ * BREAKING: no SQL wildcards (`%` or `_`) in domain names when looking up+ for privileges. This feature was ambiguous (in the same way as paths are)+ and never used anyway.++ 1.93.0 ====== @@ -72,7 +87,7 @@ as sproxy does it on every HTTP request. Sproxy2 pulls data into local SQLite3 database. * At this release Sproxy2 is compatible with Sproxy database with one exception:- SQL wildcards are not supported for HTTP methods. E. i. you have to change '%' in+ SQL wildcards are not supported for HTTP methods. I. e. you have to change '%' in the database to specific methods like GET, POST, etc. * OAuth2 callback URLs changed: Sproxy2 uses `/.sproxy/oauth2/:provider`,
README.md view
@@ -33,9 +33,11 @@ application's API. It would likely be [a single-page application](https://en.wikipedia.org/wiki/Single-page_application). Examples are [MyWatch](https://hackage.haskell.org/package/mywatch) and- [Juan de la Cosa](https://hackage.haskell.org/package/juandelacosa)+ [Juan de la Cosa](https://hackage.haskell.org/package/juandelacosa). + * Replace HTTP Basic authentication. + How it works ============ @@ -135,7 +137,7 @@ - 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.+- SQL wildcards (`_` and `%`) are supported for emails, paths (this _will_ change in future versions). HTTP headers passed to the back-end server
sproxy.example.yml view
@@ -8,7 +8,10 @@ # The port Sproxy listens on (HTTPS). # Optional. Default is 443. #-# listen: 443+# Example:+# listen: 8443+#+# listen: # Whether SSL is used on port defined by `listen`. # You should only set it to false iff you intent to do SSL-termination@@ -26,7 +29,7 @@ # 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.+# If `https_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@@ -144,11 +147,10 @@ # Unix sockets should be secured with proper unix file permissions. # # Backend attributes:-# name - the host name as in the Host HTTP header.+# name - the domain name as in the Host HTTP header (without optional colon and port). # 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.+# Optional. Default is "*". # 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.
sproxy.sql view
@@ -166,7 +166,10 @@ INSERT INTO group_member ("group", email) VALUES ('dev', '%'); INSERT INTO privilege (domain, privilege) VALUES ('example.com', 'full'); INSERT INTO group_privilege ("group", domain, privilege) VALUES ('dev', 'example.com', 'full');- INSERT INTO privilege_rule (domain, privilege, path, method) VALUES ('example.com', 'full', '%', '%');+ INSERT INTO privilege_rule (domain, privilege, path, method) VALUES ('example.com', 'full', '%', 'GET');+ INSERT INTO privilege_rule (domain, privilege, path, method) VALUES ('example.com', 'full', '%', 'HEAD');+ INSERT INTO privilege_rule (domain, privilege, path, method) VALUES ('example.com', 'full', '%', 'POST');+ INSERT INTO privilege_rule (domain, privilege, path, method) VALUES ('example.com', 'full', '%', 'PUT'); */ END;
sproxy2.cabal view
@@ -1,5 +1,5 @@ name: sproxy2-version: 1.93.0+version: 1.94.0 synopsis: Secure HTTP proxy for authenticating users via OAuth2 description: Sproxy is secure by default. No requests makes it to the backend
src/Sproxy/Application.hs view
@@ -55,14 +55,13 @@ redirect :: Word16 -> W.Application redirect p req resp =- case W.requestHeaderHost req of+ case requestDomain req of Nothing -> badRequest "missing host" req resp- Just host -> do+ Just domain -> do Log.info $ "redirecting to " ++ show location ++ ": " ++ showReq req resp $ W.responseBuilder status [(hLocation, location)] mempty where status = if W.requestMethod req == methodGet then movedPermanently301 else temporaryRedirect307- (domain, _) = BS.break (== _colon) host newhost = if p == 443 then domain else domain <> ":" <> pack (show p) location = "https://" <> newhost <> W.rawPathInfo req <> W.rawQueryString req @@ -70,10 +69,10 @@ sproxy :: ByteString -> Database -> HashMap Text OAuth2Client -> [(Pattern, BackendConf, BE.Manager)] -> W.Application sproxy key db oa2 backends = logException $ \req resp -> do Log.debug $ "sproxy <<< " ++ showReq req- case W.requestHeaderHost req of+ case requestDomain req of Nothing -> badRequest "missing host" req resp- Just host ->- case find (\(p, _, _) -> match p (unpack host)) backends of+ Just domain ->+ case find (\(p, _, _) -> match p (unpack domain)) backends of Nothing -> notFound "backend" req resp Just (_, be, mgr) -> do let cookieName = pack $ beCookieName be@@ -145,8 +144,7 @@ authenticate :: ByteString -> BackendConf -> AuthUser -> ByteString -> W.Application authenticate key be user path req resp = do now <- epochTime- let host = fromJust $ W.requestHeaderHost req- domain = pack <$> beCookieDomain be+ let domain = pack <$> beCookieDomain be expiry = now + CTime (beCookieMaxAge be) authCookie = AuthCookie { acUser = user, acExpiry = expiry } cookie = WC.def {@@ -160,7 +158,7 @@ , WC.setCookieExpires = Just . posixSecondsToUTCTime . realToFrac $ expiry } resp $ W.responseLBS seeOther303 [- (hLocation, "https://" <> host <> path)+ (hLocation, "https://" <> fromJust (W.requestHeaderHost req) <> path) , ("Set-Cookie", toByteString $ WC.renderSetCookie cookie) ] "" @@ -169,7 +167,7 @@ authorize db (authCookie, otherCookies) req = do let user = acUser authCookie- domain = decodeUtf8 . fromJust $ W.requestHeaderHost req+ domain = decodeUtf8 . fromJust $ requestDomain req email = getEmail user emailUtf8 = getEmailUtf8 user familyUtf8 = getFamilyNameUtf8 user@@ -386,6 +384,12 @@ redirectURL req provider = "https://" <> fromJust (W.requestHeaderHost req) <> "/.sproxy/oauth2/" <> encodeUtf8 provider+++requestDomain :: W.Request -> Maybe ByteString+requestDomain req = do+ h <- W.requestHeaderHost req+ return . fst . BS.break (== _colon) $ h -- XXX: make sure not to reveal the cookie, which can be valid (!)
src/Sproxy/Server/DB.hs view
@@ -66,10 +66,10 @@ withResource db $ \c -> fmap SQLite.fromOnly <$> SQLite.queryNamed c [q| SELECT gm."group" FROM group_privilege gp JOIN group_member gm ON gm."group" = gp."group" WHERE :email LIKE gm.email- AND :domain LIKE gp.domain+ AND gp.domain = :domain AND gp.privilege IN ( SELECT privilege FROM privilege_rule- WHERE :domain LIKE domain+ WHERE domain = domain AND :path LIKE path AND method = :method ORDER BY length(path) - length(replace(path, '/', '')) DESC LIMIT 1