From 1e8138f0478e92133ae5824c924324a2aa7b1ab2 Mon Sep 17 00:00:00 2001 From: Andy Lo-A-Foe Date: Fri, 10 Feb 2023 14:05:25 +0100 Subject: [PATCH] Support for private key passphprases #61 Signed-off-by: Andy Lo-A-Foe --- docs/resources/resource.md | 10 ++++++++++ ssh/resource_resource.go | 27 +++++++++++++++++++-------- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/docs/resources/resource.md b/docs/resources/resource.md index a47da26..de61406 100644 --- a/docs/resources/resource.md +++ b/docs/resources/resource.md @@ -88,6 +88,16 @@ Each `file` block can contain the following fields. Use either `content` or `sou * `owner` - (Optional, string) The file owner. Default owner the SSH user * `group` - (Optional, string) The file group. Default group is the SSH user's group +### Passphrases on SSH private keys + +The provider supports using private keys with a passphrases. However, to prevent passphrases from being stored +in Terraform state they can only be provided through the environment variables: + +| Environment | Description | +|------------------------------------|--------------------------------------------| +| SSH_PRIVATE_KEY_PASSPHRASE | Passphrase for the host target private key | +| SSH_BASTION_PRIVATE_KEY_PASSPHRASE | Passphrase for the bastion private key | + ## Attributes Reference The following attributes are exported: diff --git a/ssh/resource_resource.go b/ssh/resource_resource.go index b3724a8..3ec87d1 100644 --- a/ssh/resource_resource.go +++ b/ssh/resource_resource.go @@ -7,6 +7,7 @@ import ( "math/rand" "net/http" "os" + "strings" "time" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -294,8 +295,10 @@ func mainRun(_ context.Context, d *schema.ResourceData, m interface{}, onUpdate password := d.Get("password").(string) bastionPassword := d.Get("bastion_password").(string) privateKey := d.Get("private_key").(string) + privateKeyPassphrase, _ := schema.EnvDefaultFunc("SSH_PRIVATE_KEY_PASSPHRASE", "")() hostPrivateKey := d.Get("host_private_key").(string) bastionPrivateKey := d.Get("bastion_private_key").(string) + bastionPrivateKeyPassphrase, _ := schema.EnvDefaultFunc("SSH_BASTION_PRIVATE_KEY_PASSPHRASE", "")() host := d.Get("host").(string) timeout := d.Get("timeout").(string) retryDelay := d.Get("retry_delay").(string) @@ -331,15 +334,17 @@ func mainRun(_ context.Context, d *schema.ResourceData, m interface{}, onUpdate // Collect SSH details privateIP := host ssh := &easyssh.MakeConfig{ - User: hostUser, - Server: privateIP, - Port: port, - Key: privateKey, - Proxy: http.ProxyFromEnvironment, + User: hostUser, + Server: privateIP, + Port: port, + Key: privateKey, + Passphrase: privateKeyPassphrase.(string), + Proxy: http.ProxyFromEnvironment, Bastion: easyssh.DefaultConfig{ - User: user, - Server: bastionHost, - Port: bastionPort, + User: user, + Server: bastionHost, + Passphrase: bastionPrivateKeyPassphrase.(string), + Port: bastionPort, }, } if password != "" { @@ -442,9 +447,15 @@ func runCommands(ctx context.Context, retryDelay time.Duration, commands []strin if err == nil { break } + if strings.Contains(err.Error(), "no supported methods remain") { + diags = append(diags, diag.FromErr(err)...) + return stdout, diags, err + } + select { case <-time.After(retryDelay * time.Second): // Retry + case <-ctx.Done(): _, _ = config.Debug("error: %v\n", err) diags = append(diags, diag.Diagnostic{