You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为运行中的HashiCorp Vault服务更新TLS证书

问题概述

通过docker-compose部署HashiCorp Vault、Consul及自定义Golang开发的VaultManager三个独立容器,Vault使用自签名TLS证书。VaultManager启动时会生成新的.crt和.key证书并覆盖Vault的证书路径,需要实现Vault自动加载新证书的功能,无需人工干预。此前尝试在VaultManager容器内执行docker restart Vault提示docker command not found,同时希望了解如何使用SIGHUP信号实现证书重载。

附原配置:

docker-compose.yaml

version: '3.6'

services:

  vault:
   
    image: imagename
    networks:
      - nwname
    command: server -config=/vault/config/vault-config.json
    cap_add:
      - IPC_LOCK
    restart: always
    
  consul:
    
    image: imagename
    networks:
      - nwname
    command: agent -server -bind 0.0.0.0 -client 0.0.0.0 -bootstrap-expect 1 -config-file=/consul/config/config.json
    restart: always
 
  vaultmanager:
    image: imagename
    devices:
      - "/dev/tpm0:/dev/tpm0"
    networks:
      - nwname
    restart: always
   
networks:
  nwname:
    name: nwname
    driver: bridge   

vault-config.json

{
  "backend": {
    "consul": {
      "address": "consul:8500",
      "path": "vault/"
    }
  },
  "listener": {
    "tcp":{
      "address": "0.0.0.0:8200",   
      "tls_disable": 0,
      "tls_cert_file" : "/vault/config/certificate.crt",
      "tls_key_file" : "/vault/config/private.key"
    }
  },
  "ui": true
}
解决方案

核心前提:确保证书文件可被Vault容器访问

首先需要让VaultManager生成的新证书能被Vault容器读取,通过共享卷实现:

  1. 修改docker-compose.yaml,添加共享证书卷:

    version: '3.6'
    
    services:
      vault:
        image: imagename
        networks:
          - nwname
        command: server -config=/vault/config/vault-config.json
        cap_add:
          - IPC_LOCK
        restart: always
        volumes:
          - vault-certs:/vault/config/certs  # 挂载共享卷到Vault的证书目录
        container_name: vault  # 指定容器名,方便后续操作
    
      consul:
        image: imagename
        networks:
          - nwname
        command: agent -server -bind 0.0.0.0 -client 0.0.0.0 -bootstrap-expect 1 -config-file=/consul/config/config.json
        restart: always
    
      vaultmanager:
        image: imagename
        devices:
          - "/dev/tpm0:/dev/tpm0"
        networks:
          - nwname
        restart: always
        volumes:
          - vault-certs:/app/certs  # 挂载共享卷到VaultManager的证书生成目录
    
    networks:
      nwname:
        name: nwname
        driver: bridge
    
    volumes:
      vault-certs:  # 定义named volume
    
  2. 修改vault-config.json的证书路径为共享卷路径:

    {
      "backend": {
        "consul": {
          "address": "consul:8500",
          "path": "vault/"
        }
      },
      "listener": {
        "tcp":{
          "address": "0.0.0.0:8200",   
          "tls_disable": 0,
          "tls_cert_file" : "/vault/config/certs/certificate.crt",
          "tls_key_file" : "/vault/config/certs/private.key"
        }
      },
      "ui": true
    }
    
  3. 调整VaultManager的证书生成逻辑,将新证书输出到共享卷路径/app/certs(对应上面的挂载配置),并设置文件权限为644,确保Vault容器的vault用户可读取。


方案1:通过SIGHUP信号触发Vault证书重载

Vault支持接收SIGHUP信号重新加载TLS证书,无需重启服务,实现优雅重载。需要让VaultManager能给Vault容器发送该信号,有两种实现方式:

方式A:挂载Docker Socket到VaultManager容器

让VaultManager容器直接调用Docker命令发送信号:

  1. 修改docker-compose.yaml的vaultmanager服务,添加Docker相关挂载:

    vaultmanager:
      image: imagename
      devices:
        - "/dev/tpm0:/dev/tpm0"
      networks:
        - nwname
      restart: always
      volumes:
        - vault-certs:/app/certs
        - /var/run/docker.sock:/var/run/docker.sock  # 挂载Docker socket
        - /usr/bin/docker:/usr/bin/docker  # 挂载Docker二进制文件(部分环境需要)
    
  2. 在VaultManager的Golang代码中执行以下命令发送SIGHUP:

    cmd := exec.Command("docker", "kill", "-s", "HUP", "vault")
    err := cmd.Run()
    if err != nil {
        // 处理错误
    }
    

注意:挂载Docker Socket会赋予VaultManager容器主机的Root权限,仅在可信环境中使用。

方式B:通过共享PID命名空间(不推荐生产环境)

将Vault容器的PID命名空间共享到主机,VaultManager可通过进程ID发送信号:

  1. 修改docker-compose.yaml的vault服务:

    vault:
      image: imagename
      networks:
        - nwname
      command: server -config=/vault/config/vault-config.json
      cap_add:
        - IPC_LOCK
      restart: always
      volumes:
        - vault-certs:/vault/config/certs
      pid: "host"  # 共享主机PID命名空间
    
  2. 在VaultManager中通过pgrep vault找到进程ID,再发送SIGHUP:

    // 先获取Vault进程ID
    pgrepCmd := exec.Command("pgrep", "vault")
    pidOutput, err := pgrepCmd.Output()
    if err != nil {
        // 处理错误
    }
    pid := strings.TrimSpace(string(pidOutput))
    
    // 发送SIGHUP信号
    killCmd := exec.Command("kill", "-s", "HUP", pid)
    err = killCmd.Run()
    if err != nil {
        // 处理错误
    }
    

注意:共享PID命名空间会破坏容器隔离性,存在安全风险,不建议生产环境使用。


方案2:通过Vault API触发证书重载(推荐)

Vault提供了官方API端点/v1/sys/reload用于重载配置和证书,无需依赖Docker环境,更安全符合微服务隔离原则:

  1. 确保VaultManager持有具有sys/reload权限的Vault Token(可通过Root Token生成受限Token)。

  2. 在VaultManager的Golang代码中调用API:

    import (
        "fmt"
        "net/http"
        "strings"
        "crypto/tls"
    )
    
    func reloadVaultCert() error {
        client := &http.Client{
            // 若Vault使用自签名证书,需跳过证书验证(生产环境建议导入CA证书)
            Transport: &http.Transport{
                TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
            },
        }
    
        req, err := http.NewRequest("POST", "https://vault:8200/v1/sys/reload", nil)
        if err != nil {
            return err
        }
        req.Header.Set("X-Vault-Token", "your-vault-token")
    
        resp, err := client.Do(req)
        if err != nil {
            return err
        }
        defer resp.Body.Close()
    
        if resp.StatusCode != http.StatusOK {
            return fmt.Errorf("reload failed with status: %s", resp.Status)
        }
        return nil
    }
    

注意:生产环境中不要跳过证书验证,应将Vault的CA证书导入VaultManager的信任列表。


内容的提问来源于stack exchange,提问作者V. Periyasamy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 00:55:25