Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 15 additions & 0 deletions api/proto/teleport/legacy/types/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5283,6 +5283,14 @@ message SAMLConnectorSpecV2 {
// ForceAuthn specified whether re-authentication should be forced on login. UNSPECIFIED
// is treated as NO.
SAMLForceAuthn ForceAuthn = 18 [(gogoproto.jsontag) = "force_authn,omitempty"];
// PreferredRequestBinding is a preferred SAML request binding method.
// Value must be either "http-post" or "http-redirect".
// In general, the SAML identity provider lists request binding methods it supports.
// And the SAML service provider uses one of the IdP supported request binding method that it prefers.
// But we never honored request binding value provided by the IdP and always used http-redirect
// binding as a default. Setting up PreferredRequestBinding value lets us preserve existing
// auth connector behavior and only use http-post binding if it is explicitly configured.
string PreferredRequestBinding = 19 [(gogoproto.jsontag) = "preferred_request_binding,omitempty"];
}

// SAMLConnectorMFASettings contains SAML MFA settings.
Expand Down Expand Up @@ -5335,6 +5343,7 @@ message SAMLAuthRequest {
bool CheckUser = 4 [(gogoproto.jsontag) = "check_user"];

// RedirectURL will be used by browser.
// Value only set if the PreferredRequestBinding "http-redirect".
string RedirectURL = 5 [(gogoproto.jsontag) = "redirect_url"];

// PublicKey is an optional public key, users want these
Expand Down Expand Up @@ -5401,6 +5410,12 @@ message SAMLAuthRequest {
teleport.attestation.v1.AttestationStatement ssh_attestation_statement = 21 [(gogoproto.jsontag) = "ssh_attestation_statement,omitempty"];
// TlsAttestationStatement is an attestation statement for the given TLS public key.
teleport.attestation.v1.AttestationStatement tls_attestation_statement = 22 [(gogoproto.jsontag) = "tls_attestation_statement,omitempty"];
// PostForm is the HTML form value that contains the SAML authentication request data.
// Value is only set if the PreferredRequestBinding in the SAMLConnectorSpecV2
// is "http-post". In any other case, RedirectURL field will be populated.
bytes PostForm = 23 [(gogoproto.jsontag) = "post_form,omitempty"];
// ClientVersion is the version of tsh or Proxy that is sending the SAMLAuthRequest request.
string ClientVersion = 24 [(gogoproto.jsontag) = "client_version,omitempty"];
}

// AttributeMapping maps a SAML attribute statement to teleport roles.
Expand Down
18 changes: 18 additions & 0 deletions api/types/saml.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ type SAMLConnector interface {
WithMFASettings() error
// GetForceAuthn returns ForceAuthn
GetForceAuthn() bool
// GetPreferredRequestBinding returns PreferredRequestBinding.
GetPreferredRequestBinding() string
}

// NewSAMLConnector returns a new SAMLConnector based off a name and SAMLConnectorSpecV2.
Expand Down Expand Up @@ -446,6 +448,21 @@ func (o *SAMLConnectorV2) GetForceAuthn() bool {
return o.Spec.ForceAuthn == SAMLForceAuthn_FORCE_AUTHN_YES
}

const (
// SAMLRequestHTTPRedirectBinding is the SAML http-redirect binding request name.
SAMLRequestHTTPRedirectBinding = "http-redirect"
// SAMLRequestHTTPPostBinding is the SAML http-post binding request name.
SAMLRequestHTTPPostBinding = "http-post"
)

// SAMLRequestBindingValues includes supported SAML request binding values.
var SAMLRequestBindingValues = []string{SAMLRequestHTTPRedirectBinding, SAMLRequestHTTPPostBinding}

// GetPreferredRequestBinding returns PreferredRequestBinding.
func (o *SAMLConnectorV2) GetPreferredRequestBinding() string {
return o.Spec.PreferredRequestBinding
}

// setStaticFields sets static resource header and metadata fields.
func (o *SAMLConnectorV2) setStaticFields() {
o.Kind = KindSAMLConnector
Expand Down Expand Up @@ -487,6 +504,7 @@ func (o *SAMLConnectorV2) CheckAndSetDefaults() error {
return trace.BadParameter("need roles field in attributes_to_roles")
}
}

return nil
}

