From 3c9dba367a689f13a67917d0896392b322795623 Mon Sep 17 00:00:00 2001 From: Anton Miniailo Date: Mon, 4 Dec 2023 23:27:31 -0500 Subject: [PATCH] Fix client IP propagation from the Proxy to the Auth during IdP initiated SSO --- lib/auth/auth_with_roles.go | 11 ++++++++--- lib/auth/clt.go | 2 +- lib/auth/http_client.go | 5 +++-- lib/auth/saml.go | 13 +++++++++---- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/lib/auth/auth_with_roles.go b/lib/auth/auth_with_roles.go index f813dd7f32492..71c8850619250 100644 --- a/lib/auth/auth_with_roles.go +++ b/lib/auth/auth_with_roles.go @@ -3432,15 +3432,20 @@ func (a *ServerWithRoles) CreateSAMLAuthRequest(ctx context.Context, req types.S } // ValidateSAMLResponse validates SAML auth response. -func (a *ServerWithRoles) ValidateSAMLResponse(ctx context.Context, re string, connectorID string) (*SAMLAuthResponse, error) { +func (a *ServerWithRoles) ValidateSAMLResponse(ctx context.Context, samlResponse, connectorID, clientIP string) (*SAMLAuthResponse, error) { + isProxy := a.hasBuiltinRole(types.RoleProxy) + if !isProxy { + clientIP = "" // We only trust IP information coming from the Proxy. + } + // auth callback is it's own authz, no need to check extra permissions - resp, err := a.authServer.ValidateSAMLResponse(ctx, re, connectorID) + resp, err := a.authServer.ValidateSAMLResponse(ctx, samlResponse, connectorID, clientIP) if err != nil { return nil, trace.Wrap(err) } // Only the Proxy service can create web sessions via SAML connector. - if resp.Session != nil && !a.hasBuiltinRole(types.RoleProxy) { + if resp.Session != nil && !isProxy { return nil, trace.AccessDenied("this request can be only executed by a proxy") } diff --git a/lib/auth/clt.go b/lib/auth/clt.go index 728abdd19430d..cda3d2a46f224 100644 --- a/lib/auth/clt.go +++ b/lib/auth/clt.go @@ -554,7 +554,7 @@ type IdentityService interface { // CreateSAMLAuthRequest creates SAML AuthnRequest CreateSAMLAuthRequest(ctx context.Context, req types.SAMLAuthRequest) (*types.SAMLAuthRequest, error) // ValidateSAMLResponse validates SAML auth response - ValidateSAMLResponse(ctx context.Context, re string, connectorID string) (*SAMLAuthResponse, error) + ValidateSAMLResponse(ctx context.Context, samlResponse, connectorID, clientIP string) (*SAMLAuthResponse, error) // GetSAMLAuthRequest returns SAML auth request if found GetSAMLAuthRequest(ctx context.Context, authRequestID string) (*types.SAMLAuthRequest, error) diff --git a/lib/auth/http_client.go b/lib/auth/http_client.go index 89c28d9e4b820..17b69db24700f 100644 --- a/lib/auth/http_client.go +++ b/lib/auth/http_client.go @@ -774,10 +774,11 @@ func (c *HTTPClient) ValidateOIDCAuthCallback(ctx context.Context, q url.Values) } // ValidateSAMLResponse validates response returned by SAML identity provider -func (c *HTTPClient) ValidateSAMLResponse(ctx context.Context, re string, connectorID string) (*SAMLAuthResponse, error) { +func (c *HTTPClient) ValidateSAMLResponse(ctx context.Context, samlResponse, connectorID, clientIP string) (*SAMLAuthResponse, error) { out, err := c.PostJSON(ctx, c.Endpoint("saml", "requests", "validate"), ValidateSAMLResponseReq{ - Response: re, + Response: samlResponse, ConnectorID: connectorID, + ClientIP: clientIP, }) if err != nil { return nil, trace.Wrap(err) diff --git a/lib/auth/saml.go b/lib/auth/saml.go index 1fb05d0ab667d..ac72af57b82bc 100644 --- a/lib/auth/saml.go +++ b/lib/auth/saml.go @@ -47,7 +47,7 @@ type SAMLService interface { // CreateSAMLAuthRequest creates SAML AuthnRequest CreateSAMLAuthRequest(ctx context.Context, req types.SAMLAuthRequest) (*types.SAMLAuthRequest, error) // ValidateSAMLResponse validates SAML auth response - ValidateSAMLResponse(ctx context.Context, re string, connectorID string) (*SAMLAuthResponse, error) + ValidateSAMLResponse(ctx context.Context, samlResponse, connectorID, clientIP string) (*SAMLAuthResponse, error) } // UpsertSAMLConnector creates or updates a SAML connector. @@ -168,12 +168,12 @@ func (a *Server) CreateSAMLAuthRequest(ctx context.Context, req types.SAMLAuthRe // ValidateSAMLResponse delegates the method call to the samlAuthService if present, // or returns a NotImplemented error if not present. -func (a *Server) ValidateSAMLResponse(ctx context.Context, re string, connectorID string) (*SAMLAuthResponse, error) { +func (a *Server) ValidateSAMLResponse(ctx context.Context, samlResponse, connectorID, clientIP string) (*SAMLAuthResponse, error) { if a.samlAuthService == nil { return nil, trace.Wrap(ErrSAMLRequiresEnterprise) } - resp, err := a.samlAuthService.ValidateSAMLResponse(ctx, re, connectorID) + resp, err := a.samlAuthService.ValidateSAMLResponse(ctx, samlResponse, connectorID, clientIP) return resp, trace.Wrap(err) } @@ -218,8 +218,13 @@ type SAMLAuthRequest struct { // ValidateSAMLResponseReq is the request made by the proxy to validate // and activate a login via SAML. type ValidateSAMLResponseReq struct { - Response string `json:"response"` + // Response is SAML statements coming from the identity provider. + Response string `json:"response"` + // ConnectorID is ID of a SAML connector that should be used for this request. ConnectorID string `json:"connector_id,omitempty"` + // ClientIP is IP of the logging in client, used in identity provider initiated login case, + // when we don't have original client's request with their IP stored. + ClientIP string `json:"client_ip,omitempty"` } // SAMLAuthRawResponse is returned when auth server validated callback parameters