返回

GCP私有实例集成diagrams.net与PlantUML认证问题解决

java

搞定 GCP 私有实例上 diagrams.net 与 PlantUML 集成的认证问题

直接说问题,在 Google Cloud Platform (GCP) 上部署 diagrams.net 和 PlantUML,想把它们集成起来,结果认证这块儿卡住了。俩容器(应用容器和 PlantUML 容器),认证不通过,集成用不了。

问题根源在哪?

这事儿多半和下面几个原因有关:

  1. 跨域资源共享 (CORS) 限制: diagrams.net(JavaScript 客户端)尝试直接向 PlantUML 容器发送请求。因为两个服务不在同一个“域”(通常是不同的端口或不同的主机名),浏览器出于安全考虑,会阻止这种跨域请求。

  2. 服务间认证缺失/错误: GCP 上的服务通常需要正确的身份验证和授权才能互相通信。 如果 diagrams.net 应用没有以 PlantUML 服务可识别和接受的方式进行身份验证,请求就会被拒绝。

  3. 网络配置问题 :例如防火墙规则或者内部网络的访问问题等,都可能会导致容器直接互相通信失败

  4. PlantUML 容器配置问题 : PlantUML 容器可能没有正确配置来处理来自外部的请求,或者没有启用必要的认证机制。

解决方案

试试下面几个法子,总有一个适合你:

1. 使用反向代理 (推荐)

这是最常用的解决方案。在俩容器前面架一个反向代理(比如 Nginx 或 Envoy),让所有请求都通过代理来转发。

  • 原理: 反向代理作为“中间人”,接收来自 diagrams.net 的请求,处理认证,然后将请求转发给 PlantUML 容器,再把 PlantUML 的响应返回给 diagrams.net。这样就绕过了浏览器的 CORS 限制,也简化了服务间认证。

  • 步骤 (以 Nginx 为例):

    1. 创建 Nginx 配置文件:

      server {
          listen 80;
          server_name your-diagrams-net-domain.com; # 替换成你的域名
      
          location / {
              proxy_pass http://diagrams-net-app:8080; # 假设 diagrams.net 应用在 8080 端口
              proxy_set_header Host $host;
              proxy_set_header X-Real-IP $remote_addr;
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
              proxy_set_header X-Forwarded-Proto $scheme;
          }
      
          location /plantuml/ {
              proxy_pass http://plantuml-container:8080; # 假设 PlantUML 容器在 8080 端口
              proxy_set_header Host $host;
          #以下两项配置可以在反向代理上添加对所有 /plantuml/ 流量的基本HTTP认证支持。如果您的plantuml-container有特别安全需要,这可能是一种额外的保护手段
          #    auth_basic "Restricted Access"; #可选,开启 basic 认证
          #    auth_basic_user_file /etc/nginx/.htpasswd; # 可选, 用户密码文件,需自己用htpasswd生成.
      
              # 为GCP 服务间认证准备header (根据实际情况调整)
              proxy_set_header Authorization "Bearer $(gcloud auth print-identity-token)";
           }
      }
      
    2. 部署 Nginx 容器: 将 Nginx 配置文件包含在你的 Docker 镜像中,或者作为 ConfigMap 挂载到 Kubernetes pod 中。

    3. 更新 diagrams.net 配置: 修改 diagrams.net 的 PlantUML 服务器地址为 /plantuml/ (即 Nginx 代理的路径)。

  • 安全建议:

    • 如果需要更高级的认证,可以考虑在 Nginx 中配置 OAuth 2.0 或 OpenID Connect。
    • 确保 Nginx 与 PlantUML 容器之间的通信使用安全的网络(比如 VPC 内部网络)。
    • gcloud auth print-identity-token 生成的token有时效,此方法仅限开发测试环境。
  • 进阶
    可以在Nginx中启用缓存,对于经常使用的PlantUML图形请求做加速。

2. 在GCP使用 服务账户进行服务间验证

