Skip to content

Commit 233d4f4

Browse files
Merge pull request #79 from makeplane/preview
External secrets
2 parents dc2ff3f + f0eab35 commit 233d4f4

File tree

3 files changed

+246
-1
lines changed

3 files changed

+246
-1
lines changed

mint.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
"self-hosting/govern/integrations/gitlab"
9999
]
100100
},
101+
"self-hosting/govern/external-secrets",
101102
"self-hosting/govern/reverse-proxy",
102103
"self-hosting/telemetry"
103104
]
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
---
2+
title: Configure external secrets for Kubernetes deployments
3+
sidebarTitle: External secrets
4+
---
5+
6+
This guide explains how to integrate Plane with external secret management solutions, enabling secure and centralized management of sensitive configuration data. The examples provided cover AWS Secrets Manager and HashiCorp Vault integrations, but you can adapt these patterns to your preferred secret management solution.
7+
8+
## AWS Secrets Manager
9+
10+
1. Create a dedicated IAM user (e.g., `external-secret-access-user`). You can uncheck **Console Access Required**.
11+
2. Generate `ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` and keep them handy.
12+
3. Note the user's ARN for later use (format: `arn:aws:iam::<account-id>:user/<user-name>`).
13+
14+
4. Create IAM policy (e.g., `external-secret-access-policy`) with the following JSON:
15+
16+
```json
17+
{
18+
"Version": "2012-10-17",
19+
"Statement": [
20+
{
21+
"Effect": "Allow",
22+
"Action": [
23+
"secretsmanager:GetResourcePolicy",
24+
"secretsmanager:GetSecretValue",
25+
"secretsmanager:DescribeSecret",
26+
"secretsmanager:ListSecretVersionIds"
27+
],
28+
"Resource": [
29+
"arn:aws:secretsmanager:<REGION>:<ACCOUNT-ID>:secret:*"
30+
]
31+
}
32+
]
33+
}
34+
```
35+
Replace `<REGION>` and `<ACCOUNT-ID>` with your AWS region and account ID.
36+
37+
5. Create IAM role (e.g., external-secret-access-role) with the following trust relationship:
38+
39+
```json
40+
{
41+
"Version": "2012-10-17",
42+
"Statement": [
43+
{
44+
"Effect": "Allow",
45+
"Principal": {
46+
"AWS": "<IAM-USER-ARN>"
47+
},
48+
"Action": "sts:AssumeRole"
49+
}
50+
]
51+
}
52+
```
53+
54+
Replace `<IAM-USER-ARN>` with the ARN of the user created in step 1.
55+
56+
6. Attach the AWS IAM policy created in step 4 to the IAM role.
57+
58+
7. Create secrets in AWS Secrets Manager with your Plane configuration values. For example, store RabbitMQ credentials with a name like `prod/secrets/rabbitmq`.
59+
60+
|Key|Value|
61+
|-------|--------|
62+
|RABBITMQ_DEFAULT_USER|plane|
63+
|RABBITMQ_DEFAULT_PASS|plane123|
64+
65+
Follow this pattern to manage all the [environment variables](/self-hosting/methods/kubernetes#external-secrets-config) in AWS Secrets Manager.
66+
67+
8. Create a Kubernetes secret containing AWS credentials in your application namespace:
68+
```sh
69+
kubectl create secret generic aws-creds-secret \
70+
--from-literal=access-key=<AWS_ACCESS_KEY_ID> \
71+
--from-literal=secret-access-key=<AWS_SECRET_ACCESS_KEY> \
72+
-n <application_namespace>
73+
```
74+
75+
9. Apply the following YAML to create a ClusterSecretStore resource:
76+
```yaml
77+
apiVersion: external-secrets.io/v1beta1
78+
kind: ClusterSecretStore
79+
metadata:
80+
name: cluster-aws-secretsmanager
81+
spec:
82+
provider:
83+
aws:
84+
service: SecretsManager
85+
role: arn:aws:iam::<ACCOUNT-ID>:role/<IAM ROLE>
86+
region: eu-west-1
87+
auth:
88+
secretRef:
89+
accessKeyIDSecretRef:
90+
name: aws-creds-secret
91+
key: access-key
92+
secretAccessKeySecretRef:
93+
name: aws-creds-secret
94+
key: secret-access-key
95+
```
96+
Replace `<ACCOUNT-ID>` and `<IAM ROLE>` with your AWS account ID and the role name created in Step 5.
97+
98+
10. Create an ExternalSecret resource to fetch secrets from AWS and create a corresponding Kubernetes secret:
99+
```yaml
100+
apiVersion: external-secrets.io/v1beta1
101+
kind: ExternalSecret
102+
metadata:
103+
name: secret
104+
namespace: <application_namespace>
105+
spec:
106+
refreshInterval: 1m
107+
secretStoreRef:
108+
name: cluster-aws-secretsmanager # ClusterSecretStore name
109+
kind: ClusterSecretStore
110+
target:
111+
name: rabbitmq-secret # Target Kubernetes secret name
112+
creationPolicy: Owner
113+
data:
114+
- secretKey: RABBITMQ_DEFAULT_USER # Specifies the key name for the secret value in the Kubernetes secret.
115+
remoteRef:
116+
key: prod/secrets/rabbitmq # Specifies the name to the secret in the AWS Secrets Manager
117+
property: RABBITMQ_DEFAULT_USER # Specifies the name of the secret property to retrieve from the AWS Secrets Manager
118+
- secretKey: RABBITMQ_DEFAULT_PASS
119+
remoteRef:
120+
key: prod/secrets/rabbitmq
121+
property: RABBITMQ_DEFAULT_PASS
122+
```
123+
124+
Make sure to set all [environment variables](/self-hosting/methods/kubernetes#external-secrets-config) in the AWS Secrets Manager, and then access them via ExternalSecret resources in your Kubernetes cluster.
125+
126+
## HashiCorp Vault
127+
128+
1. Access the Vault UI at `https://<vault-domain>/`.
129+
130+
2. Set up a KV secrets engine if not already configured.
131+
132+
3. Create a secret with your Plane configuration values (e.g., `secrets/rabbitmq_secrets`). For this example, we're setting up RabbitMQ credentials:
133+
134+
|Key|Value|
135+
|-------|--------|
136+
|RABBITMQ_DEFAULT_USER|plane|
137+
|RABBITMQ_DEFAULT_PASS|plane123|
138+
139+
Follow this pattern to manage all the other [environment variables](/self-hosting/methods/kubernetes#external-secrets-config) in the Vault.
140+
141+
4. Create a Kubernetes secret containing your Vault token in your application namespace:
142+
```sh
143+
kubectl create secret generic vault-token -n <application_namespace> --from-literal=token=<VAULT-TOKEN>
144+
```
145+
146+
5. Apply the following YAML to create a ClusterSecretStore resource:
147+
```yaml
148+
# cluster-store.yaml
149+
apiVersion: external-secrets.io/v1beta1
150+
kind: ClusterSecretStore
151+
metadata:
152+
name: vault-backend
153+
spec:
154+
provider:
155+
vault:
156+
server: "https://<vault-domain>" #the address of your vault instance
157+
path: "secrets" #path for accessing the secrets
158+
version: "v2" #Vault API version
159+
auth:
160+
tokenSecretRef:
161+
name: "vault-token" #Use a k8s secret called vault-token
162+
key: "token" #Use this key to access the vault token
163+
```
164+
165+
Replace `<vault-domain>` with your Vault server address.
166+
167+
6. Create an ExternalSecret resource to fetch secrets from Vault and create a corresponding Kubernetes secret:
168+
```yaml
169+
apiVersion: external-secrets.io/v1beta1
170+
kind: ExternalSecret
171+
metadata:
172+
name: rabbitmq-external-secrets
173+
namespace: <application_namespace> # application-namespace
174+
spec:
175+
refreshInterval: "1m"
176+
secretStoreRef:
177+
name: vault-backend # ClusterSecretStore name
178+
kind: ClusterSecretStore
179+
target:
180+
name: rabbitmq-secret # Target Kubernetes secret name
181+
creationPolicy: Owner
182+
data:
183+
- secretKey: RABBITMQ_DEFAULT_USER # Specifies the key name for the secret value stored in the Kubernetes secret.
184+
remoteRef:
185+
key: secrets/data/rabbitmq_secrets # Specifies the name to the secret in the Vault secret store.
186+
property: RABBITMQ_DEFAULT_USER # Specifies the name of the secret property to retrieve from the Vault secret store.
187+
- secretKey: RABBITMQ_DEFAULT_PASS
188+
remoteRef:
189+
key: secrets/data/rabbitmq_secrets
190+
property: RABBITMQ_DEFAULT_PASS
191+
```
192+
193+
Follow this pattern to manage all the environment variables in the Vault, then access them via ExternalSecret resources in your Kubernetes cluster.

self-hosting/methods/kubernetes.mdx

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,14 +227,40 @@ If you want to upgrade to a paid plan, see [Plan upgrades](https://docs.plane.so
227227
| services.worker.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use the maximum memory they are allowed to use. This key sets the memory limit for this deployment to use.|
228228
| services.worker.cpuLimit | 500m | | Every deployment in kubernetes can be set to use the maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use.|
229229

230-
#### Beat-Worker deployment
230+
#### Beat-Worker Deployment
231231

232232
| Setting | Default | Required | Description |
233233
|---|:---:|:---:|---|
234234
| services.beatworker.replicas | 1 | Yes | Kubernetes helps you with scaling up or down the deployments. You can run 1 or more pods for each deployment. This key helps you set up the number of replicas you want to run for this deployment. It must be >=1 |
235235
| services.beatworker.memoryLimit | 1000Mi | | Every deployment in kubernetes can be set to use the maximum memory they are allowed to use. This key sets the memory limit for this deployment to use.|
236236
| services.beatworker.cpuLimit | 500m | | Every deployment in kubernetes can be set to use the maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use.|
237237

238+
#### External Secrets Config
239+
240+
To configure the external secrets for your application, you need to define specific environment variables for each secret category. Below is a list of the required secrets and their respective environment variables. See [External secrets](/self-hosting/govern/external-secrets) for setup details.
241+
242+
| Secret Name | Env Var Name | Required | Description | Example Value |
243+
|--- |:---|:---|:---|:---|
244+
| rabbitmq_existingSecret | `RABBITMQ_DEFAULT_USER` | Required if `rabbitmq.local_setup=true` | The default RabbitMQ user | `plane` |
245+
| | `RABBITMQ_DEFAULT_PASS` | Required if `rabbitmq.local_setup=true` | The default RabbitMQ password | `plane` |
246+
| pgdb_existingSecret | `POSTGRES_PASSWORD` | Required if `postgres.local_setup=true` | Password for PostgreSQL database | `plane` |
247+
| | `POSTGRES_DB` | Required if `postgres.local_setup=true` | Name of the PostgreSQL database | `plane` |
248+
| | `POSTGRES_USER` | Required if `postgres.local_setup=true` | PostgreSQL user | `plane` |
249+
| doc_store_existingSecret | `USE_MINIO` | Yes | Flag to enable MinIO as the storage backend | `1` |
250+
| | `MINIO_ROOT_USER` | Yes | MinIO root user | `admin` |
251+
| | `MINIO_ROOT_PASSWORD` | Yes | MinIO root password | `password` |
252+
| | `AWS_ACCESS_KEY_ID` | Yes | AWS Access Key ID | `your_aws_key` |
253+
| | `AWS_SECRET_ACCESS_KEY` | Yes | AWS Secret Access Key | `your_aws_secret` |
254+
| | `AWS_S3_BUCKET_NAME` | Yes | AWS S3 Bucket Name | `your_bucket_name` |
255+
| | `AWS_S3_ENDPOINT_URL` | Yes | Endpoint URL for AWS S3 or MinIO | `http://plane-minio.plane-ns.svc.cluster.local:9000` |
256+
| | `AWS_REGION` | Optional | AWS region where your S3 bucket is located | `your_aws_region` |
257+
| | `FILE_SIZE_LIMIT` | Yes | Limit for file uploads in your system | `5MB` |
258+
| app_env_existingSecret | `SECRET_KEY` | Yes | Random secret key | `60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5` |
259+
| | `REDIS_URL` | Yes | Redis URL | `redis://plane-redis.plane-ns.svc.cluster.local:6379/` |
260+
| | `DATABASE_URL` | Yes | PostgreSQL connection URL | **k8s service example**: `postgresql://plane:[email protected]:5432/plane` <br/> <br/>**external service example**: `postgresql://username:password@your-db-host:5432/plane` |
261+
| | `AMQP_URL` | Yes | RabbitMQ connection URL | **k8s service example**: `amqp://plane:[email protected]:5672/` <br/> <br/> **external service example**: `amqp://username:password@your-rabbitmq-host:5672/` |
262+
263+
238264
#### Ingress and SSL Setup
239265

240266
| Setting | Default | Required | Description |
@@ -450,6 +476,31 @@ If you want to upgrade to a paid plan, see [Plan upgrades](https://docs.plane.so
450476
| beatworker.cpuLimit | 500m | | Every deployment in kubernetes can be set to use the maximum cpu they are allowed to use. This key sets the cpu limit for this deployment to use.|
451477
| beatworker.image| makeplane/plane-backend | | This deployment needs a preconfigured docker image to function. Docker image name is provided by the owner and must not be changed for this deployment |
452478

479+
#### External Secrets Config
480+
481+
To configure the external secrets for your application, you need to define specific environment variables for each secret category. Below is a list of the required secrets and their respective environment variables. See [External secrets](/self-hosting/govern/external-secrets) for setup details.
482+
483+
| Secret Name | Env Var Name | Required | Description | Example Value |
484+
|--- |:---|:---|:---|:---|
485+
| rabbitmq_existingSecret | `RABBITMQ_DEFAULT_USER` | Required if `rabbitmq.local_setup=true` | The default RabbitMQ user | `plane` |
486+
| | `RABBITMQ_DEFAULT_PASS` | Required if `rabbitmq.local_setup=true` | The default RabbitMQ password | `plane` |
487+
| pgdb_existingSecret | `POSTGRES_PASSWORD` | Required if `postgres.local_setup=true` | Password for PostgreSQL database | `plane` |
488+
| | `POSTGRES_DB` | Required if `postgres.local_setup=true` | Name of the PostgreSQL database | `plane` |
489+
| | `POSTGRES_USER` | Required if `postgres.local_setup=true` | PostgreSQL user | `plane` |
490+
| doc_store_existingSecret | `USE_MINIO` | Yes | Flag to enable MinIO as the storage backend | `1` |
491+
| | `MINIO_ROOT_USER` | Yes | MinIO root user | `admin` |
492+
| | `MINIO_ROOT_PASSWORD` | Yes | MinIO root password | `password` |
493+
| | `AWS_ACCESS_KEY_ID` | Yes | AWS Access Key ID | `your_aws_key` |
494+
| | `AWS_SECRET_ACCESS_KEY` | Yes | AWS Secret Access Key | `your_aws_secret` |
495+
| | `AWS_S3_BUCKET_NAME` | Yes | AWS S3 Bucket Name | `your_bucket_name` |
496+
| | `AWS_S3_ENDPOINT_URL` | Yes | Endpoint URL for AWS S3 or MinIO | `http://plane-minio.plane-ns.svc.cluster.local:9000` |
497+
| | `AWS_REGION` | Optional | AWS region where your S3 bucket is located | `your_aws_region` |
498+
| | `FILE_SIZE_LIMIT` | Yes | Limit for file uploads in your system | `5MB` |
499+
| app_env_existingSecret | `SECRET_KEY` | Yes | Random secret key | `60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5` |
500+
| | `REDIS_URL` | Yes | Redis URL | `redis://plane-redis.plane-ns.svc.cluster.local:6379/` |
501+
| | `DATABASE_URL` | Yes | PostgreSQL connection URL | **k8s service example**: `postgresql://plane:[email protected]:5432/plane` <br/> <br/>**external service example**: `postgresql://username:password@your-db-host:5432/plane` |
502+
| | `AMQP_URL` | Yes | RabbitMQ connection URL | **k8s service example**: `amqp://plane:[email protected]:5672/` <br/> <br/> **external service example**: `amqp://username:password@your-rabbitmq-host:5672/` |
503+
453504
#### Ingress and SSL Setup
454505

455506
| Setting | Default | Required | Description |

0 commit comments

Comments
 (0)