diff --git a/oauthproxy.go b/oauthproxy.go index 240dc81827..5ef92c331d 100644 --- a/oauthproxy.go +++ b/oauthproxy.go @@ -25,11 +25,11 @@ import ( sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/app/pagewriter" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/app/redirect" - "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/audit" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/authentication/basic" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/cookies" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption" proxyhttp "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/http" + picsaudit "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/pics/audit" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/ip" @@ -114,7 +114,8 @@ type OAuthProxy struct { appDirector redirect.AppDirector encodeState bool - AuditClient *audit.Client + + picsAuditClient *picsaudit.Client } // NewOAuthProxy creates a new instance of OAuthProxy from the options provided @@ -213,7 +214,7 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr Validator: redirectValidator, }) - auditClient, err := audit.NewAuditClient(&audit.ClientOpts{ + picsAuditClient, err := picsaudit.NewAuditClient(&picsaudit.ClientOpts{ URL: opts.AuditURL, Enabled: opts.EnableAudit, ProductName: opts.AuditProductName, @@ -256,7 +257,7 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr redirectValidator: redirectValidator, appDirector: appDirector, encodeState: opts.EncodeState, - AuditClient: auditClient, + picsAuditClient: picsAuditClient, } p.buildServeMux(opts.ProxyPrefix) @@ -434,7 +435,7 @@ func buildSessionChain(opts *options.Options, provider providers.Provider, sessi if oidcProviderSettings.CookieRefreshURL == "" { oidcProviderSettings.CookieRefreshURL = fmt.Sprintf("%s/session/refresh", oidcProviderSettings.IssuerURL) } - chain = chain.Append(middleware.NewCookieRefresh(&middleware.CookieRefreshOptions{CookieRefreshURL: oidcProviderSettings.CookieRefreshURL, CookieRefreshName: oidcProviderSettings.CookieRefreshName})) + chain = chain.Append(middleware.PicsNewCookieRefresh(&middleware.CookieRefreshOptions{CookieRefreshURL: oidcProviderSettings.CookieRefreshURL, CookieRefreshName: oidcProviderSettings.CookieRefreshName})) logger.Printf("Enabling OIDC cookie refresh functionality for the cookie '%s' using the url '%s' because OIDCEnableCookieRefresh is enabled", oidcProviderSettings.CookieRefreshURL, oidcProviderSettings.CookieRefreshName) } @@ -925,7 +926,7 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) { if !csrf.CheckOAuthState(nonce) { errorMsg := "Invalid authentication via OAuth2: CSRF token mismatch, potential attack" logger.PrintAuthf(session.Email, req, logger.AuthFailure, errorMsg) - p.AuditClient.CreateFailedLoginAuditEntry(session, appRedirect, req.Header.Get("edisp-org-id"), errorMsg) + p.picsAuditClient.CreateFailedLoginAuditEntry(session, appRedirect, req.Header.Get("edisp-org-id"), errorMsg) p.ErrorPage(rw, req, http.StatusForbidden, "CSRF token mismatch, potential attack", "Login Failed: Unable to find a valid CSRF token. Please try again.") return } @@ -954,7 +955,7 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) { p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error()) return } - p.AuditClient.CreateSuccessfulLoginAuditEntry(session, appRedirect, req.Header.Get("edisp-org-id")) + p.picsAuditClient.CreateSuccessfulLoginAuditEntry(session, appRedirect, req.Header.Get("edisp-org-id")) http.Redirect(rw, req, appRedirect, http.StatusFound) } else { logger.PrintAuthf(session.Email, req, logger.AuthFailure, "Invalid authentication via OAuth2: unauthorized") diff --git a/pkg/apis/options/legacy_options.go b/pkg/apis/options/legacy_options.go index 64ae80f4a8..3105528c8f 100644 --- a/pkg/apis/options/legacy_options.go +++ b/pkg/apis/options/legacy_options.go @@ -247,7 +247,7 @@ func (l *LegacyHeaders) getRequestHeaders() []Header { } if l.PassAuthorization { - requestHeaders = append(requestHeaders, getAuthorizationHeader()...) + requestHeaders = append(requestHeaders, PicsGetAuthorizationHeader()...) } for i := range requestHeaders { @@ -272,11 +272,11 @@ func (l *LegacyHeaders) getResponseHeaders() []Header { } if l.SetAuthorization { - responseHeaders = append(responseHeaders, getAuthorizationHeader()...) + responseHeaders = append(responseHeaders, PicsGetAuthorizationHeader()...) } if l.SetIntrospectionValue { - responseHeaders = append(responseHeaders, getXAuthIntrospectionValueHeaders()) + responseHeaders = append(responseHeaders, PicsGetXAuthIntrospectionValueHeaders()) } return responseHeaders } @@ -369,32 +369,20 @@ func getPassAccessTokenHeader() Header { } } -func getAuthorizationHeader() []Header { - headers := []Header{ - { - Name: "Authorization", - Values: []HeaderValue{ - { - ClaimSource: &ClaimSource{ - Claim: "id_token", - Prefix: "Bearer ", - }, - }, - }, - }, - { - Name: "x-auth-request-id-token", - Values: []HeaderValue{ - { - ClaimSource: &ClaimSource{ - Claim: "id_token", - }, - }, - }, - }, - } - return headers -} +// PICS: changed to PicsGetAuthorizationHeader, this one is not used anywhere +// func getAuthorizationHeader() Header { +// return Header{ +// Name: "Authorization", +// Values: []HeaderValue{ +// { +// ClaimSource: &ClaimSource{ +// Claim: "id_token", +// Prefix: "Bearer ", +// }, +// }, +// }, +// } +// } func getPreferredUsernameHeader() Header { return Header{ @@ -469,19 +457,6 @@ func getXAuthRequestAccessTokenHeader() Header { } } -func getXAuthIntrospectionValueHeaders() Header { - return Header{ - Name: "X-Auth-Introspect-Value", - Values: []HeaderValue{ - { - ClaimSource: &ClaimSource{ - Claim: "introspect-claims", - }, - }, - }, - } -} - type LegacyServer struct { MetricsAddress string `flag:"metrics-address" cfg:"metrics_address"` MetricsSecureAddress string `flag:"metrics-secure-address" cfg:"metrics_secure_address"` diff --git a/pkg/apis/options/pics_legacy_options.go b/pkg/apis/options/pics_legacy_options.go new file mode 100644 index 0000000000..a612f1c0f5 --- /dev/null +++ b/pkg/apis/options/pics_legacy_options.go @@ -0,0 +1,41 @@ +package options + +func PicsGetAuthorizationHeader() []Header { + headers := []Header{ + { + Name: "Authorization", + Values: []HeaderValue{ + { + ClaimSource: &ClaimSource{ + Claim: "id_token", + Prefix: "Bearer ", + }, + }, + }, + }, + { + Name: "x-auth-request-id-token", + Values: []HeaderValue{ + { + ClaimSource: &ClaimSource{ + Claim: "id_token", + }, + }, + }, + }, + } + return headers +} + +func PicsGetXAuthIntrospectionValueHeaders() Header { + return Header{ + Name: "X-Auth-Introspect-Value", + Values: []HeaderValue{ + { + ClaimSource: &ClaimSource{ + Claim: "introspect-claims", + }, + }, + }, + } +} diff --git a/pkg/middleware/cookie_refresh.go b/pkg/middleware/pics_cookie_refresh.go similarity index 96% rename from pkg/middleware/cookie_refresh.go rename to pkg/middleware/pics_cookie_refresh.go index 890594abca..38127f7557 100644 --- a/pkg/middleware/cookie_refresh.go +++ b/pkg/middleware/pics_cookie_refresh.go @@ -15,7 +15,7 @@ type CookieRefreshOptions struct { CookieRefreshURL string } -func NewCookieRefresh(opts *CookieRefreshOptions) alice.Constructor { +func PicsNewCookieRefresh(opts *CookieRefreshOptions) alice.Constructor { cr := &cookieRefresh{ HTTPClient: &http.Client{}, CookieRefreshName: opts.CookieRefreshName, diff --git a/pkg/audit/audit_client.go b/pkg/pics/audit/audit_client.go similarity index 98% rename from pkg/audit/audit_client.go rename to pkg/pics/audit/audit_client.go index 2603dfde23..46b7079c80 100644 --- a/pkg/audit/audit_client.go +++ b/pkg/pics/audit/audit_client.go @@ -33,7 +33,7 @@ type Client struct { func NewAuditClient(opts *ClientOpts) (*Client, error) { if opts.Enabled { log.Print("Audit entries will be created since OAUTH2_PROXY_ENABLE_AUDIT is true") - err := opts.Validate() + err := opts.validate() if err != nil { return nil, err } @@ -154,7 +154,7 @@ func (c *Client) send(msg string) error { return nil } -func (c *ClientOpts) Validate() error { +func (c *ClientOpts) validate() error { err := errors.New("") if strings.TrimSpace(c.URL) == "" { err = errors.New("the OAUTH2_PROXY_AUDIT_URL must be set") diff --git a/pkg/audit/audit_error.go b/pkg/pics/audit/audit_error.go similarity index 100% rename from pkg/audit/audit_error.go rename to pkg/pics/audit/audit_error.go diff --git a/pkg/audit/audit_event.go b/pkg/pics/audit/audit_event.go similarity index 99% rename from pkg/audit/audit_event.go rename to pkg/pics/audit/audit_event.go index c0b9555efa..6d1429d06e 100644 --- a/pkg/audit/audit_event.go +++ b/pkg/pics/audit/audit_event.go @@ -81,6 +81,7 @@ type ExtensionContent struct { URL string `json:"url,omitempty"` ValueString string `json:"valueString,omitempty"` } + type Extension struct { URL string `json:"url,omitempty"` Extension []*ExtensionContent `json:"extension,omitempty"` diff --git a/pkg/audit/audit_metrics.go b/pkg/pics/audit/audit_metrics.go similarity index 100% rename from pkg/audit/audit_metrics.go rename to pkg/pics/audit/audit_metrics.go diff --git a/pkg/audit/signature.go b/pkg/pics/audit/signature.go similarity index 100% rename from pkg/audit/signature.go rename to pkg/pics/audit/signature.go diff --git a/providers/oidc.go b/providers/oidc.go index 0b664964d0..dae8f08914 100644 --- a/providers/oidc.go +++ b/providers/oidc.go @@ -1,19 +1,15 @@ package providers import ( - "bytes" "context" - b64 "encoding/base64" "errors" "fmt" - "net/http" "net/url" "time" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" - "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" "golang.org/x/oauth2" ) @@ -98,7 +94,8 @@ func (p *OIDCProvider) Redeem(ctx context.Context, redirectURL, code, codeVerifi // EnrichSession is called after Redeem to allow providers to enrich session fields // such as User, Email, Groups with provider specific API calls. func (p *OIDCProvider) EnrichSession(ctx context.Context, s *sessions.SessionState) error { - err := p.enrichFromIntrospectURL(ctx, s) + + err := p.PicsEnrichFromIntrospectURL(ctx, s) if err != nil { logger.Errorf("Warning: Introspect URL request failed: %v", err) } @@ -130,40 +127,6 @@ func (p *OIDCProvider) ValidateSession(ctx context.Context, s *sessions.SessionS return true } -// enrichFromIntrospectURL enriches a session's claims and permissions via the JSON response of -// an OIDC Introspection URL -func (p *OIDCProvider) enrichFromIntrospectURL(ctx context.Context, s *sessions.SessionState) error { - clientSecret, err := p.GetClientSecret() - if err != nil { - return err - } - params := url.Values{} - params.Add("token", s.AccessToken) - basicAuth := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", p.ClientID, clientSecret))) - if p.IntrospectURL == nil { - p.IntrospectURL = &url.URL{ - Scheme: p.RedeemURL.Scheme, - Host: p.RedeemURL.Host, - Path: "/authorize/oauth2/v4/introspect", - } - } - logger.Printf("Requesting introspect from '%s'", p.IntrospectURL) - - result := requests.New(p.IntrospectURL.String()). - WithContext(ctx). - WithMethod("POST"). - WithBody(bytes.NewBufferString(params.Encode())). - SetHeader("Authorization", fmt.Sprintf("Basic %s", basicAuth)). - SetHeader("Content-Type", "application/x-www-form-urlencoded"). - Do() - - if result.StatusCode() != http.StatusOK { - return fmt.Errorf("error while requesting introspect claims, status code - %d", result.StatusCode()) - } - s.IntrospectClaims = b64.StdEncoding.EncodeToString(result.Body()) - return nil -} - // RefreshSession uses the RefreshToken to fetch new Access and ID Tokens func (p *OIDCProvider) RefreshSession(ctx context.Context, s *sessions.SessionState) (bool, error) { if s == nil || s.RefreshToken == "" { diff --git a/providers/pics_oidc.go b/providers/pics_oidc.go new file mode 100644 index 0000000000..6ca2c7aa8d --- /dev/null +++ b/providers/pics_oidc.go @@ -0,0 +1,48 @@ +package providers + +import ( + "bytes" + "context" + b64 "encoding/base64" + "fmt" + "net/http" + "net/url" + + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" + "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests" +) + +// enrichFromIntrospectURL enriches a session's claims and permissions via the JSON response of +// an OIDC Introspection URL +func (p *OIDCProvider) PicsEnrichFromIntrospectURL(ctx context.Context, s *sessions.SessionState) error { + clientSecret, err := p.GetClientSecret() + if err != nil { + return err + } + params := url.Values{} + params.Add("token", s.AccessToken) + basicAuth := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", p.ClientID, clientSecret))) + if p.IntrospectURL == nil { + p.IntrospectURL = &url.URL{ + Scheme: p.RedeemURL.Scheme, + Host: p.RedeemURL.Host, + Path: "/authorize/oauth2/v4/introspect", + } + } + logger.Printf("Requesting introspect from '%s'", p.IntrospectURL) + + result := requests.New(p.IntrospectURL.String()). + WithContext(ctx). + WithMethod("POST"). + WithBody(bytes.NewBufferString(params.Encode())). + SetHeader("Authorization", fmt.Sprintf("Basic %s", basicAuth)). + SetHeader("Content-Type", "application/x-www-form-urlencoded"). + Do() + + if result.StatusCode() != http.StatusOK { + return fmt.Errorf("error while requesting introspect claims, status code - %d", result.StatusCode()) + } + s.IntrospectClaims = b64.StdEncoding.EncodeToString(result.Body()) + return nil +}