diff --git a/content/en/docs/private-platform/secret-management/pmp-configure-azure-key-vault.md b/content/en/docs/private-platform/secret-management/pmp-configure-azure-key-vault.md index a5e904abac2..b351d9b26e7 100644 --- a/content/en/docs/private-platform/secret-management/pmp-configure-azure-key-vault.md +++ b/content/en/docs/private-platform/secret-management/pmp-configure-azure-key-vault.md @@ -58,24 +58,24 @@ When creating the JSON structure for your secret, you must use a flat key-value * Bitbucket - * **VCS.BitbucketProjectAdminPAT** - Personal access token for the Bitbucket project admin - * **VCS.BitbucketAdminPassword** - Password for the Bitbucket admin user + * **VCS-BitbucketProjectAdminPAT** - Personal access token for the Bitbucket project admin + * **VCS-BitbucketAdminPassword** - Password for the Bitbucket admin user * GitLab - * **VCS.GitlabGroupOwnerPAT** - Personal access token for the GitLab group owner - * **VCS.GitlabAdminPAT** - Personal access token for the GitLab admin + * **VCS-GitlabGroupOwnerPAT** - Personal access token for the GitLab group owner + * **VCS-GitlabAdminPAT** - Personal access token for the GitLab admin * GitHub - * **VCS.GithubOrgOwnerPAT** - Personal access token for the GitHub organization owner - * **VCS.GithubAdminPAT** - Personal access token for the GitHub admin - * **VCS.GithubEnterpriseClientSecret** - Client secret for the GitHub Enterprise app + * **VCS-GithubOrgOwnerPAT** - Personal access token for the GitHub organization owner + * **VCS-GithubAdminPAT** - Personal access token for the GitHub admin + * **VCS-GithubEnterpriseClientSecret** - Client secret for the GitHub Enterprise app * Azure - * **VCS.AzureDevOpsOrgAdminPAT** - Personal access token for the Azure DevOps organization owner - * **VCS.AzureAuthSecret** - Currently unused + * **VCS-AzureDevOpsOrgAdminPAT** - Personal access token for the Azure DevOps organization owner + * **VCS-AzureAuthSecret** - Currently unused * Kubernetes Build Settings @@ -83,74 +83,78 @@ When creating the JSON structure for your secret, you must use a flat key-value * fileServerBasic - * **BuildPackage.FileBasicAuthPassword** - Password for the file server + * **BuildPackage-FileBasicAuthPassword** - Password for the file server * AwsAKSK - * **BuildPackage.AwsSecretAccessKey** - AWS secret access key for the file server + * **BuildPackage-AwsSecretAccessKey** - AWS secret access key for the file server * RuntimeBaseImage * privateRegistry - * **RuntimeBaseImage.PrivateRegistryPassword** - Base image for the runtime + * **RuntimeBaseImage-PrivateRegistryPassword** - Base image for the runtime * S3compatibleAccessKey - * **RuntimeBaseImage.S3CompatibleAccessKey** - S3-compatible access key for the base image + * **RuntimeBaseImage-S3CompatibleAccessKey** - S3-compatible access key for the base image * MDAStorage * fileServerBasic - * **MDAStorage.FileBasicAuthPassword** - Password for the file server + * **MDAStorage-FileBasicAuthPassword** - Password for the file server * awsAKSK - * **MDAStorage.AwsSecretAccessKey** - AWS secret access key for the MDA storage + * **MDAStorage-AwsSecretAccessKey** - AWS secret access key for the MDA storage * OCIRegistry * privateRegistry - * **OCIRegistry.PrivateRegistryPassword** - Password for the private registry + * **OCIRegistry-PrivateRegistryPassword** - Password for the private registry * S3compatibleAccessKey - * **OCIRegistry.S3CompatibleAccessKey** - S3 compatible access key for the OCI registry + * **OCIRegistry-S3CompatibleAccessKey** - S3 compatible access key for the OCI registry * Build Cluster Settings - * **BuildCluster.KubernetesConfigureToken** - Token for the Kubernetes cluster configuration - * **CIAdmin.JenkinsConfigureAPIToken** - Token for the Jenkins configuration - * **CIAdmin.JenkinsTriggerAuthToken** - Token for the Jenkins trigger configuration - * **CIAdmin.AzureOrgAdminPAT** - Personal access token for the Azure DevOps configuration - * **CIAdmin.AzureBlobStorageToken** - SAS token for the Azure Blob Storage - * **CIAdmin.AzureAwsS3SK** - Name of the Azure DevOps organization + * **BuildCluster-KubernetesConfigureToken** - Token for the Kubernetes cluster configuration + * **CIAdmin-JenkinsConfigureAPIToken** - Token for the Jenkins configuration + * **CIAdmin-JenkinsTriggerAuthToken** - Token for the Jenkins trigger configuration + * **CIAdmin-AzureOrgAdminPAT** - Personal access token for the Azure DevOps configuration + * **CIAdmin-AzureBlobStorageToken** - SAS token for the Azure Blob Storage + * **CIAdmin-AzureAwsS3SK** - Name of the Azure DevOps organization * **Cluster Manager** - * **ClusterManager.OCIRegistryAWSSK** - OCI Registry: AWS secret access key for the OCI registry - * **serverClusterManager.OCIBasicAuthPassword** - OCI Registry: Password for the OCI registry - * **serverClusterManager.MDAAWSSK** - MDA Storage: AWS secret access key for the MDA file - * **serverClusterManager.MDAFileBasicAuthPassword** - MDA Storage: Password for the MDA file - * **serverClusterManager.KubernetesApiToken** - Kubernetes API: Token for accessing the Kubernetes API - * **serverClusterManager.GrafanaAPIKey** - Grafana: API key for accessing the Grafana server + * **ClusterManager-OCIRegistryAWSSK** - OCI Registry: AWS secret access key for the OCI registry + * **serverClusterManager-OCIBasicAuthPassword** - OCI Registry: Password for the OCI registry + * **serverClusterManager-MDAAWSSK** - MDA Storage: AWS secret access key for the MDA file + * **serverClusterManager-MDAFileBasicAuthPassword** - MDA Storage: Password for the MDA file + * **serverClusterManager-KubernetesApiToken** - Kubernetes API: Token for accessing the Kubernetes API + * **serverClusterManager-GrafanaAPIKey** - Grafana: API key for accessing the Grafana server * Marketplace - * **Marketplace.ImportCDNPassword** - Personal access token for the Marketplace admin + * **Marketplace-ImportCDNPassword** - Personal access token for the Marketplace admin * Email - * **Email.SMTPPassword** - Password for the SMTP server + * **Email-SMTPPassword** - Password for the SMTP server * Maia - * **Maia.AmazonBedrockApiKey** - An API key to allow [Maia](/private-mendix-platform/maia/) to connect to a custom AWS Bedrock LLM - * **Maia.AzureAiApiKey** - An API key to allow [Maia](/private-mendix-platform/maia/) to connect to a custom Azure LLM - * **Maia.AnthropicApiKey** - An API key to allow [Maia](/private-mendix-platform/maia/) to connect to a custom Azure LLM - * **Maia.OpenAiApiKey** - An API key to allow [Maia](/private-mendix-platform/maia/) to connect to a custom Azure LLM + * **Maia-AmazonBedrockApiKey** - An API key to allow [Maia](/private-mendix-platform/maia/) to connect to a custom AWS Bedrock LLM + * **Maia-AzureAiApiKey** - An API key to allow [Maia](/private-mendix-platform/maia/) to connect to a custom Azure LLM + * **Maia-AnthropicApiKey** - An API key to allow [Maia](/private-mendix-platform/maia/) to connect to a custom Azure LLM + * **Maia-OpenAiApiKey** - An API key to allow [Maia](/private-mendix-platform/maia/) to connect to a custom Azure LLM + +{{% alert color="info" %}} +Azure Key Vault secret names cannot contain underscores (`_`), periods (`.`), spaces, and special symbols. Private Mendix Platform converts any periods in a constant or runtime setting name to hyphens for the Key Vault entry (`objectName`). The `objectAlias` - that is, the file name actually mounted into the pod and read by the Mendix Runtime - always keeps the periods, exactly matching the format in the official documentation, for example, `mx-const-MyFirstModule.WelcomePageTitle`. Private Mendix Platform performs this conversion consistently both when it writes the value to Key Vault and when it builds the SecretProviderClass entry, so the two always stay in sync. However, other special characters are not automatically converted, so constant names that contain them cannot be stored in Azure Key Vault. Because of that, we recommend that you do not create secret names with underscores (`_`), periods (`.`), spaces, and special symbols. +{{% /alert %}} ### Configuring Azure AD Workload Identity diff --git a/content/en/docs/private-platform/secret-management/pmp-key-vault-integration.md b/content/en/docs/private-platform/secret-management/pmp-key-vault-integration.md index 7bb855c571a..b36920c1235 100644 --- a/content/en/docs/private-platform/secret-management/pmp-key-vault-integration.md +++ b/content/en/docs/private-platform/secret-management/pmp-key-vault-integration.md @@ -11,59 +11,142 @@ Private Mendix Platform can use Azure Key Vault to automatically manage sensitiv The integration uses the [Kubernetes Secrets Store CSI Driver](https://secrets-store-csi-driver.sigs.k8s.io/) with the Azure Key Vault provider. Every environment must have a User-Assigned Managed Identity (UAMI) from Azure Workload Identity. Private Mendix Platform automates the Kubernetes-side configuration of the `ServiceAccount` and `SecretProviderClass` objects and the Key Vault writes. The customer only needs to manage the Azure-side infrastructure (such as Key Vaults, managed identities, and RBAC), instead of manually configuring Kubernetes manifests for every environment. -### Secret Values Storage - Secret values are never stored in the Private Mendix Platform database or converted to a Kubernetes Secret object. They exist in Azure Key Vault, and are mounted into the running app pod as in-memory (`tmpfs`) files by the CSI driver. -## Prerequisites +## Prerequisites {#prerequisites} + +The customer is responsible for configuring the following prerequisites. They must be in place once per cluster, before any environment can use this feature. + +* Your cluster type must be set to AKS. To confirm this, go to **Switch to Admin Mode > Manage > Cluster Manager**, and edit or create a cluster. On the **Cluster Information** page, verify that the **Cluster Type** field shows **AKS**. Other cluster types do not currently support Azure Key Vault integration. +* You must map the following supported secret types 1-to-1 to entries in the environment's `SecretProviderClass`: -Your Private Mendix Platform must be deployed in standalone mode on AKS. + * App constant - This secret type is configured as a constant name and value in the Private Mendix Platform UI. It represents a Mendix app constant, for example, `MyFirstModule.WelcomePageTitle`. + * Runtime custom setting - This secret type is configured as a setting name and value in the Private Mendix Platform UI. It represents a [Mendix Runtime custom setting](/refguide/custom-settings/) for `m2ee` configuration. + * MxAdmin Password - This secret type is a fixed value, configured once per environment. It represents the password of the environment's administrator account. + * Debugger Password - This secret type is a fixed value, configured once per environment. It represents the password of the environment's remote debugger. -Your environments must be configured to use Azure Key Vault as the external secret store through the CSI Secrets Store Driver. +* You must install the Secrets Store CSI Driver and Azure Key Vault provider cluster-wide (typically in `kube-system`), by using the following Helm commands: -You must map the following supported secret types 1-to-1 to entries in the environment's `SecretProviderClass`: + ```text + helm repo add csi-secrets-store-provider-azure https://azure.github.io/secrets-store-csi-driver-provider-azure/charts + helm install csi-secrets-store-provider-azure csi-secrets-store-provider-azure/csi-secrets-store-provider-azure -n kube-system + ``` -* App constant - This secret type is configured as a constant name and value in the Private Mendix Platform UI. It represents a Mendix app constant, for example, `MyFirstModule.WelcomePageTitle`. -* Runtime custom setting - This secret type is configured as a setting name and value in the Private Mendix Platform UI. It represents a [Mendix Runtime custom setting](/refguide/custom-settings/) for `m2ee` configuration. - Setting name + value -* MxAdmin Password - This secret type is a fixed value, configured once per environment. It represents the password of the environment's administrator account. -* Debugger Password - This secret type is a fixed value, configured once per environment. It represents the password of the environment's remote debugger. + This chart bundles the generic `secrets-store-csi-driver` as a sub-chart dependency. Installing it also installs the underlying driver. -{{% alert color="info" %}} -Database and blob storage connection secrets are not supported through this automatic flow. You must provision them manually. For more information, refer to the general Secret Store documentation. +* The Azure Workload Identity webhook must be enabled and active on the cluster. +* OIDC issuer must be enabled on the AKS cluster. This is required for federated credentials on managed identities. +* Kubernetes RBAC must be configured as described in [Kubernetes RBAC Configuration](#rbac-configuration). + +### Kubernetes RBAC Configuration {#rbac-configuration} + +Private Mendix Platform authenticates to the Kubernetes API using a bearer token (a dedicated ServiceAccount, for example, `mxplatform-cicd`), distinct from the app-environment identities. This token needs a namespaced Role and RoleBinding in every namespace where app environments using this feature are deployed, granting the following: + +| Resource | Verbs | Used for | +| --- | --- | --- | +| `secretproviderclasses` | `get, create, patch, delete` | Creating, updating, or removing the environment's SecretProviderClass | +| `serviceaccounts` | `get, create, patch` | Reading the UAMI annotation (for Approach A) or creating and annotating the ServiceAccount (Approach B) | -The integration does not support deployments in Interactor-Agent mode, or non-AKS and on-premises Kubernetes clusters.{{% /alert %}} +{{% alert color="warning" %}} +Without Kubernetes RBAC for the Private Mendix Platform cluster credentials, calls from Private Mendix Platform fail with a *403 Forbidden* error and a message like the following `system:serviceaccount:kube-system:mxplatform-cicd cannot ... in the namespace`. +{{% /alert %}} -## Onboarding Approaches +Verify the configuration by using the following command: -The approach to implementing the Key Vault integration is slightly different for environments that do not use an Azure Workload Identity (`azure-wi`) storage or database plan. +```text +kubectl auth can-i create secretproviderclasses --as=system:serviceaccount:kube-system:mxplatform-cicd -n +``` + +## Known Limitations + +The following limitations apply: + +* In the current version of Private Mendix Platform, Azure Key Vault integration can only be enabled during environment creation. It cannot be enabled for existing environments. +* Database and blob storage connection secrets are not supported through this automatic flow. You must provision them manually. For more information, refer to the general Secret Store documentation. +* Azure-wi storage and database plans are not compatible with Interactor-Agent deployments that also use the external secret store. The `azure.workload.identity/client-id` annotation on the ServiceAccount can only hold one value at a time, and the Operator may overwrite it. This feature is supported only in Standalone mode. +* An environment can use either a `SecretProviderClass` or a regular Kubernetes Secret for its configuration, never both at the same time. + +## Configuring the Integration + +To enable the integration, you must first enable secret store support for the namespace where you want to create an environment with Azure Key Vault integration. To do this, perform the following steps: + +1. Log in to Private Mendix Platform as a user with [company administrator](/private-mendix-platform/reference-guide/admin/company/) access rights. +2. Switch to Admin Mode by clicking the profile picture in the top right corner of the screen and selecting **Switch to Admin Mode**. +3. In the left navigation menu, open the **Manage** section and go to **Deployment > Cluster Manager**. +4. Select or create a cluster with the **Cluster Type** set to **AKS**. +5. Edit or create the target namespace and toggle **Enable Secret Store**. +6. Select **Automatic (Azure Key Vault Only)** as the mode. + + In automatic mode, if the environment uses an `azure-wi` storage or database plan, Mendix Operator creates the UAMI, including federated credentials, Secret Provider Class and ServiceAccount annotations during environment provisioning. Automatic mode is required for Azure Key Vault integration. Manual mode can be used to configure secret stores other than Azure Key Vault. + +{{< figure src="/attachments/private-platform/pmp-keyvault2.png" class="no-border" >}} + +After enabling the secret store support for the namespace, proceed by creating the environment and configuring the necessary settings. The specific steps are slightly different depending on whether your environment will use an Azure Workload Identity (`azure-wi`) storage or database plan. Private Mendix Platform automatically determines the correct path. When you enable the secret store for an environment, Private Mendix Platform checks whether a Kubernetes ServiceAccount already exists for that environment: + +* If the ServiceAccount exists, Private Mendix Platform reads the UAMI Client ID directly from the ServiceAccount's existing annotation. No customer input is required. +* If the ServiceAccount does not exist, Private Mendix Platform shows a message like the following: + + *Private Mendix Platform has detected that an auto-provisioned managed identity does not exist for this environment. In order to use the automatic secret store, request your infrastructure team to provision an Azure Managed Identity for this environment and provide the Client ID.* + + The customer's infrastructure team must then create the UAMI in Azure and supply its Client ID in this prompt. Private Mendix Platform stores it and creates the Kubernetes ServiceAccount with the correct [name and annotations](#naming-conventions). The customer does not need to create or annotate the service account manually. + +For more details, refer to the sections below. ### Approach A - With Azure Workload Identity Storage -For environments which use an `azure-wi` storage or database plan, the managed identity (UAMI) is automatically created by the Mendix Operator during environment provisioning. The Kubernetes ServiceAccount is created by the Operator and annotated with the UAMI Client ID. Because of that, the customer must only configure Key Vault and RBAC. +For environments which will use an `azure-wi` storage or database plan, the managed identity (UAMI) is automatically created by the Mendix Operator during environment provisioning. The Kubernetes ServiceAccount is created by the Operator and annotated with the UAMI Client ID. Because of that, the customer must only configure Key Vault and RBAC. + +The configuration requires the following steps: + +1. The customer (portal user) or Mendix Operator [creates the environment](/private-mendix-platform/user-guide/#deploy) in the Private Mendix Platform wizard, selecting the following options: -### Approach B - Without Azure Workload Identity Storage + * **Cluster** and **Namespace** - Select the cluster and namespace where you previously enabled secret store support. + * **Database Plan** and **Storage Plan** - Select an azure-wi database and/or storage plan. + * **Secrets** - Select the checkbox **Use external secret store for environment configurations**. -For environments which do not use `azure-wi` plans, the managed identity (UAMI) must be manually created by the customer before enabling Key Vault. Since the + {{< figure src="/attachments/private-platform/pmp-keyvault1.png" class="no-border" >}} + + The Mendix Operator automatically creates the UAMI, its federated credential, and annotates the environment's ServiceAccount with the Client ID. + +2. The customer's Infrastructure Team creates an empty Key Vault named `mendix-`, with Azure RBAC selected as the permission model. For more information, see [Naming Conventions and Kubernetes Object Reference](#naming-conventions). +3. The customer's Infrastructure Team grants the Key Vault Secrets Officer role on that Key Vault to the [Private Mendix Platform managed identity](#identity-pmp). +4. The customer's Infrastructure Team grants the Key Vault Secrets User role on that Key Vault to the environment's [UAMI](#identity-uami). + +No manual Kubernetes object creation or Client ID entry is needed for this path. + +### Approach B - Without Azure Workload Identity Storage {#environment-b} + +For environments which will not use `azure-wi` plans, the managed identity (UAMI) must be manually created by the customer before enabling Key Vault. Since the Kubernetes ServiceAccount does not exist, Private Mendix Platform detects that it is missing and asks for the Client ID, and then created the ServiceAccount. This approach requires the customer to configure the UAMI, in addition to Key Vault and RBAC. -### Process +The configuration requires the following steps: -Private Mendix Platform automatically determines the correct path. When you enable the secret store for an environment, Private Mendix Platform checks whether a Kubernetes ServiceAccount already exists for that environment. +1. The customer (portal user) [creates the environment](/private-mendix-platform/user-guide/#deploy) in the Private Mendix Platform wizard, selecting the following options: -If the ServiceAccount exists, Private Mendix Platform reads the UAMI Client ID directly from the ServiceAccount's existing annotation. No customer input is required. + * **Cluster** and **Namespace** - Select the cluster and namespace where you previously enabled secret store support. + * **Database Plan** and **Storage Plan** - Select a database and/or storage plan without `azure-wi`. + * **Secrets** - Select the checkbox **Use external secret store for environment configurations**. -If the ServiceAccount does not exist, Private Mendix PlatformP shows a message like the following: + {{< figure src="/attachments/private-platform/pmp-keyvault1.png" class="no-border" >}} + +2. The customer's Infrastructure Team creates a User-Assigned Managed Identity for this environment in Azure. The recommended naming for the UAMI is `mendix-`. This matches the convention the Operator itself uses in Approach A, for consistency. +3. The customer's Infrastructure Team adds a federated credential to the UAMI, where the issuer is the cluster's OIDC issuer URL, and the subject is `system:serviceaccount::`. +4. Create an empty Key Vault named `mendix-`, with Azure RBAC selected as the permission model. +5. The customer's Infrastructure Team grants the Key Vault Secrets Officer role on that Key Vault to the [Private Mendix Platform managed identity](#identity-pmp). +6. The customer's Infrastructure Team grants the Key Vault Secrets User role on that Key Vault to the environment's [UAMI](#identity-uami). +7. The customer (portal user) enters the UAMI's Client ID created in step 2 above. Private Mendix Platform creates the Kubernetes ServiceAccount for the environment with both required annotations, and stores the Client ID for use when secrets are written. + +You do not need to manually run any `kubectl create serviceaccount` or `kubectl annotate` commands. Private Mendix Platform performs this step after the user has entered the Client ID. -*Private Mendix Platform has detected that an auto-provisioned managed identity does not exist for this environment. In order to use the automatic secret store, request your infrastructure team to provision an Azure Managed Identity for this environment and provide the Client ID.* +## Reference -The customer's infrastructure team must create the UAMI in Azure and supply its Client ID in this prompt. Private Mendix Platform stores it and creates the Kubernetes ServiceAccount with the correct [name and annotations](#naming-conventions). The customer does not need to create or annotate the service account manually. +Refer to the following sections for more information about the required permissions, naming conventions, and other concerns. -## Permissions Reference for Managed Identities and Azure RBAC +### Permissions Reference for Managed Identities and Azure RBAC All of the following identities must be correctly configured, each with a different Azure role, a different scope, and a different party responsible for granting it. Incorrectly configuring any of the identities will cause errors with the integration. -### Private Mendix Platform Managed Identity {#identity-pmp} +#### Private Mendix Platform Managed Identity {#identity-pmp} The `PMP-KeyVault-Identity` is the Workload Identity assigned to the Private Mendix Platform pod (not to any customer environment). Private Mendix Platform uses it through `DefaultAzureCredentialBuilder` for every write, update, delete, and recover operation against a customer's Key Vault. @@ -76,7 +159,7 @@ The `PMP-KeyVault-Identity` is the Workload Identity assigned to the Private Men | **When** | Once per environment, before the customer toggles *Enable Secret Store* | | **Applies to** | Approach A and B | -### Environment-Level UAMI {#identity-uami} +#### Environment-Level UAMI {#identity-uami} This is the managed identity tied to a specific environment. It is created automatically by the Operator in Approach A, or manually by the customer in Approach B. It is used exclusively at runtime by the CSI driver running inside the app pod. Private Mendix Platform code never uses this identity. @@ -89,7 +172,7 @@ This is the managed identity tied to a specific environment. It is created autom | **When** | After the Operator or the customer creates the UAMI, and before the customer toggles **Enable Secret Store**. | | **Applies to** | Approach A and B; only the creator of the identity differs | -### Mendix Operator Provisioning Identity +#### Mendix Operator Provisioning Identity This identity not a Key Vault access identity. It allows the Mendix Operator to automatically create the UAMI and connect it to an `azure-wi` storage or database plan. It is required for Approach A to work. @@ -101,99 +184,7 @@ This identity not a Key Vault access identity. It allows the Mendix Operator to | **When** | Once per cluste. This is a cluster bootstrap step, done before any environment can use an `azure-wi` storage or database plan. It is not granted per environment, and it is not part of enabling the Key Vault secret store feature specifically. However, it is required for Approach A to work. | | **Applies to** | Approach A only. Approach B environments do not use `azure-wi` plans, so this identity is never involved. | -### Additional Concerns {#additional-concerns} - -#### Key Vault Access Model - -The Key Vault's access configuration must use the Azure RBAC permission model, not the legacy Vault access policies model. Role assignments like Secrets Officer or Secrets User only take effect under the RBAC model. - -#### Tenant ID Source - -The directory tenant ID used in the `SecretProviderClass` is found on the Key Vault's Overview page in the Azure Portal - it is the Azure AD tenant the Key Vault belongs to, not anything environment-specific. - -#### Soft-Delete and Recovery - -Azure Key Vault has soft-delete enabled by default and it cannot be disabled. Private Mendix Platform relies on this - when a secret name that was previously deleted is written again, Private Mendix Platform automatically recovers the soft-deleted secret before writing the new value, rather than failing. If a secret is ever purged (permanently deleted) directly in Azure, this automatic recovery is no longer possible and requires manual intervention. - -#### Resource Providers - -`Microsoft.KeyVault` and `Microsoft.ManagedIdentity` must be registered on the subscription. - -## Prerequisites {#prerequisites} - -The customer is responsible for configuring the following prerequisites. They must be in place once per cluster, before any environment can use this feature. - -### Secrets Store CSI Driver and Azure Key Vault Provider - -The Secrets Store CSI Driver and Azure Key Vault provider must be installed cluster-wide (typically in `kube-system`), by using the following Helm commands: - -```text -helm repo add csi-secrets-store-provider-azure https://azure.github.io/secrets-store-csi-driver-provider-azure/charts -helm install csi-secrets-store-provider-azure csi-secrets-store-provider-azure/csi-secrets-store-provider-azure -n kube-system -``` - -This chart bundles the generic `secrets-store-csi-driver` as a sub-chart dependenc. Installing it also installs the underlying driver. - -### Azure Workload Identity Webhook - -The Azure Workload Identity webhook must be enabled and active on the cluster. - -### OIDC Issuer Enabled - -OIDC issuer must be enabled on the AKS cluster. This is required for federated credentials on managed identities. - -### Kubernetes RBAC - -Private Mendix Platform authenticates to the Kubernetes API using a bearer token (a dedicated ServiceAccount, for example, `pmp-event-all`), distinct from the app-environment identities. This token needs a namespaced Role and RoleBinding in every namespace where app environments using this feature are deployed, granting the following: - -| Resource | Verbs | Used for | -| --- | --- | --- | -| `secretproviderclasses` | `get, create, patch, delete` | Creating, updating, or removing the environment's SecretProviderClass | -| `serviceaccounts` | `get, create, patch` | Reading the UAMI annotation (for Approach A) or creating and annotating the ServiceAccount (Approach B) | - -{{% alert color="warning" %}} -Without Kubernetes RBAC for the Private Mendix Platform cluster credentials, calls from Private Mendix Platform fail with a *403 Forbidden* error and a message like the following `system:serviceaccount:kube-system:pmp-event-all cannot ... in the namespace`. - -Verify the configuration by using the following command: - -```text -kubectl auth can-i create secretproviderclasses --as=system:serviceaccount:kube-system:pmp-event-all -n -``` - -{{% /alert %}} - -### Namespace-Level Flag - -The namespace must have external secret store support enabled through Automatic AKS" mode before any environment in it can toggle the per-environment flag below. - -## Per-Environment Setup - Approach A - -For environments which use an `azure-wi` storage or database plan, the configuration requires the following steps: - -1. The customer (portal user) or Mendix Operator creates the environment in the Private Mendix Platform wizard, selecting an azure-wi storage and/or database plan. The Mendix Operator automatically creates the UAMI, its federated credential, and annotates the environment's ServiceAccount with the Client ID. -2. The customer's Infrastructure Team creates an empty Key Vault named `mendix-`, with Azure RBAC selected as the permission model. For more information, see [Naming Conventions and Kubernetes Object Reference](#naming-conventions). -3. The customer's Infrastructure Team grants the Key Vault Secrets Officer role on that Key Vault to the [Private Mendix Platform managed identity](#identity-pmp). -4. The customer's Infrastructure Team grants the Key Vault Secrets User role on that Key Vault to the environment's [UAMI](#identity-uami). -5. The customer (portal user) toggles **Enable Secret Store** for the environment in the Private Mendix Platform portal. Private Mendix Platform reads the UAMI Client ID from the existing ServiceAccount annotation automatically. - -No manual Kubernetes object creation or Client ID entry is needed for this path. - -## Per-Environment Setup - Approach B {#environment-b} - -For environments which do not use any `azure-wi` plans, the configuration requires the following steps: - -1. The customer (portal user) creates the environment without an azure-wi storage or database plan. -2. The customer's Infrastructure Team creates a User-Assigned Managed Identity for this environment in Azure. The recommended naming for the UAMI is `mendix-`. This matches the convention the Operator itself uses in Approach A, for consistency. -3. The customer's Infrastructure Team adds a federated credential to the UAMI, where the issuer is the cluster's OIDC issuer URL, and the subject is `system:serviceaccount::`. -4. Create an empty Key Vault named `mendix-`, with Azure RBAC selected as the permission model. -5. The customer's Infrastructure Team grants the Key Vault Secrets Officer role on that Key Vault to the [Private Mendix Platform managed identity](#identity-pmp). -6. The customer's Infrastructure Team grants the Key Vault Secrets User role on that Key Vault to the environment's [UAMI](#identity-uami). -7. The customer (portal user) toggle **Enable Secret Store** for the environment in the Private Mendix Platform portal. Private Mendix Platform detects that no ServiceAccount exists yet and prompts the user to provide a Client ID. -8. The customer (portal user) enters the UAMI's Client ID created in step 2 above. Private Mendix Platform creates the Kubernetes ServiceAccount for the environment with both required annotations, and stores the Client ID for use when secrets are written. - -You do not need to manually run any `kubectl create serviceaccount` or `kubectl annotate` commands. Private Mendix Platform performs this step after the user has entered the Client ID. - -## Naming Conventions and Kubernetes Object Reference {#naming-conventions} +### Naming Conventions and Kubernetes Object Reference {#naming-conventions} | Object | Name | Notes | | --- | --- | --- | @@ -204,7 +195,7 @@ You do not need to manually run any `kubectl create serviceaccount` or `kubectl | SecretProviderClass annotation | `privatecloud.mendix.com/environment-class: "true"` | Identifies this SPC as a Private Mendix Platform-managed environment secret class. | | Managed Identity (Approach A) | `mendix-` | Auto-generated by the Mendix Operator. | -### Secret Key Naming +#### Secret Key Naming Secret key naming inside the Key Vault or SecretProviderClass follows the general. Mendix conventions. For more information, see [Retrieve Environment-Sensitive Data from a Secret Store](/developerportal/deploy/secret-store-credentials/). @@ -215,11 +206,27 @@ Secret key naming inside the Key Vault or SecretProviderClass follows the genera | MxAdmin Password | `mx-admin-password` (fixed literal) | `mx-admin-password` | | Debugger Password | `mx-debugger-password` (fixed literal) | `mx-debugger-password` | -#### Disallowed Characters for Azure Key Vault Secret Names +##### Disallowed Characters for Azure Key Vault Secret Names + +Azure Key Vault secret names cannot contain underscores (`_`), periods (`.`), spaces, and special symbols. Private Mendix Platform converts any periods in a constant or runtime setting name to hyphens for the Key Vault entry (`objectName`). The `objectAlias` - that is, the file name actually mounted into the pod and read by the Mendix Runtime - always keeps the periods, exactly matching the format in the official documentation, for example, `mx-const-MyFirstModule.WelcomePageTitle`. Private Mendix Platform performs this conversion consistently both when it writes the value to Key Vault and when it builds the SecretProviderClass entry, so the two always stay in sync. However, other special characters are not automatically converted, so constant names that contain them cannot be stored in Azure Key Vault. Because of that, we recommend that you do not create secret names with underscores (`_`), periods (`.`), spaces, and special symbols. + +### Additional Concerns {#additional-concerns} + +#### Key Vault Access Model + +The Key Vault's access configuration must use the Azure RBAC permission model, not the legacy Vault access policies model. Role assignments like Secrets Officer or Secrets User only take effect under the RBAC model. -Azure Key Vault secret names cannot contain underscores (`_`), periods (`.`), spaces, and special symbols. Private Mendix Platform converts any periods in a constant or runtime setting name to hyphens for the Key Vault entry (`objectName`). The `objectAlias` - that is, the file name actually mounted into the pod and read by the Mendix Runtime - always keeps the periods, exactly matching the format in the official documentation, for example, `mx-const-MyFirstModule.WelcomePageTitle`. Private Mendix Platform performs this conversion consistently both when it writes the value to Key Vault and when it builds the SecretProviderClass entry, so the two always stay in sync. +#### Tenant ID Source -However, other special characters are not automatically converted, so constant names that contain them cannot be stored in Azure Key Vault. +The directory tenant ID used in the `SecretProviderClass` is found on the Key Vault's Overview page in the Azure Portal - it is the Azure AD tenant the Key Vault belongs to, not anything environment-specific. + +#### Soft-Delete and Recovery + +Azure Key Vault has soft-delete enabled by default and it cannot be disabled. Private Mendix Platform relies on this - when a secret name that was previously deleted is written again, Private Mendix Platform automatically recovers the soft-deleted secret before writing the new value, rather than failing. If a secret is ever purged (permanently deleted) directly in Azure, this automatic recovery is no longer possible and requires manual intervention. + +#### Resource Providers + +`Microsoft.KeyVault` and `Microsoft.ManagedIdentity` must be registered on the subscription. ## Using Key Vault Integration @@ -351,9 +358,3 @@ Recreate or reconfigure the Key Vault to use the [Azure RBAC permission model](# {{% alert color="warning" %}} Once an environment's constants, runtime settings, or passwords are managed through Key Vault, all further changes to them must go through the Private Mendix Platform portal. Editing these secrets directly in the Azure Portal or through the Azure CLI will cause the `SecretProviderClass` (Private Mendix Platform's record of what should exist) to go out of sync with Key Vault, with no automatic reconciliation. {{% /alert %}} - -## Known Limitations - -* Database and blob storage plans cannot be provisioned through this automatic flow. Configure them manually according to the general Mendix secret store documentation if needed. -* An environment can use either a `SecretProviderClass` or a regular Kubernetes Secret for its configuration, never both at the same time. -* Azure-wi storage and database plans are not compatible with Interactor-Agent deployments that also use the external secret store. The `azure.workload.identity/client-id` annotation on the ServiceAccount can only hold one value at a time, and the Operator may overwrite it. This feature is supported only in Standalone mode. diff --git a/static/attachments/private-platform/pmp-keyvault1.png b/static/attachments/private-platform/pmp-keyvault1.png new file mode 100644 index 00000000000..3a6afcc5590 Binary files /dev/null and b/static/attachments/private-platform/pmp-keyvault1.png differ diff --git a/static/attachments/private-platform/pmp-keyvault2.png b/static/attachments/private-platform/pmp-keyvault2.png new file mode 100644 index 00000000000..fbf4a60501a Binary files /dev/null and b/static/attachments/private-platform/pmp-keyvault2.png differ