security: remove hardcoded credentials and add envsubst for go-zero configs

- Clear default passwords in all service configs and local dev YAMLs
- Add entrypoint.sh with envsubst to resolve ${ENV} vars in go-zero YAML
- Update Dockerfiles to install gettext and use entrypoint
- Update docker-compose to pass secrets via environment and require via ${VAR:?...}
- Add .gitignore rules for .env files, add .env.example template

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Chever John 2026-06-17 23:16:28 +08:00
parent ec8ae6a73b
commit a5b1b406ea
18 changed files with 86 additions and 22 deletions

5
.gitignore vendored
View File

@ -99,3 +99,8 @@ Temporary Items
# Local runtime data and certificates # Local runtime data and certificates
data/ data/
deploy/bin/ deploy/bin/
# Environment secrets
.env
deploy/.env
!deploy/.env.example

13
deploy/.env.example Normal file
View File

@ -0,0 +1,13 @@
# Copy this file to .env and fill in real values
# NEVER commit .env to git
# Database passwords (REQUIRED)
MYSQL_ROOT_PASSWORD=your_mysql_password_here
POSTGRES_PASSWORD=your_postgres_password_here
# JWT secret for gateway auth (REQUIRED)
ACCESS_SECRET=your_jwt_secret_here
# Neo4j (only needed for graph stack: docker-compose.graph.yaml)
NEO4J_AUTH=neo4j/your_neo4j_password_here
NEO4J_PASS=your_neo4j_password_here

View File

@ -11,10 +11,12 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /server ./gateway/gateway.
FROM ${BASE_REGISTRY}/alpine:3.20 FROM ${BASE_REGISTRY}/alpine:3.20
RUN sed -i "s|dl-cdn.alpinelinux.org|mirrors.aliyun.com|g" /etc/apk/repositories RUN sed -i "s|dl-cdn.alpinelinux.org|mirrors.aliyun.com|g" /etc/apk/repositories
RUN apk add --no-cache ca-certificates tzdata RUN apk add --no-cache ca-certificates tzdata gettext
WORKDIR /app WORKDIR /app
COPY --from=builder /server ./gateway COPY --from=builder /server ./gateway
COPY deploy/etc/gateway.yaml etc/gateway.yaml COPY deploy/etc/gateway.yaml etc/gateway.yaml
COPY deploy/tools/ tools/ COPY deploy/tools/ tools/
COPY deploy/entrypoint.sh /entrypoint.sh
EXPOSE 8888 EXPOSE 8888
ENTRYPOINT ["/entrypoint.sh"]
CMD ["./gateway", "-f", "etc/gateway.yaml"] CMD ["./gateway", "-f", "etc/gateway.yaml"]

View File

@ -11,9 +11,11 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /server ./rpc/inventory/in
FROM ${BASE_REGISTRY}/alpine:3.20 FROM ${BASE_REGISTRY}/alpine:3.20
RUN sed -i "s|dl-cdn.alpinelinux.org|mirrors.aliyun.com|g" /etc/apk/repositories RUN sed -i "s|dl-cdn.alpinelinux.org|mirrors.aliyun.com|g" /etc/apk/repositories
RUN apk add --no-cache ca-certificates tzdata RUN apk add --no-cache ca-certificates tzdata gettext
WORKDIR /app WORKDIR /app
COPY --from=builder /server ./inventory COPY --from=builder /server ./inventory
COPY deploy/etc/inventory.yaml etc/inventory.yaml COPY deploy/etc/inventory.yaml etc/inventory.yaml
COPY deploy/entrypoint.sh /entrypoint.sh
EXPOSE 9002 EXPOSE 9002
ENTRYPOINT ["/entrypoint.sh"]
CMD ["./inventory", "-f", "etc/inventory.yaml"] CMD ["./inventory", "-f", "etc/inventory.yaml"]

View File

