├── 1-configure-kubernetes.md ├── 2-configure-vault.md ├── 3-deploy-basic.md ├── 4-deploy-sidecar.md ├── 5-deploy-aws.md ├── LICENSE ├── README.md ├── aws ├── Dockerfile ├── build ├── build_container ├── deployment.yaml └── main.go ├── basic ├── Dockerfile ├── build ├── build_container ├── deployment.yaml └── main.go ├── kube-auth.hcl ├── sidecar ├── Dockerfile ├── Dockerfile-consul-template ├── build ├── build_container ├── config.ctmpl ├── consul-template-wrapper.sh ├── deployment.yaml └── main.go ├── vault-auth.yaml ├── vault-reviewer-rbac.yaml └── vault-reviewer.yaml /1-configure-kubernetes.md: -------------------------------------------------------------------------------- 1 | # Guide 2 | 3 | This guide will walk you configuring Vault's Kubernetes Auth Backend and 4 | using the backend to authenticate applications. 5 | Requirements: 6 | 7 | * Vault >0.9.0 8 | * Kubernetes cluster 9 | 10 | ## Configure a Kubernetes Service Account for Verifying JWTs 11 | 12 | The Kubernetes Authentication Backend has a `token_reviewer_jwt` field which 13 | takes a Service Account Token that is in charge of verifying the validity of 14 | other service account tokens. This Service Account will call into Kubernetes 15 | TokenReview API and verify the service account tokens provided during login 16 | are valid. 17 | 18 | ### Prerequisites 19 | 20 | Vault uses the Kubernetes TokenReview API to validate that JWT tokens are still 21 | valid, and have not been deleted from within Kubernetes. 22 | 23 | To ensure stale/deleted Service Accounts tokens can not authenticate with vault 24 | the Kubernetes API server must be running with `--service-account-lookup`. This 25 | is defaulted to on in Kubernetes 1.7 but prior versions should ensure this is 26 | set. 27 | 28 | ### Create the Service Account 29 | 30 | The service account is defined in `vault-reviewer.yaml` and can be created with this 31 | command: 32 | 33 | ``` 34 | kubectl create -f vault-reviewer.yaml 35 | ``` 36 | 37 | ### RBAC 38 | 39 | If your kubernetes cluster uses RBAC authorization you will need to provide the 40 | service account with a role that gives it access to the TokenReview API. If not, 41 | this step can be skipped. 42 | 43 | The RBAC role is defined in `vault-reviewer-rbac.yaml` and can be created with 44 | this command: 45 | 46 | ``` 47 | kubectl create -f vault-reviewer-rbac.yaml 48 | ``` 49 | ### Read Service Account Token 50 | 51 | This token will need to be provided to Vault in the next step. The following command will print out the Service Account JWT token. (requires `jq`) 52 | 53 | ``` 54 | kubectl get secret \ 55 | $(kubectl get serviceaccount vault-reviewer -o json | jq -r '.secrets[0].name') \ 56 | -o json | jq -r '.data .token' | base64 -D - 57 | ``` 58 | 59 | ## Configure a Kubernetes Service Account for login in 60 | 61 | This service account will be used to login to the auth backend. 62 | 63 | ### Create the Service Account 64 | 65 | The service account is defined in `vault-auth.yaml` and can be created with this 66 | command: 67 | 68 | ``` 69 | kubectl create -f vault-auth.yaml 70 | ``` 71 | 72 | This service account does not need any RBAC permissions. 73 | 74 | ## Next Steps 75 | 76 | We now have a service account setup with the appropriate permissions. Next we 77 | will [configure the Kubernetes Auth Backend](./2-configure-vault.md). 78 | -------------------------------------------------------------------------------- /2-configure-vault.md: -------------------------------------------------------------------------------- 1 | # Guide 2 | 3 | ## Configure the Kubernetes Auth Backend 4 | 5 | ### Configuring the backend 6 | Mount the kubernetes auth backend: 7 | 8 | ``` 9 | vault auth-enable kubernetes 10 | ``` 11 | 12 | Configure the auth backend with the pulblic key of Kubernetes' JWT signing key, 13 | the host for the Kubernetes API, and the CA cert used for the API. Depending on 14 | your configuration, most of these values can be found through the `kubectl 15 | config view` command. Replace the values below with the values for your system. 16 | 17 | ``` 18 | vault write auth/kubernetes/config \ 19 | token_reviewer_jwt= \ 20 | kubernetes_host=https://192.168.99.100:8443 \ 21 | kubernetes_ca_cert=@/path/to/ca.crt 22 | ``` 23 | 24 | ### Configuring a Role 25 | 26 | Roles are used to bind Kubernetes Service Account names and namespaces to a set 27 | of Vault policies and token settings. 28 | 29 | First create the policy we want this role to gain: 30 | 31 | ``` 32 | vault policy-write kube-auth kube-auth.hcl 33 | ``` 34 | 35 | To create a role with the S.A. name "vault-auth" in the "default" namespace: 36 | 37 | ``` 38 | vault write auth/kubernetes/role/demo \ 39 | bound_service_account_names=vault-auth \ 40 | bound_service_account_namespaces=default \ 41 | policies=kube-auth \ 42 | period=60s 43 | ``` 44 | 45 | Notice we set a period of 60s, this means the resulting token is a [periodic token](https://www.vaultproject.io/docs/concepts/tokens.html#periodic-tokens) and 46 | must be renewed by the application at least every 60s. 47 | 48 | Read the demo role to verify everything was configured properly: 49 | 50 | ``` 51 | vault read auth/kubernetes/role/demo 52 | ``` 53 | Should produce the following output: 54 | ``` 55 | Key Value 56 | --- ----- 57 | bound_service_account_names [vault-auth] 58 | bound_service_account_namespaces [default] 59 | max_ttl 0 60 | num_uses 0 61 | period 60 62 | policies [default kube-auth] 63 | ttl 0 64 | ``` 65 | 66 | ### Write a secret 67 | 68 | This will be used later in the demo 69 | 70 | ``` 71 | vault write secret/creds username=demo password=test 72 | ``` 73 | 74 | ## Next Steps 75 | 76 | We now have a service account setup with the appropriate permissions and a Vault 77 | server configured to authenticate Service Account JWT tokens for the "vault-auth" 78 | Service Account in the "default" namespace. Next we will [deploy a basic 79 | application](./3-deploy-basic.md). 80 | -------------------------------------------------------------------------------- /3-deploy-basic.md: -------------------------------------------------------------------------------- 1 | # Guide 2 | 3 | ## Deploy the basic example 4 | 5 | This example application will read the servcie account JWT token and use it to 6 | authenticate with Vault. It will then log the token (don't log secrets in a 7 | real application!) and keep the token renewed. 8 | 9 | ### Build the basic example 10 | 11 | The easiest way to build the container is to connect your local docker agent 12 | to the remote one in kubernetes. With minikube this can be done with: 13 | 14 | ``` 15 | eval $(minikube docker-env) 16 | ``` 17 | 18 | Then we can build the container: 19 | ``` 20 | cd basic/ 21 | ./build_container 22 | ``` 23 | 24 | ### Run the basic example 25 | 26 | The `VAULT_ADDR` variable in the deployment file should be updated to your vault 27 | server address 28 | 29 | Now we can run the application: 30 | 31 | ``` 32 | kubectl create -f deployment.yaml 33 | ``` 34 | 35 | ### View the logs 36 | 37 | ``` 38 | kubectl logs -f $(kubectl \ 39 | get pods -l app=basic-example \ 40 | -o jsonpath='{.items[0].metadata.name}') 41 | ``` 42 | 43 | You should log output similar to: 44 | ``` 45 | 2017/09/13 23:17:06 ==> WARNING: Don't ever write secrets to logs. 46 | 2017/09/13 23:17:06 ==> This is for demonstration only. 47 | 2017/09/13 23:17:06 b18c700d-2577-8b08-d0c7-49e01aefd8f3 48 | 2017/09/13 23:17:06 Starting renewal loop 49 | ``` 50 | 51 | Then you should see a token renewal approximately every 20s. 52 | 53 | ### Cleanup 54 | 55 | When done delete the deployment and go back to the parent directory: 56 | 57 | ``` 58 | kubectl delete -f deployment.yaml 59 | cd .. 60 | ``` 61 | 62 | ## Next Steps 63 | 64 | Next we will [run a sidecar example](./4-deploy-sidecar.md). 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /4-deploy-sidecar.md: -------------------------------------------------------------------------------- 1 | # Guide 2 | 3 | ## Deploy the sidecar example 4 | 5 | This example will run two containers, the first is running consul 6 | template that first uses the Kubernetes backend to authenticate to Vault. 7 | It then writes secrets to a template file that is in a volume shared with the 8 | second container. Our example app is running in the second container and loads 9 | the template file then logs the secret (don't log secrets in a 10 | real application!) from the template file. Consul template 11 | will keep the secret up to date and the vault token renewed. 12 | 13 | ### Build the sidecar example 14 | 15 | The easiest way to build the containers is to connect your local docker agent 16 | to the remote one in kubernetes. With minikube this can be done with: 17 | 18 | ``` 19 | eval $(minikube docker-env) 20 | ``` 21 | 22 | Then we can build the container: 23 | ``` 24 | cd sidecar/ 25 | ./build_container 26 | ``` 27 | 28 | ### Run the sidecar example 29 | 30 | The `VAULT_ADDR` variable in the deployment file should be updated to your vault 31 | server address 32 | 33 | Now we can run the containers: 34 | 35 | ``` 36 | kubectl create -f deployment.yaml 37 | ``` 38 | 39 | ### View the logs 40 | 41 | ``` 42 | kubectl logs -f $(kubectl \ 43 | get pods -l app=sidecar-example \ 44 | -o jsonpath='{.items[0].metadata.name}') -c app 45 | ``` 46 | 47 | You should log output similar to: 48 | ``` 49 | 2017/09/13 23:38:59 ==> WARNING: Don't ever write secrets to logs. 50 | 2017/09/13 23:38:59 ==> This is for demonstration only. 51 | 2017/09/13 23:38:59 Username: demo 52 | 2017/09/13 23:38:59 Password: test 53 | ``` 54 | 55 | Then you should see a token renewal approximately every 20s. 56 | 57 | ### Cleanup 58 | 59 | When done delete the deployment and go back to the parent directory: 60 | 61 | ``` 62 | kubectl delete -f deployment.yaml 63 | cd .. 64 | ``` 65 | 66 | ## Next Steps 67 | 68 | Next we will [run an example](./5-deploy-aws.md) that can generate AWS IAM credentials dynamically using a Vault token generated by the kubernetes backend. -------------------------------------------------------------------------------- /5-deploy-aws.md: -------------------------------------------------------------------------------- 1 | # Guide 2 | 3 | 4 | ## Deploy the aws example 5 | 6 | This example application will read the servcie account JWT token and use it to 7 | authenticate with Vault. It will then log the token (don't log secrets in a real 8 | application!) and keep the token renewed. Additionally, it will make a request 9 | to the [Vault AWS Secret Backend][vault-aws-secret-backend] dynamically get a 10 | set of IAM credentials. The following setup requires an AWS account and all 11 | operations are performed on the us-east-1 region. It also assumes that you have 12 | your AWS CLI properly configured, with enough permissions to perform actions 13 | against IAM to create a [Vault root user][vault-aws-root-creds]. 14 | 15 | ## Setup the AWS account 16 | 17 | In this step, we wil create a programatic user with enough permissions manage 18 | IAM resources. We will be using the IAMFullAccess policy here, but a more locked 19 | down custom policy can be provided. For an example on such policy template, 20 | refer to the Vault documentation regarding the backend's 21 | [root credentials][vault-aws-root-creds]. 22 | 23 | Create the user: 24 | 25 | ```sh 26 | $ aws iam create-user \ 27 | --user-name vault-root 28 | ``` 29 | 30 | Attach the IAMFullAccess policy to the user: 31 | 32 | ```sh 33 | $ aws iam attach-user-policy \ 34 | --user-name vault-root \ 35 | --policy-arn arn:aws:iam::aws:policy/IAMFullAccess 36 | ``` 37 | 38 | Generate a access key and secret access key pair: 39 | 40 | ```sh 41 | $ aws iam create-access-key \ 42 | --user-name vault-root 43 | ``` 44 | 45 | ## Setup Vault 46 | 47 | Read the `kube-auth` policy to verify that the role has read access to `aws/creds/readonly`: 48 | 49 | ```sh 50 | $ vault policies kube-auth 51 | path "secret/creds" { 52 | capabilities = ["read"] 53 | } 54 | 55 | path "aws/creds/readonly" { 56 | capabilities = ["read"] 57 | } 58 | ``` 59 | 60 | Mount the vault secret backend: 61 | 62 | ```sh 63 | $ vault mount aws 64 | Successfully mounted 'aws' at 'aws'! 65 | ``` 66 | 67 | Configure the aws secret backend: 68 | 69 | ```sh 70 | $ vault write aws/config/root \ 71 | access_key= \ 72 | secret_key= \ 73 | region=us-east-1 74 | Success! Data written to: aws/config/root 75 | ``` 76 | 77 | Create a aws secret backend role that has read-only permissions on EC2 instances 78 | for the account using a AWS managed policy this policy: 79 | 80 | ```sh 81 | $ vault write aws/roles/readonly \ 82 | arn=arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess 83 | Success! Data written to: aws/roles/readonly 84 | ``` 85 | 86 | The vault client on the example application will be using this role to request 87 | IAM credentials during runtime. 88 | 89 | ### Build the aws example 90 | 91 | The easiest way to build the container is to connect your local docker agent 92 | to the remote one in kubernetes. With minikube this can be done with: 93 | 94 | ``` 95 | eval $(minikube docker-env) 96 | ``` 97 | 98 | Then we can build the container: 99 | ``` 100 | cd basic/ 101 | ./build_container 102 | ``` 103 | 104 | ### Run the aws example 105 | 106 | The `VAULT_ADDR` variable in the deployment file should be updated to your vault 107 | server address 108 | 109 | Now we can run the application: 110 | 111 | ``` 112 | kubectl create -f deployment.yaml 113 | ``` 114 | 115 | ### View the logs 116 | 117 | ```sh 118 | kubectl logs -f $(kubectl \ 119 | get pods -l app=aws-example \ 120 | -o jsonpath='{.items[0].metadata.name}') 121 | 2017/11/21 22:39:10 ==> WARNING: Don't ever write secrets to logs. 122 | 2017/11/21 22:39:10 ==> This is for demonstration only. 123 | 2017/11/21 22:39:10 Vault token: e8052101-1e9c-ef0d-d91a-be18192a37c6 124 | 2017/11/21 22:39:41 AWS Access Key: AKIAIVUVHJF3REXAMPLE 125 | 2017/11/21 22:39:41 AWS Secret Key: mkQZa9SW0c0tWn5F297FCGpPUSXYsZqvrEXAMPLE 126 | 2017/11/21 22:39:41 ==> Listing EC2 clients using generated credentials 127 | 2017/11/21 22:39:41 i-050168cbc1c0dd111 128 | 2017/11/21 22:39:41 Starting renewal loop 129 | ``` 130 | 131 | Then you should see a token renewal approximately every 20s. 132 | 133 | ### Cleanup 134 | 135 | When done delete the deployment and go back to the parent directory: 136 | 137 | ``` 138 | kubectl delete -f deployment.yaml 139 | cd .. 140 | ``` 141 | 142 | [vault-aws-secret-backend]: https://www.vaultproject.io/docs/secrets/aws/index.html 143 | [vault-aws-root-creds]: https://www.vaultproject.io/docs/secrets/aws/index.html#root-credentials-for-dynamic-iam-users 144 | 145 | ## Next Steps 146 | 147 | Read more about the kubernetes auth backend! -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Brian Kassouf 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vault-kubernetes-demo 2 | 3 | ## Status 4 | 5 | This is a demo. Do not use this in production. 6 | 7 | ## Usage 8 | 9 | This is a demo on how to setup the Vault Kubernetes Auth Backend. It will takes steps to configure a the Kubernetes Auth backend, create service accounts in Kubernetes and run some demo applications on Kubernetes that access Vault. 10 | 11 | ## Next Steps 12 | 13 | The first step in the guide is to [configure a Kubernetes Service Account](1-configure-kubernetes.md) 14 | -------------------------------------------------------------------------------- /aws/Dockerfile: -------------------------------------------------------------------------------- 1 | from scratch 2 | 3 | ADD vault-example-aws /vault-example-aws 4 | ENTRYPOINT ["/vault-example-aws"] 5 | -------------------------------------------------------------------------------- /aws/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | GOOS=linux go build \ 3 | -a --ldflags '-extldflags "-static"' \ 4 | -tags netgo \ 5 | -installsuffix netgo \ 6 | -o vault-example-aws . 7 | -------------------------------------------------------------------------------- /aws/build_container: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./build 4 | docker build -t hashicorp/vault-example-aws:0.0.1 . 5 | rm vault-example-aws 6 | -------------------------------------------------------------------------------- /aws/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: aws-example 5 | spec: 6 | replicas: 1 7 | template: 8 | metadata: 9 | labels: 10 | app: aws-example 11 | spec: 12 | serviceAccountName: vault-auth 13 | containers: 14 | - name: app 15 | image: "hashicorp/vault-example-aws:0.0.1" 16 | imagePullPolicy: IfNotPresent 17 | env: 18 | - name: VAULT_ADDR 19 | value: "http://10.0.2.2:8200" 20 | -------------------------------------------------------------------------------- /aws/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | "os/signal" 9 | "syscall" 10 | "time" 11 | 12 | "github.com/hashicorp/vault/api" 13 | ) 14 | 15 | func main() { 16 | config := api.DefaultConfig() 17 | vaultClient, err := api.NewClient(config) 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | 22 | content, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token") 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | 27 | s, err := vaultClient.Logical().Write("/auth/kubernetes/login", map[string]interface{}{ 28 | "role": "demo", 29 | "jwt": string(content[:]), 30 | }) 31 | if err != nil { 32 | fmt.Println(err) 33 | return 34 | } 35 | 36 | log.Println("==> WARNING: Don't ever write secrets to logs.") 37 | log.Println("==> This is for demonstration only.") 38 | log.Printf("Vault token: %s\n", s.Auth.ClientToken) 39 | 40 | vaultClient.SetToken(s.Auth.ClientToken) 41 | s, err = vaultClient.Logical().Read("/aws/creds/readonly") 42 | if err != nil { 43 | fmt.Println(err) 44 | return 45 | } 46 | 47 | // Give some time for IAM cred creation to propagate since this action is 48 | // eventually consistent 49 | time.Sleep(30 * time.Second) 50 | 51 | accessKey := s.Data["access_key"].(string) 52 | secretKey := s.Data["secret_key"].(string) 53 | log.Println("==> WARNING: Don't ever write secrets to logs.") 54 | log.Println("==> This is for demonstration only.") 55 | log.Printf("AWS Access Key: %s\n", accessKey) 56 | log.Printf("AWS Secret Key: %s\n", secretKey) 57 | 58 | quit := make(chan os.Signal, 1) 59 | signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) 60 | 61 | // Keep token renewed 62 | renewer, err := vaultClient.NewRenewer(&api.RenewerInput{ 63 | Secret: s, 64 | Grace: 1 * time.Second, 65 | }) 66 | if err != nil { 67 | log.Fatal(err) 68 | } 69 | 70 | log.Println("Starting renewal loop") 71 | go renewer.Renew() 72 | defer renewer.Stop() 73 | 74 | for { 75 | select { 76 | case err := <-renewer.DoneCh(): 77 | if err != nil { 78 | log.Fatal(err) 79 | } 80 | case renewal := <-renewer.RenewCh(): 81 | log.Printf("Successfully renewed: %#v", renewal) 82 | case <-quit: 83 | log.Fatal("Shutdown signal received, exiting...") 84 | } 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /basic/Dockerfile: -------------------------------------------------------------------------------- 1 | from scratch 2 | 3 | ADD vault-init /vault-init 4 | ENTRYPOINT ["/vault-init"] 5 | -------------------------------------------------------------------------------- /basic/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | GOOS=linux go build \ 3 | -a --ldflags '-extldflags "-static"' \ 4 | -tags netgo \ 5 | -installsuffix netgo \ 6 | -o vault-init . 7 | -------------------------------------------------------------------------------- /basic/build_container: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./build 4 | docker build -t hashicorp/vault-example-init:0.0.1 . 5 | rm vault-init 6 | -------------------------------------------------------------------------------- /basic/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: basic-example 5 | spec: 6 | replicas: 1 7 | template: 8 | metadata: 9 | labels: 10 | app: basic-example 11 | spec: 12 | serviceAccountName: vault-auth 13 | containers: 14 | - name: app 15 | image: "hashicorp/vault-example-init:0.0.1" 16 | imagePullPolicy: IfNotPresent 17 | env: 18 | - name: VAULT_ADDR 19 | value: "http://10.0.2.2:8200" 20 | -------------------------------------------------------------------------------- /basic/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | "os/signal" 9 | "syscall" 10 | "time" 11 | 12 | "github.com/hashicorp/vault/api" 13 | ) 14 | 15 | func main() { 16 | config := api.DefaultConfig() 17 | vaultClient, err := api.NewClient(config) 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | 22 | content, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token") 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | 27 | s, err := vaultClient.Logical().Write("/auth/kubernetes/login", map[string]interface{}{ 28 | "role": "demo", 29 | "jwt": string(content[:]), 30 | }) 31 | if err != nil { 32 | fmt.Println(err) 33 | return 34 | } 35 | 36 | log.Println("==> WARNING: Don't ever write secrets to logs.") 37 | log.Println("==> This is for demonstration only.") 38 | log.Println(s.Auth.ClientToken) 39 | 40 | quit := make(chan os.Signal, 1) 41 | signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) 42 | 43 | // Keep token renewed 44 | renewer, err := vaultClient.NewRenewer(&api.RenewerInput{ 45 | Secret: s, 46 | Grace: 1 * time.Second, 47 | }) 48 | if err != nil { 49 | log.Fatal(err) 50 | } 51 | 52 | log.Println("Starting renewal loop") 53 | go renewer.Renew() 54 | defer renewer.Stop() 55 | 56 | for { 57 | select { 58 | case err := <-renewer.DoneCh(): 59 | if err != nil { 60 | log.Fatal(err) 61 | } 62 | case renewal := <-renewer.RenewCh(): 63 | log.Printf("Successfully renewed: %#v", renewal) 64 | case <-quit: 65 | log.Fatal("Shutdown signal received, exiting...") 66 | } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /kube-auth.hcl: -------------------------------------------------------------------------------- 1 | path "secret/creds" { 2 | capabilities = ["read"] 3 | } 4 | 5 | path "aws/creds/readonly" { 6 | capabilities = ["read"] 7 | } -------------------------------------------------------------------------------- /sidecar/Dockerfile: -------------------------------------------------------------------------------- 1 | from scratch 2 | 3 | ADD vault-example-sidecar-app /vault-example-sidecar-app 4 | ENTRYPOINT ["/vault-example-sidecar-app"] 5 | -------------------------------------------------------------------------------- /sidecar/Dockerfile-consul-template: -------------------------------------------------------------------------------- 1 | from hashicorp/consul-template:alpine 2 | 3 | RUN apk add --no-cache jq 4 | ADD consul-template-wrapper.sh /consul-template-wrapper.sh 5 | ADD config.ctmpl /config.ctmpl 6 | CMD ["sh", "/consul-template-wrapper.sh"] 7 | -------------------------------------------------------------------------------- /sidecar/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | GOOS=linux go build \ 3 | -o vault-example-sidecar-app . 4 | -------------------------------------------------------------------------------- /sidecar/build_container: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./build 4 | docker build -t hashicorp/vault-example-sidecar-app:0.0.1 . 5 | rm vault-example-sidecar-app 6 | 7 | docker build -t hashicorp/vault-example-sidecar-consul-template:0.0.1 -f Dockerfile-consul-template . 8 | -------------------------------------------------------------------------------- /sidecar/config.ctmpl: -------------------------------------------------------------------------------- 1 | { 2 | {{ with secret "secret/creds"}} 3 | {{ if .Data.username }} 4 | "username": "{{ .Data.username }}", 5 | {{ end }} 6 | {{ if .Data.password }} 7 | "password": "{{ .Data.password }}" 8 | {{ end }} 9 | {{ end }} 10 | } 11 | -------------------------------------------------------------------------------- /sidecar/consul-template-wrapper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | SERVICE_ACCOUNT_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) 4 | 5 | VAULT_TOKEN=$(curl -sb \ 6 | --request POST \ 7 | --data "{\"role\": \"demo\", \"jwt\": \"${SERVICE_ACCOUNT_TOKEN}\"}" \ 8 | "${VAULT_ADDR}/v1/auth/kubernetes/login" | jq -r '.auth .client_token') 9 | 10 | /bin/consul-template \ 11 | --vault-token=$VAULT_TOKEN \ 12 | --vault-addr=$VAULT_ADDR \ 13 | --vault-renew-token=true \ 14 | -template "/config.ctmpl:/etc/example-app/config.json" 15 | -------------------------------------------------------------------------------- /sidecar/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: sidecar-example 5 | spec: 6 | replicas: 1 7 | template: 8 | metadata: 9 | labels: 10 | app: sidecar-example 11 | spec: 12 | serviceAccountName: vault-auth 13 | containers: 14 | - name: app 15 | image: "hashicorp/vault-example-sidecar-app:0.0.1" 16 | imagePullPolicy: IfNotPresent 17 | volumeMounts: 18 | - name: app-secrets 19 | mountPath: "/etc/example-app/" 20 | - name: consul-template 21 | image: "hashicorp/vault-example-sidecar-consul-template:0.0.1" 22 | imagePullPolicy: IfNotPresent 23 | env: 24 | - name: VAULT_ADDR 25 | value: "http://10.0.2.2:8200" 26 | volumeMounts: 27 | - name: app-secrets 28 | mountPath: "/etc/example-app/" 29 | volumes: 30 | - name: app-secrets 31 | emptyDir: 32 | medium: "Memory" 33 | -------------------------------------------------------------------------------- /sidecar/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | "os/signal" 9 | "syscall" 10 | "time" 11 | ) 12 | 13 | const configLocation string = "/etc/example-app/config.json" 14 | 15 | type config struct { 16 | Username string `json:"username"` 17 | Password string `json:"password"` 18 | } 19 | 20 | func main() { 21 | config, err := getConfig() 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | 26 | log.Println("==> WARNING: Don't ever write secrets to logs.") 27 | log.Println("==> This is for demonstration only.") 28 | log.Printf("Username: %s", config.Username) 29 | log.Printf("Password: %s", config.Password) 30 | 31 | signalChan := make(chan os.Signal, 1) 32 | signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) 33 | <-signalChan 34 | 35 | log.Printf("Shutdown signal received shutting down gracefully...") 36 | } 37 | 38 | func getConfig() (*config, error) { 39 | for { 40 | content, err := ioutil.ReadFile(configLocation) 41 | switch { 42 | case os.IsNotExist(err): 43 | time.Sleep(5 * time.Millisecond) 44 | continue 45 | case err != nil: 46 | return nil, err 47 | } 48 | 49 | var config *config = &config{} 50 | err = json.Unmarshal(content, config) 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | return config, nil 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /vault-auth.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: vault-auth 5 | -------------------------------------------------------------------------------- /vault-reviewer-rbac.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1beta1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: role-tokenreview-binding 5 | namespace: default 6 | roleRef: 7 | apiGroup: rbac.authorization.k8s.io 8 | kind: ClusterRole 9 | name: system:auth-delegator 10 | subjects: 11 | - kind: ServiceAccount 12 | name: vault-reviewer 13 | namespace: default 14 | -------------------------------------------------------------------------------- /vault-reviewer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: vault-reviewer 5 | --------------------------------------------------------------------------------