Merge pull request #7 from UKHomeOffice/cert_bundle
- adding the bundle format option
This commit is contained in:
commit
7d4c6add13
|
@ -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")
|
||||
}
|
||||
|
||||
|
|
2
main.go
2
main.go
|
@ -26,7 +26,7 @@ import (
|
|||
|
||||
const (
|
||||
Prog = "vault-sidekick"
|
||||
Version = "v0.0.3"
|
||||
Version = "v0.0.4"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
22
utils.go
22
utils.go
|
@ -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
136
vault.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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{
|
||||
|
|
Reference in a new issue