Skip to content

Commit

Permalink
Add support for openssh comments when generating a private key
Browse files Browse the repository at this point in the history
  • Loading branch information
F21 committed Aug 3, 2023
1 parent 73fb919 commit 24eeba8
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 2 deletions.
1 change: 1 addition & 0 deletions docs/data-sources/public_key.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ data "tls_public_key" "private_key_openssh-example" {

- `algorithm` (String) The name of the algorithm used by the given private key. Possible values are: `RSA`, `ECDSA`, `ED25519`.
- `id` (String) Unique identifier for this data source: hexadecimal representation of the SHA1 checksum of the data source.
- `openssh_comment` (String) The OpenSSH comment.
- `public_key_fingerprint_md5` (String) The fingerprint of the public key data in OpenSSH MD5 hash format, e.g. `aa:bb:cc:...`. Only available if the selected private key format is compatible, as per the rules for `public_key_openssh` and [ECDSA P224 limitations](../../docs#limitations).
- `public_key_fingerprint_sha256` (String) The fingerprint of the public key data in OpenSSH SHA256 hash format, e.g. `SHA256:...`. Only available if the selected private key format is compatible, as per the rules for `public_key_openssh` and [ECDSA P224 limitations](../../docs#limitations).
- `public_key_openssh` (String) The public key, in [OpenSSH PEM (RFC 4716)](https://datatracker.ietf.org/doc/html/rfc4716) format. This is also known as ['Authorized Keys'](https://www.ssh.com/academy/ssh/authorized_keys/openssh#format-of-the-authorized-keys-file) format. This is not populated for `ECDSA` with curve `P224`, as it is [not supported](../../docs#limitations). **NOTE**: the [underlying](https://pkg.go.dev/encoding/pem#Encode) [libraries](https://pkg.go.dev/golang.org/x/crypto/ssh#MarshalAuthorizedKey) that generate this value append a `\n` at the end of the PEM. In case this disrupts your use case, we recommend using [`trimspace()`](https://www.terraform.io/language/functions/trimspace).
Expand Down
1 change: 1 addition & 0 deletions docs/resources/private_key.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ resource "tls_private_key" "ed25519-example" {
### Optional

- `ecdsa_curve` (String) When `algorithm` is `ECDSA`, the name of the elliptic curve to use. Currently-supported values are: `P224`, `P256`, `P384`, `P521`. (default: `P224`).
- `openssh_comment` (String) Comment to add to the OpenSSH key (default: `""`).
- `rsa_bits` (Number) When `algorithm` is `RSA`, the size of the generated RSA key, in bits (default: `2048`).

### Read-Only
Expand Down
9 changes: 9 additions & 0 deletions internal/provider/common_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"crypto/x509"
"encoding/pem"
"fmt"
"strings"

"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
Expand Down Expand Up @@ -201,6 +202,14 @@ func setPublicKeyAttributes(ctx context.Context, s *tfsdk.State, prvKey crypto.P
sshPubKeyBytes := ssh.MarshalAuthorizedKey(sshPubKey)

pubKeySSH = string(sshPubKeyBytes)

var comment *string
diags.Append(s.GetAttribute(ctx, path.Root("openssh_comment"), &comment)...)

if comment != nil {
pubKeySSH = fmt.Sprintf("%s %s", strings.TrimSuffix(pubKeySSH, "\n"), *comment)
}

pubKeySSHFingerprintMD5 = ssh.FingerprintLegacyMD5(sshPubKey)
pubKeySSHFingerprintSHA256 = ssh.FingerprintSHA256(sshPubKey)
}
Expand Down
4 changes: 4 additions & 0 deletions internal/provider/data_source_public_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ func (d *publicKeyDataSource) Schema(ctx context.Context, req datasource.SchemaR
Description: "The name of the algorithm used by the given private key. " +
fmt.Sprintf("Possible values are: `%s`. ", strings.Join(supportedAlgorithmsStr(), "`, `")),
},
"openssh_comment": schema.StringAttribute{
Computed: true,
Description: "The OpenSSH comment.",
},
"public_key_pem": schema.StringAttribute{
Computed: true,
Description: "The public key, in [PEM (RFC 1421)](https://datatracker.ietf.org/doc/html/rfc1421) format. " +
Expand Down
1 change: 1 addition & 0 deletions internal/provider/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type certificateSubjectModel struct {

type privateKeyResourceModel struct {
Algorithm types.String `tfsdk:"algorithm"`
OpenSSHComment types.String `tfsdk:"openssh_comment"`
RSABits types.Int64 `tfsdk:"rsa_bits"`
ECDSACurve types.String `tfsdk:"ecdsa_curve"`
PrivateKeyPem types.String `tfsdk:"private_key_pem"`
Expand Down
14 changes: 12 additions & 2 deletions internal/provider/resource_private_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ func (r *privateKeyResource) Schema(_ context.Context, req resource.SchemaReques
},

// Optional attributes
"openssh_comment": schema.StringAttribute{
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
MarkdownDescription: "Comment to add to the OpenSSH key (default: `\"\"`).",
},
"rsa_bits": schema.Int64Attribute{
Optional: true,
Computed: true,
Expand Down Expand Up @@ -159,6 +166,10 @@ func privateKeyResourceSchemaV1() schema.Schema {
},

// Optional attributes
"openssh_comment": schema.StringAttribute{
Optional: true,
MarkdownDescription: "Comment to add to the OpenSSH key (default: `\"\"`).",
},
"rsa_bits": schema.Int64Attribute{
Optional: true,
Computed: true,
Expand Down Expand Up @@ -318,12 +329,11 @@ func (r *privateKeyResource) Create(ctx context.Context, req resource.CreateRequ
tflog.Debug(ctx, "Marshalling private key to OpenSSH PEM (if supported)")
newState.PrivateKeyOpenSSH = types.StringValue("")
if prvKeySupportsOpenSSHMarshalling(prvKey) {
openSSHKeyPemBlock, err := openssh.MarshalPrivateKey(prvKey, "")
openSSHKeyPemBlock, err := openssh.MarshalPrivateKey(prvKey, newState.OpenSSHComment.ValueString())
if err != nil {
res.Diagnostics.AddError("Unable to marshal private key into OpenSSH format", err.Error())
return
}

newState.PrivateKeyOpenSSH = types.StringValue(string(pem.EncodeToMemory(openSSHKeyPemBlock)))
}

Expand Down
26 changes: 26 additions & 0 deletions internal/provider/resource_private_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,3 +401,29 @@ func TestAccPrivateKeyED25519_UpgradeFromVersion3_4_0(t *testing.T) {
},
})
}

func TestOpenSSHComment(t *testing.T) {
r.UnitTest(t, r.TestCase{
ProtoV5ProviderFactories: protoV5ProviderFactories(),
Steps: []r.TestStep{
{
Config: `
resource "tls_private_key" "test" {
algorithm = "ED25519"
openssh_comment = "test@test"
}
`,
Check: r.ComposeAggregateTestCheckFunc(
tu.TestCheckPEMFormat("tls_private_key.test", "private_key_pem", PreamblePrivateKeyPKCS8.String()),
tu.TestCheckPEMFormat("tls_private_key.test", "public_key_pem", PreamblePublicKey.String()),
tu.TestCheckPEMFormat("tls_private_key.test", "private_key_openssh", PreamblePrivateKeyOpenSSH.String()),
tu.TestCheckPEMFormat("tls_private_key.test", "private_key_pem_pkcs8", PreamblePrivateKeyPKCS8.String()),
r.TestMatchResourceAttr("tls_private_key.test", "public_key_openssh", regexp.MustCompile(`^ssh-ed25519 `)),
r.TestMatchResourceAttr("tls_private_key.test", "public_key_openssh", regexp.MustCompile(` test@test$`)),
r.TestMatchResourceAttr("tls_private_key.test", "public_key_fingerprint_md5", regexp.MustCompile(`^([abcdef\d]{2}:){15}[abcdef\d]{2}`)),
r.TestMatchResourceAttr("tls_private_key.test", "public_key_fingerprint_sha256", regexp.MustCompile(`^SHA256:`)),
),
},
},
})
}

0 comments on commit 24eeba8

Please sign in to comment.