diff --git a/command/commands.go b/command/commands.go index d73f8b92b..a9b31fccf 100644 --- a/command/commands.go +++ b/command/commands.go @@ -46,7 +46,6 @@ import ( physCockroachDB "github.com/hashicorp/vault/physical/cockroachdb" physConsul "github.com/hashicorp/vault/physical/consul" physCouchDB "github.com/hashicorp/vault/physical/couchdb" - physEtcd "github.com/hashicorp/vault/physical/etcd" physFoundationDB "github.com/hashicorp/vault/physical/foundationdb" physMySQL "github.com/hashicorp/vault/physical/mysql" physOCI "github.com/hashicorp/vault/physical/oci" @@ -178,7 +177,6 @@ var ( "consul": physConsul.NewConsulBackend, "couchdb_transactional": physCouchDB.NewTransactionalCouchDBBackend, "couchdb": physCouchDB.NewCouchDBBackend, - "etcd": physEtcd.NewEtcdBackend, "file_transactional": physFile.NewTransactionalFileBackend, "file": physFile.NewFileBackend, "foundationdb": physFoundationDB.NewFDBBackend, diff --git a/go.mod b/go.mod index dc49c9752..6623e5342 100644 --- a/go.mod +++ b/go.mod @@ -159,9 +159,6 @@ require ( github.com/shirou/gopsutil/v3 v3.22.6 github.com/stretchr/testify v1.8.4 go.etcd.io/bbolt v1.3.7 - go.etcd.io/etcd/client/pkg/v3 v3.5.7 - go.etcd.io/etcd/client/v2 v2.305.5 - go.etcd.io/etcd/client/v3 v3.5.7 go.opentelemetry.io/otel v1.22.0 go.opentelemetry.io/otel/sdk v1.22.0 go.opentelemetry.io/otel/trace v1.22.0 @@ -229,8 +226,6 @@ require ( github.com/containerd/continuity v0.4.2 // indirect github.com/containerd/log v0.1.0 // indirect github.com/coreos/go-oidc/v3 v3.5.0 // indirect - github.com/coreos/go-semver v0.3.0 // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba // indirect github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc // indirect @@ -363,19 +358,14 @@ require ( github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect github.com/zclconf/go-cty v1.12.1 // indirect - go.etcd.io/etcd/api/v3 v3.5.7 // indirect go.mongodb.org/mongo-driver v1.11.6 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect go.opentelemetry.io/otel/metric v1.22.0 // indirect - go.uber.org/multierr v1.7.0 // indirect - go.uber.org/zap v1.19.1 // indirect golang.org/x/mod v0.15.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/api v0.163.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/jcmturner/goidentity.v3 v3.0.0 // indirect diff --git a/go.sum b/go.sum index c3f515a94..6cdb7a3e9 100644 --- a/go.sum +++ b/go.sum @@ -998,7 +998,6 @@ github.com/aws/aws-sdk-go v1.49.22/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3Tj github.com/axiomhq/hyperloglog v0.0.0-20220105174342-98591331716a h1:eqjiAL3qooftPm8b9C1GsSSRcmlw7iOva8vdBTmV2PY= github.com/axiomhq/hyperloglog v0.0.0-20220105174342-98591331716a/go.mod h1:2stgcRjl6QmW+gU2h5E7BQXg4HU0gzxKWDuT5HviN9s= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -1248,7 +1247,6 @@ github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHo github.com/coreos/go-oidc/v3 v3.5.0 h1:VxKtbccHZxs8juq7RdJntSqtXFtde9YpNpGn0yqgEHw= github.com/coreos/go-oidc/v3 v3.5.0/go.mod h1:ecXRtV4romGPeO6ieExAsUK9cb/3fp9hXNz1tlv8PIM= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -1259,7 +1257,6 @@ github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7 github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= @@ -2900,19 +2897,12 @@ go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8= -go.etcd.io/etcd/api/v3 v3.5.7 h1:sbcmosSVesNrWOJ58ZQFitHMdncusIifYcrBfwrlJSY= -go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/pkg/v3 v3.5.5/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ= -go.etcd.io/etcd/client/pkg/v3 v3.5.7 h1:y3kf5Gbp4e4q7egZdn5T7W9TSHUvkClN6u+Rq9mEOmg= -go.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v2 v2.305.5 h1:DktRP60//JJpnPC0VBymAN/7V71GHMdjDCBt4ZPXDjI= go.etcd.io/etcd/client/v2 v2.305.5/go.mod h1:zQjKllfqfBVyVStbt4FaosoX2iYd8fV/GRy/PbowgP4= go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= go.etcd.io/etcd/client/v3 v3.5.5/go.mod h1:aApjR4WGlSumpnJ2kloS75h6aHUmAyaPLjHMxpc7E7c= -go.etcd.io/etcd/client/v3 v3.5.7 h1:u/OhpiuCgYY8awOHlhIhmGIGpxfBU/GZBUP3m/3/Iz4= -go.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/NabQgw= go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= go.etcd.io/etcd/pkg/v3 v3.5.5/go.mod h1:6ksYFxttiUGzC2uxyqiyOEvhAiD0tuIqSZkX3TyPdaE= go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= @@ -3024,7 +3014,6 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU= go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= @@ -3032,16 +3021,12 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= -go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= -go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= diff --git a/helper/testhelpers/etcd/etcdhelper.go b/helper/testhelpers/etcd/etcdhelper.go deleted file mode 100644 index dc8f796e1..000000000 --- a/helper/testhelpers/etcd/etcdhelper.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package etcd - -import ( - "context" - "fmt" - "net/url" - "os" - "testing" - "time" - - "github.com/hashicorp/vault/sdk/helper/docker" - clientv3 "go.etcd.io/etcd/client/v3" -) - -type Config struct { - docker.ServiceURL -} - -// PrepareTestContainer creates etcd docker container. If environment variabe -// ETCD_ADDR is set, the tests are executed against specified address and etcd -// container is not launched. -func PrepareTestContainer(t *testing.T, version string) (func(), *Config) { - if addr := os.Getenv("ETCD_ADDR"); addr != "" { - url, err := docker.NewServiceURLParse(addr) - if err != nil { - t.Fatal(err) - } - return func() {}, &Config{ServiceURL: *url} - } - - // Check https://github.com/etcd-io/etcd/releases for latest releases. - runner, err := docker.NewServiceRunner(docker.RunOptions{ - ContainerName: "etcd", - ImageRepo: "gcr.io/etcd-development/etcd", - ImageTag: version, - Cmd: []string{ - "/usr/local/bin/etcd", - "--name", "s1", - "--listen-client-urls", "http://0.0.0.0:2379", - "--advertise-client-urls", "http://0.0.0.0:2379", - "--listen-peer-urls", "http://0.0.0.0:2380", - "--initial-advertise-peer-urls", "http://0.0.0.0:2380", - "--initial-cluster", "s1=http://0.0.0.0:2380", - "--initial-cluster-token", "tkn", - "--initial-cluster-state", "new", - "--log-level", "info", - "--logger", "zap", - "--log-outputs", "stderr", - }, - Ports: []string{"2379/tcp"}, - }) - if err != nil { - t.Fatalf("Could not start docker etcd container: %s", err) - } - - svc, err := runner.StartService(context.Background(), func(ctx context.Context, host string, port int) (docker.ServiceConfig, error) { - address := fmt.Sprintf("%s:%d", host, port) - s := docker.NewServiceURL(url.URL{ - Scheme: "http", - Host: address, - }) - - client, err := clientv3.New(clientv3.Config{ - Endpoints: []string{address}, - DialTimeout: 2 * time.Minute, - }) - if err != nil { - return nil, fmt.Errorf("could not connect to etcd container: %w", err) - } - - // Enable authentication for the tests. - client.RoleAdd(ctx, "root") - client.UserAdd(ctx, "root", "insecure") - client.UserGrantRole(ctx, "root", "root") - client.AuthEnable(ctx) - client.Close() - - return &Config{ - ServiceURL: *s, - }, nil - }) - if err != nil { - t.Fatalf("Could not start docker etcd container: %s", err) - } - - return svc.Cleanup, svc.Config.(*Config) -} diff --git a/physical/etcd/etcd.go b/physical/etcd/etcd.go deleted file mode 100644 index 1d332dc9c..000000000 --- a/physical/etcd/etcd.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package etcd - -import ( - "errors" - "fmt" - "net/url" - "os" - "strings" - - log "github.com/hashicorp/go-hclog" - "github.com/hashicorp/vault/sdk/physical" - "go.etcd.io/etcd/client/v2" -) - -var ( - EtcdMultipleBootstrapError = errors.New("client setup failed: multiple discovery or bootstrap flags specified, use either \"address\" or \"discovery_srv\"") - EtcdAddressError = errors.New("client setup failed: address must be valid URL (ex. 'scheme://host:port')") - EtcdLockHeldError = errors.New("lock already held") - EtcdLockNotHeldError = errors.New("lock not held") - EtcdVersionUnknown = errors.New("etcd: unknown API version") -) - -// NewEtcdBackend constructs a etcd backend using a given machine address. -func NewEtcdBackend(conf map[string]string, logger log.Logger) (physical.Backend, error) { - var ( - apiVersion string - ok bool - ) - - if apiVersion, ok = conf["etcd_api"]; !ok { - apiVersion = os.Getenv("ETCD_API") - } - - if apiVersion == "" { - apiVersion = "v3" - } - - switch apiVersion { - case "3", "etcd3", "v3": - return newEtcd3Backend(conf, logger) - default: - return nil, EtcdVersionUnknown - } -} - -// Retrieves the config option in order of priority: -// 1. The named environment variable if it exist -// 2. The key in the config map -func getEtcdOption(conf map[string]string, confKey, envVar string) (string, bool) { - confVal, inConf := conf[confKey] - envVal, inEnv := os.LookupEnv(envVar) - if inEnv { - return envVal, true - } - return confVal, inConf -} - -func getEtcdEndpoints(conf map[string]string) ([]string, error) { - address, staticBootstrap := getEtcdOption(conf, "address", "ETCD_ADDR") - domain, useSrv := getEtcdOption(conf, "discovery_srv", "ETCD_DISCOVERY_SRV") - if useSrv && staticBootstrap { - return nil, EtcdMultipleBootstrapError - } - - if staticBootstrap { - endpoints := strings.Split(address, ",") - // Verify that the machines are valid URLs - for _, e := range endpoints { - u, urlErr := url.Parse(e) - if urlErr != nil || u.Scheme == "" { - return nil, EtcdAddressError - } - } - return endpoints, nil - } - - if useSrv { - srvName, _ := getEtcdOption(conf, "discovery_srv_name", "ETCD_DISCOVERY_SRV_NAME") - discoverer := client.NewSRVDiscover() - endpoints, err := discoverer.Discover(domain, srvName) - if err != nil { - return nil, fmt.Errorf("failed to discover etcd endpoints through SRV discovery: %w", err) - } - return endpoints, nil - } - - // Set a default endpoints list if no option was set - return []string{"http://127.0.0.1:2379"}, nil -} diff --git a/physical/etcd/etcd3.go b/physical/etcd/etcd3.go deleted file mode 100644 index 62781f011..000000000 --- a/physical/etcd/etcd3.go +++ /dev/null @@ -1,383 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package etcd - -import ( - "context" - "errors" - "fmt" - "os" - "path" - "strconv" - "strings" - "sync" - "time" - - "github.com/armon/go-metrics" - log "github.com/hashicorp/go-hclog" - "github.com/hashicorp/go-secure-stdlib/parseutil" - "github.com/hashicorp/go-secure-stdlib/strutil" - "github.com/hashicorp/vault/sdk/physical" - "go.etcd.io/etcd/client/pkg/v3/transport" - clientv3 "go.etcd.io/etcd/client/v3" - "go.etcd.io/etcd/client/v3/concurrency" -) - -// EtcdBackend is a physical backend that stores data at specific -// prefix within etcd. It is used for most production situations as -// it allows Vault to run on multiple machines in a highly-available manner. -type EtcdBackend struct { - logger log.Logger - path string - haEnabled bool - lockTimeout time.Duration - requestTimeout time.Duration - - permitPool *physical.PermitPool - - etcd *clientv3.Client -} - -// Verify EtcdBackend satisfies the correct interfaces -var ( - _ physical.Backend = (*EtcdBackend)(nil) - _ physical.HABackend = (*EtcdBackend)(nil) - _ physical.Lock = (*EtcdLock)(nil) -) - -// newEtcd3Backend constructs a etcd3 backend. -func newEtcd3Backend(conf map[string]string, logger log.Logger) (physical.Backend, error) { - // Get the etcd path form the configuration. - path, ok := conf["path"] - if !ok { - path = "/vault" - } - - // Ensure path is prefixed. - if !strings.HasPrefix(path, "/") { - path = "/" + path - } - - endpoints, err := getEtcdEndpoints(conf) - if err != nil { - return nil, err - } - - cfg := clientv3.Config{ - Endpoints: endpoints, - } - - haEnabled := os.Getenv("ETCD_HA_ENABLED") - if haEnabled == "" { - haEnabled = conf["ha_enabled"] - } - if haEnabled == "" { - haEnabled = "false" - } - haEnabledBool, err := strconv.ParseBool(haEnabled) - if err != nil { - return nil, fmt.Errorf("value [%v] of 'ha_enabled' could not be understood", haEnabled) - } - - cert, hasCert := conf["tls_cert_file"] - key, hasKey := conf["tls_key_file"] - ca, hasCa := conf["tls_ca_file"] - if (hasCert && hasKey) || hasCa { - tls := transport.TLSInfo{ - TrustedCAFile: ca, - CertFile: cert, - KeyFile: key, - } - - tlscfg, err := tls.ClientConfig() - if err != nil { - return nil, err - } - cfg.TLS = tlscfg - } - - // Set credentials. - username := os.Getenv("ETCD_USERNAME") - if username == "" { - username, _ = conf["username"] - } - - password := os.Getenv("ETCD_PASSWORD") - if password == "" { - password, _ = conf["password"] - } - - if username != "" && password != "" { - cfg.Username = username - cfg.Password = password - } - - if maxReceive, ok := conf["max_receive_size"]; ok { - // grpc converts this to uint32 internally, so parse as that to avoid passing invalid values - val, err := strconv.ParseUint(maxReceive, 10, 32) - if err != nil { - return nil, fmt.Errorf("value of 'max_receive_size' (%v) could not be understood: %w", maxReceive, err) - } - cfg.MaxCallRecvMsgSize = int(val) - } - - etcd, err := clientv3.New(cfg) - if err != nil { - return nil, err - } - - sReqTimeout := conf["request_timeout"] - if sReqTimeout == "" { - // etcd3 default request timeout is set to 5s. It should be long enough - // for most cases, even with internal retry. - sReqTimeout = "5s" - } - reqTimeout, err := parseutil.ParseDurationSecond(sReqTimeout) - if err != nil { - return nil, fmt.Errorf("value [%v] of 'request_timeout' could not be understood: %w", sReqTimeout, err) - } - - ssync, ok := conf["sync"] - if !ok { - ssync = "true" - } - sync, err := strconv.ParseBool(ssync) - if err != nil { - return nil, fmt.Errorf("value of 'sync' (%v) could not be understood: %w", ssync, err) - } - - if sync { - ctx, cancel := context.WithTimeout(context.Background(), reqTimeout) - err := etcd.Sync(ctx) - cancel() - if err != nil { - return nil, err - } - } - - sLock := conf["lock_timeout"] - if sLock == "" { - // etcd3 default lease duration is 60s. set to 15s for faster recovery. - sLock = "15s" - } - lock, err := parseutil.ParseDurationSecond(sLock) - if err != nil { - return nil, fmt.Errorf("value [%v] of 'lock_timeout' could not be understood: %w", sLock, err) - } - - return &EtcdBackend{ - path: path, - etcd: etcd, - permitPool: physical.NewPermitPool(physical.DefaultParallelOperations), - logger: logger, - haEnabled: haEnabledBool, - lockTimeout: lock, - requestTimeout: reqTimeout, - }, nil -} - -func (c *EtcdBackend) Put(ctx context.Context, entry *physical.Entry) error { - defer metrics.MeasureSince([]string{"etcd", "put"}, time.Now()) - - c.permitPool.Acquire() - defer c.permitPool.Release() - - ctx, cancel := context.WithTimeout(context.Background(), c.requestTimeout) - defer cancel() - _, err := c.etcd.Put(ctx, path.Join(c.path, entry.Key), string(entry.Value)) - return err -} - -func (c *EtcdBackend) Get(ctx context.Context, key string) (*physical.Entry, error) { - defer metrics.MeasureSince([]string{"etcd", "get"}, time.Now()) - - c.permitPool.Acquire() - defer c.permitPool.Release() - - ctx, cancel := context.WithTimeout(context.Background(), c.requestTimeout) - defer cancel() - resp, err := c.etcd.Get(ctx, path.Join(c.path, key)) - if err != nil { - return nil, err - } - - if len(resp.Kvs) == 0 { - return nil, nil - } - if len(resp.Kvs) > 1 { - return nil, errors.New("unexpected number of keys from a get request") - } - return &physical.Entry{ - Key: key, - Value: resp.Kvs[0].Value, - }, nil -} - -func (c *EtcdBackend) Delete(ctx context.Context, key string) error { - defer metrics.MeasureSince([]string{"etcd", "delete"}, time.Now()) - - c.permitPool.Acquire() - defer c.permitPool.Release() - - ctx, cancel := context.WithTimeout(context.Background(), c.requestTimeout) - defer cancel() - _, err := c.etcd.Delete(ctx, path.Join(c.path, key)) - if err != nil { - return err - } - return nil -} - -func (c *EtcdBackend) List(ctx context.Context, prefix string) ([]string, error) { - defer metrics.MeasureSince([]string{"etcd", "list"}, time.Now()) - - c.permitPool.Acquire() - defer c.permitPool.Release() - - ctx, cancel := context.WithTimeout(context.Background(), c.requestTimeout) - defer cancel() - prefix = path.Join(c.path, prefix) + "/" - resp, err := c.etcd.Get(ctx, prefix, clientv3.WithPrefix(), clientv3.WithKeysOnly()) - if err != nil { - return nil, err - } - - keys := []string{} - for _, kv := range resp.Kvs { - key := strings.TrimPrefix(string(kv.Key), prefix) - key = strings.TrimPrefix(key, "/") - - if len(key) == 0 { - continue - } - - if i := strings.Index(key, "/"); i == -1 { - keys = append(keys, key) - } else if i != -1 { - keys = strutil.AppendIfMissing(keys, key[:i+1]) - } - } - return keys, nil -} - -func (e *EtcdBackend) HAEnabled() bool { - return e.haEnabled -} - -// EtcdLock implements a lock using and etcd backend. -type EtcdLock struct { - lock sync.Mutex - held bool - timeout time.Duration - requestTimeout time.Duration - - etcdSession *concurrency.Session - etcdMu *concurrency.Mutex - - prefix string - value string - - etcd *clientv3.Client -} - -// Lock is used for mutual exclusion based on the given key. -func (c *EtcdBackend) LockWith(key, value string) (physical.Lock, error) { - p := path.Join(c.path, key) - return &EtcdLock{ - prefix: p, - value: value, - etcd: c.etcd, - timeout: c.lockTimeout, - requestTimeout: c.requestTimeout, - }, nil -} - -func (c *EtcdLock) Lock(stopCh <-chan struct{}) (<-chan struct{}, error) { - c.lock.Lock() - defer c.lock.Unlock() - - if c.etcdMu == nil { - if err := c.initMu(); err != nil { - return nil, err - } - } - - if c.held { - return nil, EtcdLockHeldError - } - - select { - case _, ok := <-c.etcdSession.Done(): - if !ok { - // The session's done channel is closed, so the session is over, - // and we need a new lock with a new session. - if err := c.initMu(); err != nil { - return nil, err - } - } - default: - } - - ctx, cancel := context.WithCancel(context.Background()) - go func() { - <-stopCh - cancel() - }() - if err := c.etcdMu.Lock(ctx); err != nil { - if err == context.Canceled { - return nil, nil - } - return nil, err - } - - pctx, cancel := context.WithTimeout(context.Background(), c.requestTimeout) - defer cancel() - if _, err := c.etcd.Put(pctx, c.etcdMu.Key(), c.value, clientv3.WithLease(c.etcdSession.Lease())); err != nil { - return nil, err - } - - c.held = true - - return c.etcdSession.Done(), nil -} - -func (c *EtcdLock) Unlock() error { - c.lock.Lock() - defer c.lock.Unlock() - - if !c.held { - return EtcdLockNotHeldError - } - - ctx, cancel := context.WithTimeout(context.Background(), c.requestTimeout) - defer cancel() - return c.etcdMu.Unlock(ctx) -} - -func (c *EtcdLock) Value() (bool, string, error) { - ctx, cancel := context.WithTimeout(context.Background(), c.requestTimeout) - defer cancel() - - resp, err := c.etcd.Get(ctx, - c.prefix, clientv3.WithPrefix(), - clientv3.WithSort(clientv3.SortByCreateRevision, clientv3.SortAscend)) - if err != nil { - return false, "", err - } - if len(resp.Kvs) == 0 { - return false, "", nil - } - - return true, string(resp.Kvs[0].Value), nil -} - -func (c *EtcdLock) initMu() error { - session, err := concurrency.NewSession(c.etcd, concurrency.WithTTL(int(c.timeout.Seconds()))) - if err != nil { - return err - } - c.etcdSession = session - c.etcdMu = concurrency.NewMutex(session, c.prefix) - return nil -} diff --git a/physical/etcd/etcd3_test.go b/physical/etcd/etcd3_test.go deleted file mode 100644 index 7af1ecd71..000000000 --- a/physical/etcd/etcd3_test.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: BUSL-1.1 - -package etcd - -import ( - "fmt" - "testing" - "time" - - log "github.com/hashicorp/go-hclog" - "github.com/hashicorp/vault/helper/testhelpers/etcd" - "github.com/hashicorp/vault/sdk/helper/logging" - "github.com/hashicorp/vault/sdk/physical" -) - -func TestEtcd3Backend(t *testing.T) { - cleanup, config := etcd.PrepareTestContainer(t, "v3.5.0") - defer cleanup() - - logger := logging.NewVaultLogger(log.Debug) - configMap := map[string]string{ - "address": config.URL().String(), - "path": fmt.Sprintf("/vault-%d", time.Now().Unix()), - "etcd_api": "3", - "username": "root", - "password": "insecure", - - // Syncing advertised client urls should be disabled since docker port mapping confuses the client. - "sync": "false", - } - - b, err := NewEtcdBackend(configMap, logger) - if err != nil { - t.Fatalf("err: %s", err) - } - - b2, err := NewEtcdBackend(configMap, logger) - if err != nil { - t.Fatalf("err: %s", err) - } - - physical.ExerciseBackend(t, b) - physical.ExerciseBackend_ListPrefix(t, b) - physical.ExerciseHABackend(t, b.(physical.HABackend), b2.(physical.HABackend)) -}