尝试在 HA 配置中手动解封时出现 Vault 错误:提供的服务器 ID 没有预期答案

问题描述 投票:0回答:1

我正在尝试为 Vault 设置 HA 集群,但遇到了一些无法解决的错误。我已经在网上搜索了很多并测试了各种解决方案,但我无法完成这项工作,我快疯了。

我正在尝试以最简单的方式生成 Vault 集群。一切都是手动的,没有自动加入,没有自动解封。我的测试设置是:3 个带有 Docker 引擎的虚拟机,每台机器上运行一个 Vault 实例。

我的程序是这样的:

  1. 在机器 1 中运行带有 Vault 的 docker 容器(IP:10.20.110.50)。
    vault.json
    配置本地保存在该机器中,并且本地创建
    vault-data
    卷。
  2. 初始化Vault,保存令牌和密钥。解封金库。
  3. 在机器 2 (10.20.110.100) 中运行带有 Vault 的 docker 容器并执行
    vault operator join http://10.20.110.50:8200
    ,得到连接的响应:true。
  4. 在机器 3 (10.20.110.150) 中运行带有 Vault 的 docker 容器并执行
    vault operator join http://10.20.110.50:8200
    ,得到连接的响应:true。

此时,我可以在 UI 中以及使用 list-peers 命令看到 Vault 集群。 2 和 3 实例是追随者,实例 1 是领导者。 实例 2 和实例 3 不是选民!

然后,我尝试使用Vault操作符unseal手动解封其中一个追随者实例,其中密钥是实例一生成的密钥。这里我得到错误:

Error unsealing: Error making API request.

URL: PUT http://127.0.0.1:8200/v1/sys/unseal
Code: 500. Errors:

* Error making API request.

URL: PUT http://10.20.110.50:8200/v1/sys/storage/raft/bootstrap/answer
Code: 400. Errors:

* no expected answer for the server id provided

我在leader日志中看到的也是:

2024-01-16T10:18:21.563Z [ERROR] storage.raft: failed to appendEntries to: peer="{Nonvoter 0bff0048-5c43-c93c-b8a3-443ce621bf2c 10.20.110.150:8201}" error="dial tcp 10.20.110.150:8201: connect: connection refused"

这些日志会连续打印,因为领导者正在尝试连接到对等点。 我还测试了节点之间的连接:

Ping 正常,节点可达。 所有节点之间都可以连接到端口 8200。 仅当我连接到领导者时,到端口 8201 的连接才会成功。如果我尝试连接到机器 2 和 3 的端口 8201,我会收到连接被拒绝的消息。 奇怪的是,如果我尝试从容器内连接到端口 8201(即 127.0.0.1:8201),它会起作用。但我调查过,这似乎是正常的。在地窖解封之前我会被拒绝连接。这就是为什么 Machine 1 是我唯一可以连接的机器。 我没有启用防火墙。

我的配置:

vault.json:

{
  "storage": {
    "raft": {
      "path": "/opt/vault/data/raft"
    }
  },
  "listener": {
    "tcp": {
      "address": "0.0.0.0:8200",
      "cluster_address": "0.0.0.0:8201",
      "tls_disable": true
    }
  },
  "telemetry": {
    "unauthenticated_metrics_access": true
  },
  "cluster_name": "vc",
  "api_addr": "http://{{HOST}}:8200",
  "cluster_addr": "https://{{HOST}}:8201",
  "ui": true,
  "disable_mlock": true,
  "log_level": "debug",
  "default_lease_ttl": "168h",
  "max_lease_ttl": "0h"
}

此配置会本地复制到每台服务器。 HOST 是托管 Vault 实例的计算机的 IP。因此,如果配置进入 10.20.110.50 服务器,则 HOST 值将为 10.20.110.50。

docker-compose.yaml:

version: '3.8'

services:

  vault:
    image: hashicorp/vault:1.15.2
    container_name: vault
    hostname: vault
    entrypoint: [ "vault", "server", "-config=/vault/config/vault.json" ]
    volumes:
      - vault-data:/opt/vault/data/raft
      - /home/root/vault/vault.json:/vault/config/vault.json:ro
    environment:
      - VAULT_ADDR=http://127.0.0.1:8200
    networks: 
      - vault-network  
    ports:
      - 8200:8200
      - 8201:8201
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--spider", "--tries=1", "http://127.0.0.1:8200"]
      interval: 5s
      timeout: 5s
      retries: 10
      start_period: 5s
    cap_add:
      - IPC_LOCK

