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
17 changes: 17 additions & 0 deletions api/proto/teleport/legacy/types/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1340,6 +1340,12 @@ message ProvisionTokenSpecV2CircleCI {
// ProvisionTokenSpecV2Kubernetes contains the Kubernetes-specific part of the
// ProvisionTokenSpecV2
message ProvisionTokenSpecV2Kubernetes {
message StaticJWKSConfig {
// JWKS should be the JSON Web Key Set formatted public keys of that the
// Kubernetes Cluster uses to sign service account tokens.
// This can be fetched from /openid/v1/jwks on the Kubernetes API Server.
string JWKS = 1 [(gogoproto.jsontag) = "jwks,omitempty"];
}
// Rule is a set of properties the Kubernetes-issued token might have to be
// allowed to use this ProvisionToken
message Rule {
Expand All @@ -1350,6 +1356,17 @@ message ProvisionTokenSpecV2Kubernetes {
// Allow is a list of Rules, nodes using this token must match one
// allow rule to use this token.
repeated Rule Allow = 1 [(gogoproto.jsontag) = "allow,omitempty"];
// Type controls which behavior should be used for validating the Kubernetes
// Service Account token. Support values:
// - `in_cluster`
// - `static_jwks`
// 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"];
}

// ProvisionTokenSpecV2Azure contains the Azure-specific part of the
Expand Down
51 changes: 41 additions & 10 deletions api/types/provisioning.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ func ValidateJoinMethod(method JoinMethod) error {
return nil
}

type KubernetesJoinType string

var (
KubernetesJoinTypeUnspecified KubernetesJoinType = ""
KubernetesJoinTypeInCluster KubernetesJoinType = "in_cluster"
KubernetesJoinTypeStaticJWKS KubernetesJoinType = "static_jwks"
)

// ProvisionToken is a provisioning token
type ProvisionToken interface {
ResourceWithOrigin
Expand Down Expand Up @@ -265,7 +273,7 @@ func (p *ProvisionTokenV2) CheckAndSetDefaults() error {
)
}
if err := providerCfg.checkAndSetDefaults(); err != nil {
return trace.Wrap(err)
return trace.Wrap(err, "spec.kubernetes:")
}
case JoinMethodAzure:
providerCfg := p.Spec.Azure
Expand Down Expand Up @@ -585,26 +593,49 @@ func (a *ProvisionTokenSpecV2CircleCI) checkAndSetDefaults() error {

func (a *ProvisionTokenSpecV2Kubernetes) checkAndSetDefaults() error {
if len(a.Allow) == 0 {
return trace.BadParameter(
"the %q join method requires defined kubernetes allow rules",
JoinMethodKubernetes,
)
return trace.BadParameter("allow: at least one rule must be set")
}
for _, allowRule := range a.Allow {
for i, allowRule := range a.Allow {
if allowRule.ServiceAccount == "" {
return trace.BadParameter(
"the %q join method requires kubernetes allow rules with non-empty service account name",
JoinMethodKubernetes,
"allow[%d].service_account: name of service account must be set",
i,
)
}
if len(strings.Split(allowRule.ServiceAccount, ":")) != 2 {
return trace.BadParameter(
`the %q join method service account rule format is "namespace:service_account", got %q instead`,
JoinMethodKubernetes,
`allow[%d].service_account: name of service account should be in format "namespace:service_account", got %q instead`,
i,
allowRule.ServiceAccount,
)
}
}

if a.Type == KubernetesJoinTypeUnspecified {
// For compatibility with older resources which did not have a Type
// field we default to "in_cluster".
a.Type = KubernetesJoinTypeInCluster
}
switch a.Type {
case KubernetesJoinTypeInCluster:
if a.StaticJWKS != nil {
return trace.BadParameter("static_jwks: must not be set when type is %q", KubernetesJoinTypeInCluster)
}
case KubernetesJoinTypeStaticJWKS:
if a.StaticJWKS == nil {
return trace.BadParameter("static_jwks: must be set when type is %q", KubernetesJoinTypeStaticJWKS)
}
if a.StaticJWKS.JWKS == "" {
return trace.BadParameter("static_jwks.jwks: must be set when type is %q", KubernetesJoinTypeStaticJWKS)
}
default:
return trace.BadParameter(
"type: must be one of (%s), got %q",
apiutils.JoinStrings(JoinMethods, ", "),
a.Type,
)
}

return nil
}

Expand Down
112 changes: 109 additions & 3 deletions api/types/provisioning_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ func TestProvisionTokenV2_CheckAndSetDefaults(t *testing.T) {
expectedErr: &trace.BadParameterError{},
},
{
desc: "kubernetes valid",
desc: "kubernetes: in_cluster defaults",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
Expand All @@ -411,9 +411,115 @@ func TestProvisionTokenV2_CheckAndSetDefaults(t *testing.T) {
},
},
},
expected: &ProvisionTokenV2{
Kind: "token",
Version: "v2",
Metadata: Metadata{
Name: "test",
Namespace: "default",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: JoinMethodKubernetes,
Kubernetes: &ProvisionTokenSpecV2Kubernetes{
Type: KubernetesJoinTypeInCluster,
Allow: []*ProvisionTokenSpecV2Kubernetes_Rule{
{
ServiceAccount: "namespace:my-service-account",
},
},
},
},
},
},
{
desc: "kubernetes: valid in_cluster",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: JoinMethodKubernetes,
Kubernetes: &ProvisionTokenSpecV2Kubernetes{
Type: KubernetesJoinTypeInCluster,
Allow: []*ProvisionTokenSpecV2Kubernetes_Rule{
{
ServiceAccount: "namespace:my-service-account",
},
},
},
},
},
},
{
desc: "kubernetes: valid static_jwks",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: JoinMethodKubernetes,
Kubernetes: &ProvisionTokenSpecV2Kubernetes{
Type: KubernetesJoinTypeStaticJWKS,
Allow: []*ProvisionTokenSpecV2Kubernetes_Rule{
{
ServiceAccount: "namespace:my-service-account",
},
},
StaticJWKS: &ProvisionTokenSpecV2Kubernetes_StaticJWKSConfig{
JWKS: `{"keys":[{"use":"sig","kty":"RSA","kid":"-snip-","alg":"RS256","n":"-snip-","e":"-snip-"}]}`,
},
},
},
},
},
{
desc: "kubernetes: missing static_jwks",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: JoinMethodKubernetes,
Kubernetes: &ProvisionTokenSpecV2Kubernetes{
Type: KubernetesJoinTypeStaticJWKS,
Allow: []*ProvisionTokenSpecV2Kubernetes_Rule{
{
ServiceAccount: "namespace:my-service-account",
},
},
},
},
},
expectedErr: &trace.BadParameterError{},
},
{
desc: "kubernetes: missing static_jwks.jwks",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
},
Spec: ProvisionTokenSpecV2{
Roles: []SystemRole{RoleNode},
JoinMethod: JoinMethodKubernetes,
Kubernetes: &ProvisionTokenSpecV2Kubernetes{
Type: KubernetesJoinTypeStaticJWKS,
Allow: []*ProvisionTokenSpecV2Kubernetes_Rule{
{
ServiceAccount: "namespace:my-service-account",
},
},
StaticJWKS: &ProvisionTokenSpecV2Kubernetes_StaticJWKSConfig{},
},
},
},
expectedErr: &trace.BadParameterError{},
},
{
desc: "kubernetes wrong service account name",
desc: "kubernetes: wrong service account name",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
Expand All @@ -433,7 +539,7 @@ func TestProvisionTokenV2_CheckAndSetDefaults(t *testing.T) {
expectedErr: &trace.BadParameterError{},
},
{
desc: "kubernetes allow rule blank",
desc: "kubernetes: allow rule blank",
token: &ProvisionTokenV2{
Metadata: Metadata{
Name: "test",
Expand Down
Loading