使用vault API包的身份验证方法

huangapple go评论72阅读模式
英文:

Authentication methods using the vault API package

问题

我正在尝试使用 Vault Golang Package 来通过 API 进行身份验证。

我创建了一个新的客户端,然后可以设置我的令牌:

client, err := api.NewClient(&api.Config{Address: vaultAddr, HttpClient: httpClient})

if err != nil {
  return nil, errors.Wrap(err, "could not create vault client")
}

client.SetToken(token)

这很好,但我想使用其他身份验证方法(如 LDAP、Userpass)来对 API 进行身份验证。

这种方式是否可行?我如何使用 API 检索令牌?

我猜我可以使用 net/http 发起 API 调用来检索令牌,但是否有其他身份验证方法呢?

英文:

I am trying to use the Vault Golang Package to authenticate using the API.

I created a new client, and then can set my token:

client, err := api.NewClient(&api.Config{Address: vaultAddr, HttpClient: httpClient})

 if err != nil {
   return nil, errors.Wrap(err, "could not create vault client")
 }

client.SetToken(token)

That's great and all, but I want to auth against the API using one of the other auth methods, (LDAP, Userpass etc)

Is this even possible? How can I retrieve a token using the API?

I guess I could just use net/http to retrieve the token using an API call, but is there any method to actually auth in another way?

答案1

得分: 8

我最终成功解决了这个问题。虽然不是完全明显,但是很有道理。

Vault有一个通用的写入方法,用于写入数据。你可以利用这个方法通过构建URL并向该端点发送PUT请求来执行API登录。

代码看起来有点像这样:

// 创建一个Vault客户端
client, err := api.NewClient(&api.Config{Address: url, HttpClient: httpClient})
if err != nil {
    panic(err)
}

// 传递密码
options := map[string]interface{}{
    "password": password,
}

// 登录路径
// 这是可配置的,可以将userpass更改为ldap等
path := fmt.Sprintf("auth/userpass/login/%s", username)

// 发送PUT请求以获取令牌
secret, err := client.Logical().Write(path, options)
英文:

I managed to figure this out, eventually. It's not totally obvious, but makes sense.

Vault has a generic write method it uses to write data. You can utilise this to perform a login with the API by simply building the URL and sending a PUT request to that endpoint

It looks a bit like this:

// create a vault client
client, err := api.NewClient(&api.Config{Address: url, HttpClient: httpClient})
if err != nil {
	panic(err)
}

// to pass the password
options := map[string]interface{}{
   "password": password,    	
}

// the login path
// this is configurable, change userpass to ldap etc
path := fmt.Sprintf("auth/userpass/login/%s", username)

// PUT call to get a token
secret, err := client.Logical().Write(path, options)

答案2

得分: 1

在没有足够声望的情况下,可以在接受的答案中添加额外的提示,而不是评论。可以从授权响应中提取令牌,类似于以下方式:

client.SetToken(secret.Auth.ClientToken)

相关的接口文档在这里:

https://godoc.org/github.com/hashicorp/vault/api#Secret

https://godoc.org/github.com/hashicorp/vault/api#SecretAuth

英文:

Without having enough reputation adding an extra hint here instead of a comment to the accepted answer. The token can be extracted from the auth response something like this:

client.SetToken(secret.Auth.ClientToken)

The relevant interfaces are documented here:

https://godoc.org/github.com/hashicorp/vault/api#Secret

https://godoc.org/github.com/hashicorp/vault/api#SecretAuth

答案3

得分: 1

我写了一个 GitHub Gist,其中包含一个函数,用于使用 AWS IAM 角色对你的 Go 程序进行身份验证。这是链接

注意:仅通过身份验证到 Vault 只会得到一个令牌。如果 AWS IAM 角色允许,该令牌稍后可以用于读取密钥。

你需要创建一个 AWS STS 请求,从中提取一些信息,然后将其发送到 Vault。

以下是来自 GitHub Gist 的函数。它将使用 AWS IAM 角色和一些 Vault 环境变量(如 VAULT_ADDR)作为输入。这里还有一些其他环境变量,Vault Golang 库可能会读取这些变量。该函数将返回一个经过身份验证的 Vault *api.Client、令牌和身份验证请求的响应。

它基于这个 GitHub 项目

package vault

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"

	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/hashicorp/vault/api"
)

