Skip to content

Commit f30dfa5

Browse files
authored
Merge pull request #429 from koendelaat/feature/iam_service_certificate
Added `self_managed_certificate` to IAM Service
2 parents d897c3b + 39e2d67 commit f30dfa5

File tree

3 files changed

+86
-30
lines changed

3 files changed

+86
-30
lines changed

go.mod

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ require (
3434
github.com/davecgh/go-spew v1.1.1 // indirect
3535
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a // indirect
3636
github.com/fatih/color v1.16.0 // indirect
37-
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
37+
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
3838
github.com/go-playground/locales v0.14.1 // indirect
3939
github.com/go-playground/universal-translator v0.18.1 // indirect
40-
github.com/go-playground/validator/v10 v10.17.0 // indirect
40+
github.com/go-playground/validator/v10 v10.22.0 // indirect
4141
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
4242
github.com/golang/protobuf v1.5.4 // indirect
4343
github.com/google/go-cmp v0.6.0 // indirect
@@ -63,7 +63,7 @@ require (
6363
github.com/kr/pretty v0.3.1 // indirect
6464
github.com/labstack/echo/v4 v4.9.0 // indirect
6565
github.com/labstack/gommon v0.3.1 // indirect
66-
github.com/leodido/go-urn v1.2.4 // indirect
66+
github.com/leodido/go-urn v1.4.0 // indirect
6767
github.com/mattn/go-colorable v0.1.13 // indirect
6868
github.com/mattn/go-isatty v0.0.20 // indirect
6969
github.com/mitchellh/copystructure v1.2.0 // indirect
@@ -87,7 +87,7 @@ require (
8787
golang.org/x/crypto v0.26.0 // indirect
8888
golang.org/x/mod v0.19.0 // indirect
8989
golang.org/x/net v0.25.0 // indirect
90-
golang.org/x/oauth2 v0.17.0 // indirect
90+
golang.org/x/oauth2 v0.23.0 // indirect
9191
golang.org/x/sync v0.8.0 // indirect
9292
golang.org/x/sys v0.23.0 // indirect
9393
golang.org/x/text v0.17.0 // indirect

go.sum

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,8 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF
150150
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
151151
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
152152
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
153-
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
154-
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
153+
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
154+
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
155155
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
156156
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
157157
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
@@ -216,8 +216,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
216216
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
217217
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
218218
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
219-
github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74=
220-
github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
219+
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
220+
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
221221
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
222222
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
223223
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
@@ -422,8 +422,8 @@ github.com/labstack/echo/v4 v4.9.0 h1:wPOF1CE6gvt/kmbMR4dGzWvHMPT+sAEUJOwOTtvITV
422422
github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
423423
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
424424
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
425-
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
426-
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
425+
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
426+
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
427427
github.com/loafoe/easyssh-proxy/v2 v2.0.4 h1:EzD0FWq2Ro44sDIbk95cs5oUlWFi2GKc6u/A8sxAxBM=
428428
github.com/loafoe/easyssh-proxy/v2 v2.0.4/go.mod h1:PzKetBuhTzg74M1AapAmDQAwBILp/MIiUkeUQ9bx42k=
429429
github.com/loafoe/ferrite v0.2.0 h1:4sIUGPCUpN116Nu7tZksAovFKVYBnqN4PFalk9/6r+A=
@@ -594,19 +594,14 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM
594594
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
595595
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
596596
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
597-
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
598-
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
599597
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
600598
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
601599
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
602600
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
603601
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
604602
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
605603
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
606-
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
607604
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
608-
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
609-
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
610605
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
611606
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
612607
github.com/tchap/go-patricia v0.0.0-20160729071656-dd168db6051b/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
@@ -729,8 +724,8 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
729724
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
730725
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
731726
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
732-
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
733-
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
727+
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
728+
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
734729
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
735730
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
736731
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

internal/services/iam/service/resource_iam_service.go

Lines changed: 74 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,23 @@ func ResourceIAMService() *schema.Resource {
8181
Description: "Access Token Lifetime (in seconds). Default: 1800 (30 minutes), Maximum: 2592000 (30 days).",
8282
},
8383
"self_managed_private_key": {
84-
Type: schema.TypeString,
85-
Sensitive: true,
86-
Optional: true,
87-
Description: "RSA private key in PEM format. When provided, overrides the generated certificate / private key combination of the IAM service. This gives you full control over the credentials. When not specified, a private key will be generated by IAM.",
84+
Type: schema.TypeString,
85+
Sensitive: true,
86+
Optional: true,
87+
Description: "RSA private key in PEM format. When provided, overrides the generated certificate / private key combination of the IAM service. This gives you full control over the credentials. When not specified, a private key will be generated by IAM.\n" +
88+
"Mutually exclusive with `self_managed_certificate`",
8889
},
8990
"self_managed_expires_on": {
9091
Type: schema.TypeString,
9192
Optional: true,
92-
Description: "Sets the certificate validity. When not specified, the certificate will have a validity of 5 years.",
93+
Description: "Sets the certificate validity. When not specified, the certificate will have a validity of 5 years.\nOnly applicable when `self_managed_private_key` is used",
94+
},
95+
"self_managed_certificate": {
96+
Type: schema.TypeString,
97+
Sensitive: true,
98+
Optional: true,
99+
Description: "X509 Certificate in PEM format. When provided, overrides the generated certificate / private key combination of the IAM service. This gives you full control over the credentials. When not specified, a private key will be generated by IAM.\n" +
100+
"Mutually exclusive with `self_managed_private_key`",
93101
},
94102
"private_key": {
95103
Type: schema.TypeString,
@@ -151,9 +159,16 @@ func resourceIAMServiceCreate(ctx context.Context, d *schema.ResourceData, m int
151159
defaultScopes := tools.ExpandStringList(d.Get("default_scopes").(*schema.Set).List())
152160
selfExpiresOn := d.Get("self_managed_expires_on").(string)
153161
selfPrivateKey := d.Get("self_managed_private_key").(string)
162+
selfCertificate := d.Get("self_managed_certificate").(string)
154163
if selfPrivateKey == "" && selfExpiresOn != "" {
155164
return diag.FromErr(fmt.Errorf("you cannot set 'self_managed_expires_on' value without also specifying the 'self_managed_private_key'"))
156165
}
166+
if selfCertificate != "" && selfExpiresOn != "" {
167+
return diag.FromErr(fmt.Errorf("you cannot set 'self_managed_expires_on' value in combination with 'self_managed_certificate'"))
168+
}
169+
if selfCertificate != "" && selfPrivateKey != "" {
170+
return diag.FromErr(fmt.Errorf("you cannot set 'self_managed_private_key' value in combination with 'self_managed_certificate'"))
171+
}
157172

158173
var createdService *iam.Service
159174

@@ -176,12 +191,19 @@ func resourceIAMServiceCreate(ctx context.Context, d *schema.ResourceData, m int
176191

177192
// Set certificate if set from the get go
178193
if selfPrivateKey != "" {
179-
diags = setSelfManaged(client, *createdService, d)
194+
diags = setSelfManagedPrivateKey(client, *createdService, d)
195+
if len(diags) > 0 {
196+
_, _, _ = client.Services.DeleteService(*createdService) // Cleanup
197+
return diags
198+
}
199+
}
200+
// Set certificate if set from publicKey
201+
if selfCertificate != "" {
202+
diags = setSelfManagedCertificate(client, *createdService, d)
180203
if len(diags) > 0 {
181204
_, _, _ = client.Services.DeleteService(*createdService) // Cleanup
182205
return diags
183206
}
184-
_ = d.Set("private_key", selfPrivateKey)
185207
}
186208

187209
// Set scopes and default_scopes
@@ -291,14 +313,23 @@ func resourceIAMServiceUpdate(ctx context.Context, d *schema.ResourceData, m int
291313
_, _, _ = client.Services.AddScopes(s, []string{}, toAdd)
292314
}
293315
}
294-
if d.HasChange("self_managed_expires_on") || d.HasChange("self_managed_private_key") {
295-
_, npk := d.GetChange("self_managed_private_key")
316+
if d.HasChange("self_managed_expires_on") || d.HasChange("self_managed_private_key") || d.HasChange("self_managed_certificate") {
317+
_, newPrivateKey := d.GetChange("self_managed_private_key")
318+
_, newCertificate := d.GetChange("self_managed_certificate")
296319
privateKey := d.Get("private_key").(string)
297320

298-
if npk.(string) == "" && privateKey == "" {
299-
return diag.FromErr(fmt.Errorf("you cannot revert to a server side managed private key once you set a self managed private key"))
321+
if newPrivateKey.(string) == "" && newCertificate.(string) == "" && privateKey == "" {
322+
return diag.FromErr(fmt.Errorf("you cannot revert to a server side managed private key once you set a self managed private key or certificate"))
323+
}
324+
if newCertificate.(string) != "" && newPrivateKey.(string) != "" {
325+
return diag.FromErr(fmt.Errorf("you cannot set 'self_managed_private_key' value in combination with 'self_managed_certificate'"))
326+
}
327+
if newPrivateKey.(string) != "" {
328+
diags = setSelfManagedPrivateKey(client, s, d)
329+
}
330+
if newCertificate.(string) != "" {
331+
diags = setSelfManagedCertificate(client, s, d)
300332
}
301-
diags = setSelfManaged(client, s, d)
302333
if len(diags) > 0 {
303334
return diags
304335
}
@@ -330,7 +361,7 @@ func resourceIAMServiceDelete(_ context.Context, d *schema.ResourceData, m inter
330361
return diags
331362
}
332363

333-
func setSelfManaged(client *iam.Client, service iam.Service, d *schema.ResourceData) diag.Diagnostics {
364+
func setSelfManagedPrivateKey(client *iam.Client, service iam.Service, d *schema.ResourceData) diag.Diagnostics {
334365
var diags diag.Diagnostics
335366

336367
selfPrivateKey := d.Get("self_managed_private_key").(string)
@@ -367,3 +398,33 @@ func setSelfManaged(client *iam.Client, service iam.Service, d *schema.ResourceD
367398
}
368399
return diags
369400
}
401+
402+
func setSelfManagedCertificate(client *iam.Client, service iam.Service, d *schema.ResourceData) diag.Diagnostics {
403+
var diags diag.Diagnostics
404+
405+
selfCertificate := d.Get("self_managed_certificate").(string)
406+
fixedPEM := iam.FixPEM(selfCertificate)
407+
block, _ := pem.Decode([]byte(fixedPEM))
408+
if block == nil {
409+
block, _ = pem.Decode([]byte(selfCertificate)) // Try unmodified decode
410+
if block == nil {
411+
return diag.FromErr(fmt.Errorf("error decoding 'self_managed_certificate'"))
412+
}
413+
}
414+
cert, err := x509.ParseCertificate(block.Bytes)
415+
if err != nil {
416+
return diag.FromErr(fmt.Errorf("parsing certificate: %w", err))
417+
}
418+
commonName := cert.Subject.CommonName
419+
if commonName != service.ServiceID {
420+
return diag.FromErr(fmt.Errorf("certificate subject CommonName should match `service_id`: %s != %s", commonName, service.ServiceID))
421+
}
422+
_, _, err = client.Services.UpdateServiceCertificateDER(service, block.Bytes)
423+
if err != nil {
424+
return diag.FromErr(fmt.Errorf("setting certificate: %w", err))
425+
}
426+
if fixedPEM != "" {
427+
_ = d.Set("private_key", nil)
428+
}
429+
return diags
430+
}

0 commit comments

Comments
 (0)