Skip to content

Commit 67f0fb9

Browse files
committed
chore: strengthen deployment hardening
1 parent c444507 commit 67f0fb9

29 files changed

Lines changed: 471 additions & 90 deletions

.env.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ OB_ALLOW_SELECT_STAR=false
3232
# OB_CONNECT_TIMEOUT_MS=10000
3333
# OB_QUERY_TIMEOUT_MS=30000
3434
# OB_MAX_ROWS=100
35+
# OB_MAX_RESULT_BYTES=1048576
3536
# OB_MAX_SQL_LENGTH=20000
3637
# OB_MAX_PARAM_COUNT=500
38+
# OB_MAX_CONCURRENT_QUERIES=5
3739
# OB_ENFORCE_READ_ONLY_TX=true
3840
# OB_PASSWORD_FILE=/run/secrets/ob_password
3941
# OB_ALLOWED_TABLES=biz_test.ob_sample,biz_test.*
@@ -56,9 +58,13 @@ OB_ALLOW_SELECT_STAR=false
5658
# OB_MCP_PATH=/mcp
5759
# OB_MCP_AUTH_TOKEN=
5860
# OB_MCP_AUTH_TOKEN_FILE=/run/secrets/ob_mcp_auth_token
61+
# OB_MCP_AUTH_TOKENS_FILE=/run/secrets/ob_mcp_auth_tokens.json
5962
# OB_MCP_ALLOWED_HOSTS=localhost,127.0.0.1
6063
# OB_MCP_ALLOWED_IPS=127.0.0.1,10.0.0.0/8,203.0.113.10
6164
# OB_MCP_TRUST_PROXY=false
65+
# OB_MCP_METRICS_AUTH_REQUIRED=false
66+
# OB_MCP_HEALTH_AUTH_REQUIRED=false
67+
# OB_MCP_CLIENT_ID_HEADER=x-ob-mcp-client-id
6268
# OB_MCP_BODY_LIMIT=256kb
6369
# OB_MCP_RATE_LIMIT_WINDOW_MS=60000
6470
# OB_MCP_RATE_LIMIT_MAX=60

.github/dependabot.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: npm
4+
directory: /
5+
schedule:
6+
interval: weekly
7+
open-pull-requests-limit: 10
8+
9+
- package-ecosystem: github-actions
10+
directory: /
11+
schedule:
12+
interval: weekly
13+
open-pull-requests-limit: 5
14+
15+
- package-ecosystem: docker
16+
directory: /
17+
schedule:
18+
interval: weekly
19+
open-pull-requests-limit: 5

.github/workflows/ci.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ jobs:
3939
- name: 依赖审计
4040
run: npm audit --omit=dev --audit-level=moderate
4141

42+
- name: 生成 SBOM
43+
run: npm sbom --sbom-format cyclonedx --omit=dev > sbom.cdx.json
44+
45+
- name: 上传 SBOM
46+
uses: actions/upload-artifact@v4
47+
with:
48+
name: sbom-cyclonedx
49+
path: sbom.cdx.json
50+
if-no-files-found: error
51+
4252
- name: npm 打包检查
4353
run: npm pack --dry-run
4454

.github/workflows/release.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,19 @@ jobs:
9696
- name: 测试
9797
run: npm test
9898

99+
- name: 生成 SBOM
100+
run: npm sbom --sbom-format cyclonedx --omit=dev > sbom.cdx.json
101+
99102
- name: 打包
100103
run: npm pack
101104

102105
- name: 上传 npm 包产物
103106
uses: actions/upload-artifact@v4
104107
with:
105108
name: npm-package
106-
path: "*.tgz"
109+
path: |
110+
"*.tgz"
111+
sbom.cdx.json
107112
if-no-files-found: error
108113

