Add support for EKS cluster authentication.

This commit is contained in:
Robert Bo Davis
2018-11-28 12:52:07 -05:00
parent 915077d25e
commit 563dfda1ea
6 changed files with 140 additions and 4 deletions
+61
View File
@@ -187,6 +187,67 @@ Get the token for the default service account in the default namespace:
KUBERNETES_TOKEN=$(kubectl get secret $(kubectl get sa default -o jsonpath='{.secrets[].name}{"\n"}') -o jsonpath="{.data.token}" | base64 -D)
```
## Deploying to EKS
To deploy to EKS, you should have `api_server` and `kubernetes_certificate` secrets set in drone. If drone is deploying from outside of AWS, you should also have an `aws_access_key_id`, `aws_secret_access_key` secret. An `AWS_DEFAULT_REGION` environmental variable should also be set for the deployment.
### Use the AWS IAM keys for the user who created the EKS cluster (not recommended):
```YAML
pipeline
helm_deploy:
image: quay.io/ipedrazas/drone-helm
chart: ./charts/my-chart
release: ${DRONE_BRANCH}
values: image.tag=${DRONE_BRANCH}-${DRONE_COMMIT_SHA:0:7}
prefix: STAGING
namespace: staging
eks_cluster: my-eks-cluster
environment:
- AWS_DEFAULT_REGION=us-east-1
secrets: [ aws_access_key_id, aws_secret_access_key, api_server, kubernetes_certificate ]
```
### Use role based EKS access
You must first [configure an IAM Role for cluster access](https://docs.aws.amazon.com/eks/latest/userguide/add-user-role.html) and configure policy to allow an iam user or iam role to assume the new role.
Running drone agent on an ec2 instance that has Role based access to assume the cluster access Role created above:
```YAML
pipeline
helm_deploy:
image: quay.io/ipedrazas/drone-helm
chart: ./charts/my-chart
release: ${DRONE_BRANCH}
values: image.tag=${DRONE_BRANCH}-${DRONE_COMMIT_SHA:0:7}
prefix: STAGING
namespace: staging
eks_cluster: my-eks-cluster
eks_role_arn: arn:aws:iam::[ACCOUNT ID HERE]:role/eks-master
environment:
- AWS_DEFAULT_REGION=us-east-1
secrets: [ api_server, kubernetes_certificate ]
```
Using IAM keys with access to assume the cluster access Role created above:
```YAML
pipeline
helm_deploy:
image: quay.io/ipedrazas/drone-helm
chart: ./charts/my-chart
release: ${DRONE_BRANCH}
values: image.tag=${DRONE_BRANCH}-${DRONE_COMMIT_SHA:0:7}
prefix: STAGING
namespace: staging
eks_cluster: my-eks-cluster
eks_role_arn: arn:aws:iam::[ACCOUNT ID HERE]:role/eks-master
environment:
- AWS_DEFAULT_REGION=us-east-1
secrets: [ aws_access_key_id, aws_secret_access_key, api_server, kubernetes_certificate ]
```
## Advanced customisations and debugging
This plugin installs [Tiller](https://github.com/kubernetes/helm/blob/master/docs/architecture.md) in the cluster, if you want to specify the namespace where `tiller` ins installed, use the `tiller_ns` attribute.
+3
View File
@@ -17,10 +17,13 @@ RUN set -ex \
&& apk add --no-cache curl ca-certificates \
&& curl -o /tmp/${FILENAME} http://storage.googleapis.com/kubernetes-helm/${FILENAME} \
&& curl -o /tmp/kubectl https://storage.googleapis.com/kubernetes-release/release/${KUBECTL}/bin/linux/amd64/kubectl \
&& curl -o /tmp/aws-iam-authenticator https://amazon-eks.s3-us-west-2.amazonaws.com/1.10.3/2018-07-26/bin/linux/amd64/aws-iam-authenticator \
&& tar -zxvf /tmp/${FILENAME} -C /tmp \
&& mv /tmp/linux-amd64/helm /bin/helm \
&& chmod +x /tmp/kubectl \
&& mv /tmp/kubectl /bin/kubectl \
&& chmod +x /tmp/aws-iam-authenticator \
&& mv /tmp/aws-iam-authenticator /bin/aws-iam-authenticator \
&& rm -rf /tmp/*
LABEL description="Kubectl and Helm."
+16 -2
View File
@@ -14,7 +14,7 @@ contexts:
cluster: helm
{{ if .Namespace }}
namespace: {{ .Namespace }}
{{ end}}
{{ end }}
user: {{ .ServiceAccount }}
name: helm
current-context: "helm"
@@ -23,4 +23,18 @@ preferences: {}
users:
- name: {{ .ServiceAccount }}
user:
token: {{ .Token }}
{{ if .Token }}
token: {{ .Token }}
{{ else if .EKSCluster }}
exec:
apiVersion: client.authentication.k8s.io/v1alpha1
command: aws-iam-authenticator
args:
- "token"
- "-i"
- "{{ .EKSCluster }}"
{{ if .EKSRoleARN }}
- "-r"
- "{{ .EKSRoleARN }}"
{{ end }}
{{ end }}
+12
View File
@@ -58,6 +58,16 @@ func main() {
Usage: "specify the exact chart version to use. If this is not specified, the latest version is used",
EnvVar: "PLUGIN_CHART_VERSION,CHART_VERSION",
},
cli.StringFlag{
Name: "eks_cluster",
Usage: "Name of EKS cluster. Requires AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_DEFAULT_REGION secrets AND/OR EKS_ROLE and proper role configuration",
EnvVar: "PLUGIN_EKS_CLUSTER,EKS_CLUSTER",
},
cli.StringFlag{
Name: "eks_role_arn",
Usage: "ARN of EKS role to assume for EKS authentication.",
EnvVar: "PLUGIN_EKS_ROLE_ARN,EKS_ROLE_ARN",
},
cli.StringFlag{
Name: "values",
Usage: "Kubernetes helm release",
@@ -175,6 +185,8 @@ func run(c *cli.Context) error {
HelmRepos: c.StringSlice("helm_repos"),
Chart: c.String("chart"),
Version: c.String("chart-version"),
EKSCluster: c.String("eks_cluster"),
EKSRoleARN: c.String("eks_role_arn"),
Debug: c.Bool("debug"),
DryRun: c.Bool("dry-run"),
Secrets: c.StringSlice("secrets"),
+6 -2
View File
@@ -30,6 +30,8 @@ type (
Release string `json:"release"`
Chart string `json:"chart"`
Version string `json:"version"`
EKSCluster string `json:"eks_cluster"`
EKSRoleARN string `json:"eks_role_arn"`
Values string `json:"values"`
StringValues string `json:"string_values"`
ValuesFiles string `json:"values_files"`
@@ -234,8 +236,10 @@ func (p *Plugin) Exec() error {
if p.Config.APIServer == "" {
return fmt.Errorf("Error: API Server is needed to deploy.")
}
if p.Config.Token == "" {
return fmt.Errorf("Error: Token is needed to deploy.")
if p.Config.EKSCluster == "" {
if p.Config.Token == "" {
return fmt.Errorf("Error: Token is needed to deploy.")
}
}
initialiseKubeconfig(&p.Config, KUBECONFIG, p.Config.KubeConfig)
}
+42
View File
@@ -42,6 +42,48 @@ func TestInitialiseKubeconfig(t *testing.T) {
if !strings.Contains(kubeConfigStr, "my-cert-data") {
t.Errorf("Kubeconfig doesn't render certificate")
}
if strings.Contains(kubeConfigStr, "aws-iam-authenticator") {
t.Errorf("Kubeconfig renders EKS cluster configuration")
}
}
func TestInitialiseKubeconfigEKS(t *testing.T) {
plugin := Plugin{
Config: Config{
APIServer: "http://myapiserver",
Certificate: "my-cert-data",
EKSCluster: "my-eks-cluster-name",
EKSRoleARN: "my-eks-role-arn",
HelmCommand: "",
Namespace: "default",
SkipTLSVerify: false, // if set the true with Certificate, this test will fail
},
}
configfile := "config3.test"
initialiseKubeconfig(&plugin.Config, "../kubeconfig", configfile)
data, err := ioutil.ReadFile(configfile)
if err != nil {
t.Errorf("Error reading file %v", err)
}
kubeConfigStr := string(data)
if strings.Contains(kubeConfigStr, "token:") {
t.Errorf("Kubeconfig renders token")
}
if !strings.Contains(kubeConfigStr, "http://myapiserver") {
t.Errorf("Kubeconfig doesn't render APIServer")
}
if !strings.Contains(kubeConfigStr, "my-cert-data") {
t.Errorf("Kubeconfig doesn't render certificate")
}
if !strings.Contains(kubeConfigStr, "my-eks-cluster-name") {
t.Errorf("Kubeconfig doesn't render EKS cluster name")
}
if !strings.Contains(kubeConfigStr, "my-eks-role-arn") {
t.Errorf("Kubeconfig doesn't render EKS role ARN")
}
}
func TestGetHelmCommandEmptyPushEvent(t *testing.T) {