Skip to content

Commit

Permalink
Initial version.
Browse files Browse the repository at this point in the history
  • Loading branch information
stefansundin committed Nov 25, 2017
0 parents commit 759655c
Show file tree
Hide file tree
Showing 8 changed files with 218 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
crash.log
terraform-provider-*
terraform.d
.terraform
*.tfstate
*.tfstate.backup
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[submodule "vendor/github.com/hashicorp/terraform"]
path = vendor/github.com/hashicorp/terraform
url = https://github.com/hashicorp/terraform.git
[submodule "vendor/golang.org/x/crypto"]
path = vendor/golang.org/x/crypto
url = https://go.googlesource.com/crypto.git
148 changes: 148 additions & 0 deletions data_source_ssh_tunnel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package main

import (
"encoding/pem"
"fmt"
"io"
"log"
"net"

"github.com/hashicorp/terraform/helper/schema"
"golang.org/x/crypto/ssh"
)

func dataSourceSSHTunnel() *schema.Resource {
return &schema.Resource{
Read: dataSourceSSHTunnelRead,
Schema: map[string]*schema.Schema{
"user": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "The username",
},
"host": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "The hostname",
},
"private_key": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "The private SSH key",
},
"local_address": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "The local bind address (e.g. localhost:8500)",
},
"remote_address": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "The remote bind address (e.g. localhost:8500)",
},
"tunnel_established": {
// Probably not the proper way to store this
Type: schema.TypeBool,
Optional: true,
Default: false,
},
},
}
}

// copied from https://github.com/hashicorp/terraform/blob/7149894e418d06274bc5827c872edd58d887aad9/communicator/ssh/provisioner.go#L213-L232
func readPrivateKey(pk string) (ssh.AuthMethod, error) {
// We parse the private key on our own first so that we can
// show a nicer error if the private key has a password.
block, _ := pem.Decode([]byte(pk))
if block == nil {
return nil, fmt.Errorf("Failed to read key %q: no key found", pk)
}
if block.Headers["Proc-Type"] == "4,ENCRYPTED" {
return nil, fmt.Errorf(
"Failed to read key %q: password protected keys are\n"+
"not supported. Please decrypt the key prior to use.", pk)
}

signer, err := ssh.ParsePrivateKey([]byte(pk))
if err != nil {
return nil, fmt.Errorf("Failed to parse key file %q: %s", pk, err)
}

return ssh.PublicKeys(signer), nil
}

func dataSourceSSHTunnelRead(d *schema.ResourceData, meta interface{}) error {
user := d.Get("user").(string)
host := d.Get("host").(string)
privateKey := d.Get("private_key").(string)
localAddress := d.Get("local_address").(string)
remoteAddress := d.Get("remote_address").(string)
tunnelEstablished := d.Get("tunnel_established").(bool)

log.Printf("[DEBUG] user: %v", user)
log.Printf("[DEBUG] host: %v", host)
log.Printf("[DEBUG] localAddress: %v", localAddress)
log.Printf("[DEBUG] remoteAddress: %v", remoteAddress)
log.Printf("[DEBUG] tunnelEstablished: %v", tunnelEstablished)

if tunnelEstablished == false {
d.Set("tunnel_established", true)

sshConf := &ssh.ClientConfig{
User: user,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Auth: []ssh.AuthMethod{},
}

pubKeyAuth, err := readPrivateKey(privateKey)
if err != nil {
panic(err)
}
sshConf.Auth = append(sshConf.Auth, pubKeyAuth)

localListener, err := net.Listen("tcp", localAddress)
if err != nil {
panic(err)
}

go func() {
sshClientConn, err := ssh.Dial("tcp", host, sshConf)
if err != nil {
panic(err)
}

// The accept loop
for {
localConn, err := localListener.Accept()
if err != nil {
panic(err)
}

sshConn, err := sshClientConn.Dial("tcp", remoteAddress)
if err != nil {
panic(err)
}

// Send traffic from the SSH server -> local program
go func() {
_, err = io.Copy(sshConn, localConn)
if err != nil {
panic(err)
}
}()

// Send traffic from the local program -> SSH server
go func() {
_, err = io.Copy(localConn, sshConn)
if err != nil {
panic(err)
}
}()
}
}()
}
d.SetId(localAddress)

return nil
}
14 changes: 14 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package main

import (
"github.com/hashicorp/terraform/plugin"
"github.com/hashicorp/terraform/terraform"
)

func main() {
plugin.Serve(&plugin.ServeOpts{
ProviderFunc: func() terraform.ResourceProvider {
return Provider()
},
})
}
27 changes: 27 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
data "ssh_tunnel" "consul" {
user = "stefan"
host = "bastion.example.com:22"
private_key = "${file(pathexpand("~/.ssh/id_rsa"))}"
local_address = "localhost:8500"
remote_address = "localhost:8500"
}

provider "consul" {
version = "~> 1.0"
address = "${data.ssh_tunnel.consul.local_address}"
scheme = "http"
}

data "consul_keys" "keys" {
key {
name = "revision"
path = "revision"
}
}

output "local_address" {
value = "${data.ssh_tunnel.consul.local_address}"
}
output "revision" {
value = "${data.consul_keys.keys.var.revision}"
}
15 changes: 15 additions & 0 deletions provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package main

import (
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)

// Provider returns a terraform.ResourceProvider
func Provider() terraform.ResourceProvider {
return &schema.Provider{
DataSourcesMap: map[string]*schema.Resource{
"ssh_tunnel": dataSourceSSHTunnel(),
},
}
}
1 change: 1 addition & 0 deletions vendor/github.com/hashicorp/terraform
Submodule terraform added at 9f1a77
1 change: 1 addition & 0 deletions vendor/golang.org/x/crypto
Submodule crypto added at b080dc

0 comments on commit 759655c

Please sign in to comment.