diff --git a/Makefile b/Makefile deleted file mode 100644 index 103bbaf..0000000 --- a/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -VERSION = v0.1.0 -GOARCH = amd64 -linux: export GOOS=linux -linux_arm: export GOOS=linux -linux_arm: export GOARCH=arm -linux_arm: export GOARM=6 -linux_arm64: export GOOS=linux -linux_arm64: export GOARCH=arm64 -darwin: export GOOS=darwin -windows: export GOOS=windows - -.PHONY: all pb linux linux_arm linux_arm64 darwin windows clean - -all: linux linux_arm linux_arm64 darwin windows - -linux: - mkdir -p release - rm -f terraform-provider-ssh_${VERSION} release/terraform-provider-ssh_${VERSION}_${GOOS}_${GOARCH}.zip - go build -o release/terraform-provider-ssh_${VERSION} - zip release/terraform-provider-ssh_${VERSION}_${GOOS}_${GOARCH}.zip release/terraform-provider-ssh_${VERSION} - -linux_arm: - mkdir -p release - rm -f terraform-provider-ssh_${VERSION} release/terraform-provider-ssh_${VERSION}_${GOOS}_${GOARCH}.zip - go build -o release/terraform-provider-ssh_${VERSION} - zip release/terraform-provider-ssh_${VERSION}_${GOOS}_${GOARCH}.zip release/terraform-provider-ssh_${VERSION} - -linux_arm64: - mkdir -p release - rm -f terraform-provider-ssh_${VERSION} release/terraform-provider-ssh_${VERSION}_${GOOS}_${GOARCH}.zip - go build -o release/terraform-provider-ssh_${VERSION} - zip release/terraform-provider-ssh_${VERSION}_${GOOS}_${GOARCH}.zip release/terraform-provider-ssh_${VERSION} - -darwin: - mkdir -p release - rm -f terraform-provider-ssh_${VERSION} release/terraform-provider-ssh_${VERSION}_${GOOS}_${GOARCH}.zip - go build -o release/terraform-provider-ssh_${VERSION} - zip release/terraform-provider-ssh_${VERSION}_${GOOS}_${GOARCH}.zip release/terraform-provider-ssh_${VERSION} - -windows: - mkdir -p release - rm -f terraform-provider-ssh_${VERSION}.exe release/terraform-provider-ssh_${VERSION}_${GOOS}_${GOARCH}.zip - go build -o release/terraform-provider-ssh_${VERSION}.exe - zip release/terraform-provider-ssh_${VERSION}_${GOOS}_${GOARCH}.zip release/terraform-provider-ssh_${VERSION}.exe - -clean: - rm -rf release diff --git a/README.md b/README.md index d5f302c..55805d0 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,22 @@ ### terraform-provider-ssh -This provider enables SSH port forwarding in Terraform. It is intended as a +This experimental provider enables SSH port forwarding in Terraform. It is intended as a bandaid [until it is supported in Terraform itself](https://github.com/hashicorp/terraform/issues/8367). -**Note: Terraform v0.12 support is highly experimental!** - #### Example -See [main.tf](main.tf). +See [example/main.tf](main.tf). #### Installation -On Linux: - -```shell -mkdir -p terraform.d/plugins/linux_amd64 -wget https://github.com/stefansundin/terraform-provider-ssh/releases/download/v0.0.4/terraform-provider-ssh_v0.0.4_linux_amd64.zip -unzip terraform-provider-ssh_v0.0.4_linux_amd64.zip -d terraform.d/plugins/linux_amd64 -rm terraform-provider-ssh_v0.0.4_linux_amd64.zip -terraform init -``` - -On Mac: - -```shell -mkdir -p terraform.d/plugins/darwin_amd64 -wget https://github.com/stefansundin/terraform-provider-ssh/releases/download/v0.0.4/terraform-provider-ssh_v0.0.4_darwin_amd64.zip -unzip terraform-provider-ssh_v0.0.4_darwin_amd64.zip -d terraform.d/plugins/darwin_amd64 -rm terraform-provider-ssh_v0.0.4_darwin_amd64.zip -terraform init -``` - -#### Applying an output file - -Note that there is a gotcha when trying to apply a generated plan output file (see [issue #1](https://github.com/stefansundin/terraform-provider-ssh/issues/1)). In this case, the SSH tunnels will not be automatically opened. - -As a workaround, before you apply, run the companion program `terraform-open-ssh-tunnels` on the plan file first in order to reopen the SSH tunnels. [Download from the releases.](https://github.com/stefansundin/terraform-provider-ssh/releases/latest) - -Because of [this commit](https://github.com/stefansundin/terraform-provider-ssh/commit/37fa9835b75fde095c863fca89e2f28a0169919d), only the SSH agent is currently supported in this program. Let me know if you can think of a good fix for this. - -#### TODO - -- Support another hop (ProxyJump-like behavior) -- Note that the Windows binary is completely untested! - -#### Building +Provider can be automatically installed using Terraform >= 0.13 by providing a `terraform` configuration block: ``` -$ go get -u github.com/golang/protobuf/protoc-gen-go -$ make +terraform { + required_providers { + ssh = { + source = "AndrewChubatiuk/ssh" + } + } +} ``` diff --git a/main.go b/main.go index 5104250..cabe518 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,8 @@ func main() { var proto string var err error + log.SetFlags(0) + flag.IntVar(&ppid, "ppid", 0, "parent process pid") flag.StringVar(&addr, "addr", os.Getenv("TF_SSH_PROVIDER_TUNNEL_ADDR"), "set rpc server address") flag.StringVar(&proto, "proto", os.Getenv("TF_SSH_PROVIDER_TUNNEL_PROTO"), "set rpc server protocol") diff --git a/provider/data_source_ssh_tunnel.go b/provider/data_source_ssh_tunnel.go index cea3292..79bdebd 100644 --- a/provider/data_source_ssh_tunnel.go +++ b/provider/data_source_ssh_tunnel.go @@ -1,6 +1,7 @@ package provider import ( + "bufio" "context" "fmt" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -303,8 +304,18 @@ func dataSourceSSHTunnelRead(ctx context.Context, d *schema.ResourceData, m inte return diag.FromErr(err) } - go io.Copy(os.Stdout, stdout) - go io.Copy(os.Stderr, stderr) + redirectStd := func(std io.ReadCloser) { + in := bufio.NewScanner(std) + for in.Scan() { + log.Printf(in.Text()) + } + if err := in.Err(); err != nil { + log.Printf("[ERROR] %s", err) + } + } + + go redirectStd(stdout) + go redirectStd(stderr) var commandError error timer := time.NewTimer(30 * time.Second) @@ -322,7 +333,7 @@ func dataSourceSSHTunnelRead(ctx context.Context, d *schema.ResourceData, m inte }() for !tunnelServer.Ready { - log.Printf("[DEBUG] waiting for local port availability") + log.Printf("[DEBUG] Waiting for local port availability") if commandError != nil { return diag.FromErr(commandError) } @@ -331,7 +342,7 @@ func dataSourceSSHTunnelRead(ctx context.Context, d *schema.ResourceData, m inte tunnelServerInbound.Close() - log.Printf("[DEBUG] local port: %v", sshTunnel.Local.Port) + log.Printf("[DEBUG] Local port: %v", sshTunnel.Local.Port) d.Set("local", flattenEndpoint(sshTunnel.Local)) d.SetId(sshTunnel.Local.Address()) diff --git a/ssh/tunnel.go b/ssh/tunnel.go index ea0cdaa..7d879f4 100644 --- a/ssh/tunnel.go +++ b/ssh/tunnel.go @@ -88,10 +88,10 @@ func (pk SSHPrivateKey) Enabled() bool { func (pk SSHPrivateKey) Authenticate() (methods []ssh.AuthMethod, err error) { var signer ssh.Signer if pk.Password != "" { - log.Println("[DEBUG] using private key with password for authentication") + log.Println("[DEBUG] Using private key with password for authentication") signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(pk.PrivateKey), []byte(pk.Password)) } else { - log.Println("[DEBUG] using private key without password for authentication") + log.Println("[DEBUG] Using private key without password for authentication") signer, err = ssh.ParsePrivateKey([]byte(pk.PrivateKey)) } if err != nil { @@ -99,14 +99,14 @@ func (pk SSHPrivateKey) Authenticate() (methods []ssh.AuthMethod, err error) { } methods = append(methods, ssh.PublicKeys(signer)) if pk.Certificate != "" { - log.Println("[DEBUG] using client certificate for authentication") + log.Println("[DEBUG] Using client certificate for authentication") pcert, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pk.Certificate)) if err != nil { - return nil, fmt.Errorf("failed to parse certificate %q: %s", pk.Certificate, err) + return nil, fmt.Errorf("Failed to parse certificate %q: %s", pk.Certificate, err) } certSigner, err := ssh.NewCertSigner(pcert.(*ssh.Certificate), signer) if err != nil { - return nil, fmt.Errorf("failed to create cert signer %q: %s", certSigner, err) + return nil, fmt.Errorf("Failed to create cert signer %q: %s", certSigner, err) } methods = append(methods, ssh.PublicKeys(certSigner)) } @@ -134,7 +134,7 @@ func (st *SSHTunnel) Run(proto, serverAddress string, ppid int) error { gob.Register(SSHPassword{}) client, err := rpc.Dial("tcp", serverAddress) if err != nil { - log.Fatal("[ERROR] failed to connect to RPC server:\n", err) + log.Fatal("[ERROR] Failed to connect to RPC server:\n", err) } defer client.Close()