Expand Down
4,157 changes: 2,157 additions & 2,000 deletions api/types/types.pb.go

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ resource, which you can apply after installing the Teleport Kubernetes operator.
|force_authn|string or integer|ForceAuthn specified whether re-authentication should be forced on login. UNSPECIFIED is treated as NO. Can be either the string or the integer representation of each option.|
|issuer|string|Issuer is the identity provider issuer.|
|mfa|[object](#specmfa)|MFASettings contains settings to enable SSO MFA checks through this auth connector.|
|preferred_request_binding|string|PreferredRequestBinding is a preferred SAML request binding method. Value must be either "http-post" or "http-redirect". In general, the SAML identity provider lists request binding methods it supports. And the SAML service provider uses one of the IdP supported request binding method that it prefers. But we never honored request binding value provided by the IdP and always used http-redirect binding as a default. Setting up PreferredRequestBinding value lets us preserve existing auth connector behavior and only use http-post binding if it is explicitly configured.|
|provider|string|Provider is the external identity provider.|
|service_provider_issuer|string|ServiceProviderIssuer is the issuer of the service provider (Teleport).|
|signing_key_pair|[object](#specsigning_key_pair)|SigningKeyPair is an x509 key pair used to sign AuthnRequest.|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Optional:
- `force_authn` (Number) ForceAuthn specified whether re-authentication should be forced on login. UNSPECIFIED is treated as NO.
- `issuer` (String) Issuer is the identity provider issuer.
- `mfa` (Attributes) MFASettings contains settings to enable SSO MFA checks through this auth connector. (see [below for nested schema](#nested-schema-for-specmfa))
- `preferred_request_binding` (String) PreferredRequestBinding is a preferred SAML request binding method. Value must be either "http-post" or "http-redirect". In general, the SAML identity provider lists request binding methods it supports. And the SAML service provider uses one of the IdP supported request binding method that it prefers. But we never honored request binding value provided by the IdP and always used http-redirect binding as a default. Setting up PreferredRequestBinding value lets us preserve existing auth connector behavior and only use http-post binding if it is explicitly configured.
- `provider` (String) Provider is the external identity provider.
- `service_provider_issuer` (String) ServiceProviderIssuer is the issuer of the service provider (Teleport).
- `signing_key_pair` (Attributes) SigningKeyPair is an x509 key pair used to sign AuthnRequest. (see [below for nested schema](#nested-schema-for-specsigning_key_pair))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ Optional:
- `force_authn` (Number) ForceAuthn specified whether re-authentication should be forced on login. UNSPECIFIED is treated as NO.
- `issuer` (String) Issuer is the identity provider issuer.
- `mfa` (Attributes) MFASettings contains settings to enable SSO MFA checks through this auth connector. (see [below for nested schema](#nested-schema-for-specmfa))
- `preferred_request_binding` (String) PreferredRequestBinding is a preferred SAML request binding method. Value must be either "http-post" or "http-redirect". In general, the SAML identity provider lists request binding methods it supports. And the SAML service provider uses one of the IdP supported request binding method that it prefers. But we never honored request binding value provided by the IdP and always used http-redirect binding as a default. Setting up PreferredRequestBinding value lets us preserve existing auth connector behavior and only use http-post binding if it is explicitly configured.
- `provider` (String) Provider is the external identity provider.
- `service_provider_issuer` (String) ServiceProviderIssuer is the issuer of the service provider (Teleport).
- `signing_key_pair` (Attributes) SigningKeyPair is an x509 key pair used to sign AuthnRequest. (see [below for nested schema](#nested-schema-for-specsigning_key_pair))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,17 @@ spec:
Usually set from EntityDescriptor.
type: string
type: object
preferred_request_binding:
description: PreferredRequestBinding is a preferred SAML request binding
method. Value must be either "http-post" or "http-redirect". In
general, the SAML identity provider lists request binding methods
it supports. And the SAML service provider uses one of the IdP supported
request binding method that it prefers. But we never honored request
binding value provided by the IdP and always used http-redirect
binding as a default. Setting up PreferredRequestBinding value lets
us preserve existing auth connector behavior and only use http-post
binding if it is explicitly configured.
type: string
provider:
description: Provider is the external identity provider.
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,17 @@ spec:
Usually set from EntityDescriptor.
type: string
type: object
preferred_request_binding:
description: PreferredRequestBinding is a preferred SAML request binding
method. Value must be either "http-post" or "http-redirect". In
general, the SAML identity provider lists request binding methods
it supports. And the SAML service provider uses one of the IdP supported
request binding method that it prefers. But we never honored request
binding value provided by the IdP and always used http-redirect
binding as a default. Setting up PreferredRequestBinding value lets
us preserve existing auth connector behavior and only use http-post
binding if it is explicitly configured.
type: string
provider:
description: Provider is the external identity provider.
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import (

var samlSpec = &types.SAMLConnectorSpecV2{
Issuer: "issuer",
SSO: "sso",
SSO: "https://example.com",
AssertionConsumerService: "acs",
Audience: "audience",
ServiceProviderIssuer: "spi",
Expand Down
44 changes: 44 additions & 0 deletions integrations/terraform/tfschema/types_terraform.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/auth/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1448,7 +1448,7 @@ func TestSAMLConnectorCRUDEventsEmitted(t *testing.T) {
saml, err := types.NewSAMLConnector("test", types.SAMLConnectorSpecV2{
AssertionConsumerService: "a",
Issuer: "b",
SSO: "c",
SSO: "https://example.com",
AttributesToRoles: []types.AttributeMapping{
{
Name: "dummy",
Expand Down
2 changes: 1 addition & 1 deletion lib/auth/grpcserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4498,7 +4498,7 @@ func TestSAMLValidation(t *testing.T) {
const minimalEntityDescriptor = `
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="http://example.com">
<md:IDPSSODescriptor>
<md:SingleSignOnService Location="http://example.com" />
<md:SingleSignOnService Location="https://example.com" />
</md:IDPSSODescriptor>
</md:EntityDescriptor>`

Expand Down
7 changes: 6 additions & 1 deletion lib/client/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -4213,8 +4213,13 @@ func (tc *TeleportClient) SSOLoginFn(connectorID, connectorName, connectorType s
}
defer rd.Close()

ssoCeremony := sso.NewCLICeremony(rd, tc.ssoLoginInitFn(keyRing, connectorID, connectorType))
if connectorType == constants.SAML {
ssoCeremony := sso.NewCLISAMLCeremony(rd, tc.samlSSOLoginInitFn(keyRing, connectorID, connectorType))
resp, err := ssoCeremony.Run(ctx)
return resp, trace.Wrap(err)

}
ssoCeremony := sso.NewCLICeremony(rd, tc.ssoLoginInitFn(keyRing, connectorID, connectorType))
resp, err := ssoCeremony.Run(ctx)
return resp, trace.Wrap(err)
}
Expand Down
74 changes: 45 additions & 29 deletions lib/client/sso.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (

"github.com/gravitational/trace"

"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/utils/prompt"
"github.com/gravitational/teleport/lib/client/sso"
"github.com/gravitational/teleport/lib/utils"
Expand Down Expand Up @@ -61,42 +62,57 @@ func (tc *TeleportClient) ssoRedirectorConfig(ctx context.Context, connectorDisp

func (tc *TeleportClient) ssoLoginInitFn(keyRing *KeyRing, connectorID, connectorType string) sso.CeremonyInit {
return func(ctx context.Context, clientCallbackURL string) (redirectURL string, err error) {
sshLogin, err := tc.NewSSHLogin(keyRing)
redirectURL, _ /* postform */, err = tc.loginInitFn(ctx, keyRing, clientCallbackURL, connectorID, connectorType)
if err != nil {
return "", trace.Wrap(err)
}
return redirectURL, nil
}
}

// initiate SSO login through the Proxy.
req := SSOLoginConsoleReq{
RedirectURL: clientCallbackURL,
SSOUserPublicKeys: SSOUserPublicKeys{
SSHPubKey: sshLogin.SSHPubKey,
TLSPubKey: sshLogin.TLSPubKey,
SSHAttestationStatement: sshLogin.SSHAttestationStatement,
TLSAttestationStatement: sshLogin.TLSAttestationStatement,
},
CertTTL: sshLogin.TTL,
ConnectorID: connectorID,
Compatibility: sshLogin.Compatibility,
RouteToCluster: sshLogin.RouteToCluster,
KubernetesCluster: sshLogin.KubernetesCluster,
}
func (tc *TeleportClient) loginInitFn(ctx context.Context, keyRing *KeyRing, clientCallbackURL string, connectorID, connectorType string) (redirectURL string, postForm string, err error) {
sshLogin, err := tc.NewSSHLogin(keyRing)
if err != nil {
return "", "", trace.Wrap(err)
}

clt, _, err := initClient(sshLogin.ProxyAddr, sshLogin.Insecure, sshLogin.Pool, sshLogin.ExtraHeaders)
if err != nil {
return "", trace.Wrap(err)
}
// initiate SSO login through the Proxy.
req := SSOLoginConsoleReq{
RedirectURL: clientCallbackURL,
SSOUserPublicKeys: SSOUserPublicKeys{
SSHPubKey: sshLogin.SSHPubKey,
TLSPubKey: sshLogin.TLSPubKey,
SSHAttestationStatement: sshLogin.SSHAttestationStatement,
TLSAttestationStatement: sshLogin.TLSAttestationStatement,
},
CertTTL: sshLogin.TTL,
ConnectorID: connectorID,
Compatibility: sshLogin.Compatibility,
RouteToCluster: sshLogin.RouteToCluster,
KubernetesCluster: sshLogin.KubernetesCluster,
ClientVersion: teleport.Version,
}

out, err := clt.PostJSON(ctx, clt.Endpoint("webapi", connectorType, "login", "console"), req)
if err != nil {
return "", trace.Wrap(err)
}
clt, _, err := initClient(sshLogin.ProxyAddr, sshLogin.Insecure, sshLogin.Pool, sshLogin.ExtraHeaders)
if err != nil {
return "", "", trace.Wrap(err)
}

var re SSOLoginConsoleResponse
if err := json.Unmarshal(out.Bytes(), &re); err != nil {
return "", trace.Wrap(err)
}
out, err := clt.PostJSON(ctx, clt.Endpoint("webapi", connectorType, "login", "console"), req)
if err != nil {
return "", "", trace.Wrap(err)
}

var re SSOLoginConsoleResponse
if err := json.Unmarshal(out.Bytes(), &re); err != nil {
return "", "", trace.Wrap(err)
}

return re.RedirectURL, re.PostForm, nil
}

return re.RedirectURL, nil
func (tc *TeleportClient) samlSSOLoginInitFn(keyRing *KeyRing, connectorID, connectorType string) sso.SAMLCeremonyInit {
return func(ctx context.Context, clientCallbackURL string) (redirectURL string, postForm string, err error) {
return tc.loginInitFn(ctx, keyRing, clientCallbackURL, connectorID, connectorType)
}
}
36 changes: 36 additions & 0 deletions lib/client/sso/ceremony.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,42 @@ func NewCLICeremony(rd *Redirector, init CeremonyInit) *Ceremony {
}
}

// SAMLCeremony handles SAML login ceremony.
type SAMLCeremony struct {
clientCallbackURL string
Init SAMLCeremonyInit
HandleRequest func(ctx context.Context, redirectURL, postformData string) error
GetCallbackResponse func(ctx context.Context) (*authclient.SSHLoginResponse, error)
}

// SAMLCeremonyInit initializes an SAML based SSO login ceremony.
type SAMLCeremonyInit func(ctx context.Context, clientCallbackURL string) (redirectURL, postformData string, err error)

// Run the SAML SSO ceremony.
func (c *SAMLCeremony) Run(ctx context.Context) (*authclient.SSHLoginResponse, error) {
redirectURL, postformData, err := c.Init(ctx, c.clientCallbackURL)
if err != nil {
return nil, trace.Wrap(err)
}

if err := c.HandleRequest(ctx, redirectURL, postformData); err != nil {
return nil, trace.Wrap(err)
}

resp, err := c.GetCallbackResponse(ctx)
return resp, trace.Wrap(err)
}

// NewCLISAMLCeremony creates a new CLI SAML SSO ceremony from the given redirector.
func NewCLISAMLCeremony(rd *Redirector, init SAMLCeremonyInit) *SAMLCeremony {
return &SAMLCeremony{
clientCallbackURL: rd.ClientCallbackURL,
Init: init,
HandleRequest: rd.OpenLoginURL,
GetCallbackResponse: rd.WaitForResponse,
}
}

// Ceremony is a customizable SSO MFA ceremony.
type MFACeremony struct {
close func()
Expand Down
Loading
Loading