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.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.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.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")
|
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 (
|
const (
|
||||||
Prog = "vault-sidekick"
|
Prog = "vault-sidekick"
|
||||||
Version = "v0.0.3"
|
Version = "v0.0.4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
22
utils.go
22
utils.go
|
@ -30,6 +30,7 @@ import (
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -79,7 +80,7 @@ func readConfigFile(filename string) (map[string]string, error) {
|
||||||
suffix := path.Ext(filename)
|
suffix := path.Ext(filename)
|
||||||
switch suffix {
|
switch suffix {
|
||||||
case ".yaml":
|
case ".yaml":
|
||||||
return readYAMLFile(filename)
|
fallthrough
|
||||||
case ".yml":
|
case ".yml":
|
||||||
return readYAMLFile(filename)
|
return readYAMLFile(filename)
|
||||||
default:
|
default:
|
||||||
|
@ -163,7 +164,7 @@ func writeResource(rn *VaultResource, data map[string]interface{}) error {
|
||||||
// step: determine the resource path
|
// step: determine the resource path
|
||||||
resourcePath := rn.GetFilename()
|
resourcePath := rn.GetFilename()
|
||||||
if !strings.HasPrefix(resourcePath, "/") {
|
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)
|
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)
|
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" {
|
if rn.format == "cert" {
|
||||||
files := map[string]string{
|
files := map[string]string{
|
||||||
"certificate": "crt",
|
"certificate": "crt",
|
||||||
|
|
136
vault.go
136
vault.go
|
@ -21,6 +21,7 @@ import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -42,6 +43,7 @@ type AuthInterface interface {
|
||||||
// VaultService is the main interface into the vault API - placing into a structure
|
// 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
|
// allows one to easily mock it and two to simplify the interface for us
|
||||||
type VaultService struct {
|
type VaultService struct {
|
||||||
|
vaultURL string
|
||||||
// the vault client
|
// the vault client
|
||||||
client *api.Client
|
client *api.Client
|
||||||
// the vault config
|
// the vault config
|
||||||
|
@ -69,59 +71,24 @@ func NewVaultService(url string) (*VaultService, error) {
|
||||||
|
|
||||||
// step: create the config for client
|
// step: create the config for client
|
||||||
service := new(VaultService)
|
service := new(VaultService)
|
||||||
service.config = api.DefaultConfig()
|
service.vaultURL = url
|
||||||
service.config.Address = url
|
|
||||||
service.listeners = make([]chan VaultEvent, 0)
|
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
|
// step: create the service processor channels
|
||||||
service.resourceChannel = make(chan *watchedResource, 20)
|
service.resourceChannel = make(chan *watchedResource, 20)
|
||||||
|
|
||||||
// step: create the actual client
|
// step: retrieve a vault client
|
||||||
if service.client, err = api.NewClient(service.config); err != nil {
|
service.client, err = newVaultClient(&options)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
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
|
// step: start the service processor off
|
||||||
service.vaultServiceProcessor()
|
service.vaultServiceProcessor()
|
||||||
|
|
||||||
return service, nil
|
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
|
// AddListener ... add a listener to the events listeners
|
||||||
func (r *VaultService) AddListener(ch chan VaultEvent) {
|
func (r *VaultService) AddListener(ch chan VaultEvent) {
|
||||||
glog.V(10).Infof("adding the listener: %v", ch)
|
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
|
// scheduleNow ... a helper method to perform an immediate reschedule into a channel
|
||||||
// rn : a pointer to the watched resource you wish to reschedule
|
// rn : a pointer to the watched resource you wish to reschedule
|
||||||
// ch : the channel the resource should be placed into
|
// ch : the channel the resource should be placed into
|
||||||
|
@ -403,3 +350,74 @@ func (r VaultService) get(rn *watchedResource) (err error) {
|
||||||
|
|
||||||
return err
|
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 (
|
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
|
// a map of valid resource to retrieve from vault
|
||||||
validResources = map[string]bool{
|
validResources = map[string]bool{
|
||||||
|
|
Reference in a new issue