diff --git a/docs/infrastructure/01-Introduction.md b/docs/infrastructure/01-Introduction.md index b456cc8..38a9764 100644 --- a/docs/infrastructure/01-Introduction.md +++ b/docs/infrastructure/01-Introduction.md @@ -23,8 +23,13 @@ Why not a simple VM? AKS gives us the flexibility of a 10s pod deployment, reliability (with ArgoCD ensuring all pods are up and running all the time) and allows us to be more precise and clean in our infrastructure, with better monitoring and allocated resources for each application depending on the importance. ## About this documentation +:::important +Most of the docs explain procedures with the Azure CLI and the Kubernetes CLI. +Make sure to [install and configure them](./Tutorials/setup#azure-cli--kubectl) to follow along. +Alternatively, you can see the [official Azure documentation](https://learn.microsoft.com/en-us/azure/?product=popular) for how to use the Azure portal. +::: -The best place to start is going through the [Tutorials](./Tutorials/setup) section to get a high level overview of how things are organized. -If you're looking for a guide on how to achieve a specific outcome, you can look in the [How To Guides](./How%20To%20Guides/Adding%20a%20Secret). - \ No newline at end of file +The best place to start is going through the [Tutorials](./Tutorials/setup) section to get a high level overview of how things are organized. +If you're looking for a guide on how to achieve a specific outcome, you can look in the [Guides](./Guides/Add%20a%20Secret). + diff --git a/docs/infrastructure/03-Guides/Add a Secret.md b/docs/infrastructure/03-Guides/Add a Secret.md new file mode 100644 index 0000000..42f7145 --- /dev/null +++ b/docs/infrastructure/03-Guides/Add a Secret.md @@ -0,0 +1,470 @@ +# Add a Secret + +## Introduction + +Secrets are used to configure deployments with **sensitive** data like database login, API keys, and so on. +We have deployed a "Azure Key vault" (the "KV") named "kv-polinetwork" that stores all the secrets. +Next, we can pass a secret from the KV to the k8s pod using a `SecretProviderClass`. + +The following sections explain how to add a new secret. + +## Basics + + +There are three required steps: + +1. Add the secret into the "KV" +2. Create and configure a `SecretProviderClass` +3. Mount the secret volume inside the pod + +:::tip +Whenever there is `` in the code or in a command, that's a paramter you need to set. +::: + +### Add the secret into the "KV" + +See [this section](#add-or-update-a-secret). + +### Create the `SecretProviderClass` + +References: +- [Microsoft Guide](https://learn.microsoft.com/en-us/azure/aks/csi-secrets-store-identity-access?tabs=azure-portal&pivots=access-with-a-user-assigned-managed-identity) +- [Secrets Store CSI Driver](https://secrets-store-csi-driver.sigs.k8s.io) + +:::important +Check if the namespace already contains a `SecretProviderClass` before creating a new one. +Running the following command: + +```sh +kubectl get secretproviderclass --namespace +``` + +you should see a message like `No resources found in namespace.` + +Otherwise the `SecretProviderClass` already exists and you can directly +[add a new secret](#add-kv-secrets-into-the-secretproviderclass). + +::: + +This is a basic `SecretProviderClass` manifest: +```yaml title="spc.yaml" +apiVersion: secrets-store.csi.x-k8s.io/v1 +kind: SecretProviderClass +metadata: + name: # a recommended name convention is "-spc" + namespace: # if not specified, the default k8s namespace is "default" +spec: + provider: azure + parameters: + usePodIdentity: 'false' + useVMManagedIdentity: 'true' # Set to true for using managed identity + userAssignedIdentityID: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' # Set the clientID of the managed identity to use + tenantId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' # The tenant ID of the key vault + keyvaultName: 'kv-polinetwork' # Set to the name of your key vault +``` + +A few things to note: +- `usePodIdentity` should be set to `false` and `useVMManagedIdentity` should be set to `true` since we are using a VM managed identity. +- `userAssignedIdentityID` should be set to the client ID of the managed identity we linked to the key vault. +- `tenantId` should be set to the tenant ID of our Azure tenant. + +:::tip +If you are unsure about what the `userAssignedIdentityID` and `tenantId` are, you probably aren't one of the heads of IT in PoliNetwork. +Changes to such parts of the manifest should only be done by someone with the right permissions. + +Anyway here's how to get them: + +- `tenantId` + +```sh +az account show --query tenantId --output tsv +``` + +- `userAssignedIdentityID` + +```sh +az aks show --resource-group --name --query addonProfiles.azureKeyvaultSecretsProvider.identity.clientId -o tsv +``` +`` and `` can be found both in our [Terraform](https://github.com/PoliNetworkOrg/terraform/) or in the Azure Portal. + +::: + +### Add "KV" secrets into the `SecretProviderClass` + +Inside the `SecretProviderClass` manifest, add "KV" secrets: +```yaml title="spc.yaml" +apiVersion: secrets-store.csi.x-k8s.io/v1 +kind: SecretProviderClass +metadata: + name: + namespace: +spec: + provider: azure + parameters: + usePodIdentity: 'false' + useVMManagedIdentity: 'true' + userAssignedIdentityID: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + tenantId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + keyvaultName: 'kv-polinetwork' + # add-highlight-start + objects: | + array: + - | + objectName: + objectType: secret + - | + objectName: + objectType: secret + # add-highlight-end +``` + +In this example, we map two secrets (`` and ``) +from the key vault `kv-polinetwork` to a volume you can mount in your pod. + +### Mount the secret volume inside the pod + +The secret must be mounted as a volume. +Here is an example pod manifest: + +```yaml title="my-spc-example-pod.yaml" +kind: Pod +apiVersion: v1 +metadata: + name: my-spc-example-pod + namespace: +spec: + containers: + - name: busybox + image: registry.k8s.io/e2e-test-images/busybox:1.29-4 + command: + - '/bin/sleep' + - '10000' + # add-highlight-start + volumeMounts: + - name: secrets-store # Name of the volume defined below + mountPath: '/mnt/secrets-store' # Path where the secrets are mounted inside the pod + readOnly: true # A secret should be mounted as read-only + # add-highlight-end + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 250m + memory: 256Mi + +# add-highlight-start + volumes: + - name: secrets-store # Name of the volume - it can be whatever you want + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: '-spc' # The name of the SecretProviderClass +#add-highlight-end +``` + +After applying this manifest to the cluster, you can retrieve the secret by reading the files in the `/mnt/secrets-store/` directory (`mountPath` parameter). +To test if everything works, try running the following command. + +```sh +kubectl exec -it my-spc-example-pod -- cat /mnt/secrets-store/ +``` + +:::tip +More than one secret can be mounted at the same time by adding more entries in the array, +all of them can be found in the same directory, you can see them by running: + +```sh +kubectl exec -it my-spc-example-pod -- ls /mnt/secrets-store +``` + +In the [previous section](#add-kv-secrets-into-the-secretproviderclass), we have added `` and ``. + +::: + +## Loading the secret inside an Environment Variable + +:::important +Even if you want to load the secret as an ENV variable, it's **REQUIRED** to follow every steps in the previous section, including +[mounting the secret volume](#mount-the-secret-volume-inside-the-pod). + +You can find more about why this is the case [here](https://github.com/kubernetes-sigs/secrets-store-csi-driver/issues/813) +::: + +To load the secret as an Environment Variable follows the following steps. + +### Register the secret as a k8s secret +Inside the `ServiceProviderClass` manifest, add a new field `secretObjects` to create a +new k8s secret collection: + +```yaml title="spc.yaml" +apiVersion: secrets-store.csi.x-k8s.io/v1 +kind: SecretProviderClass +metadata: + name: + namespace: +spec: + provider: azure + parameters: + usePodIdentity: 'false' + useVMManagedIdentity: 'true' + userAssignedIdentityID: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + tenantId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + keyvaultName: 'kv-polinetwork' + # add-highlight-start + secretObjects: + # each of these is a COLLECTION of secrets. + # multiple separate collections can be defined, but to identify Azure secrets from other k8s secrets, + # we use a collection that contains multiple secrets (as if it were an Object in fact). + # important: it only needs to be created the first time, then just add a key underneath + - secretName: azure-kv + type: Opaque + data: + # the secret that we want to expose also as k8s secret should be added here. + # important to distinguish objectName (reference to "KV") from key (custom name) + - objectName: # secret name inside the "KV" + key: example-secret # custom k8s secret's key + # add-highlight-end + objects: | + array: + - | + objectName: + objectType: secret +``` + + +### Use the secret as environment variable +After [mounting the secret volume](#mount-the-secret-volume-inside-the-pod) and [configuring the k8s secret](#register-the-secret-as-a-k8s-secret), +you can add an environment variable with the secret as value reference: + +```yaml title="my-spc-example-pod.yaml" +kind: Pod +apiVersion: v1 +metadata: + name: my-spc-example-pod + namespace: +spec: + containers: + - name: busybox + image: registry.k8s.io/e2e-test-images/busybox:1.29-4 + command: + - '/bin/sleep' + - '10000' + volumeMounts: + - name: secrets-store + mountPath: '/mnt/secrets-store' + readOnly: true + # add-highlight-start + env: + - name: EXAMPLE_SECRET # env variable name (independent from the name of the secret) + valueFrom: + secretKeyRef: + name: azure-kv # k8s secret collection name + key: example-secret # secret key inside of k8s secret collection specified in the line above + # add-highlight-end + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 250m + memory: 256Mi + + volumes: + - name: secrets-store + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: '-spc' +``` + +## "KV" management +### Add or update a secret +You can add (or update) a secret with the following command: +```sh +az keyvault secret set --vault-name "kv-polinetwork" --name "" --value "" +``` + +:::tip +If the secret value is too long or you don't want to copy/paste in terminal you can use a file instead. +1. Paste the secret value into a text file (no extension or ".txt" is valid) on a **single line** +2. Run the following command + ```sh + az keyvault secret set --vault-name "kv-polinetwork" --name "" --file "" + ``` +::: + +### Delete a secret +You can delete a secret with the following command: +```sh +az keyvault secret delete --vault-name "kv-polinetwork" --name "" +``` + +After the deletion, the secret remains in a `Recoverable State` for 7 days. +During this period the secret can be recovered with the following command: +```sh +az keyvault secret recover --vault-name "kv-polinetwork" --name "" +``` + +To **permanently delete** that secret, after 7 days from deletion you can run the following command: +```sh +az keyvault secret purge --vault-name "kv-polinetwork" --name "" +``` + +:::note +To **update/re-set** that secret, during the 7 days retention period, you must recover it first, +then run the command to update it + +If you try to `set` a secret that has been deleted within the last 7 days without restoring it first, you get the following error: + +```sh +#error-highlight-start +(Conflict) Secret is currently in a deleted but recoverable state, and its name cannot be reused; in this state, the secret can only be recovered or purged. +Code: Conflict +Message: Secret is currently in a deleted but recoverable state, and its name cannot be reused; in this state, the secret can only be recovered or purged. +Inner error: { + "code": "ObjectIsDeletedButRecoverable" +} +#error-highlight-end +``` +::: + + +## Examples +In this section you can find working examples. +It is recommended to read the ["Basics" section](#basics) to understand how it works. + +:::note +You still need to set `tenantId`, `userAssignedIdentityID` and secret keys to make it work. +::: + +### Minimal +```yaml title="test-secret/spc.yaml" +apiVersion: secrets-store.csi.x-k8s.io/v1 +kind: SecretProviderClass +metadata: + name: test-secret-spc + namespace: test-secret +spec: + provider: azure + parameters: + usePodIdentity: 'false' + useVMManagedIdentity: 'true' + userAssignedIdentityID: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + tenantId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + keyvaultName: 'kv-polinetwork' + objects: | + array: + - | + objectName: + objectType: secret + - | + objectName: + objectType: secret +``` + +```yaml title="test-secret/example-pod.yaml" +kind: Pod +apiVersion: v1 +metadata: + name: example-pod + namespace: test-secret +spec: + containers: + - name: busybox + image: registry.k8s.io/e2e-test-images/busybox:1.29-4 + command: + - '/bin/sleep' + - '10000' + volumeMounts: + - name: secrets-store + mountPath: '/mnt/secrets-store' + readOnly: true + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 250m + memory: 256Mi + + volumes: + - name: secrets-store + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: 'test-secret-spc' +``` + +### Environment Variable + +```yaml title="test-secret-env/spc.yaml" +apiVersion: secrets-store.csi.x-k8s.io/v1 +kind: SecretProviderClass +metadata: + name: test-secret-env-spc + namespace: test-secret-env +spec: + provider: azure + parameters: + usePodIdentity: 'false' + useVMManagedIdentity: 'true' + userAssignedIdentityID: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + tenantId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + keyvaultName: 'kv-polinetwork' + secretObjects: + - secretName: azure-kv + type: Opaque + data: + - objectName: + key: example-secret + objects: | + array: + - | + objectName: + objectType: secret +``` + +```yaml title="test-secret-env/example-pod.yaml" +kind: Pod +apiVersion: v1 +metadata: + name: example-pod + namespace: test-secret-env +spec: + containers: + - name: busybox + image: registry.k8s.io/e2e-test-images/busybox:1.29-4 + command: + - '/bin/sleep' + - '10000' + volumeMounts: + - name: secrets-store + mountPath: '/mnt/secrets-store' + readOnly: true + env: + - name: EXAMPLE_SECRET + valueFrom: + secretKeyRef: + name: azure-kv + key: example-secret + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 250m + memory: 256Mi + + volumes: + - name: secrets-store + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: 'test-secret-env-spc' +``` + diff --git a/docs/infrastructure/03-How To Guides/Adding a Secret.md b/docs/infrastructure/03-How To Guides/Adding a Secret.md deleted file mode 100644 index c3b987b..0000000 --- a/docs/infrastructure/03-How To Guides/Adding a Secret.md +++ /dev/null @@ -1,151 +0,0 @@ -# Adding a Secret - -Secrets are stored inside a Azure Key Vault. To add a new secret, this page will -explain how to reference the secret in k8s deployments. - -## Basics - -A secret can be added to your deployment by using a `SecretProviderClass` object. -This object will automatically reference the secret stored in the Azure Key Vault. - -```yaml -apiVersion: secrets-store.csi.x-k8s.io/v1 -kind: SecretProviderClass -metadata: - name: sc-demo-keyvault-csi -spec: - provider: azure - parameters: - usePodIdentity: "false" - useVMManagedIdentity: "true" # Set to true for using managed identity - userAssignedIdentityID: "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # Set the clientID of the managed identity to use - tenantId: "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # The tenant ID of the key vault - keyvaultName: "kv-polinetwork" # Set to the name of your key vault - objects: | - array: - - | - objectName: ExampleSecret # keyvault secret name - objectType: secret -``` - -A detailed guide can be found in the [official documentation](https://learn.microsoft.com/en-us/azure/aks/csi-secrets-store-identity-access?tabs=azure-portal&pivots=access-with-a-user-assigned-managed-identity). - -This maps the secret `ExampleSecret` from the key vault `kv-polinetwork` to a volume you can mount in your pod. -A few things to note - -- `usePodIdentity` should be set to `false` and `useVMManagedIdentity` should be set to `true` since we are using a VM managed identity. -- `userAssignedIdentityID` should be set to the client ID of the managed identity we linked to the key vault. -- `tenantId` should be set to the tenant ID of our Azure tenant. - -:::tip -If you are unsure about what the clinet ID and tenant ID are, you probably aren't one of the heads of IT in PoliNetwork. -Changes to such parts of the manifest should only be done by someone with the right permissions. - -Anyway here's how to get them: - -- Tenant ID: - -```sh -az account show --query tenantId --output tsv -``` - -- Client ID: - -```sh -az aks show --resource-group --name --query addonProfiles.azureKeyvaultSecretsProvider.identity.clientId -o tsv -``` - -::: - -## Different ways to mount your secret - -### Volume - -In the above example, the secret can be mounted as a volume and then read from disk in your pod like in the example below: - -```yaml -kind: Pod -apiVersion: v1 -metadata: - name: sc-demo-keyvault-csi -spec: - containers: - - name: busybox - image: registry.k8s.io/e2e-test-images/busybox:1.29-4 - command: - - "/bin/sleep" - - "10000" - volumeMounts: - - name: secrets-store01-inline # Name of the volume - mountPath: "/mnt/secrets-store" # Path where the secrets are mounted inside the pod - readOnly: true # A secret should be mounted as read-only - resources: - requests: - cpu: 100m - memory: 128Mi - limits: - cpu: 250m - memory: 256Mi - volumes: - - name: secrets-store01-inline # Name of the volume - csi: - driver: secrets-store.csi.k8s.io - readOnly: true - volumeAttributes: - secretProviderClass: "sc-demo-keyvault-csi" # The name of the SecretProviderClass -``` - -Assuming this is the pod manifest you applied to the cluster, you can retrieve the secret by reading the file `/mnt/secrets-store/ExampleSecret`. - -```sh -kubectl exec -it sc-demo-keyvault-csi -- cat /mnt/secrets-store/ExampleSecret -``` - -:::tip -More than one secret can be mounted at the same time by adding more entries in the array, all of them can be found in the same directory, you can see them by running: - -```sh -kubectl exec -it sc-demo-keyvault-csi -- ls /mnt/secrets-store -``` - -::: - -### Environment Variables - -You can also mount the secret as an environment variable in your pod by first -syncing the secret with the k8s secrets like in the example below: - -```yaml -... -spec: - provider: azure - ... - secretObjects: - # ognuno di questi e' una COLLEZIONE di secrets - # si possono definire piu' collezioni separate, ma per identificare i secret Azure dagli altri k8s secrets, - # utilizziamo una collezione che contiene più secrets (come se fosse un Object appunto) - # importante: va creato solo la prima volta, poi basta aggiungere una key sotto - - secretName: azure-kv - type: Opaque - data: - # qui va aggiunto il secret che vogliamo esporre anche come k8s secret - # importante distinguere objectName (reference al KV) dalla key (nome personalizzato) - - objectName: ExampleSecret # nome del secret dentro il KV di azure - key: example-secret # key personalizzata del k8s secret -``` - -Like before, you can then reference the secret in your pod manifest: - -```yaml -... -spec: - containers: - env: - # aggiungiamo l'env variable - - name: EXAMPLE_SECRET # nome dell'env variable (indipendente dal nome del secret) - valueFrom: - secretKeyRef: - name: azure-kv # nome della collezione di k8s secret - key: example-secret # key del secret specifico da utilizzare dentro la collezione -... -``` diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 91cc811..b31b191 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -143,6 +143,28 @@ const config: Config = { prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula, + magicComments: [ + { + className: 'theme-code-block-highlighted-line', + line: 'highlight-next-line', + block: {start: 'highlight-start', end: 'highlight-end'}, + }, + { + className: 'code-block-add-line', + line: 'add-highlight-next-line', + block: {start: 'add-highlight-start', end: 'add-highlight-end'}, + }, + { + className: 'code-block-remove-line', + line: 'remove-highlight-next-line', + block: {start: 'remove-highlight-start', end: 'remove-highlight-end'}, + }, + { + className: 'code-block-error-line', + line: 'error-highlight-next-line', + block: {start: 'error-highlight-start', end: 'error-highlight-end'}, + }, + ] }, } satisfies Preset.ThemeConfig, } diff --git a/src/css/custom.css b/src/css/custom.css index 753957a..731465c 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -6,25 +6,60 @@ /* You can override the default Infima variables here. */ :root { - --ifm-color-primary: #1055AD; + --ifm-color-primary: #1055ad; --ifm-color-primary-dark: #0e4c9b; --ifm-color-primary-darker: #0c448a; - --ifm-color-primary-darkest: #0B3B79; - --ifm-color-primary-light: #2766B5; - --ifm-color-primary-lighter: #3F76BD; - --ifm-color-primary-lightest: #5788C5; + --ifm-color-primary-darkest: #0b3b79; + --ifm-color-primary-light: #2766b5; + --ifm-color-primary-lighter: #3f76bd; + --ifm-color-primary-lightest: #5788c5; --ifm-code-font-size: 95%; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); + --add-highlight-line-bg: rgba(46, 160, 67, 0.1); + --add-highlight-line-border: rgba(46, 160, 67, 0.4); + --remove-highlight-line-bg: rgba(248, 81, 73, 0.1); + --remove-highlight-line-border: rgba(248, 81, 73, 0.4); + --error-highlight-line-color: rgba(248, 0, 0, 1); } /* For readability concerns, you should choose a lighter palette in dark mode. */ [data-theme='dark'] { - --ifm-color-primary: #1055AD; + --ifm-color-primary: #1055ad; --ifm-color-primary-dark: #0e4c9b; --ifm-color-primary-darker: #0c448a; - --ifm-color-primary-darkest: #0B3B79; - --ifm-color-primary-light: #2766B5; - --ifm-color-primary-lighter: #3F76BD; - --ifm-color-primary-lightest: #5788C5; + --ifm-color-primary-darkest: #0b3b79; + --ifm-color-primary-light: #2766b5; + --ifm-color-primary-lighter: #3f76bd; + --ifm-color-primary-lightest: #5788c5; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); + --add-highlight-line-bg: rgba(46, 160, 67, 0.1); + --add-highlight-line-border: rgba(46, 160, 67, 0.4); + --remove-highlight-line-bg: rgba(248, 81, 73, 0.1); + --remove-highlight-line-border: rgba(248, 81, 73, 0.4); + --error-highlight-line-color: rgba(255, 74, 63, 1); +} + +.code-block-add-line { + background-color: var(--add-highlight-line-bg); + display: block; + margin: 0 calc(-1 * var(--ifm-pre-padding)); + padding: 0 var(--ifm-pre-padding); + border-left: 3px solid var(--add-highlight-line-border); +} + +.code-block-remove-line{ + background-color: var(--remove-highlight-line-bg); + display: block; + margin: 0 calc(-1 * var(--ifm-pre-padding)); + padding: 0 var(--ifm-pre-padding); + border-left: 3px solid var(--remove-highlight-line-border); +} + +.code-block-error-line { + color: var(--error-highlight-line-color) !important; + display: block; + margin: 0 calc(-1 * var(--ifm-pre-padding)); + padding: 0 var(--ifm-pre-padding); }