Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GOAL-1240] New parameters (username and tenants) for user #5

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f5dbf16
Adapt repo to Stratio CICD flow (#10)
majimenez-stratio Apr 22, 2021
08320f7
Add sis provider and jwt session store (#11)
majimenez-stratio Apr 22, 2021
5d1f6c6
Sign out features (#12)
majimenez-stratio Apr 22, 2021
91d82d0
[BUILD] 7.1.2: New Pre-release
stratiocommit Apr 22, 2021
da3e134
Prepare for next version: 7.1.2-SNAPSHOT
stratiocommit Apr 22, 2021
dccf2d9
Use extra cookies info from request (#13)
majimenez-stratio Jun 14, 2021
c9cb489
[BUILD] 7.1.2: New Pre-release
stratiocommit Jun 14, 2021
bfa7615
Prepare for next version: 7.1.2-SNAPSHOT
stratiocommit Jun 14, 2021
ac90836
Make sis path configurable (#14)
majimenez-stratio Sep 7, 2021
1b06fec
Fix vuln (#15)
majimenez-stratio Sep 10, 2021
fb7cb0d
[BUILD] 7.1.2: New Pre-release
stratiocommit Sep 10, 2021
60ca41a
Prepare for next version: 7.1.2-SNAPSHOT
stratiocommit Sep 10, 2021
aee4e9a
Prepare for next version: 7.1.3-SNAPSHOT
stratiocommit Dec 15, 2021
0de6927
Update Jenkinsfile for new Jenkins (#20)
unai-ttxu Jun 21, 2022
30848a8
Set GOCACHE in Jenkinsfile (#22)
scervantes-stratio Jun 22, 2022
9a65cf3
Adapt versioning to new schema
vjacynycz Jul 19, 2022
b8fe6f2
Update Jenkinsfile
vjacynycz Jul 19, 2022
a4a0c63
[GOAL-1240] New parameters (username and tenants) for user
jorgejimenez-stratio Feb 8, 2023
5b81547
rebuild
jorgejimenez-stratio Feb 8, 2023
62dd65e
[GOAL-1240] New parameters (username and tenants) for user
jorgejimenez-stratio Feb 8, 2023
d484750
[GOAL-1240] New parameters (username and tenants) for user
jorgejimenez-stratio Feb 8, 2023
507a5e6
[GOAL-1240] New parameters (username and tenants) for user
jorgejimenez-stratio Feb 8, 2023
49f7ada
[GOAL-1240] New parameters (username and tenants) for user
jorgejimenez-stratio Feb 8, 2023
0c02fea
[GOAL-1240] New parameters (username and tenants) for user
jorgejimenez-stratio Feb 9, 2023
98951de
[GOAL-1240] New parameters (username and tenants) for user
jorgejimenez-stratio Feb 9, 2023
88a230e
rebuild
jorgejimenez-stratio Jun 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
815 changes: 19 additions & 796 deletions CHANGELOG.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.16-buster AS builder
FROM golang:1.16 AS builder

# Copy sources
WORKDIR $GOPATH/src/github.com/oauth2-proxy/oauth2-proxy
Expand All @@ -21,7 +21,7 @@ ARG VERSION
RUN VERSION=${VERSION} make build && touch jwt_signing_key.pem

# Copy binary to alpine
FROM alpine:3.13
FROM alpine:3.16
COPY nsswitch.conf /etc/nsswitch.conf
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=builder /go/src/github.com/oauth2-proxy/oauth2-proxy/oauth2-proxy /bin/oauth2-proxy
Expand Down
17 changes: 17 additions & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@Library('libpipelines') _

hose {
EMAIL = '[email protected]'
BUILDTOOL_IMAGE = 'golang:1.16'
BUILDTOOL = 'make'
DEVTIMEOUT = 30
ANCHORE_POLICY = "production"
VERSIONING_TYPE = 'stratioVersion-3-3'
UPSTREAM_VERSION = '7.1.2'
ANCHORE_TEST = true

DEV = { config ->
doUT(conf: config, parameters: "GOCACHE=/tmp")
doDocker(conf: config, image: 'oauth2-proxy')
}
}
14 changes: 13 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Stratio CICD flow
# Get version
version ?= $(shell cat VERSION)

GO ?= go
GOLANGCILINT ?= golangci-lint

Expand All @@ -19,6 +23,10 @@ ifeq ($(COVER),true)
TESTCOVER ?= -coverprofile c.out
endif

ifeq ($(LINT),true)
TESTLINT ?= lint
endif

.PHONY: all
all: lint $(BINARY)

Expand Down Expand Up @@ -78,7 +86,7 @@ verify-generate: generate
git diff --exit-code

.PHONY: test
test: lint
test: $(TESTLINT)
GO111MODULE=on $(GO) test $(TESTCOVER) -v -race ./...

.PHONY: release
Expand Down Expand Up @@ -106,3 +114,7 @@ validate-go-version:
.PHONY: local-env-%
local-env-%:
make -C contrib/local-environment $*

# Stratio CICD flow
change-version:
@echo $(version) > VERSION
1 change: 1 addition & 0 deletions VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.1.0-SNAPSHOT
2 changes: 1 addition & 1 deletion contrib/oauth2-proxy_autocomplete.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ _oauth2_proxy() {
COMPREPLY=( $(compgen -W 'X-Real-IP X-Forwarded-For X-ProxyUser-IP' -- ${cur}) )
return 0
;;
--@(http-address|https-address|redirect-url|upstream|basic-auth-password|skip-auth-regex|flush-interval|extra-jwt-issuers|email-domain|whitelist-domain|trusted-ip|keycloak-group|azure-tenant|bitbucket-team|bitbucket-repository|github-org|github-team|github-repo|github-token|gitlab-group|github-user|google-group|google-admin-email|google-service-account-json|client-id|client_secret|banner|footer|proxy-prefix|ping-path|cookie-name|cookie-secret|cookie-domain|cookie-path|cookie-expire|cookie-refresh|cookie-samesite|redist-sentinel-master-name|redist-sentinel-connection-urls|redist-cluster-connection-urls|logging-max-size|logging-max-age|logging-max-backups|standard-logging-format|request-logging-format|exclude-logging-paths|auth-logging-format|oidc-issuer-url|oidc-jwks-url|login-url|redeem-url|profile-url|resource|validate-url|scope|approval-prompt|signature-key|acr-values|jwt-key|pubjwk-url))
--@(http-address|https-address|redirect-url|upstream|basic-auth-password|skip-auth-regex|flush-interval|extra-jwt-issuers|email-domain|whitelist-domain|trusted-ip|keycloak-group|azure-tenant|bitbucket-team|bitbucket-repository|github-org|github-team|github-repo|github-token|gitlab-group|github-user|google-group|google-admin-email|google-service-account-json|client-id|client_secret|banner|footer|proxy-prefix|ping-path|cookie-name|cookie-secret|cookie-domain|cookie-path|cookie-expire|cookie-refresh|cookie-samesite|redist-sentinel-master-name|redist-sentinel-connection-urls|redist-cluster-connection-urls|logging-max-size|logging-max-age|logging-max-backups|standard-logging-format|request-logging-format|exclude-logging-paths|auth-logging-format|oidc-issuer-url|oidc-jwks-url|login-url|redeem-url|profile-url|resource|validate-url|scope|sign-out-url|approval-prompt|signature-key|acr-values|jwt-key|pubjwk-url|sis-root-url|jwt-session-key|jwt-session-key-file))
return 0
;;
esac
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/configuration/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/
| `--authenticated-emails-file` | string | authenticate against emails via file (one per line) | |
| `--azure-tenant` | string | go to a tenant-specific or common (tenant-independent) endpoint. | `"common"` |
| `--basic-auth-password` | string | the password to set when passing the HTTP Basic Auth header | |
| `--clear-extra-cookie-names` | string \| list | extra cookie names to clear when signing out | |
| `--client-id` | string | the OAuth Client ID, e.g. `"123456.apps.googleusercontent.com"` | |
| `--client-secret` | string | the OAuth Client Secret | |
| `--client-secret-file` | string | the file with OAuth Client Secret | |
Expand Down Expand Up @@ -119,6 +120,7 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/
| `--set-authorization-header` | bool | set Authorization Bearer response header (useful in Nginx auth_request mode) | false |
| `--set-basic-auth` | bool | set HTTP Basic Auth information in response (useful in Nginx auth_request mode) | false |
| `--show-debug-on-error` | bool | show detailed error information on error pages (WARNING: this may contain sensitive information - do not use in production) | false |
| `--sign-out-url` | string | Sign out endpoint | |
| `--signature-key` | string | GAP-Signature request signature key (algorithm:secretkey) | |
| `--silence-ping-logging` | bool | disable logging of requests to ping endpoint | false |
| `--skip-auth-preflight` | bool | will skip authentication for OPTIONS requests | false |
Expand Down
15 changes: 15 additions & 0 deletions docs/docs/configuration/sessions.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ data in one of the available session storage backends.
At present the available backends are (as passed to `--session-store-type`):
- [cookie](#cookie-storage) (default)
- [redis](#redis-storage)
- [jwt](#jwt-storage)

### Cookie Storage

Expand Down Expand Up @@ -65,3 +66,17 @@ Redis Cluster is available to be the backend store as well. To leverage it, you
`--redis-use-cluster=true` flag, and configure the flags `--redis-cluster-connection-urls` appropriately.

Note that flags `--redis-use-sentinel=true` and `--redis-use-cluster=true` are mutually exclusive.

### JWT Storage

The JWT Storage backend stores sessions as signed JWTs.

All session information is stored in token claims and send back to the browser as a cookie,
so it is transferred with each request.

Only basic information is persisted, no OIDC tokens or access tokens are created as claims in order to limit the size of the token itself.

#### Usage

When using the jwt store, specify `--session-store-type=jwt` as well as the signing key, via
`--jwt-session-key=\"${OAUTH2_PROXY_JWT_SESSION_KEY}\"` or `--jwt-session-key-file=/etc/ssl/private/jwt_session_signing_key.pem`.
28 changes: 26 additions & 2 deletions docs/versioned_docs/version-6.1.x/configuration/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/
| `--reverse-proxy` | bool | are we running behind a reverse proxy, controls whether headers like X-Real-IP are accepted | false |
| `--scope` | string | OAuth scope specification | |
| `--session-cookie-minimal` | bool | strip OAuth tokens from cookie session stores if they aren't needed (cookie session store only) | false |
| `--session-store-type` | string | [Session data storage backend](sessions.md); redis or cookie | cookie |
| `--session-store-type` | string | [Session data storage backend](sessions.md); redis, cookie or jwt | cookie |
| `--set-xauthrequest` | bool | set X-Auth-Request-User, X-Auth-Request-Email and X-Auth-Request-Preferred-Username response headers (useful in Nginx auth_request mode). When used with `--pass-access-token`, X-Auth-Request-Access-Token is added to response headers. | false |
| `--set-authorization-header` | bool | set Authorization Bearer response header (useful in Nginx auth_request mode) | false |
| `--set-basic-auth` | bool | set HTTP Basic Auth information in response (useful in Nginx auth_request mode) | false |
Expand All @@ -124,6 +124,7 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/
| `--ssl-upstream-insecure-skip-verify` | bool | skip validation of certificates presented when using HTTPS upstreams | false |
| `--standard-logging` | bool | Log standard runtime information | true |
| `--standard-logging-format` | string | Template for standard log lines | see [Logging Configuration](#logging-configuration) |
| `--sis-root-url` | string | Stratio SIS root URL | |
| `--tls-cert-file` | string | path to certificate file | |
| `--tls-key-file` | string | path to private key file | |
| `--upstream` | string \| list | the http url(s) of the upstream endpoint, file:// paths for static files or `static://<status_code>` for static response. Routing is based on the path | |
Expand All @@ -132,6 +133,8 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/
| `--version` | n/a | print version string | |
| `--whitelist-domain` | string \| list | allowed domains for redirection after authentication. Prefix domain with a `.` to allow subdomains (e.g. `.example.com`)&nbsp;\[[2](#footnote2)\] | |
| `--trusted-ip` | string \| list | list of IPs or CIDR ranges to allow to bypass authentication (may be given multiple times). When combined with `--reverse-proxy` and optionally `--real-client-ip-header` this will evaluate the trust of the IP stored in an HTTP header by a reverse proxy rather than the layer-3/4 remote address. WARNING: trusting IPs has inherent security flaws, especially when obtaining the IP address from an HTTP header (reverse-proxy mode). Use this option only if you understand the risks and how to manage them. | |
| `--jwt-session-key` | string | private key in PEM format used to sign session JWT, so that you can say something like `--jwt-session-key="${OAUTH2_PROXY_JWT_SESSION_KEY}"` | |
| `--jwt-session-key-file` | string | path to the private key file in PEM format used to sign the session JWT so that you can say something like `--jwt-session-key-file=/etc/ssl/private/jwt_session_signing_key.pem` | |

\[<a name="footnote1">1</a>\]: Only these providers support `--cookie-refresh`: GitLab, Google and OIDC

Expand Down Expand Up @@ -354,4 +357,25 @@ You have to substitute *name* with the actual cookie name you configured via --c

:::note
If you set up your OAuth2 provider to rotate your client secret, you can use the `client-secret-file` option to reload the secret when it is updated.
:::

## Using and testing the SIS provider
To test the SIS provider with JWT backend, a few things are needed before we launch the proxy.

1. Register a new app in SIS

Send a request like the one in the example below to the sis-api component:

`curl -X PUT --cert /opt/mesosphere/etc/pki/node.pem --key /opt/mesosphere/etc/pki/node.key -H "Content-Type: application/json" -d '{"name": "local","serviceId": "http://127.0.0.1:4180/oauth2/callback","clientSecret": "local"}' https://sis-api.service.eos.mike.hetzner.stratio.com:9006/registry/services/local`

2. Grab SIS CA file and save it to your local filesystem
3. Generate a new JWT signing key:

`openssl genrsa -out jwt-key 2048`

4. Launch `oauth2-proxy` with the following flags:

`oauth2-proxy --provider=sis --client-id=local --client-secret=local --email-domain="*" --redirect-url=http://127.0.0.1:4180 --provider-ca-file=sis-ca.crt --cookie-secure=false --sis-root-url=https://bootstrap.mike.hetzner.stratio.com:9005/sso --session-store-type=jwt --jwt-session-key-file=jwt-key`

This will launch a new proxy using the sis provider and jwt session storage.

5. Point your browser to http://127.0.0.1:4180/oauth2/start?rd=/oauth2/userinfo, you should be redirected to SIS login page, and after you are successfully authenticated, you'll be redirected to the userinfo page where your username and email is shown.
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/
| `--set-authorization-header` | bool | set Authorization Bearer response header (useful in Nginx auth_request mode) | false |
| `--set-basic-auth` | bool | set HTTP Basic Auth information in response (useful in Nginx auth_request mode) | false |
| `--show-debug-on-error` | bool | show detailed error information on error pages (WARNING: this may contain sensitive information - do not use in production) | false |
| `--sign-out-url` | string | Sign out endpoint | |
| `--signature-key` | string | GAP-Signature request signature key (algorithm:secretkey) | |
| `--silence-ping-logging` | bool | disable logging of requests to ping endpoint | false |
| `--skip-auth-preflight` | bool | will skip authentication for OPTIONS requests | false |
Expand Down
62 changes: 50 additions & 12 deletions oauthproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ type OAuthProxy struct {
realClientIPParser ipapi.RealClientIPParser
trustedIPs *ip.NetSet

ClearExtraCookieNames []string

sessionChain alice.Chain
headersChain alice.Chain
preAuthChain alice.Chain
Expand Down Expand Up @@ -196,18 +198,19 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
AuthOnlyPath: fmt.Sprintf("%s/auth", opts.ProxyPrefix),
UserInfoPath: fmt.Sprintf("%s/userinfo", opts.ProxyPrefix),

ProxyPrefix: opts.ProxyPrefix,
provider: opts.GetProvider(),
sessionStore: sessionStore,
serveMux: upstreamProxy,
redirectURL: redirectURL,
allowedRoutes: allowedRoutes,
whitelistDomains: opts.WhitelistDomains,
skipAuthPreflight: opts.SkipAuthPreflight,
skipJwtBearerTokens: opts.SkipJwtBearerTokens,
realClientIPParser: opts.GetRealClientIPParser(),
SkipProviderButton: opts.SkipProviderButton,
trustedIPs: trustedIPs,
ProxyPrefix: opts.ProxyPrefix,
provider: opts.GetProvider(),
sessionStore: sessionStore,
serveMux: upstreamProxy,
redirectURL: redirectURL,
allowedRoutes: allowedRoutes,
whitelistDomains: opts.WhitelistDomains,
skipAuthPreflight: opts.SkipAuthPreflight,
skipJwtBearerTokens: opts.SkipJwtBearerTokens,
realClientIPParser: opts.GetRealClientIPParser(),
SkipProviderButton: opts.SkipProviderButton,
trustedIPs: trustedIPs,
ClearExtraCookieNames: opts.ClearExtraCookieNames,

basicAuthValidator: basicAuthValidator,
sessionChain: sessionChain,
Expand Down Expand Up @@ -457,6 +460,18 @@ func (p *OAuthProxy) makeCookie(req *http.Request, name string, value string, ex
}
}

func (p *OAuthProxy) makeExtraCookie(req *http.Request, name string, value string, expiration time.Duration, now time.Time) *http.Cookie {
return &http.Cookie{
Name: name,
Value: value,
Path: p.CookiePath,
HttpOnly: p.CookieHTTPOnly,
Secure: p.CookieSecure,
Expires: now.Add(expiration),
SameSite: cookies.ParseSameSite(p.CookieSameSite),
}
}

// ClearCSRFCookie creates a cookie to unset the CSRF cookie stored in the user's
// session
func (p *OAuthProxy) ClearCSRFCookie(rw http.ResponseWriter, req *http.Request) {
Expand All @@ -479,6 +494,19 @@ func (p *OAuthProxy) LoadCookiedSession(req *http.Request) (*sessionsapi.Session
return p.sessionStore.Load(req)
}

// ClearExtraCookies clears extra cookies if found in request
func (p *OAuthProxy) ClearExtraCookies(rw http.ResponseWriter, req *http.Request) {
for _, name := range p.ClearExtraCookieNames {
c, err := req.Cookie(name)
if err != nil {
logger.Printf("Cookie %s could not be retrieved from request: %v", name, err)
continue
}
logger.Printf("Extra cookie %s found in request: %#v", name, c)
http.SetCookie(rw, p.makeExtraCookie(req, c.Name, "", time.Hour*-1, time.Now()))
}
}

// SaveSession creates a new session cookie value and sets this on the response
func (p *OAuthProxy) SaveSession(rw http.ResponseWriter, req *http.Request, s *sessionsapi.SessionState) error {
return p.sessionStore.Save(rw, req, s)
Expand Down Expand Up @@ -631,6 +659,7 @@ func (p *OAuthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code
p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
return
}
p.ClearExtraCookies(rw, req)
rw.WriteHeader(code)

redirectURL, err := p.getAppRedirect(req)
Expand Down Expand Up @@ -708,11 +737,17 @@ func (p *OAuthProxy) UserInfo(rw http.ResponseWriter, req *http.Request) {
Email string `json:"email"`
Groups []string `json:"groups,omitempty"`
PreferredUsername string `json:"preferredUsername,omitempty"`
Tenant string `json:"tenant,omitempty"`
Username string `json:"username,omitempty"`
Tenants []string `json:"tenants,omitempty"`
}{
User: session.User,
Email: session.Email,
Groups: session.Groups,
PreferredUsername: session.PreferredUsername,
Tenant: session.Tenant,
Username: session.Username,
Tenants: session.Tenants,
}

rw.Header().Set("Content-Type", "application/json")
Expand All @@ -732,12 +767,14 @@ func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) {
p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
return
}
redirect = p.provider.GetSignOutURL(redirect)
err = p.ClearSessionCookie(rw, req)
if err != nil {
logger.Errorf("Error clearing session cookie: %v", err)
p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
return
}
p.ClearExtraCookies(rw, req)
http.Redirect(rw, req, redirect, http.StatusFound)
}

Expand Down Expand Up @@ -1154,6 +1191,7 @@ func (p *OAuthProxy) getAuthenticatedSession(rw http.ResponseWriter, req *http.R
if err != nil {
logger.Errorf("Error clearing session cookie: %v", err)
}
p.ClearExtraCookies(rw, req)
return nil, ErrAccessDenied
}

Expand Down
Loading