Merge pull request #8 from gambol99/fix/ca_root

Fix/ca root
This commit is contained in:
Rohith 2015-10-12 11:06:05 +01:00
commit 4e2640171b
6 changed files with 165 additions and 23 deletions

36
CONDUCT_CODE.md Normal file
View file

@ -0,0 +1,36 @@
# Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of fostering an open and
welcoming community, we pledge to respect all people who contribute through reporting issues,
posting feature requests, updating documentation, submitting pull requests or patches, and other
activities.
We are committed to making participation in this project a harassment-free experience for everyone,
regardless of level of experience, gender, gender identity and expression, sexual orientation,
disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic addresses, without explicit
permission
* Other unethical or unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits,
code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By
adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently
applying these principles to every aspect of managing this project. Project maintainers who do not
follow or enforce the Code of Conduct may be permanently removed from the project team.
This code of conduct applies both within project spaces and in public spaces when an individual is
representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an
issue or contacting one or more of the project maintainers.
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org),
version 1.2.0, available at
[http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/)

54
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,54 @@
# Contributing
When contributing to this repository, please first discuss the change you wish to make via issue,
email, or any other method with the owners of this repository before making a change.
Please note we have a code of conduct, please follow it in all your interactions with the project.
## Pull Request Process
1. Ensure any install or build dependencies are removed before the end of the layer when doing a
build.
2. Update the README.md with details of changes to the interface, this includes new environment
variables, exposed ports, useful file locations and container parameters.
3. Increase the version numbers in any examples files and the README.md to the new version that this
Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you
do not have permission to do that, you may request the second reviewer to merge it for you.
## Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of fostering an open and
welcoming community, we pledge to respect all people who contribute through reporting issues,
posting feature requests, updating documentation, submitting pull requests or patches, and other
activities.
We are committed to making participation in this project a harassment-free experience for everyone,
regardless of level of experience, gender, gender identity and expression, sexual orientation,
disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic addresses, without explicit
permission
* Other unethical or unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits,
code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By
adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently
applying these principles to every aspect of managing this project. Project maintainers who do not
follow or enforce the Code of Conduct may be permanently removed from the project team.
This code of conduct applies both within project spaces and in public spaces when an individual is
representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an
issue or contacting one or more of the project maintainers.
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org),
version 1.2.0, available at
[http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/)

View file