109114
image:
@@ -255,4 +260,5 @@ jobs:
255260
prerelease: ${{ github.event_name == 'workflow_dispatch' && inputs.prerelease == true }}
256261
files: |
257262
dist-release/*.tgz
263+
dist-release/*.json
258264
dist-release/*.tar.gz

Dockerfile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
FROM node:22-alpine AS build
1+
FROM node:22-alpine@sha256:16e22a550f3863206a3f701448c45f7912c6896a62de43add43bb9c86130c3e2 AS build
22

33
WORKDIR /app
44
COPY package.json package-lock.json tsconfig.json ./
5-
RUN npm ci
5+
RUN npm ci --ignore-scripts
66

77
COPY src ./src
88
RUN npm run build
99
RUN npm prune --omit=dev
1010

11-
FROM node:22-alpine AS runtime
11+
FROM node:22-alpine@sha256:16e22a550f3863206a3f701448c45f7912c6896a62de43add43bb9c86130c3e2 AS runtime
1212

1313
ENV NODE_ENV=production
1414
WORKDIR /app

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ OceanBase MCP is read-only by design. It has no write tool, rejects write/contro
2828
- Local stdio MCP server for OpenCode desktop/local workflows.
2929
- Streamable HTTP MCP server for shared or remote deployments.
3030
- Multiple profiles for dev, staging, production, tenants, or clusters.
31-
- Schema/table allowlist, denied system schemas, denied sensitive columns, and masked sample rows.
31+
- Schema/table allowlist, denied system schemas, denied sensitive columns, hard row/response caps, and masked sample rows.
3232
- Source IP allowlist with exact IP and CIDR entries through `OB_MCP_ALLOWED_IPS`.
33-
- HTTP bearer token, Host allowlist, body limit, and rate limiting.
33+
- HTTP bearer token policies, Host allowlist, body limit, endpoint auth, and rate limiting.
3434
- Health endpoint, Prometheus metrics, JSON audit logs, and config diagnostics.
35+
- Database grant verification through a read-only permission check tool.
3536
- Docker, Docker Compose, Kubernetes, Nginx, systemd, and local macOS/Linux/Windows client examples.
3637
- GitHub Actions for CI, tagged releases, GHCR multi-architecture images, and offline image archives.
3738

@@ -63,6 +64,8 @@ OB_MCP_READ_ONLY=true
6364
OB_ALLOWED_SCHEMAS=app_schema
6465
OB_ALLOW_SELECT_STAR=false
6566
OB_ENFORCE_READ_ONLY_TX=true
67+
OB_MAX_ROWS=100
68+
OB_MAX_RESULT_BYTES=1048576
6669
OB_MCP_HOST=127.0.0.1
6770
OB_MCP_AUTH_TOKEN_FILE=/run/secrets/ob_mcp_auth_token
6871
OB_MCP_ALLOWED_IPS=127.0.0.1,10.0.0.0/8
@@ -76,6 +79,7 @@ Use `OB_MCP_HOST=0.0.0.0` only behind TLS/reverse proxy or a trusted network bou
7679
| --- | --- |
7780
| `ob_list_profiles` | List configured profiles with secrets redacted. |
7881
| `ob_config_diagnostics` | Report deployment and policy checks. |
82+
| `ob_verify_permissions` | Check current database grants for write/admin privileges. |
7983
| `ob_ping` | Verify the OceanBase connection. |
8084
| `ob_policy_explain` | Explain whether SQL would be allowed. |
8185
| `ob_query` | Run strict read-only SQL. |
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# 可选生产增强模板。应用前请按集群命名空间、Ingress、监控和 OceanBase 网络修改。
2+
---
3+
apiVersion: policy/v1
4+
kind: PodDisruptionBudget
5+
metadata:
6+
name: ob-mcp
7+
spec:
8+
minAvailable: 1
9+
selector:
10+
matchLabels:
11+
app: ob-mcp
12+
---
13+
apiVersion: autoscaling/v2
14+
kind: HorizontalPodAutoscaler
15+
metadata:
16+
name: ob-mcp
17+
spec:
18+
scaleTargetRef:
19+
apiVersion: apps/v1
20+
kind: Deployment
21+
name: ob-mcp
22+
minReplicas: 2
23+
maxReplicas: 6
24+
metrics:
25+
- type: Resource
26+
resource:
27+
name: cpu
28+
target:
29+
type: Utilization
30+
averageUtilization: 70
31+
---
32+
apiVersion: networking.k8s.io/v1
33+
kind: NetworkPolicy
34+
metadata:
35+
name: ob-mcp
36+
spec:
37+
podSelector:
38+
matchLabels:
39+
app: ob-mcp
40+
policyTypes:
41+
- Ingress
42+
- Egress
43+
ingress:
44+
# 修改为实际 Ingress Controller 或内网客户端所在 namespace。
45+
- from:
46+
- namespaceSelector:
47+
matchLabels:
48+
kubernetes.io/metadata.name: ingress-nginx
49+
ports:
50+
- protocol: TCP
51+
port: 3000
52+
# 如 Prometheus 不经过 Ingress 抓取 /metrics,请改成实际监控 namespace。
53+
- from:
54+
- namespaceSelector:
55+
matchLabels:
56+
kubernetes.io/metadata.name: monitoring
57+
ports:
58+
- protocol: TCP
59+
port: 3000
60+
egress:
61+
# 允许 DNS 解析。若集群 DNS namespace 不同,请调整。
62+
- to:
63+
- namespaceSelector:
64+
matchLabels:
65+
kubernetes.io/metadata.name: kube-system
66+
ports:
67+
- protocol: UDP
68+
port: 53
69+
- protocol: TCP
70+
port: 53
71+
# 修改为 OBProxy 或 OceanBase 的真实网段和端口。
72+
- to:
73+
- ipBlock:
74+
cidr: 10.0.0.0/8
75+
ports:
76+
- protocol: TCP
77+
port: 2883
78+
---
79+
apiVersion: monitoring.coreos.com/v1
80+
kind: ServiceMonitor
81+
metadata:
82+
name: ob-mcp
83+
labels:
84+
release: prometheus
85+
spec:
86+
selector:
87+
matchLabels:
88+
app: ob-mcp
89+
endpoints:
90+
- port: http
91+
path: /metrics
92+
interval: 30s
93+
bearerTokenSecret:
94+
name: ob-mcp-secrets
95+
key: ob_mcp_auth_token

deploy/kubernetes/ob-mcp.yaml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ metadata:
44
name: ob-mcp-secrets
55
type: Opaque
66
stringData:
7-
# 应用到真实集群前必须替换这两个值。
7+
# 应用到真实集群前必须替换这两个值;token 建议不少于 32 字节
88
ob_password: replace-me
9-
ob_mcp_auth_token: replace-me-long-random-token
9+
ob_mcp_auth_token: replace-me-long-random-token-32b
1010
---
1111
apiVersion: v1
1212
kind: ConfigMap
@@ -25,14 +25,20 @@ data:
2525
OB_ALLOWED_SCHEMAS: app_schema
2626
OB_ALLOW_SELECT_STAR: "false"
2727
OB_ENFORCE_READ_ONLY_TX: "true"
28+
OB_MAX_ROWS: "100"
29+
OB_MAX_RESULT_BYTES: "1048576"
30+
OB_MAX_CONCURRENT_QUERIES: "5"
2831
OB_MCP_HOST: 0.0.0.0
2932
OB_MCP_PORT: "3000"
3033
OB_MCP_PATH: /mcp
3134
OB_MCP_ALLOWED_HOSTS: your-mcp.example.com
3235
# 允许的客户端、VPN、NAT 或反向代理来源 IP/CIDR。
3336
OB_MCP_ALLOWED_IPS: 10.0.0.0/8,203.0.113.10
34-
# 仅在 Ingress 可信且客户端无法绕过时启用。
35-
OB_MCP_TRUST_PROXY: "true"
37+
# Ingress 前置时通常只信任 1 跳;不要让客户端绕过 Ingress 直连 Pod/Service。
38+
OB_MCP_TRUST_PROXY: "1"
39+
# metrics 建议鉴权;health 默认不鉴权,便于 Kubernetes probe。
40+
OB_MCP_METRICS_AUTH_REQUIRED: "true"
41+
OB_MCP_HEALTH_AUTH_REQUIRED: "false"
3642
OB_MCP_BODY_LIMIT: 256kb
3743
OB_MCP_RATE_LIMIT_WINDOW_MS: "60000"
3844
OB_MCP_RATE_LIMIT_MAX: "60"
@@ -108,6 +114,8 @@ apiVersion: v1
108114
kind: Service
109115
metadata:
110116
name: ob-mcp
117+
labels:
118+
app: ob-mcp
111119
spec:
112120
selector:
113121
app: ob-mcp

docker-compose.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,20 @@ services:
2121
OB_ALLOWED_SCHEMAS: app_schema
2222
OB_ALLOW_SELECT_STAR: "false"
2323
OB_ENFORCE_READ_ONLY_TX: "true"
24+
OB_MAX_ROWS: "100"
25+
OB_MAX_RESULT_BYTES: "1048576"
26+
OB_MAX_CONCURRENT_QUERIES: "5"
2427
OB_MCP_HOST: 0.0.0.0
2528
OB_MCP_PORT: "3000"
2629
OB_MCP_PATH: /mcp
2730
OB_MCP_AUTH_TOKEN_FILE: /run/secrets/ob_mcp_auth_token
2831
OB_MCP_ALLOWED_HOSTS: localhost,127.0.0.1,your-mcp.example.com
2932
# 允许访问 /mcp、/healthz、/metrics 的来源 IP 或 CIDR。
3033
OB_MCP_ALLOWED_IPS: 127.0.0.1,10.0.0.0/8,203.0.113.10
31-
# 仅在可信反向代理后启用,且代理必须控制转发头。
32-
OB_MCP_TRUST_PROXY: "true"
34+
# 有一层可信反向代理时使用 1;本机直连可设为 false。
35+
OB_MCP_TRUST_PROXY: "1"
36+
OB_MCP_METRICS_AUTH_REQUIRED: "true"
37+
OB_MCP_HEALTH_AUTH_REQUIRED: "false"
3338
OB_MCP_BODY_LIMIT: 256kb
3439
OB_MCP_RATE_LIMIT_WINDOW_MS: "60000"
3540
OB_MCP_RATE_LIMIT_MAX: "60"

docs/en/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ OceanBase MCP is a read-only MCP gateway for OceanBase. It is designed for OpenC
99
- Connects to OceanBase through OBProxy or a MySQL protocol endpoint.
1010
- Supports OceanBase MySQL and Oracle compatibility modes.
1111
- Exposes only read-only MCP tools.
12-
- Enforces schema/table allowlists, denied system schemas, denied sensitive columns, row limits, SQL length limits, and read-only transactions.
12+
- Enforces schema/table allowlists, denied system schemas, denied sensitive columns, hard row/response caps, SQL length limits, and read-only transactions.
1313
- Provides stdio mode for local development and HTTP mode for shared deployments.
14-
- Protects HTTP mode with bearer auth, Host allowlist, source IP allowlist, body limits, and rate limiting.
14+
- Protects HTTP mode with bearer token policies, Host allowlist, source IP allowlist, endpoint auth, body limits, and rate limiting.
15+
- Provides a read-only database account permission verification tool.
1516
- Ships Docker, Kubernetes, Nginx, systemd, and local OpenCode examples.
1617

1718
## Read-Only Contract
@@ -102,6 +103,7 @@ When binding to a non-local interface such as `0.0.0.0`, the server requires:
102103
| --- | --- |
103104
| `ob_list_profiles` | List configured profiles with secrets redacted. |
104105
| `ob_config_diagnostics` | Report deployment and policy checks. |
106+
| `ob_verify_permissions` | Check current database grants for write or administrative privileges. |
105107
| `ob_ping` | Verify the OceanBase connection. |
106108
| `ob_policy_explain` | Explain whether SQL would be allowed. |
107109
| `ob_query` | Run strict read-only SQL. |

0 commit comments

Comments
 (0)