commit
09dfcb2120
44
README.md
44
README.md
|
@ -2,7 +2,7 @@
|
||||||
### **Vault Side Kick**
|
### **Vault Side Kick**
|
||||||
|
|
||||||
**Summary:**
|
**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.
|
(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:**
|
**Usage:**
|
||||||
|
@ -31,17 +31,17 @@ The below is taken from a [Kubernetes](https://github.com/kubernetes/kubernetes)
|
||||||
```YAML
|
```YAML
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: vault-side-kick
|
- name: vault-side-kick
|
||||||
image: gambol99/vault-sidekick:latest
|
image: gambol99/vault-sidekick:latest
|
||||||
args:
|
args:
|
||||||
- -output=/etc/secrets
|
- -output=/etc/secrets
|
||||||
- -rn=pki:example.com:cn=commons.example.com,exec=/usr/bin/nginx_restart.sh,ctr=.*nginx_server.*
|
- -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=secret:db/prod/password
|
||||||
- -rn=aws:s3_backsup:fn=.s3_creds
|
- -rn=aws:s3_backsup:fn=.s3_creds
|
||||||
- -rb=template:database_credentials:tpl=/etc/templates/db.tmpl,fn=/etc/credentials
|
- -rb=template:database_credentials:tpl=/etc/templates/db.tmpl,fn=/etc/credentials
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: secrets
|
- name: secrets
|
||||||
mountPath: /etc/secrets
|
mountPath: /etc/secrets
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -50,13 +50,13 @@ The above say's
|
||||||
- Write all the secrets to the /etc/secrets directory
|
- 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 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
|
- 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
|
- 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
|
- Read the template at /etc/templates/db.tmpl, produce the content from Vault and write to /etc/credentials file
|
||||||
|
|
||||||
**Secret Renewals**
|
**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
|
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,
|
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
|
your using the mysql dynamic secrets, you want to renew the secret not replace it
|
||||||
|
|
||||||
```shell
|
```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
|
Or you want to rotate the secret every **1h** and **revoke** the previous one
|
||||||
|
|
||||||
```shell
|
```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**
|
**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
|
Using the following at the demo secrets
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
[jest@starfury vault-sidekick]$ vault write secret/password this=is demo=value nothing=more
|
[jest@starfury vault-sidekick]$ vault write secret/password this=is demo=value nothing=more
|
||||||
Success! Data written to: secret/password
|
Success! Data written to: secret/password
|
||||||
[jest@starfury vault-sidekick]$ vault read secret/password
|
[jest@starfury vault-sidekick]$ vault read secret/password
|
||||||
Key Value
|
Key Value
|
||||||
lease_id secret/password/7908eceb-9bde-e7de-23da-96131505214a
|
lease_id secret/password/7908eceb-9bde-e7de-23da-96131505214a
|
||||||
lease_duration 2592000
|
lease_duration 2592000
|
||||||
|
@ -91,22 +91,24 @@ nothing more
|
||||||
this is
|
this is
|
||||||
```
|
```
|
||||||
|
|
||||||
In order to change the output format:
|
In order to change the output format:
|
||||||
|
|
||||||
```shell
|
```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=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=json -logtostderr=true -dry-run
|
||||||
[jest@starfury vault-sidekick]$ build/vault-sidekick -cn=secret:password:fmt=yaml -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
|
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
|
(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**
|
**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
|
- **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
|
- **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)
|
- **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
|
- **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.
|
- **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.
|
||||||
|
|
30
main.go
30
main.go
|
@ -20,6 +20,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -97,6 +98,28 @@ func processResource(rn *vaultResource, data map[string]interface{}) error {
|
||||||
buf.WriteString(fmt.Sprintf("%s = %s\n", key, val))
|
buf.WriteString(fmt.Sprintf("%s = %s\n", key, val))
|
||||||
}
|
}
|
||||||
content = buf.Bytes()
|
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":
|
case "txt":
|
||||||
keys := getKeys(data)
|
keys := getKeys(data)
|
||||||
if len(keys) > 1 {
|
if len(keys) > 1 {
|
||||||
|
@ -140,15 +163,10 @@ func writeFile(filename string, content []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := os.Create(filename)
|
err := ioutil.WriteFile(filename, content, 0660)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
if _, err := file.Write(content); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
|
@ -44,7 +44,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
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
|
// a map of valid resource to retrieve from vault
|
||||||
validResources = map[string]bool{
|
validResources = map[string]bool{
|
||||||
|
|
Reference in a new issue