// AWSLogin 将创建一个 Vault 客户端,通过 AWS 角色进行登录,并返回一个有效的 Vault 令牌和客户端,可用于获取密钥。
// authProvider 可能是 "aws"。它是这些文档中描述的 "Path" 列:
// https://www.vaultproject.io/api/auth/aws#login。
// serverID 是一个可选值,将放置在 HTTP 请求的 X-Vault-AWS-IAM-Server-ID 标头中。
// role 是一个 AWS IAM 角色。它需要能够从 Vault 中读取密钥。
func AWSLogin(authProvider, serverID, role string) (client *api.Client, token string, secret *api.Secret, err error) {

	// 创建 Vault 客户端。
	//
	// 配置是通过上游 vault 包从环境变量中获取的。相关的环境变量如 VAULT_ADDR 和 VAULT_SKIP_VERIFY 是相关的。不应该需要 VAULT_TOKEN 环境变量。
	// https://www.vaultproject.io/docs/commands#environment-variables
	if client, err = api.NewClient(nil); err != nil {
		return nil, "", nil, fmt.Errorf("failed to create Vault client: %w", err)
	}

	// 获取 AWS 会话。
	var sess *session.Session
	if sess, err = session.NewSession(); err != nil {
		return nil, "", nil, fmt.Errorf("failed to create AWS session: %w", err)
	}

	// 创建一个用于与 AWS 令牌服务通信的 Go 结构。
	tokenService := sts.New(sess)

	// 创建一个请求,该请求将请求当前主机的身份。
	request, _ := tokenService.GetCallerIdentityRequest(&sts.GetCallerIdentityInput{})

	// 如果存在,添加一个服务器 ID IAM 标头。
	if serverID != "" {
		request.HTTPRequest.Header.Add("X-Vault-AWS-IAM-Server-ID", serverID)
	}

	// 对 AWS 令牌服务的请求进行签名。
	if err = request.Sign(); err != nil {
		return nil, "", nil, fmt.Errorf("failed to sign AWS identity request: %w", err)
	}

	// 将标头进行 JSON 编组。
	var headers []byte
	if headers, err = json.Marshal(request.HTTPRequest.Header); err != nil {
		return nil, "", nil, fmt.Errorf("failed to JSON marshal HTTP headers for AWS identity request: %w", err)
	}

	// 读取请求的正文。
	var body []byte
	if body, err = ioutil.ReadAll(request.HTTPRequest.Body); err != nil {
		return nil, "", nil, fmt.Errorf("failed to JSON marshal HTTP body for AWS identity request: %w", err)
	}

	// 创建要写入 Vault 的数据。
	data := make(map[string]interface{})
	data["iam_http_request_method"] = request.HTTPRequest.Method
	data["iam_request_url"] = base64.StdEncoding.EncodeToString([]byte(request.HTTPRequest.URL.String()))
	data["iam_request_headers"] = base64.StdEncoding.EncodeToString(headers)
	data["iam_request_body"] = base64.StdEncoding.EncodeToString(body)
	data["role"] = role

	// 创建要写入 Vault 的路径。
	//
	// authProvider 是此文档中引用的值的路径列。可能是 "aws"。
	// https://www.vaultproject.io/api/auth/aws#login
	path := fmt.Sprintf("auth/%s/login", authProvider)

	// 将 AWS 令牌服务请求写入 Vault。
	if secret, err = client.Logical().Write(path, data); err != nil {
		return nil, "", nil, fmt.Errorf("failed to write data to Vault to get token: %w", err)
	}
	if secret == nil {
		return nil, "", nil, fmt.Errorf("failed to get token from Vault: %w", ErrSecret)
	}

	// 从响应中获取 Vault 令牌。
	if token, err = secret.TokenID(); err != nil {
		return nil, "", nil, fmt.Errorf("failed to get token from Vault response: %w", err)
	}

	// 将刚刚接收到的令牌设置为客户端的令牌。
	client.SetToken(token)

	return client, token, secret, nil
}

希望对你有帮助!

英文:

I wrote a GitHub gist that contains a function to authenticate your Go program using an AWS IAM role. Here's a link.

Note: authenticating to Vault only gets you a token. The token may later be used to read secrets, if the AWS IAM role allows.

You'll need to create an AWS STS request, extract some information from it, then send it to Vault instead.

Here's the function from the GitHub gist. It will consume an AWS IAM role and some Vault environment variables such as VAULT_ADDR. Here's some other environment variables that the Vault Golang library may read. It produces an authenticated Vault *api.Client, token, and response from the authentication request.

It's based off of this GitHub project.

package vault

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"

	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/sts"
	"github.com/hashicorp/vault/api"
)

