forked from hashicorp/otto
/
infra.go
151 lines (133 loc) · 4.26 KB
/
infra.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package aws
import (
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/hashicorp/otto/helper/bindata"
"github.com/hashicorp/otto/helper/sshagent"
"github.com/hashicorp/otto/helper/terraform"
"github.com/hashicorp/otto/infrastructure"
"github.com/hashicorp/otto/ui"
"github.com/mitchellh/go-homedir"
)
//go:generate go-bindata -pkg=aws -nomemcopy -nometadata ./data/...
// Infra returns the infrastructure.Infrastructure implementation.
// This function is a infrastructure.Factory.
func Infra() (infrastructure.Infrastructure, error) {
return &terraform.Infrastructure{
CredsFunc: creds,
VerifyCredsFunc: verifyCreds,
Bindata: &bindata.Data{
Asset: Asset,
AssetDir: AssetDir,
},
Variables: map[string]string{
"aws_region": "us-east-1",
},
}, nil
}
func creds(ctx *infrastructure.Context) (map[string]string, error) {
fields := []*ui.InputOpts{
&ui.InputOpts{
Id: "aws_access_key",
Query: "AWS Access Key",
Description: "AWS access key used for API calls.",
EnvVars: []string{"AWS_ACCESS_KEY_ID"},
},
&ui.InputOpts{
Id: "aws_secret_key",
Query: "AWS Secret Key",
Description: "AWS secret key used for API calls.",
EnvVars: []string{"AWS_SECRET_ACCESS_KEY"},
},
&ui.InputOpts{
Id: "ssh_public_key_path",
Query: "SSH Public Key Path",
Description: "Path to an SSH public key that will be granted access to EC2 instances",
Default: "~/.ssh/id_rsa.pub",
EnvVars: []string{"AWS_SSH_PUBLIC_KEY_PATH"},
},
}
result := make(map[string]string, len(fields))
for _, f := range fields {
value, err := ctx.Ui.Input(f)
if err != nil {
return nil, err
}
result[f.Id] = value
}
// Load SSH public key contents
sshPath, err := homedir.Expand(result["ssh_public_key_path"])
if err != nil {
return nil, fmt.Errorf("Error expanding homedir for SSH key: %s", err)
}
sshKey, err := ioutil.ReadFile(sshPath)
if err != nil {
return nil, fmt.Errorf("Error reading SSH key: %s", err)
}
result["ssh_public_key"] = string(sshKey)
return result, nil
}
func verifyCreds(ctx *infrastructure.Context) error {
found, err := sshagent.HasKey(ctx.InfraCreds["ssh_public_key"])
if err != nil {
return sshAgentError(err)
}
if !found {
ok, _ := guessAndLoadPrivateKey(
ctx.Ui, ctx.InfraCreds["ssh_public_key_path"])
if ok {
ctx.Ui.Message(
"A private key was found and loaded. Otto will now check\n" +
"the SSH Agent again and continue if the correct key is loaded")
found, err = sshagent.HasKey(ctx.InfraCreds["ssh_public_key"])
if err != nil {
return sshAgentError(err)
}
}
}
if !found {
return sshAgentError(fmt.Errorf(
"You specified an SSH public key of: %q, but the private key from this\n"+
"keypair is not loaded the SSH Agent. To load it, run:\n\n"+
" ssh-add [PATH_TO_PRIVATE_KEY]",
ctx.InfraCreds["ssh_public_key_path"]))
}
return nil
}
func sshAgentError(err error) error {
return fmt.Errorf(
"Otto uses your SSH Agent to authenticate with instances created in\n"+
"AWS, but it could not verify that your SSH key is loaded into the agent.\n"+
"The error message follows:\n\n%s", err)
}
// guessAndLoadPrivateKey takes a path to a public key and determines if a
// private key exists by just stripping ".pub" from the end of it. if so,
// it attempts to load that key into the agent.
func guessAndLoadPrivateKey(ui ui.Ui, pubKeyPath string) (bool, error) {
fullPath, err := homedir.Expand(pubKeyPath)
if err != nil {
return false, err
}
if !strings.HasSuffix(fullPath, ".pub") {
return false, fmt.Errorf("No .pub suffix, cannot guess path.")
}
privKeyGuess := strings.TrimSuffix(fullPath, ".pub")
if _, err := os.Stat(privKeyGuess); os.IsNotExist(err) {
return false, fmt.Errorf("No file at guessed path.")
}
ui.Header("Loading key into SSH Agent")
ui.Message(fmt.Sprintf(
"The key you provided (%s) was not found in your SSH Agent.", pubKeyPath))
ui.Message(fmt.Sprintf(
"However, Otto found a private key here: %s", privKeyGuess))
ui.Message(fmt.Sprintf(
"Automatically running 'ssh-add %s'.", privKeyGuess))
ui.Message("If your SSH key has a passphrase, you will be prompted for it.")
ui.Message("")
if err := sshagent.Add(ui, privKeyGuess); err != nil {
return false, err
}
return true, nil
}