golang使用Spiffe JWT与Hashicorp Vault实现无密码认证的插件库Spiffe-Vault

Golang使用Spiffe JWT与Hashicorp Vault实现无密码认证的插件库Spiffe-Vault

概述

SPIFFE Vault是一个将SPIFFE SVID认证与Hashicorp Vault集成的工具,用于获取VAULT_TOKEN而不需要提供传统密码或密钥。

使用场景

  1. 从Hashicorp Vault读取秘密:无需提供传统认证密钥,而是使用SPIFFE SVID进行身份验证
  2. 无秘密/无密钥代码签名:利用Hashicorp Vault Transit引擎作为软件定义的HSM,解决本地机器上存储签名密钥的问题

构建

编译

make build

Docker镜像构建

使用默认DOCKER_HOST构建镜像:

make image

使用特定DOCKER_HOST构建镜像(例如colima):

DOCKER_HOST=unix:///Users/marco/.colima/default/docker.sock make image

使用示例

基础用法

# 设置Vault地址
export VAULT_ADDR=http://localhost:8200

# 使用spiffe-vault进行认证
bin/spiffe-vault auth -role my-role

# 输出会提示设置VAULT_TOKEN环境变量
# Export following environment variable to authenticate to Hashicorp Vault
export VAULT_TOKEN=s.IK1LBrCGXFQDAgawmhNLbcDH

高级用法

Bash

# 设置Vault地址
export VAULT_ADDR=http://localhost:8200

# 将认证输出保存到临时文件并执行
echo "$(bin/spiffe-vault auth -role my-role)" > /tmp/spiffe-vault
source /tmp/spiffe-vault

# 获取秘密
vault kv get secrets/my-key

# 查看令牌信息
vault token lookup

# 续订令牌
vault token renew

# 使用Transit引擎签名
vault write transit/sign/my-key input="$(echo stuff | base64)"

# 验证签名
vault write transit/verify/my-key input="$(echo stuff | base64)" signature=vault:v1:MEUCIFAWmHPyLJ6V0mjMgqr5UnV40bkCEUEGqApcYI54VAPIAiEAqyG2VkFc2wpYs/n47mK4vgfTVbXjWJzMM7Fxr/bR7LE=

Zsh

# 设置Vault地址
export VAULT_ADDR=http://localhost:8200

# 直接执行认证输出
source <(bin/spiffe-vault auth -role my-role)

# 获取秘密
vault kv get secrets/my-key

# 查看令牌信息
vault token lookup

# 续订令牌
vault token renew

# 使用Transit引擎签名
vault write transit/sign/my-key input="$(echo stuff | base64)"

# 验证签名
vault write transit/verify/my-key input="$(echo stuff | base64)" signature=vault:v1:MEUCIFAWmHPyLJ6V0mjMgqr5UnV40bkCEUEGqApcYI54VAPIAiEAqyG2VkFc2wpYs/n47mK4vgfTVbXjWJzMM7Fxr/bR7LE=

完整Golang示例

package main

import (
	"fmt"
	"log"
	"os"

	"github.com/hashicorp/vault/api"
	"github.com/philips-labs/spiffe-vault/pkg/auth"
)

func main() {
	// 设置Vault地址
	vaultAddr := "http://localhost:8200"
	if addr := os.Getenv("VAULT_ADDR"); addr != "" {
		vaultAddr = addr
	}

	// 创建Vault客户端配置
	config := &api.Config{
		Address: vaultAddr,
	}

	// 创建Vault客户端
	client, err := api.NewClient(config)
	if err != nil {
		log.Fatalf("Failed to create Vault client: %v", err)
	}

	// 使用SPIFFE认证
	spiffeAuth := &auth.Spiffe{
		Role: "my-role", // 设置Vault角色
	}

	// 登录Vault
	secret, err := client.Logical().Write("auth/jwt/login", spiffeAuth.LoginData())
	if err != nil {
		log.Fatalf("Failed to login to Vault: %v", err)
	}

	// 设置令牌
	client.SetToken(secret.Auth.ClientToken)

	// 获取秘密示例
	secret, err = client.Logical().Read("secret/data/my-key")
	if err != nil {
		log.Fatalf("Failed to read secret: %v", err)
	}

	// 打印秘密
	fmt.Printf("Secret data: %v\n", secret.Data)

	// 使用Transit引擎签名示例
	signResult, err := client.Logical().Write("transit/sign/my-key", map[string]interface{}{
		"input": "dGVzdA==", // "test"的base64编码
	})
	if err != nil {
		log.Fatalf("Failed to sign data: %v", err)
	}

	fmt.Printf("Signature: %v\n", signResult.Data["signature"])
}

