Merge pull request #7 from UKHomeOffice/cert_bundle

- adding the bundle format option
This commit is contained in:
Rohith 2016-02-25 15:33:34 +00:00
commit 7d4c6add13
5 changed files with 100 additions and 64 deletions

View file

@ -59,7 +59,7 @@ func init() {
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.DurationVar(&options.statsInterval, "stats", time.Duration(1)*time.Hour, "the interval to produce statistics on the accessed resources")
flag.Var(options.resources, "cn", "a resource to retrieve and monitor from vault")
}

View file

@ -26,7 +26,7 @@ import (
const (
Prog = "vault-sidekick"
Version = "v0.0.3"
Version = "v0.0.4"
)
func main() {

View file

@ -30,6 +30,7 @@ import (
"github.com/golang/glog"
"gopkg.in/yaml.v2"
"path/filepath"
)
func init() {
@ -79,7 +80,7 @@ func readConfigFile(filename string) (map[string]string, error) {
suffix := path.Ext(filename)
switch suffix {
case ".yaml":
return readYAMLFile(filename)
fallthrough
case ".yml":
return readYAMLFile(filename)
default:
@ -163,7 +164,7 @@ func writeResource(rn *VaultResource, data map[string]interface{}) error {
// step: determine the resource path
resourcePath := rn.GetFilename()
if !strings.HasPrefix(resourcePath, "/") {
resourcePath = fmt.Sprintf("%s/%s", options.outputDir, resourcePath)
resourcePath = fmt.Sprintf("%s/%s", options.outputDir, filepath.Base(resourcePath))
}
glog.V(10).Infof("writing the resource: %s, format: %s", resourcePath, rn.format)
@ -187,6 +188,23 @@ func writeResource(rn *VaultResource, data map[string]interface{}) error {
return writeFile(resourcePath, content)
}
if rn.format == "bundle" {
certificateFile := fmt.Sprintf("%s.crt", resourcePath)
caFile := fmt.Sprintf("%s.ca", resourcePath)
certificate := fmt.Sprintf("%s\n\n%s", data["certificate"], data["private_key"])
ca := fmt.Sprintf("%s", data["issuing_ca"])
if err := writeFile(certificateFile, []byte(certificate)); err != nil {
glog.Errorf("failed to write the bundled certificate file, error: %s", err)
return err
}
if err := writeFile(caFile, []byte(ca)); err != nil {
glog.Errorf("failed to write the ca certificate file, errro: %s", err)
return err
}
}
if rn.format == "cert" {
files := map[string]string{
"certificate": "crt",

136
vault.go
View file

@ -21,6 +21,7 @@ import (
"crypto/x509"
"fmt"
"io/ioutil"
"net"
"net/http"
"time"
@ -42,6 +43,7 @@ type AuthInterface interface {
// VaultService is the main interface into the vault API - placing into a structure
// allows one to easily mock it and two to simplify the interface for us
type VaultService struct {
vaultURL string
// the vault client
client *api.Client
// the vault config
@ -69,59 +71,24 @@ func NewVaultService(url string) (*VaultService, error) {
// step: create the config for client
service := new(VaultService)
service.config = api.DefaultConfig()
service.config.Address = url
service.vaultURL = url
service.listeners = make([]chan VaultEvent, 0)
service.config.HttpClient.Transport, err = service.buildHTTPTransport()
if err != nil {
return nil, err
}
// step: create the service processor channels
service.resourceChannel = make(chan *watchedResource, 20)
// step: create the actual client
if service.client, err = api.NewClient(service.config); err != nil {
// step: retrieve a vault client
service.client, err = newVaultClient(&options)
if err != nil {
return nil, err
}
// step: are we using a token? or do we need to authenticate and grab a token
if service.token, err = service.authenticate(options.vaultAuthOptions); err != nil {
return nil, err
}
// step: set the token for the client
service.client.SetToken(service.token)
// step: start the service processor off
service.vaultServiceProcessor()
return service, nil
}
func (r *VaultService) buildHTTPTransport() (*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)
@ -267,26 +234,6 @@ func (r *VaultService) vaultServiceProcessor() {
}()
}
// authenticate ... we need to authenticate to teh vault to grab a toke
// auth : a map containing the options required for authentication
func (r VaultService) authenticate(auth map[string]string) (string, error) {
var secret string
var err error
plugin, _ := auth[VaultAuth]
switch plugin {
case "userpass":
secret, err = NewUserPassPlugin(r.client).Create(auth)
case "token":
auth["filename"] = options.vaultAuthFile
secret, err = NewUserTokenPlugin(r.client).Create(auth)
default:
return "", fmt.Errorf("unsupported authentication plugin: %s", plugin)
}
return secret, err
}
// 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
@ -403,3 +350,74 @@ func (r VaultService) get(rn *watchedResource) (err error) {
return err
}
// newVaultClient creates and authenticates a vault client
func newVaultClient(opts *config) (*api.Client, error) {
var err error
var token string
config := api.DefaultConfig()
config.Address = opts.vaultURL
config.HttpClient.Transport, err = buildHTTPTransport(opts)
if err != nil {
return nil, err
}
// step: create the actual client
client, err := api.NewClient(config)
if err != nil {
return nil, err
}
plugin, _ := opts.vaultAuthOptions[VaultAuth]
switch plugin {
case "userpass":
token, err = NewUserPassPlugin(client).Create(opts.vaultAuthOptions)
case "token":
opts.vaultAuthOptions["filename"] = options.vaultAuthFile
token, err = NewUserTokenPlugin(client).Create(opts.vaultAuthOptions)
default:
return nil, fmt.Errorf("unsupported authentication plugin: %s", plugin)
}
// step: set the token for the client
client.SetToken(token)
return client, nil
}
// buildHTTPTransport constructs a http transport for the http client
func buildHTTPTransport(opts *config) (*http.Transport, error) {
// step: create the vault sidekick
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
TLSHandshakeTimeout: 10 * time.Second,
}
// step: are we skip the tls verify?
if options.tlsVerify {
transport.TLSClientConfig = &tls.Config{
InsecureSkipVerify: true,
}
}
// step: are we loading a CA file
if opts.vaultCaFile != "" {
// step: load the ca file
caCert, err := ioutil.ReadFile(opts.vaultCaFile)
if err != nil {
return nil, fmt.Errorf("unable to read in the ca: %s, reason: %s", opts.vaultCaFile, err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
// step: add the ca to the root
transport.TLSClientConfig.RootCAs = caCertPool
}
return transport, nil
}

View file

@ -43,7 +43,7 @@ const (
)
var (
resourceFormatRegex = regexp.MustCompile("^(yaml|json|ini|txt|cert|csv)$")
resourceFormatRegex = regexp.MustCompile("^(yaml|json|ini|txt|cert|bundle|csv)$")
// a map of valid resource to retrieve from vault
validResources = map[string]bool{