// AWSLogin will create a Vault client, login via an AWS role, and return a valid Vault token and client that can be
// used to get secrets.
// The authProvider is likely "aws". It's the "Path" column as described in these docs:
// https://www.vaultproject.io/api/auth/aws#login.
// The serverID is an optional value to be placed in the X-Vault-AWS-IAM-Server-ID header of the HTTP request.
// The role is an AWS IAM role. It needs to be able to read secrets from Vault.
func AWSLogin(authProvider, serverID, role string) (client *api.Client, token string, secret *api.Secret, err error) {

	// Create the Vault client.
	//
	// Configuration is gathered from environment variables by upstream vault package. Environment variables like
	// VAULT_ADDR and VAULT_SKIP_VERIFY are relevant. The VAULT_TOKEN environment variable shouldn't be needed.
	// https://www.vaultproject.io/docs/commands#environment-variables
	if client, err = api.NewClient(nil); err != nil {
		return nil, "", nil, fmt.Errorf("failed to create Vault client: %w", err)
	}

	// Acquire an AWS session.
	var sess *session.Session
	if sess, err = session.NewSession(); err != nil {
		return nil, "", nil, fmt.Errorf("failed to create AWS session: %w", err)
	}

	// Create a Go structure to talk to the AWS token service.
	tokenService := sts.New(sess)

	// Create a request to the token service that will ask for the current host's identity.
	request, _ := tokenService.GetCallerIdentityRequest(&sts.GetCallerIdentityInput{})

	// Add an server ID IAM header, if present.
	if serverID != "" {
		request.HTTPRequest.Header.Add("X-Vault-AWS-IAM-Server-ID", serverID)
	}

	// Sign the request to the AWS token service.
	if err = request.Sign(); err != nil {
		return nil, "", nil, fmt.Errorf("failed to sign AWS identity request: %w", err)
	}

	// JSON marshal the headers.
	var headers []byte
	if headers, err = json.Marshal(request.HTTPRequest.Header); err != nil {
		return nil, "", nil, fmt.Errorf("failed to JSON marshal HTTP headers for AWS identity request: %w", err)
	}

	// Read the body of the request.
	var body []byte
	if body, err = ioutil.ReadAll(request.HTTPRequest.Body); err != nil {
		return nil, "", nil, fmt.Errorf("failed to JSON marshal HTTP body for AWS identity request: %w", err)
	}

	// Create the data to write to Vault.
	data := make(map[string]interface{})
	data["iam_http_request_method"] = request.HTTPRequest.Method
	data["iam_request_url"] = base64.StdEncoding.EncodeToString([]byte(request.HTTPRequest.URL.String()))
	data["iam_request_headers"] = base64.StdEncoding.EncodeToString(headers)
	data["iam_request_body"] = base64.StdEncoding.EncodeToString(body)
	data["role"] = role

	// Create the path to write to for Vault.
	//
	// The authProvider is the value referenced in the "Path" column in this documentation. It's likely "aws".
	// https://www.vaultproject.io/api/auth/aws#login
	path := fmt.Sprintf("auth/%s/login", authProvider)

	// Write the AWS token service request to Vault.
	if secret, err = client.Logical().Write(path, data); err != nil {
		return nil, "", nil, fmt.Errorf("failed to write data to Vault to get token: %w", err)
	}
	if secret == nil {
		return nil, "", nil, fmt.Errorf("failed to get token from Vault: %w", ErrSecret)
	}

	// Get the Vault token from the response.
	if token, err = secret.TokenID(); err != nil {
		return nil, "", nil, fmt.Errorf("failed to get token from Vault response: %w", err)
	}

	// Set the token for the client as the one it just received.
	client.SetToken(token)

	return client, token, secret, nil
}

答案4

得分: -1

api包中有一个标准的AuthMethod接口。

type AuthMethod interface {
	Login(ctx context.Context, client *Client) (*Secret, error)
}

可以使用任何登录方法来实现这个接口。下面是一个基本的用户名/密码登录的示例:

type CredentialsAuthMethod struct {
	username string
	password string
}

func (m *CredentialsAuthMethod) Login(ctx context.Context, client *api.Client) (*api.Secret, error) {
	options := map[string]interface{}{
		"password": m.password,
	}
	path := fmt.Sprintf("auth/userpass/login/%s", m.username)

	secret, err := client.Logical().WriteWithContext(ctx, path, options)
	if err != nil {
		return nil, errorx.EnhanceStackTrace(err, "failed to authorize in vault with credentials")
	}

	return secret, nil
}

一旦实现了新的AuthMethod,就可以用它来为客户端获取令牌:

client, err := api.NewClient(&api.Config{Address: "https://vault.local"})
if err != nil {
	return nil, errorx.EnhanceStackTrace(err, "failed to initialize vault client")
}

secret, err := client.Auth().Login(context.Background(), &CredentialsAuthMethod{
	username: c.username,
	password: c.password,
})
if err != nil {
	return nil, errorx.EnhanceStackTrace(err, "failed to login in vault")
}

token := secret.Auth.ClientToken
client.SetToken(token)
英文:

There's a standard AuthMethod interface in api package.

type AuthMethod interface {
Login(ctx context.Context, client *Client) (*Secret, error)
}

One may implement this interface with any login method. Here's an example of basic username/password login

type CredentialsAuthMethod struct {
username string
password string
}
func (m *CredentialsAuthMethod) Login(ctx context.Context, client *api.Client) (*api.Secret, error) {
options := map[string]any{
"password": m.password,
}
path := fmt.Sprintf("auth/userpass/login/%s", m.username)
secret, err := client.Logical().WriteWithContext(ctx, path, options)
if err != nil {
return nil, errorx.EnhanceStackTrace(err, "failed to authorize in vault with credentials")
}
return secret, nil
}

Once new AuthMethod implemented, it can be used to get a token for the client:

	client, err := api.NewClient(&api.Config{Address: "https://vault.local"})
if err != nil {
return nil, errorx.EnhanceStackTrace(err, "failed to initialize vault client")
}
secret, err := client.Auth().Login(context.Background(), &CredentialsAuthMethod{
username: c.username,
password: c.password,
})
if err != nil {
return nil, errorx.EnhanceStackTrace(err, "failed to login in vault")
}
token := secret.Auth.ClientToken
client.SetToken(token)

huangapple
  • 本文由 发表于 2017年3月1日 02:08:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/42515866.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定