diff --git a/go.mod b/go.mod index 5676d203..60c6fcfb 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.12 require ( github.com/Venafi/vcert v0.0.0-20200514165030-4f70baad986f github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf + github.com/fatih/color v1.9.0 // indirect github.com/fatih/structs v1.1.0 github.com/go-test/deep v1.0.2 github.com/hashicorp/consul/api v1.3.0 // indirect @@ -12,13 +13,18 @@ require ( github.com/hashicorp/vault v1.3.2 github.com/hashicorp/vault/api v1.0.5-0.20200117231345-460d63e36490 github.com/hashicorp/vault/sdk v0.1.14-0.20200121232954-73f411823aa0 + github.com/mattn/go-colorable v0.1.6 // indirect github.com/michaelklishin/rabbit-hole v1.5.0 // indirect github.com/mitchellh/mapstructure v1.1.2 github.com/onsi/ginkgo v1.10.1 // indirect github.com/onsi/gomega v1.7.0 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/ryanuber/go-glob v1.0.0 + github.com/spf13/pflag v1.0.3 // indirect github.com/stretchr/testify v1.4.0 // indirect golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect + golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9 // indirect + gotest.tools/gotestsum v0.5.3 // indirect ) diff --git a/go.sum b/go.sum index 600d8351..a8d7aa87 100644 --- a/go.sum +++ b/go.sum @@ -138,6 +138,8 @@ github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkg github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= @@ -223,6 +225,8 @@ github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeq github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= 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/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU= @@ -436,10 +440,16 @@ github.com/martini-contrib/render v0.0.0-20150707142108-ec18f8345a11/go.mod h1:A github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-shellwords v1.0.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/matttproud/golang_protobuf_extensions v1.0.0/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -532,6 +542,8 @@ github.com/pierrec/lz4 v2.2.6+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -597,6 +609,8 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94 h1:0ngsPmuP6XIjiFRNFYlvKwSr5zff2v+uPHaffZ6/M4k= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -737,6 +751,11 @@ golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190730183949-1393eb018365/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9 h1:YTzHMGlqJu67/uEo1lBv0n3wBXhXNeUbB1XfN2vmTm0= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= @@ -760,6 +779,8 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190718200317-82a3ea8a504c h1:/w2shrE71LqqE7724PI+5iMYc5YbPnUaRosCQncZ/Nk= golang.org/x/tools v0.0.0-20190718200317-82a3ea8a504c/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= @@ -833,8 +854,14 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v1.4.0 h1:BjtEgfuw8Qyd+jPvQz8CfoxiO/UjFEidWinwEXZiWv0= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/gotestsum v0.5.2 h1:sSKWtEFqorHhuBCHU6MeUl50cq9U2J3d1m5NlQTVrbY= +gotest.tools/gotestsum v0.5.2/go.mod h1:hC9TQserDVTWcJuARh76Ydp3ZwuE+pIIWpt2BzDLD6M= +gotest.tools/gotestsum v0.5.3 h1:9SkwB5ou8GYUqSgbCCYCMk9BVXueDmzwk5WjaZeZW2o= +gotest.tools/gotestsum v0.5.3/go.mod h1:hC9TQserDVTWcJuARh76Ydp3ZwuE+pIIWpt2BzDLD6M= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/plugin/pki/path_venafi_policy.go b/plugin/pki/path_venafi_policy.go index eb0f61c3..1e68a198 100644 --- a/plugin/pki/path_venafi_policy.go +++ b/plugin/pki/path_venafi_policy.go @@ -10,9 +10,9 @@ import ( "github.com/Venafi/vcert/pkg/certificate" "github.com/Venafi/vcert/pkg/endpoint" "github.com/hashicorp/vault/sdk/framework" - "github.com/hashicorp/vault/sdk/logical" "log" + "regexp" "strings" ) @@ -39,7 +39,22 @@ func pathVenafiPolicy(b *backend) *framework.Path { "tpp_url": { Type: framework.TypeString, - Description: `URL of Venafi Platform. Example: https://tpp.venafi.example/vedsdk`, + Description: `URL of Venafi Platform. Deprecated, use 'url' instead`, + Deprecated: true, + }, + "url": { + Type: framework.TypeString, + Description: `URL of Venafi API endpoint. Example: https://tpp.venafi.example/vedsdk`, + Required: true, + }, + "access_token": { + Type: framework.TypeString, + Description: `Access token for TPP, user should use this for authentication`, + Required: true, + }, + "refresh_token": { + Type: framework.TypeString, + Description: `Refresh token for obtaining a new access token for TPP`, Required: true, }, "zone": { @@ -77,7 +92,8 @@ Example: }, "cloud_url": { Type: framework.TypeString, - Description: `URL for Venafi Cloud. Set it only if you want to use non production Cloud`, + Description: `URL for Venafi Cloud. Set it only if you want to use non production Cloud. Deprecated, use 'url' instead`, + Deprecated: true, }, "ext_key_usage": { Type: framework.TypeCommaStringSlice, @@ -279,10 +295,26 @@ func (b *backend) pathUpdateVenafiPolicy(ctx context.Context, req *logical.Reque name := data.Get("name").(string) log.Printf("%s Write policy endpoint configuration into storage", logPrefixVenafiPolicyEnforcement) + + url := data.Get("url").(string) + var tppUrl, cloudUrl string + + if url == "" { + tppUrl = data.Get("tpp_url").(string) + url = tppUrl + } + if url == "" { + cloudUrl = data.Get("cloud_url").(string) + url = cloudUrl + } + venafiPolicyConfig := &venafiPolicyConfigEntry{ venafiConnectionConfig: venafiConnectionConfig{ - TPPURL: data.Get("tpp_url").(string), - CloudURL: data.Get("cloud_url").(string), + TPPURL: tppUrl, + URL: url, + AccessToken: data.Get("access_token").(string), + RefreshToken: data.Get("refresh_token").(string), + CloudURL: cloudUrl, Zone: data.Get("zone").(string), TPPPassword: data.Get("tpp_password").(string), Apikey: data.Get("apikey").(string), @@ -299,9 +331,15 @@ func (b *backend) pathUpdateVenafiPolicy(ctx context.Context, req *logical.Reque if err != nil { return } - if venafiPolicyConfig.Apikey == "" && (venafiPolicyConfig.TPPURL == "" || venafiPolicyConfig.TPPUser == "" || venafiPolicyConfig.TPPPassword == "") { - return logical.ErrorResponse("Invalid mode. apikey or tpp credentials required"), nil + + if venafiPolicyConfig.Apikey == "" && (venafiPolicyConfig.URL == "" || venafiPolicyConfig.TPPUser == "" || venafiPolicyConfig.TPPPassword == "") && (venafiPolicyConfig.URL == "" || venafiPolicyConfig.AccessToken == "") { + return logical.ErrorResponse("Invalid mode. apikey or tpp credentials/token required"), nil + } + + if(venafiPolicyConfig.AccessToken != "" && (venafiPolicyConfig.TPPPassword != "" || venafiPolicyConfig.TPPUser != "")) { + return logical.ErrorResponse("Mixed credentials, access token and user/password are set"), nil } + jsonEntry, err := logical.StorageEntryJSON(venafiPolicyPath+name, venafiPolicyConfig) if err != nil { return nil, err @@ -329,9 +367,11 @@ func (b *backend) pathUpdateVenafiPolicy(ctx context.Context, req *logical.Reque //Send policy to the user output respData := formPolicyRespData(*policyEntry) + warnings := getWarnings(venafiPolicyConfig, name) return &logical.Response{ - Data: respData, + Data: respData, + Warnings: warnings, }, nil } @@ -508,12 +548,54 @@ func (b *backend) getPolicyFromVenafi(ctx context.Context, storage logical.Stora if err != nil { return } - log.Printf("%s Getting policy from Venafi endpoint", logPrefixVenafiPolicyEnforcement) policy, err = cl.ReadPolicyConfiguration() - if err != nil { - return + if (err != nil) && (cl.GetType() == endpoint.ConnectorTypeTPP) { + msg := err.Error() + + //catch the scenario when token is expired and deleted. + var regex = regexp.MustCompile("(Token).*(not found)") + + //validate if the error is related to a expired accces token, at this moment the only way can validate this is using the error message + //and verify if that message describes errors related to expired access token. + if (strings.Contains(msg, "\"error\":\"expired_token\"") && strings.Contains(msg, "\"error_description\":\"Access token expired\"")) || regex.MatchString(msg) { + + cfg, err := b.getConfing(ctx, storage, policyConfig) + + if err != nil { + return nil, err + } + + if cfg.Credentials.RefreshToken != "" { + err = updateAccessToken(cfg, b, ctx, &storage, policyConfig) + + if err != nil { + return nil, err + } + + //everything went fine so get the new client with the new refreshed acces token + cl, err := b.ClientVenafi(ctx, storage, policyConfig) + if err != nil { + return nil, err + } + + b.Logger().Debug("Making certificate request again") + + policy, err = cl.ReadPolicyConfiguration() + if err != nil { + return nil, err + } else { + return policy, nil + } + } else { + err = fmt.Errorf("Tried to get new access token but refresh token is empty") + return nil, err + } + + } else { + return nil, err + } } if policy == nil { err = fmt.Errorf("expected policy but got nil from Venafi endpoint %v", policy) @@ -552,20 +634,40 @@ func (b *backend) pathReadVenafiPolicy(ctx context.Context, req *logical.Request return nil, err } + var tppPass, accessToken, refreshToken, apiKey, strMask string + + strMask = "********" + + if config.TPPPassword != "" { + tppPass = strMask + } + if config.Apikey != "" { + apiKey = strMask + } + if config.AccessToken != "" { + accessToken = strMask + } + if config.RefreshToken != "" { + refreshToken = strMask + } + //Send config to the user output respData := map[string]interface{}{ - "tpp_url": config.TPPURL, + "url": config.URL, "zone": config.Zone, "tpp_user": config.TPPUser, + "tpp_password": tppPass, + "apikey": apiKey, + "access_token": accessToken, + "refresh_token": refreshToken, "trust_bundle_file": config.TrustBundleFile, - "cloud_url": config.CloudURL, policyFieldImportRoles: rolesList.importRoles, policyFieldDefaultsRoles: rolesList.defaultsRoles, policyFieldEnforcementRoles: rolesList.enforceRoles, "auto_refresh_interval": config.AutoRefreshInterval, "last_policy_update_time": config.LastPolicyUpdateTime, - "import_timeout": config.VenafiImportTimeout, - "import_workers": config.VenafiImportWorkers, + "import_timeout": config.VenafiImportTimeout, + "import_workers": config.VenafiImportWorkers, "create_role": config.CreateRole, } @@ -580,6 +682,34 @@ type rolesListForVenafiPolicy struct { defaultsRoles []string } +func getWarnings(entry *venafiPolicyConfigEntry, name string) []string { + + var warnings []string + + if entry.venafiConnectionConfig.TPPURL != "" { + warnings = append(warnings, "tpp_url is deprecated, please use url instead") + } + + if entry.venafiConnectionConfig.CloudURL != "" { + warnings = append(warnings, "cloud_url is deprecated, please use url instead") + } + + if entry.venafiConnectionConfig.TPPUser != "" { + warnings = append(warnings, "tpp_user is deprecated, please use access_token instead") + } + + if entry.venafiConnectionConfig.TPPPassword != "" { + warnings = append(warnings, "tpp_password is deprecated, please use access_token instead") + } + + //Include success message in warnings + if len(warnings) > 0 { + warnings = append(warnings, "Policy: "+name+" saved successfully") + } + + return warnings +} + func (b *backend) getRolesListForVenafiPolicy(ctx context.Context, storage logical.Storage, policyName string) (rolesList rolesListForVenafiPolicy, err error) { //In this function we're getting a role list for Venafi policy. diff --git a/plugin/pki/path_venafi_policy_test.go b/plugin/pki/path_venafi_policy_test.go index ef7169d3..ff28b8e1 100644 --- a/plugin/pki/path_venafi_policy_test.go +++ b/plugin/pki/path_venafi_policy_test.go @@ -19,6 +19,11 @@ func TestVenafiPolicyTPP(t *testing.T) { venafiPolicyTests(t, policyData, domain) } +func TestVenafiPolicyToken(t *testing.T) { + domain, policyData := makeVenafiTokenConfig() + venafiPolicyTests(t, policyData, domain) +} + func TestVenafiPolicyCloudSignBeforeConfigure(t *testing.T) { domain, _ := makeVenafiCloudConfig() venafiPolicyTestSignBeforeConfigure(t, domain) @@ -29,6 +34,11 @@ func TestVenafiPolicyTPPSignBeforeConfigure(t *testing.T) { venafiPolicyTestSignBeforeConfigure(t, domain) } +func TestVenafiPolicyTokenSignBeforeConfigure(t *testing.T) { + domain, _ := makeVenafiTokenConfig() + venafiPolicyTestSignBeforeConfigure(t, domain) +} + func venafiPolicyTestSignBeforeConfigure(t *testing.T, domain string) { b, storage := createBackendWithStorage(t) rootData := map[string]interface{}{ @@ -59,6 +69,11 @@ func TestVenafiPolicyTPPWriteAndReadPolicy(t *testing.T) { venafiPolicyWriteAndReadTest(t, policyData) } +func TestVenafiPolicyTokenWriteAndReadPolicy(t *testing.T) { + _, policyData := makeVenafiTokenConfig() + venafiPolicyWriteAndReadTest(t, policyData) +} + func venafiPolicyWriteAndReadTest(t *testing.T, policyData map[string]interface{}) { // create the backend b, storage := createBackendWithStorage(t) diff --git a/plugin/pki/util.go b/plugin/pki/util.go index b274ed36..8723bd7f 100644 --- a/plugin/pki/util.go +++ b/plugin/pki/util.go @@ -1,14 +1,22 @@ package pki import ( + "context" + "crypto/tls" "crypto/x509" "encoding/asn1" "fmt" + "github.com/Venafi/vcert" "github.com/Venafi/vcert/pkg/certificate" "github.com/Venafi/vcert/pkg/endpoint" + "github.com/Venafi/vcert/pkg/venafi/tpp" + "github.com/hashicorp/vault/sdk/logical" + "net" + "net/http" "regexp" "strconv" "strings" + "time" ) func normalizeSerial(serial string) string { @@ -214,3 +222,132 @@ func checkStringArrByRegexp(ss []string, regexs []string, optional bool) (matche func ecdsaCurvesSizesToName(bitLen int) string { return fmt.Sprintf("P%d", bitLen) } + +func getTppConnector(cfg *vcert.Config) (*tpp.Connector, error) { + + var connectionTrustBundle *x509.CertPool + if cfg.ConnectionTrust != "" { + connectionTrustBundle = x509.NewCertPool() + if !connectionTrustBundle.AppendCertsFromPEM([]byte(cfg.ConnectionTrust)) { + return nil, fmt.Errorf("Failed to parse PEM trust bundle") + } + } + tppConnector, err := tpp.NewConnector(cfg.BaseUrl, "", cfg.LogVerbose, connectionTrustBundle) + if err != nil { + return nil, fmt.Errorf("could not create TPP connector: %s", err) + } + + return tppConnector, nil +} + +func updateAccessToken(cfg *vcert.Config, b *backend, ctx context.Context, storage *logical.Storage, roleName string) error { + tppConnector, _ := getTppConnector(cfg) + + httpClient, err := getHTTPClient(cfg.ConnectionTrust) + if err != nil { + return err + } + + tppConnector.SetHTTPClient(httpClient) + + resp, err := tppConnector.RefreshAccessToken(&endpoint.Authentication{ + RefreshToken: cfg.Credentials.RefreshToken, + ClientId: "hashicorp-vault-monitor-by-venafi", + Scope: "certificate:discover,manage", + }) + if resp.Access_token != "" && resp.Refresh_token != "" { + + err := storeAccessData(b, ctx, storage, roleName, resp) + if err != nil { + return err + } + + } else { + return err + } + return nil +} + +func storeAccessData(b *backend, ctx context.Context, storage *logical.Storage, policyName string, resp tpp.OauthRefreshAccessTokenResponse) error { + policy, err := b.getVenafiPolicyConfig(ctx, (*storage), policyName) + + if err != nil { + return err + } + connConfig := policy.venafiConnectionConfig + + connConfig.RefreshToken = resp.Refresh_token + + connConfig.AccessToken = resp.Access_token + + policy.venafiConnectionConfig = connConfig + + // Store it + jsonEntry, err := logical.StorageEntryJSON(venafiPolicyPath+policyName, policy) + if err != nil { + return err + } + + if err := (*storage).Put(ctx, jsonEntry); err != nil { + return err + } + return nil +} + +func getHTTPClient(trustBundlePem string) (*http.Client, error) { + + var netTransport = &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + DualStack: true, + }).DialContext, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + } + + tlsConfig := http.DefaultTransport.(*http.Transport).TLSClientConfig + + if tlsConfig == nil { + /* #nosec */ + tlsConfig = &tls.Config{} + } else { + tlsConfig = tlsConfig.Clone() + } + + if trustBundlePem != "" { + trustBundle, err := parseTrustBundlePEM(trustBundlePem) + if err != nil { + return nil, err + } + tlsConfig.RootCAs = trustBundle + } + + tlsConfig.Renegotiation = tls.RenegotiateFreelyAsClient + netTransport.TLSClientConfig = tlsConfig + + client := &http.Client{ + Timeout: time.Second * 30, + Transport: netTransport, + } + + return client, nil +} + +func parseTrustBundlePEM(trustBundlePem string) (*x509.CertPool, error) { + var connectionTrustBundle *x509.CertPool + + if trustBundlePem != "" { + connectionTrustBundle = x509.NewCertPool() + if !connectionTrustBundle.AppendCertsFromPEM([]byte(trustBundlePem)) { + return nil, fmt.Errorf("failed to parse PEM trust bundle") + } + } else { + return nil, fmt.Errorf("trust bundle PEM data is empty") + } + + return connectionTrustBundle, nil +} diff --git a/plugin/pki/vcert.go b/plugin/pki/vcert.go index c4246cc4..509182f9 100644 --- a/plugin/pki/vcert.go +++ b/plugin/pki/vcert.go @@ -33,6 +33,24 @@ func (b *backend) ClientVenafi(ctx context.Context, s logical.Storage, policyNam return policy.venafiConnectionConfig.getConnection() } +func (b *backend) getConfing(ctx context.Context, s logical.Storage, policyName string) ( + *vcert.Config, error) { + + if policyName == "" { + return nil, fmt.Errorf("empty policy name") + } + + policy, err := b.getVenafiPolicyConfig(ctx, s, policyName) + if err != nil { + return nil, err + } + if policy == nil { + return nil, fmt.Errorf("expected policy but got nil from Vault storage %v", policy) + } + + return policy.venafiConnectionConfig.getConfig(true) +} + func pp(a interface{}) string { b, err := json.MarshalIndent(a, "", " ") if err != nil { @@ -43,6 +61,9 @@ func pp(a interface{}) string { type venafiConnectionConfig struct { TPPURL string `json:"tpp_url"` + URL string `json:"url"` + AccessToken string `json:"access_token"` + RefreshToken string `json:"refresh_token"` Zone string `json:"zone"` TPPPassword string `json:"tpp_password"` TPPUser string `json:"tpp_user"` @@ -52,13 +73,51 @@ type venafiConnectionConfig struct { } func (c venafiConnectionConfig) getConnection() (endpoint.Connector, error) { - cfg := vcert.Config{ + + cfg, err := c.getConfig(false) + if err == nil { + client, err := vcert.NewClient(cfg) + if err != nil { + return nil, fmt.Errorf("failed to get Venafi issuer client: %s", err) + } else { + return client, nil + } + + } else { + return nil, err + } + +} + +func (c venafiConnectionConfig) getConfig(includeRefToken bool) (*vcert.Config, error) { + var cfg = &vcert.Config{ Zone: c.Zone, LogVerbose: true, } - if c.TPPURL != "" && c.TPPUser != "" && c.TPPPassword != "" { + + if c.URL != "" && c.AccessToken != "" { cfg.ConnectorType = endpoint.ConnectorTypeTPP - cfg.BaseUrl = c.TPPURL + cfg.BaseUrl = c.URL + cfg.Credentials = &endpoint.Authentication{ + AccessToken: c.AccessToken, + } + + if c.TrustBundleFile != "" { + trustBundle, err := ioutil.ReadFile(c.TrustBundleFile) + if err != nil { + log.Printf("Can`t read trust bundle from file %s: %v\n", c.TrustBundleFile, err) + return nil, err + } + cfg.ConnectionTrust = string(trustBundle) + } + + if includeRefToken { + cfg.Credentials.RefreshToken = c.RefreshToken + } + + } else if c.URL != "" && c.TPPUser != "" && c.TPPPassword != "" && c.AccessToken == "" { + cfg.ConnectorType = endpoint.ConnectorTypeTPP + cfg.BaseUrl = c.URL cfg.Credentials = &endpoint.Authentication{ User: c.TPPUser, Password: c.TPPPassword, @@ -72,18 +131,15 @@ func (c venafiConnectionConfig) getConnection() (endpoint.Connector, error) { } cfg.ConnectionTrust = string(trustBundle) } + } else if c.Apikey != "" { cfg.ConnectorType = endpoint.ConnectorTypeCloud - cfg.BaseUrl = c.CloudURL + cfg.BaseUrl = c.URL cfg.Credentials = &endpoint.Authentication{ APIKey: c.Apikey, } } else { return nil, fmt.Errorf("failed to build config for Venafi conection") } - client, err := vcert.NewClient(&cfg) - if err != nil { - return nil, fmt.Errorf("failed to get Venafi issuer client: %s", err) - } - return client, nil + return cfg, nil } diff --git a/plugin/pki/venafi_util.go b/plugin/pki/venafi_util.go index 222d9a5b..64a75973 100644 --- a/plugin/pki/venafi_util.go +++ b/plugin/pki/venafi_util.go @@ -151,6 +151,15 @@ var venafiTestCloudConfigRestricted = map[string]interface{}{ "zone": os.Getenv("CLOUD_ZONE_RESTRICTED"), "auto_refresh_interval": 1, } + +var venafiTestTokenConfigRestricted = map[string]interface{}{ + "url": os.Getenv("TPP_TOKEN_URL"), + "access_token": os.Getenv("TPP_ACCESS_TOKEN"), + "zone": os.Getenv("TPP_ZONE_RESTRICTED"), + "trust_bundle_file": os.Getenv("TRUST_BUNDLE"), + "auto_refresh_interval": 1, +} + var venafiTestCloudConfigAllAllow = map[string]interface{}{ "cloud_url": os.Getenv("CLOUD_URL"), "apikey": os.Getenv("CLOUD_APIKEY"), @@ -181,6 +190,12 @@ func makeVenafiTPPConfig() (domain string, policyData map[string]interface{}) { return } +func makeVenafiTokenConfig() (domain string, policyData map[string]interface{}) { + domain = "vfidev.com" + policyData = copyMap(venafiTestTokenConfigRestricted) + return +} + func writePolicy(b *backend, storage logical.Storage, policyData map[string]interface{}, t *testing.T, policyName string) *logical.Response { t.Log("Writing Venafi policy configuration") resp, err := b.HandleRequest(context.Background(), &logical.Request{