From 420b32121dceecd3a9555c49689e0cce59e7abfc Mon Sep 17 00:00:00 2001 From: Peyton Walters Date: Thu, 2 Jan 2020 14:48:24 -0500 Subject: [PATCH 01/21] Add Spire trustsource Signed-off-by: Peyton Walters --- cmd/plugin/vault-auth-spire.go | 17 ++- go.mod | 24 +--- go.sum | 132 +---------------- internal/common/common.go | 5 + internal/common/filetrustsource.go | 13 +- internal/common/settings.go | 36 ++++- internal/common/spiffetrustsource_test.go | 155 ++++++++++++++++++++ internal/common/spiretrustsource.go | 165 ++++++++++++++++++++++ internal/common/svidverifier.go | 2 + vault-auth-spire-filesource.json | 20 +++ 10 files changed, 405 insertions(+), 164 deletions(-) create mode 100644 internal/common/common.go create mode 100644 internal/common/spiffetrustsource_test.go create mode 100644 internal/common/spiretrustsource.go create mode 100644 vault-auth-spire-filesource.json diff --git a/cmd/plugin/vault-auth-spire.go b/cmd/plugin/vault-auth-spire.go index a42101b..39fbf23 100644 --- a/cmd/plugin/vault-auth-spire.go +++ b/cmd/plugin/vault-auth-spire.go @@ -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 } @@ -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") + } + if settings.SourceOfTrust.Spire != nil { + trustSource, err := common.NewSpireTrustSource(settings.SourceOfTrust.Spire.URLs, settings.SourceOfTrust.Spire.CertLocation) + if err != nil { + return nil, errors.New("vault-auth-spire: Failed to initialize file 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 truth in settings") } // Calls standard Vault plugin setup - magic happens here I bet :shrugs: but if it fails then we're gonna diff --git a/go.mod b/go.mod index f8cd07c..d9b43b4 100644 --- a/go.mod +++ b/go.mod @@ -3,39 +3,23 @@ module 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/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 diff --git a/go.sum b/go.sum index cefaa2e..688f923 100644 --- a/go.sum +++ b/go.sum @@ -1,21 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.47.0/go.mod h1:5p3Ky/7f3N10VBkhuR5LFtddroTiMyjZV/Kj5qOQFxU= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.2.0/go.mod h1:Cqg1qaK3wRdys8sKlow0jIBVFwSTiHoFx5um4ujCpyE= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.1.2/go.mod h1:/03MkR5FWjF0OpcKpdJ4RgWybEaYAr2boHXq5RDlxbw= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -33,8 +18,6 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -46,7 +29,6 @@ github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -59,15 +41,12 @@ github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3a github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -75,19 +54,9 @@ github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191028172815-5e965273ee43/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= @@ -132,8 +101,6 @@ github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKe github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= @@ -144,7 +111,6 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= @@ -189,8 +155,6 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= @@ -208,13 +172,10 @@ github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spiffe/go-spiffe v0.0.0-20190820222348-6adcf1eecbcc h1:YeGvOZLHZxE8nrxPGDNH1LGv8zzkAIDfIRgy256rotk= -github.com/spiffe/go-spiffe v0.0.0-20190820222348-6adcf1eecbcc/go.mod h1:HyNeJnVYkDyQgB2qcSPxVYkAA2F3lQu51bDxNpFcKxY= github.com/spiffe/go-spiffe v0.0.0-20190922191205-018e7197ed1c h1:wpwh25WjvKF8/+N+wMy1u9nMiOXfw5sqpmL5ZSAFIWU= github.com/spiffe/go-spiffe v0.0.0-20190922191205-018e7197ed1c/go.mod h1:HyNeJnVYkDyQgB2qcSPxVYkAA2F3lQu51bDxNpFcKxY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= @@ -224,72 +185,36 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf h1:fnPsqIDRbCSgumaMCRpoIoF2s4qxv0xSSS0BVZUE/ss= golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= -golang.org/x/exp v0.0.0-20191014171548-69215a2ee97e/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191024150812-c286b889502e/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mobile v0.0.0-20191025110607-73ccc5ba0426/go.mod h1:p895TfNkDgPEmEQrNiOtIl3j98d/tGU95djDj7NfyjQ= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271 h1:N66aaryRB3Ax92gH0v3hp1QYZ3zWWCCUR/j8Ifh45Ss= golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -297,25 +222,16 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e h1:nFYrTHrdrAOpShe27kaFHjsqYSEQ0KWqdWLu3xuZJts= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c h1:S/FtSvpNLtFBgjTqcKsRpsa6aVsI6iztaz1bQd9BJwE= golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db h1:6/JqlYfC1CCaLnGceQTI+sDGhC9UBSPAsBqI0Gun6kU= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= @@ -326,59 +242,19 @@ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190909214602-067311248421/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191010171213-8abd42400456/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191022210528-83d82311fd1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191028194131-d78a1f2664a0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191030062658-86caa796c7ab/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.11.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107 h1:xtNn7qFlagY2mQNFHMSRPjT2RkOV4OXM7P5TVy9xATo= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6 h1:UXl+Zk3jqqcbEVV7ace5lrt4YdA4tXiz3f/KbmD29Vo= google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw= google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -386,8 +262,8 @@ gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUy gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= @@ -400,8 +276,4 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/internal/common/common.go b/internal/common/common.go new file mode 100644 index 0000000..3dc4e59 --- /dev/null +++ b/internal/common/common.go @@ -0,0 +1,5 @@ +package common + +import "github.com/spf13/afero" + +var appFS = afero.NewOsFs() diff --git a/internal/common/filetrustsource.go b/internal/common/filetrustsource.go index 258412f..be119fb 100644 --- a/internal/common/filetrustsource.go +++ b/internal/common/filetrustsource.go @@ -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 @@ -63,9 +65,14 @@ func (source *FileTrustSource) loadCertificates() error { domainCertificates := make([]*x509.Certificate, 0) for _, path := range paths { - data, err := ioutil.ReadFile(path) + 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()) + return errors.New("Failed to load certificates for domain " + domain + " from file " + path + ": " + err.Error()) } certificates := ExtractCertificatesFromPem(data) diff --git a/internal/common/settings.go b/internal/common/settings.go index e9a2596..b0610fc 100644 --- a/internal/common/settings.go +++ b/internal/common/settings.go @@ -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 { + URLs map[string]string + CertLocation string +} + type LogSettings struct { Filename string Level string @@ -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 } @@ -132,3 +137,20 @@ 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") + } + + spireSettings := new(SpireTrustSourceSettings) + spireSettings.URLs = viper.GetStringMapString("trustsource.spire.domains") + viper.SetDefault("trustsource.spire.certLocation", "/tmp/vault-spire-certs.json") + viper.SetDefault("trustsource.spire.storeEnabled", true) + spireSettings.CertLocation = viper.GetString("trustsource.spire.certLocation") + if !viper.GetBool("trustsource.spire.storeEnabled") { + spireSettings.CertLocation = "" + } + + return spireSettings, nil +} diff --git a/internal/common/spiffetrustsource_test.go b/internal/common/spiffetrustsource_test.go new file mode 100644 index 0000000..2968aa7 --- /dev/null +++ b/internal/common/spiffetrustsource_test.go @@ -0,0 +1,155 @@ +package common + +import ( + "crypto" + "crypto/x509" + "testing" + "time" + + "github.com/spf13/afero" + "github.com/spiffe/go-spiffe/spiffetest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestInitalLoad(t *testing.T) { + appFS = afero.NewMemMapFs() + + certJSON := `{ +"certs": { + "spiffe://example.org": ["MIIB4TCCAUoCCQCfmw3vMgPS5TANBgkqhkiG9w0BAQQFADA1MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTERMA8GA1UEChMITUQ1IEluYy4wHhcNMTUxMjAzMTkyOTMyWhcNMjkwODEyMTkyOTMyWjA1MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTERMA8GA1UEChMITUQ1IEluYy4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANrq2nhLQj5mlXbpVX3QUPhfEm/vdEqPkoWtR/jRZIWm4WGfWpq/LKHJx2Pqwn+t117syN8l4U5unyAi1BJSXjBwPZNd7dXjcuJ+bRLV7FZ/iuvscfYyQQFTxan4TaJMd0x1HoNDbNbjHa02IyjjYE/r3mb/PIg+J2t5AZEh80lPAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAjGzp3K3ey/YfKHohf33yHHWd695HQxDAP+wYcs9/TAyLR+gJzJP7d18EcDDLJWVi7bhfa4EAD86di05azOh9kWSn4b3o9QYRGCSwGNnI3Zk0cwNKA49hZntKKiy22DhRk7JAHF01d6Bu3KkHkmENrtJ+zj/+159WAnUaqViorq4="] +} +}` + afero.WriteFile(appFS, "vault-spire-certs.json", []byte(certJSON), 600) + + workloadAPI := spiffetest.NewWorkloadAPI(t, nil) + defer workloadAPI.Stop() + + source, err := NewSpireTrustSource(map[string]string{}, "vault-spire-certs.json") + require.NoError(t, err) + certs := source.TrustedCertificates()["spiffe://example.org"] + require.Len(t, certs, 1) + assert.Equal(t, x509.MD5WithRSA, certs[0].SignatureAlgorithm) +} + +func TestWriteCerts(t *testing.T) { + appFS = afero.NewMemMapFs() + + workloadAPI := spiffetest.NewWorkloadAPI(t, nil) + defer workloadAPI.Stop() + + ca := spiffetest.NewCA(t) + svidFoo, keyFoo := ca.CreateX509SVID("spiffe://example.org/foo") + + makeX509SVIDResponse := func(svid []*x509.Certificate, key crypto.Signer) *spiffetest.X509SVIDResponse { + return &spiffetest.X509SVIDResponse{ + Bundle: ca.Roots(), + SVIDs: []spiffetest.X509SVID{ + { + CertChain: svid, + Key: key, + }, + }, + } + } + workloadAPI.SetX509SVIDResponse(makeX509SVIDResponse(svidFoo, keyFoo)) + + source, err := NewSpireTrustSource(map[string]string{ + "spiffe://example.org": workloadAPI.Addr(), + }, "vault-spire-certs.json") + require.NoError(t, err) + + time.Sleep(1 * time.Second) // wait for watcher to get new certs + source.Stop() + + newSource, err := NewSpireTrustSource(map[string]string{}, "vault-spire-certs.json") + assert.Equal(t, ca.Roots(), newSource.TrustedCertificates()["spiffe://example.org"]) +} + +func TestSpireOverwrite(t *testing.T) { + appFS = afero.NewMemMapFs() + + certJSON := `{ +"certs": { + "spiffe://example.org": ["MIIB4TCCAUoCCQCfmw3vMgPS5TANBgkqhkiG9w0BAQQFADA1MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTERMA8GA1UEChMITUQ1IEluYy4wHhcNMTUxMjAzMTkyOTMyWhcNMjkwODEyMTkyOTMyWjA1MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTERMA8GA1UEChMITUQ1IEluYy4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANrq2nhLQj5mlXbpVX3QUPhfEm/vdEqPkoWtR/jRZIWm4WGfWpq/LKHJx2Pqwn+t117syN8l4U5unyAi1BJSXjBwPZNd7dXjcuJ+bRLV7FZ/iuvscfYyQQFTxan4TaJMd0x1HoNDbNbjHa02IyjjYE/r3mb/PIg+J2t5AZEh80lPAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAjGzp3K3ey/YfKHohf33yHHWd695HQxDAP+wYcs9/TAyLR+gJzJP7d18EcDDLJWVi7bhfa4EAD86di05azOh9kWSn4b3o9QYRGCSwGNnI3Zk0cwNKA49hZntKKiy22DhRk7JAHF01d6Bu3KkHkmENrtJ+zj/+159WAnUaqViorq4="] +} +}` + afero.WriteFile(appFS, "vault-spire-certs.json", []byte(certJSON), 600) + + workloadAPI := spiffetest.NewWorkloadAPI(t, nil) + defer workloadAPI.Stop() + + ca := spiffetest.NewCA(t) + svidFoo, keyFoo := ca.CreateX509SVID("spiffe://example.org/foo") + + makeX509SVIDResponse := func(svid []*x509.Certificate, key crypto.Signer) *spiffetest.X509SVIDResponse { + return &spiffetest.X509SVIDResponse{ + Bundle: ca.Roots(), + SVIDs: []spiffetest.X509SVID{ + { + CertChain: svid, + Key: key, + }, + }, + } + } + workloadAPI.SetX509SVIDResponse(makeX509SVIDResponse(svidFoo, keyFoo)) + + source, err := NewSpireTrustSource(map[string]string{ + "spiffe://example.org": workloadAPI.Addr(), + }, "vault-spire-certs.json") + require.NoError(t, err) + + time.Sleep(1 * time.Second) // wait for watcher to get new certs + assert.Equal(t, ca.Roots(), source.TrustedCertificates()["spiffe://example.org"]) +} + +func TestSpireReload(t *testing.T) { + appFS = afero.NewMemMapFs() + + workloadAPI := spiffetest.NewWorkloadAPI(t, nil) + defer workloadAPI.Stop() + + ca := spiffetest.NewCA(t) + svidFoo, keyFoo := ca.CreateX509SVID("spiffe://example.org/foo") + + makeX509SVIDResponse := func(svid []*x509.Certificate, key crypto.Signer) *spiffetest.X509SVIDResponse { + return &spiffetest.X509SVIDResponse{ + Bundle: ca.Roots(), + SVIDs: []spiffetest.X509SVID{ + { + CertChain: svid, + Key: key, + }, + }, + } + } + workloadAPI.SetX509SVIDResponse(makeX509SVIDResponse(svidFoo, keyFoo)) + + source, err := NewSpireTrustSource(map[string]string{ + "spiffe://example.org": workloadAPI.Addr(), + }, "") + require.NoError(t, err) + + time.Sleep(1 * time.Second) // wait for watcher to get new certs + assert.Equal(t, ca.Roots(), source.TrustedCertificates()["spiffe://example.org"]) + + caRot := spiffetest.NewCA(t) + svidFooRot, keyFooRot := ca.CreateX509SVID("spiffe://example.org/foo") + + makeX509SVIDResponseRot := func(svid []*x509.Certificate, key crypto.Signer) *spiffetest.X509SVIDResponse { + return &spiffetest.X509SVIDResponse{ + Bundle: caRot.Roots(), + SVIDs: []spiffetest.X509SVID{ + { + CertChain: svid, + Key: key, + }, + }, + } + } + workloadAPI.SetX509SVIDResponse(makeX509SVIDResponseRot(svidFooRot, keyFooRot)) + + time.Sleep(1 * time.Second) // wait for watcher to get new certs + assert.Equal(t, caRot.Roots(), source.TrustedCertificates()["spiffe://example.org"]) +} diff --git a/internal/common/spiretrustsource.go b/internal/common/spiretrustsource.go new file mode 100644 index 0000000..e429095 --- /dev/null +++ b/internal/common/spiretrustsource.go @@ -0,0 +1,165 @@ +package common + +import ( + "crypto/x509" + "encoding/base64" + "encoding/json" + "fmt" + "io/ioutil" + "os" + + "github.com/sirupsen/logrus" + "github.com/spiffe/go-spiffe/workload" +) + +// SpireTrustSource holds all necessary information to connect to a spire instance and store its certificates +type SpireTrustSource struct { + domainURLs map[string]string + domainCertificates map[string][]*x509.Certificate + spireClients []*workload.X509SVIDClient + certLocation string +} + +type certMap struct { + Certs map[string][]string `json:"certs"` +} + +type watcher struct { + uri string + source *SpireTrustSource +} + +// TrustedCertificates fulfills the TrustSource interface +func (s *SpireTrustSource) TrustedCertificates() map[string][]*x509.Certificate { + return s.domainCertificates +} + +// NewSpireTrustSource creates a new trust source with connectivity to one or more spire instances. +func NewSpireTrustSource(domainURLs map[string]string, certLocation string) (*SpireTrustSource, error) { + source := SpireTrustSource{ + domainURLs: domainURLs, + domainCertificates: make(map[string][]*x509.Certificate, 0), + certLocation: certLocation, + } + + if certLocation != "" { + if err := source.parseCertFile(); err != nil { + return nil, err + } + } + + if err := source.startWatchers(); err != nil { + return nil, err + } + + return &source, nil +} + +// Stop stops all spire clients and writes out certs +func (s *SpireTrustSource) Stop() { + for _, client := range s.spireClients { + client.Stop() + } + s.writeCertFile() +} + +func (s *SpireTrustSource) parseCertFile() error { + file, err := appFS.OpenFile(s.certLocation, os.O_RDWR|os.O_CREATE, 600) + if err != nil { + return err + } + defer file.Close() + + fileDat, err := ioutil.ReadAll(file) + if err != nil { + return fmt.Errorf("could not read cert file: %v", err) + } + fmt.Println(string(fileDat)) + var certStruct certMap + err = json.Unmarshal(fileDat, &certStruct) + if err != nil { + logrus.Warnf("Error unmarshaling cert file: %v\n", err) + } + for domain, encCerts := range certStruct.Certs { + decodedCerts := make([]*x509.Certificate, 0) + for _, certB64 := range encCerts { + encCert, err := base64.StdEncoding.DecodeString(certB64) + if err != nil { + logrus.Warnf("Could not base 64 decode string: %v\n", err) + continue + } + decodedCert, err := x509.ParseCertificate([]byte(encCert)) + if err != nil { + logrus.Warnf("Could not decode certificate for domain %s: %v\n", domain, err) + continue + } + decodedCerts = append(decodedCerts, decodedCert) + } + s.domainCertificates[domain] = decodedCerts + } + return nil +} + +func (s *SpireTrustSource) writeCertFile() error { + if s.certLocation == "" { + return nil + } + + certStruct := certMap{ + Certs: make(map[string][]string, 0), + } + + for domain, certObjs := range s.TrustedCertificates() { + toWrite := make([]string, 0) + for _, certObj := range certObjs { + marshalled := base64.StdEncoding.EncodeToString(certObj.Raw) + toWrite = append(toWrite, string(marshalled)) + } + certStruct.Certs[domain] = toWrite + } + + finalData, _ := json.Marshal(certStruct) + + file, err := appFS.OpenFile(s.certLocation, os.O_RDWR|os.O_CREATE, 600) + if err != nil { + return err + } + defer file.Close() + _, err = file.Write(finalData) + if err != nil { + return err + } + + return nil +} + +func (s *SpireTrustSource) startWatchers() error { + for id, url := range s.domainURLs { + opts := workload.WithAddr(url) + watch := &watcher{ + uri: id, + source: s, + } + client, err := workload.NewX509SVIDClient(watch, opts) + if err != nil { + return err + } + s.spireClients = append(s.spireClients, client) + + logrus.Infof("Starting listener for %s.\n", id) + go client.Start() + } + return nil +} + +func (w *watcher) UpdateX509SVIDs(svids *workload.X509SVIDs) { + w.source.domainCertificates[w.uri] = svids.Default().TrustBundle + err := w.source.writeCertFile() + if err != nil { + logrus.Warnf("Error writing to cert file: %v\n", err) + } +} + +func (w *watcher) OnError(err error) { + logrus.Errorf("Error encountered by watcher with uri %s: %v\n", w.uri, err) +} diff --git a/internal/common/svidverifier.go b/internal/common/svidverifier.go index 28db45e..76d1aa9 100644 --- a/internal/common/svidverifier.go +++ b/internal/common/svidverifier.go @@ -82,6 +82,8 @@ func (verifier *SvidVerifier) verifyAndExtractSpiffeIdFromX509(svid string) (str svidPrincipalCert := svidCertChain[0] logrus.Debug("Building map of domains -> trusted certificate pools") + // reads these in reverse order of priority, so in a list of [file, spire], if there are domain conflicts, spire domains will dominate + // TODO: pre-build and store this (regenerating when Spire truststore updates) so it doesn't have to be regenerated for each request trustedCertificatePools := make(map[string]*x509.CertPool, 0) for _, source := range verifier.trustSources { for domain, certificates := range source.TrustedCertificates() { diff --git a/vault-auth-spire-filesource.json b/vault-auth-spire-filesource.json new file mode 100644 index 0000000..d755d45 --- /dev/null +++ b/vault-auth-spire-filesource.json @@ -0,0 +1,20 @@ +{ + "trustsource": { + "spire": { + "certLocation": "/tmp/vault-spire-certs.json", + "storeEnabled": true, + "domains": { + "spiffe://dev.foo.com": "unix:///tmp/agent.sock" + } + } + }, + + "log": { + "filename": "/tmp/vault-auth-spire.log", + "level": "DEBUG", + "maxSize": 10, + "maxBackups": 4, + "maxAge": 14, + "compress": true + } +} \ No newline at end of file From 2d317b96b22b1c3bb43bfda4de6bf374e3874842 Mon Sep 17 00:00:00 2001 From: Peyton Walters Date: Thu, 2 Jan 2020 15:07:06 -0500 Subject: [PATCH 02/21] Move out certJSON Signed-off-by: Peyton Walters --- internal/common/spiffetrustsource_test.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/internal/common/spiffetrustsource_test.go b/internal/common/spiffetrustsource_test.go index 2968aa7..025e9c4 100644 --- a/internal/common/spiffetrustsource_test.go +++ b/internal/common/spiffetrustsource_test.go @@ -12,14 +12,17 @@ import ( "github.com/stretchr/testify/require" ) -func TestInitalLoad(t *testing.T) { - appFS = afero.NewMemMapFs() - - certJSON := `{ +var ( + certJSON = `{ "certs": { "spiffe://example.org": ["MIIB4TCCAUoCCQCfmw3vMgPS5TANBgkqhkiG9w0BAQQFADA1MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTERMA8GA1UEChMITUQ1IEluYy4wHhcNMTUxMjAzMTkyOTMyWhcNMjkwODEyMTkyOTMyWjA1MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTERMA8GA1UEChMITUQ1IEluYy4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANrq2nhLQj5mlXbpVX3QUPhfEm/vdEqPkoWtR/jRZIWm4WGfWpq/LKHJx2Pqwn+t117syN8l4U5unyAi1BJSXjBwPZNd7dXjcuJ+bRLV7FZ/iuvscfYyQQFTxan4TaJMd0x1HoNDbNbjHa02IyjjYE/r3mb/PIg+J2t5AZEh80lPAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAjGzp3K3ey/YfKHohf33yHHWd695HQxDAP+wYcs9/TAyLR+gJzJP7d18EcDDLJWVi7bhfa4EAD86di05azOh9kWSn4b3o9QYRGCSwGNnI3Zk0cwNKA49hZntKKiy22DhRk7JAHF01d6Bu3KkHkmENrtJ+zj/+159WAnUaqViorq4="] } }` +) + +func TestInitalLoad(t *testing.T) { + appFS = afero.NewMemMapFs() + afero.WriteFile(appFS, "vault-spire-certs.json", []byte(certJSON), 600) workloadAPI := spiffetest.NewWorkloadAPI(t, nil) @@ -69,11 +72,6 @@ func TestWriteCerts(t *testing.T) { func TestSpireOverwrite(t *testing.T) { appFS = afero.NewMemMapFs() - certJSON := `{ -"certs": { - "spiffe://example.org": ["MIIB4TCCAUoCCQCfmw3vMgPS5TANBgkqhkiG9w0BAQQFADA1MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTERMA8GA1UEChMITUQ1IEluYy4wHhcNMTUxMjAzMTkyOTMyWhcNMjkwODEyMTkyOTMyWjA1MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTERMA8GA1UEChMITUQ1IEluYy4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANrq2nhLQj5mlXbpVX3QUPhfEm/vdEqPkoWtR/jRZIWm4WGfWpq/LKHJx2Pqwn+t117syN8l4U5unyAi1BJSXjBwPZNd7dXjcuJ+bRLV7FZ/iuvscfYyQQFTxan4TaJMd0x1HoNDbNbjHa02IyjjYE/r3mb/PIg+J2t5AZEh80lPAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAjGzp3K3ey/YfKHohf33yHHWd695HQxDAP+wYcs9/TAyLR+gJzJP7d18EcDDLJWVi7bhfa4EAD86di05azOh9kWSn4b3o9QYRGCSwGNnI3Zk0cwNKA49hZntKKiy22DhRk7JAHF01d6Bu3KkHkmENrtJ+zj/+159WAnUaqViorq4="] -} -}` afero.WriteFile(appFS, "vault-spire-certs.json", []byte(certJSON), 600) workloadAPI := spiffetest.NewWorkloadAPI(t, nil) From 63dcb1dcec37d218ce1314ff8ea992f3ec7d95c2 Mon Sep 17 00:00:00 2001 From: Peyton Walters Date: Thu, 2 Jan 2020 21:14:14 -0500 Subject: [PATCH 03/21] factor out making svid response Signed-off-by: Peyton Walters --- internal/common/spiffetrustsource_test.go | 71 +++++++---------------- 1 file changed, 21 insertions(+), 50 deletions(-) diff --git a/internal/common/spiffetrustsource_test.go b/internal/common/spiffetrustsource_test.go index 025e9c4..2b0a73c 100644 --- a/internal/common/spiffetrustsource_test.go +++ b/internal/common/spiffetrustsource_test.go @@ -20,6 +20,19 @@ var ( }` ) +// makeX509SVIDResponse is a convenience function for generating X509 responses +func makeX509SVIDResponse(ca *spiffetest.CA, svid []*x509.Certificate, key crypto.Signer) *spiffetest.X509SVIDResponse { + return &spiffetest.X509SVIDResponse{ + Bundle: ca.Roots(), + SVIDs: []spiffetest.X509SVID{ + { + CertChain: svid, + Key: key, + }, + }, + } +} + func TestInitalLoad(t *testing.T) { appFS = afero.NewMemMapFs() @@ -30,6 +43,8 @@ func TestInitalLoad(t *testing.T) { source, err := NewSpireTrustSource(map[string]string{}, "vault-spire-certs.json") require.NoError(t, err) + defer source.Stop() + certs := source.TrustedCertificates()["spiffe://example.org"] require.Len(t, certs, 1) assert.Equal(t, x509.MD5WithRSA, certs[0].SignatureAlgorithm) @@ -44,18 +59,7 @@ func TestWriteCerts(t *testing.T) { ca := spiffetest.NewCA(t) svidFoo, keyFoo := ca.CreateX509SVID("spiffe://example.org/foo") - makeX509SVIDResponse := func(svid []*x509.Certificate, key crypto.Signer) *spiffetest.X509SVIDResponse { - return &spiffetest.X509SVIDResponse{ - Bundle: ca.Roots(), - SVIDs: []spiffetest.X509SVID{ - { - CertChain: svid, - Key: key, - }, - }, - } - } - workloadAPI.SetX509SVIDResponse(makeX509SVIDResponse(svidFoo, keyFoo)) + workloadAPI.SetX509SVIDResponse(makeX509SVIDResponse(ca, svidFoo, keyFoo)) source, err := NewSpireTrustSource(map[string]string{ "spiffe://example.org": workloadAPI.Addr(), @@ -80,23 +84,13 @@ func TestSpireOverwrite(t *testing.T) { ca := spiffetest.NewCA(t) svidFoo, keyFoo := ca.CreateX509SVID("spiffe://example.org/foo") - makeX509SVIDResponse := func(svid []*x509.Certificate, key crypto.Signer) *spiffetest.X509SVIDResponse { - return &spiffetest.X509SVIDResponse{ - Bundle: ca.Roots(), - SVIDs: []spiffetest.X509SVID{ - { - CertChain: svid, - Key: key, - }, - }, - } - } - workloadAPI.SetX509SVIDResponse(makeX509SVIDResponse(svidFoo, keyFoo)) + workloadAPI.SetX509SVIDResponse(makeX509SVIDResponse(ca, svidFoo, keyFoo)) source, err := NewSpireTrustSource(map[string]string{ "spiffe://example.org": workloadAPI.Addr(), }, "vault-spire-certs.json") require.NoError(t, err) + defer source.Stop() time.Sleep(1 * time.Second) // wait for watcher to get new certs assert.Equal(t, ca.Roots(), source.TrustedCertificates()["spiffe://example.org"]) @@ -110,43 +104,20 @@ func TestSpireReload(t *testing.T) { ca := spiffetest.NewCA(t) svidFoo, keyFoo := ca.CreateX509SVID("spiffe://example.org/foo") - - makeX509SVIDResponse := func(svid []*x509.Certificate, key crypto.Signer) *spiffetest.X509SVIDResponse { - return &spiffetest.X509SVIDResponse{ - Bundle: ca.Roots(), - SVIDs: []spiffetest.X509SVID{ - { - CertChain: svid, - Key: key, - }, - }, - } - } - workloadAPI.SetX509SVIDResponse(makeX509SVIDResponse(svidFoo, keyFoo)) + workloadAPI.SetX509SVIDResponse(makeX509SVIDResponse(ca, svidFoo, keyFoo)) source, err := NewSpireTrustSource(map[string]string{ "spiffe://example.org": workloadAPI.Addr(), }, "") require.NoError(t, err) + defer source.Stop() time.Sleep(1 * time.Second) // wait for watcher to get new certs assert.Equal(t, ca.Roots(), source.TrustedCertificates()["spiffe://example.org"]) caRot := spiffetest.NewCA(t) svidFooRot, keyFooRot := ca.CreateX509SVID("spiffe://example.org/foo") - - makeX509SVIDResponseRot := func(svid []*x509.Certificate, key crypto.Signer) *spiffetest.X509SVIDResponse { - return &spiffetest.X509SVIDResponse{ - Bundle: caRot.Roots(), - SVIDs: []spiffetest.X509SVID{ - { - CertChain: svid, - Key: key, - }, - }, - } - } - workloadAPI.SetX509SVIDResponse(makeX509SVIDResponseRot(svidFooRot, keyFooRot)) + workloadAPI.SetX509SVIDResponse(makeX509SVIDResponse(caRot, svidFooRot, keyFooRot)) time.Sleep(1 * time.Second) // wait for watcher to get new certs assert.Equal(t, caRot.Roots(), source.TrustedCertificates()["spiffe://example.org"]) From e6906561281e30f884e5feabe11c501e2b637ff3 Mon Sep 17 00:00:00 2001 From: Peyton Walters Date: Fri, 3 Jan 2020 11:46:52 -0500 Subject: [PATCH 04/21] Fix error messages truth -> trust Signed-off-by: Peyton Walters --- cmd/plugin/vault-auth-spire.go | 6 +++--- internal/common/settings.go | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd/plugin/vault-auth-spire.go b/cmd/plugin/vault-auth-spire.go index 39fbf23..d7a4ed4 100644 --- a/cmd/plugin/vault-auth-spire.go +++ b/cmd/plugin/vault-auth-spire.go @@ -114,14 +114,14 @@ func BackendFactory(ctx context.Context, backendConfig *logical.BackendConfig) ( spirePlugin.verifier.AddTrustSource(&trustSource) } if settings.SourceOfTrust.Spire != nil { - trustSource, err := common.NewSpireTrustSource(settings.SourceOfTrust.Spire.URLs, settings.SourceOfTrust.Spire.CertLocation) + trustSource, err := common.NewSpireTrustSource(settings.SourceOfTrust.Spire.SpireEndpoints, settings.SourceOfTrust.Spire.CertStorePath) if err != nil { - return nil, errors.New("vault-auth-spire: Failed to initialize file TrustSource - " + err.Error()) + 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 truth in settings") + 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 diff --git a/internal/common/settings.go b/internal/common/settings.go index b0610fc..0a7d7e8 100644 --- a/internal/common/settings.go +++ b/internal/common/settings.go @@ -36,8 +36,8 @@ type FileTrustSourceSettings struct { } type SpireTrustSourceSettings struct { - URLs map[string]string - CertLocation string + SpireEndpoints map[string]string + CertStorePath string } type LogSettings struct { @@ -144,12 +144,12 @@ func readSpireSourceOfTrustSettings() (*SpireTrustSourceSettings, error) { } spireSettings := new(SpireTrustSourceSettings) - spireSettings.URLs = viper.GetStringMapString("trustsource.spire.domains") + spireSettings.SpireEndpoints = viper.GetStringMapString("trustsource.spire.domains") viper.SetDefault("trustsource.spire.certLocation", "/tmp/vault-spire-certs.json") viper.SetDefault("trustsource.spire.storeEnabled", true) - spireSettings.CertLocation = viper.GetString("trustsource.spire.certLocation") + spireSettings.CertStorePath = viper.GetString("trustsource.spire.certLocation") if !viper.GetBool("trustsource.spire.storeEnabled") { - spireSettings.CertLocation = "" + spireSettings.CertStorePath = "" } return spireSettings, nil From 0f7d827c044027bd100d6e81837b91b3e4f71518 Mon Sep 17 00:00:00 2001 From: Peyton Walters Date: Fri, 3 Jan 2020 15:35:10 -0500 Subject: [PATCH 05/21] Consolidate setX509SVIDResponse Signed-off-by: Peyton Walters --- cmd/plugin/vault-auth-spire.go | 2 +- go.mod | 2 +- internal/common/settings.go | 7 ++++--- internal/common/spiretrustsource.go | 4 +--- ...trustsource_test.go => spiretrustsource_test.go} | 13 +++++++------ 5 files changed, 14 insertions(+), 14 deletions(-) rename internal/common/{spiffetrustsource_test.go => spiretrustsource_test.go} (88%) diff --git a/cmd/plugin/vault-auth-spire.go b/cmd/plugin/vault-auth-spire.go index d7a4ed4..feb5f28 100644 --- a/cmd/plugin/vault-auth-spire.go +++ b/cmd/plugin/vault-auth-spire.go @@ -23,7 +23,7 @@ import ( "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/logical" "github.com/sirupsen/logrus" - "vault-auth-spire/internal/common" + "github.com/bloomberg/vault-auth-spire/internal/common" "log" "os" diff --git a/go.mod b/go.mod index d9b43b4..ad6e38f 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module vault-auth-spire +module github.com/bloomberg/vault-auth-spire go 1.12 diff --git a/internal/common/settings.go b/internal/common/settings.go index 0a7d7e8..10093d6 100644 --- a/internal/common/settings.go +++ b/internal/common/settings.go @@ -143,11 +143,12 @@ func readSpireSourceOfTrustSettings() (*SpireTrustSourceSettings, error) { return nil, errors.New("trustsource.spire.domains is required but not found") } - spireSettings := new(SpireTrustSourceSettings) - spireSettings.SpireEndpoints = viper.GetStringMapString("trustsource.spire.domains") viper.SetDefault("trustsource.spire.certLocation", "/tmp/vault-spire-certs.json") viper.SetDefault("trustsource.spire.storeEnabled", true) - spireSettings.CertStorePath = viper.GetString("trustsource.spire.certLocation") + spireSettings := &SpireTrustSourceSettings{ + SpireEndpoints: viper.GetStringMapString("trustsource.spire.domains"), + CertStorePath: viper.GetString("trustsource.spire.certLocation"), + } if !viper.GetBool("trustsource.spire.storeEnabled") { spireSettings.CertStorePath = "" } diff --git a/internal/common/spiretrustsource.go b/internal/common/spiretrustsource.go index e429095..582cb1e 100644 --- a/internal/common/spiretrustsource.go +++ b/internal/common/spiretrustsource.go @@ -74,10 +74,8 @@ func (s *SpireTrustSource) parseCertFile() error { if err != nil { return fmt.Errorf("could not read cert file: %v", err) } - fmt.Println(string(fileDat)) var certStruct certMap - err = json.Unmarshal(fileDat, &certStruct) - if err != nil { + if err = json.Unmarshal(fileDat, &certStruct); err != nil { logrus.Warnf("Error unmarshaling cert file: %v\n", err) } for domain, encCerts := range certStruct.Certs { diff --git a/internal/common/spiffetrustsource_test.go b/internal/common/spiretrustsource_test.go similarity index 88% rename from internal/common/spiffetrustsource_test.go rename to internal/common/spiretrustsource_test.go index 2b0a73c..17ac7e9 100644 --- a/internal/common/spiffetrustsource_test.go +++ b/internal/common/spiretrustsource_test.go @@ -21,8 +21,8 @@ var ( ) // makeX509SVIDResponse is a convenience function for generating X509 responses -func makeX509SVIDResponse(ca *spiffetest.CA, svid []*x509.Certificate, key crypto.Signer) *spiffetest.X509SVIDResponse { - return &spiffetest.X509SVIDResponse{ +func setX509SVIDResponse(api *spiffetest.WorkloadAPI, ca *spiffetest.CA, svid []*x509.Certificate, key crypto.Signer) { + response := &spiffetest.X509SVIDResponse{ Bundle: ca.Roots(), SVIDs: []spiffetest.X509SVID{ { @@ -31,6 +31,7 @@ func makeX509SVIDResponse(ca *spiffetest.CA, svid []*x509.Certificate, key crypt }, }, } + api.SetX509SVIDResponse(response) } func TestInitalLoad(t *testing.T) { @@ -59,7 +60,7 @@ func TestWriteCerts(t *testing.T) { ca := spiffetest.NewCA(t) svidFoo, keyFoo := ca.CreateX509SVID("spiffe://example.org/foo") - workloadAPI.SetX509SVIDResponse(makeX509SVIDResponse(ca, svidFoo, keyFoo)) + setX509SVIDResponse(workloadAPI, ca, svidFoo, keyFoo) source, err := NewSpireTrustSource(map[string]string{ "spiffe://example.org": workloadAPI.Addr(), @@ -84,7 +85,7 @@ func TestSpireOverwrite(t *testing.T) { ca := spiffetest.NewCA(t) svidFoo, keyFoo := ca.CreateX509SVID("spiffe://example.org/foo") - workloadAPI.SetX509SVIDResponse(makeX509SVIDResponse(ca, svidFoo, keyFoo)) + setX509SVIDResponse(workloadAPI, ca, svidFoo, keyFoo) source, err := NewSpireTrustSource(map[string]string{ "spiffe://example.org": workloadAPI.Addr(), @@ -104,7 +105,7 @@ func TestSpireReload(t *testing.T) { ca := spiffetest.NewCA(t) svidFoo, keyFoo := ca.CreateX509SVID("spiffe://example.org/foo") - workloadAPI.SetX509SVIDResponse(makeX509SVIDResponse(ca, svidFoo, keyFoo)) + setX509SVIDResponse(workloadAPI, ca, svidFoo, keyFoo) source, err := NewSpireTrustSource(map[string]string{ "spiffe://example.org": workloadAPI.Addr(), @@ -117,7 +118,7 @@ func TestSpireReload(t *testing.T) { caRot := spiffetest.NewCA(t) svidFooRot, keyFooRot := ca.CreateX509SVID("spiffe://example.org/foo") - workloadAPI.SetX509SVIDResponse(makeX509SVIDResponse(caRot, svidFooRot, keyFooRot)) + setX509SVIDResponse(workloadAPI, caRot, svidFooRot, keyFooRot) time.Sleep(1 * time.Second) // wait for watcher to get new certs assert.Equal(t, caRot.Roots(), source.TrustedCertificates()["spiffe://example.org"]) From 9ae3bbffbff3dee95ff512fe80ee4616f49489c9 Mon Sep 17 00:00:00 2001 From: Peyton Walters Date: Fri, 3 Jan 2020 16:21:33 -0500 Subject: [PATCH 06/21] move from time.sleep to an update method Signed-off-by: Peyton Walters --- go.mod | 1 + go.sum | 4 ++++ internal/common/spiretrustsource.go | 11 ++++++++++- internal/common/spiretrustsource_test.go | 19 ++++++++++++++----- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index ad6e38f..1f604ac 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/hashicorp/vault/api v1.0.4 github.com/hashicorp/vault/sdk v0.1.13 github.com/natefinch/lumberjack v2.0.0+incompatible + 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 diff --git a/go.sum b/go.sum index 688f923..9e9c29b 100644 --- a/go.sum +++ b/go.sum @@ -2,7 +2,9 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -150,6 +152,7 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -257,6 +260,7 @@ google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw= google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/common/spiretrustsource.go b/internal/common/spiretrustsource.go index 582cb1e..2fb6840 100644 --- a/internal/common/spiretrustsource.go +++ b/internal/common/spiretrustsource.go @@ -7,6 +7,7 @@ import ( "fmt" "io/ioutil" "os" + "time" "github.com/sirupsen/logrus" "github.com/spiffe/go-spiffe/workload" @@ -18,6 +19,8 @@ type SpireTrustSource struct { domainCertificates map[string][]*x509.Certificate spireClients []*workload.X509SVIDClient certLocation string + updateChan chan struct{} + updateTimeout time.Duration } type certMap struct { @@ -40,6 +43,8 @@ func NewSpireTrustSource(domainURLs map[string]string, certLocation string) (*Sp domainURLs: domainURLs, domainCertificates: make(map[string][]*x509.Certificate, 0), certLocation: certLocation, + updateChan: make(chan struct{}, 0), + updateTimeout: 5 * time.Second, } if certLocation != "" { @@ -145,7 +150,7 @@ func (s *SpireTrustSource) startWatchers() error { s.spireClients = append(s.spireClients, client) logrus.Infof("Starting listener for %s.\n", id) - go client.Start() + client.Start() } return nil } @@ -156,6 +161,10 @@ func (w *watcher) UpdateX509SVIDs(svids *workload.X509SVIDs) { if err != nil { logrus.Warnf("Error writing to cert file: %v\n", err) } + select { + case w.source.updateChan <- struct{}{}: + case <-time.After(w.source.updateTimeout): + } } func (w *watcher) OnError(err error) { diff --git a/internal/common/spiretrustsource_test.go b/internal/common/spiretrustsource_test.go index 17ac7e9..939b422 100644 --- a/internal/common/spiretrustsource_test.go +++ b/internal/common/spiretrustsource_test.go @@ -12,12 +12,13 @@ import ( "github.com/stretchr/testify/require" ) -var ( +const ( certJSON = `{ "certs": { "spiffe://example.org": ["MIIB4TCCAUoCCQCfmw3vMgPS5TANBgkqhkiG9w0BAQQFADA1MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTERMA8GA1UEChMITUQ1IEluYy4wHhcNMTUxMjAzMTkyOTMyWhcNMjkwODEyMTkyOTMyWjA1MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTERMA8GA1UEChMITUQ1IEluYy4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANrq2nhLQj5mlXbpVX3QUPhfEm/vdEqPkoWtR/jRZIWm4WGfWpq/LKHJx2Pqwn+t117syN8l4U5unyAi1BJSXjBwPZNd7dXjcuJ+bRLV7FZ/iuvscfYyQQFTxan4TaJMd0x1HoNDbNbjHa02IyjjYE/r3mb/PIg+J2t5AZEh80lPAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAjGzp3K3ey/YfKHohf33yHHWd695HQxDAP+wYcs9/TAyLR+gJzJP7d18EcDDLJWVi7bhfa4EAD86di05azOh9kWSn4b3o9QYRGCSwGNnI3Zk0cwNKA49hZntKKiy22DhRk7JAHF01d6Bu3KkHkmENrtJ+zj/+159WAnUaqViorq4="] } }` + updateTimeout = 5 * time.Second ) // makeX509SVIDResponse is a convenience function for generating X509 responses @@ -67,7 +68,7 @@ func TestWriteCerts(t *testing.T) { }, "vault-spire-certs.json") require.NoError(t, err) - time.Sleep(1 * time.Second) // wait for watcher to get new certs + source.waitForUpdate(t) source.Stop() newSource, err := NewSpireTrustSource(map[string]string{}, "vault-spire-certs.json") @@ -93,7 +94,7 @@ func TestSpireOverwrite(t *testing.T) { require.NoError(t, err) defer source.Stop() - time.Sleep(1 * time.Second) // wait for watcher to get new certs + source.waitForUpdate(t) assert.Equal(t, ca.Roots(), source.TrustedCertificates()["spiffe://example.org"]) } @@ -113,13 +114,21 @@ func TestSpireReload(t *testing.T) { require.NoError(t, err) defer source.Stop() - time.Sleep(1 * time.Second) // wait for watcher to get new certs + source.waitForUpdate(t) assert.Equal(t, ca.Roots(), source.TrustedCertificates()["spiffe://example.org"]) caRot := spiffetest.NewCA(t) svidFooRot, keyFooRot := ca.CreateX509SVID("spiffe://example.org/foo") setX509SVIDResponse(workloadAPI, caRot, svidFooRot, keyFooRot) - time.Sleep(1 * time.Second) // wait for watcher to get new certs + source.waitForUpdate(t) assert.Equal(t, caRot.Roots(), source.TrustedCertificates()["spiffe://example.org"]) } + +func (s *SpireTrustSource) waitForUpdate(t *testing.T) { + select { + case <-s.updateChan: + case <-time.After(updateTimeout): + require.Fail(t, "Timeout exceeding waiting for updates.") + } +} From 973fc23af1d3ec2c7f05ddb542f550e433b2c54f Mon Sep 17 00:00:00 2001 From: Peyton Walters Date: Tue, 7 Jan 2020 10:22:45 -0500 Subject: [PATCH 07/21] Add make test Signed-off-by: Peyton Walters --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 66eb12e..3fe899a 100644 --- a/Makefile +++ b/Makefile @@ -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 ./... + clean: @rm -f vault-auth-spire From 3bc630c94729755d3ce0813a73764bd4e6dd60f8 Mon Sep 17 00:00:00 2001 From: Peyton Walters Date: Tue, 7 Jan 2020 12:24:06 -0500 Subject: [PATCH 08/21] Move over filetrustsource Signed-off-by: Peyton Walters --- cmd/plugin/vault-auth-spire.go | 4 +- internal/common/common_test.go | 67 ++++++++++++++++ internal/common/filetrustsource.go | 87 +++++++++++++++------ internal/common/settings.go | 2 +- internal/common/spiretrustsource.go | 98 ++++++++---------------- internal/common/spiretrustsource_test.go | 28 ++++--- 6 files changed, 183 insertions(+), 103 deletions(-) create mode 100644 internal/common/common_test.go diff --git a/cmd/plugin/vault-auth-spire.go b/cmd/plugin/vault-auth-spire.go index feb5f28..f340678 100644 --- a/cmd/plugin/vault-auth-spire.go +++ b/cmd/plugin/vault-auth-spire.go @@ -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" - "github.com/bloomberg/vault-auth-spire/internal/common" "log" "os" @@ -111,7 +111,7 @@ func BackendFactory(ctx context.Context, backendConfig *logical.BackendConfig) ( if err != nil { return nil, errors.New("vault-auth-spire: Failed to initialize file TrustSource - " + err.Error()) } - spirePlugin.verifier.AddTrustSource(&trustSource) + spirePlugin.verifier.AddTrustSource(trustSource) } if settings.SourceOfTrust.Spire != nil { trustSource, err := common.NewSpireTrustSource(settings.SourceOfTrust.Spire.SpireEndpoints, settings.SourceOfTrust.Spire.CertStorePath) diff --git a/internal/common/common_test.go b/internal/common/common_test.go new file mode 100644 index 0000000..69675ff --- /dev/null +++ b/internal/common/common_test.go @@ -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 +) diff --git a/internal/common/filetrustsource.go b/internal/common/filetrustsource.go index be119fb..96de17b 100644 --- a/internal/common/filetrustsource.go +++ b/internal/common/filetrustsource.go @@ -18,9 +18,12 @@ package common import ( "crypto/x509" + "encoding/pem" "errors" "fmt" "io/ioutil" + "os" + "strings" "github.com/sirupsen/logrus" ) @@ -39,17 +42,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 @@ -58,32 +61,70 @@ func (source *FileTrustSource) TrustedCertificates() map[string][]*x509.Certific return source.domainCertificates } +func (source *FileTrustSource) updateCertificates(certs []*x509.Certificate, spiffeID, path string) error { + builder := strings.Builder{} + for _, cert := range certs { + block := &pem.Block{ + Type: "CERTIFICATE", + Bytes: cert.Raw, + } + builder.Write(pem.EncodeToMemory(block)) + } + + file, err := appFS.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600) + if err != nil { + return err + } + defer file.Close() + _, err = file.WriteString(builder.String()) + if err != nil { + return err + } + + err = source.loadDomain(spiffeID) + if err != nil { + return err + } + + return nil +} + // 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 _, 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()) - } - - 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 domain := range source.domainPaths { + err := source.loadDomain(domain) + if err != nil { + return err } + } + + return nil +} + +func (source *FileTrustSource) loadDomain(domain string) error { + paths := source.domainPaths[domain] + domainCertificates := make([]*x509.Certificate, 0) - source.domainCertificates[domain] = domainCertificates + 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()) + } + + 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 } diff --git a/internal/common/settings.go b/internal/common/settings.go index 10093d6..30161d3 100644 --- a/internal/common/settings.go +++ b/internal/common/settings.go @@ -143,7 +143,7 @@ func readSpireSourceOfTrustSettings() (*SpireTrustSourceSettings, error) { return nil, errors.New("trustsource.spire.domains is required but not found") } - viper.SetDefault("trustsource.spire.certLocation", "/tmp/vault-spire-certs.json") + viper.SetDefault("trustsource.spire.certLocation", "/var/run/spire/certs/") viper.SetDefault("trustsource.spire.storeEnabled", true) spireSettings := &SpireTrustSourceSettings{ SpireEndpoints: viper.GetStringMapString("trustsource.spire.domains"), diff --git a/internal/common/spiretrustsource.go b/internal/common/spiretrustsource.go index 2fb6840..8668a0c 100644 --- a/internal/common/spiretrustsource.go +++ b/internal/common/spiretrustsource.go @@ -2,11 +2,10 @@ package common import ( "crypto/x509" - "encoding/base64" - "encoding/json" "fmt" - "io/ioutil" "os" + "regexp" + "strings" "time" "github.com/sirupsen/logrus" @@ -17,6 +16,7 @@ import ( type SpireTrustSource struct { domainURLs map[string]string domainCertificates map[string][]*x509.Certificate + fileBacking *FileTrustSource spireClients []*workload.X509SVIDClient certLocation string updateChan chan struct{} @@ -48,7 +48,7 @@ func NewSpireTrustSource(domainURLs map[string]string, certLocation string) (*Sp } if certLocation != "" { - if err := source.parseCertFile(); err != nil { + if err := source.initFileTrustSource(); err != nil { return nil, err } } @@ -65,74 +65,38 @@ func (s *SpireTrustSource) Stop() { for _, client := range s.spireClients { client.Stop() } - s.writeCertFile() } -func (s *SpireTrustSource) parseCertFile() error { - file, err := appFS.OpenFile(s.certLocation, os.O_RDWR|os.O_CREATE, 600) - if err != nil { - return err - } - defer file.Close() - - fileDat, err := ioutil.ReadAll(file) - if err != nil { - return fmt.Errorf("could not read cert file: %v", err) - } - var certStruct certMap - if err = json.Unmarshal(fileDat, &certStruct); err != nil { - logrus.Warnf("Error unmarshaling cert file: %v\n", err) - } - for domain, encCerts := range certStruct.Certs { - decodedCerts := make([]*x509.Certificate, 0) - for _, certB64 := range encCerts { - encCert, err := base64.StdEncoding.DecodeString(certB64) - if err != nil { - logrus.Warnf("Could not base 64 decode string: %v\n", err) - continue - } - decodedCert, err := x509.ParseCertificate([]byte(encCert)) - if err != nil { - logrus.Warnf("Could not decode certificate for domain %s: %v\n", domain, err) - continue - } - decodedCerts = append(decodedCerts, decodedCert) +func (s *SpireTrustSource) initFileTrustSource() error { + re := regexp.MustCompile(`spiffe://(\S+)`) + domainPaths := make(map[string][]string, 0) + for uri := range s.domainURLs { + matches := re.FindStringSubmatch(uri) + if len(matches) < 2 { + return fmt.Errorf("expected domain of form spiffe:// but got %s", uri) } - s.domainCertificates[domain] = decodedCerts - } - return nil -} - -func (s *SpireTrustSource) writeCertFile() error { - if s.certLocation == "" { - return nil - } - - certStruct := certMap{ - Certs: make(map[string][]string, 0), - } + domain := matches[1] + if strings.Contains(domain, "/") { + return fmt.Errorf("expected domain without slash but got %s", domain) + } + certPath := s.certLocation + "/" + domain + ".pem" + domainPaths[uri] = []string{certPath} - for domain, certObjs := range s.TrustedCertificates() { - toWrite := make([]string, 0) - for _, certObj := range certObjs { - marshalled := base64.StdEncoding.EncodeToString(certObj.Raw) - toWrite = append(toWrite, string(marshalled)) + f, err := appFS.OpenFile(certPath, os.O_CREATE, 0600) + if err != nil { + return fmt.Errorf("error when trying to open file %s: %v", certPath, err) } - certStruct.Certs[domain] = toWrite + f.Close() } - finalData, _ := json.Marshal(certStruct) - - file, err := appFS.OpenFile(s.certLocation, os.O_RDWR|os.O_CREATE, 600) - if err != nil { - return err - } - defer file.Close() - _, err = file.Write(finalData) + var err error + s.fileBacking, err = NewFileTrustSource(domainPaths) if err != nil { return err } + s.domainCertificates = s.fileBacking.TrustedCertificates() + return nil } @@ -156,10 +120,14 @@ func (s *SpireTrustSource) startWatchers() error { } func (w *watcher) UpdateX509SVIDs(svids *workload.X509SVIDs) { - w.source.domainCertificates[w.uri] = svids.Default().TrustBundle - err := w.source.writeCertFile() - if err != nil { - logrus.Warnf("Error writing to cert file: %v\n", err) + certs := svids.Default().TrustBundle + w.source.domainCertificates[w.uri] = certs + if w.source.certLocation != "" { + certPath := w.source.fileBacking.domainPaths[w.uri][0] + err := w.source.fileBacking.updateCertificates(certs, w.uri, certPath) + if err != nil { + logrus.Warnf("error writing to cert file: %v", err) + } } select { case w.source.updateChan <- struct{}{}: diff --git a/internal/common/spiretrustsource_test.go b/internal/common/spiretrustsource_test.go index 939b422..1c32184 100644 --- a/internal/common/spiretrustsource_test.go +++ b/internal/common/spiretrustsource_test.go @@ -13,11 +13,6 @@ import ( ) const ( - certJSON = `{ -"certs": { - "spiffe://example.org": ["MIIB4TCCAUoCCQCfmw3vMgPS5TANBgkqhkiG9w0BAQQFADA1MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTERMA8GA1UEChMITUQ1IEluYy4wHhcNMTUxMjAzMTkyOTMyWhcNMjkwODEyMTkyOTMyWjA1MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTERMA8GA1UEChMITUQ1IEluYy4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANrq2nhLQj5mlXbpVX3QUPhfEm/vdEqPkoWtR/jRZIWm4WGfWpq/LKHJx2Pqwn+t117syN8l4U5unyAi1BJSXjBwPZNd7dXjcuJ+bRLV7FZ/iuvscfYyQQFTxan4TaJMd0x1HoNDbNbjHa02IyjjYE/r3mb/PIg+J2t5AZEh80lPAgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAjGzp3K3ey/YfKHohf33yHHWd695HQxDAP+wYcs9/TAyLR+gJzJP7d18EcDDLJWVi7bhfa4EAD86di05azOh9kWSn4b3o9QYRGCSwGNnI3Zk0cwNKA49hZntKKiy22DhRk7JAHF01d6Bu3KkHkmENrtJ+zj/+159WAnUaqViorq4="] -} -}` updateTimeout = 5 * time.Second ) @@ -38,18 +33,22 @@ func setX509SVIDResponse(api *spiffetest.WorkloadAPI, ca *spiffetest.CA, svid [] func TestInitalLoad(t *testing.T) { appFS = afero.NewMemMapFs() - afero.WriteFile(appFS, "vault-spire-certs.json", []byte(certJSON), 600) + afero.WriteFile(appFS, "certs/example.org.pem", []byte(leafCert), 600) workloadAPI := spiffetest.NewWorkloadAPI(t, nil) defer workloadAPI.Stop() - source, err := NewSpireTrustSource(map[string]string{}, "vault-spire-certs.json") + source, err := NewSpireTrustSource(map[string]string{ + "spiffe://example.org": workloadAPI.Addr(), + }, "certs/") require.NoError(t, err) defer source.Stop() certs := source.TrustedCertificates()["spiffe://example.org"] require.Len(t, certs, 1) - assert.Equal(t, x509.MD5WithRSA, certs[0].SignatureAlgorithm) + assert.Equal(t, "US", certs[0].Subject.Country[0]) + assert.Equal(t, "test1.acme.com", certs[0].Subject.Organization[0]) + assert.Equal(t, "blog", certs[0].Subject.CommonName) } func TestWriteCerts(t *testing.T) { @@ -65,20 +64,25 @@ func TestWriteCerts(t *testing.T) { source, err := NewSpireTrustSource(map[string]string{ "spiffe://example.org": workloadAPI.Addr(), - }, "vault-spire-certs.json") + }, "certs/") require.NoError(t, err) source.waitForUpdate(t) source.Stop() - newSource, err := NewSpireTrustSource(map[string]string{}, "vault-spire-certs.json") + dummyWorkloadAPI := spiffetest.NewWorkloadAPI(t, nil) + defer dummyWorkloadAPI.Stop() + + newSource, err := NewSpireTrustSource(map[string]string{ + "spiffe://example.org": dummyWorkloadAPI.Addr(), + }, "certs/") assert.Equal(t, ca.Roots(), newSource.TrustedCertificates()["spiffe://example.org"]) } func TestSpireOverwrite(t *testing.T) { appFS = afero.NewMemMapFs() - afero.WriteFile(appFS, "vault-spire-certs.json", []byte(certJSON), 600) + afero.WriteFile(appFS, "certs/example.org.pem", []byte(leafCert), 600) workloadAPI := spiffetest.NewWorkloadAPI(t, nil) defer workloadAPI.Stop() @@ -90,7 +94,7 @@ func TestSpireOverwrite(t *testing.T) { source, err := NewSpireTrustSource(map[string]string{ "spiffe://example.org": workloadAPI.Addr(), - }, "vault-spire-certs.json") + }, "certs/") require.NoError(t, err) defer source.Stop() From e53003f019b5ebe1110a890c297b5ab5a84957d4 Mon Sep 17 00:00:00 2001 From: Peyton Walters Date: Tue, 7 Jan 2020 15:15:11 -0500 Subject: [PATCH 09/21] Add more spire tests Signed-off-by: Peyton Walters --- internal/common/spiretrustsource_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/internal/common/spiretrustsource_test.go b/internal/common/spiretrustsource_test.go index 1c32184..a9e9638 100644 --- a/internal/common/spiretrustsource_test.go +++ b/internal/common/spiretrustsource_test.go @@ -51,6 +51,20 @@ func TestInitalLoad(t *testing.T) { assert.Equal(t, "blog", certs[0].Subject.CommonName) } +func TestInvalidURI(t *testing.T) { + _, err := NewSpireTrustSource(map[string]string{ + "spirffe://example.org": "", + }, "certs/") + require.Error(t, err) +} + +func TestInvalidDomain(t *testing.T) { + _, err := NewSpireTrustSource(map[string]string{ + "spiffe://example.org/test": "", + }, "certs/") + require.Error(t, err) +} + func TestWriteCerts(t *testing.T) { appFS = afero.NewMemMapFs() From 89c379951a13d44280d89097122fcce95abe88b0 Mon Sep 17 00:00:00 2001 From: Dennis Gove Date: Wed, 8 Jan 2020 13:37:38 -0500 Subject: [PATCH 10/21] Suggestions for changes Signed-off-by: Peyton Walters --- cmd/plugin/vault-auth-spire.go | 2 +- internal/common/settings.go | 4 +- internal/common/spiretrustsource.go | 193 ++++++++++++++++------------ 3 files changed, 113 insertions(+), 86 deletions(-) diff --git a/cmd/plugin/vault-auth-spire.go b/cmd/plugin/vault-auth-spire.go index f340678..786b1d1 100644 --- a/cmd/plugin/vault-auth-spire.go +++ b/cmd/plugin/vault-auth-spire.go @@ -114,7 +114,7 @@ func BackendFactory(ctx context.Context, backendConfig *logical.BackendConfig) ( spirePlugin.verifier.AddTrustSource(trustSource) } if settings.SourceOfTrust.Spire != nil { - trustSource, err := common.NewSpireTrustSource(settings.SourceOfTrust.Spire.SpireEndpoints, settings.SourceOfTrust.Spire.CertStorePath) + 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()) } diff --git a/internal/common/settings.go b/internal/common/settings.go index 30161d3..0eeb9de 100644 --- a/internal/common/settings.go +++ b/internal/common/settings.go @@ -36,8 +36,8 @@ type FileTrustSourceSettings struct { } type SpireTrustSourceSettings struct { - SpireEndpoints map[string]string - CertStorePath string + SpireEndpointUrls map[string]string + LocalBackupPath string } type LogSettings struct { diff --git a/internal/common/spiretrustsource.go b/internal/common/spiretrustsource.go index 8668a0c..2d93aaa 100644 --- a/internal/common/spiretrustsource.go +++ b/internal/common/spiretrustsource.go @@ -2,6 +2,7 @@ package common import ( "crypto/x509" + "errors" "fmt" "os" "regexp" @@ -12,24 +13,42 @@ import ( "github.com/spiffe/go-spiffe/workload" ) +type SpireLoadState int + +const ( + Pending SpireLoadState = iota + Loaded + LoadedFromBackup + Failed +) + // SpireTrustSource holds all necessary information to connect to a spire instance and store its certificates +type SpireEndpoint struct { + domain string + loadState SpireLoadState + spireUrl string + + client *workload.X509SVIDClient +} type SpireTrustSource struct { - domainURLs map[string]string + spireEndpoints map[string]*SpireEndpoint + + localBackupDir string + + updateChan chan struct{} + updateTimeout time.Duration + domainCertificates map[string][]*x509.Certificate - fileBacking *FileTrustSource - spireClients []*workload.X509SVIDClient - certLocation string - updateChan chan struct{} - updateTimeout time.Duration } type certMap struct { Certs map[string][]string `json:"certs"` } -type watcher struct { - uri string - source *SpireTrustSource +type workloadWatcher struct { + domain string + source *SpireTrustSource + localStoragePath string } // TrustedCertificates fulfills the TrustSource interface @@ -38,103 +57,111 @@ func (s *SpireTrustSource) TrustedCertificates() map[string][]*x509.Certificate } // NewSpireTrustSource creates a new trust source with connectivity to one or more spire instances. -func NewSpireTrustSource(domainURLs map[string]string, certLocation string) (*SpireTrustSource, error) { - source := SpireTrustSource{ - domainURLs: domainURLs, +func NewSpireTrustSource(spireEndpointUrls map[string]string, localBackupDir string) (*SpireTrustSource, error) { + source := &SpireTrustSource{ + spireEndpoints: make(map[string]*SpireEndpoint, 0), + localBackupDir: localBackupDir, domainCertificates: make(map[string][]*x509.Certificate, 0), - certLocation: certLocation, updateChan: make(chan struct{}, 0), updateTimeout: 5 * time.Second, } - if certLocation != "" { - if err := source.initFileTrustSource(); err != nil { - return nil, err + for domain, spireUrl := range spireEndpointUrls { + localStoragePath := "" + if localBackupDir != "" { + localStoragePath = "" // TODO: pull out from domain } - } - - if err := source.startWatchers(); err != nil { - return nil, err - } - - return &source, nil -} - -// Stop stops all spire clients and writes out certs -func (s *SpireTrustSource) Stop() { - for _, client := range s.spireClients { - client.Stop() - } -} -func (s *SpireTrustSource) initFileTrustSource() error { - re := regexp.MustCompile(`spiffe://(\S+)`) - domainPaths := make(map[string][]string, 0) - for uri := range s.domainURLs { - matches := re.FindStringSubmatch(uri) - if len(matches) < 2 { - return fmt.Errorf("expected domain of form spiffe:// but got %s", uri) - } - domain := matches[1] - if strings.Contains(domain, "/") { - return fmt.Errorf("expected domain without slash but got %s", domain) + client, err := workload.NewX509SVIDClient( + &workloadWatcher{ + domain: domain, + source: source, + localStoragePath: localStoragePath, + }, + workload.WithAddr(spireUrl), + ) + if err != nil { + return nil, fmt.Errorf("failed to construct a new NewX509SVIDClient for %s - %v", spireUrl, err) } - certPath := s.certLocation + "/" + domain + ".pem" - domainPaths[uri] = []string{certPath} - f, err := appFS.OpenFile(certPath, os.O_CREATE, 0600) - if err != nil { - return fmt.Errorf("error when trying to open file %s: %v", certPath, err) + source.spireEndpoints[domain] = &SpireEndpoint{ + domain: domain, + loadState: Pending, + spireUrl: spireUrl, + client: client, } - f.Close() } - var err error - s.fileBacking, err = NewFileTrustSource(domainPaths) - if err != nil { - return err + for _, spireEndpoint := range source.spireEndpoints { + if err := spireEndpoint.client.Start(); err != nil { + return nil, fmt.Errorf("failed to start NewX509SVIDClient for domain %s - %v", spireEndpoint.domain, err) + } } - s.domainCertificates = s.fileBacking.TrustedCertificates() - - return nil + return source, nil } -func (s *SpireTrustSource) startWatchers() error { - for id, url := range s.domainURLs { - opts := workload.WithAddr(url) - watch := &watcher{ - uri: id, - source: s, - } - client, err := workload.NewX509SVIDClient(watch, opts) - if err != nil { - return err +// Stop stops all spire clients +func (s *SpireTrustSource) Stop() error { + errs := make([]string, 0) + for _, spireEndpoint := range s.spireEndpoints { + if err := spireEndpoint.client.Stop(); err != nil { + errs = append(errs, fmt.Sprintf("domain %s - %v", spireEndpoint.domain, err)) } - s.spireClients = append(s.spireClients, client) + } - logrus.Infof("Starting listener for %s.\n", id) - client.Start() + if len(errs) != 0 { + return fmt.Errorf("failed to stop NewX509SVIDClients: (%s)", strings.Join(errs, "),(")) } + return nil } -func (w *watcher) UpdateX509SVIDs(svids *workload.X509SVIDs) { - certs := svids.Default().TrustBundle - w.source.domainCertificates[w.uri] = certs - if w.source.certLocation != "" { - certPath := w.source.fileBacking.domainPaths[w.uri][0] - err := w.source.fileBacking.updateCertificates(certs, w.uri, certPath) - if err != nil { - logrus.Warnf("error writing to cert file: %v", err) +func (w *workloadWatcher) UpdateX509SVIDs(svids *workload.X509SVIDs) { + + // TODO: + // 1. Pull out certs from passed in svids and update w.source.domainCertificates + // 2. Update w.source.spireEndpoints[w.url].loadState = Loaded + // 3. write to w.source.updateChan + // + // if w.source.localBackupPath != "" + // 4. write certs to storage path from w.source.localBackupPath/[domain without spiffe:// part].pem + + //certs := svids.Default().TrustBundle + //w.source.domainCertificates[w.uri] = certs + //if w.source.certLocation != "" { + // certPath := w.source.fileBacking.domainPaths[w.uri][0] + // err := w.source.fileBacking.updateCertificates(certs, w.uri, certPath) + // if err != nil { + // logrus.Warnf("error writing to cert file: %v", err) + // } + //} + //select { + //case w.source.updateChan <- struct{}{}: + //case <-time.After(w.source.updateTimeout): + //} +} + +func (w *workloadWatcher) OnError(err error) { + if w.source.spireEndpoints[w.domain].loadState == Pending { + if w.localStoragePath != "" { + domainPaths := map[string][]string{ + w.domain: []string{w.localStoragePath}, + } + + if fileTrustSource, err := NewFileTrustSource(domainPaths); err != nil { + // TODO: log error + w.source.spireEndpoints[w.domain].loadState = Failed + } else { + // TODO: + // 1. Pull out certs from fileTrustSource and update w.source.domainCertificates + // 2. Update w.source.spireEndpoints[w.url].loadState = Loaded + } + } else { + // TODO: log error + w.source.spireEndpoints[w.domain].loadState = Failed } } - select { - case w.source.updateChan <- struct{}{}: - case <-time.After(w.source.updateTimeout): - } -} -func (w *watcher) OnError(err error) { - logrus.Errorf("Error encountered by watcher with uri %s: %v\n", w.uri, err) + // if the state was already Loaded, LoadedFromBackup, or Failed then don't do anything } From 2d9603f4acd6b8035e0433bcadea1ecb499b85bd Mon Sep 17 00:00:00 2001 From: Dennis Gove Date: Wed, 8 Jan 2020 13:40:47 -0500 Subject: [PATCH 11/21] Fixed comment about checking local storage path Signed-off-by: Peyton Walters --- internal/common/spiretrustsource.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/common/spiretrustsource.go b/internal/common/spiretrustsource.go index 2d93aaa..d5e85a1 100644 --- a/internal/common/spiretrustsource.go +++ b/internal/common/spiretrustsource.go @@ -124,7 +124,7 @@ func (w *workloadWatcher) UpdateX509SVIDs(svids *workload.X509SVIDs) { // 2. Update w.source.spireEndpoints[w.url].loadState = Loaded // 3. write to w.source.updateChan // - // if w.source.localBackupPath != "" + // if w.localStoragePath != "" // 4. write certs to storage path from w.source.localBackupPath/[domain without spiffe:// part].pem //certs := svids.Default().TrustBundle From 105f661f2eefce18e229a272b5dcad122de065f2 Mon Sep 17 00:00:00 2001 From: Peyton Walters Date: Wed, 8 Jan 2020 17:49:40 -0500 Subject: [PATCH 12/21] Move to new spire trust source design with state tracking Signed-off-by: Peyton Walters --- cmd/plugin/vault-auth-spire.go | 2 +- internal/common/filetrustsource.go | 31 ------- internal/common/settings.go | 10 +-- internal/common/spiretrustsource.go | 107 ++++++++++++++--------- internal/common/spiretrustsource_test.go | 3 + 5 files changed, 75 insertions(+), 78 deletions(-) diff --git a/cmd/plugin/vault-auth-spire.go b/cmd/plugin/vault-auth-spire.go index 786b1d1..a258283 100644 --- a/cmd/plugin/vault-auth-spire.go +++ b/cmd/plugin/vault-auth-spire.go @@ -114,7 +114,7 @@ func BackendFactory(ctx context.Context, backendConfig *logical.BackendConfig) ( spirePlugin.verifier.AddTrustSource(trustSource) } if settings.SourceOfTrust.Spire != nil { - trustSource, err := common.NewSpireTrustSource(settings.SourceOfTrust.Spire.SpireEndpointUrls, settings.SourceOfTrust.Spire.LocalBackupPath) + 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()) } diff --git a/internal/common/filetrustsource.go b/internal/common/filetrustsource.go index 96de17b..b5498fd 100644 --- a/internal/common/filetrustsource.go +++ b/internal/common/filetrustsource.go @@ -18,12 +18,9 @@ package common import ( "crypto/x509" - "encoding/pem" "errors" "fmt" "io/ioutil" - "os" - "strings" "github.com/sirupsen/logrus" ) @@ -61,34 +58,6 @@ func (source *FileTrustSource) TrustedCertificates() map[string][]*x509.Certific return source.domainCertificates } -func (source *FileTrustSource) updateCertificates(certs []*x509.Certificate, spiffeID, path string) error { - builder := strings.Builder{} - for _, cert := range certs { - block := &pem.Block{ - Type: "CERTIFICATE", - Bytes: cert.Raw, - } - builder.Write(pem.EncodeToMemory(block)) - } - - file, err := appFS.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600) - if err != nil { - return err - } - defer file.Close() - _, err = file.WriteString(builder.String()) - if err != nil { - return err - } - - err = source.loadDomain(spiffeID) - if err != nil { - return err - } - - return nil -} - // For each domain/file mapping found in source.domainPaths, load the PEM and read all // certificates from the file. func (source *FileTrustSource) loadCertificates() error { diff --git a/internal/common/settings.go b/internal/common/settings.go index 0eeb9de..f4c0b4e 100644 --- a/internal/common/settings.go +++ b/internal/common/settings.go @@ -36,7 +36,7 @@ type FileTrustSourceSettings struct { } type SpireTrustSourceSettings struct { - SpireEndpointUrls map[string]string + SpireEndpointURLs map[string]string LocalBackupPath string } @@ -143,14 +143,14 @@ func readSpireSourceOfTrustSettings() (*SpireTrustSourceSettings, error) { return nil, errors.New("trustsource.spire.domains is required but not found") } - viper.SetDefault("trustsource.spire.certLocation", "/var/run/spire/certs/") + viper.SetDefault("trustsource.spire.backupPath", "/var/run/spire/certs/") viper.SetDefault("trustsource.spire.storeEnabled", true) spireSettings := &SpireTrustSourceSettings{ - SpireEndpoints: viper.GetStringMapString("trustsource.spire.domains"), - CertStorePath: viper.GetString("trustsource.spire.certLocation"), + SpireEndpointURLs: viper.GetStringMapString("trustsource.spire.domains"), + LocalBackupPath: viper.GetString("trustsource.spire.backupPath"), } if !viper.GetBool("trustsource.spire.storeEnabled") { - spireSettings.CertStorePath = "" + spireSettings.LocalBackupPath = "" } return spireSettings, nil diff --git a/internal/common/spiretrustsource.go b/internal/common/spiretrustsource.go index d5e85a1..6eff614 100644 --- a/internal/common/spiretrustsource.go +++ b/internal/common/spiretrustsource.go @@ -2,7 +2,7 @@ package common import ( "crypto/x509" - "errors" + "encoding/pem" "fmt" "os" "regexp" @@ -13,6 +13,7 @@ import ( "github.com/spiffe/go-spiffe/workload" ) +// SpireLoadState represents the current state of a Spire connection type SpireLoadState int const ( @@ -22,14 +23,16 @@ const ( Failed ) -// SpireTrustSource holds all necessary information to connect to a spire instance and store its certificates +// SpireEndpoint represents a single trust domain and its associated spire server connection type SpireEndpoint struct { domain string loadState SpireLoadState - spireUrl string + spireURL string client *workload.X509SVIDClient } + +// SpireTrustSource holds all necessary information to connect to a spire instance and store its certificates type SpireTrustSource struct { spireEndpoints map[string]*SpireEndpoint @@ -57,7 +60,7 @@ func (s *SpireTrustSource) TrustedCertificates() map[string][]*x509.Certificate } // NewSpireTrustSource creates a new trust source with connectivity to one or more spire instances. -func NewSpireTrustSource(spireEndpointUrls map[string]string, localBackupDir string) (*SpireTrustSource, error) { +func NewSpireTrustSource(spireEndpointURLs map[string]string, localBackupDir string) (*SpireTrustSource, error) { source := &SpireTrustSource{ spireEndpoints: make(map[string]*SpireEndpoint, 0), localBackupDir: localBackupDir, @@ -66,35 +69,44 @@ func NewSpireTrustSource(spireEndpointUrls map[string]string, localBackupDir str updateTimeout: 5 * time.Second, } - for domain, spireUrl := range spireEndpointUrls { + re := regexp.MustCompile(`^spiffe://(\S+)$`) + for trustDomain, spireURL := range spireEndpointURLs { localStoragePath := "" if localBackupDir != "" { - localStoragePath = "" // TODO: pull out from domain + matches := re.FindStringSubmatch(trustDomain) + if len(matches) < 2 { + return nil, fmt.Errorf("expected domain of form spiffe:// but got %s", trustDomain) + } + domain := matches[1] + if strings.Contains(domain, "/") { + return nil, fmt.Errorf("expected domain without slash but got %s", domain) + } + localStoragePath = localBackupDir + "/" + domain + ".pem" } client, err := workload.NewX509SVIDClient( &workloadWatcher{ - domain: domain, + domain: trustDomain, source: source, localStoragePath: localStoragePath, }, - workload.WithAddr(spireUrl), + workload.WithAddr(spireURL), ) if err != nil { - return nil, fmt.Errorf("failed to construct a new NewX509SVIDClient for %s - %v", spireUrl, err) + return nil, fmt.Errorf("failed to construct a new NewX509SVIDClient for %s: %v", spireURL, err) } - source.spireEndpoints[domain] = &SpireEndpoint{ - domain: domain, + source.spireEndpoints[trustDomain] = &SpireEndpoint{ + domain: trustDomain, loadState: Pending, - spireUrl: spireUrl, + spireURL: spireURL, client: client, } } for _, spireEndpoint := range source.spireEndpoints { if err := spireEndpoint.client.Start(); err != nil { - return nil, fmt.Errorf("failed to start NewX509SVIDClient for domain %s - %v", spireEndpoint.domain, err) + return nil, fmt.Errorf("failed to start NewX509SVIDClient for domain %s: %v", spireEndpoint.domain, err) } } @@ -106,7 +118,7 @@ func (s *SpireTrustSource) Stop() error { errs := make([]string, 0) for _, spireEndpoint := range s.spireEndpoints { if err := spireEndpoint.client.Stop(); err != nil { - errs = append(errs, fmt.Sprintf("domain %s - %v", spireEndpoint.domain, err)) + errs = append(errs, fmt.Sprintf("domain %s: %v", spireEndpoint.domain, err)) } } @@ -118,28 +130,35 @@ func (s *SpireTrustSource) Stop() error { } func (w *workloadWatcher) UpdateX509SVIDs(svids *workload.X509SVIDs) { + certs := svids.Default().TrustBundle + w.source.domainCertificates[w.domain] = certs + w.source.spireEndpoints[w.domain].loadState = Loaded + + if w.localStoragePath != "" { + builder := strings.Builder{} + for _, cert := range certs { + block := &pem.Block{ + Type: "CERTIFICATE", + Bytes: cert.Raw, + } + builder.Write(pem.EncodeToMemory(block)) + } + file, err := appFS.OpenFile(w.localStoragePath, os.O_WRONLY|os.O_CREATE, 0600) + if err != nil { + logrus.Warnf("could not open backup file for trust domain %s at %s: %v", w.domain, w.localStoragePath, err) + } else { + defer file.Close() + _, err = file.WriteString(builder.String()) + if err != nil { + logrus.Warnf("could not write to backup file for trust domain %s at %s: %v", w.domain, w.localStoragePath, err) + } + } + } - // TODO: - // 1. Pull out certs from passed in svids and update w.source.domainCertificates - // 2. Update w.source.spireEndpoints[w.url].loadState = Loaded - // 3. write to w.source.updateChan - // - // if w.localStoragePath != "" - // 4. write certs to storage path from w.source.localBackupPath/[domain without spiffe:// part].pem - - //certs := svids.Default().TrustBundle - //w.source.domainCertificates[w.uri] = certs - //if w.source.certLocation != "" { - // certPath := w.source.fileBacking.domainPaths[w.uri][0] - // err := w.source.fileBacking.updateCertificates(certs, w.uri, certPath) - // if err != nil { - // logrus.Warnf("error writing to cert file: %v", err) - // } - //} - //select { - //case w.source.updateChan <- struct{}{}: - //case <-time.After(w.source.updateTimeout): - //} + select { + case w.source.updateChan <- struct{}{}: + case <-time.After(w.source.updateTimeout): + } } func (w *workloadWatcher) OnError(err error) { @@ -150,18 +169,24 @@ func (w *workloadWatcher) OnError(err error) { } if fileTrustSource, err := NewFileTrustSource(domainPaths); err != nil { - // TODO: log error + logrus.Warnf("could not load certs for domain %s from disk: %v", w.domain, err) w.source.spireEndpoints[w.domain].loadState = Failed } else { - // TODO: - // 1. Pull out certs from fileTrustSource and update w.source.domainCertificates - // 2. Update w.source.spireEndpoints[w.url].loadState = Loaded + w.source.domainCertificates[w.domain] = fileTrustSource.TrustedCertificates()[w.domain] + w.source.spireEndpoints[w.domain].loadState = LoadedFromBackup + logrus.Infof("loaded certs for domain %s from disk", w.domain) } } else { - // TODO: log error w.source.spireEndpoints[w.domain].loadState = Failed + logrus.Warnf("could not connect to spire server for domain %s and local storage disabled", w.domain) } + } else { + // if the state was already Loaded, LoadedFromBackup, or Failed then don't do anything + w.source.spireEndpoints[w.domain].loadState = Failed } - // if the state was already Loaded, LoadedFromBackup, or Failed then don't do anything + select { + case w.source.updateChan <- struct{}{}: + case <-time.After(w.source.updateTimeout): + } } diff --git a/internal/common/spiretrustsource_test.go b/internal/common/spiretrustsource_test.go index a9e9638..84b3a2f 100644 --- a/internal/common/spiretrustsource_test.go +++ b/internal/common/spiretrustsource_test.go @@ -44,6 +44,8 @@ func TestInitalLoad(t *testing.T) { require.NoError(t, err) defer source.Stop() + source.waitForUpdate(t) + certs := source.TrustedCertificates()["spiffe://example.org"] require.Len(t, certs, 1) assert.Equal(t, "US", certs[0].Subject.Country[0]) @@ -90,6 +92,7 @@ func TestWriteCerts(t *testing.T) { newSource, err := NewSpireTrustSource(map[string]string{ "spiffe://example.org": dummyWorkloadAPI.Addr(), }, "certs/") + newSource.waitForUpdate(t) assert.Equal(t, ca.Roots(), newSource.TrustedCertificates()["spiffe://example.org"]) } From b558ea28793e4c82ed7040cee40a9b39561d369a Mon Sep 17 00:00:00 2001 From: Peyton Walters Date: Wed, 15 Jan 2020 11:45:41 -0500 Subject: [PATCH 13/21] Bugfix, add spire testing flag Signed-off-by: Peyton Walters --- internal/common/spiretrustsource.go | 35 ++++++++++++++++-------- internal/common/spiretrustsource_test.go | 18 ++++++------ 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/internal/common/spiretrustsource.go b/internal/common/spiretrustsource.go index 6eff614..e79902a 100644 --- a/internal/common/spiretrustsource.go +++ b/internal/common/spiretrustsource.go @@ -5,6 +5,7 @@ import ( "encoding/pem" "fmt" "os" + "path/filepath" "regexp" "strings" "time" @@ -42,10 +43,7 @@ type SpireTrustSource struct { updateTimeout time.Duration domainCertificates map[string][]*x509.Certificate -} - -type certMap struct { - Certs map[string][]string `json:"certs"` + testing bool } type workloadWatcher struct { @@ -81,7 +79,7 @@ func NewSpireTrustSource(spireEndpointURLs map[string]string, localBackupDir str if strings.Contains(domain, "/") { return nil, fmt.Errorf("expected domain without slash but got %s", domain) } - localStoragePath = localBackupDir + "/" + domain + ".pem" + localStoragePath = filepath.Join(localBackupDir, domain+".pem") } client, err := workload.NewX509SVIDClient( @@ -113,6 +111,16 @@ func NewSpireTrustSource(spireEndpointURLs map[string]string, localBackupDir str return source, nil } +// NewSpireTestSource creates a new spire trust source with the test flag on. +func NewSpireTestSource(spireEndpointURLs map[string]string, localBackupDir string) (*SpireTrustSource, error) { + ts, err := NewSpireTrustSource(spireEndpointURLs, localBackupDir) + if err != nil { + return nil, err + } + ts.testing = true + return ts, nil +} + // Stop stops all spire clients func (s *SpireTrustSource) Stop() error { errs := make([]string, 0) @@ -155,9 +163,11 @@ func (w *workloadWatcher) UpdateX509SVIDs(svids *workload.X509SVIDs) { } } - select { - case w.source.updateChan <- struct{}{}: - case <-time.After(w.source.updateTimeout): + if w.source.testing { + select { + case w.source.updateChan <- struct{}{}: + case <-time.After(w.source.updateTimeout): + } } } @@ -182,11 +192,12 @@ func (w *workloadWatcher) OnError(err error) { } } else { // if the state was already Loaded, LoadedFromBackup, or Failed then don't do anything - w.source.spireEndpoints[w.domain].loadState = Failed } - select { - case w.source.updateChan <- struct{}{}: - case <-time.After(w.source.updateTimeout): + if w.source.testing { + select { + case w.source.updateChan <- struct{}{}: + case <-time.After(w.source.updateTimeout): + } } } diff --git a/internal/common/spiretrustsource_test.go b/internal/common/spiretrustsource_test.go index 84b3a2f..1687fa8 100644 --- a/internal/common/spiretrustsource_test.go +++ b/internal/common/spiretrustsource_test.go @@ -30,7 +30,7 @@ func setX509SVIDResponse(api *spiffetest.WorkloadAPI, ca *spiffetest.CA, svid [] api.SetX509SVIDResponse(response) } -func TestInitalLoad(t *testing.T) { +func TestInitialLoad(t *testing.T) { appFS = afero.NewMemMapFs() afero.WriteFile(appFS, "certs/example.org.pem", []byte(leafCert), 600) @@ -38,7 +38,7 @@ func TestInitalLoad(t *testing.T) { workloadAPI := spiffetest.NewWorkloadAPI(t, nil) defer workloadAPI.Stop() - source, err := NewSpireTrustSource(map[string]string{ + source, err := NewSpireTestSource(map[string]string{ "spiffe://example.org": workloadAPI.Addr(), }, "certs/") require.NoError(t, err) @@ -54,14 +54,14 @@ func TestInitalLoad(t *testing.T) { } func TestInvalidURI(t *testing.T) { - _, err := NewSpireTrustSource(map[string]string{ + _, err := NewSpireTestSource(map[string]string{ "spirffe://example.org": "", }, "certs/") require.Error(t, err) } func TestInvalidDomain(t *testing.T) { - _, err := NewSpireTrustSource(map[string]string{ + _, err := NewSpireTestSource(map[string]string{ "spiffe://example.org/test": "", }, "certs/") require.Error(t, err) @@ -78,7 +78,7 @@ func TestWriteCerts(t *testing.T) { setX509SVIDResponse(workloadAPI, ca, svidFoo, keyFoo) - source, err := NewSpireTrustSource(map[string]string{ + source, err := NewSpireTestSource(map[string]string{ "spiffe://example.org": workloadAPI.Addr(), }, "certs/") require.NoError(t, err) @@ -89,7 +89,7 @@ func TestWriteCerts(t *testing.T) { dummyWorkloadAPI := spiffetest.NewWorkloadAPI(t, nil) defer dummyWorkloadAPI.Stop() - newSource, err := NewSpireTrustSource(map[string]string{ + newSource, err := NewSpireTestSource(map[string]string{ "spiffe://example.org": dummyWorkloadAPI.Addr(), }, "certs/") newSource.waitForUpdate(t) @@ -109,7 +109,7 @@ func TestSpireOverwrite(t *testing.T) { setX509SVIDResponse(workloadAPI, ca, svidFoo, keyFoo) - source, err := NewSpireTrustSource(map[string]string{ + source, err := NewSpireTestSource(map[string]string{ "spiffe://example.org": workloadAPI.Addr(), }, "certs/") require.NoError(t, err) @@ -119,7 +119,7 @@ func TestSpireOverwrite(t *testing.T) { assert.Equal(t, ca.Roots(), source.TrustedCertificates()["spiffe://example.org"]) } -func TestSpireReload(t *testing.T) { +func TestSpireRotation(t *testing.T) { appFS = afero.NewMemMapFs() workloadAPI := spiffetest.NewWorkloadAPI(t, nil) @@ -129,7 +129,7 @@ func TestSpireReload(t *testing.T) { svidFoo, keyFoo := ca.CreateX509SVID("spiffe://example.org/foo") setX509SVIDResponse(workloadAPI, ca, svidFoo, keyFoo) - source, err := NewSpireTrustSource(map[string]string{ + source, err := NewSpireTestSource(map[string]string{ "spiffe://example.org": workloadAPI.Addr(), }, "") require.NoError(t, err) From 9dbb3acc36b07f077cb664862ba3e6ddaef97925 Mon Sep 17 00:00:00 2001 From: Peyton Walters Date: Thu, 2 Jan 2020 22:06:07 -0500 Subject: [PATCH 14/21] Add filetrustsource tests Signed-off-by: Peyton Walters --- internal/common/filetrustsource.go | 22 ++++ internal/common/filetrustsource_test.go | 137 ++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 internal/common/filetrustsource_test.go diff --git a/internal/common/filetrustsource.go b/internal/common/filetrustsource.go index b5498fd..14fee0b 100644 --- a/internal/common/filetrustsource.go +++ b/internal/common/filetrustsource.go @@ -61,10 +61,32 @@ 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 { +<<<<<<< HEAD for domain := range source.domainPaths { err := source.loadDomain(domain) if err != nil { return err +======= + for domain, paths := range source.domainPaths { + domainCertificates := make([]*x509.Certificate, 0) + + 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()) + } + + certificates := ExtractCertificatesFromPem(data) + if len(certificates) == 0 || certificates == nil { + logrus.Info("Did not load any certificates for domain " + domain + " from file " + path) + } + domainCertificates = append(domainCertificates, certificates...) +>>>>>>> Add filetrustsource tests } } diff --git a/internal/common/filetrustsource_test.go b/internal/common/filetrustsource_test.go new file mode 100644 index 0000000..cc9dbde --- /dev/null +++ b/internal/common/filetrustsource_test.go @@ -0,0 +1,137 @@ +package common + +import ( + "testing" + + "github.com/spf13/afero" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +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-----` + certChain = ` +-----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-----` + leafCert +) + +func TestSingleCertificate(t *testing.T) { + appFS = afero.NewMemMapFs() + + afero.WriteFile(appFS, "leaf.pem", []byte(leafCert), 600) + + source, err := NewFileTrustSource(map[string][]string{ + "spiffe://example.org": []string{"leaf.pem"}, + }) + require.NoError(t, err) + + exampleDomain := source.TrustedCertificates()["spiffe://example.org"] + require.Len(t, exampleDomain, 1) + + assert.Equal(t, "US", exampleDomain[0].Subject.Country[0]) + assert.Equal(t, "test1.acme.com", exampleDomain[0].Subject.Organization[0]) + assert.Equal(t, "blog", exampleDomain[0].Subject.CommonName) +} + +func TestDoubleCertificate(t *testing.T) { + appFS = afero.NewMemMapFs() + + afero.WriteFile(appFS, "chain.pem", []byte(certChain), 600) + + source, err := NewFileTrustSource(map[string][]string{ + "spiffe://example.org": []string{"chain.pem"}, + }) + require.NoError(t, err) + + exampleDomain := source.TrustedCertificates()["spiffe://example.org"] + require.Len(t, exampleDomain, 2) + + assert.Equal(t, "US", exampleDomain[0].Subject.Country[0]) + assert.Equal(t, "test1.acme.com", exampleDomain[0].Subject.Organization[0]) + assert.Equal(t, "IntermediaetCA", exampleDomain[0].Subject.CommonName) + + assert.Equal(t, "US", exampleDomain[1].Subject.Country[0]) + assert.Equal(t, "test1.acme.com", exampleDomain[1].Subject.Organization[0]) + assert.Equal(t, "blog", exampleDomain[1].Subject.CommonName) +} + +func TestBadFile(t *testing.T) { + appFS = afero.NewMemMapFs() + + _, err := NewFileTrustSource(map[string][]string{ + "spiffe://example.org": []string{"chain.pem"}, + }) + require.Error(t, err) +} + +func TestEmptyCerts(t *testing.T) { + appFS = afero.NewMemMapFs() + + afero.WriteFile(appFS, "chain.pem", []byte(""), 600) + + source, err := NewFileTrustSource(map[string][]string{ + "spiffe://example.org": []string{"chain.pem"}, + }) + require.NoError(t, err) + + exampleDomain := source.TrustedCertificates()["spiffe://example.org"] + require.Len(t, exampleDomain, 0) +} From 36eaf04a4517f0aee06ca16cbc5cfb07d490646a Mon Sep 17 00:00:00 2001 From: Peyton Walters Date: Thu, 2 Jan 2020 22:06:19 -0500 Subject: [PATCH 15/21] Clean up with go-lint Signed-off-by: Peyton Walters --- cmd/plugin/vault-auth-spire.go | 13 +++++++------ internal/common/logger.go | 1 + internal/common/svidverifier.go | 1 + internal/common/svidverifier_test.go | 0 internal/common/trustsource.go | 2 ++ 5 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 internal/common/svidverifier_test.go diff --git a/cmd/plugin/vault-auth-spire.go b/cmd/plugin/vault-auth-spire.go index a258283..4b4d508 100644 --- a/cmd/plugin/vault-auth-spire.go +++ b/cmd/plugin/vault-auth-spire.go @@ -142,11 +142,12 @@ func parseSettings() (*common.Settings, error) { settingsFlags.StringVar(&settingsFilePath, "settings-file", "", "Path to plugin settings") settingsFlags.Parse(os.Args[1:]) - if settings, err := common.ReadSettings(settingsFilePath); err != nil { + settings, err := common.ReadSettings(settingsFilePath) + if err != nil { return nil, errors.New("vault-auth-spire: Failed to read settings from '" + settingsFilePath + "' - " + err.Error()) - } else { - return settings, nil } + + return settings, nil } // spirePlugin is-a framework.Backend as per the embedded unnamed anon field @@ -166,7 +167,7 @@ func (spirePlugin *spirePlugin) pathAuthLogin(_ context.Context, req *logical.Re return nil, logical.ErrInvalidRequest } - spiffeId, err := spirePlugin.verifier.VerifyAndExtractSpiffeId(svid) + spiffeID, err := spirePlugin.verifier.VerifyAndExtractSpiffeId(svid) if err != nil { logrus.Debug("Provided svid could not be verified - " + err.Error()) return nil, logical.ErrPermissionDenied @@ -182,10 +183,10 @@ func (spirePlugin *spirePlugin) pathAuthLogin(_ context.Context, req *logical.Re }, Policies: []string{ //"Trust Bundles: " + strconv.Itoa(len(b.svidWatcher.TrustBundle)), - "Result: We've been verified and I found SPIFFE ID: " + spiffeId, + "Result: We've been verified and I found SPIFFE ID: " + spiffeID, }, Metadata: map[string]string{ - "spiffeId": spiffeId, + "spiffeId": spiffeID, }, LeaseOptions: logical.LeaseOptions{ Renewable: false, diff --git a/internal/common/logger.go b/internal/common/logger.go index 1b4ccae..977eacf 100644 --- a/internal/common/logger.go +++ b/internal/common/logger.go @@ -54,6 +54,7 @@ func (u utcFormatter) Format(e *logrus.Entry) ([]byte, error) { return u.formatter.Format(e) } +// InitializeLogger creates a new logger with the specified settings func InitializeLogger(settings *Settings) error { logDirectory := filepath.Dir(settings.Log.Filename) if err := os.MkdirAll(logDirectory, 0755); err != nil { diff --git a/internal/common/svidverifier.go b/internal/common/svidverifier.go index 76d1aa9..1bc14b2 100644 --- a/internal/common/svidverifier.go +++ b/internal/common/svidverifier.go @@ -31,6 +31,7 @@ type SvidVerifier struct { trustSources []TrustSource } +// NewSvidVerifier creates a new SVID verifier func NewSvidVerifier() SvidVerifier { return SvidVerifier{ trustSources: make([]TrustSource, 0), diff --git a/internal/common/svidverifier_test.go b/internal/common/svidverifier_test.go new file mode 100644 index 0000000..e69de29 diff --git a/internal/common/trustsource.go b/internal/common/trustsource.go index 00cfbb5..98614fd 100644 --- a/internal/common/trustsource.go +++ b/internal/common/trustsource.go @@ -20,6 +20,8 @@ import ( "crypto/x509" ) +// TrustSource is an interface that lets sources of trust return a map of SPIFFE domain +// names to x509 certificates type TrustSource interface { TrustedCertificates() map[string][]*x509.Certificate } From 8fc8f7182a09e7e8e1e68b21b93b08256a9feb3f Mon Sep 17 00:00:00 2001 From: Peyton Walters Date: Thu, 2 Jan 2020 22:35:55 -0500 Subject: [PATCH 16/21] Add SVID verifier tests and fix ID naming Signed-off-by: Peyton Walters --- cmd/plugin/vault-auth-spire.go | 2 +- internal/common/common.go | 66 ++++++++++++++++++ internal/common/filetrustsource_test.go | 65 ----------------- internal/common/svidverifier.go | 12 ++-- internal/common/svidverifier_test.go | 93 +++++++++++++++++++++++++ 5 files changed, 166 insertions(+), 72 deletions(-) diff --git a/cmd/plugin/vault-auth-spire.go b/cmd/plugin/vault-auth-spire.go index 4b4d508..3642eac 100644 --- a/cmd/plugin/vault-auth-spire.go +++ b/cmd/plugin/vault-auth-spire.go @@ -167,7 +167,7 @@ func (spirePlugin *spirePlugin) pathAuthLogin(_ context.Context, req *logical.Re return nil, logical.ErrInvalidRequest } - spiffeID, err := spirePlugin.verifier.VerifyAndExtractSpiffeId(svid) + spiffeID, err := spirePlugin.verifier.VerifyAndExtractSpiffeID(svid) if err != nil { logrus.Debug("Provided svid could not be verified - " + err.Error()) return nil, logical.ErrPermissionDenied diff --git a/internal/common/common.go b/internal/common/common.go index 3dc4e59..8cf3c43 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -3,3 +3,69 @@ package common import "github.com/spf13/afero" var appFS = afero.NewOsFs() + +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 +) diff --git a/internal/common/filetrustsource_test.go b/internal/common/filetrustsource_test.go index cc9dbde..83b940d 100644 --- a/internal/common/filetrustsource_test.go +++ b/internal/common/filetrustsource_test.go @@ -8,71 +8,6 @@ import ( "github.com/stretchr/testify/require" ) -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-----` - certChain = ` ------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-----` + leafCert -) - func TestSingleCertificate(t *testing.T) { appFS = afero.NewMemMapFs() diff --git a/internal/common/svidverifier.go b/internal/common/svidverifier.go index 1bc14b2..ea4bd14 100644 --- a/internal/common/svidverifier.go +++ b/internal/common/svidverifier.go @@ -43,26 +43,26 @@ func (verifier *SvidVerifier) AddTrustSource(source TrustSource) { verifier.trustSources = append(verifier.trustSources, source) } -// VerifyAndExtractSpiffeId will take the provided SVID and verify its source against any of the known +// VerifyAndExtractSpiffeID will take the provided SVID and verify its source against any of the known // sources of trust. If the SVID was generated using any of the known sources of trust and adheres to // all required SPIFFE requirements for an SVID then the SVID will be considered verified and the // SPIFFE ID of the SVID will be returned. If the SVID cannot be verified then an error will // be returned. -func (verifier *SvidVerifier) VerifyAndExtractSpiffeId(svid string) (string, error) { +func (verifier *SvidVerifier) VerifyAndExtractSpiffeID(svid string) (string, error) { logrus.Debug("Beginning SVID verification") // right now we only support X509 verification - spiffeId, err := verifier.verifyAndExtractSpiffeIdFromX509(svid) + spiffeID, err := verifier.verifyAndExtractSpiffeIDFromX509(svid) if err != nil { return "", err } // SPIFFE validation requirements - if err := spiffe.ValidateID(spiffeId, spiffe.AllowAnyTrustDomainWorkload()); err != nil { + if err := spiffe.ValidateID(spiffeID, spiffe.AllowAnyTrustDomainWorkload()); err != nil { return "", errors.New("SVID is invalid - invalid SPIFFE ID found - " + err.Error()) } - return spiffeId, nil + return spiffeID, nil } // VerifyAndExtractSpiffeIdFromX509 will take the provided X509-SVID and verify its source against any @@ -72,7 +72,7 @@ func (verifier *SvidVerifier) VerifyAndExtractSpiffeId(svid string) (string, err // be returned. The SPIFFE ID of an X509-SVID is defined as the first SAN URI found in the first certificate // of the SVID. That is, if it contains multiple certificates then the first is the one containing the // 'caller' SPIFFE ID. -func (verifier *SvidVerifier) verifyAndExtractSpiffeIdFromX509(svid string) (string, error) { +func (verifier *SvidVerifier) verifyAndExtractSpiffeIDFromX509(svid string) (string, error) { logrus.Debug("Beginning SVID X509 verification") logrus.Debug("Extracting certificates from provided SVID") diff --git a/internal/common/svidverifier_test.go b/internal/common/svidverifier_test.go index e69de29..78bd4b8 100644 --- a/internal/common/svidverifier_test.go +++ b/internal/common/svidverifier_test.go @@ -0,0 +1,93 @@ +package common + +import ( + "crypto/x509" + "encoding/pem" + "testing" + + "github.com/spf13/afero" + "github.com/spiffe/go-spiffe/spiffetest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func certToFile(cert *x509.Certificate, fileName string) { + block := &pem.Block{ + Type: "CERTIFICATE", + Bytes: cert.Raw, + } + afero.WriteFile(appFS, fileName, pem.EncodeToMemory(block), 600) +} + +func TestValid(t *testing.T) { + appFS = afero.NewMemMapFs() + + ca := spiffetest.NewCA(t) + certToFile(ca.Roots()[0], "ca.pem") + svidFoo, _ := ca.CreateX509SVID("spiffe://example.org/foo") + + source, err := NewFileTrustSource(map[string][]string{ + "spiffe://example.org": []string{"ca.pem"}, + }) + require.NoError(t, err) + + verif := NewSvidVerifier() + verif.AddTrustSource(source) + + svidBlock := &pem.Block{ + Type: "CERTIFICATE", + Bytes: svidFoo[0].Raw, + } + + id, err := verif.VerifyAndExtractSpiffeId(string(pem.EncodeToMemory(svidBlock))) + assert.Equal(t, "spiffe://example.org/foo", id) +} + +func TestDifferentDomain(t *testing.T) { + appFS = afero.NewMemMapFs() + + ca := spiffetest.NewCA(t) + certToFile(ca.Roots()[0], "ca.pem") + svidFoo, _ := ca.CreateX509SVID("spiffe://wrong.org/foo") + + source, err := NewFileTrustSource(map[string][]string{ + "spiffe://example.org": []string{"ca.pem"}, + }) + require.NoError(t, err) + + verif := NewSvidVerifier() + verif.AddTrustSource(source) + + svidBlock := &pem.Block{ + Type: "CERTIFICATE", + Bytes: svidFoo[0].Raw, + } + + _, err = verif.VerifyAndExtractSpiffeId(string(pem.EncodeToMemory(svidBlock))) + assert.Error(t, err) +} + +func TestBadCA(t *testing.T) { + appFS = afero.NewMemMapFs() + + ca := spiffetest.NewCA(t) + certToFile(ca.Roots()[0], "ca.pem") + ca2 := spiffetest.NewCA(t) + svidFoo, _ := ca2.CreateX509SVID("spiffe://example.org/foo") + + source, err := NewFileTrustSource(map[string][]string{ + "spiffe://example.org": []string{"ca.pem"}, + }) + require.NoError(t, err) + + verif := NewSvidVerifier() + verif.AddTrustSource(source) + + svidBlock := &pem.Block{ + Type: "CERTIFICATE", + Bytes: svidFoo[0].Raw, + } + + _, err = verif.VerifyAndExtractSpiffeId(string(pem.EncodeToMemory(svidBlock))) + assert.Error(t, err) +} From 4c7ff55ea397ac4cf3f179b7bc0f6d72a6ce3e71 Mon Sep 17 00:00:00 2001 From: Peyton Walters Date: Mon, 6 Jan 2020 10:38:45 -0500 Subject: [PATCH 17/21] Move out common_test Signed-off-by: Peyton Walters --- internal/common/common.go | 66 --------------------------------------- 1 file changed, 66 deletions(-) diff --git a/internal/common/common.go b/internal/common/common.go index 8cf3c43..3dc4e59 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -3,69 +3,3 @@ package common import "github.com/spf13/afero" var appFS = afero.NewOsFs() - -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 -) From 9d1df6d8a21ca2ddcd086ef6685d178065ccd852 Mon Sep 17 00:00:00 2001 From: Peyton Walters Date: Tue, 7 Jan 2020 15:29:35 -0500 Subject: [PATCH 18/21] Fix even golint errors Signed-off-by: Peyton Walters --- internal/common/filetrustsource.go | 22 ---------------------- internal/common/settings.go | 6 ++++++ internal/common/svidverifier_test.go | 6 +++--- 3 files changed, 9 insertions(+), 25 deletions(-) diff --git a/internal/common/filetrustsource.go b/internal/common/filetrustsource.go index 14fee0b..b5498fd 100644 --- a/internal/common/filetrustsource.go +++ b/internal/common/filetrustsource.go @@ -61,32 +61,10 @@ 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 { -<<<<<<< HEAD for domain := range source.domainPaths { err := source.loadDomain(domain) if err != nil { return err -======= - for domain, paths := range source.domainPaths { - domainCertificates := make([]*x509.Certificate, 0) - - 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()) - } - - certificates := ExtractCertificatesFromPem(data) - if len(certificates) == 0 || certificates == nil { - logrus.Info("Did not load any certificates for domain " + domain + " from file " + path) - } - domainCertificates = append(domainCertificates, certificates...) ->>>>>>> Add filetrustsource tests } } diff --git a/internal/common/settings.go b/internal/common/settings.go index f4c0b4e..1e857cf 100644 --- a/internal/common/settings.go +++ b/internal/common/settings.go @@ -21,25 +21,30 @@ import ( "github.com/spf13/viper" ) +// Settings is the root set of settings for root plugin type Settings struct { Log *LogSettings SourceOfTrust *SourceOfTrustSettings } +// SourceOfTrustSettings holds settings for all source of truth providers type SourceOfTrustSettings struct { File *FileTrustSourceSettings Spire *SpireTrustSourceSettings } +// FileTrustSourceSettings holds a mapping of domains to certificate paths type FileTrustSourceSettings struct { Domains map[string][]string } +// SpireTrustSourceSettings holds spire endpoint locations and a path to store certs from Spire in type SpireTrustSourceSettings struct { SpireEndpointURLs map[string]string LocalBackupPath string } +// LogSettings holds relevant logging settings type LogSettings struct { Filename string Level string @@ -53,6 +58,7 @@ func wrapError(err error) error { return errors.New("parse-settings: " + err.Error()) } +// ReadSettings reads settings from JSON into config objects using Viper func ReadSettings(fromPath string) (*Settings, error) { settings := new(Settings) diff --git a/internal/common/svidverifier_test.go b/internal/common/svidverifier_test.go index 78bd4b8..8b43c09 100644 --- a/internal/common/svidverifier_test.go +++ b/internal/common/svidverifier_test.go @@ -39,7 +39,7 @@ func TestValid(t *testing.T) { Bytes: svidFoo[0].Raw, } - id, err := verif.VerifyAndExtractSpiffeId(string(pem.EncodeToMemory(svidBlock))) + id, err := verif.VerifyAndExtractSpiffeID(string(pem.EncodeToMemory(svidBlock))) assert.Equal(t, "spiffe://example.org/foo", id) } @@ -63,7 +63,7 @@ func TestDifferentDomain(t *testing.T) { Bytes: svidFoo[0].Raw, } - _, err = verif.VerifyAndExtractSpiffeId(string(pem.EncodeToMemory(svidBlock))) + _, err = verif.VerifyAndExtractSpiffeID(string(pem.EncodeToMemory(svidBlock))) assert.Error(t, err) } @@ -88,6 +88,6 @@ func TestBadCA(t *testing.T) { Bytes: svidFoo[0].Raw, } - _, err = verif.VerifyAndExtractSpiffeId(string(pem.EncodeToMemory(svidBlock))) + _, err = verif.VerifyAndExtractSpiffeID(string(pem.EncodeToMemory(svidBlock))) assert.Error(t, err) } From 80e301f3b3714ce5d9c919c90e79cae9e0f86540 Mon Sep 17 00:00:00 2001 From: Peyton Walters Date: Tue, 7 Jan 2020 15:31:20 -0500 Subject: [PATCH 19/21] Add lint instruction to makefile Signed-off-by: Peyton Walters --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 3fe899a..8cd05a2 100644 --- a/Makefile +++ b/Makefile @@ -6,5 +6,8 @@ build: clean cmd/plugin/vault-auth-spire.go test: go test ./... +lint: + golint ./... + clean: @rm -f vault-auth-spire From 4b901b9d9b6def747d6772da2c88538e232c55d8 Mon Sep 17 00:00:00 2001 From: Peyton Walters Date: Thu, 9 Jan 2020 11:12:43 -0500 Subject: [PATCH 20/21] Fix logrus fields & misc Signed-off-by: Peyton Walters --- internal/common/common_test.go | 2 +- internal/common/filetrustsource.go | 5 ++++- internal/common/filetrustsource_test.go | 14 ++++++++------ internal/common/spiretrustsource.go | 22 +++++++++++++++++----- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/internal/common/common_test.go b/internal/common/common_test.go index 69675ff..2326731 100644 --- a/internal/common/common_test.go +++ b/internal/common/common_test.go @@ -1,6 +1,6 @@ package common -var ( +const ( leafCert = ` -----BEGIN CERTIFICATE----- MIIE1DCCArygAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwPzELMAkGA1UEBhMCVVMx diff --git a/internal/common/filetrustsource.go b/internal/common/filetrustsource.go index b5498fd..745c65b 100644 --- a/internal/common/filetrustsource.go +++ b/internal/common/filetrustsource.go @@ -88,7 +88,10 @@ func (source *FileTrustSource) loadDomain(domain string) error { certificates := ExtractCertificatesFromPem(data) if len(certificates) == 0 { - logrus.Info("Did not load any certificates for domain " + domain + " from file " + path) + logrus.WithFields(logrus.Fields{ + "domain": domain, + "path": path, + }).Info("Did not load any certificates from file") } domainCertificates = append(domainCertificates, certificates...) } diff --git a/internal/common/filetrustsource_test.go b/internal/common/filetrustsource_test.go index 83b940d..fafb114 100644 --- a/internal/common/filetrustsource_test.go +++ b/internal/common/filetrustsource_test.go @@ -39,13 +39,15 @@ func TestDoubleCertificate(t *testing.T) { exampleDomain := source.TrustedCertificates()["spiffe://example.org"] require.Len(t, exampleDomain, 2) - assert.Equal(t, "US", exampleDomain[0].Subject.Country[0]) - assert.Equal(t, "test1.acme.com", exampleDomain[0].Subject.Organization[0]) - assert.Equal(t, "IntermediaetCA", exampleDomain[0].Subject.CommonName) + assert := assert.New(t) + + assert.Equal("US", exampleDomain[0].Subject.Country[0]) + assert.Equal("test1.acme.com", exampleDomain[0].Subject.Organization[0]) + assert.Equal("IntermediaetCA", exampleDomain[0].Subject.CommonName) - assert.Equal(t, "US", exampleDomain[1].Subject.Country[0]) - assert.Equal(t, "test1.acme.com", exampleDomain[1].Subject.Organization[0]) - assert.Equal(t, "blog", exampleDomain[1].Subject.CommonName) + assert.Equal("US", exampleDomain[1].Subject.Country[0]) + assert.Equal("test1.acme.com", exampleDomain[1].Subject.Organization[0]) + assert.Equal("blog", exampleDomain[1].Subject.CommonName) } func TestBadFile(t *testing.T) { diff --git a/internal/common/spiretrustsource.go b/internal/common/spiretrustsource.go index e79902a..8a1ac31 100644 --- a/internal/common/spiretrustsource.go +++ b/internal/common/spiretrustsource.go @@ -153,12 +153,18 @@ func (w *workloadWatcher) UpdateX509SVIDs(svids *workload.X509SVIDs) { } file, err := appFS.OpenFile(w.localStoragePath, os.O_WRONLY|os.O_CREATE, 0600) if err != nil { - logrus.Warnf("could not open backup file for trust domain %s at %s: %v", w.domain, w.localStoragePath, err) + logrus.WithFields(logrus.Fields{ + "domain": w.domain, + "path": w.localStoragePath, + }).Warnf("could not open backup file for trust domain: %v", err) } else { defer file.Close() _, err = file.WriteString(builder.String()) if err != nil { - logrus.Warnf("could not write to backup file for trust domain %s at %s: %v", w.domain, w.localStoragePath, err) + logrus.WithFields(logrus.Fields{ + "domain": w.domain, + "path": w.localStoragePath, + }).Warnf("could not write to backup file for trust domain: %v", err) } } } @@ -179,16 +185,22 @@ func (w *workloadWatcher) OnError(err error) { } if fileTrustSource, err := NewFileTrustSource(domainPaths); err != nil { - logrus.Warnf("could not load certs for domain %s from disk: %v", w.domain, err) + logrus.WithFields(logrus.Fields{ + "domain": w.domain, + }).Warnf("could not load backup certs for from disk: %v", err) w.source.spireEndpoints[w.domain].loadState = Failed } else { w.source.domainCertificates[w.domain] = fileTrustSource.TrustedCertificates()[w.domain] w.source.spireEndpoints[w.domain].loadState = LoadedFromBackup - logrus.Infof("loaded certs for domain %s from disk", w.domain) + logrus.WithFields(logrus.Fields{ + "domain": w.domain, + }).Infof("loaded backup certs from disk") } } else { w.source.spireEndpoints[w.domain].loadState = Failed - logrus.Warnf("could not connect to spire server for domain %s and local storage disabled", w.domain) + logrus.WithFields(logrus.Fields{ + "domain": w.domain, + }).Warn("could not connect to spire server and local storage disabled") } } else { // if the state was already Loaded, LoadedFromBackup, or Failed then don't do anything From 1ceae6b66164dcf257075a50f52d62b139d69921 Mon Sep 17 00:00:00 2001 From: Peyton Walters Date: Thu, 9 Jan 2020 11:30:59 -0500 Subject: [PATCH 21/21] Change errors a bit Signed-off-by: Peyton Walters --- cmd/plugin/vault-auth-spire.go | 4 +++- internal/common/settings.go | 10 +++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/cmd/plugin/vault-auth-spire.go b/cmd/plugin/vault-auth-spire.go index 3642eac..923c033 100644 --- a/cmd/plugin/vault-auth-spire.go +++ b/cmd/plugin/vault-auth-spire.go @@ -20,6 +20,8 @@ import ( "context" "errors" "flag" + "fmt" + "github.com/bloomberg/vault-auth-spire/internal/common" "github.com/hashicorp/vault/sdk/framework" "github.com/hashicorp/vault/sdk/logical" @@ -144,7 +146,7 @@ func parseSettings() (*common.Settings, error) { settings, err := common.ReadSettings(settingsFilePath) if err != nil { - return nil, errors.New("vault-auth-spire: Failed to read settings from '" + settingsFilePath + "' - " + err.Error()) + return nil, fmt.Errorf("vault-auth-spire: Failed to read settings from %s: %v", settingsFilePath, err) } return settings, nil diff --git a/internal/common/settings.go b/internal/common/settings.go index 1e857cf..75e703a 100644 --- a/internal/common/settings.go +++ b/internal/common/settings.go @@ -54,10 +54,6 @@ type LogSettings struct { Compress bool } -func wrapError(err error) error { - return errors.New("parse-settings: " + err.Error()) -} - // ReadSettings reads settings from JSON into config objects using Viper func ReadSettings(fromPath string) (*Settings, error) { settings := new(Settings) @@ -68,17 +64,17 @@ func ReadSettings(fromPath string) (*Settings, error) { // Load the config from disk at sourcePath viper.SetConfigFile(fromPath) if err = viper.ReadInConfig(); err != nil { - return nil, wrapError(err) + return nil, err } // Read logging settings if settings.Log, err = readLogSettings(); err != nil { - return nil, wrapError(err) + return nil, err } // Read our source of trust settings if settings.SourceOfTrust, err = readSourceOfTrustSettings(); err != nil { - return nil, wrapError(err) + return nil, err } return settings, nil