Skip to content

Commit

Permalink
Merge pull request #55 from appuio/feat/service-accounts
Browse files Browse the repository at this point in the history
Enable Namespace creation through service accounts
  • Loading branch information
glrf authored Jan 17, 2022
2 parents 62e2017 + dd68bd6 commit d38b916
Show file tree
Hide file tree
Showing 4 changed files with 352 additions and 36 deletions.
164 changes: 150 additions & 14 deletions component/namespace-policies.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,12 @@ local matchProjectRequestProjects = {
} ],
};

local setDefaultOrgPolicy(name, match, exclude, username) = {

local setDefaultOrgPolicy(name, match, exclude, preconditions, username) = {
name: name,
match: match,
exclude: exclude,
preconditions: preconditions,
context: [
{
name: 'ocpuser',
Expand All @@ -86,6 +88,42 @@ local setDefaultOrgPolicy(name, match, exclude, username) = {
},
};

local notServiceAccount = {
all: [
{
key: '{{serviceAccountName}}',
operator: 'Equals',
value: '',
},
],
};

local isServiceAccount = {
all: [
{
key: '{{serviceAccountName}}',
operator: 'NotEquals',
value: '',
},
],
};


local organizationProjects = kyverno.ClusterPolicy('organization-projects') {
spec: {
validationFailureAction: 'enforce',
background: false,
rules: [
setDefaultOrgPolicy(
'set-default-organization',
matchProjectRequestProjects,
{},
{},
'{{request.object.metadata.annotations."openshift.io/requester"}}',
),
],
},
};
/**
* Organization Namespaces
* This policy will:
Expand All @@ -102,21 +140,100 @@ local organizationNamespaces = kyverno.ClusterPolicy('organization-namespaces')
background: false,
rules: [
setDefaultOrgPolicy(
'set-default-organization-ns',
'set-default-organization',
common.MatchNamespaces(),
common.BypassNamespaceRestrictionsSubjects(),
notServiceAccount,
'{{request.userInfo.username}}'
),
setDefaultOrgPolicy(
'set-default-organization-project',
matchProjectRequestProjects,
{},
'{{request.object.metadata.annotations."openshift.io/requester"}}',
),
{
name: 'has-organization',
match: common.MatchNamespaces(),
exclude: common.BypassNamespaceRestrictionsSubjects(),
preconditions: notServiceAccount,
validate: {
message: 'Namespace must have organization',
pattern: {
metadata: {
labels: {
'appuio.io/organization': '?*',
},
},
},
},
},
{
name: 'is-in-organization',
match: common.MatchNamespaces(),
exclude: common.BypassNamespaceRestrictionsSubjects(),
preconditions: notServiceAccount {
all+:
[
{
key: '{{request.object.metadata.labels."appuio.io/organization"}}',
operator: 'NotEquals',
value: '',
},
],
},
validate: {
message: 'Creating namespace for {{request.object.metadata.labels."appuio.io/organization"}} but {{request.userInfo.username}} is not in organization',
deny: {
conditions: [
{
key: '{{request.object.metadata.labels."appuio.io/organization"}}',
operator: 'NotIn',
value: '{{request.userInfo.groups}}',
},
],
},
},
},
],
},
};

local organizationSaNamespaces = kyverno.ClusterPolicy('organization-sa-namespaces') {
spec: {
validationFailureAction: 'enforce',
background: false,
rules: [
{
name: 'add-organization',
match: common.MatchNamespaces(),
exclude: common.BypassNamespaceRestrictionsSubjects(),
preconditions: isServiceAccount,
context: [
{
name: 'saNamespace',
apiCall: {
urlPath: '/apis/v1/namespaces/{{serviceAccountNamespace}}',
// We want the full output of the API call. Despite the docs not
// saying anything, if we omit jmesPath here, we don't get the
// variable ocpuser in the resulting context at all. Instead, we
// provide '@' for jmesPath which responds to the current
// element, giving us the full response as ocpuser.
jmesPath: '@',
},
},

],
mutate: {
patchStrategicMerge: {
metadata: {
labels: {
'+(appuio.io/organization)':
'{{saNamespace.metadata.labels."appuio.io/organization"}}',
},
},
},
},
},
{
name: 'has-organization',
match: common.MatchNamespaces(),
exclude: common.BypassNamespaceRestrictionsSubjects(),
preconditions: isServiceAccount,
validate: {
message: 'Namespace must have organization',
pattern: {
Expand All @@ -132,21 +249,38 @@ local organizationNamespaces = kyverno.ClusterPolicy('organization-namespaces')
name: 'is-in-organization',
match: common.MatchNamespaces(),
exclude: common.BypassNamespaceRestrictionsSubjects(),
preconditions: [
context: [
{
key: '{{request.object.metadata.labels."appuio.io/organization"}}',
operator: 'NotEquals',
value: '',
name: 'saNamespace',
apiCall: {
urlPath: '/apis/v1/namespaces/{{serviceAccountNamespace}}',
// We want the full output of the API call. Despite the docs not
// saying anything, if we omit jmesPath here, we don't get the
// variable ocpuser in the resulting context at all. Instead, we
// provide '@' for jmesPath which responds to the current
// element, giving us the full response as ocpuser.
jmesPath: '@',
},
},
],
preconditions: isServiceAccount {
all+:
[
{
key: '{{request.object.metadata.labels."appuio.io/organization"}}',
operator: 'NotEquals',
value: '',
},
],
},
validate: {
message: 'Creating namespace for {{request.object.metadata.labels."appuio.io/organization"}} but {{request.userInfo.username}} is not in organization',
message: 'Creating namespace for {{request.object.metadata.labels."appuio.io/organization"}} but {{serviceAccountName}} is not in organization',
deny: {
conditions: [
{
key: '{{request.object.metadata.labels."appuio.io/organization"}}',
operator: 'NotIn',
value: '{{request.userInfo.groups}}',
value: '{{saNamespace.metadata.labels."appuio.io/organization"}}',
},
],
},
Expand Down Expand Up @@ -277,6 +411,8 @@ local validateNamespaceMetadata = kyverno.ClusterPolicy('validate-namespace-meta
'01_appuio_ns_provisioner_role': appuioNsProvisionerRole + common.DefaultLabels,
'01_appuio_ns_provisioners_crb': appuioNsProvisionersRoleBinding + common.DefaultLabels,
'02_organization_namespaces': organizationNamespaces + common.DefaultLabels,
'02_organization_sa_namespaces': organizationSaNamespaces + common.DefaultLabels,
'02_organization_projects': organizationProjects + common.DefaultLabels,
'02_disallow_reserved_namespaces': disallowReservedNamespaces + common.DefaultLabels,
'02_validate_namespace_metadata': validateNamespaceMetadata + common.DefaultLabels,
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,26 +50,12 @@ spec:
metadata:
labels:
+(appuio.io/organization): '{{ocpuser.metadata.annotations."appuio.io/default-organization"}}'
name: set-default-organization-ns
- context:
- apiCall:
jmesPath: '@'
urlPath: /apis/user.openshift.io/v1/users/{{request.object.metadata.annotations."openshift.io/requester"}}
name: ocpuser
exclude: {}
match:
name: set-default-organization
preconditions:
all:
- resources:
annotations:
openshift.io/requester: ?*
kinds:
- Project
mutate:
patchStrategicMerge:
metadata:
labels:
+(appuio.io/organization): '{{ocpuser.metadata.annotations."appuio.io/default-organization"}}'
name: set-default-organization-project
- key: '{{serviceAccountName}}'
operator: Equals
value: ''
- exclude:
clusterRoles:
- cluster-admin
Expand Down Expand Up @@ -100,6 +86,11 @@ spec:
kinds:
- Namespace
name: has-organization
preconditions:
all:
- key: '{{serviceAccountName}}'
operator: Equals
value: ''
validate:
message: Namespace must have organization
pattern:
Expand Down Expand Up @@ -137,9 +128,13 @@ spec:
- Namespace
name: is-in-organization
preconditions:
- key: '{{request.object.metadata.labels."appuio.io/organization"}}'
operator: NotEquals
value: ''
all:
- key: '{{serviceAccountName}}'
operator: Equals
value: ''
- key: '{{request.object.metadata.labels."appuio.io/organization"}}'
operator: NotEquals
value: ''
validate:
deny:
conditions:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
annotations: {}
labels:
app.kubernetes.io/component: appuio-cloud
app.kubernetes.io/managed-by: commodore
app.kubernetes.io/name: appuio-cloud
name: organization-projects
name: organization-projects
spec:
background: false
rules:
- context:
- apiCall:
jmesPath: '@'
urlPath: /apis/user.openshift.io/v1/users/{{request.object.metadata.annotations."openshift.io/requester"}}
name: ocpuser
exclude: {}
match:
all:
- resources:
annotations:
openshift.io/requester: ?*
kinds:
- Project
mutate:
patchStrategicMerge:
metadata:
labels:
+(appuio.io/organization): '{{ocpuser.metadata.annotations."appuio.io/default-organization"}}'
name: set-default-organization
preconditions: {}
validationFailureAction: enforce
Loading

0 comments on commit d38b916

Please sign in to comment.