Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
fccf9d1
feat: migrate secrets to SOPS encryption
MoeexT May 25, 2026
21484df
feat: update Makefile for SOPS secret management
MoeexT May 25, 2026
aefc5cf
fix: remove hardcoded JWT secret and unify property naming
MoeexT May 25, 2026
25972b0
fix: enforce encrypted private key and restrict permissions
MoeexT May 25, 2026
cdc393a
fix: remove hardcoded passwords in Python config and exclude dev .env…
MoeexT May 25, 2026
1c9b269
refactor: encapsulate SOPS in scripts/secrets.sh, remove helm-secrets…
MoeexT May 25, 2026
1cd2316
fix: pass Label Studio credentials to backend-python container
MoeexT May 25, 2026
638931b
fix: add LABEL_STUDIO_USERNAME env var for auto-login to Label Studio
MoeexT May 25, 2026
659758d
docs: add LABEL_STUDIO_USERNAME to .env.example
MoeexT May 25, 2026
cdaa8a3
fix: remove duplicate case blocks in scripts/secrets.sh causing synta…
MoeexT May 25, 2026
7fc4f8a
fix: add LABEL_STUDIO_USERNAME to Helm backend-python env
MoeexT May 26, 2026
ffe8f95
feat: migrate from SOPS to Sealed Secrets for K8s secret management
MoeexT May 26, 2026
977e818
fix: pgbouncer also reads POSTGRE_PASSWORD from existingSecret
MoeexT May 27, 2026
0e0d29c
fix: set secrets.create: false to prevent Helm-SealedSecret conflict
MoeexT May 27, 2026
ba9eabd
fix: gateway reads JWT_SECRET from datamate-conf Secret
MoeexT May 27, 2026
61a7a61
fix: set created_by='system' for seed data to work with JWT data scop…
MoeexT May 27, 2026
d6bd31e
fix: remove hardcoded DB_PASSWORD default fallback in gateway config
MoeexT May 28, 2026
1c14709
docs: add Sealed Secrets setup guide for online and offline environments
MoeexT May 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Environment files (local dev only, never in images)
**/.env
!.env.example

# Python
**/__pycache__/
**/*.pyc
**/.venv/
**/venv/

# Logs
**/logs/
**/*.log

# IDE
.idea/
.vscode/
*.iml

# Git
.git/
.gitignore
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -191,4 +191,7 @@ Thumbs.db
# Milvus
**/volumes/

**/rag_storage/
**/rag_storage/
# Environment files - ignore local .env, but allow templates
.env
!.env.example
25 changes: 24 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ help:
@echo " make install Install datamate + milvus (prompts for method)"
@echo " make install INSTALLER=docker Install using Docker Compose"
@echo " make install INSTALLER=k8s Install using Kubernetes/Helm"
@echo " make install INSTALLER=k8s (requires Sealed Secrets Controller)"
@echo " make install-<component> Install specific component (prompts)"
@echo " make <component>-docker-install Install component via Docker"
@echo " make <component>-k8s-install Install component via Kubernetes"
Expand All @@ -69,6 +70,7 @@ help:
@echo " make download VERSION=<version> Pull all images with specific version"
@echo " make download REGISTRY=<registry> Pull images from specific registry"
@echo " make load-images Load all downloaded images from dist/"
@echo " make download-sealed-secrets Download Sealed Secrets image (for offline)"
@echo ""
@echo "Utility Commands:"
@echo " make create-namespace Create Kubernetes namespace"
Expand Down Expand Up @@ -254,6 +256,13 @@ VALID_SERVICE_TARGETS := datamate backend frontend runtime backend-python databa
done; \
exit 1; \
fi
@if [ ! -f deployment/docker/datamate/.env ]; then \
echo "ERROR: deployment/docker/datamate/.env not found."; \
echo "Create it from the template:"; \
echo " cp deployment/docker/datamate/.env.example deployment/docker/datamate/.env"; \
echo "Then edit it with your actual passwords."; \
exit 1; \
fi
@if [ "$*" = "label-studio" ]; then \
REGISTRY=$(REGISTRY) docker compose -f deployment/docker/datamate/docker-compose.yml --profile label-studio up -d; \
elif [ "$*" = "datamate" ]; then \
Expand Down Expand Up @@ -326,18 +335,21 @@ VALID_K8S_TARGETS := datamate deer-flow milvus label-studio data-juicer mineru m
exit 1; \
fi
@if [ "$*" = "label-studio" ]; then \
kubectl apply -f deployment/kubernetes/sealed-secrets/label-studio.yaml; \
helm upgrade label-studio deployment/helm/label-studio/ -n $(NAMESPACE) --install; \
elif [ "$*" = "mineru" ] || [ "$*" = "mineru-910B" ] || [ "$*" = "mineru-910C" ]; then \
kubectl apply -f deployment/kubernetes/mineru/deploy-910.yaml -n $(NAMESPACE); \
elif [ "$*" = "mineru-310P" ]; then \
kubectl apply -f deployment/kubernetes/mineru/deploy-310.yaml -n $(NAMESPACE); \
elif [ "$*" = "datamate" ]; then \
helm upgrade datamate deployment/helm/datamate/ -n $(NAMESPACE) --install --set global.image.repository=$(REGISTRY); \
kubectl apply -f deployment/kubernetes/sealed-secrets/datamate.yaml; \
helm upgrade datamate deployment/helm/datamate/ -n $(NAMESPACE) --install --set global.image.repository=$(REGISTRY) --set public.secrets.create=false; \
elif [ "$*" = "deer-flow" ]; then \
cp runtime/deer-flow/.env deployment/helm/deer-flow/charts/public/.env; \
cp runtime/deer-flow/conf.yaml deployment/helm/deer-flow/charts/public/conf.yaml; \
helm upgrade deer-flow deployment/helm/deer-flow -n $(NAMESPACE) --install --set global.image.repository=$(REGISTRY); \
elif [ "$*" = "milvus" ]; then \
kubectl apply -f deployment/kubernetes/sealed-secrets/milvus.yaml; \
helm upgrade milvus deployment/helm/milvus -n $(NAMESPACE) --install; \
elif [ "$*" = "data-juicer" ] || [ "$*" = "dj" ]; then \
kubectl apply -f deployment/kubernetes/data-juicer/deploy.yaml -n $(NAMESPACE); \
Expand Down Expand Up @@ -471,6 +483,17 @@ DEER_FLOW_IMAGES := \
download-deer-flow:
$(MAKE) download DOWNLOAD_IMAGES="$(DEER_FLOW_IMAGES)"