@ -1,7 +1,9 @@
FROM alpine:3.19 FROM alpine:3.19
RUN apk add --no-cache ca-certificates tzdata RUN apk add --no-cache ca-certificates tzdata gettext
WORKDIR /app WORKDIR /app
COPY deploy/bin/purchase . COPY deploy/bin/purchase .
COPY deploy/etc/purchase.yaml etc/purchase.yaml COPY deploy/etc/purchase.yaml etc/purchase.yaml
COPY deploy/entrypoint.sh /entrypoint.sh
EXPOSE 9003 EXPOSE 9003
ENTRYPOINT ["/entrypoint.sh"]
CMD ["./purchase", "-f", "etc/purchase.yaml"] CMD ["./purchase", "-f", "etc/purchase.yaml"]

View File

@ -11,9 +11,11 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /server ./rpc/system/syste
FROM ${BASE_REGISTRY}/alpine:3.20 FROM ${BASE_REGISTRY}/alpine:3.20
RUN sed -i "s|dl-cdn.alpinelinux.org|mirrors.aliyun.com|g" /etc/apk/repositories RUN sed -i "s|dl-cdn.alpinelinux.org|mirrors.aliyun.com|g" /etc/apk/repositories
RUN apk add --no-cache ca-certificates tzdata RUN apk add --no-cache ca-certificates tzdata gettext
WORKDIR /app WORKDIR /app
COPY --from=builder /server ./system COPY --from=builder /server ./system
COPY deploy/etc/system.yaml etc/system.yaml COPY deploy/etc/system.yaml etc/system.yaml
COPY deploy/entrypoint.sh /entrypoint.sh
EXPOSE 9001 EXPOSE 9001
ENTRYPOINT ["/entrypoint.sh"]
CMD ["./system", "-f", "etc/system.yaml"] CMD ["./system", "-f", "etc/system.yaml"]

View File

@ -5,7 +5,7 @@ services:
restart: always restart: always
environment: environment:
POSTGRES_USER: muyu POSTGRES_USER: muyu
POSTGRES_PASSWORD: muyu2026 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}
POSTGRES_DB: muyu_crm POSTGRES_DB: muyu_crm
TZ: Asia/Shanghai TZ: Asia/Shanghai
ports: ports:
@ -70,7 +70,7 @@ services:
container_name: muyu-neo4j container_name: muyu-neo4j
restart: always restart: always
environment: environment:
NEO4J_AUTH: neo4j/muyu2026 NEO4J_AUTH: ${NEO4J_AUTH:?Set NEO4J_AUTH in .env (e.g. neo4j/yourpassword)}
NEO4J_server_memory_heap_initial__size: 512m NEO4J_server_memory_heap_initial__size: 512m
NEO4J_server_memory_heap_max__size: 1024m NEO4J_server_memory_heap_max__size: 1024m
NEO4J_server_default__listen__address: 0.0.0.0 NEO4J_server_default__listen__address: 0.0.0.0
@ -103,7 +103,7 @@ services:
KAFKA_GROUP: graphsync KAFKA_GROUP: graphsync
NEO4J_URI: bolt://neo4j:7687 NEO4J_URI: bolt://neo4j:7687
NEO4J_USER: neo4j NEO4J_USER: neo4j
NEO4J_PASS: muyu2026 NEO4J_PASS: ${NEO4J_PASS:?Set NEO4J_PASS in .env}
TZ: Asia/Shanghai TZ: Asia/Shanghai
kafka-ui: kafka-ui:

View File