如果你不想引入额外的反向代理,可以使用GCP提供的内置服务账户进行服务间验证

  • 原理 diagrams.net应用所在的容器,应使用某个具有特定权限的服务账户。通过此账户,拿到token,从而被授权能调用plantuml的API。

  • 步骤
    1. 创建plantuml服务的专用服务账户
    创建一个用于plantuml的服务账户,给此服务账户授权,使它能处理请求
    2. diagram.net app的服务账户
    给予diagrams.net应用服务账户权限。让其能调用Cloud Run Admin API 或者类似能认证其他服务的权限(这取决于plantuml运行的位置,是cloud run还是compute engine)
    3. diagrams.net 应用容器中获取token
    可以通过google-auth-library 或者 直接使用metadata server 去拿到token,并且,在向plantuml容器发起请求的时候把token加入请求头里
    4. 在 PlantUML 服务中验证令牌: PlantUML 服务需要能够验证接收到的身份令牌。

  • 示例代码 (Node.js, 获取身份令牌):

    const {GoogleAuth} = require('google-auth-library');
    
    async function getAuthToken() {
        const auth = new GoogleAuth();
        const url = 'http://plantuml-container:8080';//plantuml 内部地址
        const client = await auth.getIdTokenClient(url); //这里已经实现了目标受众群体的token生成过程。
        const res = await client.request({url}); //用拿到的客户端身份,随便发送一次请求。仅仅为了拿token演示。
       console.log("===============");
       console.info(res.config.headers.Authorization)
    }
    
    getAuthToken();
    
  • 进阶
    客户端(这里指diagrams.net )的Token自动刷新逻辑,必须有效处理,因为token会过期. google-auth-library会帮助你搞定这个,只要保持client 对象存活.

3. 调整 CORS 配置(如果 PlantUML 容器允许)

如果 PlantUML 容器的 Web 服务器允许修改 CORS 配置,你可以直接设置允许 diagrams.net 的来源进行跨域访问。 但大多数生产环境下,不建议你这样做

  • 原理: PlantUML 服务器返回的 HTTP 响应头中包含 Access-Control-Allow-Origin 字段,指定允许访问的域名。

  • 操作 (取决于 PlantUML 的 Web 服务器,以 Tomcat 为例):
    web.xml内添加filter.

    <filter>
      <filter-name>CorsFilter</filter-name>
      <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
      <init-param>
        <param-name>cors.allowed.origins</param-name>
        <param-value>https://your-diagrams-net-domain.com</param-value>  <!-- 允许的 diagrams.net 域名 -->
      </init-param>
      <init-param>
        <param-name>cors.allowed.methods</param-name>
        <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
      </init-param>
       <init-param>
    	<param-name>cors.allowed.headers</param-name>
    	<param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization</param-value>
       </init-param>
    </filter>
    <filter-mapping>
      <filter-name>CorsFilter</filter-name>
      <url-pattern>/*</url-pattern> <!--应用到所有url -->
    </filter-mapping>
    
    
  • 风险点: 不要把 cors.allowed.origins 设置为 *,除非你的 PlantUML 服务是完全公开的。这会允许任何网站跨域访问你的 PlantUML 服务。

  • 局限: 如果plantuml根本不提供给你修改CORS的手段, 那这个方法就无法使用了

4. 如果上述都受限:JSONP(不推荐,仅作备选)

JSONP 是一种古老的绕过跨域限制的方法,但现在已经很少使用。它利用了 <script> 标签可以跨域加载资源的特性。

  • 原理: PlantUML 服务需要支持 JSONP 格式的响应。 diagrams.net 通过动态创建一个 <script> 标签,src 属性指向 PlantUML 服务的一个特殊 URL,并携带一个回调函数名。PlantUML 服务返回一段 JavaScript 代码,这段代码会调用指定的回调函数,并将数据作为参数传递。
  • 非常不推荐: JSONP 只支持 GET 请求,而且安全性较差,容易受到 XSS 攻击。不到万不得已,别用。这里不提供操作步骤了.
    ###总结:
    按照实际项目环境,老老实实走前两种方式(反向代理和GCP服务账户认证)。如果还有困难, 请详细检查配置,一步一步debug,或考虑GCP支持服务.