diff --git a/k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-external-identity.yaml b/k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-external-identity.yaml index 20c406ed5c..f1073265ac 100644 --- a/k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-external-identity.yaml +++ b/k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-external-identity.yaml @@ -48,14 +48,17 @@ metadata: policies.kyverno.io/title: Restrict GitHub Team External Identity policies.kyverno.io/category: Security, GitHub policies.kyverno.io/severity: high - policies.kyverno.io/subject: Team + policies.kyverno.io/subject: Team, TeamMembership, TeamRepository policies.kyverno.io/minversion: 1.13.0 policies.kyverno.io/description: >- Refuses a github-config Team whose crossplane.io/external-name is set or changed to anything other than the approved numeric identity of that team, so an allow-listed Team object cannot be pointed at a different GitHub - team after it was admitted. The provider's own record of a team it - created is exempt. + team after it was admitted, and a TeamMembership or TeamRepository whose + crossplane.io/external-name disagrees with the team and the user or + repository the object declares, so a compliant-looking membership or + grant cannot stand for another one. The provider's own records are + exempt. spec: background: false rules: @@ -117,3 +120,172 @@ spec: - key: "{{ externalName == '' || externalName == previousExternalName || contains(['admins/18573307', 'maintainers/17046606'], join('/', [request.object.metadata.name, externalName])) }}" operator: Equals value: false + # A membership or repository grant has a stored identity of its own: + # ":" or ":" (#4518). Read from the + # released sources of the provider version prod runs (provider-upjet-github + # v0.20.0, terraform-provider-github v6.13.0, upjet v2.2.0): + # + # - a repository grant is read, updated and removed by that stored identity + # alone, so a grant object that satisfies every rule in + # restrict-github-team-management could change or remove the access of a + # different team than the one it references; + # - a membership refuses an update when its stored identity names another + # team or user, but its removal acts on what was read through that + # identity. + # + # So the stored identity has to agree with what the object itself declares. + # From anyone but the provider it may be absent, exactly what the object + # already carries while it keeps referencing the same team and the same + # repository or user, or the approved identity of the referenced team + # followed by the declared repository or user. The two team identities are + # the ones the Team rule approves, repeated here for the reason given at the + # top of this file: a change of one is a change of all three rules. + # + # "Unchanged" covers the team reference and the repository or user as well + # as the identity, so an object cannot keep an identity while being + # re-declared to stand for something else. The provider records identities + # in exactly the approved shape (measured on prod 2026-10-05: all 49 + # memberships and grants), so the artifact's routine re-apply passes either + # way. + - name: teamrepositories-external-name-matches-declared-grant + match: + any: + - resources: + kinds: + - team.github.m.upbound.io/*/TeamRepository + operations: + - CREATE + - UPDATE + namespaces: + - github-config + exclude: + any: + - subjects: + - kind: ServiceAccount + name: provider-upjet-github-* + namespace: crossplane-system + context: + - name: externalName + variable: + jmesPath: request.object.metadata.annotations."crossplane.io/external-name" + default: "" + - name: teamRef + variable: + jmesPath: request.object.spec.forProvider.teamIdRef.name + default: "" + - name: subject + variable: + jmesPath: request.object.spec.forProvider.repository + default: "" + - name: initSubject + variable: + jmesPath: request.object.spec.initProvider.repository + default: "" + - name: previousExternalName + variable: + jmesPath: request.oldObject.metadata.annotations."crossplane.io/external-name" + default: "" + - name: previousTeamRef + variable: + jmesPath: request.oldObject.spec.forProvider.teamIdRef.name + default: "" + - name: previousSubject + variable: + jmesPath: request.oldObject.spec.forProvider.repository + default: "" + validate: + failureAction: Enforce + # As on the Team rule: an object that already fails must not be waved + # through on update. + allowExistingViolations: false + message: >- + The crossplane.io/external-name of a github-config TeamRepository may + only be left unset, removed or left unchanged while its team reference + and repository stay as they were, or set to the approved numeric identity of the referenced + team followed by a colon and spec.forProvider.repository. A stored + identity cannot be combined with spec.initProvider.repository. To point an + object at another team or repository, delete it and create it again. + deny: + conditions: + all: + # One literal JMESPath expression, for the reason given on the + # Team rule. The team reference is compared on its own and the + # team identity is a literal prefix, so no value can borrow a + # separator to look like another pair. An empty repository is never an + # approved identity, and one stated under initProvider is not + # compared here, so it may not accompany a stored identity at all + # (no object on prod states one: measured 2026-10-05). + - key: "{{ (externalName == '' && (previousExternalName == '' || (teamRef == previousTeamRef && subject == previousSubject))) || (initSubject == '' && ((externalName == previousExternalName && teamRef == previousTeamRef && subject == previousSubject) || (subject != '' && ((teamRef == 'admins' && externalName == join('', ['18573307:', subject])) || (teamRef == 'maintainers' && externalName == join('', ['17046606:', subject])))))) }}" + operator: Equals + value: false + - name: teammemberships-external-name-matches-declared-member + match: + any: + - resources: + kinds: + - team.github.m.upbound.io/*/TeamMembership + operations: + - CREATE + - UPDATE + namespaces: + - github-config + exclude: + any: + - subjects: + - kind: ServiceAccount + name: provider-upjet-github-* + namespace: crossplane-system + context: + - name: externalName + variable: + jmesPath: request.object.metadata.annotations."crossplane.io/external-name" + default: "" + - name: teamRef + variable: + jmesPath: request.object.spec.forProvider.teamIdRef.name + default: "" + - name: subject + variable: + jmesPath: request.object.spec.forProvider.username + default: "" + - name: initSubject + variable: + jmesPath: request.object.spec.initProvider.username + default: "" + - name: previousExternalName + variable: + jmesPath: request.oldObject.metadata.annotations."crossplane.io/external-name" + default: "" + - name: previousTeamRef + variable: + jmesPath: request.oldObject.spec.forProvider.teamIdRef.name + default: "" + - name: previousSubject + variable: + jmesPath: request.oldObject.spec.forProvider.username + default: "" + validate: + failureAction: Enforce + # As on the Team rule: an object that already fails must not be waved + # through on update. + allowExistingViolations: false + message: >- + The crossplane.io/external-name of a github-config TeamMembership may + only be left unset, removed or left unchanged while its team reference + and username stay as they were, or set to the approved numeric identity of the referenced + team followed by a colon and spec.forProvider.username. A stored + identity cannot be combined with spec.initProvider.username. To point an + object at another team or username, delete it and create it again. + deny: + conditions: + all: + # One literal JMESPath expression, for the reason given on the + # Team rule. The team reference is compared on its own and the + # team identity is a literal prefix, so no value can borrow a + # separator to look like another pair. An empty username is never an + # approved identity, and one stated under initProvider is not + # compared here, so it may not accompany a stored identity at all + # (no object on prod states one: measured 2026-10-05). + - key: "{{ (externalName == '' && (previousExternalName == '' || (teamRef == previousTeamRef && subject == previousSubject))) || (initSubject == '' && ((externalName == previousExternalName && teamRef == previousTeamRef && subject == previousSubject) || (subject != '' && ((teamRef == 'admins' && externalName == join('', ['18573307:', subject])) || (teamRef == 'maintainers' && externalName == join('', ['17046606:', subject])))))) }}" + operator: Equals + value: false diff --git a/tests/policy-failure-actions.json b/tests/policy-failure-actions.json index bc65a4bbbf..14702c8afb 100644 --- a/tests/policy-failure-actions.json +++ b/tests/policy-failure-actions.json @@ -4,6 +4,8 @@ "audit-privileged-rbac/privileged-rules": "Enforce", "disallow-latest-tag/require-image-tag": "Audit", "disallow-latest-tag/validate-image-tag": "Audit", + "restrict-github-team-external-identity/teammemberships-external-name-matches-declared-member": "Enforce", + "restrict-github-team-external-identity/teamrepositories-external-name-matches-declared-grant": "Enforce", "restrict-github-team-external-identity/teams-external-name-is-an-approved-identity": "Enforce", "restrict-github-team-management/teams-allow-listed": "Enforce", "restrict-github-team-management/teams-bind-provider-identity-to-object-name": "Enforce", diff --git a/tests/restrict-github-team-external-identity/grants-create/kyverno-test.yaml b/tests/restrict-github-team-external-identity/grants-create/kyverno-test.yaml new file mode 100644 index 0000000000..15f10f0014 --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-create/kyverno-test.yaml @@ -0,0 +1,37 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Test +metadata: + name: restrict-github-team-external-identity-grants-create +policies: + - >- + ../../../k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-external-identity.yaml +resources: + - resources.yaml +variables: values.yaml +userinfo: user-info.yaml +results: + - policy: restrict-github-team-external-identity + rule: teamrepositories-external-name-matches-declared-grant + resources: + - github-config/admins-approved + - github-config/maintainers-approved + - github-config/unset + kind: TeamRepository + result: pass + - policy: restrict-github-team-external-identity + rule: teamrepositories-external-name-matches-declared-grant + resources: + - github-config/swapped-team + - github-config/swapped-team-reverse + - github-config/other-subject + - github-config/foreign-team + - github-config/slug-identity + - github-config/wildcard + - github-config/wildcard-subject + - github-config/longer-subject + - github-config/undeclared-subject + - github-config/init-provider-subject + - github-config/init-provider-second-subject + kind: TeamRepository + result: fail diff --git a/tests/restrict-github-team-external-identity/grants-create/resources.yaml b/tests/restrict-github-team-external-identity/grants-create/resources.yaml new file mode 100644 index 0000000000..94ac3c8125 --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-create/resources.yaml @@ -0,0 +1,224 @@ +--- +# Recovery after a rebuild: the artifact states the identity the provider would record. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: admins-approved + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:ksail" +spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + policy: + resolve: Always +--- +# The same for the other approved team. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: maintainers-approved + namespace: github-config + annotations: + crossplane.io/external-name: "17046606:ksail" +spec: + forProvider: + repository: ksail + teamIdRef: + name: maintainers + policy: + resolve: Always +--- +# The usual shape: no identity, the provider creates and records it. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: unset + namespace: github-config +spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + policy: + resolve: Always +--- +# References admins but stands for the maintainers team. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: swapped-team + namespace: github-config + annotations: + crossplane.io/external-name: "17046606:ksail" +spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + policy: + resolve: Always +--- +# Names one repository and stands for another. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: other-subject + namespace: github-config + annotations: + crossplane.io/external-name: "17046606:platform" +spec: + forProvider: + repository: ksail + teamIdRef: + name: maintainers + policy: + resolve: Always +--- +# A team that is not approved at all. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: foreign-team + namespace: github-config + annotations: + crossplane.io/external-name: "99999999:ksail" +spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + policy: + resolve: Always +--- +# The provider also accepts a team slug; only the approved numeric identity is. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: slug-identity + namespace: github-config + annotations: + crossplane.io/external-name: "admins:ksail" +spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + policy: + resolve: Always +--- +# A wildcard is not an identity. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: wildcard + namespace: github-config + annotations: + crossplane.io/external-name: "*" +spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + policy: + resolve: Always +--- +# Nor is a wildcard in place of the repository. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: wildcard-subject + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:*" +spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + policy: + resolve: Always +--- +# The declared repository is only a prefix of the stored one. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: longer-subject + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:ksail-other" +spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + policy: + resolve: Always +--- +# Nothing declared under forProvider, so nothing can be approved. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: undeclared-subject + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:" +spec: + forProvider: + teamIdRef: + name: admins + policy: + resolve: Always +--- +# References maintainers but stands for the admins team. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: swapped-team-reverse + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:ksail" +spec: + forProvider: + repository: ksail + teamIdRef: + name: maintainers + policy: + resolve: Always +--- +# The repository under initProvider is not what this rule reads, so it may not accompany a stored identity. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: init-provider-subject + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:ksail" +spec: + forProvider: + teamIdRef: + name: admins + policy: + resolve: Always + initProvider: + repository: ksail +--- +# Nor next to a declared one. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: init-provider-second-subject + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:ksail" +spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + policy: + resolve: Always + initProvider: + repository: platform diff --git a/tests/restrict-github-team-external-identity/grants-create/user-info.yaml b/tests/restrict-github-team-external-identity/grants-create/user-info.yaml new file mode 100644 index 0000000000..00cdd9d2b8 --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-create/user-info.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: UserInfo +metadata: + name: github-config-source +userInfo: + username: system:serviceaccount:github-config:github-config + groups: + - system:serviceaccounts + - system:serviceaccounts:github-config + - system:authenticated diff --git a/tests/restrict-github-team-external-identity/grants-create/values.yaml b/tests/restrict-github-team-external-identity/grants-create/values.yaml new file mode 100644 index 0000000000..82b86be385 --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-create/values.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Values +metadata: + name: values +globalValues: + request.operation: CREATE diff --git a/tests/restrict-github-team-external-identity/grants-lookalike/kyverno-test.yaml b/tests/restrict-github-team-external-identity/grants-lookalike/kyverno-test.yaml new file mode 100644 index 0000000000..a153c2a041 --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-lookalike/kyverno-test.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Test +metadata: + name: restrict-github-team-external-identity-grants-lookalike +policies: + - >- + ../../../k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-external-identity.yaml +resources: + - resources.yaml +variables: values.yaml +userinfo: user-info.yaml +results: + - policy: restrict-github-team-external-identity + rule: teamrepositories-external-name-matches-declared-grant + resources: + - github-config/lookalike-recorded + kind: TeamRepository + result: fail diff --git a/tests/restrict-github-team-external-identity/grants-lookalike/resources.yaml b/tests/restrict-github-team-external-identity/grants-lookalike/resources.yaml new file mode 100644 index 0000000000..3ac684961d --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-lookalike/resources.yaml @@ -0,0 +1,16 @@ +--- +# The same service account name in a tenant namespace is not the provider. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: lookalike-recorded + namespace: github-config + annotations: + crossplane.io/external-name: "99999999:ksail" +spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + policy: + resolve: Always diff --git a/tests/restrict-github-team-external-identity/grants-lookalike/user-info.yaml b/tests/restrict-github-team-external-identity/grants-lookalike/user-info.yaml new file mode 100644 index 0000000000..54c1dbf15c --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-lookalike/user-info.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: UserInfo +metadata: + name: lookalike +userInfo: + username: system:serviceaccount:github-config:provider-upjet-github-f5d83a3e04ea + groups: + - system:serviceaccounts + - system:serviceaccounts:github-config + - system:authenticated diff --git a/tests/restrict-github-team-external-identity/grants-lookalike/values.yaml b/tests/restrict-github-team-external-identity/grants-lookalike/values.yaml new file mode 100644 index 0000000000..82b86be385 --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-lookalike/values.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Values +metadata: + name: values +globalValues: + request.operation: CREATE diff --git a/tests/restrict-github-team-external-identity/grants-other-account/kyverno-test.yaml b/tests/restrict-github-team-external-identity/grants-other-account/kyverno-test.yaml new file mode 100644 index 0000000000..f19b44ae8c --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-other-account/kyverno-test.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Test +metadata: + name: restrict-github-team-external-identity-grants-other-account +policies: + - >- + ../../../k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-external-identity.yaml +resources: + - resources.yaml +variables: values.yaml +userinfo: user-info.yaml +results: + - policy: restrict-github-team-external-identity + rule: teamrepositories-external-name-matches-declared-grant + resources: + - github-config/lookalike-recorded + kind: TeamRepository + result: fail diff --git a/tests/restrict-github-team-external-identity/grants-other-account/resources.yaml b/tests/restrict-github-team-external-identity/grants-other-account/resources.yaml new file mode 100644 index 0000000000..3ac684961d --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-other-account/resources.yaml @@ -0,0 +1,16 @@ +--- +# The same service account name in a tenant namespace is not the provider. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: lookalike-recorded + namespace: github-config + annotations: + crossplane.io/external-name: "99999999:ksail" +spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + policy: + resolve: Always diff --git a/tests/restrict-github-team-external-identity/grants-other-account/user-info.yaml b/tests/restrict-github-team-external-identity/grants-other-account/user-info.yaml new file mode 100644 index 0000000000..c3400502e8 --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-other-account/user-info.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: UserInfo +metadata: + name: other-account +userInfo: + username: system:serviceaccount:crossplane-system:provider-upjet-aws-f5d83a3e04ea + groups: + - system:serviceaccounts + - system:serviceaccounts:crossplane-system + - system:authenticated diff --git a/tests/restrict-github-team-external-identity/grants-other-account/values.yaml b/tests/restrict-github-team-external-identity/grants-other-account/values.yaml new file mode 100644 index 0000000000..82b86be385 --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-other-account/values.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Values +metadata: + name: values +globalValues: + request.operation: CREATE diff --git a/tests/restrict-github-team-external-identity/grants-provider-update/kyverno-test.yaml b/tests/restrict-github-team-external-identity/grants-provider-update/kyverno-test.yaml new file mode 100644 index 0000000000..985dc74f07 --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-provider-update/kyverno-test.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Test +metadata: + name: restrict-github-team-external-identity-grants-provider-update +policies: + - >- + ../../../k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-external-identity.yaml +resources: + - resources.yaml +variables: values.yaml +userinfo: user-info.yaml +results: + - policy: restrict-github-team-external-identity + rule: teamrepositories-external-name-matches-declared-grant + resources: + - github-config/provider-recorded + kind: TeamRepository + result: skip diff --git a/tests/restrict-github-team-external-identity/grants-provider-update/resources.yaml b/tests/restrict-github-team-external-identity/grants-provider-update/resources.yaml new file mode 100644 index 0000000000..69a55f967c --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-provider-update/resources.yaml @@ -0,0 +1,16 @@ +--- +# The provider records whatever GitHub returned; it is exempt. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: provider-recorded + namespace: github-config + annotations: + crossplane.io/external-name: "99999999:ksail" +spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + policy: + resolve: Always diff --git a/tests/restrict-github-team-external-identity/grants-provider-update/user-info.yaml b/tests/restrict-github-team-external-identity/grants-provider-update/user-info.yaml new file mode 100644 index 0000000000..5d2f307c4d --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-provider-update/user-info.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: UserInfo +metadata: + name: provider +userInfo: + username: system:serviceaccount:crossplane-system:provider-upjet-github-f5d83a3e04ea + groups: + - system:serviceaccounts + - system:serviceaccounts:crossplane-system + - system:authenticated diff --git a/tests/restrict-github-team-external-identity/grants-provider-update/values.yaml b/tests/restrict-github-team-external-identity/grants-provider-update/values.yaml new file mode 100644 index 0000000000..dab25f2928 --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-provider-update/values.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Values +metadata: + name: values +globalValues: + request.operation: UPDATE diff --git a/tests/restrict-github-team-external-identity/grants-provider/kyverno-test.yaml b/tests/restrict-github-team-external-identity/grants-provider/kyverno-test.yaml new file mode 100644 index 0000000000..503e8d738d --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-provider/kyverno-test.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Test +metadata: + name: restrict-github-team-external-identity-grants-provider +policies: + - >- + ../../../k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-external-identity.yaml +resources: + - resources.yaml +variables: values.yaml +userinfo: user-info.yaml +results: + - policy: restrict-github-team-external-identity + rule: teamrepositories-external-name-matches-declared-grant + resources: + - github-config/provider-recorded + kind: TeamRepository + result: skip diff --git a/tests/restrict-github-team-external-identity/grants-provider/resources.yaml b/tests/restrict-github-team-external-identity/grants-provider/resources.yaml new file mode 100644 index 0000000000..69a55f967c --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-provider/resources.yaml @@ -0,0 +1,16 @@ +--- +# The provider records whatever GitHub returned; it is exempt. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: provider-recorded + namespace: github-config + annotations: + crossplane.io/external-name: "99999999:ksail" +spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + policy: + resolve: Always diff --git a/tests/restrict-github-team-external-identity/grants-provider/user-info.yaml b/tests/restrict-github-team-external-identity/grants-provider/user-info.yaml new file mode 100644 index 0000000000..5d2f307c4d --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-provider/user-info.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: UserInfo +metadata: + name: provider +userInfo: + username: system:serviceaccount:crossplane-system:provider-upjet-github-f5d83a3e04ea + groups: + - system:serviceaccounts + - system:serviceaccounts:crossplane-system + - system:authenticated diff --git a/tests/restrict-github-team-external-identity/grants-provider/values.yaml b/tests/restrict-github-team-external-identity/grants-provider/values.yaml new file mode 100644 index 0000000000..82b86be385 --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-provider/values.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Values +metadata: + name: values +globalValues: + request.operation: CREATE diff --git a/tests/restrict-github-team-external-identity/grants-update/kyverno-test.yaml b/tests/restrict-github-team-external-identity/grants-update/kyverno-test.yaml new file mode 100644 index 0000000000..da51acbfab --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-update/kyverno-test.yaml @@ -0,0 +1,33 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Test +metadata: + name: restrict-github-team-external-identity-grants-update +policies: + - >- + ../../../k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-external-identity.yaml +resources: + - resources.yaml +variables: values.yaml +userinfo: user-info.yaml +results: + - policy: restrict-github-team-external-identity + rule: teamrepositories-external-name-matches-declared-grant + resources: + - github-config/unchanged-unusual + - github-config/cleared + - github-config/set-approved + kind: TeamRepository + result: pass + - policy: restrict-github-team-external-identity + rule: teamrepositories-external-name-matches-declared-grant + resources: + - github-config/changed + - github-config/retargeted-team + - github-config/retargeted-subject + - github-config/from-wildcard + - github-config/unchanged-init-provider-subject + - github-config/cleared-retargeted-team + - github-config/cleared-retargeted-subject + kind: TeamRepository + result: fail diff --git a/tests/restrict-github-team-external-identity/grants-update/resources.yaml b/tests/restrict-github-team-external-identity/grants-update/resources.yaml new file mode 100644 index 0000000000..fbe4dc08dc --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-update/resources.yaml @@ -0,0 +1,157 @@ +--- +# An identity the provider recorded in a shape this rule would not approve is left as it is by the next apply, which must not be refused. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: unchanged-unusual + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:ksail-as-recorded" +spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + policy: + resolve: Always +--- +# Removing the identity makes the provider create the declared one again. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: cleared + namespace: github-config +spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + policy: + resolve: Always +--- +# Stating the approved identity on an object that had none. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: set-approved + namespace: github-config + annotations: + crossplane.io/external-name: "17046606:ksail" +spec: + forProvider: + repository: ksail + teamIdRef: + name: maintainers + policy: + resolve: Always +--- +# The identity moves to another team. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: changed + namespace: github-config + annotations: + crossplane.io/external-name: "99999999:ksail" +spec: + forProvider: + repository: ksail + teamIdRef: + name: maintainers + policy: + resolve: Always +--- +# The identity stays, the team reference moves to admins. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: retargeted-team + namespace: github-config + annotations: + crossplane.io/external-name: "17046606:ksail" +spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + policy: + resolve: Always +--- +# The identity stays, the declared repository moves. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: retargeted-subject + namespace: github-config + annotations: + crossplane.io/external-name: "17046606:ksail" +spec: + forProvider: + repository: platform + teamIdRef: + name: maintainers + policy: + resolve: Always +--- +# A wildcard as the previous identity does not make every new one unchanged. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: from-wildcard + namespace: github-config + annotations: + crossplane.io/external-name: "99999999:ksail" +spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + policy: + resolve: Always +--- +# Unchanged identity, but the repository is stated under initProvider, where it could be moved unseen. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: unchanged-init-provider-subject + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:ksail" +spec: + forProvider: + teamIdRef: + name: admins + policy: + resolve: Always + initProvider: + repository: platform +--- +# Removing the stored identity while pointing the object somewhere else: the provider +# may write the old identity back onto an object that now declares another team. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: cleared-retargeted-team + namespace: github-config +spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + policy: + resolve: Always +--- +# Removing the stored identity while pointing the object somewhere else: the provider +# may write the old identity back onto an object that now declares another subject. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamRepository +metadata: + name: cleared-retargeted-subject + namespace: github-config +spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + policy: + resolve: Always diff --git a/tests/restrict-github-team-external-identity/grants-update/user-info.yaml b/tests/restrict-github-team-external-identity/grants-update/user-info.yaml new file mode 100644 index 0000000000..00cdd9d2b8 --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-update/user-info.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: UserInfo +metadata: + name: github-config-source +userInfo: + username: system:serviceaccount:github-config:github-config + groups: + - system:serviceaccounts + - system:serviceaccounts:github-config + - system:authenticated diff --git a/tests/restrict-github-team-external-identity/grants-update/values.yaml b/tests/restrict-github-team-external-identity/grants-update/values.yaml new file mode 100644 index 0000000000..c4382c866e --- /dev/null +++ b/tests/restrict-github-team-external-identity/grants-update/values.yaml @@ -0,0 +1,95 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Values +metadata: + name: values +globalValues: + request.operation: UPDATE +# kyverno test presents an update with the previous object equal to the new +# one. The values below are merged into the previous object, so each row tests +# a change; "no identity" has to be stated as an empty one. +policies: + - name: restrict-github-team-external-identity + resources: + - name: cleared + values: + request.oldObject: + metadata: + name: cleared + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:ksail" + spec: + forProvider: + repository: ksail + teamIdRef: + name: admins + - name: set-approved + values: + request.oldObject: + metadata: + name: set-approved + namespace: github-config + annotations: + crossplane.io/external-name: "" + - name: changed + values: + request.oldObject: + metadata: + name: changed + namespace: github-config + annotations: + crossplane.io/external-name: "17046606:ksail" + - name: retargeted-team + values: + request.oldObject: + metadata: + name: retargeted-team + namespace: github-config + spec: + forProvider: + teamIdRef: + name: maintainers + - name: retargeted-subject + values: + request.oldObject: + metadata: + name: retargeted-subject + namespace: github-config + spec: + forProvider: + repository: ksail + - name: from-wildcard + values: + request.oldObject: + metadata: + name: from-wildcard + namespace: github-config + annotations: + crossplane.io/external-name: "*" + - name: cleared-retargeted-team + values: + request.oldObject: + metadata: + name: cleared-retargeted-team + namespace: github-config + annotations: + crossplane.io/external-name: "17046606:ksail" + spec: + forProvider: + repository: ksail + teamIdRef: + name: maintainers + - name: cleared-retargeted-subject + values: + request.oldObject: + metadata: + name: cleared-retargeted-subject + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:platform" + spec: + forProvider: + repository: platform + teamIdRef: + name: admins diff --git a/tests/restrict-github-team-external-identity/members-create/kyverno-test.yaml b/tests/restrict-github-team-external-identity/members-create/kyverno-test.yaml new file mode 100644 index 0000000000..24166ab4a1 --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-create/kyverno-test.yaml @@ -0,0 +1,37 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Test +metadata: + name: restrict-github-team-external-identity-members-create +policies: + - >- + ../../../k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-external-identity.yaml +resources: + - resources.yaml +variables: values.yaml +userinfo: user-info.yaml +results: + - policy: restrict-github-team-external-identity + rule: teammemberships-external-name-matches-declared-member + resources: + - github-config/admins-approved + - github-config/maintainers-approved + - github-config/unset + kind: TeamMembership + result: pass + - policy: restrict-github-team-external-identity + rule: teammemberships-external-name-matches-declared-member + resources: + - github-config/swapped-team + - github-config/swapped-team-reverse + - github-config/other-subject + - github-config/foreign-team + - github-config/slug-identity + - github-config/wildcard + - github-config/wildcard-subject + - github-config/longer-subject + - github-config/undeclared-subject + - github-config/init-provider-subject + - github-config/init-provider-second-subject + kind: TeamMembership + result: fail diff --git a/tests/restrict-github-team-external-identity/members-create/resources.yaml b/tests/restrict-github-team-external-identity/members-create/resources.yaml new file mode 100644 index 0000000000..d85366e708 --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-create/resources.yaml @@ -0,0 +1,224 @@ +--- +# Recovery after a rebuild: the artifact states the identity the provider would record. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: admins-approved + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:devantler" +spec: + forProvider: + username: devantler + teamIdRef: + name: admins + policy: + resolve: Always +--- +# The same for the other approved team. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: maintainers-approved + namespace: github-config + annotations: + crossplane.io/external-name: "17046606:devantler" +spec: + forProvider: + username: devantler + teamIdRef: + name: maintainers + policy: + resolve: Always +--- +# The usual shape: no identity, the provider creates and records it. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: unset + namespace: github-config +spec: + forProvider: + username: devantler + teamIdRef: + name: admins + policy: + resolve: Always +--- +# References admins but stands for the maintainers team. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: swapped-team + namespace: github-config + annotations: + crossplane.io/external-name: "17046606:devantler" +spec: + forProvider: + username: devantler + teamIdRef: + name: admins + policy: + resolve: Always +--- +# Names one username and stands for another. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: other-subject + namespace: github-config + annotations: + crossplane.io/external-name: "17046606:someone-else" +spec: + forProvider: + username: devantler + teamIdRef: + name: maintainers + policy: + resolve: Always +--- +# A team that is not approved at all. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: foreign-team + namespace: github-config + annotations: + crossplane.io/external-name: "99999999:devantler" +spec: + forProvider: + username: devantler + teamIdRef: + name: admins + policy: + resolve: Always +--- +# The provider also accepts a team slug; only the approved numeric identity is. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: slug-identity + namespace: github-config + annotations: + crossplane.io/external-name: "admins:devantler" +spec: + forProvider: + username: devantler + teamIdRef: + name: admins + policy: + resolve: Always +--- +# A wildcard is not an identity. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: wildcard + namespace: github-config + annotations: + crossplane.io/external-name: "*" +spec: + forProvider: + username: devantler + teamIdRef: + name: admins + policy: + resolve: Always +--- +# Nor is a wildcard in place of the username. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: wildcard-subject + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:*" +spec: + forProvider: + username: devantler + teamIdRef: + name: admins + policy: + resolve: Always +--- +# The declared username is only a prefix of the stored one. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: longer-subject + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:devantler-other" +spec: + forProvider: + username: devantler + teamIdRef: + name: admins + policy: + resolve: Always +--- +# Nothing declared under forProvider, so nothing can be approved. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: undeclared-subject + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:" +spec: + forProvider: + teamIdRef: + name: admins + policy: + resolve: Always +--- +# References maintainers but stands for the admins team. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: swapped-team-reverse + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:devantler" +spec: + forProvider: + username: devantler + teamIdRef: + name: maintainers + policy: + resolve: Always +--- +# The username under initProvider is not what this rule reads, so it may not accompany a stored identity. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: init-provider-subject + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:devantler" +spec: + forProvider: + teamIdRef: + name: admins + policy: + resolve: Always + initProvider: + username: devantler +--- +# Nor next to a declared one. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: init-provider-second-subject + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:devantler" +spec: + forProvider: + username: devantler + teamIdRef: + name: admins + policy: + resolve: Always + initProvider: + username: someone-else diff --git a/tests/restrict-github-team-external-identity/members-create/user-info.yaml b/tests/restrict-github-team-external-identity/members-create/user-info.yaml new file mode 100644 index 0000000000..00cdd9d2b8 --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-create/user-info.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: UserInfo +metadata: + name: github-config-source +userInfo: + username: system:serviceaccount:github-config:github-config + groups: + - system:serviceaccounts + - system:serviceaccounts:github-config + - system:authenticated diff --git a/tests/restrict-github-team-external-identity/members-create/values.yaml b/tests/restrict-github-team-external-identity/members-create/values.yaml new file mode 100644 index 0000000000..82b86be385 --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-create/values.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Values +metadata: + name: values +globalValues: + request.operation: CREATE diff --git a/tests/restrict-github-team-external-identity/members-lookalike/kyverno-test.yaml b/tests/restrict-github-team-external-identity/members-lookalike/kyverno-test.yaml new file mode 100644 index 0000000000..3c6108e90f --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-lookalike/kyverno-test.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Test +metadata: + name: restrict-github-team-external-identity-members-lookalike +policies: + - >- + ../../../k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-external-identity.yaml +resources: + - resources.yaml +variables: values.yaml +userinfo: user-info.yaml +results: + - policy: restrict-github-team-external-identity + rule: teammemberships-external-name-matches-declared-member + resources: + - github-config/lookalike-recorded + kind: TeamMembership + result: fail diff --git a/tests/restrict-github-team-external-identity/members-lookalike/resources.yaml b/tests/restrict-github-team-external-identity/members-lookalike/resources.yaml new file mode 100644 index 0000000000..c14b122379 --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-lookalike/resources.yaml @@ -0,0 +1,16 @@ +--- +# The same service account name in a tenant namespace is not the provider. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: lookalike-recorded + namespace: github-config + annotations: + crossplane.io/external-name: "99999999:devantler" +spec: + forProvider: + username: devantler + teamIdRef: + name: admins + policy: + resolve: Always diff --git a/tests/restrict-github-team-external-identity/members-lookalike/user-info.yaml b/tests/restrict-github-team-external-identity/members-lookalike/user-info.yaml new file mode 100644 index 0000000000..54c1dbf15c --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-lookalike/user-info.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: UserInfo +metadata: + name: lookalike +userInfo: + username: system:serviceaccount:github-config:provider-upjet-github-f5d83a3e04ea + groups: + - system:serviceaccounts + - system:serviceaccounts:github-config + - system:authenticated diff --git a/tests/restrict-github-team-external-identity/members-lookalike/values.yaml b/tests/restrict-github-team-external-identity/members-lookalike/values.yaml new file mode 100644 index 0000000000..82b86be385 --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-lookalike/values.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Values +metadata: + name: values +globalValues: + request.operation: CREATE diff --git a/tests/restrict-github-team-external-identity/members-other-account/kyverno-test.yaml b/tests/restrict-github-team-external-identity/members-other-account/kyverno-test.yaml new file mode 100644 index 0000000000..9a436b5f26 --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-other-account/kyverno-test.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Test +metadata: + name: restrict-github-team-external-identity-members-other-account +policies: + - >- + ../../../k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-external-identity.yaml +resources: + - resources.yaml +variables: values.yaml +userinfo: user-info.yaml +results: + - policy: restrict-github-team-external-identity + rule: teammemberships-external-name-matches-declared-member + resources: + - github-config/lookalike-recorded + kind: TeamMembership + result: fail diff --git a/tests/restrict-github-team-external-identity/members-other-account/resources.yaml b/tests/restrict-github-team-external-identity/members-other-account/resources.yaml new file mode 100644 index 0000000000..c14b122379 --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-other-account/resources.yaml @@ -0,0 +1,16 @@ +--- +# The same service account name in a tenant namespace is not the provider. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: lookalike-recorded + namespace: github-config + annotations: + crossplane.io/external-name: "99999999:devantler" +spec: + forProvider: + username: devantler + teamIdRef: + name: admins + policy: + resolve: Always diff --git a/tests/restrict-github-team-external-identity/members-other-account/user-info.yaml b/tests/restrict-github-team-external-identity/members-other-account/user-info.yaml new file mode 100644 index 0000000000..c3400502e8 --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-other-account/user-info.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: UserInfo +metadata: + name: other-account +userInfo: + username: system:serviceaccount:crossplane-system:provider-upjet-aws-f5d83a3e04ea + groups: + - system:serviceaccounts + - system:serviceaccounts:crossplane-system + - system:authenticated diff --git a/tests/restrict-github-team-external-identity/members-other-account/values.yaml b/tests/restrict-github-team-external-identity/members-other-account/values.yaml new file mode 100644 index 0000000000..82b86be385 --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-other-account/values.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Values +metadata: + name: values +globalValues: + request.operation: CREATE diff --git a/tests/restrict-github-team-external-identity/members-provider-update/kyverno-test.yaml b/tests/restrict-github-team-external-identity/members-provider-update/kyverno-test.yaml new file mode 100644 index 0000000000..14091f38b6 --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-provider-update/kyverno-test.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Test +metadata: + name: restrict-github-team-external-identity-members-provider-update +policies: + - >- + ../../../k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-external-identity.yaml +resources: + - resources.yaml +variables: values.yaml +userinfo: user-info.yaml +results: + - policy: restrict-github-team-external-identity + rule: teammemberships-external-name-matches-declared-member + resources: + - github-config/provider-recorded + kind: TeamMembership + result: skip diff --git a/tests/restrict-github-team-external-identity/members-provider-update/resources.yaml b/tests/restrict-github-team-external-identity/members-provider-update/resources.yaml new file mode 100644 index 0000000000..74257f0ae6 --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-provider-update/resources.yaml @@ -0,0 +1,16 @@ +--- +# The provider records whatever GitHub returned; it is exempt. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: provider-recorded + namespace: github-config + annotations: + crossplane.io/external-name: "99999999:devantler" +spec: + forProvider: + username: devantler + teamIdRef: + name: admins + policy: + resolve: Always diff --git a/tests/restrict-github-team-external-identity/members-provider-update/user-info.yaml b/tests/restrict-github-team-external-identity/members-provider-update/user-info.yaml new file mode 100644 index 0000000000..5d2f307c4d --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-provider-update/user-info.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: UserInfo +metadata: + name: provider +userInfo: + username: system:serviceaccount:crossplane-system:provider-upjet-github-f5d83a3e04ea + groups: + - system:serviceaccounts + - system:serviceaccounts:crossplane-system + - system:authenticated diff --git a/tests/restrict-github-team-external-identity/members-provider-update/values.yaml b/tests/restrict-github-team-external-identity/members-provider-update/values.yaml new file mode 100644 index 0000000000..dab25f2928 --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-provider-update/values.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Values +metadata: + name: values +globalValues: + request.operation: UPDATE diff --git a/tests/restrict-github-team-external-identity/members-provider/kyverno-test.yaml b/tests/restrict-github-team-external-identity/members-provider/kyverno-test.yaml new file mode 100644 index 0000000000..9c076ac760 --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-provider/kyverno-test.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Test +metadata: + name: restrict-github-team-external-identity-members-provider +policies: + - >- + ../../../k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-external-identity.yaml +resources: + - resources.yaml +variables: values.yaml +userinfo: user-info.yaml +results: + - policy: restrict-github-team-external-identity + rule: teammemberships-external-name-matches-declared-member + resources: + - github-config/provider-recorded + kind: TeamMembership + result: skip diff --git a/tests/restrict-github-team-external-identity/members-provider/resources.yaml b/tests/restrict-github-team-external-identity/members-provider/resources.yaml new file mode 100644 index 0000000000..74257f0ae6 --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-provider/resources.yaml @@ -0,0 +1,16 @@ +--- +# The provider records whatever GitHub returned; it is exempt. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: provider-recorded + namespace: github-config + annotations: + crossplane.io/external-name: "99999999:devantler" +spec: + forProvider: + username: devantler + teamIdRef: + name: admins + policy: + resolve: Always diff --git a/tests/restrict-github-team-external-identity/members-provider/user-info.yaml b/tests/restrict-github-team-external-identity/members-provider/user-info.yaml new file mode 100644 index 0000000000..5d2f307c4d --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-provider/user-info.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: UserInfo +metadata: + name: provider +userInfo: + username: system:serviceaccount:crossplane-system:provider-upjet-github-f5d83a3e04ea + groups: + - system:serviceaccounts + - system:serviceaccounts:crossplane-system + - system:authenticated diff --git a/tests/restrict-github-team-external-identity/members-provider/values.yaml b/tests/restrict-github-team-external-identity/members-provider/values.yaml new file mode 100644 index 0000000000..82b86be385 --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-provider/values.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Values +metadata: + name: values +globalValues: + request.operation: CREATE diff --git a/tests/restrict-github-team-external-identity/members-update/kyverno-test.yaml b/tests/restrict-github-team-external-identity/members-update/kyverno-test.yaml new file mode 100644 index 0000000000..74dcb855b3 --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-update/kyverno-test.yaml @@ -0,0 +1,33 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Test +metadata: + name: restrict-github-team-external-identity-members-update +policies: + - >- + ../../../k8s/bases/infrastructure/cluster-policies/best-practices/restrict-github-team-external-identity.yaml +resources: + - resources.yaml +variables: values.yaml +userinfo: user-info.yaml +results: + - policy: restrict-github-team-external-identity + rule: teammemberships-external-name-matches-declared-member + resources: + - github-config/unchanged-unusual + - github-config/cleared + - github-config/set-approved + kind: TeamMembership + result: pass + - policy: restrict-github-team-external-identity + rule: teammemberships-external-name-matches-declared-member + resources: + - github-config/changed + - github-config/retargeted-team + - github-config/retargeted-subject + - github-config/from-wildcard + - github-config/unchanged-init-provider-subject + - github-config/cleared-retargeted-team + - github-config/cleared-retargeted-subject + kind: TeamMembership + result: fail diff --git a/tests/restrict-github-team-external-identity/members-update/resources.yaml b/tests/restrict-github-team-external-identity/members-update/resources.yaml new file mode 100644 index 0000000000..8ba0b67379 --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-update/resources.yaml @@ -0,0 +1,157 @@ +--- +# An identity the provider recorded in a shape this rule would not approve is left as it is by the next apply, which must not be refused. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: unchanged-unusual + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:devantler-as-recorded" +spec: + forProvider: + username: devantler + teamIdRef: + name: admins + policy: + resolve: Always +--- +# Removing the identity makes the provider create the declared one again. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: cleared + namespace: github-config +spec: + forProvider: + username: devantler + teamIdRef: + name: admins + policy: + resolve: Always +--- +# Stating the approved identity on an object that had none. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: set-approved + namespace: github-config + annotations: + crossplane.io/external-name: "17046606:devantler" +spec: + forProvider: + username: devantler + teamIdRef: + name: maintainers + policy: + resolve: Always +--- +# The identity moves to another team. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: changed + namespace: github-config + annotations: + crossplane.io/external-name: "99999999:devantler" +spec: + forProvider: + username: devantler + teamIdRef: + name: maintainers + policy: + resolve: Always +--- +# The identity stays, the team reference moves to admins. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: retargeted-team + namespace: github-config + annotations: + crossplane.io/external-name: "17046606:devantler" +spec: + forProvider: + username: devantler + teamIdRef: + name: admins + policy: + resolve: Always +--- +# The identity stays, the declared username moves. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: retargeted-subject + namespace: github-config + annotations: + crossplane.io/external-name: "17046606:devantler" +spec: + forProvider: + username: someone-else + teamIdRef: + name: maintainers + policy: + resolve: Always +--- +# A wildcard as the previous identity does not make every new one unchanged. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: from-wildcard + namespace: github-config + annotations: + crossplane.io/external-name: "99999999:devantler" +spec: + forProvider: + username: devantler + teamIdRef: + name: admins + policy: + resolve: Always +--- +# Unchanged identity, but the username is stated under initProvider, where it could be moved unseen. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: unchanged-init-provider-subject + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:devantler" +spec: + forProvider: + teamIdRef: + name: admins + policy: + resolve: Always + initProvider: + username: someone-else +--- +# Removing the stored identity while pointing the object somewhere else: the provider +# may write the old identity back onto an object that now declares another team. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: cleared-retargeted-team + namespace: github-config +spec: + forProvider: + username: devantler + teamIdRef: + name: admins + policy: + resolve: Always +--- +# Removing the stored identity while pointing the object somewhere else: the provider +# may write the old identity back onto an object that now declares another subject. +apiVersion: team.github.m.upbound.io/v1alpha1 +kind: TeamMembership +metadata: + name: cleared-retargeted-subject + namespace: github-config +spec: + forProvider: + username: devantler + teamIdRef: + name: admins + policy: + resolve: Always diff --git a/tests/restrict-github-team-external-identity/members-update/user-info.yaml b/tests/restrict-github-team-external-identity/members-update/user-info.yaml new file mode 100644 index 0000000000..00cdd9d2b8 --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-update/user-info.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: UserInfo +metadata: + name: github-config-source +userInfo: + username: system:serviceaccount:github-config:github-config + groups: + - system:serviceaccounts + - system:serviceaccounts:github-config + - system:authenticated diff --git a/tests/restrict-github-team-external-identity/members-update/values.yaml b/tests/restrict-github-team-external-identity/members-update/values.yaml new file mode 100644 index 0000000000..f752c84f3b --- /dev/null +++ b/tests/restrict-github-team-external-identity/members-update/values.yaml @@ -0,0 +1,95 @@ +--- +apiVersion: cli.kyverno.io/v1alpha1 +kind: Values +metadata: + name: values +globalValues: + request.operation: UPDATE +# kyverno test presents an update with the previous object equal to the new +# one. The values below are merged into the previous object, so each row tests +# a change; "no identity" has to be stated as an empty one. +policies: + - name: restrict-github-team-external-identity + resources: + - name: cleared + values: + request.oldObject: + metadata: + name: cleared + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:devantler" + spec: + forProvider: + username: devantler + teamIdRef: + name: admins + - name: set-approved + values: + request.oldObject: + metadata: + name: set-approved + namespace: github-config + annotations: + crossplane.io/external-name: "" + - name: changed + values: + request.oldObject: + metadata: + name: changed + namespace: github-config + annotations: + crossplane.io/external-name: "17046606:devantler" + - name: retargeted-team + values: + request.oldObject: + metadata: + name: retargeted-team + namespace: github-config + spec: + forProvider: + teamIdRef: + name: maintainers + - name: retargeted-subject + values: + request.oldObject: + metadata: + name: retargeted-subject + namespace: github-config + spec: + forProvider: + username: devantler + - name: from-wildcard + values: + request.oldObject: + metadata: + name: from-wildcard + namespace: github-config + annotations: + crossplane.io/external-name: "*" + - name: cleared-retargeted-team + values: + request.oldObject: + metadata: + name: cleared-retargeted-team + namespace: github-config + annotations: + crossplane.io/external-name: "17046606:devantler" + spec: + forProvider: + username: devantler + teamIdRef: + name: maintainers + - name: cleared-retargeted-subject + values: + request.oldObject: + metadata: + name: cleared-retargeted-subject + namespace: github-config + annotations: + crossplane.io/external-name: "18573307:someone-else" + spec: + forProvider: + username: someone-else + teamIdRef: + name: admins