Skip to content

Commit

Permalink
Re-add integration tests for jwks
Browse files Browse the repository at this point in the history
We removed them from kubernetes#10756, but they can be re-added.
  • Loading branch information
justinsb committed Mar 21, 2021
1 parent 15e4028 commit 3ebe8d0
Show file tree
Hide file tree
Showing 12 changed files with 81 additions and 39 deletions.
24 changes: 24 additions & 0 deletions cmd/kops/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ func newIntegrationTest(clusterName, srcDir string) *integrationTest {
}
}

// withCAKey indicates that we should use a fixed ca.crt & ca.key from the source directory as our CA.
// This is needed when the CA is exposed, for example when using AWS WebIdentity federation.
func (i *integrationTest) withCAKey() *integrationTest {
i.caKey = true
return i
}
func (i *integrationTest) withVersion(version string) *integrationTest {
i.version = version
return i
Expand Down Expand Up @@ -114,6 +120,12 @@ func (i *integrationTest) withPrivate() *integrationTest {
return i
}

// withServiceAccountRoles indicates we expect to assign per-ServiceAccount IAM roles (instead of just using the node roles)
func (i *integrationTest) withServiceAccountRoles() *integrationTest {
i.expectServiceAccountRoles = true
return i
}

func (i *integrationTest) withBastionUserData() *integrationTest {
i.bastionUserData = true
return i
Expand Down Expand Up @@ -262,6 +274,18 @@ func TestPrivateDns2(t *testing.T) {
newIntegrationTest("privatedns2.example.com", "privatedns2").withPrivate().runTestTerraformAWS(t)
}

// TestPublicJWKS runs a simple configuration, but with UseServiceAccountIAM and PublicJWKS enabled
func TestPublicJWKS(t *testing.T) {
featureflag.ParseFlags("+UseServiceAccountIAM,+PublicJWKS")
unsetFeatureFlags := func() {
featureflag.ParseFlags("-UseServiceAccountIAM,-PublicJWKS")
}
defer unsetFeatureFlags()

// We have to use a fixed CA because the fingerprint is inserted into the AWS WebIdentity configuration.
newIntegrationTest("minimal.example.com", "public-jwks").withCAKey().withServiceAccountRoles().runTestTerraformAWS(t)
}

// TestSharedSubnet runs the test on a configuration with a shared subnet (and VPC)
func TestSharedSubnet(t *testing.T) {
newIntegrationTest("sharedsubnet.example.com", "shared_subnet").runTestTerraformAWS(t)
Expand Down
1 change: 1 addition & 0 deletions pkg/apis/kops/validation/BUILD.bazel

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

20 changes: 18 additions & 2 deletions pkg/apis/kops/validation/legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"k8s.io/kops/pkg/featureflag"
"k8s.io/kops/pkg/util/subnet"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/util/pkg/vfs"
)

// legacy contains validation functions that don't match the apimachinery style
Expand Down Expand Up @@ -374,8 +375,23 @@ func ValidateCluster(c *kops.Cluster, strict bool) field.ErrorList {

publicDataStore := c.Spec.PublicDataStore
if publicDataStore != "" {
if !strings.HasPrefix(publicDataStore, "s3://") {
allErrs = append(allErrs, field.Invalid(fieldSpec.Child("publicDataStore"), publicDataStore, "S3 is the only supported VFS for publicStore"))
base, err := vfs.Context.BuildVfsPath(publicDataStore)
if err != nil {
allErrs = append(allErrs, field.Invalid(fieldSpec.Child("publicDataStore"), publicDataStore, "not a valid VFS path"))
} else {
switch base := base.(type) {
case *vfs.S3Path:
// OK
case *vfs.MemFSPath:
if !base.IsClusterReadable() {
// If this _is_ a test, we should call MarkClusterReadable
allErrs = append(allErrs, field.Invalid(fieldSpec.Child("publicDataStore"), publicDataStore, "S3 is the only supported VFS for publicStore"))
} else {
// memfs is OK for tests
}
default:
allErrs = append(allErrs, field.Invalid(fieldSpec.Child("publicDataStore"), publicDataStore, "S3 is the only supported VFS for publicStore"))
}
}
}

Expand Down
7 changes: 7 additions & 0 deletions pkg/model/iam/subject.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package iam

import (
"fmt"
"strings"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
Expand Down Expand Up @@ -108,6 +109,12 @@ func ServiceAccountIssuer(clusterSpec *kops.ClusterSpec) (string, error) {
return "", err
}
return baseURL + "/oidc", nil
case *vfs.MemFSPath:
if !base.IsClusterReadable() {
// If this _is_ a test, we should call MarkClusterReadable
return "", fmt.Errorf("cluster.spec.publicDataStore=%q is only supported in tests", clusterSpec.PublicDataStore)
}
return strings.Replace(base.Path(), "memfs://", "https://", 1) + "/oidc", nil
default:
return "", fmt.Errorf("cluster.spec.publicDataStore=%q is of unexpected type %T", clusterSpec.PublicDataStore, base)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"api.minimal.example.com:sub": "system:serviceaccount:kube-system:dns-controller"
"discovery.example.com/minimal.example.com/oidc:sub": "system:serviceaccount:kube-system:dns-controller"
}
},
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/api.minimal.example.com"
"Federated": "arn:aws:iam::123456789012:oidc-provider/discovery.example.com/minimal.example.com/oidc"
}
}
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,35 +135,6 @@
"Resource": [
"*"
]
},
{
"Action": [
"route53:ChangeResourceRecordSets",
"route53:ListResourceRecordSets",
"route53:GetHostedZone"
],
"Effect": "Allow",
"Resource": [
"arn:aws:route53:::hostedzone/Z1AFAKE1ZON3YO"
]
},
{
"Action": [
"route53:GetChange"
],
"Effect": "Allow",
"Resource": [
"arn:aws:route53:::change/*"
]
},
{
"Action": [
"route53:ListHostedZones"
],
"Effect": "Allow",
"Resource": [
"*"
]
}
],
"Version": "2012-10-17"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ etcdClusters:
version: 3.4.13
kubeAPIServer:
allowPrivileged: true
anonymousAuth: true
anonymousAuth: false
apiAudiences:
- kubernetes.svc.default
apiServerCount: 1
Expand All @@ -190,6 +190,8 @@ kubeAPIServer:
- http://127.0.0.1:4001
etcdServersOverrides:
- /events#http://127.0.0.1:4002
featureGates:
ServiceAccountIssuerDiscovery: "true"
image: k8s.gcr.io/kube-apiserver:v1.20.0
kubeletPreferredAddressTypes:
- InternalIP
Expand All @@ -205,8 +207,8 @@ kubeAPIServer:
requestheaderUsernameHeaders:
- X-Remote-User
securePort: 443
serviceAccountIssuer: https://api.minimal.example.com
serviceAccountJWKSURI: https://api.minimal.example.com/openid/v1/jwks
serviceAccountIssuer: https://discovery.example.com/minimal.example.com/oidc
serviceAccountJWKSURI: https://discovery.example.com/minimal.example.com/oidc/keys.json
serviceClusterIPRange: 100.64.0.0/13
storageBackend: etcd3
kubeControllerManager:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ spec:
networking:
cni: {}
nonMasqueradeCIDR: 100.64.0.0/10
publicDataStore: memfs://discovery.example.com/minimal.example.com
sshAccess:
- 0.0.0.0/0
topology:
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/update_cluster/public-jwks/kubernetes.tf
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,8 @@ resource "aws_iam_instance_profile" "nodes-minimal-example-com" {

resource "aws_iam_openid_connect_provider" "minimal-example-com" {
client_id_list = ["amazonaws.com"]
thumbprint_list = ["a8de31f85544b9e73aeb26ded19330e0e996fb79"]
url = "https://api.minimal.example.com"
thumbprint_list = ["9e99a48a9960b14926bb7f3b02e22da2b0ab7280", "a9d53002e97e00e043244f3d170d6f4c414104fd"]
url = "https://discovery.example.com/minimal.example.com/oidc"
}

resource "aws_iam_role_policy" "dns-controller-kube-system-sa-minimal-example-com" {
Expand Down
14 changes: 14 additions & 0 deletions upup/pkg/fi/cloudup/bootstrapchannelbuilder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,20 @@ func TestBootstrapChannelBuilder_BuildTasks(t *testing.T) {
runChannelBuilderTest(t, "awsiamauthenticator", []string{"authentication.aws-k8s-1.12"})
}

func TestBootstrapChannelBuilder_PublicJWKS(t *testing.T) {
h := testutils.NewIntegrationTestHarness(t)
defer h.Close()

h.SetupMockAWS()

featureflag.ParseFlags("+PublicJWKS,+UseServiceAccountIAM")
unsetFeatureFlag := func() {
featureflag.ParseFlags("-PublicJWKS,-UseServiceAccountIAM")
}
defer unsetFeatureFlag()
runChannelBuilderTest(t, "public-jwks", []string{"dns-controller.addons.k8s.io-k8s-1.12", "kops-controller.addons.k8s.io-k8s-1.16", "anonymous-issuer-discovery.addons.k8s.io-k8s-1.16"})
}

func TestBootstrapChannelBuilder_AWSCloudController(t *testing.T) {
h := testutils.NewIntegrationTestHarness(t)
defer h.Close()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ spec:
networking:
kubenet: {}
nonMasqueradeCIDR: 100.64.0.0/10
publicDataStore: memfs://discovery.example.com/minimal.example.com
sshAccess:
- 0.0.0.0/0
topology:
Expand Down
7 changes: 6 additions & 1 deletion upup/pkg/fi/fitasks/managedfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,16 @@ func (_ *ManagedFile) Render(c *fi.Context, a, e, changes *ManagedFile) error {

var acl vfs.ACL
if fi.BoolValue(e.Public) {
switch p.(type) {
switch p := p.(type) {
case *vfs.S3Path:
acl = &vfs.S3Acl{
RequestACL: fi.String("public-read"),
}
case *vfs.MemFSPath:
if !p.IsClusterReadable() {
return fmt.Errorf("the %q path is intended for use in tests", p.Path())
}
acl = nil
default:
return fmt.Errorf("the %q path does not support public ACL", p.Path())
}
Expand Down

0 comments on commit 3ebe8d0

Please sign in to comment.