aboutsummaryrefslogtreecommitdiff
path: root/credentials.go
diff options
context:
space:
mode:
Diffstat (limited to 'credentials.go')
-rw-r--r--credentials.go153
1 files changed, 153 insertions, 0 deletions
diff --git a/credentials.go b/credentials.go
new file mode 100644
index 0000000..a9412da
--- /dev/null
+++ b/credentials.go
@@ -0,0 +1,153 @@
1package main
2
3import (
4 "time"
5
6 "github.com/aws/aws-sdk-go/aws"
7 "github.com/aws/aws-sdk-go/aws/credentials"
8 "github.com/aws/aws-sdk-go/aws/session"
9 "github.com/aws/aws-sdk-go/service/sts"
10 jww "github.com/spf13/jwalterweatherman"
11)
12
13// Try to refresh credentials 3 times an hour but in the worst case if the
14// credential refresh fails twice try to get one last refresh in before the end
15// of the hour when the credential expires.
16const REFRESH_INTERVAL = time.Duration(19) * time.Minute
17
18type CredentialHandler interface {
19 Start()
20 InGoodState() bool
21 SetBootstrapCredential(*credentials.Credentials)
22 Output() chan *IAMCredentials
23}
24
25type credentialHandler struct {
26 region *string
27 roleARN *string
28 sessionName *string
29 bootstrapCreds *credentials.Credentials
30 output chan *IAMCredentials
31 input chan *credentials.Credentials
32}
33
34func NewCredentialHandler(region, arn, name *string) CredentialHandler {
35 return &credentialHandler{
36 region: region,
37 roleARN: arn,
38 sessionName: name,
39 output: make(chan *IAMCredentials),
40 input: make(chan *credentials.Credentials, 1), // 1-item buffer to allow pre-start bootstrapping
41 }
42}
43
44func (h *credentialHandler) Output() chan *IAMCredentials {
45 return h.output
46}
47
48func (h *credentialHandler) InGoodState() bool {
49 c := <-h.Output()
50 return c.Code == "Success"
51}
52
53func (h *credentialHandler) SetBootstrapCredential(bc *credentials.Credentials) {
54 h.input <- bc
55}
56
57func (h *credentialHandler) Start() {
58 c := &IAMCredentials{Code: "Failure"}
59 updateChan := make(chan *IAMCredentials)
60
61 ticker := time.NewTicker(REFRESH_INTERVAL)
62 defer ticker.Stop()
63
64 jww.INFO.Printf("Starting credential handler, awaiting bootstrap")
65
66 for {
67 select {
68 // Read and update bootstrap credentials
69 case h.bootstrapCreds = <-h.input:
70 go h.refreshCredential(nil, updateChan)
71 // HTTP handler requests credential
72 case h.output <- c:
73 // Time to refresh credentials
74 case <-ticker.C:
75 go h.refreshCredential(c.rawCredentials, updateChan)
76 // Updated credentials arrive
77 case up := <-updateChan:
78 if up == nil && c.Expiration.After(time.Now()) {
79 c = &IAMCredentials{Code: "Failure"}
80 } else {
81 c = up
82 }
83 }
84 }
85}
86
87func (h *credentialHandler) refreshCredential(creds *credentials.Credentials, out chan *IAMCredentials) {
88 jww.INFO.Printf("Attempting to obtain credentials")
89
90 if creds == nil && h.bootstrapCreds == nil {
91 jww.WARN.Printf("No session or bootstrap credentials available")
92 return
93 }
94
95 if creds != nil {
96 jww.DEBUG.Printf("Attempting to use session credentials")
97
98 c, err := h.assumeRole(creds)
99 if err != nil {
100 jww.WARN.Printf("Failed to obtain with session credentials: %s", err)
101 } else {
102 jww.INFO.Printf("Successfully obtained credentials")
103 out <- c
104 return
105 }
106 }
107
108 if h.bootstrapCreds != nil {
109 jww.DEBUG.Printf("Attempting to use bootstrap credentials")
110
111 c, err := h.assumeRole(h.bootstrapCreds)
112 if err != nil {
113 jww.WARN.Printf("Failed to obtain with bootstrap credentials: %s", err)
114 } else {
115 jww.INFO.Printf("Successfully obtained credentials")
116 out <- c
117 return
118 }
119 }
120
121 jww.ERROR.Printf("Failed to obtain credentials")
122 out <- nil
123}
124
125func (h *credentialHandler) assumeRole(creds *credentials.Credentials) (*IAMCredentials, error) {
126 ses := session.New(&aws.Config{
127 Region: h.region,
128 Credentials: creds,
129 })
130
131 assumed, err := sts.New(ses).AssumeRole(&sts.AssumeRoleInput{
132 RoleArn: h.roleARN,
133 RoleSessionName: h.sessionName,
134 })
135 if err != nil {
136 return nil, err
137 }
138
139 return &IAMCredentials{
140 Code: "Success",
141 Type: "AWS-HMAC",
142 AccessKeyId: *assumed.Credentials.AccessKeyId,
143 SecretAccessKey: *assumed.Credentials.SecretAccessKey,
144 Token: *assumed.Credentials.SessionToken,
145 LastUpdated: time.Now().UTC().Round(time.Second),
146 Expiration: *assumed.Credentials.Expiration,
147 rawCredentials: credentials.NewStaticCredentials(
148 *assumed.Credentials.AccessKeyId,
149 *assumed.Credentials.SecretAccessKey,
150 *assumed.Credentials.SessionToken,
151 ),
152 }, nil
153}