更多关于golang使用Spiffe JWT与Hashicorp Vault实现无密码认证的插件库Spiffe-Vault的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang使用Spiffe JWT与Hashicorp Vault实现无密码认证的插件库Spiffe-Vault的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用SPIFFE JWT与HashiCorp Vault实现无密码认证

SPIFFE (Secure Production Identity Framework For Everyone) 和 HashiCorp Vault 结合可以实现强大的无密码认证系统。下面我将介绍如何使用SPIFFE JWT与Vault实现这一目标。

核心概念

  1. SPIFFE JWT:SPIFFE标准定义的JWT格式的身份令牌
  2. Vault JWT Auth方法:Vault支持JWT认证的后端
  3. SPIRE:SPIFFE参考实现,用于颁发工作负载身份

实现步骤

1. 配置SPIRE服务器

首先需要部署SPIRE服务器来颁发SPIFFE JWT令牌。

// 示例SPIRE客户端代码,获取JWT令牌
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/spiffe/go-spiffe/v2/workloadapi"
)

func fetchJWTToken() (string, error) {
	// 连接到SPIRE Agent的Workload API
	client, err := workloadapi.New(context.Background(), 
		workloadapi.WithAddr("unix:///tmp/spire-agent/public/api.sock"))
	if err != nil {
		return "", fmt.Errorf("无法连接到Workload API: %v", err)
	}
	defer client.Close()

	// 获取JWT令牌
	jwt, err := client.FetchJWTSVID(context.Background(), 
		workloadapi.JWTSVIDRequest{
			Audience: []string{"vault.example.com"},
		})
	if err != nil {
		return "", fmt.Errorf("获取JWT SVID失败: %v", err)
	}

	return jwt.Token, nil
}

func main() {
	token, err := fetchJWTToken()
	if err != nil {
		log.Fatalf("获取JWT令牌失败: %v", err)
	}
	
	fmt.Printf("获取的JWT令牌: %s\n", token)
}

2. 配置Vault JWT认证方法

在Vault中启用并配置JWT认证方法:

vault auth enable jwt

vault write auth/jwt/config \
    jwt_validation_pubkeys=@spire-jwt.pem \
    bound_issuer="spiffe://example.org"

3. 创建Vault角色

vault write auth/jwt/role/demo \
    role_type="jwt" \
    bound_audiences="vault.example.com" \
    user_claim="sub" \
    bound_subject="spiffe://example.org/ns/default/sa/default" \
    policies="default" \
    ttl=1h

4. Go客户端认证代码

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
)

type vaultLoginRequest struct {
	Role string `json:"role"`
	JWT  string `json:"jwt"`
}

type vaultLoginResponse struct {
	Auth struct {
		ClientToken string `json:"client_token"`
	} `json:"auth"`
}

func loginToVault(jwtToken string) (string, error) {
	url := "http://vault-server:8200/v1/auth/jwt/login"
	
	reqBody, err := json.Marshal(vaultLoginRequest{
		Role: "demo",
		JWT:  jwtToken,
	})
	if err != nil {
		return "", fmt.Errorf("编码请求体失败: %v", err)
	}

	resp, err := http.Post(url, "application/json", bytes.NewBuffer(reqBody))
	if err != nil {
		return "", fmt.Errorf("Vault登录请求失败: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("Vault返回非200状态码: %d", resp.StatusCode)
	}

	var loginResp vaultLoginResponse
	if err := json.NewDecoder(resp.Body).Decode(&loginResp); err != nil {
		return "", fmt.Errorf("解析Vault响应失败: %v", err)
	}

	return loginResp.Auth.ClientToken, nil
}

func main() {
	// 获取SPIFFE JWT令牌
	jwtToken, err := fetchJWTToken()
	if err != nil {
		log.Fatalf("获取JWT令牌失败: %v", err)
	}

	// 使用JWT登录Vault
	vaultToken, err := loginToVault(jwtToken)
	if err != nil {
		log.Fatalf("Vault登录失败: %v", err)
	}

	fmt.Printf("成功获取Vault令牌: %s\n", vaultToken)
}

安全注意事项

  1. 令牌有效期:确保JWT令牌有合理的短有效期
  2. 绑定约束:在Vault角色中配置严格的绑定约束(bound_subject, bound_audiences等)
  3. 传输安全:始终使用HTTPS/TLS保护通信
  4. 权限最小化:遵循最小权限原则分配Vault策略

扩展功能

  1. 动态秘密:结合Vault的动态秘密功能,实现自动化的秘密管理
  2. SPIRE代理:在生产环境中使用SPIRE代理来简化工作负载注册
  3. 审计日志:启用Vault审计日志记录所有认证事件

这种基于SPIFFE和Vault的无密码认证方案特别适合云原生环境,能够为微服务提供安全、可扩展的身份认证机制。

回到顶部