Skip to content

Commit b95a31b

Browse files
authored
Fix metadata handling and updates (#2)
* Fix metadata handling and updates * Fix comment * Change k8s auth to have the same synthax as the official provider * Update docs
1 parent a77a19e commit b95a31b

File tree

6 files changed

+100
-96
lines changed

6 files changed

+100
-96
lines changed

docs/index.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
---
22
# generated by https://github.com/hashicorp/terraform-plugin-docs
3-
page_title: "vaultsecret Provider"
3+
page_title: "vaultprov Provider"
44
subcategory: ""
55
description: |-
66
A provider to generate secrets and have them stored directly into Vault without any copy in the Terraform State. Once the secret has been generated, its value only exist into Vault. Terraform will not track any change in the value, only in the secret attribute (metadata, etc.`).
77
---
88

9-
# vaultsecret Provider
9+
# vaultprov Provider
1010

1111
A provider to generate secrets and have them stored directly into Vault without any copy in the Terraform State. Once the secret has been generated, its value only exist into Vault. Terraform will not track any change in the value, only in the secret attribute (`metadata`, etc.`).
1212

1313
## Example Usage
1414

1515
```terraform
16-
provider "vaultsecret" {
16+
provider "vaultprov" {
1717
address = "http://some.vault:8200"
1818
auth = {
1919
path = "auth/kubernetes/login"

docs/resources/random.md renamed to docs/resources/random_secret.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
---
22
# generated by https://github.com/hashicorp/terraform-plugin-docs
3-
page_title: "vaultsecret_random Resource - vaultsecret"
3+
page_title: "vaultprov_random_secret Resource - vaultprov"
44
subcategory: ""
55
description: |-
66
A cryptographic randomly generated secret stored as bytes in a Vault secret. The resulting Vault secret will have a custom metadata secret_type with the value random_secret and a custom metadata secret_length with the same value as the length attribute.
77
---
88

9-
# vaultsecret_random (Resource)
9+
# vaultprov_random_secret (Resource)
1010

1111
A cryptographic randomly generated secret stored as bytes in a Vault secret. The resulting Vault secret will have a custom metadata `secret_type` with the value `random_secret` and a custom metadata `secret_length` with the same value as the `length` attribute.
1212

1313
## Example Usage
1414

1515
```terraform
16-
resource "vaultsecret_random" "example" {
17-
path = "/secrets/foo/bar"
18-
length = 32
19-
metadata = {
20-
owner = "my_team"
21-
some-key = "some-value"
22-
}
16+
resource "vaultprov_random_secret" "example" {
17+
path = "/secret/foo/bar"
18+
length = 32
19+
metadata = {
20+
owner = "my_team"
21+
some-key = "some-value"
22+
}
2323
}
2424
```
2525

internal/provider/provider.go

Lines changed: 67 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@ import (
44
"context"
55
"fmt"
66
vaultapi "github.com/blablacar/terraform-provider-vaultprov/internal/vault"
7-
87
"github.com/hashicorp/terraform-plugin-framework/datasource"
98
"github.com/hashicorp/terraform-plugin-framework/provider"
109
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
1110
"github.com/hashicorp/terraform-plugin-framework/resource"
1211
"github.com/hashicorp/terraform-plugin-framework/types"
1312
"github.com/hashicorp/terraform-plugin-log/tflog"
1413
vault "github.com/hashicorp/vault/api"
15-
auth "github.com/hashicorp/vault/api/auth/kubernetes"
1614
)
1715

1816
const providerName = "vaultprov"
@@ -25,9 +23,9 @@ type vaultSecretProvider struct {
2523

2624
// Provider schema struct
2725
type providerModel struct {
28-
Address types.String `tfsdk:"address"`
29-
Token types.String `tfsdk:"token"`
30-
Auth providerAuthModel `tfsdk:"auth"`
26+
Address types.String `tfsdk:"address"`
27+
Token types.String `tfsdk:"token"`
28+
Auth *providerAuthModel `tfsdk:"auth"`
3129
}
3230

3331
type providerAuthModel struct {
@@ -42,75 +40,6 @@ func New() func() provider.Provider {
4240
}
4341
}
4442

45-
func (p *vaultSecretProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
46-
var config providerModel
47-
diags := req.Config.Get(ctx, &config)
48-
resp.Diagnostics.Append(diags...)
49-
if resp.Diagnostics.HasError() {
50-
return
51-
}
52-
53-
vaultConf := vault.DefaultConfig()
54-
vaultConf.Address = config.Address.ValueString()
55-
56-
client, err := vault.NewClient(vaultConf)
57-
if err != nil {
58-
tflog.Error(ctx, "Error creating vault client", map[string]interface{}{"address": vaultConf.Address, "error": err})
59-
resp.Diagnostics.AddError(
60-
"Error configuring provider",
61-
fmt.Sprintf("Can't create vault client for %s: %s", vaultConf.Address, err.Error()),
62-
)
63-
return
64-
}
65-
66-
if !config.Token.IsNull() {
67-
client.SetToken(config.Token.ValueString()) //DEBUG
68-
tflog.Warn(ctx, "Auth token provided. Ignoring other auth parameters. FOR DEBUG ONLY, DO NOT USE IN PRODUCTION.", nil)
69-
70-
p.vaultApi = vaultapi.NewVaultApi(client)
71-
resp.ResourceData = p.vaultApi
72-
return
73-
}
74-
75-
role := config.Auth.Role.ValueString()
76-
jwt := config.Auth.Jwt.ValueString()
77-
78-
k8sAuth, err := auth.NewKubernetesAuth(
79-
role,
80-
auth.WithServiceAccountToken(jwt),
81-
)
82-
if err != nil {
83-
tflog.Error(ctx, "Error initializing Vault kubernetes auth method", map[string]interface{}{"role": role, "jwt": jwt, "error": err})
84-
resp.Diagnostics.AddError(
85-
"Error configuring provider",
86-
fmt.Sprintf("Unable to initialize Vault Kubernetes authentication with role %s and JWT %s: %s", role, jwt, err.Error()),
87-
)
88-
return
89-
}
90-
91-
authInfo, err := client.Auth().Login(context.Background(), k8sAuth)
92-
if err != nil {
93-
tflog.Error(ctx, "Error login in with Vault kubernetes auth method", map[string]interface{}{"role": role, "jwt": jwt, "error": err})
94-
resp.Diagnostics.AddError(
95-
"Error configuring provider",
96-
fmt.Sprintf("Unable to log in with Vault Kubernetes authentication with role %s and JWT %s: %s", role, jwt, err.Error()),
97-
)
98-
return
99-
}
100-
101-
if authInfo == nil {
102-
tflog.Error(ctx, "Not auth info returned for kubernetes auth", map[string]interface{}{"role": role, "jwt": jwt})
103-
resp.Diagnostics.AddError(
104-
"Error configuring provider",
105-
fmt.Sprintf("Not auth info returned for kubernetes auth with role %s and JWT %s: %s", role, jwt, err.Error()),
106-
)
107-
return
108-
}
109-
110-
p.vaultApi = vaultapi.NewVaultApi(client)
111-
resp.ResourceData = p.vaultApi
112-
}
113-
11443
func (p *vaultSecretProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) {
11544
resp.TypeName = providerName
11645
}
@@ -157,3 +86,67 @@ func (p *vaultSecretProvider) Schema(ctx context.Context, req provider.SchemaReq
15786
MarkdownDescription: "A provider to generate secrets and have them stored directly into Vault without any copy in the Terraform State. Once the secret has been generated, its value only exist into Vault. Terraform will not track any change in the value, only in the secret attribute (`metadata`, etc.`).",
15887
}
15988
}
89+
90+
func (p *vaultSecretProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
91+
var config providerModel
92+
diags := req.Config.Get(ctx, &config)
93+
resp.Diagnostics.Append(diags...)
94+
if resp.Diagnostics.HasError() {
95+
return
96+
}
97+
98+
vaultConf := vault.DefaultConfig()
99+
vaultConf.Address = config.Address.ValueString()
100+
101+
client, err := vault.NewClient(vaultConf)
102+
if err != nil {
103+
tflog.Error(ctx, "Error creating vault client", map[string]interface{}{"address": vaultConf.Address, "error": err})
104+
resp.Diagnostics.AddError(
105+
"Error configuring provider",
106+
fmt.Sprintf("Can't create vault client for %s: %s", vaultConf.Address, err.Error()),
107+
)
108+
return
109+
}
110+
111+
authConf := config.Auth
112+
if !config.Token.IsNull() {
113+
client.SetToken(config.Token.ValueString()) //DEBUG
114+
tflog.Warn(ctx, "Auth token provided. Ignoring other auth parameters. FOR DEBUG ONLY, DO NOT USE IN PRODUCTION.", nil)
115+
} else if authConf != nil {
116+
err = setupVaultClientAuth(client, authConf)
117+
if err != nil {
118+
tflog.Error(ctx, "Error while configuring vault client auth", map[string]interface{}{"address": vaultConf.Address, "error": err})
119+
resp.Diagnostics.AddError(
120+
"Error configuring provider",
121+
fmt.Sprintf("Can't create vault client for %s: %s", vaultConf.Address, err.Error()),
122+
)
123+
}
124+
}
125+
126+
p.vaultApi = vaultapi.NewVaultApi(client)
127+
resp.ResourceData = p.vaultApi
128+
}
129+
130+
func setupVaultClientAuth(client *vault.Client, authConf *providerAuthModel) error {
131+
role := authConf.Role.ValueString()
132+
jwt := authConf.Jwt.ValueString()
133+
134+
//We don't use auth.NewKubernetesAuth in order to have the same input parameters as the official Vault provider
135+
// (otherwise 'path' would have to be replaced by 'mount')
136+
loginData := map[string]interface{}{
137+
"jwt": jwt,
138+
"role": role,
139+
}
140+
141+
path := authConf.Path.ValueString()
142+
authInfo, err := client.Logical().Write(path, loginData)
143+
if err != nil {
144+
return fmt.Errorf("unable to log in with Vault Kubernetes authentication with role %s and JWT %s: %w", role, jwt, err)
145+
}
146+
147+
if authInfo == nil {
148+
return fmt.Errorf("not auth info returned for kubernetes auth with role %s and JWT %s: %s", role, jwt, err)
149+
}
150+
151+
return nil
152+
}

