Files
teleport/lib/web/saml.go
T
Brian Joerger ce20b20753 PIV login enforcement (#15874)
Add private key policy enforcement.

  - Add private key policy cert extensions and enforcement.

  - Add private key policy settings and attestation logic.

  - Wire attestation request through login endpoints.

  - Store attestation data for reissue requests.

  - Add private key policy discovery and logic.

  - Relogin on hardware key policy errors.

  - Include integration with Teleport Connect.
2022-09-30 23:27:48 +00:00

171 lines
5.5 KiB
Go

/*
Copyright 2015-2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package web
import (
"errors"
"net/http"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/client"
"github.com/gravitational/teleport/lib/httplib"
"github.com/gravitational/form"
"github.com/gravitational/trace"
"github.com/julienschmidt/httprouter"
)
func (h *Handler) samlSSO(w http.ResponseWriter, r *http.Request, p httprouter.Params) string {
logger := h.log.WithField("auth", "saml")
logger.Debug("Web login start.")
req, err := parseSSORequestParams(r)
if err != nil {
logger.WithError(err).Error("Failed to extract SSO parameters from request.")
return client.LoginFailedRedirectURL
}
response, err := h.cfg.ProxyClient.CreateSAMLAuthRequest(r.Context(), types.SAMLAuthRequest{
ConnectorID: req.connectorID,
CSRFToken: req.csrfToken,
CreateWebSession: true,
ClientRedirectURL: req.clientRedirectURL,
})
if err != nil {
logger.WithError(err).Error("Error creating auth request.")
return client.LoginFailedRedirectURL
}
return response.RedirectURL
}
func (h *Handler) samlSSOConsole(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) {
logger := h.log.WithField("auth", "saml")
logger.Debug("Console login start.")
req := new(client.SSOLoginConsoleReq)
if err := httplib.ReadJSON(r, req); err != nil {
logger.WithError(err).Error("Error reading json.")
return nil, trace.AccessDenied(ssoLoginConsoleErr)
}
if err := req.CheckAndSetDefaults(); err != nil {
logger.WithError(err).Error("Missing request parameters.")
return nil, trace.AccessDenied(ssoLoginConsoleErr)
}
response, err := h.cfg.ProxyClient.CreateSAMLAuthRequest(r.Context(), types.SAMLAuthRequest{
ConnectorID: req.ConnectorID,
ClientRedirectURL: req.RedirectURL,
PublicKey: req.PublicKey,
CertTTL: req.CertTTL,
Compatibility: req.Compatibility,
RouteToCluster: req.RouteToCluster,
KubernetesCluster: req.KubernetesCluster,
AttestationStatement: req.AttestationStatement.ToProto(),
})
if err != nil {
logger.WithError(err).Error("Failed to create SAML auth request.")
return nil, trace.AccessDenied(ssoLoginConsoleErr)
}
return &client.SSOLoginConsoleResponse{RedirectURL: response.RedirectURL}, nil
}
func (h *Handler) samlACS(w http.ResponseWriter, r *http.Request, p httprouter.Params) string {
logger := h.log.WithField("auth", "saml")
logger.Debug("Callback start.")
var samlResponse string
if err := form.Parse(r, form.String("SAMLResponse", &samlResponse, form.Required())); err != nil {
logger.WithError(err).Error("Error parsing response.")
return client.LoginFailedRedirectURL
}
response, err := h.cfg.ProxyClient.ValidateSAMLResponse(r.Context(), samlResponse, p.ByName("connector"))
if err != nil {
logger.WithError(err).Error("Error while processing callback.")
// try to find the auth request, which bears the original client redirect URL.
// if found, use it to terminate the flow.
//
// this improves the UX by terminating the failed SSO flow immediately, rather than hoping for a timeout.
if requestID, errParse := auth.ParseSAMLInResponseTo(samlResponse); errParse == nil {
if request, errGet := h.cfg.ProxyClient.GetSAMLAuthRequest(r.Context(), requestID); errGet == nil && !request.CreateWebSession {
if url, errEnc := redirectURLWithError(request.ClientRedirectURL, err); errEnc == nil {
return url.String()
}
}
}
if errors.Is(err, auth.ErrSAMLNoRoles) {
return client.LoginFailedUnauthorizedRedirectURL
}
return client.LoginFailedBadCallbackRedirectURL
}
// if we created web session, set session cookie and redirect to original url
if response.Req.CreateWebSession {
logger.Debug("Redirecting to web browser.")
redirect := response.Req.ClientRedirectURL
if redirect == "" {
redirect = "/web/"
}
res := &ssoCallbackResponse{
csrfToken: response.Req.CSRFToken,
username: response.Username,
sessionName: response.Session.GetName(),
clientRedirectURL: redirect,
}
if err := ssoSetWebSessionAndRedirectURL(w, r, res, response.Req.CSRFToken != ""); err != nil {
logger.WithError(err).Error("Error setting web session.")
return client.LoginFailedRedirectURL
}
return res.clientRedirectURL
}
logger.Debug("Callback redirecting to console login.")
if len(response.Req.PublicKey) == 0 {
logger.Error("Not a web or console login request.")
return client.LoginFailedRedirectURL
}
redirectURL, err := ConstructSSHResponse(AuthParams{
ClientRedirectURL: response.Req.ClientRedirectURL,
Username: response.Username,
Identity: response.Identity,
Session: response.Session,
Cert: response.Cert,
TLSCert: response.TLSCert,
HostSigners: response.HostSigners,
})
if err != nil {
logger.WithError(err).Error("Error constructing ssh response.")
return client.LoginFailedRedirectURL
}
return redirectURL.String()
}