diff --git a/Makefile b/Makefile index c504613..7b3a0a4 100644 --- a/Makefile +++ b/Makefile @@ -180,6 +180,7 @@ helm-manifest-install: sed -i '/providerContainer:/,/providervol:/s/^#//g' manifest_staging/charts/secrets-store-sync-controller/temp_values.yaml; \ fi helm install secrets-store-sync-controller --wait --timeout=5m \ + --namespace secrets-store-sync-controller-system --create-namespace \ -f manifest_staging/charts/secrets-store-sync-controller/temp_values.yaml \ --set image.repository=$(REGISTRY)/$(IMAGE_NAME) \ --set image.tag=$(VERSION) \ @@ -280,18 +281,18 @@ release-manifest: $(MAKE) manifests @if [[ "$$(uname)" == "Darwin" ]]; then \ sed -i '' "s/version: .*/version: ${NEWVERSION}/" manifest_staging/charts/secrets-store-sync-controller/Chart.yaml; \ - sed -i '' "s/appVersion: .*/appVersion: ${NEWVERSION}/" manifest_staging/charts/secrets-store-sync-controller/Chart.yaml; \ - sed -i '' "s/tag: \"v${CURRENTVERSION}/tag: \"v${NEWVERSION}/" manifest_staging/charts/secrets-store-sync-controller/values.yaml; \ + sed -i '' "s/appVersion: v .*/appVersion: v${NEWVERSION}/" manifest_staging/charts/secrets-store-sync-controller/Chart.yaml; \ + sed -i '' "s/tag: v${CURRENTVERSION}/tag: v${NEWVERSION}/" manifest_staging/charts/secrets-store-sync-controller/values.yaml; \ sed -i '' "s/v${CURRENTVERSION}/v${NEWVERSION}/" manifest_staging/charts/secrets-store-sync-controller/README.md; \ else \ sed -i "s/version: .*/version: ${NEWVERSION}/" manifest_staging/charts/secrets-store-sync-controller/Chart.yaml; \ - sed -i "s/appVersion: .*/appVersion: ${NEWVERSION}/" manifest_staging/charts/secrets-store-sync-controller/Chart.yaml; \ - sed -i "s/tag: \"v${CURRENTVERSION}/tag: \"v${NEWVERSION}/" manifest_staging/charts/secrets-store-sync-controller/values.yaml; \ + sed -i "s/appVersion: v .*/appVersion: v${NEWVERSION}/" manifest_staging/charts/secrets-store-sync-controller/Chart.yaml; \ + sed -i "s/tag: v${CURRENTVERSION}/tag: v${NEWVERSION}/" manifest_staging/charts/secrets-store-sync-controller/values.yaml; \ sed -i "s/v${CURRENTVERSION}/v${NEWVERSION}/" manifest_staging/charts/secrets-store-sync-controller/README.md; \ fi .PHONY: promote-staging-manifest promote-staging-manifest: #promote staging manifests to release dir $(MAKE) release-manifest - @rm -rf charts/secrets-store-sync-controller + @rm -rf charts/secrets-store-sync-controller/ @cp -r manifest_staging/charts ./charts diff --git a/charts/charts/secrets-store-sync-controller/.helmignore b/charts/charts/secrets-store-sync-controller/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/charts/secrets-store-sync-controller/Chart.yaml b/charts/charts/secrets-store-sync-controller/Chart.yaml new file mode 100644 index 0000000..fe1b5d3 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: secrets-store-sync-controller +version: 0.0.1 +appVersion: v0.0.1 +kubeVersion: ">=1.27.0" +description: A Helm chart to install the Secrets Store Sync Controller and its associated resources inside a Kubernetes cluster. diff --git a/charts/charts/secrets-store-sync-controller/README.md b/charts/charts/secrets-store-sync-controller/README.md new file mode 100644 index 0000000..4f1fbb3 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/README.md @@ -0,0 +1,30 @@ +# Deploying the Secrets Store Sync Controller +You can deploy the Secrets Store Sync Controller with Helm using following command: +```sh +helm install -f values secrets-store-sync-controller charts/secrets-store-sync-controller +``` + +## Configuration and Parameters +You can customize the installation by modifying values in the `values.yaml` file or by passing parameters to the helm install command using the `--set key=value[,key=value]` argument. + +| Parameter Name | Description | Default Value | +|--------------------------------------------------|---------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `providerContainer` | The container for the Secrets Store Sync Controller. | `[- name: provider-aws-installer ...]` | +| `controllerName` | The name of the Secrets Store Sync Controller. | `secrets-store-sync-controller-manager` | +| `tokenRequestAudience` | The audience for the token request. | `[]` | +| `logVerbosity` | The log level. | `5` | +| `validatingAdmissionPolicies.applyPolicies` | Determines whether the Secrets Store Sync Controller should apply policies. | `true` | +| `validatingAdmissionPolicies.allowedSecretTypes` | The types of secrets that the Secrets Store Sync Controller should allow. | `["Opaque", "kubernetes.io/basic-auth", "bootstrap.kubernetes.io/token", "kubernetes.io/dockerconfigjson", "kubernetes.io/dockercfg", "kubernetes.io/ssh-auth", "kubernetes.io/tls"]` | +| `validatingAdmissionPolicies.deniedSecretTypes` | The types of secrets that the Secrets Store Sync Controller should deny. | `["kubernetes.io/service-account-token"]` | +| `image.repository` | The image repository of the Secrets Store Sync Controller. | `registry.k8s.io/secrets-store-sync/controller` | +| `image.pullPolicy` | Image pull policy. | `IfNotPresent` | +| `image.tag` | The specific image tag to use. Overrides the image tag whose default is the chart's `appVersion`. | `v0.0.1` | +| `securityContext` | Security context for the Secrets Store Sync Controller. | `{ allowPrivilegeEscalation: false, capabilities: { drop: [ALL] } }` | +| `resources` | The resource request/limits for the Secrets Store Sync Controller image. | `limits: 500m CPU, 128Mi; requests: 10m CPU, 64Mi` | +| `podAnnotations` | Annotations to be added to pods. | `{ kubectl.kubernetes.io/default-container: "manager" }` | +| `podLabels` | Labels to be added to pods. | `{ control-plane: "controller-manager", secrets-store.io/system: "true", app: "secrets-store-sync-controller" }` | +| `nodeSelector` | Node labels for pod assignment. | `{}` | +| `tolerations` | Tolerations for pod assignment. | `[{ operator: "Exists" }]` | + + +These parameters offer flexibility in configuring and deploying the Secrets Store Sync Controller according to specific requirements in your Kubernetes environment. Remember to replace values appropriately or use the `--set` flag when installing the chart via Helm. diff --git a/charts/charts/secrets-store-sync-controller/crds/secret-sync.x-k8s.io_secretsyncs.yaml b/charts/charts/secrets-store-sync-controller/crds/secret-sync.x-k8s.io_secretsyncs.yaml new file mode 100644 index 0000000..285ee6f --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/crds/secret-sync.x-k8s.io_secretsyncs.yaml @@ -0,0 +1,268 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.11.1 + creationTimestamp: null + name: secretsyncs.secret-sync.x-k8s.io +spec: + group: secret-sync.x-k8s.io + names: + kind: SecretSync + listKind: SecretSyncList + plural: secretsyncs + singular: secretsync + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: SecretSync represents the desired state and observed state of + the secret synchronization process. The SecretSync name is used to as the + secret object created by the controller. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: SecretSyncSpec defines the desired state for synchronizing + secret. + properties: + forceSynchronization: + description: forceSynchronization can be used to force the secret + synchronization. The secret synchronization is triggered, by changing + the value in this field. This field is not used to resolve synchronization + conflicts. It is not related with the force query parameter in the + Apply operation. https://kubernetes.io/docs/reference/using-api/server-side-apply/#conflicts + maxLength: 253 + pattern: ^[A-Za-z0-9]([-A-Za-z0-9]+([-._a-zA-Z0-9]?[A-Za-z0-9])*)? + type: string + secretObject: + description: secretObject specifies the configuration for the synchronized + Kubernetes secret object. + properties: + annotations: + additionalProperties: + type: string + description: 'annotations contains key-value pairs representing + annotations associated with the Kubernetes secret object. The + following annotation prefix is reserved: secrets-store.sync.x-k8s.io/. + Creation fails if the annotation key is specified in the SecretSync + object by the user.' + type: object + x-kubernetes-validations: + - message: Annotations should have < 253 characters for both keys + and values. + rule: (self.all(x, x.size() < 253 && self[x].size() < 253) == + true) + - message: Annotations should not contain secrets-store.sync.x-k8s.io. + This key is reserved for the controller. + rule: (self.all(x, x.startsWith('secrets-store.sync.x-k8s.io') + == false)) + data: + description: data is a slice of SecretObjectData containing secret + data source from the Secret Provider Class and the corresponding + data field key used in the Kubernetes secret object. + items: + description: SecretObjectData defines the desired state of synchronized + data within a Kubernetes secret object. + properties: + sourcePath: + description: sourcePath is the data source value of the + secret defined in the Secret Provider Class. This matches + the path of a file in the MountResponse returned from + the provider. + maxLength: 253 + minLength: 1 + pattern: ^[A-Za-z0-9.]([-A-Za-z0-9]+([-._a-zA-Z0-9]?[A-Za-z0-9])*)?(\/([0-9]+))*$ + type: string + targetKey: + description: 'targetKey is the key in the Kubernetes secret''s + data field as described in the Kubernetes API reference: + https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/secret-v1/' + maxLength: 253 + minLength: 1 + pattern: ^[A-Za-z0-9.]([-A-Za-z0-9]+([-._a-zA-Z0-9]?[A-Za-z0-9])*)?(\/([0-9]+))*$ + type: string + required: + - sourcePath + - targetKey + type: object + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - targetKey + x-kubernetes-list-type: map + labels: + additionalProperties: + type: string + description: 'labels contains key-value pairs representing labels + associated with the Kubernetes secret object. The labels are + used to identify the secret object created by the controller. + On secret creation, the following label is added: secrets-store.sync.x-k8s.io/secretsync=. + The following label prefix is reserved: secrets-store.sync.x-k8s.io/. + Creation fails if the label is specified in the SecretSync object + with a different value. On secret update, if the validation + admission policy is set, the controller will check if the label + secrets-store.sync.x-k8s.io/secretsync= is + present. If the label is not present, controller fails to update + the secret.' + type: object + x-kubernetes-validations: + - message: Labels should have < 63 characters for both keys and + values. + rule: (self.all(x, x.size() < 63 && self[x].size() < 63) == + true) + - message: Labels should not contain secrets-store.sync.x-k8s.io. + This key is reserved for the controller. + rule: (self.all(x, x.startsWith('secrets-store.sync.x-k8s.io') + == false)) + type: + description: type specifies the type of the Kubernetes secret + object, e.g. "Opaque";"kubernetes.io/basic-auth";"kubernetes.io/ssh-auth";"kubernetes.io/tls" + The controller must have permission to create secrets of the + specified type. + maxLength: 253 + minLength: 1 + type: string + required: + - data + - type + type: object + secretProviderClassName: + description: secretProviderClassName specifies the name of the secret + provider class used to pass information to access the secret store. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + secretSyncControllerName: + default: "" + description: secretSyncControllerName specifies the name of the secret + sync controller used to synchronize the secret. + type: string + serviceAccountName: + description: serviceAccountName specifies the name of the service + account used to access the secret store. The audience field in the + service account token must be passed as parameter in the controller + configuration. The audience is used when requesting a token from + the API server for the service account; the supported audiences + are defined by each provider. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - secretObject + - secretProviderClassName + - serviceAccountName + type: object + status: + description: SecretSyncStatus defines the observed state of the secret + synchronization process. + properties: + conditions: + items: + description: "Condition contains details for one aspect of the current + state of this API Resource. --- This struct is intended for direct + use as an array at the field path .status.conditions. For example, + \n type FooStatus struct{ // Represents the observations of a + foo's current state. // Known .status.conditions.type are: \"Available\", + \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge + // +listType=map // +listMapKey=type Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 16 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + lastSuccessfulSyncTime: + description: lastSuccessfulSyncTime represents the last time the secret + was retrieved from the Provider and updated. + format: date-time + type: string + syncHash: + description: 'syncHash contains the hash of the secret object data, + data from the SecretProviderClass (e.g. UID, and metadata.generation), + and similar data from the SecretSync. This hash is used to determine + if the secret changed. The hash is calculated using the HMAC (Hash-based + Message Authentication Code) algorithm, using bcrypt hashing, with + the SecretsSync''s UID as the key. The secret is updated if: 1. + the hash is different 2. the lastSuccessfulSyncTime indicates a + rotation is required - the rotation poll interval is passed as a + parameter in the controller configuration 3. the SecretUpdateStatus + is ''Failed''' + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/charts/charts/secrets-store-sync-controller/crds/secrets-store.csi.x-k8s.io_secretproviderclasses.yaml b/charts/charts/secrets-store-sync-controller/crds/secrets-store.csi.x-k8s.io_secretproviderclasses.yaml new file mode 100644 index 0000000..fcf63c6 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/crds/secrets-store.csi.x-k8s.io_secretproviderclasses.yaml @@ -0,0 +1,177 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.12.1 + name: secretproviderclasses.secrets-store.csi.x-k8s.io +spec: + group: secrets-store.csi.x-k8s.io + names: + kind: SecretProviderClass + listKind: SecretProviderClassList + plural: secretproviderclasses + singular: secretproviderclass + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: SecretProviderClass is the Schema for the secretproviderclasses + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: SecretProviderClassSpec defines the desired state of SecretProviderClass + properties: + parameters: + additionalProperties: + type: string + description: Configuration for specific provider + type: object + provider: + description: Configuration for provider name + type: string + secretObjects: + items: + description: SecretObject defines the desired state of synced K8s + secret objects + properties: + annotations: + additionalProperties: + type: string + description: annotations of k8s secret object + type: object + data: + items: + description: SecretObjectData defines the desired state of + synced K8s secret object data + properties: + key: + description: data field to populate + type: string + objectName: + description: name of the object to sync + type: string + type: object + type: array + labels: + additionalProperties: + type: string + description: labels of K8s secret object + type: object + secretName: + description: name of the K8s secret object + type: string + type: + description: type of K8s secret object + type: string + type: object + type: array + type: object + status: + description: SecretProviderClassStatus defines the observed state of SecretProviderClass + type: object + type: object + served: true + storage: true + - deprecated: true + deprecationWarning: secrets-store.csi.x-k8s.io/v1alpha1 is deprecated. Use secrets-store.csi.x-k8s.io/v1 + instead. + name: v1alpha1 + schema: + openAPIV3Schema: + description: SecretProviderClass is the Schema for the secretproviderclasses + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: SecretProviderClassSpec defines the desired state of SecretProviderClass + properties: + parameters: + additionalProperties: + type: string + description: Configuration for specific provider + type: object + provider: + description: Configuration for provider name + type: string + secretObjects: + items: + description: SecretObject defines the desired state of synced K8s + secret objects + properties: + annotations: + additionalProperties: + type: string + description: annotations of k8s secret object + type: object + data: + items: + description: SecretObjectData defines the desired state of + synced K8s secret object data + properties: + key: + description: data field to populate + type: string + objectName: + description: name of the object to sync + type: string + type: object + type: array + labels: + additionalProperties: + type: string + description: labels of K8s secret object + type: object + secretName: + description: name of the K8s secret object + type: string + type: + description: type of K8s secret object + type: string + type: object + type: array + type: object + status: + description: SecretProviderClassStatus defines the observed state of SecretProviderClass + properties: + byPod: + items: + description: ByPodStatus defines the state of SecretProviderClass + as seen by an individual controller + properties: + id: + description: id of the pod that wrote the status + type: string + namespace: + description: namespace of the pod that wrote the status + type: string + type: object + type: array + type: object + type: object + served: true + storage: false diff --git a/charts/charts/secrets-store-sync-controller/templates/VAPB_create_update_secrets_type.yaml b/charts/charts/secrets-store-sync-controller/templates/VAPB_create_update_secrets_type.yaml new file mode 100644 index 0000000..a7108c4 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/VAPB_create_update_secrets_type.yaml @@ -0,0 +1,9 @@ +{{- if .Values.validatingAdmissionPolicies.applyPolicies -}} +{{ include "secrets-store-sync-controller.admissionApiVersion" . }} +kind: ValidatingAdmissionPolicyBinding +metadata: + name: "secrets-store-sync-controller-create-update-policy-binding" +spec: + policyName: "secrets-store-sync-controller-create-update-policy" + validationActions: [Deny] +{{- end -}} diff --git a/charts/charts/secrets-store-sync-controller/templates/VAPB_create_update_token_request_deny.yaml b/charts/charts/secrets-store-sync-controller/templates/VAPB_create_update_token_request_deny.yaml new file mode 100644 index 0000000..d2aeae3 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/VAPB_create_update_token_request_deny.yaml @@ -0,0 +1,9 @@ +{{- if .Values.validatingAdmissionPolicies.applyPolicies -}} +{{ include "secrets-store-sync-controller.admissionApiVersion" . }} +kind: ValidatingAdmissionPolicyBinding +metadata: + name: "secrets-store-sync-controller-create-update-token-deny-policy-binding" +spec: + policyName: "secrets-store-sync-controller-create-update-token-deny-policy" + validationActions: [Deny] +{{- end -}} diff --git a/charts/charts/secrets-store-sync-controller/templates/VAPB_delete_secrets.yaml b/charts/charts/secrets-store-sync-controller/templates/VAPB_delete_secrets.yaml new file mode 100644 index 0000000..09e7457 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/VAPB_delete_secrets.yaml @@ -0,0 +1,9 @@ +{{- if .Values.validatingAdmissionPolicies.applyPolicies -}} +{{ include "secrets-store-sync-controller.admissionApiVersion" . }} +kind: ValidatingAdmissionPolicyBinding +metadata: + name: "secrets-store-sync-controller-delete-policy-binding" +spec: + policyName: "secrets-store-sync-controller-delete-policy" + validationActions: [Deny] +{{- end -}} diff --git a/charts/charts/secrets-store-sync-controller/templates/VAPB_update_owners_check_old_object.yaml b/charts/charts/secrets-store-sync-controller/templates/VAPB_update_owners_check_old_object.yaml new file mode 100644 index 0000000..df89d18 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/VAPB_update_owners_check_old_object.yaml @@ -0,0 +1,9 @@ +{{- if .Values.validatingAdmissionPolicies.applyPolicies -}} +{{ include "secrets-store-sync-controller.admissionApiVersion" . }} +kind: ValidatingAdmissionPolicyBinding +metadata: + name: "secrets-store-sync-controller-update-owners-check-oldobject-policy-binding" +spec: + policyName: "secrets-store-sync-controller-update-owners-check-oldobject-policy" + validationActions: [Deny] +{{- end -}} diff --git a/charts/charts/secrets-store-sync-controller/templates/VAPB_update_secrets_based_on_label.yaml b/charts/charts/secrets-store-sync-controller/templates/VAPB_update_secrets_based_on_label.yaml new file mode 100644 index 0000000..990c9f5 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/VAPB_update_secrets_based_on_label.yaml @@ -0,0 +1,9 @@ +{{- if .Values.validatingAdmissionPolicies.applyPolicies -}} +{{ include "secrets-store-sync-controller.admissionApiVersion" . }} +kind: ValidatingAdmissionPolicyBinding +metadata: + name: "secrets-store-sync-controller-update-label-policy-binding" +spec: + policyName: "secrets-store-sync-controller-update-label-policy" + validationActions: [Deny] +{{- end -}} diff --git a/charts/charts/secrets-store-sync-controller/templates/VAPB_validate_annotation_format_secret_sync.yaml b/charts/charts/secrets-store-sync-controller/templates/VAPB_validate_annotation_format_secret_sync.yaml new file mode 100644 index 0000000..fba6d8b --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/VAPB_validate_annotation_format_secret_sync.yaml @@ -0,0 +1,9 @@ +{{- if .Values.validatingAdmissionPolicies.applyPolicies -}} +{{ include "secrets-store-sync-controller.admissionApiVersion" . }} +kind: ValidatingAdmissionPolicyBinding +metadata: + name: "secrets-store-sync-controller-validate-annotation-policy-binding" +spec: + policyName: "secrets-store-sync-controller-validate-annotation-policy" + validationActions: [Deny] +{{- end -}} diff --git a/charts/charts/secrets-store-sync-controller/templates/VAPB_validate_label_format_secret_sync.yaml b/charts/charts/secrets-store-sync-controller/templates/VAPB_validate_label_format_secret_sync.yaml new file mode 100644 index 0000000..f20bbd5 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/VAPB_validate_label_format_secret_sync.yaml @@ -0,0 +1,9 @@ +{{- if .Values.validatingAdmissionPolicies.applyPolicies -}} +{{ include "secrets-store-sync-controller.admissionApiVersion" . }} +kind: ValidatingAdmissionPolicyBinding +metadata: + name: "secrets-store-sync-controller-validate-label-policy-binding" +spec: + policyName: "secrets-store-sync-controller-validate-label-policy" + validationActions: [Deny] +{{- end -}} diff --git a/charts/charts/secrets-store-sync-controller/templates/VAPB_validate_token_config.yaml b/charts/charts/secrets-store-sync-controller/templates/VAPB_validate_token_config.yaml new file mode 100644 index 0000000..39cfbe2 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/VAPB_validate_token_config.yaml @@ -0,0 +1,9 @@ +{{- if .Values.validatingAdmissionPolicies.applyPolicies -}} +{{ include "secrets-store-sync-controller.admissionApiVersion" . }} +kind: ValidatingAdmissionPolicyBinding +metadata: + name: "secrets-store-sync-controller-validate-token-policy-binding" +spec: + policyName: "secrets-store-sync-controller-validate-token-policy" + validationActions: [Deny] +{{- end -}} diff --git a/charts/charts/secrets-store-sync-controller/templates/_helpers.tpl b/charts/charts/secrets-store-sync-controller/templates/_helpers.tpl new file mode 100644 index 0000000..66fd60c --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/_helpers.tpl @@ -0,0 +1,95 @@ +{{/* +Generate subjects for role binding and cluster role binding. +*/}} +{{- define "secrets-store-sync-controller.subjects" -}} +- kind: ServiceAccount + name: "secrets-store-sync-controller-manager" + namespace: {{ .Release.Namespace }} +{{- end }} + +{{/* Generate match condition expression */}} +{{- define "chartname.matchConditionExpression" -}} +{{- printf "request.userInfo.username == 'system:serviceaccount:%s:%s'" .Release.Namespace .Values.controllerName -}} +{{- end -}} + +{{/* +Generate allowed secret types list as a complete expression. +*/}} +{{- define "chartname.secretTypesList" -}} +{{- $secretTypes := . -}} +{{- if not $secretTypes -}} +false +{{- else -}} +(object.type in [{{ range $index, $type := $secretTypes }}{{ if $index }}, {{ end }}"{{ $type }}"{{ end }}]) +{{- end -}} +{{- end -}} + +{{/* Define a constant value for labelKey */}} +{{- define "secrets-store-sync-controller.labelKey" -}} +secrets-store.sync.x-k8s.io +{{- end -}} + +{{/* Define a constant value for labelValue */}} +{{- define "secrets-store-sync-controller.labelValue" -}} +'' +{{- end -}} + +{{/* +Check if the old secret has the expected label key. +*/}} +{{- define "secrets-store-sync-controller.oldSecretHasExpectedLabelKey" -}} +variables.oldSecretHasLabels && ('{{ include "secrets-store-sync-controller.labelKey" . }}' in oldObject.metadata.labels) ? true : false +{{- end -}} + +{{/* +Check if the old secret has the expected label value. +*/}} +{{- define "secrets-store-sync-controller.oldSecretHasExpectedLabelValue" -}} +{{ include "secrets-store-sync-controller.labelValue" . }} == oldObject.metadata.labels['{{ include "secrets-store-sync-controller.labelKey" . }}'] ? true : false +{{- end -}} + + +{{/* +Generate token audience comparison expression. +Returns 'false' if tokenRequestAudience list is empty. +*/}} +{{- define "secrets-store-sync-controller.tokenAudienceComparison" -}} +{{- $tokenAudiences := .Values.tokenRequestAudience -}} +{{- if not $tokenAudiences -}} +false +{{- else -}} +{{- $audienceExpressions := list -}} +{{- range $index, $audience := $tokenAudiences }} + {{- $expressionPart := printf "object.spec.audiences.exists(w, w == '%s')" $audience.audience -}} + {{- $audienceExpressions = append $audienceExpressions $expressionPart -}} +{{- end -}} +{{- join " || " $audienceExpressions -}} +{{- end -}} +{{- end -}} + +{{/* +Generate a comma-separated string from a list. +*/}} +{{- define "secrets-store-sync-controller.listToString" -}} +{{- $tokenRequests := .Values.tokenRequestAudience -}} +{{- $audiences := list -}} +{{- range $index, $request := $tokenRequests }} + {{- $audiences = append $audiences $request.audience -}} +{{- end -}} +{{- join ", " $audiences -}} +{{- end -}} + +{{/* +Determine the api version for the validating admission policies. +*/}} +{{- define "secrets-store-sync-controller.admissionApiVersion" -}} +{{- if semverCompare "~1.27.0" .Values.validatingAdmissionPolicies.kubernetesReleaseVersion -}} +apiVersion: admissionregistration.k8s.io/v1alpha1 +{{- else if semverCompare "~1.28.0" .Values.validatingAdmissionPolicies.kubernetesReleaseVersion -}} +apiVersion: admissionregistration.k8s.io/v1beta1 +{{- else if semverCompare "^1.29.x" .Values.validatingAdmissionPolicies.kubernetesReleaseVersion -}} +apiVersion: admissionregistration.k8s.io/v1 +{{- else -}} +apiVersion: unsupported-validating-admission-api-version +{{- end }} +{{- end -}} diff --git a/charts/charts/secrets-store-sync-controller/templates/create_update_secrets_types.yaml b/charts/charts/secrets-store-sync-controller/templates/create_update_secrets_types.yaml new file mode 100644 index 0000000..44121b7 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/create_update_secrets_types.yaml @@ -0,0 +1,26 @@ +{{- if .Values.validatingAdmissionPolicies.applyPolicies -}} +{{ include "secrets-store-sync-controller.admissionApiVersion" . }} +kind: ValidatingAdmissionPolicy +metadata: + name: "secrets-store-sync-controller-create-update-policy" +spec: + failurePolicy: Fail + matchConditions: + - name: 'user-is-secrets-store-sync-controller' + expression: {{ include "chartname.matchConditionExpression" . | quote }} + matchConstraints: + resourceRules: + - apiGroups: [""] + apiVersions: ["v1"] + operations: ["CREATE", "UPDATE"] + resources: ["secrets"] + variables: + - name: hasOneSecretSyncOwner + expression: "(size(object.metadata.ownerReferences) == 1 && object.metadata.ownerReferences.all(o, o.kind == 'SecretSync' && o.apiVersion.startsWith('secret-sync.x-k8s.io/') && o.name == object.metadata.name))" + - name: allowedSecretTypes + expression: {{ include "chartname.secretTypesList" .Values.validatingAdmissionPolicies.allowedSecretTypes | quote }} + validations: + - expression: "variables.allowedSecretTypes == true && variables.hasOneSecretSyncOwner == true" + message: "Only secrets with types defined in the allowedSecretTypes are allowed." + messageExpression: "'secrets-store-sync-controller has failed to ' + string(request.operation) + ' secret with ' + string(object.type) + ' type ' + 'in the ' + string(request.namespace) + ' namespace. The controller can only create or update secrets in the allowed types list with a single secretsync owner.'" +{{- end -}} diff --git a/charts/charts/secrets-store-sync-controller/templates/create_update_token_request_deny.yaml b/charts/charts/secrets-store-sync-controller/templates/create_update_token_request_deny.yaml new file mode 100644 index 0000000..6f68917 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/create_update_token_request_deny.yaml @@ -0,0 +1,24 @@ +{{- if .Values.validatingAdmissionPolicies.applyPolicies -}} +{{ include "secrets-store-sync-controller.admissionApiVersion" . }} +kind: ValidatingAdmissionPolicy +metadata: + name: "secrets-store-sync-controller-create-update-token-deny-policy" +spec: + failurePolicy: Fail + matchConditions: + - name: 'user-is-secrets-store-sync-controller' + expression: {{ include "chartname.matchConditionExpression" . | quote }} + matchConstraints: + resourceRules: + - apiGroups: [""] + apiVersions: ["v1"] + operations: ["CREATE", "UPDATE"] + resources: ["secrets"] + variables: + - name: deniedSecretTypes + expression: {{ include "chartname.secretTypesList" .Values.validatingAdmissionPolicies.deniedSecretTypes | quote }} + validations: + - expression: "variables.deniedSecretTypes == false" + message: "Only secrets with types defined in the allowedSecretTypes are allowed." + messageExpression: "'secrets-store-sync-controller has failed to ' + string(request.operation) + ' secret with ' + string(object.type) + ' type ' + 'in the ' + string(request.namespace) + ' namespace. The controller is not allowed to create or update secrets with this type.'" +{{- end -}} diff --git a/charts/charts/secrets-store-sync-controller/templates/delete_secrets.yaml b/charts/charts/secrets-store-sync-controller/templates/delete_secrets.yaml new file mode 100644 index 0000000..1441227 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/delete_secrets.yaml @@ -0,0 +1,21 @@ +{{- if .Values.validatingAdmissionPolicies.applyPolicies -}} +{{ include "secrets-store-sync-controller.admissionApiVersion" . }} +kind: ValidatingAdmissionPolicy +metadata: + name: "secrets-store-sync-controller-delete-policy" +spec: + failurePolicy: Fail + matchConditions: + - name: 'user-is-secrets-store-sync-controller' + expression: {{ include "chartname.matchConditionExpression" . | quote }} + matchConstraints: + resourceRules: + - apiGroups: [""] + apiVersions: ["v1"] + operations: ["DELETE"] + resources: ["secrets"] + validations: + - expression: "request.operation == 'delete'" + message: "The controller is not allowed to delete secrets." + messageExpression: "'secrets-store-sync-controller has failed to ' + string(request.operation) + ' secrets in the ' + string(request.namespace) + ' namespace. The controller is not allowed to delete secrets.'" +{{- end -}} diff --git a/charts/charts/secrets-store-sync-controller/templates/leader_election_role.yaml b/charts/charts/secrets-store-sync-controller/templates/leader_election_role.yaml new file mode 100644 index 0000000..a022f51 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/leader_election_role.yaml @@ -0,0 +1,46 @@ +# permissions to do leader election. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/name: role + app.kubernetes.io/instance: leader-election-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: secrets-store-sync-controller + app.kubernetes.io/part-of: secrets-store-sync-controller + app.kubernetes.io/managed-by: kustomize + secrets-store.io/system: "true" + name: secrets-store-sync-controller-leader-election-role + namespace: {{ .Release.Namespace }} +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch diff --git a/charts/charts/secrets-store-sync-controller/templates/leader_election_role_binding.yaml b/charts/charts/secrets-store-sync-controller/templates/leader_election_role_binding.yaml new file mode 100644 index 0000000..2e31ed3 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/leader_election_role_binding.yaml @@ -0,0 +1,19 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/name: rolebinding + app.kubernetes.io/instance: leader-election-rolebinding + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: secrets-store-sync-controller + app.kubernetes.io/part-of: secrets-store-sync-controller + app.kubernetes.io/managed-by: kustomize + secrets-store.io/system: "true" + name: secrets-store-sync-controller-leader-election-rolebinding + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: secrets-store-sync-controller-leader-election-role +subjects: + {{- include "secrets-store-sync-controller.subjects" . | nindent 2 }} diff --git a/manifest_staging/charts/secrets-store-sync-controller/templates/secret-sync-controller.yaml b/charts/charts/secrets-store-sync-controller/templates/secrets-store-sync-controller.yaml similarity index 97% rename from manifest_staging/charts/secrets-store-sync-controller/templates/secret-sync-controller.yaml rename to charts/charts/secrets-store-sync-controller/templates/secrets-store-sync-controller.yaml index 3c1b514..7479b51 100644 --- a/manifest_staging/charts/secrets-store-sync-controller/templates/secret-sync-controller.yaml +++ b/charts/charts/secrets-store-sync-controller/templates/secrets-store-sync-controller.yaml @@ -2,14 +2,14 @@ apiVersion: v1 kind: ServiceAccount metadata: labels: - app.kuberentes.io/instance: controller-manager + app.kubernetes.io/instance: controller-manager app.kubernetes.io/component: rbac app.kubernetes.io/created-by: secrets-store-sync-controller app.kubernetes.io/name: serviceaccount app.kubernetes.io/part-of: secrets-store-sync-controller secrets-store.io/system: "true" name: "secrets-store-sync-controller-manager" - namespace: {{ .Values.namespace }} + namespace: {{ .Release.Namespace }} annotations: --- @@ -86,7 +86,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: secrets-store-sync-controller-manager - namespace: {{ .Values.namespace }} + namespace: {{ .Release.Namespace }} labels: app.kubernetes.io/component: manager app.kubernetes.io/created-by: secrets-store-sync-controller diff --git a/charts/charts/secrets-store-sync-controller/templates/secretsync_editor_role.yaml b/charts/charts/secrets-store-sync-controller/templates/secretsync_editor_role.yaml new file mode 100644 index 0000000..1ac9b23 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/secretsync_editor_role.yaml @@ -0,0 +1,31 @@ +# permissions for end users to edit secretsyncs. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: secretsync-editor-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: secrets-store-sync-controller + app.kubernetes.io/part-of: secrets-store-sync-controller + app.kubernetes.io/managed-by: kustomize + name: secretsync-editor-role +rules: +- apiGroups: + - secret-sync.x-k8s.io + resources: + - secretsyncs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - secret-sync.x-k8s.io + resources: + - secretsyncs/status + verbs: + - get diff --git a/charts/charts/secrets-store-sync-controller/templates/secretsync_viewer_role.yaml b/charts/charts/secrets-store-sync-controller/templates/secretsync_viewer_role.yaml new file mode 100644 index 0000000..4d018a3 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/secretsync_viewer_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to view secretsyncs. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: secretsync-viewer-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: secrets-store-sync-controller + app.kubernetes.io/part-of: secrets-store-sync-controller + app.kubernetes.io/managed-by: kustomize + name: secretsync-viewer-role +rules: +- apiGroups: + - secret-sync.x-k8s.io + resources: + - secretsyncs + verbs: + - get + - list + - watch +- apiGroups: + - secret-sync.x-k8s.io + resources: + - secretsyncs/status + verbs: + - get diff --git a/charts/charts/secrets-store-sync-controller/templates/update_owners_check_old_object.yaml b/charts/charts/secrets-store-sync-controller/templates/update_owners_check_old_object.yaml new file mode 100644 index 0000000..06484d4 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/update_owners_check_old_object.yaml @@ -0,0 +1,30 @@ +{{- if .Values.validatingAdmissionPolicies.applyPolicies -}} +{{ include "secrets-store-sync-controller.admissionApiVersion" . }} +kind: ValidatingAdmissionPolicy +metadata: + name: "secrets-store-sync-controller-update-check-oldobject-policy" +spec: + failurePolicy: Fail + paramKind: + apiVersion: v1 + kind: ConfigMap + matchConditions: + - name: 'user-is-secrets-store-sync-controller' + expression: {{ include "chartname.matchConditionExpression" . | quote }} + matchConstraints: + resourceRules: + - apiGroups: [""] + apiVersions: ["v1"] + operations: ["UPDATE"] + resources: ["secrets"] + variables: + - name: hasOneSecretSyncOwner + expression: "has(oldObject.metadata.ownerReferences) && (size(oldObject.metadata.ownerReferences) == 1 && oldObject.metadata.ownerReferences.all(o, o.kind == 'SecretSync' && o.apiVersion.startsWith('secret-sync.x-k8s.io/') && o.name == object.metadata.name))" + - name: allowedSecretTypes + expression: {{ include "chartname.secretTypesList" .Values.validatingAdmissionPolicies.allowedSecretTypes | quote }} + validations: + - expression: "variables.allowedSecretTypes == true && variables.hasOneSecretSyncOwner == true" + message: "Only secrets with one secret sync owner and with types defined in the allowedSecretTypes list can be updated by the controller" + messageExpression: "string(params.data.controllerName) + ' has failed to ' + string(request.operation) + ' old secret with ' + string(object.type) + ' type ' + 'in the ' + string(request.namespace) + ' namespace. The controller can only update secrets in the allowed types list with a single secrets-store-sync-controller owner.'" + reason: "Forbidden" +{{- end -}} diff --git a/charts/charts/secrets-store-sync-controller/templates/update_secrets_based_on_label.yaml b/charts/charts/secrets-store-sync-controller/templates/update_secrets_based_on_label.yaml new file mode 100644 index 0000000..5354a34 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/update_secrets_based_on_label.yaml @@ -0,0 +1,28 @@ +{{- if .Values.validatingAdmissionPolicies.applyPolicies -}} +{{ include "secrets-store-sync-controller.admissionApiVersion" . }} +kind: ValidatingAdmissionPolicy +metadata: + name: "secrets-store-sync-controller-update-label-policy" +spec: + failurePolicy: Fail + matchConditions: + - name: 'user-is-secrets-store-sync-controller' + expression: {{ include "chartname.matchConditionExpression" . | quote }} + matchConstraints: + resourceRules: + - apiGroups: [""] + apiVersions: ["v1"] + operations: ["UPDATE"] + resources: ["secrets"] + variables: + - name: oldSecretHasLabels + expression: "has(oldObject.metadata.labels) ? true : false" + - name: oldSecretHasExpectedLabelKey + expression: {{ include "secrets-store-sync-controller.oldSecretHasExpectedLabelKey" . | quote }} + - name: oldSecretHasExpectedLabelValue + expression: {{ include "secrets-store-sync-controller.oldSecretHasExpectedLabelValue" . | quote }} + validations: + - expression: "variables.oldSecretHasExpectedLabelKey && variables.oldSecretHasExpectedLabelValue" + message: "Only secrets with the correct label can be updated" + messageExpression: "'secrets-store-sync-controller has failed to ' + string(request.operation) + ' secret with ' + string(object.type) + ' type ' + 'in the ' + string(request.namespace) + ' namespace because it does not have the correct label. Delete the secret and force the controller to recreate it with the correct label.'" +{{- end -}} diff --git a/charts/charts/secrets-store-sync-controller/templates/validate_annotation_format_secret_sync.yaml b/charts/charts/secrets-store-sync-controller/templates/validate_annotation_format_secret_sync.yaml new file mode 100644 index 0000000..8e306cc --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/validate_annotation_format_secret_sync.yaml @@ -0,0 +1,26 @@ +{{- if .Values.validatingAdmissionPolicies.applyPolicies -}} +{{ include "secrets-store-sync-controller.admissionApiVersion" . }} +kind: ValidatingAdmissionPolicy +metadata: + name: "secrets-store-sync-controller-validate-annotation-policy" +spec: + failurePolicy: Fail + matchConstraints: + resourceRules: + - apiGroups: ["secret-sync.x-k8s.io"] + apiVersions: ["*"] + operations: ["CREATE", "UPDATE"] + resources: ["secretsyncs"] + variables: + - name: secretHasAnnotation + expression: "has(object.spec.secretObject.annotations) ? true : false" + - name: secretHasCorrectAnnotationsFormat + expression: "variables.secretHasAnnotation && object.spec.secretObject.annotations.all(x, size(x) < 253 && x.matches('^([A-Za-z0-9][-A-Za-z0-9_.]*[A-Za-z0-9])?(/[A-Za-z0-9]([-A-Za-z0-9_.]*[A-Za-z0-9])*)?$') == true)" + - name: secretHasCorrectAnnotationsValueFormat + expression: "variables.secretHasAnnotation && object.spec.secretObject.annotations.all(x, size(object.spec.secretObject.annotations[x]) < 63 && object.spec.secretObject.annotations[x].matches('^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$') == true)" + validations: + - expression: "variables.secretHasAnnotation == false || (variables.secretHasCorrectAnnotationsFormat && variables.secretHasCorrectAnnotationsValueFormat) == true" + message: "One of the annotations applied on the secret has an invalid format. Update the annotation and try again." + messageExpression: "string(request.userInfo.username) + ' has failed to ' + string(request.operation) + ' secret with ' + string(object.type) + ' type ' + 'in the ' + string(request.namespace) + ' namespace. One of the annotations applied on the secret has an invalid format. Update the annotation and try again.'" + reason: "Invalid" +{{- end -}} diff --git a/charts/charts/secrets-store-sync-controller/templates/validate_label_format_secret_sync.yaml b/charts/charts/secrets-store-sync-controller/templates/validate_label_format_secret_sync.yaml new file mode 100644 index 0000000..586bb22 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/validate_label_format_secret_sync.yaml @@ -0,0 +1,26 @@ +{{- if .Values.validatingAdmissionPolicies.applyPolicies -}} +{{ include "secrets-store-sync-controller.admissionApiVersion" . }} +kind: ValidatingAdmissionPolicy +metadata: + name: "secrets-store-sync-controller-validate-label-policy" +spec: + failurePolicy: Fail + matchConstraints: + resourceRules: + - apiGroups: ["secret-sync.x-k8s.io"] + apiVersions: ["*"] + operations: ["CREATE", "UPDATE"] + resources: ["secretsyncs"] + variables: + - name: secretHasLabel + expression: "has(object.spec.secretObject.labels) ? true : false" + - name: secretHasCorrectLabelsFormat + expression: "variables.secretHasLabel && object.spec.secretObject.labels.all(x, size(x) < 253 && x.matches('^([A-Za-z0-9][-A-Za-z0-9_.]*[A-Za-z0-9])?(/[A-Za-z0-9]([-A-Za-z0-9_.]*[A-Za-z0-9])*)?$') == true)" + - name: secretHasCorrectLabelsValueFormat + expression: "variables.secretHasLabel && object.spec.secretObject.labels.all(x, size(object.spec.secretObject.labels[x]) < 63 && object.spec.secretObject.labels[x].matches('^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$') == true)" + validations: + - expression: "variables.secretHasLabel == false || (variables.secretHasCorrectLabelsFormat && variables.secretHasCorrectLabelsValueFormat) == true" + message: "One of the labels applied on the secret has an invalid format. Update the label and try again." + messageExpression: "string(request.userInfo.username) + ' has failed to ' + string(request.operation) + ' secret with ' + string(object.type) + ' type ' + 'in the ' + string(request.namespace) + ' namespace because it does not have the correct label. Delete the secret and force the controller to recreate it with the correct label.'" + reason: "Invalid" +{{- end -}} diff --git a/charts/charts/secrets-store-sync-controller/templates/validate_token_config.yaml b/charts/charts/secrets-store-sync-controller/templates/validate_token_config.yaml new file mode 100644 index 0000000..b3a03c2 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/templates/validate_token_config.yaml @@ -0,0 +1,29 @@ +{{- if .Values.validatingAdmissionPolicies.applyPolicies -}} +{{ include "secrets-store-sync-controller.admissionApiVersion" . }} +kind: ValidatingAdmissionPolicy +metadata: + name: "secrets-store-sync-controller-validate-token-policy" +spec: + failurePolicy: Fail + matchConditions: + - name: 'user-is-secrets-store-sync-controller' + expression: {{ include "chartname.matchConditionExpression" . | quote }} + matchConstraints: + resourceRules: + - apiGroups: [""] + apiVersions: ["v1"] + operations: ["CREATE"] + resources: ["serviceaccounts/token"] + variables: + - name: expirationSeconds + expression: "string(object.spec.expirationSeconds) == '600'" + - name: hasCorrectAudience + expression: {{ include "secrets-store-sync-controller.tokenAudienceComparison" . | quote }} + - name: requestHasOnlyOneAudience + expression: "object.spec.audiences.size() == 1" + validations: + - expression: "variables.hasCorrectAudience == true && variables.expirationSeconds == true && variables.requestHasOnlyOneAudience == true" + message: "'Creating a serviceaccount token has failed because the configuration isn't correct.'" + messageExpression: "'secrets-store-sync-controller has failed to ' + string(request.operation) + ' ' + string(request.name) + ' token in the ' + string(request.namespace) + ' namespace. Check the configuration.'" + reason: "Forbidden" +{{- end -}} diff --git a/charts/charts/secrets-store-sync-controller/values.yaml b/charts/charts/secrets-store-sync-controller/values.yaml new file mode 100644 index 0000000..a494fc5 --- /dev/null +++ b/charts/charts/secrets-store-sync-controller/values.yaml @@ -0,0 +1,85 @@ +# Default values for secrets-store-sync-controller. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. +controllerName: secrets-store-sync-controller-manager + +tokenRequestAudience: + - audience: # e.g. api://TokenAudienceExample + +logVerbosity: 5 + +validatingAdmissionPolicies: + applyPolicies: true + kubernetesReleaseVersion: "1.28.0" + allowedSecretTypes: + - "Opaque" + - "kubernetes.io/basic-auth" + - "bootstrap.kubernetes.io/token" + - "kubernetes.io/dockerconfigjson" + - "kubernetes.io/dockercfg" + - "kubernetes.io/ssh-auth" + - "kubernetes.io/tls" + + deniedSecretTypes: + - "kubernetes.io/service-account-token" + +image: + repository: registry.k8s.io/secrets-store-sync/controller # e.g. my-registry.example.com/my-repo + pullPolicy: IfNotPresent + tag: v0.0.1 + +securityContext: + # Default values, can be overridden or extended + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + +resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + +podAnnotations: + kubectl.kubernetes.io/default-container: manager + +podLabels: + control-plane: controller-manager + secrets-store.io/system: "true" + app: secrets-store-sync-controller + +nodeSelector: + +tolerations: +- operator: Exists + +affinity: + +metricsPort: 8085 + +providerContainer: +# - name: provider-e2e-installer +# image: aramase/e2e-provider:v0.0.1 +# imagePullPolicy: IfNotPresent +# args: +# - --endpoint=unix:///provider/e2e-provider.sock +# resources: +# requests: +# cpu: 50m +# memory: 100Mi +# limits: +# cpu: 50m +# memory: 100Mi +# securityContext: +# allowPrivilegeEscalation: false +# readOnlyRootFilesystem: true +# runAsUser: 0 +# capabilities: +# drop: +# - ALL +# volumeMounts: +# - mountPath: "/provider" +# name: providervol diff --git a/docker/Makefile b/docker/Makefile index a4441c3..4f9fd68 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -14,6 +14,7 @@ REGISTRY?=docker.io IMAGE_NAME=controller +# to trigger staging image build we should update the IMAGE_VERSION while cutting a release IMAGE_VERSION?=v0.0.1 BUILD_TIMESTAMP := $(shell date +%Y-%m-%d-%H:%M) BUILD_COMMIT := $(shell git rev-parse --short HEAD) diff --git a/manifest_staging/charts/secrets-store-sync-controller/README.md b/manifest_staging/charts/secrets-store-sync-controller/README.md index 5c0eb87..4f1fbb3 100644 --- a/manifest_staging/charts/secrets-store-sync-controller/README.md +++ b/manifest_staging/charts/secrets-store-sync-controller/README.md @@ -1,36 +1,30 @@ # Deploying the Secrets Store Sync Controller -You can deploy the Secrets Store Sync Controller using Helm. This guide provides instructions for deploying the Secrets Store Sync Controller using Helm. - -You can use the following commands to deploy the Secrets Store Sync Controller using Helm: +You can deploy the Secrets Store Sync Controller with Helm using following command: ```sh helm install -f values secrets-store-sync-controller charts/secrets-store-sync-controller ``` -# Configuration and Parameters -You can customize the installation by modifying values in the values.yaml file or by passing parameters to the helm install command using the --set key=value[,key=value] argument. +## Configuration and Parameters +You can customize the installation by modifying values in the `values.yaml` file or by passing parameters to the helm install command using the `--set key=value[,key=value]` argument. | Parameter Name | Description | Default Value | |--------------------------------------------------|---------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `providerContainer` | The container for the Secrets Store Sync Provider | `[- name: provider-aws-installer ...]` | -| `controllerName` | The name of the Secrets Store Sync Controller. | `secret-sync-controller-manager` | -| `namespace` | The namespace to deploy the chart to. | `secret-sync-controller-system` | +| `providerContainer` | The container for the Secrets Store Sync Controller. | `[- name: provider-aws-installer ...]` | +| `controllerName` | The name of the Secrets Store Sync Controller. | `secrets-store-sync-controller-manager` | | `tokenRequestAudience` | The audience for the token request. | `[]` | | `logVerbosity` | The log level. | `5` | | `validatingAdmissionPolicies.applyPolicies` | Determines whether the Secrets Store Sync Controller should apply policies. | `true` | | `validatingAdmissionPolicies.allowedSecretTypes` | The types of secrets that the Secrets Store Sync Controller should allow. | `["Opaque", "kubernetes.io/basic-auth", "bootstrap.kubernetes.io/token", "kubernetes.io/dockerconfigjson", "kubernetes.io/dockercfg", "kubernetes.io/ssh-auth", "kubernetes.io/tls"]` | | `validatingAdmissionPolicies.deniedSecretTypes` | The types of secrets that the Secrets Store Sync Controller should deny. | `["kubernetes.io/service-account-token"]` | -| `image.repository` | The image repository of the Secrets Store Sync Controller. | `registry.k8s.io/secrets-store-sync-controller:v0.0.1` | +| `image.repository` | The image repository of the Secrets Store Sync Controller. | `registry.k8s.io/secrets-store-sync/controller` | | `image.pullPolicy` | Image pull policy. | `IfNotPresent` | -| `image.tag` | The specific image tag to use. Overrides the image tag whose default is the chart's `appVersion`. | `""` | -| `imagePullSecrets` | Array of image pull secrets for accessing private registries. | `[{"name": "regcred"}]` | -| `nameOverride` | A string to partially override `secretsync.fullname` template (will maintain the release name). | `""` | -| `fullnameOverride` | A string to fully override `secretsync.fullname` template. | `""` | +| `image.tag` | The specific image tag to use. Overrides the image tag whose default is the chart's `appVersion`. | `v0.0.1` | | `securityContext` | Security context for the Secrets Store Sync Controller. | `{ allowPrivilegeEscalation: false, capabilities: { drop: [ALL] } }` | | `resources` | The resource request/limits for the Secrets Store Sync Controller image. | `limits: 500m CPU, 128Mi; requests: 10m CPU, 64Mi` | | `podAnnotations` | Annotations to be added to pods. | `{ kubectl.kubernetes.io/default-container: "manager" }` | -| `podLabels` | Labels to be added to pods. | `{ control-plane: "controller-manager", secrets-store.io/system: "true" }` | -| `nodeSelector` | Node labels for pod assignment. | `{ kubernetes.io/os: "linux" }` | +| `podLabels` | Labels to be added to pods. | `{ control-plane: "controller-manager", secrets-store.io/system: "true", app: "secrets-store-sync-controller" }` | +| `nodeSelector` | Node labels for pod assignment. | `{}` | | `tolerations` | Tolerations for pod assignment. | `[{ operator: "Exists" }]` | -These parameters offer flexibility in configuring and deploying the Secrets Store Sync Controller according to specific requirements in your Kubernetes environment. Remember to replace values appropriately or use the --set flag when installing the chart via Helm. +These parameters offer flexibility in configuring and deploying the Secrets Store Sync Controller according to specific requirements in your Kubernetes environment. Remember to replace values appropriately or use the `--set` flag when installing the chart via Helm. diff --git a/manifest_staging/charts/secrets-store-sync-controller/templates/_helpers.tpl b/manifest_staging/charts/secrets-store-sync-controller/templates/_helpers.tpl index 9818447..66fd60c 100644 --- a/manifest_staging/charts/secrets-store-sync-controller/templates/_helpers.tpl +++ b/manifest_staging/charts/secrets-store-sync-controller/templates/_helpers.tpl @@ -4,12 +4,12 @@ Generate subjects for role binding and cluster role binding. {{- define "secrets-store-sync-controller.subjects" -}} - kind: ServiceAccount name: "secrets-store-sync-controller-manager" - namespace: {{ .Values.namespace }} + namespace: {{ .Release.Namespace }} {{- end }} {{/* Generate match condition expression */}} {{- define "chartname.matchConditionExpression" -}} -{{- printf "request.userInfo.username == 'system:serviceaccount:%s:%s'" .Values.namespace .Values.controllerName -}} +{{- printf "request.userInfo.username == 'system:serviceaccount:%s:%s'" .Release.Namespace .Values.controllerName -}} {{- end -}} {{/* @@ -87,7 +87,7 @@ Determine the api version for the validating admission policies. apiVersion: admissionregistration.k8s.io/v1alpha1 {{- else if semverCompare "~1.28.0" .Values.validatingAdmissionPolicies.kubernetesReleaseVersion -}} apiVersion: admissionregistration.k8s.io/v1beta1 -{{- else if semverCompare "~1.29.0" .Values.validatingAdmissionPolicies.kubernetesReleaseVersion -}} +{{- else if semverCompare "^1.29.x" .Values.validatingAdmissionPolicies.kubernetesReleaseVersion -}} apiVersion: admissionregistration.k8s.io/v1 {{- else -}} apiVersion: unsupported-validating-admission-api-version diff --git a/manifest_staging/charts/secrets-store-sync-controller/templates/leader_election_role.yaml b/manifest_staging/charts/secrets-store-sync-controller/templates/leader_election_role.yaml index e5b0413..a022f51 100644 --- a/manifest_staging/charts/secrets-store-sync-controller/templates/leader_election_role.yaml +++ b/manifest_staging/charts/secrets-store-sync-controller/templates/leader_election_role.yaml @@ -11,7 +11,7 @@ metadata: app.kubernetes.io/managed-by: kustomize secrets-store.io/system: "true" name: secrets-store-sync-controller-leader-election-role - namespace: {{ .Values.namespace }} + namespace: {{ .Release.Namespace }} rules: - apiGroups: - "" diff --git a/manifest_staging/charts/secrets-store-sync-controller/templates/leader_election_role_binding.yaml b/manifest_staging/charts/secrets-store-sync-controller/templates/leader_election_role_binding.yaml index 05ae288..2e31ed3 100644 --- a/manifest_staging/charts/secrets-store-sync-controller/templates/leader_election_role_binding.yaml +++ b/manifest_staging/charts/secrets-store-sync-controller/templates/leader_election_role_binding.yaml @@ -10,7 +10,7 @@ metadata: app.kubernetes.io/managed-by: kustomize secrets-store.io/system: "true" name: secrets-store-sync-controller-leader-election-rolebinding - namespace: {{ .Values.namespace }} + namespace: {{ .Release.Namespace }} roleRef: apiGroup: rbac.authorization.k8s.io kind: Role diff --git a/manifest_staging/charts/secrets-store-sync-controller/templates/namespace.yaml b/manifest_staging/charts/secrets-store-sync-controller/templates/namespace.yaml deleted file mode 100644 index 4b52fee..0000000 --- a/manifest_staging/charts/secrets-store-sync-controller/templates/namespace.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - app.kubernetes.io/component: manager - app.kubernetes.io/created-by: secrets-store-sync-controller - app.kubernetes.io/instance: system - app.kubernetes.io/name: namespace - app.kubernetes.io/part-of: secrets-store-sync-controller - control-plane: controller-manager - secrets-store.io/system: "true" - name: {{ .Values.namespace }} diff --git a/manifest_staging/charts/secrets-store-sync-controller/templates/secrets-store-sync-controller.yaml b/manifest_staging/charts/secrets-store-sync-controller/templates/secrets-store-sync-controller.yaml new file mode 100644 index 0000000..7479b51 --- /dev/null +++ b/manifest_staging/charts/secrets-store-sync-controller/templates/secrets-store-sync-controller.yaml @@ -0,0 +1,179 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/instance: controller-manager + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: secrets-store-sync-controller + app.kubernetes.io/name: serviceaccount + app.kubernetes.io/part-of: secrets-store-sync-controller + secrets-store.io/system: "true" + name: "secrets-store-sync-controller-manager" + namespace: {{ .Release.Namespace }} + annotations: + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: secrets-store-sync-controller-manager-role +rules: +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - patch +- apiGroups: + - "" + resources: + - serviceaccounts/token + verbs: + - create +- apiGroups: + - secrets-store.csi.x-k8s.io + resources: + - secretproviderclasses + verbs: + - get + - list + - watch +- apiGroups: + - secret-sync.x-k8s.io + resources: + - secretsyncs + verbs: + - get + - list + - watch +- apiGroups: + - secret-sync.x-k8s.io + resources: + - secretsyncs/status + verbs: + - get + - patch + - update +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: secrets-store-sync-controller + app.kubernetes.io/instance: manager-rolebinding + + app.kubernetes.io/name: clusterrolebinding + app.kubernetes.io/part-of: secrets-store-sync-controller + secrets-store.io/system: "true" + name: secrets-store-sync-controller-manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: secrets-store-sync-controller-manager-role +subjects: + {{- include "secrets-store-sync-controller.subjects" . | nindent 2 }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: secrets-store-sync-controller-manager + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/component: manager + app.kubernetes.io/created-by: secrets-store-sync-controller + control-plane: controller-manager + app.kubernetes.io/name: deployment + app.kubernetes.io/part-of: secrets-store-sync-controller + app.kubernetes.io/instance: controller-manager + secrets-store.io/system: "true" +spec: + selector: + matchLabels: + control-plane: controller-manager + secrets-store.io/system: "true" + replicas: 1 + template: + metadata: + annotations: + {{- toYaml .Values.podAnnotations | nindent 8 }} + labels: + {{- toYaml .Values.podLabels | nindent 8 }} + spec: + nodeSelector: + kubernetes.io/os: linux +{{- if .Values.nodeSelector }} +{{- toYaml .Values.nodeSelector | nindent 8 }} +{{- end }} + tolerations: + {{- toYaml .Values.tolerations | nindent 8 }} + affinity: + {{- toYaml .Values.affinity | nindent 8 }} + containers: + {{- if and .Values.providerContainer (gt (len .Values.providerContainer) 0) }} + {{- toYaml .Values.providerContainer | nindent 6 }} + {{- end }} + - name: manager + image: {{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + args: + - --provider-volume=/provider + - --token-request-audience={{ include "secrets-store-sync-controller.listToString" . }} + - --health-probe-bind-address=:8081 + - --metrics-bind-address=:{{ .Values.metricsPort }} + - --leader-elect + env: + - name: SYNC_CONTROLLER_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: SYNC_CONTROLLER_POD_UID + valueFrom: + fieldRef: + fieldPath: metadata.uid + - name: SYNC_CONTROLLER_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + ports: + - name: metrics + containerPort: {{ .Values.metricsPort }} + protocol: TCP + securityContext: + {{- toYaml .Values.securityContext | nindent 10 }} + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + command: + - /secrets-store-sync-controller +{{- with .Values.resources }} + resources: +{{ toYaml . | indent 10 }} +{{- end }} + volumeMounts: + - mountPath: "/provider" + name: providervol + serviceAccountName: "secrets-store-sync-controller-manager" + terminationGracePeriodSeconds: 10 + volumes: + - name: providervol + hostPath: + path: "/var/run/secrets-store-sync-providers" + type: DirectoryOrCreate diff --git a/manifest_staging/charts/secrets-store-sync-controller/values.yaml b/manifest_staging/charts/secrets-store-sync-controller/values.yaml index c737185..a494fc5 100644 --- a/manifest_staging/charts/secrets-store-sync-controller/values.yaml +++ b/manifest_staging/charts/secrets-store-sync-controller/values.yaml @@ -2,7 +2,6 @@ # This is a YAML-formatted file. # Declare variables to be passed into your templates. controllerName: secrets-store-sync-controller-manager -namespace: secrets-store-sync-controller-system tokenRequestAudience: - audience: # e.g. api://TokenAudienceExample @@ -25,9 +24,9 @@ validatingAdmissionPolicies: - "kubernetes.io/service-account-token" image: - repository: controller # e.g. my-registry.example.com/my-repo + repository: registry.k8s.io/secrets-store-sync/controller # e.g. my-registry.example.com/my-repo pullPolicy: IfNotPresent - tag: "v0.0.1" + tag: v0.0.1 securityContext: # Default values, can be overridden or extended