# Download Sealed Secrets controller image for offline/air-gapped environments
SEALED_SECRETS_IMAGE := bitnami/sealed-secrets-controller:latest
.PHONY: download-sealed-secrets
download-sealed-secrets:
@echo "Pulling Sealed Secrets controller image..."
@mkdir -p dist
docker pull $(SEALED_SECRETS_IMAGE)
docker save $(SEALED_SECRETS_IMAGE) -o dist/sealed-secrets-controller.tar
@echo "✅ Saved to dist/sealed-secrets-controller.tar"
@echo "Transfer to offline environment and load with: docker load -i dist/sealed-secrets-controller.tar"

# Load all downloaded images from dist/ directory
.PHONY: load-images
load-images:
Expand Down
44 changes: 43 additions & 1 deletion README-zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,50 @@
- Make (用于构建和安装)
- Docker (用于构建镜像和部署服务)
- Docker-Compose (用于部署服务-docker方式)
- kubernetes (用于部署服务-k8s方式)
- Kubernetes (用于部署服务-k8s方式)
- Helm (用于部署服务-k8s方式)
- **K8s 部署额外需要**: [Sealed Secrets Controller](https://github.com/bitnami-labs/sealed-secrets)(用于加密管理敏感配置)

### 密钥管理(仅 K8s 部署需要)

DataMate K8s 部署使用 **Bitnami Sealed Secrets** 管理数据库密码、JWT 密钥等敏感信息。所有密钥以加密形式存储在 Git 中(`deployment/kubernetes/sealed-secrets/`),部署时由集群内的 Sealed Secrets Controller 自动解密。

**在线环境安装 Sealed Secrets Controller:**

```bash
# 通过 Helm 安装(推荐)
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm install sealed-secrets sealed-secrets/sealed-secrets -n kube-system

# 验证安装
kubectl get pods -n kube-system | grep sealed-secrets
```

**离线环境:**

1. 在有网络的机器上下载 Sealed Secrets 镜像:
```bash
# 下载 controller 镜像(约 60MB)
docker pull bitnami/sealed-secrets-controller:latest
docker save bitnami/sealed-secrets-controller:latest -o sealed-secrets-controller.tar

# 下载 kubeseal 工具(用于更新密钥)
# macOS:
brew install kubeseal
# Linux:
wget https://github.com/bitnami-labs/sealed-secrets/releases/latest/download/kubeseal-linux-amd64
```

2. 将镜像导入离线环境的镜像仓库,通过 Helm 安装时指定镜像地址。

**更新密钥:**

```bash
# 如果数据库密码等敏感信息发生变更,使用 kubeseal 重新加密
echo -n "new-password" | kubeseal --raw --name datamate-conf --namespace datamate --scope namespace-wide
```

> 注意:Docker 部署方式不需要 Sealed Secrets,密钥统一通过 `.env` 文件管理(已在 `.gitignore` 中排除)。

### Docker一键部署
```shell
Expand Down
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,48 @@ If you like this project, please give it a Star⭐️!
- Docker-Compose (for service deployment - Docker method)
- Kubernetes (for service deployment - k8s method)
- Helm (for service deployment - k8s method)
- **K8s deployment additionally requires**: [Sealed Secrets Controller](https://github.com/bitnami-labs/sealed-secrets) (for encrypted secret management)

### Secret Management (K8s deployment only)

DataMate K8s deployment uses **Bitnami Sealed Secrets** to manage sensitive configuration such as database passwords and JWT secrets. All secrets are stored in encrypted form in Git (`deployment/kubernetes/sealed-secrets/`) and automatically decrypted by the Sealed Secrets Controller in the cluster at deploy time.

**Online environment - install Sealed Secrets Controller:**

```bash
# Install via Helm (recommended)
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm install sealed-secrets sealed-secrets/sealed-secrets -n kube-system

# Verify installation
kubectl get pods -n kube-system | grep sealed-secrets
```

**Air-gapped / offline environment:**

1. Download the Sealed Secrets image on an internet-connected machine:
```bash
# Download controller image (~60MB)
docker pull bitnami/sealed-secrets-controller:latest
docker save bitnami/sealed-secrets-controller:latest -o sealed-secrets-controller.tar

# Download kubeseal CLI (for updating secrets)
# macOS:
brew install kubeseal
# Linux:
wget https://github.com/bitnami-labs/sealed-secrets/releases/latest/download/kubeseal-linux-amd64
```

2. Transfer the image to your offline registry, then install via Helm with the custom image reference.

**Updating secrets:**

```bash
# When passwords change, re-encrypt with kubeseal
echo -n "new-password" | kubeseal --raw --name datamate-conf --namespace datamate --scope namespace-wide
```

> Note: Docker deployments do not require Sealed Secrets — secrets are managed via the `.env` file (excluded from Git via `.gitignore`).

### Docker Quick deploy
```shell
Expand Down
2 changes: 1 addition & 1 deletion backend/api-gateway/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ spring:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://datamate-database:5432/datamate?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: ${DB_USERNAME:postgres}
password: ${DB_PASSWORD:password}
password: ${DB_PASSWORD}
hikari:
maximum-pool-size: 20
minimum-idle: 5
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ spring:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://datamate-database:5432/datamate?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: ${DB_USERNAME:postgres}
password: ${DB_PASSWORD:password}
password: ${DB_PASSWORD}
hikari:
maximum-pool-size: 20
minimum-idle: 5
Expand Down Expand Up @@ -59,7 +59,7 @@ spring:
host: datamate-redis
port: 6379
timeout: 2000
password: ${REDIS_PASSWORD:password}
password: ${REDIS_PASSWORD}
lettuce:
pool:
max-active: 20
Expand Down Expand Up @@ -131,6 +131,10 @@ management:

# 平台配置
datamate:
# JWT配置
jwt:
secret: ${JWT_SECRET}

# 通用配置


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
@Component
public class JwtUtils {

@Value("${jwt.secret:datamate-secret-key-for-jwt-token-generation}")
@Value("${datamate.jwt.secret}")
private String secret;

@Value("${jwt.expiration:86400}") // 24小时
Expand Down
30 changes: 30 additions & 0 deletions deployment/docker/datamate/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# DataMate Environment Variables Template
# Copy this file to .env and fill in the values
# cp .env.example .env
# IMPORTANT: Never commit .env to git! It is already in .gitignore.
#
# For K8s/Helm deployment: secrets are managed via Sealed Secrets.
# For Docker deployment: use this .env file (gitignored, local only).

# Database
DB_PASSWORD=your-secure-password-here

# JWT Authentication
JWT_SECRET=your-secure-jwt-secret-here
DATAMATE_JWT_ENABLE=false

# MinIO (for Milvus storage)
MINIO_ACCESS_KEY=your-minio-access-key
MINIO_SECRET_KEY=your-minio-secret-key

# Label Studio
LABEL_STUDIO_USERNAME=admin@demo.com
LABEL_STUDIO_PASSWORD=your-labelstudio-password
LABEL_STUDIO_USER_TOKEN=your-labelstudio-token
LABEL_STUDIO_HOST=

# Optional: SSL Certificate Password (for encrypted private keys)
CERT_PASS=

# Optional: Domain for HTTPS
DOMAIN=
28 changes: 17 additions & 11 deletions deployment/docker/datamate/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ services:
restart: on-failure
privileged: true
environment:
- DB_PASSWORD=${DB_PASSWORD:-password}
- DB_PASSWORD=${DB_PASSWORD:?DB_PASSWORD is required. Set in .env file}
- JWT_SECRET=${JWT_SECRET:?JWT_SECRET is required. Set in .env file}
- datamate.jwt.enable=${DATAMATE_JWT_ENABLE:-false}
volumes:
- dataset_volume:/dataset
Expand All @@ -30,9 +31,14 @@ services:
- "18000:18000"
environment:
- log_level=DEBUG
- pgsql_password=${DB_PASSWORD:-password}
- PGSQL_HOST=datamate-database
- PGSQL_PORT=5432
- pgsql_password=${DB_PASSWORD:?DB_PASSWORD is required. Set in .env file}
- datamate_jwt_enable=${DATAMATE_JWT_ENABLE:-false}
- milvus_uri=${MILVUS_URI:-http://milvus:19530}
- LABEL_STUDIO_USERNAME=${LABEL_STUDIO_USERNAME:-admin@demo.com}
- LABEL_STUDIO_USER_TOKEN=${LABEL_STUDIO_USER_TOKEN:-}
- LABEL_STUDIO_PASSWORD=${LABEL_STUDIO_PASSWORD:-}
volumes:
- dataset_volume:/dataset
- flow_volume:/flow
Expand All @@ -52,7 +58,7 @@ services:
ports:
- '8080:8080'
environment:
- JWT_SECRET=default-insecure-key-change-in-production
- JWT_SECRET=${JWT_SECRET:-}
- datamate.jwt.enable=${DATAMATE_JWT_ENABLE:-false}
networks: [ datamate ]

Expand All @@ -75,7 +81,7 @@ services:
restart: on-failure
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=${DB_PASSWORD:-password}
- POSTGRES_PASSWORD=${DB_PASSWORD:?DB_PASSWORD is required. Set in .env file}
volumes:
- postgresql_volume:/var/lib/postgresql/data
- database_log_volume:/var/log/datamate/database
Expand All @@ -93,7 +99,7 @@ services:
PG_HOST: "datamate-database"
PG_PORT: "5432"
PG_USER: "postgres"
PG_PASSWORD: ${DB_PASSWORD:-password}
PG_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD is required. Set in .env file}
PG_DATABASE: "datamate"
command:
- python
Expand Down Expand Up @@ -213,7 +219,7 @@ services:
- DB_PORT=5432
- DB_NAME=labelstudio
- DB_USER=postgres
- DB_PASSWORD=${DB_PASSWORD:-password}
- DB_PASSWORD=${DB_PASSWORD:?DB_PASSWORD is required. Set in .env file}
- AUTH_TYPE=scram-sha-256
- POOL_MODE=transaction
- MAX_CLIENT_CONN=100
Expand Down Expand Up @@ -241,17 +247,17 @@ services:
- DJANGO_DB=default
- POSTGRE_NAME=labelstudio
- POSTGRE_USER=postgres
- POSTGRE_PASSWORD=${DB_PASSWORD:-password}
- POSTGRE_PASSWORD=${DB_PASSWORD:?DB_PASSWORD is required. Set in .env file}
- POSTGRE_PORT=5432
- POSTGRE_HOST=label-studio-pgbouncer
- LABEL_STUDIO_HOST=${LABEL_STUDIO_HOST:-}
- LOCAL_FILES_SERVING_ENABLED=true
- LOCAL_FILES_DOCUMENT_ROOT=/label-studio/local
- USE_USERNAME_FOR_LOGIN=true
- LABEL_STUDIO_USERNAME=admin@demo.com
- LABEL_STUDIO_PASSWORD=demoadmin
- LABEL_STUDIO_PASSWORD=${LABEL_STUDIO_PASSWORD:-}
- LABEL_STUDIO_ENABLE_LEGACY_API_TOKEN=true
- LABEL_STUDIO_USER_TOKEN=abc123abc123
- LABEL_STUDIO_USER_TOKEN=${LABEL_STUDIO_USER_TOKEN:-}
- LOG_LEVEL=DEBUG
volumes:
- label-studio-data:/label-studio/data:rw
Expand Down Expand Up @@ -290,8 +296,8 @@ services:
container_name: milvus-minio
image: minio/minio:RELEASE.2024-12-18T13-15-44Z
environment:
MINIO_ACCESS_KEY: minioadmin
MINIO_SECRET_KEY: minioadmin
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY:-}
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY:-}
ports:
- "9001:9001"
- "9000:9000"
Expand Down
4 changes: 3 additions & 1 deletion deployment/helm/datamate/charts/public/templates/secret.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
{{- if not (eq (.Values.secrets.create | toString) "false") }}
apiVersion: v1
kind: Secret
metadata:
name: datamate-conf
data:
{{- range $key, $val := .Values.secrets.data }}
{{ $key }}: {{ $val | toString | b64enc | quote }}
{{- end }}
{{- end }}
{{- end }}
Loading
Loading