github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/integration/integration_vault_test.go (about)

     1  //go:build integration
     2  // +build integration
     3  
     4  // can be executed with
     5  // go test -v -tags integration -run TestVaultIntegration ./integration/...
     6  
     7  package main
     8  
     9  import (
    10  	"context"
    11  	"encoding/json"
    12  	"fmt"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/SAP/jenkins-library/pkg/vault"
    17  	"github.com/hashicorp/vault/api"
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/testcontainers/testcontainers-go"
    20  	"github.com/testcontainers/testcontainers-go/wait"
    21  )
    22  
    23  type SecretData = map[string]interface{}
    24  
    25  func TestVaultIntegrationGetSecret(t *testing.T) {
    26  	t.Parallel()
    27  	ctx := context.Background()
    28  	const testToken = "vault-token"
    29  
    30  	req := testcontainers.GenericContainerRequest{
    31  		ContainerRequest: testcontainers.ContainerRequest{
    32  			AlwaysPullImage: true,
    33  			Image:           "vault:1.4.3",
    34  			ExposedPorts:    []string{"8200/tcp"},
    35  			Env:             map[string]string{"VAULT_DEV_ROOT_TOKEN_ID": testToken},
    36  			WaitingFor:      wait.ForLog("Vault server started!").WithStartupTimeout(20 * time.Second)},
    37  
    38  		Started: true,
    39  	}
    40  
    41  	vaultContainer, err := testcontainers.GenericContainer(ctx, req)
    42  	assert.NoError(t, err)
    43  	defer vaultContainer.Terminate(ctx)
    44  
    45  	ip, err := vaultContainer.Host(ctx)
    46  	assert.NoError(t, err)
    47  	port, err := vaultContainer.MappedPort(ctx, "8200")
    48  	host := fmt.Sprintf("http://%s:%s", ip, port.Port())
    49  	config := &vault.Config{Config: &api.Config{Address: host}}
    50  	// setup vault for testing
    51  	secretData := SecretData{
    52  		"key1": "value1",
    53  		"key2": "value2",
    54  	}
    55  	setupVault(t, config, testToken, secretData)
    56  
    57  	client, err := vault.NewClient(config, testToken)
    58  	assert.NoError(t, err)
    59  	secret, err := client.GetKvSecret("secret/test")
    60  	assert.NoError(t, err)
    61  	assert.Equal(t, "value1", secret["key1"])
    62  	assert.Equal(t, "value2", secret["key2"])
    63  
    64  	secret, err = client.GetKvSecret("kv/test")
    65  	assert.NoError(t, err)
    66  	assert.Equal(t, "value1", secret["key1"])
    67  	assert.Equal(t, "value2", secret["key2"])
    68  
    69  }
    70  
    71  func TestVaultIntegrationWriteSecret(t *testing.T) {
    72  	t.Parallel()
    73  	ctx := context.Background()
    74  	const testToken = "vault-token"
    75  
    76  	req := testcontainers.GenericContainerRequest{
    77  		ContainerRequest: testcontainers.ContainerRequest{
    78  			AlwaysPullImage: true,
    79  			Image:           "vault:1.4.3",
    80  			ExposedPorts:    []string{"8200/tcp"},
    81  			Env:             map[string]string{"VAULT_DEV_ROOT_TOKEN_ID": testToken},
    82  			WaitingFor:      wait.ForLog("Vault server started!").WithStartupTimeout(20 * time.Second)},
    83  
    84  		Started: true,
    85  	}
    86  
    87  	vaultContainer, err := testcontainers.GenericContainer(ctx, req)
    88  	assert.NoError(t, err)
    89  	defer vaultContainer.Terminate(ctx)
    90  
    91  	ip, err := vaultContainer.Host(ctx)
    92  	assert.NoError(t, err)
    93  	port, err := vaultContainer.MappedPort(ctx, "8200")
    94  	host := fmt.Sprintf("http://%s:%s", ip, port.Port())
    95  	config := &vault.Config{Config: &api.Config{Address: host}}
    96  	// setup vault for testing
    97  	secretData := map[string]string{
    98  		"key1": "value1",
    99  		"key2": "value2",
   100  	}
   101  
   102  	client, err := vault.NewClient(config, testToken)
   103  	assert.NoError(t, err)
   104  
   105  	err = client.WriteKvSecret("secret/test", secretData)
   106  	assert.NoError(t, err)
   107  
   108  	secret, err := client.GetKvSecret("secret/test")
   109  	assert.NoError(t, err)
   110  	assert.Equal(t, "value1", secret["key1"])
   111  	assert.Equal(t, "value2", secret["key2"])
   112  
   113  	// enabling KV engine 1
   114  	vaultClient, err := api.NewClient(config.Config)
   115  	assert.NoError(t, err)
   116  	vaultClient.SetToken(testToken)
   117  	_, err = vaultClient.Logical().Write("sys/mounts/kv", SecretData{
   118  		"path": "kv",
   119  		"type": "kv",
   120  		"options": SecretData{
   121  			"version": "1",
   122  		},
   123  	})
   124  	assert.NoError(t, err)
   125  
   126  	err = client.WriteKvSecret("secret/test1", secretData)
   127  	assert.NoError(t, err)
   128  
   129  	secret, err = client.GetKvSecret("secret/test1")
   130  	assert.NoError(t, err)
   131  	assert.Equal(t, "value1", secret["key1"])
   132  	assert.Equal(t, "value2", secret["key2"])
   133  }
   134  
   135  func TestVaultIntegrationAppRole(t *testing.T) {
   136  	t.Parallel()
   137  	ctx := context.Background()
   138  	const testToken = "vault-token"
   139  	const appRolePath = "auth/approle/role/test"
   140  	const appRoleName = "test"
   141  
   142  	req := testcontainers.GenericContainerRequest{
   143  		ContainerRequest: testcontainers.ContainerRequest{
   144  			AlwaysPullImage: true,
   145  			Image:           "vault:1.4.3",
   146  			ExposedPorts:    []string{"8200/tcp"},
   147  			Env:             map[string]string{"VAULT_DEV_ROOT_TOKEN_ID": testToken},
   148  			WaitingFor:      wait.ForLog("Vault server started!").WithStartupTimeout(20 * time.Second)},
   149  
   150  		Started: true,
   151  	}
   152  
   153  	vaultContainer, err := testcontainers.GenericContainer(ctx, req)
   154  	assert.NoError(t, err)
   155  	defer vaultContainer.Terminate(ctx)
   156  
   157  	ip, err := vaultContainer.Host(ctx)
   158  	assert.NoError(t, err)
   159  	port, err := vaultContainer.MappedPort(ctx, "8200")
   160  	host := fmt.Sprintf("http://%s:%s", ip, port.Port())
   161  	config := &vault.Config{Config: &api.Config{Address: host}}
   162  
   163  	secretIDMetadata := map[string]interface{}{
   164  		"field1": "value1",
   165  	}
   166  
   167  	roleID, secretID := setupVaultAppRole(t, config, testToken, appRolePath, secretIDMetadata)
   168  
   169  	t.Run("Test Vault AppRole login", func(t *testing.T) {
   170  		client, err := vault.NewClientWithAppRole(config, roleID, secretID)
   171  		assert.NoError(t, err)
   172  		secret, err := client.GetSecret("auth/token/lookup-self")
   173  		meta := secret.Data["meta"].(SecretData)
   174  		assert.Equal(t, meta["field1"], "value1")
   175  		assert.Equal(t, meta["role_name"], "test")
   176  		assert.NoError(t, err)
   177  	})
   178  
   179  	t.Run("Test Vault AppRoleTTL Fetch", func(t *testing.T) {
   180  		client, err := vault.NewClient(config, testToken)
   181  		assert.NoError(t, err)
   182  		ttl, err := client.GetAppRoleSecretIDTtl(secretID, appRoleName)
   183  		assert.NoError(t, err)
   184  		assert.Equal(t, time.Duration(90*24*time.Hour), ttl.Round(time.Hour))
   185  	})
   186  
   187  	t.Run("Test Vault AppRole Rotation", func(t *testing.T) {
   188  		client, err := vault.NewClient(config, testToken)
   189  		assert.NoError(t, err)
   190  		newSecretID, err := client.GenerateNewAppRoleSecret(secretID, appRoleName)
   191  		assert.NoError(t, err)
   192  		assert.NotEmpty(t, newSecretID)
   193  		assert.NotEqual(t, secretID, newSecretID)
   194  
   195  		// verify metadata is not broken
   196  		client, err = vault.NewClientWithAppRole(config, roleID, newSecretID)
   197  		assert.NoError(t, err)
   198  		secret, err := client.GetSecret("auth/token/lookup-self")
   199  		meta := secret.Data["meta"].(SecretData)
   200  		assert.Equal(t, meta["field1"], "value1")
   201  		assert.Equal(t, meta["role_name"], "test")
   202  		assert.NoError(t, err)
   203  	})
   204  
   205  	t.Run("Test Fetching RoleName from vault", func(t *testing.T) {
   206  		client, err := vault.NewClientWithAppRole(config, roleID, secretID)
   207  		assert.NoError(t, err)
   208  		fetchedRoleName, err := client.GetAppRoleName()
   209  		assert.NoError(t, err)
   210  		assert.Equal(t, appRoleName, fetchedRoleName)
   211  	})
   212  }
   213  
   214  func TestVaultIntegrationTokenRevocation(t *testing.T) {
   215  	t.Parallel()
   216  	ctx := context.Background()
   217  	const testToken = "vault-token"
   218  	const appRolePath = "auth/approle/role/test"
   219  	const appRoleName = "test"
   220  
   221  	req := testcontainers.GenericContainerRequest{
   222  		ContainerRequest: testcontainers.ContainerRequest{
   223  			AlwaysPullImage: true,
   224  			Image:           "vault:1.4.3",
   225  			ExposedPorts:    []string{"8200/tcp"},
   226  			Env:             map[string]string{"VAULT_DEV_ROOT_TOKEN_ID": testToken},
   227  			WaitingFor:      wait.ForLog("Vault server started!").WithStartupTimeout(20 * time.Second)},
   228  
   229  		Started: true,
   230  	}
   231  
   232  	vaultContainer, err := testcontainers.GenericContainer(ctx, req)
   233  	assert.NoError(t, err)
   234  	defer vaultContainer.Terminate(ctx)
   235  
   236  	ip, err := vaultContainer.Host(ctx)
   237  	assert.NoError(t, err)
   238  	port, err := vaultContainer.MappedPort(ctx, "8200")
   239  	host := fmt.Sprintf("http://%s:%s", ip, port.Port())
   240  	config := &vault.Config{Config: &api.Config{Address: host}}
   241  
   242  	secretIDMetadata := map[string]interface{}{
   243  		"field1": "value1",
   244  	}
   245  
   246  	roleID, secretID := setupVaultAppRole(t, config, testToken, appRolePath, secretIDMetadata)
   247  
   248  	t.Run("Test Revocation works", func(t *testing.T) {
   249  		client, err := vault.NewClientWithAppRole(config, roleID, secretID)
   250  		assert.NoError(t, err)
   251  		secret, err := client.GetSecret("auth/token/lookup-self")
   252  		meta := secret.Data["meta"].(SecretData)
   253  		assert.Equal(t, meta["field1"], "value1")
   254  		assert.Equal(t, meta["role_name"], "test")
   255  		assert.NoError(t, err)
   256  
   257  		err = client.RevokeToken()
   258  		assert.NoError(t, err)
   259  
   260  		_, err = client.GetSecret("auth/token/lookup-self")
   261  		assert.Error(t, err)
   262  	})
   263  }
   264  
   265  func setupVaultAppRole(t *testing.T, config *vault.Config, token, appRolePath string, metadata map[string]interface{}) (string, string) {
   266  	t.Helper()
   267  	client, err := api.NewClient(config.Config)
   268  	assert.NoError(t, err)
   269  	client.SetToken(token)
   270  	lClient := client.Logical()
   271  
   272  	_, err = lClient.Write("sys/auth/approle", SecretData{
   273  		"type": "approle",
   274  		"config": map[string]interface{}{
   275  			"default_lease_ttl": "7776000s",
   276  			"max_lease_ttl":     "7776000s",
   277  		},
   278  	})
   279  	assert.NoError(t, err)
   280  
   281  	_, err = lClient.Write("auth/approle/role/test", SecretData{
   282  		"secret_id_ttl": 7776000,
   283  	})
   284  
   285  	assert.NoError(t, err)
   286  
   287  	metadataJson, err := json.Marshal(metadata)
   288  	assert.NoError(t, err)
   289  
   290  	res, err := lClient.Write("auth/approle/role/test/secret-id", SecretData{
   291  		"metadata": string(metadataJson),
   292  	})
   293  
   294  	assert.NoError(t, err)
   295  	secretID := res.Data["secret_id"]
   296  
   297  	res, err = lClient.Read("auth/approle/role/test/role-id")
   298  	assert.NoError(t, err)
   299  	roleID := res.Data["role_id"]
   300  
   301  	return roleID.(string), secretID.(string)
   302  }
   303  
   304  func setupVault(t *testing.T, config *vault.Config, token string, secret SecretData) {
   305  	t.Helper()
   306  	client, err := api.NewClient(config.Config)
   307  	assert.NoError(t, err)
   308  	client.SetToken(token)
   309  
   310  	_, err = client.Logical().Write("secret/data/test", SecretData{"data": secret})
   311  	assert.NoError(t, err)
   312  
   313  	// enabling KV engine 1
   314  	_, err = client.Logical().Write("sys/mounts/kv", SecretData{
   315  		"path": "kv",
   316  		"type": "kv",
   317  		"options": SecretData{
   318  			"version": "1",
   319  		},
   320  	})
   321  	assert.NoError(t, err)
   322  
   323  	_, err = client.Logical().Write("kv/test", secret)
   324  	assert.NoError(t, err)
   325  
   326  }