@ -4,7 +4,7 @@ services:
container_name: muyu-mysql container_name: muyu-mysql
restart: always restart: always
environment: environment:
MYSQL_ROOT_PASSWORD: muyu2026 MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?Set MYSQL_ROOT_PASSWORD in .env}
MYSQL_DATABASE: muyu_wms MYSQL_DATABASE: muyu_wms
TZ: Asia/Shanghai TZ: Asia/Shanghai
ports: ports:
@ -39,7 +39,7 @@ services:
restart: always restart: always
environment: environment:
POSTGRES_USER: muyu POSTGRES_USER: muyu
POSTGRES_PASSWORD: muyu2026 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}
POSTGRES_DB: muyu_crm POSTGRES_DB: muyu_crm
TZ: Asia/Shanghai TZ: Asia/Shanghai
ports: ports:
@ -90,6 +90,7 @@ services:
condition: service_healthy condition: service_healthy
environment: environment:
TZ: Asia/Shanghai TZ: Asia/Shanghai
MYSQL_DSN: "root:${MYSQL_ROOT_PASSWORD}@tcp(mysql:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai"
inventory-rpc: inventory-rpc:
build: build:
@ -106,6 +107,7 @@ services:
condition: service_healthy condition: service_healthy
environment: environment:
TZ: Asia/Shanghai TZ: Asia/Shanghai
MYSQL_DSN: "root:${MYSQL_ROOT_PASSWORD}@tcp(mysql:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai"
purchase-rpc: purchase-rpc:
build: build:
@ -122,6 +124,7 @@ services:
condition: service_healthy condition: service_healthy
environment: environment:
TZ: Asia/Shanghai TZ: Asia/Shanghai
MYSQL_DSN: "root:${MYSQL_ROOT_PASSWORD}@tcp(mysql:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai"
gateway: gateway:
build: build:
@ -142,6 +145,9 @@ services:
condition: service_healthy condition: service_healthy
environment: environment:
TZ: Asia/Shanghai TZ: Asia/Shanghai
MYSQL_DSN: "root:${MYSQL_ROOT_PASSWORD}@tcp(mysql:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai"
POSTGRES_DSN: "postgres://muyu:${POSTGRES_PASSWORD}@postgres:5432/muyu_crm?sslmode=disable"
ACCESS_SECRET: ${ACCESS_SECRET:?Set ACCESS_SECRET in .env}
nginx: nginx:
image: nginx:alpine image: nginx:alpine

27
deploy/entrypoint.sh Executable file
View File

@ -0,0 +1,27 @@
#!/bin/sh
# Substitute environment variables in YAML config templates before starting the service.
# Usage: entrypoint.sh <binary> -f <config.yaml>
#
# The YAML file is treated as a template with ${VAR} placeholders.
# envsubst replaces them with actual environment variable values at container start time.
set -e
BINARY="$1"
shift
CONFIG_FILE=""
for arg in "$@"; do
case "$prev" in
-f) CONFIG_FILE="$arg" ;;
esac
prev="$arg"
done
if [ -n "$CONFIG_FILE" ] && [ -f "$CONFIG_FILE" ]; then
# Replace ${VAR} placeholders with environment variable values
envsubst < "$CONFIG_FILE" > "${CONFIG_FILE}.tmp"
mv "${CONFIG_FILE}.tmp" "$CONFIG_FILE"
fi
exec "$BINARY" "$@"

View File

