-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathaws.go
164 lines (147 loc) · 5 KB
/
aws.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
152
153
154
155
156
157
158
159
160
161
162
163
164
// mrmanager - request temporary credentals and put them in a useful place
// aws.go: support for pulling and writing AWS credentials
//
// Copyright 2019-2022 F5 Inc.
// Licensed under the BSD 3-clause license; see LICENSE for more information.
package main
import (
"flag"
"fmt"
"os"
"strings"
"time"
"github.com/mitchellh/cli"
"github.com/mitchellh/go-homedir"
)
// AWSCommand context on the command we're running
type AWSCommand struct {
Header string
IAM bool
Passcode string
Role string
StdOut bool
UI cli.Ui
Username string
EnginePath string
TTL string
}
// Run - Pull AWS credentials
func (c *AWSCommand) Run(args []string) int {
// Flags for this subcommand
cmdFlags := flag.NewFlagSet("aws", flag.ContinueOnError)
cmdFlags.BoolVar(&c.IAM, "i", false, "Request IAM credentials instead")
cmdFlags.BoolVar(&c.StdOut, "o", false, "Output credentials to stdout (instead of .aws/credentials)")
cmdFlags.StringVar(&c.Header, "a", "default", "Change the header of credentials")
cmdFlags.StringVar(&c.Passcode, "p", "", "YubiKey OTP string (default \"DUO Push\")")
cmdFlags.StringVar(&c.Role, "r", "default", "Vault role name")
cmdFlags.StringVar(&c.Username, "u", os.Getenv("USER"), "Vault username")
cmdFlags.StringVar(&c.EnginePath, "e", "aws", "Specify secret engine path")
cmdFlags.StringVar(&c.TTL, "t", "", "Specify TTL")
cmdFlags.Usage = func() { c.UI.Output(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
}
// Need a $USER, hopefully we have it?
if c.Username == "" {
c.UI.Error("$USER empty and -u unspecified -- cant continue")
os.Exit(1)
}
// Good to have a $HOME
home, err := homedir.Dir()
if err != nil {
c.UI.Warn("$HOME is undefined. I'll write to stdout.")
c.StdOut = true
}
// Vault auth
client, err := authToVault(c.Username, c.Passcode)
if err != nil {
c.UI.Error(fmt.Sprintf("Unable to auth to Vault: %s", err))
os.Exit(1)
}
// There's probably a cleaner way to do this but whatever
// Used to fix request path for Vault - then request the creds.
var credType string
if c.IAM {
credType = "creds"
} else {
credType = "sts"
}
// Figure out what path this is going to
engine := c.EnginePath
if engine != "aws" {
engine = "aws-" + c.EnginePath
}
params := make(map[string][]string)
if c.TTL != "" {
params["ttl"] = []string{c.TTL}
}
aws, err := client.Logical().ReadWithData(fmt.Sprintf("%s/%s/%s", engine, credType, c.Role), params)
if err != nil {
c.UI.Error(fmt.Sprintf("Error reading secret. Vault says: %s", err))
os.Exit(1)
}
// Build output format
var awsCfg string
awsCfg = fmt.Sprintf("[%s]\n", c.Header)
awsCfg = awsCfg + fmt.Sprintf("aws_access_key_id = %s\n", aws.Data["access_key"])
awsCfg = awsCfg + fmt.Sprintf("aws_secret_access_key = %s", aws.Data["secret_key"])
if !c.IAM {
awsCfg = awsCfg + fmt.Sprintf("\naws_session_token = %s", aws.Data["security_token"])
}
awsCfg = awsCfg + "\n"
// Do something useful with credentials
if c.StdOut {
// file save directions
c.UI.Info("Save these credentials to ~/.aws/credentials")
// Output to stdout
c.UI.Info("----- BEGIN AWS CREDS -----")
c.UI.Info(awsCfg)
c.UI.Info("----- END AWS CREDS -----")
} else {
// Output to file
awsCfgDir := fmt.Sprintf("%s/.aws", home)
awsCfgFile := fmt.Sprintf("%s/credentials", awsCfgDir)
if _, err := os.Stat(awsCfgDir); os.IsNotExist(err) {
os.Mkdir(awsCfgDir, 0700)
}
_ = os.Remove(awsCfgDir + "/credentials")
err = os.WriteFile(awsCfgFile, []byte(awsCfg), 0600)
if err != nil {
c.UI.Error(fmt.Sprintf("Unable to write AWS creds to file: %s", err))
os.Exit(1)
}
c.UI.Info(fmt.Sprintf("Wrote AWS credentials to %s.", awsCfgFile))
}
leaseDuration := time.Duration(aws.LeaseDuration) * time.Second
c.UI.Info(fmt.Sprintf("Credental Lease Duration: %s.", leaseDuration.String()))
// IAM users should be reminded of AWS' eventual consistency.
if c.IAM {
c.UI.Info("FYI: IAM credentials take ~15 seconds to become active.")
}
return 0
}
// Help - Display help text
func (c *AWSCommand) Help() string {
helpText := `
Usage: mrmanager aws [options]
Request AWS credentials via Vault.
This command will connect ask Vault to generate temporary
credentials for AWS. Flags that can be specified include:
-a Specify a different header for .aws/credentials
(Default: "default")
-e Specify a different path (Default: aws/, uses
aws-NAME/ if -e specified)
-i Request IAM credentials. (Default: false)
-o Output credentials to stdout (Default: false)
-p YubiKey Passcode (Default: "")
-r ROLE The Vault role to use when requesting credentials.
(Default: "default")
-u Username. (Default: Your Username)
-t TTL (format: 1h1m1s. Default: not specified)
`
return strings.TrimSpace(helpText)
}
// Synopsis - Return a small summary of what we do
func (c *AWSCommand) Synopsis() string {
return "Request AWS credentials via Vault"
}