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 }