-
Notifications
You must be signed in to change notification settings - Fork 56
/
Copy pathauth_metadata.go
107 lines (87 loc) · 2.4 KB
/
auth_metadata.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
package kinesis
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
)
const (
AWSMetadataServer = "169.254.169.254"
AWSIAMCredsPath = "/latest/meta-data/iam/security-credentials"
AWSIAMCredsURL = "http://" + AWSMetadataServer + "/" + AWSIAMCredsPath
)
// NewAuthFromMetadata retrieves auth credentials from the metadata
// server. If an IAM role is associated with the instance we are running on, the
// metadata server will expose credentials for that role under a known endpoint.
//
// TODO: specify custom network (connect, read) timeouts, else this will block
// for the default timeout durations.
func NewAuthFromMetadata() (Auth, error) {
return newCachedMutexedWarmedUpAuth(&metadataCreds{})
}
type metadataCreds struct{}
func (mc *metadataCreds) ExpiringKeyForSigning(now time.Time) (*SigningKey, time.Time, error) {
role, err := retrieveIAMRole()
if err != nil {
return nil, time.Time{}, err
}
data, err := retrieveAWSCredentials(role)
if err != nil {
return nil, time.Time{}, err
}
expiry, err := time.Parse(time.RFC3339, data["Expiration"])
if err != nil {
return nil, time.Time{}, err
}
return &SigningKey{
AccessKeyId: data["AccessKeyId"],
SecretAccessKey: data["SecretAccessKey"],
SessionToken: data["Token"],
}, expiry, nil
}
func retrieveAWSCredentials(role string) (map[string]string, error) {
var bodybytes []byte
client := http.Client{
Timeout: time.Duration(10 * time.Second),
}
// Retrieve the json for this role
resp, err := client.Get(fmt.Sprintf("%s/%s", AWSIAMCredsURL, role))
if err != nil || resp.StatusCode != http.StatusOK {
return nil, err
}
defer resp.Body.Close()
bodybytes, err = ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
jsondata := make(map[string]string)
err = json.Unmarshal(bodybytes, &jsondata)
if err != nil {
return nil, err
}
return jsondata, nil
}
func retrieveIAMRole() (string, error) {
var bodybytes []byte
client := http.Client{
Timeout: time.Duration(10 * time.Second),
}
resp, err := client.Get(AWSIAMCredsURL)
if err != nil || resp.StatusCode != http.StatusOK {
return "", err
}
defer resp.Body.Close()
bodybytes, err = ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
// pick the first IAM role
role := strings.Split(string(bodybytes), "\n")[0]
if len(role) == 0 {
return "", errors.New("Unable to retrieve IAM role")
}
return role, nil
}