Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Spire trustsource #14

Open
wants to merge 14 commits into
base: develop
Choose a base branch
from
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@ all: build
build: clean cmd/plugin/vault-auth-spire.go
GOOS=linux GOARCH=amd64 go build -o vault-auth-spire cmd/plugin/vault-auth-spire.go

test:
go test ./... -race

clean:
@rm -f vault-auth-spire
21 changes: 15 additions & 6 deletions cmd/plugin/vault-auth-spire.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ import (
"context"
"errors"
"flag"
"github.com/bloomberg/vault-auth-spire/internal/common"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/logical"
"github.com/sirupsen/logrus"
"vault-auth-spire/internal/common"

"log"
"os"
Expand Down Expand Up @@ -69,7 +69,7 @@ func standardVaultPluginInit() {
func BackendFactory(ctx context.Context, backendConfig *logical.BackendConfig) (logical.Backend, error) {

settings, err := parseSettings()
if nil != err {
if err != nil {
return nil, err
}

Expand Down Expand Up @@ -105,14 +105,23 @@ func BackendFactory(ctx context.Context, backendConfig *logical.BackendConfig) (

spirePlugin.verifier = common.NewSvidVerifier()

if nil != settings.SourceOfTrust.File {
// must add these in reverse order of priority (spire settings will overwrite file settings)
if settings.SourceOfTrust.File != nil {
trustSource, err := common.NewFileTrustSource(settings.SourceOfTrust.File.Domains)
if err != nil {
return nil, errors.New("vault-auth-spire: Failed to initialize file TrustSource - " + err.Error())
}
spirePlugin.verifier.AddTrustSource(&trustSource)
} else {
return nil, errors.New("vault-auth-spire: No verifier found in settings")
spirePlugin.verifier.AddTrustSource(trustSource)
}
if settings.SourceOfTrust.Spire != nil {
trustSource, err := common.NewSpireTrustSource(settings.SourceOfTrust.Spire.SpireEndpointURLs, settings.SourceOfTrust.Spire.LocalBackupPath)
if err != nil {
return nil, errors.New("vault-auth-spire: Failed to initialize spire TrustSource - " + err.Error())
}
spirePlugin.verifier.AddTrustSource(trustSource)
}
if settings.SourceOfTrust.File == nil && settings.SourceOfTrust.Spire == nil {
return nil, errors.New("vault-auth-spire: No sources of trust in settings")
}

// Calls standard Vault plugin setup - magic happens here I bet :shrugs: but if it fails then we're gonna
Expand Down
27 changes: 6 additions & 21 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,41 +1,26 @@
module vault-auth-spire
module github.com/bloomberg/vault-auth-spire

go 1.12

require (
cloud.google.com/go v0.47.0 // indirect
cloud.google.com/go/bigquery v1.2.0 // indirect
cloud.google.com/go/storage v1.1.2 // indirect
github.com/creack/pty v1.1.9 // indirect
github.com/go-test/deep v1.0.3 // indirect
github.com/gogo/protobuf v1.3.1 // indirect
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 // indirect
github.com/google/go-cmp v0.3.1 // indirect
github.com/google/pprof v0.0.0-20191028172815-5e965273ee43 // indirect
github.com/hashicorp/golang-lru v0.5.3 // indirect
github.com/hashicorp/vault/api v1.0.4
github.com/hashicorp/vault/sdk v0.1.13
github.com/jstemmer/go-junit-report v0.9.1 // indirect
github.com/kr/pty v1.1.8 // indirect
github.com/natefinch/lumberjack v2.0.0+incompatible
github.com/rogpeppe/go-internal v1.5.0 // indirect
github.com/prometheus/common v0.4.0
github.com/sirupsen/logrus v1.2.0
github.com/spf13/afero v1.1.2
github.com/spf13/viper v1.4.0
github.com/spiffe/go-spiffe v0.0.0-20190922191205-018e7197ed1c
github.com/stretchr/objx v0.2.0 // indirect
github.com/stretchr/testify v1.4.0 // indirect
go.opencensus.io v0.22.1 // indirect
github.com/stretchr/testify v1.4.0
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf // indirect
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 // indirect
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect
golang.org/x/mobile v0.0.0-20191025110607-73ccc5ba0426 // indirect
golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 // indirect
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c // indirect
golang.org/x/text v0.3.2 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
golang.org/x/tools v0.0.0-20191030062658-86caa796c7ab // indirect
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 // indirect
google.golang.org/appengine v1.6.5 // indirect
google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6 // indirect
google.golang.org/grpc v1.24.0 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
Expand Down
136 changes: 6 additions & 130 deletions go.sum

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions internal/common/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package common

import "github.com/spf13/afero"

var appFS = afero.NewOsFs()
67 changes: 67 additions & 0 deletions internal/common/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package common

var (
leafCert = `
-----BEGIN CERTIFICATE-----
MIIE1DCCArygAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzELMAkGA1UEBhMCVVMx
FzAVBgNVBAoMDnRlc3QxLmFjbWUuY29tMRcwFQYDVQQDDA5JbnRlcm1lZGlhZXRD
QTAeFw0xNzA3MTkxNjUwMjBaFw0xNzA3MjkxNjUwMjBaMDUxCzAJBgNVBAYTAlVT
MRcwFQYDVQQKDA50ZXN0MS5hY21lLmNvbTENMAsGA1UEAwwEYmxvZzCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKm8P47lABp4+rz2nN+QYrxedbaFVWoF
FuoSkqcHsafMwbMrN+kI6wJVtlbwviDvxWFJ92q0H71QNFybTsmof3KUN/kYCp7P
+LKhBrN0ttWI5q6v5eDrjN0VdtVdnlZOYmJFbvETOgfK/qXKNRRM8HYW0tdqrtEw
CR5dIu53xVUSViBdwXpuy2c5W2mFn1gxTpdW+3hbZsL1pHrU9qPWLtTgl/KY8kjs
I7KW1cIcinE4SJomhB5L/4emhxKGY+kEa2+fN9IPjjvKSMOw9kiBKk1GHZcIY5EA
O3TIfUk3fysPzi5qA0su/bNtPQy1uXgXS10xUlV7pqRPvHjiNzgFkXUCAwEAAaOB
4zCB4DAJBgNVHRMEAjAAMB0GA1UdDgQWBBRVQ91jSOONzVr1VGBdJOlPN+3XxTBg
BgNVHSMEWTBXgBQ13bfx50rDZO3y2CZdHPgleFUEoKE7pDkwNzELMAkGA1UEBhMC
VVMxFzAVBgNVBAoMDnRlc3QxLmFjbWUuY29tMQ8wDQYDVQQDDAZSb290Q0GCAhAA
MA4GA1UdDwEB/wQEAwIDqDATBgNVHSUEDDAKBggrBgEFBQcDATAtBgNVHREEJjAk
hiJzcGlmZmU6Ly9kZXYuYWNtZS5jb20vcGF0aC9zZXJ2aWNlMA0GCSqGSIb3DQEB
CwUAA4ICAQBp2+rtUxt1VmNM/vi6PwoSoYzWFmQ2nc4OM7bsOG4uppU54wRYZ+T7
c42EcrpyBgWn+rWHT1Hi6SNcmloKHydaUTZ4pq3IlKKnBNqwivU5BzIxYLDrhR/U
wd9s1tgmLvADqkQa1XjjSFn5Auoj1R640ry4qpw8IOusdm6wVhru4ssRnHX4E2uR
jQe7b3ws38aZhjtL78Ip0BB4yPxWJRp/WmEoT33QP+cZhA4IYWECxNODr6DSJeq2
VNu/6JACGrNfM2Sjt4Wxz+nIa3cKDNCA6PR8StTUTcoQ6ZBzpn+n/Q1xSRIOJz6N
hgfkyb9O7HAMdAP+TxehjqG3gh5Ky2DgYMCIZOztVzsuOb1DGJe/kGUKeRJLl2/O
QwkctwUOcVIxckNu6OvclriFzvoXObqO77XeCI2V1Vef0wGTWlWNOdbFa4708Y7f
5UdwInYQUi87RFDnc1SDU4Jrsv4KzZiv9FCfDg8pCBIdWpWT7DAuI0d7i7PZ+iFt
ZZ6sb/YDkyiDXU4ar/dja0FDE2r7jsN9D+FfW49+iDvXr4ELQyhZpW3Zr1Ojwm58
CJzjZwbRYiVwPBRsKmiYfO1E7esvw3CmjK5chfz8c40f6/APDro9ZmYNBRv2CnJy
t/DtcM/GpAhBbLP9Tk7kPB41v5fRIxVDo50Iz/qvkr37pQ4RsejSFg==
-----END CERTIFICATE-----`
intermediateCert = `
-----BEGIN CERTIFICATE-----
MIIFiDCCA3CgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwNzELMAkGA1UEBhMCVVMx
FzAVBgNVBAoMDnRlc3QxLmFjbWUuY29tMQ8wDQYDVQQDDAZSb290Q0EwHhcNMTcw
NzE5MTY1MDIwWhcNMTcxMDI3MTY1MDIwWjA/MQswCQYDVQQGEwJVUzEXMBUGA1UE
CgwOdGVzdDEuYWNtZS5jb20xFzAVBgNVBAMMDkludGVybWVkaWFldENBMIICIjAN
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAph7QhbUKEjMWu2R/WXIc8RR0ymCL
njJTm0D5duTe7V2hklLhCo1KnZAjvDiDX9r85UfEja5MYItHmFF4HOZjSG6nY3Yg
Mm5hdJM7Jmv9NJR8DJInROabfRcaPdugs2UQ41jCEygIoWiZ9+yVlZ21MNW0yQdI
JHUNndQfXpS7dBdKw6fUqzZgpdzo86mAapZDIPL7gXv6MW8JhbvQCm+bg7SRIJOD
t/a1T0nHPNuwxdDWjGcmJEQRknffL8pheDlW9sMAd/4BtIeaWUEb3JjdDoaJ9SWU
tCgGRZOMnmry8npJnssyoLUtIPuk8949REOUB15nT94EhTtb1BMdiuD8P8HOHWC6
mcxpJCsKlCFmOQpES6WROqjckQJ0f/xPOdKAdI9W0Eg3TRtibV9XTfTvv8SPug9/
6FnkdpwF5xlJ/XcuW8GtpHUZNQ0NyxjUh2rQRAbwdTMeCvhx5fPHpT2kc6PIlzLw
h4Pt0Xvc1cNt2iJOVDqs75HvvUe4RYTfdqlK5385u/s2cxQrLuB4owJrTyrqc3yM
L/0h5JXr9P+T+axrd5WQWz2ngiaimli2vxZTR++RfBnyCgQGTiY+9UUOYYgHh6hJ
CGUPY77DsKuTfIXYlra+c65FKFAcZrC4vt1CLtBqW2mBy7U868c0L9PZ1g3+WGvA
FmnZA6MgUKQE0i0CAwEAAaOBlTCBkjAdBgNVHQ4EFgQUNd238edKw2Tt8tgmXRz4
JXhVBKAwHwYDVR0jBBgwFoAUUeBRd0yew4JtInksRTlbz71YwtEwDwYDVR0TAQH/
BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwLwYDVR0eAQH/BCUwI6AhMA+GDS5kZXYu
YWNtZS5jb20wDoYMZGV2LmFjbWUuY29tMA0GCSqGSIb3DQEBCwUAA4ICAQBLljbI
ekm5/uhFbc/aCAlomwGkXFvyMXx7eD7Pimzn4H31nYvQ5ha2M0536JC/mH1xi7nK
OOMuSBAqjpALoRk4+3O5s6r9BN8KKhcI4jDKHqOSBe38K+Ad06B52yxYyL6YmJwk
Zlazf3KvExzUWS0t4ehNuI2HJvvwEitMpOF3hhwAYk3v/x2YBtpglH+yZC4Sfoma
ItjFWeD+DA0bVAMJiErz8Pq91avnC8SPpPXiPJVOaBhaNc8po6x6cwSqY6RnI2/K
0kUWXXH65Pz0JYru53ALzy4ouwJItcgMvzZPGVaojxEbGDrjXEkWIGresFrw1Ijr
IyrQUqWa9wW6ik+IQOb6m3FZYB8jMO7nhop9Ywm9uoSmFRg6RsP2AWHBMYP7lZqw
fx3HjrAvc3ycIZ2tSPkdLd5n8YuL31CVpa9aGwT5dYzIVd/eFUrCUIGV2j8XHgkL
gMPr8JhNPAG0B4rRpEXmK3HHybo3s0M8i80Lit98xzupNpng97xKT7jJBSinNTdt
/2uU7diHQM7aPNmNG+Wu3GXVt/MZuDMSFrh9AHl0A/Y4mNu6KKqRMJuvsfy/j1DD
fDGmAkjHrspiorTruphHk+cymJfjAGqZ0l6il4Wi5w4R5jrxnJMhkxbjr/PG5xCS
+ZKPPNZbpPIY54oQ2bHkuaQgzJ7us4ZW7gxgbw==
-----END CERTIFICATE-----`
certChain = intermediateCert + leafCert
)
51 changes: 34 additions & 17 deletions internal/common/filetrustsource.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ package common
import (
"crypto/x509"
"errors"
"github.com/sirupsen/logrus"
"fmt"
"io/ioutil"

"github.com/sirupsen/logrus"
)

// FileTrustSource provides support for PEM-file based trust sources. This trust source
Expand All @@ -37,17 +39,17 @@ type FileTrustSource struct {
// result in an error. Failure to parse a certificate from a PEM file will result in
// that certificate being ignored. If no certificates are loaded from a PEM file then
// an INFO message will be added to the log.
func NewFileTrustSource(domainPaths map[string][]string) (FileTrustSource, error) {
func NewFileTrustSource(domainPaths map[string][]string) (*FileTrustSource, error) {
source := FileTrustSource{
domainPaths: domainPaths,
domainCertificates: make(map[string][]*x509.Certificate, 0),
}

if err := source.loadCertificates(); err != nil {
return source, err
return &source, err
}

return source, nil
return &source, nil
}

// TrustedCertificates returns our current maps of domains to certificates. This is a
Expand All @@ -59,24 +61,39 @@ func (source *FileTrustSource) TrustedCertificates() map[string][]*x509.Certific
// For each domain/file mapping found in source.domainPaths, load the PEM and read all
// certificates from the file.
func (source *FileTrustSource) loadCertificates() error {
for domain, paths := range source.domainPaths {
domainCertificates := make([]*x509.Certificate, 0)
for domain := range source.domainPaths {
err := source.loadDomain(domain)
if err != nil {
return err
}
}

for _, path := range paths {
data, err := ioutil.ReadFile(path)
if err != nil {
return errors.New("Failed to load certificates for domain " + domain + " from file " + path + " - " + err.Error())
}
return nil
}

func (source *FileTrustSource) loadDomain(domain string) error {
paths := source.domainPaths[domain]
domainCertificates := make([]*x509.Certificate, 0)

certificates := ExtractCertificatesFromPem(data)
if len(certificates) == 0 {
logrus.Info("Did not load any certificates for domain " + domain + " from file " + path)
}
domainCertificates = append(domainCertificates, certificates...)
for _, path := range paths {
file, err := appFS.Open(path)
if err != nil {
return fmt.Errorf("Could not open file %s while loading certificates: %v", path, err)
}
defer file.Close()
data, err := ioutil.ReadAll(file)
if err != nil {
return errors.New("Failed to load certificates for domain " + domain + " from file " + path + ": " + err.Error())
}

source.domainCertificates[domain] = domainCertificates
certificates := ExtractCertificatesFromPem(data)
if len(certificates) == 0 {
logrus.Info("Did not load any certificates for domain " + domain + " from file " + path)
}
domainCertificates = append(domainCertificates, certificates...)
}

source.domainCertificates[domain] = domainCertificates

return nil
}
37 changes: 30 additions & 7 deletions internal/common/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,19 @@ type Settings struct {
}

type SourceOfTrustSettings struct {
File *FileTrustSourceSettings
File *FileTrustSourceSettings
Spire *SpireTrustSourceSettings
}

type FileTrustSourceSettings struct {
Domains map[string][]string
}

type SpireTrustSourceSettings struct {
SpireEndpointURLs map[string]string
LocalBackupPath string
}

type LogSettings struct {
Filename string
Level string
Expand Down Expand Up @@ -112,12 +118,11 @@ func readSourceOfTrustSettings() (*SourceOfTrustSettings, error) {
}
}

// TODO: Add implementation for Spire being the source of trust
//if(viper.IsSet("trustsource.spire")){
// if trustSettings.File, err = readSpireSourceOfTrustSettings(); err != nil {
// return nil, err
// }
//}
if viper.IsSet("trustsource.spire") {
if sourceOfTrust.Spire, err = readSpireSourceOfTrustSettings(); err != nil {
return nil, err
}
}

return sourceOfTrust, nil
}
Expand All @@ -132,3 +137,21 @@ func readFileSourceOfTrustSettings() (*FileTrustSourceSettings, error) {

return fileSettings, nil
}

func readSpireSourceOfTrustSettings() (*SpireTrustSourceSettings, error) {
if !viper.IsSet("trustsource.spire.domains") {
return nil, errors.New("trustsource.spire.domains is required but not found")
}

viper.SetDefault("trustsource.spire.backupPath", "/var/run/spire/certs/")
viper.SetDefault("trustsource.spire.storeEnabled", true)
spireSettings := &SpireTrustSourceSettings{
SpireEndpointURLs: viper.GetStringMapString("trustsource.spire.domains"),
LocalBackupPath: viper.GetString("trustsource.spire.backupPath"),
}
if !viper.GetBool("trustsource.spire.storeEnabled") {
spireSettings.LocalBackupPath = ""
}

return spireSettings, nil
}
Loading