-
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
Implement support for x509 extra extensions #76
base: main
Are you sure you want to change the base?
Changes from all commits
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 |
---|---|---|
|
@@ -3,10 +3,15 @@ package tls | |
import ( | ||
"crypto/sha1" | ||
"crypto/x509/pkix" | ||
"encoding/asn1" | ||
"encoding/hex" | ||
"fmt" | ||
"regexp" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/helper/schema" | ||
"github.com/hashicorp/terraform-plugin-sdk/helper/validation" | ||
"github.com/hashicorp/terraform-plugin-sdk/terraform" | ||
) | ||
|
||
|
@@ -122,3 +127,109 @@ var nameSchema *schema.Resource = &schema.Resource{ | |
}, | ||
}, | ||
} | ||
|
||
func extensionFromResourceData(extensionMap map[string]interface{}) (*pkix.Extension, error) { | ||
result := &pkix.Extension{} | ||
|
||
// Handle the oid | ||
oidParts := strings.Split(extensionMap["oid"].(string), ".") | ||
oid := make(asn1.ObjectIdentifier, len(oidParts), len(oidParts)) | ||
for i, part := range oidParts { | ||
intPart, err := strconv.Atoi(part) | ||
if err != nil { | ||
return nil, fmt.Errorf("Invalid Extension OID %#v", extensionMap["oid"].(string)) | ||
} | ||
oid[i] = intPart | ||
} | ||
result.Id = oid | ||
|
||
// Handle the critical flag | ||
result.Critical = extensionMap["critical"].(bool) | ||
|
||
// Handle the value | ||
valueField := extensionMap["type"].(string) + "_value" | ||
switch valueField { | ||
case "integer_value": | ||
value := extensionMap["integer_value"].(int) | ||
marshalledValue, err := asn1.Marshal(value) | ||
if err != nil { | ||
return nil, fmt.Errorf("Failed to marshal value %#v", value) | ||
} | ||
result.Value = marshalledValue | ||
case "boolean_value": | ||
value := extensionMap["boolean_value"].(bool) | ||
marshalledValue, err := asn1.Marshal(value) | ||
if err != nil { | ||
return nil, fmt.Errorf("Failed to marshal value %#v", value) | ||
} | ||
result.Value = marshalledValue | ||
case "printable_string_value": | ||
value := extensionMap["printable_string_value"].(string) | ||
marshalledValue, err := asn1.MarshalWithParams(value, "printable") | ||
if err != nil { | ||
return nil, fmt.Errorf("Failed to marshal value %#v", value) | ||
} | ||
result.Value = marshalledValue | ||
case "utf8_string_value": | ||
value := extensionMap["utf8_string_value"].(string) | ||
marshalledValue, err := asn1.MarshalWithParams(value, "utf8") | ||
if err != nil { | ||
return nil, fmt.Errorf("Failed to marshal value %#v", value) | ||
} | ||
result.Value = marshalledValue | ||
} | ||
|
||
return result, nil | ||
} | ||
|
||
var supportedExtensionTypes = []string{"integer", "boolean", "printable_string", "utf8_string"} | ||
|
||
var extensionSchema *schema.Resource = &schema.Resource{ | ||
Schema: map[string]*schema.Schema{ | ||
"oid": { | ||
Type: schema.TypeString, | ||
Description: "The oid of the extension in dot format", | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validation.StringMatch(regexp.MustCompile(`\d+(\.\d+)*`), "Extension oid must use the dot notation"), | ||
}, | ||
"critical": { | ||
Type: schema.TypeBool, | ||
Description: "Whether the extension should be treated as critical", | ||
Optional: true, | ||
Default: false, | ||
ForceNew: true, | ||
}, | ||
"integer_value": { | ||
Type: schema.TypeInt, | ||
Description: "Fill this field if the extension value should be encoded as an ASN.1 INTEGER", | ||
Optional: true, | ||
ForceNew: true, | ||
}, | ||
"boolean_value": { | ||
Type: schema.TypeBool, | ||
Description: "Fill this field if the extension value should be encoded as an ASN.1 BOOLEAN", | ||
Optional: true, | ||
ForceNew: true, | ||
}, | ||
"printable_string_value": { | ||
Type: schema.TypeString, | ||
Description: "Fill this field if the extension value should be encoded as an ASN.1 PrintableString", | ||
Optional: true, | ||
ForceNew: true, | ||
}, | ||
"utf8_string_value": { | ||
Type: schema.TypeString, | ||
Description: "Fill this field if the extension value should be encoded as an ASN.1 UTF8String", | ||
Optional: true, | ||
ForceNew: true, | ||
}, | ||
"type": { | ||
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. If you have all the 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. This won't work unfortunately. And I can't call |
||
Type: schema.TypeString, | ||
Description: "The type of the value. One of: " + strings.Join(supportedExtensionTypes[:], ", "), | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validation.StringInSlice(supportedExtensionTypes[:], false), | ||
}, | ||
}, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,8 @@ package tls | |
|
||
import ( | ||
"crypto/x509" | ||
"crypto/x509/pkix" | ||
"encoding/asn1" | ||
"encoding/pem" | ||
"fmt" | ||
"strings" | ||
|
@@ -43,7 +45,25 @@ func TestCertRequest(t *testing.T) { | |
uris = [ | ||
"spiffe://example-trust-domain/workload", | ||
"spiffe://example-trust-domain/workload2", | ||
] | ||
] | ||
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. formatting seems weird here |
||
|
||
extension { | ||
oid = "1.2.3.4.5.6" | ||
integer_value = 17 | ||
type = "integer" | ||
} | ||
|
||
extension { | ||
oid = "1.3.5.7.9.11" | ||
printable_string_value = "hello" | ||
type = "printable_string" | ||
} | ||
|
||
extension { | ||
oid = "0.2.4.6.8.10" | ||
utf8_string_value = "š™£" | ||
type = "utf8_string" | ||
} | ||
|
||
key_algorithm = "RSA" | ||
private_key_pem = <<EOT | ||
|
@@ -128,6 +148,35 @@ EOT | |
return fmt.Errorf("incorrect URI 1: expected %v, got %v", expected, got) | ||
} | ||
|
||
// Convert the Extension structure to one that is easier to query | ||
var extra_extensions = map[string]*pkix.Extension{} | ||
for index, value := range csr.Extensions { | ||
extra_extensions[value.Id.String()] = &csr.Extensions[index] | ||
} | ||
if extra_extensions["1.2.3.4.5.6"] == nil { | ||
return fmt.Errorf("Extension 1.2.3.4.5.6 was not added") | ||
} | ||
var integer_extension_value int | ||
asn1.Unmarshal(extra_extensions["1.2.3.4.5.6"].Value, &integer_extension_value) | ||
if expected, got := 17, integer_extension_value; got != expected { | ||
return fmt.Errorf("Incorrect value for extension 1.2.3.4.5.6: expected %v, got %v", expected, got) | ||
} | ||
if extra_extensions["1.3.5.7.9.11"] == nil { | ||
return fmt.Errorf("Extension 1.3.5.7.9.11 was not added") | ||
} | ||
var printable_string_extension_value string | ||
asn1.Unmarshal(extra_extensions["1.3.5.7.9.11"].Value, &printable_string_extension_value) | ||
if expected, got := "hello", printable_string_extension_value; got != expected { | ||
return fmt.Errorf("Incorrect value for extension 1.3.5.7.9.11: expected %v, got %v", expected, got) | ||
} | ||
if extra_extensions["0.2.4.6.8.10"] == nil { | ||
return fmt.Errorf("Extension 0.2.4.6.8.10 was not added") | ||
} | ||
var utf8_string_extension_value string | ||
asn1.Unmarshal(extra_extensions["0.2.4.6.8.10"].Value, &utf8_string_extension_value) | ||
if expected, got := "š™£", utf8_string_extension_value; got != expected { | ||
return fmt.Errorf("Incorrect value for extension 0.2.4.6.8.10: expected %v, got %v", expected, got) | ||
} | ||
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.
You can use the
ConflictsWith
attribute to make sure that only one is set: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.
Awesome! Didn't know about this