internal/provider/resource_random.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1919
"github.com/hashicorp/terraform-plugin-framework/types"
2020
_ "github.com/hashicorp/terraform-plugin-go/tftypes"
21+
"strconv"
2122
)
2223

2324
const (
@@ -137,7 +138,7 @@ func (s *RandomSecret) Create(ctx context.Context, request resource.CreateReques
137138
customMetadata[SecretTypeMetadata] = secretType
138139
customMetadata[SecretLengthMetadata] = fmt.Sprintf("%d", secretLength)
139140

140-
data := map[string]string{
141+
data := map[string]interface{}{
141142
SecretDataKey: base64.StdEncoding.EncodeToString(key),
142143
}
143144

@@ -184,6 +185,18 @@ func (s *RandomSecret) Read(ctx context.Context, req resource.ReadRequest, resp
184185
if len(customMetadata) > 0 {
185186
additionalMetadata := make(map[string]attr.Value)
186187
for k, v := range customMetadata {
188+
if k == SecretTypeMetadata {
189+
continue
190+
}
191+
if k == SecretLengthMetadata {
192+
len, err := strconv.Atoi(v)
193+
if err != nil {
194+
resp.Diagnostics.AddError("Error reading secret length: "+v, fmt.Sprintf("Error while reading secret %s: %s", secretPath, err.Error()))
195+
return
196+
}
197+
data.Length = types.Int64Value(int64(len))
198+
continue
199+
}
187200
additionalMetadata[k] = types.StringValue(v)
188201
}
189202
data.Metadata, _ = types.MapValue(types.StringType, additionalMetadata)
@@ -211,7 +224,7 @@ func (s *RandomSecret) Update(ctx context.Context, req resource.UpdateRequest, r
211224
return
212225
}
213226

214-
// Check that path, length and type haven't changed
227+
// Check that path, hasn't changed
215228
if state.Path.ValueString() != plan.Path.ValueString() {
216229
resp.Diagnostics.AddError("Error updating random key", fmt.Sprintf("Invalid path change. Random key can't have their path changed (old: %s, new: %s). Only metadata changes are authorized. Delete and recreate the key instead.", state.Path.ValueString(), plan.Path.ValueString()))
217230
return
@@ -224,6 +237,9 @@ func (s *RandomSecret) Update(ctx context.Context, req resource.UpdateRequest, r
224237
metadata[k] = v.(types.String).ValueString()
225238
}
226239

240+
metadata[SecretTypeMetadata] = RandomSecretType
241+
metadata[SecretLengthMetadata] = plan.Length.String()
242+
227243
err := s.vaultApi.UpdateSecretMetadata(secretPath, metadata)
228244
if err != nil {
229245
resp.Diagnostics.AddError("Error updating secret", fmt.Sprintf("Error while updating metadata for secret %s: %s", secretPath, err.Error()))

internal/vault/vault_api.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const (
1414

1515
type Secret struct {
1616
Path string
17-
Data map[string]string
17+
Data map[string]interface{}
1818
Metadata map[string]string
1919
}
2020

@@ -119,7 +119,7 @@ func (c *VaultApi) ReadSecret(secretPath string) (*Secret, error) {
119119
customMetadata[k] = v.(string)
120120
}
121121

122-
data := secret.Data[SecretDataField].(map[string]string)
122+
data := secret.Data[SecretDataField].(map[string]interface{})
123123

124124
vaultSecret := &Secret{
125125
Path: secretPath,
@@ -147,13 +147,8 @@ func (c *VaultApi) UpdateSecretMetadata(secretPath string, metadata map[string]s
147147
return fmt.Errorf("missing custom metadata")
148148
}
149149

150-
currentMetadata := secretMetadata.Data[SecretCustomDataField].(map[string]interface{})
151-
152150
// Update secret's metadata from plan (only metadata can be changed)
153151
updatedMetadata := make(map[string]string)
154-
for k, v := range currentMetadata {
155-
updatedMetadata[k] = v.(string)
156-
}
157152

158153
for k, v := range metadata {
159154
updatedMetadata[k] = v

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
)
1010

1111
// Provider documentation generation.
12-
//go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs generate --provider-name vaultsecret
12+
//go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs generate --provider-name vaultprov
1313

1414
const providerUrl = "registry.terraform.io/blablacar/vaultprov"
1515

0 commit comments

Comments
 (0)