Skip to content

Commit 2c39d42

Browse files
authored
Merge pull request #22 from mikkeloscar/assume-role
Add option to define assume role at startup
2 parents 28c8aab + f313164 commit 2c39d42

File tree

3 files changed

+195
-5
lines changed

3 files changed

+195
-5
lines changed

README.md

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,180 @@ It is currently moving a lot of effort on to the users defining the pod specs.
105105
A future idea is to make the controller act as an admission controller which
106106
can inject the required configuration automatically.
107107

108+
### Setting up AWS IAM roles
109+
110+
The controller does not take care of AWS IAM role provisioning and assumes that
111+
the user provisions AWS IAM roles manually, for instance via
112+
[CloudFormation](https://aws.amazon.com/cloudformation/) or
113+
[Terraform](https://www.terraform.io/).
114+
115+
Here is an example of an AWS IAM role defined via CloudFormation:
116+
117+
```yaml
118+
Parameters:
119+
AssumeRoleARN:
120+
Description: "Role ARN of the role used by kube-aws-iam-controller"
121+
Type: String
122+
Metadata:
123+
StackName: "aws-iam-example"
124+
AWSTemplateFormatVersion: "2010-09-09"
125+
Description: "Example IAM Role"
126+
Resources:
127+
IAMRole:
128+
Type: AWS::IAM::Role
129+
Properties:
130+
RoleName: "aws-iam-example"
131+
Path: /
132+
AssumeRolePolicyDocument:
133+
Statement:
134+
- Action: sts:AssumeRole
135+
Effect: Allow
136+
Principal:
137+
AWS: !Ref "AssumeRoleARN"
138+
Version: '2012-10-17'
139+
Policies:
140+
- PolicyName: "policy"
141+
PolicyDocument:
142+
Version: '2012-10-17'
143+
Statement:
144+
- Effect: Allow
145+
Action:
146+
- "ec2:Describe*"
147+
Resource: "*"
148+
```
149+
150+
The role could be created via:
151+
152+
```sh
153+
# $ASSUME_ROLE_ARN is the ARN of the role used by the kube-aws-iam-controller deployment
154+
$ aws cloudformation create-stack --stack-name aws-iam-example \
155+
--parameters "ParameterKey=AssumeRoleARN,ParameterValue=$ASSUME_ROLE_ARN" \
156+
--template-body=file://iam-role.yaml --capabilities CAPABILITY_NAMED_IAM
157+
```
158+
159+
The important part is the `AssumeRolePolicyDocument`:
160+
161+
```yaml
162+
AssumeRolePolicyDocument:
163+
Statement:
164+
- Action: sts:AssumeRole
165+
Effect: Allow
166+
Principal:
167+
AWS: !Ref "AssumeRoleARN"
168+
Version: '2012-10-17'
169+
```
170+
171+
This allows the `kube-aws-iam-controller` to assume the role and provide
172+
credentials on behalf of the application requesting credentials via an
173+
`AWSIAMRole` resource in the cluster.
174+
175+
The `AssumeRoleARN` is the ARN of the role which the `kube-aws-iam-controller`
176+
is running with. Usually this would be the instance role of the EC2 instance
177+
were the controller is running.
178+
179+
#### Using custom Assume role
180+
181+
Sometimes it's desirable to let the controller assume roles with a specific
182+
role dedicated for that task i.e. a role different from the instance role. The
183+
controller allows specifying such a role via the
184+
`--assume-role=<controller-role>` flag providing the following setup:
185+
186+
```
187+
+-------------+
188+
| |
189+
+--> | <app-role1> |
190+
+-----------------+ +-------------------+ | | |
191+
| | | | | +-------------+
192+
| <instance-role> | -- assumes --> | <controller-role> | -- assumes --+
193+
| | | | | +-------------+
194+
+-----------------+ +-------------------+ | | |
195+
+--> | <app-role2> |
196+
| |
197+
+-------------+
198+
```
199+
200+
In this case the `<instance-role>` will only be used for the initial assuming
201+
of the `<controller-role>` and all `<app-role>s` are assumed by the
202+
`<controller-role>`. This makes it possible to have many different
203+
`<instance-role>s` while the `<app-role>s` only have to trust the single static
204+
`<controller-role>`. If you don't specify `--assume-role` then the
205+
`<instance-role>` would have to assume the `<app-role>s`.
206+
207+
Here is an example of the AWS IAM roles defined for this set-up to work:
208+
209+
```yaml
210+
Metadata:
211+
StackName: "aws-iam-assume-role-example"
212+
AWSTemplateFormatVersion: "2010-09-09"
213+
Description: "Example AWS IAM Assume Role"
214+
Resources:
215+
InstanceIAMRole:
216+
Type: AWS::IAM::Role
217+
Properties:
218+
RoleName: "instance-role"
219+
Path: /
220+
AssumeRolePolicyDocument:
221+
Statement:
222+
- Action: sts:AssumeRole
223+
Effect: Allow
224+
Principal:
225+
Service: ec2.amazonaws.com
226+
Version: '2012-10-17'
227+
Policies:
228+
- PolicyName: "policy"
229+
PolicyDocument:
230+
Version: '2012-10-17'
231+
Statement:
232+
- Effect: Allow
233+
Action:
234+
- "sts:AssumeRole"
235+
Resource: "*"
236+
237+
KubeAWSIAMControllerIAMRole:
238+
Type: AWS::IAM::Role
239+
Properties:
240+
RoleName: "kube-aws-iam-controller"
241+
Path: /
242+
AssumeRolePolicyDocument:
243+
Statement:
244+
- Action: sts:AssumeRole
245+
Effect: Allow
246+
Principal:
247+
AWS: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${InstanceIAMRole}'
248+
Version: '2012-10-17'
249+
Policies:
250+
- PolicyName: "policy"
251+
PolicyDocument:
252+
Version: '2012-10-17'
253+
Statement:
254+
- Effect: Allow
255+
Action:
256+
- "sts:AssumeRole"
257+
Resource: "*"
258+
259+
APPIAMRole:
260+
Type: AWS::IAM::Role
261+
Properties:
262+
RoleName: "app-role"
263+
Path: /
264+
AssumeRolePolicyDocument:
265+
Statement:
266+
- Action: sts:AssumeRole
267+
Effect: Allow
268+
Principal:
269+
AWS: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/${KubeAWSIAMControllerIAMRole}'
270+
Version: '2012-10-17'
271+
Policies:
272+
- PolicyName: "policy"
273+
PolicyDocument:
274+
Version: '2012-10-17'
275+
Statement:
276+
- Effect: Allow
277+
Action:
278+
- "ec2:Describe*"
279+
Resource: "*"
280+
```
281+
108282
## Setup
109283

110284
The `kube-aws-iam-controller` can be run as a deployment in the cluster.

credentials_getter.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ type STSCredentialsGetter struct {
4040
}
4141

4242
// NewSTSCredentialsGetter initializes a new STS based credentials fetcher.
43-
func NewSTSCredentialsGetter(sess *session.Session, baseRoleARN string) *STSCredentialsGetter {
43+
func NewSTSCredentialsGetter(sess *session.Session, baseRoleARN string, configs ...*aws.Config) *STSCredentialsGetter {
4444
return &STSCredentialsGetter{
45-
svc: sts.New(sess),
45+
svc: sts.New(sess, configs...),
4646
baseRoleARN: baseRoleARN,
4747
}
4848
}

main.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ import (
55
"net/url"
66
"os"
77
"os/signal"
8+
"strings"
89
"syscall"
910
"time"
1011

12+
"github.com/aws/aws-sdk-go/aws"
13+
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
1114
"github.com/aws/aws-sdk-go/aws/session"
1215
"github.com/mikkeloscar/kube-aws-iam-controller/pkg/clientset"
1316
log "github.com/sirupsen/logrus"
@@ -31,6 +34,7 @@ var (
3134
BaseRoleARN string
3235
APIServer *url.URL
3336
Namespace string
37+
AssumeRole string
3438
}
3539
)
3640

@@ -44,6 +48,8 @@ func main() {
4448
Default(defaultEventQueueSize).IntVar(&config.EventQueueSize)
4549
kingpin.Flag("base-role-arn", "Base Role ARN. If not defined it will be autodiscovered from EC2 Metadata.").
4650
StringVar(&config.BaseRoleARN)
51+
kingpin.Flag("assume-role", "Assume Role can be specified to assume a role at start-up which is used for further assuming other roles managed by the controller.").
52+
StringVar(&config.AssumeRole)
4753
kingpin.Flag("namespace", "Limit the controller to a certain namespace.").
4854
Default(v1.NamespaceAll).StringVar(&config.Namespace)
4955
kingpin.Flag("apiserver", "API server url.").URLVar(&config.APIServer)
@@ -56,7 +62,7 @@ func main() {
5662
ctx, cancel := context.WithCancel(context.Background())
5763
kubeConfig, err := clientset.ConfigureKubeConfig(config.APIServer, defaultClientGOTimeout, ctx.Done())
5864
if err != nil {
59-
log.Fatalf("Failed to setup Kubernetes config: %v", err)
65+
log.Fatalf("Failed to set up Kubernetes config: %v", err)
6066
}
6167

6268
client, err := clientset.NewForConfig(kubeConfig)
@@ -66,7 +72,7 @@ func main() {
6672

6773
awsSess, err := session.NewSession()
6874
if err != nil {
69-
log.Fatalf("Failed to setup Kubernetes client: %v", err)
75+
log.Fatalf("Failed to set up AWS session: %v", err)
7076
}
7177

7278
if config.BaseRoleARN == "" {
@@ -78,7 +84,17 @@ func main() {
7884
log.Infof("Autodiscovered Base Role ARN: %s", config.BaseRoleARN)
7985
}
8086

81-
credsGetter := NewSTSCredentialsGetter(awsSess, config.BaseRoleARN)
87+
awsConfigs := make([]*aws.Config, 0, 1)
88+
if config.AssumeRole != "" {
89+
if !strings.HasPrefix(config.AssumeRole, arnPrefix) {
90+
config.AssumeRole = config.BaseRoleARN + config.AssumeRole
91+
}
92+
log.Infof("Using custom Assume Role: %s", config.AssumeRole)
93+
creds := stscreds.NewCredentials(awsSess, config.AssumeRole)
94+
awsConfigs = append(awsConfigs, &aws.Config{Credentials: creds})
95+
}
96+
97+
credsGetter := NewSTSCredentialsGetter(awsSess, config.BaseRoleARN, awsConfigs...)
8298

8399
podsEventCh := make(chan *PodEvent, config.EventQueueSize)
84100

0 commit comments

Comments
 (0)