networks:
  vault-network:
    name: vault-network

volumes:
  vault-data:
    name: vault-data
    driver: local

此 docker-compose 在每台服务器上本地运行,以在每台服务器中生成一个 Vault 实例。

docker hashicorp-vault
1个回答
0
投票

您看到的错误是:

* no expected answer for the server id provided

还有:

2024-01-16T10:18:21.563Z [ERROR] storage.raft: failed to appendEntries to: peer="{Nonvoter 0bff0048-5c43-c93c-b8a3-443ce621bf2c 10.20.110.150:8201}" error="dial tcp 10.20.110.150:8201: connect: connection refused"

通常与 Raft 集群成员之间的网络连接问题相关。

我的直觉是这与 Docker 网络的复杂性有关。例如,在 docker-compose 中表示主机名与您在 Vault 配置中配置的主机名不同:

<snip>
    container_name: vault
    hostname: vault
<snip>

此外,作为一个简短的说明,您是正确的,使用像

8201
这样的实用程序测试集群端口 (
nc
) 上的连接将无法工作,直到集群未密封并积极侦听该端口。

我会再看看您如何在容器级别配置主机名,并确保它与您在 Vault 配置中设置

api_addr
cluster_addr
值的方式一致。

最后,我将与您分享我创建的一个脚本,该脚本使用在单个计算机上运行的 Docker 创建一个 2 节点 Raft 集群(因此端口已稍微更新以避免端口冲突)。请注意,这确实使用企业 Vault,因此您可能需要稍微更改脚本以使其适用于非企业。

# Create a Docker network so our containers can chat
docker network create vault-net

# Spin up 2 Vault servers in Docker
# Note that each set of server params maps to a variable, e.g.
#  vault-1 -> $server
#  8200    -> $api_port
#  8201    -> $cluster_port
for server_params in "vault-1,8200,8201" "vault-2,8210,8211"
do
  IFS="," read server api_port cluster_port <<< "$server_params"
  # Delete the server so we can recreate it
  docker rm -f $server
  # Delete the config directory so we can recreate it
  rm -rf $server
  # Create a directory where we will store our config and raft files
  mkdir -p $server/config $server/file
  # Write our license out
  echo $VAULT_LICENSE > $server/config/license.hclic
  # Create a config file
  tee $server/config/vault.hcl << EOF
ui            = true
license_path  = "/vault/config/license.hclic"
listener "tcp" {
  address     = "0.0.0.0:$api_port"
  tls_disable = "true"
}
storage "raft" {
  path = "/vault/file"
  node_id = "$server"
}
api_addr     = "http://$server:$api_port"
cluster_addr = "https://$server:$cluster_port"
EOF

  docker run \
    --cap-add=IPC_LOCK \
    -e VAULT_ADDR='http://0.0.0.0:$api_port' \
    -p $api_port:$api_port \
    --name $server \
    --detach \
    --network vault-net \
    -v `pwd`/$server/config:/vault/config \
    -v `pwd`/$server/file:/vault/file \
    hashicorp/vault-enterprise server
done

sleep 15

# Initialize our leader node
export VAULT_ADDR=http://localhost:8200
vault operator init \
  -key-shares=1 \
  -key-threshold=1 \
  -format=json > init.json

# Unseal our leader node
export ROOT_TOKEN=$(cat init.json | jq -r .root_token)
export UNSEAL_KEY=$(cat init.json | jq -r '.unseal_keys_b64[0]')
vault operator unseal $UNSEAL_KEY
vault login $ROOT_TOKEN

# Unseal our follower node
export VAULT_ADDR=http://localhost:8210
vault operator raft join http://vault-1:8200
vault operator unseal $UNSEAL_KEY
sleep 2

# Check our cluster membership
vault operator raft list-peers

请注意我的每个容器如何具有唯一的名称,并且我在配置

api_addr
cluster_addr
配置值时使用该名称。

© www.soinside.com 2019 - 2024. All rights reserved.