Skip to content
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

Add support for openssh comments when generating a private key #395

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/unreleased/ENHANCEMENTS-20230803-094241.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: ENHANCEMENTS
body: 'resource/tls_private_key: add openssh_comment attribute
data-source/tls_public_key: add openssh_comment attribute'
time: 2023-08-03T09:42:41.390232535Z
custom:
Issue: "395"
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
10 changes: 10 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,15 @@ func setPublicKeyAttributes(ctx context.Context, s *tfsdk.State, prvKey crypto.P
sshPubKeyBytes := ssh.MarshalAuthorizedKey(sshPubKey)

pubKeySSH = string(sshPubKeyBytes)

// Manually add the comment as MarshalAuthorizedKeys ignores it: https://github.com/golang/go/issues/46870
var comment *string
diags.Append(s.GetAttribute(ctx, path.Root("openssh_comment"), &comment)...)

if comment != nil {
pubKeySSH = fmt.Sprintf("%s %s\n", 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 @@ -60,6 +60,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 @@ -158,6 +165,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 @@ -317,12 +328,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 := ssh.MarshalPrivateKey(prvKey, "")
openSSHKeyPemBlock, err := ssh.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\n$`)),
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:`)),
),
},
},
})
}