From b2e19a1bb2bc59b7d0e8b6eb447ce6868a467c02 Mon Sep 17 00:00:00 2001 From: Rohith Date: Fri, 18 Sep 2015 17:45:34 +0100 Subject: [PATCH] - added the cert file format; essentially a hack to create the three file --- README.md | 44 +++++++++++++++++++++++--------------------- main.go | 30 ++++++++++++++++++++++++------ vault_resource.go | 2 +- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 82658e7..49b979d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ### **Vault Side Kick** **Summary:** -Vault Sidekick is a add-on container which can be used as a generic entry-point for interacting with Hashicorp [Vault](https://vaultproject.io) service, retrieving secrets +Vault Sidekick is a add-on container which can be used as a generic entry-point for interacting with Hashicorp [Vault](https://vaultproject.io) service, retrieving secrets (both static and dynamic) and PKI certs. The sidekick will take care of renewal's and extension of leases for you and renew the credentials in the specified format for you. **Usage:** @@ -31,17 +31,17 @@ The below is taken from a [Kubernetes](https://github.com/kubernetes/kubernetes) ```YAML spec: containers: - - name: vault-side-kick + - name: vault-side-kick image: gambol99/vault-sidekick:latest args: - -output=/etc/secrets - -rn=pki:example.com:cn=commons.example.com,exec=/usr/bin/nginx_restart.sh,ctr=.*nginx_server.* - - -rn=secret:db/prod/username:fn=.credentials + - -rn=secret:db/prod/username:fn=.credentials - -rn=secret:db/prod/password - -rn=aws:s3_backsup:fn=.s3_creds - -rb=template:database_credentials:tpl=/etc/templates/db.tmpl,fn=/etc/credentials volumeMounts: - - name: secrets + - name: secrets mountPath: /etc/secrets ``` @@ -50,13 +50,13 @@ The above say's - Write all the secrets to the /etc/secrets directory - Retrieve a dynamic certificate pair for me, with the common name: 'commons.example.com' and renew the cert when it expires automatically - Retrieve the two static secrets /db/prod/{username,password} and write them to .credentials and password.secret respectively - - Apply the IAM policy, renew the policy when required and file the API tokens to .s3_creds in the /etc/secrets directory - - Read the template at /etc/templates/db.tmpl, produce the content from Vault and write to /etc/credentials file - + - Apply the IAM policy, renew the policy when required and file the API tokens to .s3_creds in the /etc/secrets directory + - Read the template at /etc/templates/db.tmpl, produce the content from Vault and write to /etc/credentials file + **Secret Renewals** - -The default behaviour of vault-sidekick is **not** to renew a lease, but to retrieve a new secret and allow the previous to -expire, in order ensure the rotation of secrets. If you don't want this behaviour on a resource you can override using resource options. For exmaple, + +The default behaviour of vault-sidekick is **not** to renew a lease, but to retrieve a new secret and allow the previous to +expire, in order ensure the rotation of secrets. If you don't want this behaviour on a resource you can override using resource options. For exmaple, your using the mysql dynamic secrets, you want to renew the secret not replace it ```shell @@ -69,19 +69,19 @@ or an iam policy renewed every hour Or you want to rotate the secret every **1h** and **revoke** the previous one ```shell -[jest@starfury vault-sidekick]$ build/vault-sidekick -cn=aws:my_s3_bucket:fmt=yaml,up=1h,rv=true +[jest@starfury vault-sidekick]$ build/vault-sidekick -cn=aws:my_s3_bucket:fmt=yaml,up=1h,rv=true ``` - + **Output Formatting** -The following output formats are supported: json, yaml, ini, txt - +The following output formats are supported: json, yaml, ini, txt, cert + Using the following at the demo secrets ```shell [jest@starfury vault-sidekick]$ vault write secret/password this=is demo=value nothing=more Success! Data written to: secret/password -[jest@starfury vault-sidekick]$ vault read secret/password +[jest@starfury vault-sidekick]$ vault read secret/password Key Value lease_id secret/password/7908eceb-9bde-e7de-23da-96131505214a lease_duration 2592000 @@ -91,22 +91,24 @@ nothing more this is ``` -In order to change the output format: - +In order to change the output format: + ```shell [jest@starfury vault-sidekick]$ build/vault-sidekick -cn=secret:password:fmt=ini -logtostderr=true -dry-run [jest@starfury vault-sidekick]$ build/vault-sidekick -cn=secret:password:fmt=json -logtostderr=true -dry-run [jest@starfury vault-sidekick]$ build/vault-sidekick -cn=secret:password:fmt=yaml -logtostderr=true -dry-run ``` -The default format is 'txt' which has the following behavour. If the number of keys in a resource is > 1, a file is created per key. Thus using the example -(build/vault-sidekick -cn=secret:password:fn=test) we would end up with files: test.this, test.nothing and test.demo +The default format is 'txt' which has the following behavour. If the number of keys in a resource is > 1, a file is created per key. Thus using the example +(build/vault-sidekick -cn=secret:password:fn=test) we would end up with files: test.this, test.nothing and test.demo + +Format: 'cert' is less of a format of more file scheme i.e. is just extracts the 'certificate', 'issuing_ca' and 'private_key' and creates the three files FILE.{ca,key,crt} **Resource Options** - **fn**: (filaname) by default all file are relative to the output directory specified and will have the name NAME.RESOURCE; the fn options allows you to switch names and paths to write the files -- **up**: (update) override the lease time of this resource and get/renew a secret on the specified duration e.g 1m, 2d, 5m10s +- **up**: (update) override the lease time of this resource and get/renew a secret on the specified duration e.g 1m, 2d, 5m10s - **rn**: (renewal) override the default behavour on this resource, renew the resource when coming close to expiration e.g true, TRUE - **rv**: (revoke) revoke the old lease when you get retrieve a old one e.g. true, TRUE (default to allow the lease to expire and naturally revoke) - **fmt**: (format) allows you to specify the output format of the resource / secret, e.g json, yaml, ini, txt -- **cn**: (comman name) is used in conjunction with the PKI resource. The common argument is passed as an argument when make a request to issue the certs. \ No newline at end of file +- **cn**: (comman name) is used in conjunction with the PKI resource. The common argument is passed as an argument when make a request to issue the certs. diff --git a/main.go b/main.go index 3e85387..e9453fb 100644 --- a/main.go +++ b/main.go @@ -20,6 +20,7 @@ import ( "bytes" "encoding/json" "fmt" + "io/ioutil" "os" "os/signal" "strings" @@ -97,6 +98,28 @@ func processResource(rn *vaultResource, data map[string]interface{}) error { buf.WriteString(fmt.Sprintf("%s = %s\n", key, val)) } content = buf.Bytes() + // Less of a format and more of a standard naming scheme + case "cert": + files := map[string]string{ + "certificate": "crt", + "issuing_ca": "ca", + "private_key": "key", + } + for key, suffix := range files { + filename := fmt.Sprintf("%s.%s", resourcePath, suffix) + content, found := data[key] + if !found { + continue + } + + // step: write the file + if err := writeFile(filename, []byte(fmt.Sprintf("%s", content))); err != nil { + glog.Errorf("failed to write resource: %s, elemment: %s, filename: %s, error: %s", rn, suffix, filename, err) + continue + } + } + return nil + case "txt": keys := getKeys(data) if len(keys) > 1 { @@ -140,15 +163,10 @@ func writeFile(filename string, content []byte) error { return nil } - file, err := os.Create(filename) + err := ioutil.WriteFile(filename, content, 0660) if err != nil { return err } - defer file.Close() - - if _, err := file.Write(content); err != nil { - return err - } return nil } \ No newline at end of file diff --git a/vault_resource.go b/vault_resource.go index 8e87764..76025d5 100644 --- a/vault_resource.go +++ b/vault_resource.go @@ -44,7 +44,7 @@ const ( ) var ( - resourceFormatRegex = regexp.MustCompile("^(yaml|json|ini|txt)$") + resourceFormatRegex = regexp.MustCompile("^(yaml|json|ini|txt|cert)$") // a map of valid resource to retrieve from vault validResources = map[string]bool{