Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6a46b8c
Add JWKS baseed kubernetes toekn validator
strideynet Aug 9, 2023
1139bb3
Add types for static_jwks kubernetes join type
strideynet Aug 9, 2023
c2f4ecb
Add validation for new fields on Provisiontoken
strideynet Aug 9, 2023
f7ceea0
SPAG
strideynet Aug 9, 2023
2a4c5ec
Add support into tbot for kubernetes join method
strideynet Aug 10, 2023
d780f60
Wire static_jwks implementation into auth.Server.checkKubernetesJoinR…
strideynet Aug 10, 2023
efd6268
fix-imports
strideynet Aug 10, 2023
5235279
Add JWKS tests for `kubernetestoken`
strideynet Aug 10, 2023
122fdac
Fix TestIDTokenValidator_Validate
strideynet Aug 10, 2023
27380f8
Ensure token is bound
strideynet Aug 10, 2023
d79272a
Fix mistakenely renamed comments
strideynet Aug 10, 2023
74b658c
Tidier return type
strideynet Aug 11, 2023
2303ea8
Refactor tests for slicker UI
strideynet Aug 11, 2023
246e9f5
Inject jwks token validator for test substitution
strideynet Aug 11, 2023
453ca20
Add test cases for static_jwks to lib/auth kube joining test
strideynet Aug 11, 2023
129f8b0
Remove TODO
strideynet Aug 14, 2023
eb797af
Add more test cases to token validation
strideynet Aug 14, 2023
5748fb8
Add enforcement of maximum TTL
strideynet Aug 14, 2023
cee0b5a
Merge remote-tracking branch 'origin/master' into strideynet/simplifi…
strideynet Aug 14, 2023
d872ff1
Regenerate operator crd
strideynet Aug 14, 2023
d03cc60
Improve comments on proto
strideynet Aug 14, 2023
673c157
Rerun ?? generation ?? of ?? operator ?? protos ??
strideynet Aug 14, 2023
29a66d8
Remove outdated comment
strideynet Aug 14, 2023
a6039da
Merge remote-tracking branch 'origin/master' into strideynet/simplifi…
strideynet Sep 11, 2023
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)
}
Comment thread
strideynet marked this conversation as resolved.
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