@ -4,12 +4,12 @@ Port: 8888
Timeout: 120000 Timeout: 120000
Auth: Auth:
AccessSecret: muyu-wms-jwt-secret-key-2026 AccessSecret: ${ACCESS_SECRET}
AccessExpire: 7200 AccessExpire: 7200
DefaultTenantId: t_default_001 DefaultTenantId: t_default_001
DataSource: root:muyu2026@tcp(mysql:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai DataSource: ${MYSQL_DSN}
PostgresDataSource: "postgres://muyu:muyu2026@postgres:5432/muyu_crm?sslmode=disable" PostgresDataSource: ${POSTGRES_DSN}
SystemRpc: SystemRpc:
Etcd: Etcd:

View File

@ -6,7 +6,7 @@ Etcd:
- etcd:2379 - etcd:2379
Key: inventory.rpc Key: inventory.rpc
DataSource: root:muyu2026@tcp(mysql:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai DataSource: ${MYSQL_DSN}
Cache: Cache:
- Host: redis:6379 - Host: redis:6379

View File

@ -6,7 +6,7 @@ Etcd:
- etcd:2379 - etcd:2379
Key: purchase.rpc Key: purchase.rpc
DataSource: root:muyu2026@tcp(mysql:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai DataSource: ${MYSQL_DSN}
Cache: Cache:
- Host: redis:6379 - Host: redis:6379

View File

@ -6,7 +6,7 @@ Etcd:
- etcd:2379 - etcd:2379
Key: system.rpc Key: system.rpc
DataSource: root:muyu2026@tcp(mysql:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai DataSource: ${MYSQL_DSN}
Cache: Cache:
- Host: redis:6379 - Host: redis:6379

View File

@ -1,14 +1,15 @@
# Local development config — fill in credentials before running locally.
Name: gateway-api Name: gateway-api
Host: 0.0.0.0 Host: 0.0.0.0
Port: 8888 Port: 8888
Auth: Auth:
AccessSecret: muyu-wms-jwt-secret-key-2026 AccessSecret: your_jwt_secret_here
AccessExpire: 7200 AccessExpire: 7200
DefaultTenantId: t_default_001 DefaultTenantId: t_default_001
DataSource: root:muyu2026@tcp(127.0.0.1:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai DataSource: root:your_password@tcp(127.0.0.1:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
PostgresDataSource: "postgres://muyu:muyu2026@127.0.0.1:5432/muyu_crm?sslmode=disable" PostgresDataSource: "postgres://muyu:your_password@127.0.0.1:5432/muyu_crm?sslmode=disable"
SystemRpc: SystemRpc:
Etcd: Etcd:

View File

@ -1,3 +1,4 @@
# Local development config — fill in DataSource before running locally.
Name: inventory.rpc Name: inventory.rpc
ListenOn: 0.0.0.0:9002 ListenOn: 0.0.0.0:9002
@ -6,7 +7,7 @@ Etcd:
- 127.0.0.1:2379 - 127.0.0.1:2379
Key: inventory.rpc Key: inventory.rpc
DataSource: root:muyu2026@tcp(127.0.0.1:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai DataSource: root:your_password@tcp(127.0.0.1:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
Cache: Cache:
- Host: 127.0.0.1:6379 - Host: 127.0.0.1:6379

View File

@ -1,3 +1,4 @@
# Local development config — fill in DataSource before running locally.
Name: purchase.rpc Name: purchase.rpc
ListenOn: 0.0.0.0:9003 ListenOn: 0.0.0.0:9003
@ -6,7 +7,7 @@ Etcd:
- 127.0.0.1:2379 - 127.0.0.1:2379
Key: purchase.rpc Key: purchase.rpc
DataSource: root:muyu2026@tcp(127.0.0.1:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai DataSource: root:your_password@tcp(127.0.0.1:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
Cache: Cache:
- Host: 127.0.0.1:6379 - Host: 127.0.0.1:6379

View File

@ -1,3 +1,5 @@
# Local development config — fill in DataSource before running locally.
# For Docker deployment, see deploy/etc/system.yaml (uses envsubst).
Name: system.rpc Name: system.rpc
ListenOn: 0.0.0.0:9001 ListenOn: 0.0.0.0:9001
@ -6,7 +8,7 @@ Etcd:
- 127.0.0.1:2379 - 127.0.0.1:2379
Key: system.rpc Key: system.rpc
DataSource: root:muyu2026@tcp(127.0.0.1:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai DataSource: root:your_password@tcp(127.0.0.1:3306)/muyu_wms?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
Cache: Cache:
- Host: 127.0.0.1:6379 - Host: 127.0.0.1:6379

View File

@ -32,7 +32,7 @@ func main() {
flag.StringVar(&cfg.KafkaGroup, "kafka-group", envOr("KAFKA_GROUP", "graphsync"), "consumer group") flag.StringVar(&cfg.KafkaGroup, "kafka-group", envOr("KAFKA_GROUP", "graphsync"), "consumer group")
flag.StringVar(&cfg.Neo4jURI, "neo4j-uri", envOr("NEO4J_URI", "bolt://localhost:7687"), "Neo4j bolt URI") flag.StringVar(&cfg.Neo4jURI, "neo4j-uri", envOr("NEO4J_URI", "bolt://localhost:7687"), "Neo4j bolt URI")
flag.StringVar(&cfg.Neo4jUser, "neo4j-user", envOr("NEO4J_USER", "neo4j"), "Neo4j user") flag.StringVar(&cfg.Neo4jUser, "neo4j-user", envOr("NEO4J_USER", "neo4j"), "Neo4j user")
flag.StringVar(&cfg.Neo4jPass, "neo4j-pass", envOr("NEO4J_PASS", "muyu2026"), "Neo4j password") flag.StringVar(&cfg.Neo4jPass, "neo4j-pass", envOr("NEO4J_PASS", ""), "Neo4j password")
flag.Parse() flag.Parse()
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())