-
Notifications
You must be signed in to change notification settings - Fork 102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implemented tls_x509_crl resource #73
base: main
Are you sure you want to change the base?
Changes from 10 commits
a594d96
a5f59df
9e30f32
91dd560
8c4d16b
e4620b0
b7dee87
5ab6930
679c9d5
2856d4b
3b40c3e
d445365
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ services: | |
- docker | ||
language: go | ||
go: | ||
- "1.11.x" | ||
- "1.14.x" | ||
|
||
env: | ||
- GO111MODULE=on GOFLAGS=-mod=vendor | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
package tls | ||
|
||
import ( | ||
"crypto/rand" | ||
"crypto/x509/pkix" | ||
"encoding/pem" | ||
"fmt" | ||
"strings" | ||
"time" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/helper/schema" | ||
) | ||
|
||
func resourceCertRevocationList() *schema.Resource { | ||
s := map[string]*schema.Schema{ | ||
"certs_to_revoke": &schema.Schema{ | ||
Type: schema.TypeList, | ||
Elem: &schema.Schema{ | ||
Type: schema.TypeString, | ||
}, | ||
Required: true, | ||
Description: "PEM-encoded certificates to be revoked", | ||
ForceNew: true, | ||
StateFunc: func(v interface{}) string { | ||
return hashForState(strings.Join(v.([]string), ",")) | ||
}, | ||
}, | ||
"early_renewal_hours": { | ||
Type: schema.TypeInt, | ||
Optional: true, | ||
Default: 0, | ||
Description: "Number of hours before the CRL expiry when a new CRL will be generated", | ||
}, | ||
"validity_period_hours": { | ||
Type: schema.TypeInt, | ||
Required: true, | ||
Description: "Number of hours that the CRL will remain valid for", | ||
ForceNew: true, | ||
}, | ||
"validity_start_time": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
|
||
"validity_end_time": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"ready_for_renewal": { | ||
Type: schema.TypeBool, | ||
Computed: true, | ||
}, | ||
"crl_pem": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"ca_cert_pem": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
Description: "PEM-encoded CA certificate", | ||
ForceNew: true, | ||
StateFunc: func(v interface{}) string { | ||
return hashForState(v.(string)) | ||
}, | ||
}, | ||
"ca_private_key_pem": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
Description: "PEM-encoded CA private key used to sign the CRL", | ||
ForceNew: true, | ||
Sensitive: true, | ||
StateFunc: func(v interface{}) string { | ||
return hashForState(v.(string)) | ||
}, | ||
}, | ||
"ca_key_algorithm": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
Description: "Name of the algorithm used by the CA private key", | ||
ForceNew: true, | ||
}, | ||
} | ||
|
||
return &schema.Resource{ | ||
Create: CreateCRL, | ||
Delete: DeleteCRL, | ||
Read: ReadCRL, | ||
Update: UpdateCRL, | ||
CustomizeDiff: CustomizeCertificateDiff, | ||
Schema: s, | ||
} | ||
} | ||
|
||
func CreateCRL(d *schema.ResourceData, meta interface{}) error { | ||
certsToRevoke := make([]pkix.RevokedCertificate, 0) | ||
for _, vi := range d.Get("certs_to_revoke").([]interface{}) { | ||
certificate, err := decodeCertificateFromBytes([]byte(vi.(string))) | ||
if err != nil { | ||
return fmt.Errorf("failed to parse %q field: %w", "certs_to_revoke", err) | ||
} | ||
certsToRevoke = append(certsToRevoke, pkix.RevokedCertificate{ | ||
SerialNumber: certificate.SerialNumber, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
}) | ||
} | ||
caKey, err := parsePrivateKey(d, "ca_private_key_pem", "ca_key_algorithm") | ||
if err != nil { | ||
return fmt.Errorf("failed to parse %q field: %w", "ca_private_key_pem", err) | ||
} | ||
caCert, err := parseCertificate(d, "ca_cert_pem") | ||
if err != nil { | ||
return fmt.Errorf("failed to parse %q field: %w", "ca_cert_pem", err) | ||
} | ||
|
||
notBefore := now() | ||
notAfter := notBefore.Add(time.Duration(d.Get("validity_period_hours").(int)) * time.Hour) | ||
validFromBytes, err := notBefore.MarshalText() | ||
if err != nil { | ||
return err | ||
} | ||
validToBytes, err := notAfter.MarshalText() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
crlBytes, err := caCert.CreateCRL(rand.Reader, caKey, certsToRevoke, notBefore, notAfter) | ||
if err != nil { | ||
return fmt.Errorf("failed to create crl: %w", err) | ||
} | ||
|
||
crlPem := string(pem.EncodeToMemory(&pem.Block{Type: "X509 CRL", Bytes: crlBytes})) | ||
|
||
d.SetId(hashForState(string(crlBytes))) | ||
d.Set("crl_pem", crlPem) | ||
d.Set("ready_for_renewal", false) | ||
d.Set("validity_start_time", string(validFromBytes)) | ||
d.Set("validity_end_time", string(validToBytes)) | ||
return nil | ||
} | ||
|
||
func DeleteCRL(d *schema.ResourceData, meta interface{}) error { | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
func ReadCRL(d *schema.ResourceData, meta interface{}) error { | ||
return nil | ||
} | ||
|
||
func UpdateCRL(d *schema.ResourceData, meta interface{}) error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this function can be removed for now, if we don't support updates. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was kept because of the |
||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While technically correct, it would be better, if we would update the list of certificates in the CRL, rather than create the new one every time, as then the expiry date will be updated too for example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah... but I'm quite unsure on how to handle that lifecycle 🤔 , for instance the Id of the resource is calculated as a hash of the CRL PEM content (in the same way we do for locally signed certificates and other resources in this provider). When a CRL is "updated" actually what happens is that a new version of it is created, basically a new CRL itself. I was checking https://tools.ietf.org/html/rfc5280#section-5.1 trying to find a field that we can use to identify a CRL independently of the version, but I couldn't find any. But I can miss something, do you have any idea around this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I'm not sure if I understand the problem. If you set the ID only in
Create
function and then don't touch it inUpdate
, then what's the problem? I don't think there is a need to change the ID.Aside from that, I think this is completely fine for the initial implementation. It can always be improved later 👍