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 @@ -1804,6 +1804,18 @@ message ProvisionTokenSpecV2Kubernetes {
// This can be fetched from /openid/v1/jwks on the Kubernetes API Server.
string JWKS = 1 [(gogoproto.jsontag) = "jwks,omitempty"];
}
message OIDCConfig {
// Issuer is the URI of the OIDC issuer. It must have an accessible and
// OIDC-compliant `/.well-known/oidc-configuration` endpoint. This should
// be a valid URL and must exactly match the `issuer` field in a service
// account JWT. For example:
// https://oidc.eks.us-west-2.amazonaws.com/id/12345...
string Issuer = 1 [(gogoproto.jsontag) = "issuer,omitempty"];
Comment thread
timothyb89 marked this conversation as resolved.
Comment thread
timothyb89 marked this conversation as resolved.

// InsecureAllowHTTPIssuer is a flag that, if set, disables the requirement
// that the issuer must use HTTPS.
bool InsecureAllowHTTPIssuer = 2 [(gogoproto.jsontag) = "insecure_allow_http_issuer"];
}
// Rule is a set of properties the Kubernetes-issued token might have to be
// allowed to use this ProvisionToken
message Rule {
Expand All @@ -1818,13 +1830,16 @@ message ProvisionTokenSpecV2Kubernetes {
// Service Account token. Support values:
// - `in_cluster`
// - `static_jwks`
// - `oidc`
// If unset, this defaults to `in_cluster`.
string Type = 2 [
(gogoproto.jsontag) = "type,omitempty",
(gogoproto.casttype) = "KubernetesJoinType"
];
// StaticJWKS is the configuration specific to the `static_jwks` type.
StaticJWKSConfig StaticJWKS = 3 [(gogoproto.jsontag) = "static_jwks,omitempty"];
// OIDCConfig configures the `oidc` type.
OIDCConfig OIDC = 4 [(gogoproto.jsontag) = "oidc,omitempty"];
}

// ProvisionTokenSpecV2Azure contains the Azure-specific part of the
Expand Down
28 changes: 27 additions & 1 deletion api/types/provisioning.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"crypto/x509"
"encoding/pem"
"fmt"
"net/url"
"slices"
"strings"
"time"
Expand Down Expand Up @@ -122,6 +123,7 @@ var (
KubernetesJoinTypeUnspecified KubernetesJoinType = ""
KubernetesJoinTypeInCluster KubernetesJoinType = "in_cluster"
KubernetesJoinTypeStaticJWKS KubernetesJoinType = "static_jwks"
KubernetesJoinTypeOIDC KubernetesJoinType = "oidc"
)

// ProvisionToken is a provisioning token
Expand Down Expand Up @@ -783,10 +785,34 @@ func (a *ProvisionTokenSpecV2Kubernetes) checkAndSetDefaults() error {
if a.StaticJWKS.JWKS == "" {
return trace.BadParameter("static_jwks.jwks: must be set when type is %q", KubernetesJoinTypeStaticJWKS)
}
case KubernetesJoinTypeOIDC:
if a.OIDC == nil {
return trace.BadParameter("oidc: must be set when types is %q", KubernetesJoinTypeOIDC)
}
if a.OIDC.Issuer == "" {
return trace.BadParameter("oidc.issuer: must be set when type is %q", KubernetesJoinTypeOIDC)
}

parsed, err := url.Parse(a.OIDC.Issuer)
if err != nil {
return trace.BadParameter("oidc.issuer: must be a valid URL")
}

if parsed.Scheme == "http" {
if !a.OIDC.InsecureAllowHTTPIssuer {
return trace.BadParameter("oidc.issuer: must be https:// unless insecure_allow_http_issuer is set")
}
} else if parsed.Scheme != "https" {
return trace.BadParameter("oidc.issuer: invalid URL scheme, must be https://")
}
default:
return trace.BadParameter(
"type: must be one of (%s), got %q",
utils.JoinStrings(JoinMethods, ", "),
utils.JoinStrings([]string{
string(KubernetesJoinTypeInCluster),
string(KubernetesJoinTypeStaticJWKS),
string(KubernetesJoinTypeOIDC),
}, ", "),
a.Type,
)
}
Expand Down
73 changes: 73 additions & 0 deletions api/types/provisioning_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,79 @@ func TestProvisionTokenV2_CheckAndSetDefaults(t *testing.T) {
},
wantErr: true,
},
{
desc: "kubernetes: oidc must have valid issuer",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: JoinMethodKubernetes,
Kubernetes: &ProvisionTokenSpecV2Kubernetes{
Type: KubernetesJoinTypeOIDC,
Allow: []*ProvisionTokenSpecV2Kubernetes_Rule{
{
ServiceAccount: "namespace:my-service-account",
},
},
OIDC: &ProvisionTokenSpecV2Kubernetes_OIDCConfig{
Issuer: "https://example.com",
},
},
},
},
wantErr: false,
},
{
desc: "kubernetes: http issuers not allowed without override",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: JoinMethodKubernetes,
Kubernetes: &ProvisionTokenSpecV2Kubernetes{
Type: KubernetesJoinTypeOIDC,
Allow: []*ProvisionTokenSpecV2Kubernetes_Rule{
{
ServiceAccount: "namespace:my-service-account",
},
},
OIDC: &ProvisionTokenSpecV2Kubernetes_OIDCConfig{
Issuer: "http://example.com",
},
},
},
},
wantErr: true,
},
{
desc: "kubernetes: http issuers are allowed with override",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: JoinMethodKubernetes,
Kubernetes: &ProvisionTokenSpecV2Kubernetes{
Type: KubernetesJoinTypeOIDC,
Allow: []*ProvisionTokenSpecV2Kubernetes_Rule{
{
ServiceAccount: "namespace:my-service-account",
},
},
OIDC: &ProvisionTokenSpecV2Kubernetes_OIDCConfig{
Issuer: "http://example.com",
InsecureAllowHTTPIssuer: true,
},
},
},
},
wantErr: false,
},
{
desc: "gitlab empty allow rules",
token: &ProvisionTokenV2{
Expand Down
Loading
Loading