@ -28,6 +28,10 @@ Usage of bin/vault-sidekick:
-vmodule=: comma-separated list of pattern=N settings for file-filtered logging
```
**Building**
There is a Makefile in the base repository, so assuming you have make and go: # make
**Example Usage**
The below is taken from a [Kubernetes](https://github.com/kubernetes/kubernetes) pod specification;

View file

@ -31,6 +31,8 @@ type config struct {
vaultAuthFile string
// the authentication options
vaultAuthOptions map[string]string
// the vault ca file
vaultCaFile string
// the place to write the resources
outputDir string
// switch on dry run
@ -52,13 +54,14 @@ func init() {
options.resources = new(VaultResources)
options.vaultAuthOptions = map[string]string{VaultAuth: "token"}
flag.StringVar(&options.vaultURL, "vault", getEnv("VAULT_ADDR", "https://127.0.0.1:8200"), "the url the vault service is running behind (VAULT_ADDR if available)")
flag.StringVar(&options.vaultAuthFile, "auth", "", "a configuration file in a json or yaml containing authentication arguments")
flag.StringVar(&options.outputDir, "output", getEnv("VAULT_OUTPUT", "/etc/secrets"), "the full path to write the protected resources (VAULT_OUTPUT if available)")
flag.StringVar(&options.vaultURL, "vault", getEnv("VAULT_ADDR", "https://127.0.0.1:8200"), "url the vault service or VAULT_ADDR")
flag.StringVar(&options.vaultAuthFile, "auth", "", "a configuration file in json or yaml containing authentication arguments")
flag.StringVar(&options.outputDir, "output", getEnv("VAULT_OUTPUT", "/etc/secrets"), "the full path to write resources or VAULT_OUTPUT")
flag.BoolVar(&options.dryRun, "dryrun", false, "perform a dry run, printing the content to screen")
flag.BoolVar(&options.tlsVerify, "tls-skip-verify", false, "whether to check and verify the vault service certificate")
flag.StringVar(&options.vaultCaFile, "ca-cert", "", "the path to the file container the CA used to verify the vault service")
flag.DurationVar(&options.statsInterval, "stats", time.Duration(5)*time.Minute, "the interval to produce statistics on the accessed resources")
flag.Var(options.resources, "cn", "a resource to retrieve and monitor from vault (e.g. pki:name:cert.name, secret:db_password, aws:s3_backup)")
flag.Var(options.resources, "cn", "a resource to retrieve and monitor from vault")
}
// parseOptions ... validate the command line options and validates them
@ -87,5 +90,15 @@ func validateOptions(cfg *config) error {
}
}
if cfg.vaultCaFile != "" {
if exists, _ := fileExists(cfg.vaultCaFile); !exists {
return fmt.Errorf("the ca certificate file: %s does not exist", cfg.vaultCaFile)
}
}
if cfg.tlsVerify == true && cfg.vaultCaFile != "" {
return fmt.Errorf("you are skipping the tls but supplying a CA, doesn't make sense")
}
return nil
}

View file

@ -27,7 +27,6 @@ import (
const (
Prog = "vault-sidekick"
Version = "0.0.1"
GitSha = ""
)
func main() {

View file

@ -18,7 +18,9 @@ package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net/http"
"time"
@ -46,6 +48,7 @@ type VaultService struct {
config *api.Config
// the token to authenticate with
token string
// the listener channel - technically we only have the one listener but there a long term reasons for adding this
listeners []chan VaultEvent
// a channel to inform of a new resource to processor
@ -71,11 +74,10 @@ func NewVaultService(url string) (*VaultService, error) {
service.config.Address = url
service.listeners = make([]chan VaultEvent, 0)
// step: skip the cert verification if requested
if options.tlsVerify {
service.config.HttpClient.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
// step: setup and generate the tls options
service.config.HttpClient.Transport, err = service.getHttpTransport()
if err != nil {
return nil, err
}
// step: create the service processor channels
@ -99,6 +101,29 @@ func NewVaultService(url string) (*VaultService, error) {
return service, nil
}
func (r *VaultService) getHttpTransport() (*http.Transport, error) {
transport := &http.Transport{}
// step: are we skip the tls verify?
if options.tlsVerify {
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
// step: are we loading a CA file
if options.vaultCaFile != "" {
// step: load the ca file
caCert, err := ioutil.ReadFile(options.vaultCaFile)
if err != nil {
return nil, fmt.Errorf("unable to read in the ca: %s, reason: %s", options.vaultCaFile, err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
// step: add the ca to the root
transport.TLSClientConfig.RootCAs = caCertPool
}
return transport, nil
}
// AddListener ... add a listener to the events listeners
func (r *VaultService) AddListener(ch chan VaultEvent) {
glog.V(10).Infof("adding the listener: %v", ch)
@ -127,16 +152,17 @@ func (r *VaultService) vaultServiceProcessor() {
for {
select {
// A new resource is being added to the service processor;
// - we retrieve the resource from vault
// - if we error attempting to retrieve the secret, we background and reschedule an attempt to add it
// - if ok, we grab the lease it and lease time, we setup a notification on renewal
// - schedule the resource for retrieval
case x := <-r.resourceChannel:
glog.V(4).Infof("adding a resource into the service processor, resource: %s", x.resource)
// step: add to the list of resources
items = append(items, x)
// step: push into the retrieval channel
retrieveChannel <- x
r.scheduleNow(x, retrieveChannel)
// Retrieve a resource from vault
// - we retrieve the resource from vault
// - if we error attempting to retrieve the secret, we background and reschedule an attempt to add it
// - if ok, we grab the lease it and lease time, we setup a notification on renewal
case x := <-retrieveChannel:
// step: save the current lease if we have one
leaseID := ""
@ -150,7 +176,7 @@ func (r *VaultService) vaultServiceProcessor() {
if err != nil {
glog.Errorf("failed to retrieve the resource: %s from vault, error: %s", x.resource, err)
// reschedule the attempt for later
r.reschedule(x, retrieveChannel, 3, 10)
r.scheduleIn(x, retrieveChannel, 3, 10)
break
}
@ -180,7 +206,7 @@ func (r *VaultService) vaultServiceProcessor() {
if time.Now().Before(x.leaseExpireTime) {
glog.V(3).Infof("the lease on resource: %s has expired, we need to get a new lease", x.resource)
// push into the retrieval channel and break
retrieveChannel <- x
r.scheduleNow(x, retrieveChannel)
break
}
@ -189,7 +215,7 @@ func (r *VaultService) vaultServiceProcessor() {
// step: is the underlining resource even renewable? - otherwise we can just grab a new lease
if !x.secret.Renewable {
glog.V(10).Infof("the resource: %s is not renewable, retrieving a new lease instead", x.resource)
retrieveChannel <- x
r.scheduleNow(x, retrieveChannel)
break
}
@ -198,7 +224,7 @@ func (r *VaultService) vaultServiceProcessor() {
if err != nil {
glog.Errorf("failed to renew the resounce: %s for renewal, error: %s", x.resource, err)
// reschedule the attempt for later
r.reschedule(x, renewChannel, 3, 10)
r.scheduleIn(x, renewChannel, 3, 10)
break
}
}
@ -206,7 +232,7 @@ func (r *VaultService) vaultServiceProcessor() {
// step: the option for this resource is not to renew the secret but regenerate a new secret
if !x.resource.renewable {
glog.V(4).Infof("resource: %s flagged as not renewable, shifting to regenerating the resource", x.resource)
retrieveChannel <- x
r.scheduleNow(x, retrieveChannel)
break
}
@ -255,15 +281,25 @@ func (r VaultService) authenticate(auth map[string]string) (string, error) {
return secret, err
}
// reschedule ... reschedules an event back into a channel after n seconds
// scheduleNow ... a helper method to perform an immediate reschedule into a channel
// rn : a pointer to the watched resource you wish to reschedule
// ch : the channel the resource should be placed into
func (r VaultService) scheduleNow(rn *watchedResource, ch chan *watchedResource) {
r.scheduleIn(rn, ch, 0, 0)
}
// scheduleIn ... schedules an event back into a channel after n seconds
// rn : a pointer to the watched resource you wish to reschedule
// ch : the channel the resource should be placed into
// min : the minimum amount of time i'm willing to wait
// max : the maximum amount of time i'm willing to wait
func (r VaultService) reschedule(rn *watchedResource, ch chan *watchedResource, min, max int) {
func (r VaultService) scheduleIn(rn *watchedResource, ch chan *watchedResource, min, max int) {
go func(x *watchedResource) {
glog.V(3).Infof("rescheduling the resource: %s, channel: %v", rn.resource, ch)
<-randomWait(min, max)
// step: are we doing a random wait?
if min > 0 {
<-randomWait(min, max)
}
ch <- x
}(rn)
}