Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 28 additions & 27 deletions website/content/docs/auth/approle.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -256,16 +256,17 @@ wrapping.
package main

import (
"context"
"fmt"
"io/ioutil"
"os"
"strings"

vault "github.com/hashicorp/vault/api"
auth "github.com/hashicorp/vault/api/auth/approle"
)

// Fetches a key-value secret (kv-v2) after authenticating via AppRole,
// an auth method used by machines that are unable to use platform-based authentication mechanisms like AWS Auth, Kubernetes Auth, etc.
// an auth method used by machines that are unable to use platform-based
// authentication mechanisms like AWS Auth, Kubernetes Auth, etc.
func getSecretWithAppRole() (string, error) {
config := vault.DefaultConfig() // modify for more granular configuration

Expand All @@ -274,40 +275,38 @@ func getSecretWithAppRole() (string, error) {
return "", fmt.Errorf("unable to initialize Vault client: %w", err)
}

// A combination of a Role ID and Secret ID is required to log in to Vault with an AppRole.
// The Secret ID is a value that needs to be protected, so instead of the app having knowledge of the secret ID directly,
// we have a trusted orchestrator (https://learn.hashicorp.com/tutorials/vault/secure-introduction?in=vault/app-integration#trusted-orchestrator)
// A combination of a Role ID and Secret ID is required to log in to Vault
// with an AppRole.
// First, let's get the role ID given to us by our Vault administrator.
roleID := os.Getenv("APPROLE_ROLE_ID")
if roleID == "" {
return "", fmt.Errorf("no role ID was provided in APPROLE_ROLE_ID env var")
}

// The Secret ID is a value that needs to be protected, so instead of the
// app having knowledge of the secret ID directly, we have a trusted orchestrator (https://learn.hashicorp.com/tutorials/vault/secure-introduction?in=vault/app-integration#trusted-orchestrator)
// give the app access to a short-lived response-wrapping token (https://www.vaultproject.io/docs/concepts/response-wrapping).
// Read more at: https://learn.hashicorp.com/tutorials/vault/approle-best-practices?in=vault/auth-methods#secretid-delivery-best-practices
secretID := &auth.SecretID{FromFile: "path/to/wrapping-token"}

wrappingToken, err := ioutil.ReadFile("path/to/wrapping-token") // placed here by a trusted orchestrator
appRoleAuth, err := auth.NewAppRoleAuth(
roleID,
secretID,
auth.WithWrappingToken(), // Only required if the secret ID is response-wrapped.
)
if err != nil {
return "", fmt.Errorf("unable to read file containing wrapping token: %w", err)
return "", fmt.Errorf("unable to initialize AppRole auth method: %w", err)
}

unwrappedToken, err := client.Logical().Unwrap(strings.TrimSuffix(string(wrappingToken), "\n"))
authInfo, err := client.Auth().Login(context.TODO(), appRoleAuth)
if err != nil {
// a good opportunity to alert, in case the one-time use wrapping token appears to have already been used
return "", fmt.Errorf("unable to unwrap token: %w", err)
return "", fmt.Errorf("unable to login to AppRole auth method: %w", err)
}
secretID := unwrappedToken.Data["secret_id"]

// the role ID given to you by your administrator
roleID := os.Getenv("APPROLE_ROLE_ID")
if roleID == "" {
return "", fmt.Errorf("no role ID was provided in APPROLE_ROLE_ID env var")
}

params := map[string]interface{}{
"role_id": roleID,
"secret_id": secretID,
}
resp, err := client.Logical().Write("auth/approle/login", params)
if err != nil {
return "", fmt.Errorf("unable to log in with approle: %w", err)
if authInfo == nil {
return "", fmt.Errorf("no auth info was returned after login")
}
client.SetToken(resp.Auth.ClientToken)

// get secret
secret, err := client.Logical().Read("kv-v2/data/creds")
if err != nil {
return "", fmt.Errorf("unable to read secret: %w", err)
Expand All @@ -318,6 +317,8 @@ func getSecretWithAppRole() (string, error) {
return "", fmt.Errorf("data type assertion failed: %T %#v", secret.Data["data"], secret.Data["data"])
}

// data map can contain more than one key-value pair,
// in this case we're just grabbing one of them
key := "password"
value, ok := data[key].(string)
if !ok {
Expand Down
43 changes: 17 additions & 26 deletions website/content/docs/auth/aws.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,7 @@ details.

## Code Example

The following code snippet uses the AWS auth method to authenticate with Vault.
The following code snippet uses the AWS (IAM) auth method to authenticate with Vault.

<CodeTabs heading="AWS auth example">

Expand All @@ -756,18 +756,18 @@ The following code snippet uses the AWS auth method to authenticate with Vault.
package main

import (
"context"
"fmt"
"os"

"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-secure-stdlib/awsutil"
vault "github.com/hashicorp/vault/api"
auth "github.com/hashicorp/vault/api/auth/aws"
)

// Fetches a key-value secret (kv-v2) after authenticating to Vault via AWS IAM,
// one of two auth methods used to authenticate with AWS (the other is EC2 auth).
// A role must first be created in Vault bound to the IAM ARN you wish to authenticate with, like so:
// vault write auth/aws/role/dev-role-iam \
// A role must first be created in Vault bound to the IAM ARN you wish to
// authenticate with, like so:
// vault write auth/aws/role/dev-role-iam \
// auth_type=iam \
// bound_iam_principal_arn="arn:aws:iam::AWS-ACCOUNT-NUMBER:role/AWS-IAM-ROLE-NAME" \
// ttl=24h
Expand All @@ -780,32 +780,22 @@ func getSecretWithAWSAuthIAM() (string, error) {
return "", fmt.Errorf("unable to initialize Vault client: %w", err)
}

logger := hclog.Default()

// If environment variables are empty, will fall back on other AWS-provided mechanisms to retrieve credentials.
creds, err := awsutil.RetrieveCreds(os.Getenv("AWS_ACCESS_KEY_ID"), os.Getenv("AWS_SECRET_ACCESS_KEY"), os.Getenv("AWS_SESSION_TOKEN"), logger)
awsAuth, err := auth.NewAWSAuth(
auth.WithRole("dev-role-iam"), // if not provided, Vault will fall back on looking for a role with the IAM role name if you're using the iam auth type, or the EC2 instance's AMI id if using the ec2 auth type
)
if err != nil {
return "", fmt.Errorf("unable to retrieve creds from STS: %w", err)
return "", fmt.Errorf("unable to initialize AWS auth method: %w", err)
}

// the optional second parameter can be used to help mitigate replay attacks,
// when the role in Vault is configured with resolve_aws_unique_ids = true: https://www.vaultproject.io/docs/auth/aws#iam-auth-method
params, err := awsutil.GenerateLoginData(creds, "Replace-With-IAM-Server-Id", os.Getenv("AWS_DEFAULT_REGION"), logger)
authInfo, err := client.Auth().Login(context.TODO(), awsAuth)
if err != nil {
return "", err
return "", fmt.Errorf("unable to login to AWS auth method: %w", err)
}
if params == nil {
return "", fmt.Errorf("got nil response from GenerateLoginData")
if authInfo == nil {
return "", fmt.Errorf("no auth info was returned after login")
}
params["role"] = "dev-role-iam" // the name of the role in Vault that was created with this IAM principal ARN bound to it

resp, err := client.Logical().Write("auth/aws/login", params)
if err != nil {
return "", fmt.Errorf("unable to log in with AWS IAM auth: %w", err)
}

client.SetToken(resp.Auth.ClientToken)

// get secret
secret, err := client.Logical().Read("kv-v2/data/creds")
if err != nil {
return "", fmt.Errorf("unable to read secret: %w", err)
Expand All @@ -816,7 +806,8 @@ func getSecretWithAWSAuthIAM() (string, error) {
return "", fmt.Errorf("data type assertion failed: %T %#v", secret.Data["data"], secret.Data["data"])
}

// data map can contain more than one key-value pair, in this case we're just grabbing one of them
// data map can contain more than one key-value pair,
// in this case we're just grabbing one of them
key := "password"
value, ok := data[key].(string)
if !ok {
Expand Down
193 changes: 36 additions & 157 deletions website/content/docs/auth/azure.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -209,35 +209,13 @@ with Vault.
package main

import (
"encoding/json"
"context"
"fmt"
"io/ioutil"
"net/http"
"net/url"

vault "github.com/hashicorp/vault/api"
auth "github.com/hashicorp/vault/api/auth/azure"
)

type responseJson struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
ExpiresIn string `json:"expires_in"`
ExpiresOn string `json:"expires_on"`
NotBefore string `json:"not_before"`
Resource string `json:"resource"`
TokenType string `json:"token_type"`
}

type metadataJson struct {
Compute computeJson `json:"compute"`
}

type computeJson struct {
VirtualMachineName string `json:"name"`
SubscriptionId string `json:"subscriptionId"`
ResourceGroupName string `json:"resourceGroupName"`
}

// Fetches a key-value secret (kv-v2) after authenticating to Vault via Azure authentication.
// This example assumes you have a configured Azure AD Application.
// Learn more about Azure authentication prerequisites: https://www.vaultproject.io/docs/auth/azure
Expand All @@ -249,147 +227,48 @@ type computeJson struct {
// bound_resource_groups=test-rg \
// ttl=24h
func getSecretWithAzureAuth() (string, error) {
config := vault.DefaultConfig() // modify for more granular configuration

client, err := vault.NewClient(config)
if err != nil {
return "", fmt.Errorf("unable to initialize Vault client: %w", err)
}

// Get AccessToken
jwtResp, err := getJWT()
if err != nil {
return "", fmt.Errorf("unable to get access token: %w", err)
}

// Get metadata for Azure instance
metadataRespJson, err := getMetadata()
if err != nil {
return "", fmt.Errorf("unable to get instance metadata: %w", err)
}

// log in to Vault's auth method with signed JWT token
params := map[string]interface{}{
"role": "dev-role-azure", // the name of the role in Vault w/ bound subscription id and resource group
"jwt": jwtResp,
"vm_name": metadataRespJson.Compute.VirtualMachineName,
"subscription_id": metadataRespJson.Compute.SubscriptionId,
"resource_group_name": metadataRespJson.Compute.ResourceGroupName,
}

// log in to Vault's Azure auth method
resp, err := client.Logical().Write("auth/azure/login", params) // confirm with your Vault administrator that "azure" is the correct mount name
if err != nil {
return "", fmt.Errorf("unable to log in with Azure auth: %w", err)
}
if resp == nil || resp.Auth == nil || resp.Auth.ClientToken == "" {
return "", fmt.Errorf("login response did not return client token")
}
config := vault.DefaultConfig() // modify for more granular configuration

client.SetToken(resp.Auth.ClientToken)

// get secret
secret, err := client.Logical().Read("kv-v2/data/creds")
if err != nil {
return "", fmt.Errorf("unable to read secret: %w", err)
}
client, err := vault.NewClient(config)
if err != nil {
return "", fmt.Errorf("unable to initialize Vault client: %w", err)
}

data, ok := secret.Data["data"].(map[string]interface{})
if !ok {
return "", fmt.Errorf("data type assertion failed: %T %#v", secret.Data["data"], secret.Data["data"])
}
azureAuth, err := auth.NewAzureAuth(
"dev-role-azure",
)
if err != nil {
return "", fmt.Errorf("unable to initialize Azure auth method: %w", err)
}

// data map can contain more than one key-value pair, in this case we're just grabbing one of them
key := "password"
value, ok := data[key].(string)
if !ok {
return "", fmt.Errorf("value type assertion failed: %T %#v", data[key], data[key])
}
authInfo, err := client.Auth().Login(context.TODO(), azureAuth)
if err != nil {
return "", fmt.Errorf("unable to login to Azure auth method: %w", err)
}
if authInfo == nil {
return "", fmt.Errorf("no auth info was returned after login")
}

return value, nil
}
// get secret
secret, err := client.Logical().Read("kv-v2/data/creds")
if err != nil {
return "", fmt.Errorf("unable to read secret: %w", err)
}

// Retrieve instance metadata from Azure
func getMetadata() (metadataJson, error) {
metadataEndpoint, err := url.Parse("http://169.254.169.254/metadata/instance")
if err != nil {
fmt.Println("Error creating URL: ", err)
return metadataJson{}, err
data, ok := secret.Data["data"].(map[string]interface{})
if !ok {
return "", fmt.Errorf("data type assertion failed: %T %#v", secret.Data["data"], secret.Data["data"])
}

metadataParameters := metadataEndpoint.Query()
metadataParameters.Add("api-version", "2018-02-01")
metadataEndpoint.RawQuery = metadataParameters.Encode()
req, err := http.NewRequest("GET", metadataEndpoint.String(), nil)
if err != nil {
return metadataJson{}, fmt.Errorf("Error creating HTTP Request: %w", err)
}
req.Header.Add("Metadata", "true")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error calling token endpoint: ", err)
return metadataJson{}, fmt.Errorf("Error calling token endpoint: %w", err)
}

responseBytes, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return metadataJson{}, fmt.Errorf("Error reading response body: %w", err)
}

// Unmarshal response body into metadata struct
var r metadataJson
err = json.Unmarshal(responseBytes, &r)
if err != nil {
return metadataJson{}, fmt.Errorf("Error unmarshalling the response: %w", err)
}

return r, nil
}
// data map can contain more than one key-value pair,
// in this case we're just grabbing one of them
key := "password"
value, ok := data[key].(string)
if !ok {
return "", fmt.Errorf("value type assertion failed: %T %#v", data[key], data[key])
}

// Retrieves an access token from Azure MSI
// Learn more here: https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token
func getJWT() (string, error) {
// Create HTTP request for a managed services for Azure resources token to access Azure Resource Manager
msiEndpoint, err := url.Parse("http://169.254.169.254/metadata/identity/oauth2/token")
if err != nil {
return "", fmt.Errorf("Error creating URL: %w", err)
}

msiParameters := msiEndpoint.Query()
msiParameters.Add("api-version", "2018-02-01")
msiParameters.Add("resource", "https://management.azure.com/")
msiEndpoint.RawQuery = msiParameters.Encode()

req, err := http.NewRequest("GET", msiEndpoint.String(), nil)
if err != nil {
return "", fmt.Errorf("Error creating HTTP request: %w", err)
}
req.Header.Add("Metadata", "true")

// Call managed services for Azure resources token endpoint
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("Error calling token endpoint: %w", err)
}

responseBytes, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return "", fmt.Errorf("Error reading response body: %w", err)
}

// Unmarshal response body into struct
var r responseJson
err = json.Unmarshal(responseBytes, &r)
if err != nil {
return "", fmt.Errorf("Error unmarshalling the response: %w", err)
}

return r.AccessToken, nil
return value, nil
}
```
</CodeBlockConfig>
Expand Down
Loading