github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/config/etcd/etcd.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package etcd 19 20 import ( 21 "crypto/tls" 22 "crypto/x509" 23 "strings" 24 "time" 25 26 "github.com/minio/minio/internal/config" 27 "github.com/minio/pkg/v2/env" 28 xnet "github.com/minio/pkg/v2/net" 29 clientv3 "go.etcd.io/etcd/client/v3" 30 "go.etcd.io/etcd/client/v3/namespace" 31 "go.uber.org/zap" 32 ) 33 34 const ( 35 // Default values used while communicating with etcd. 36 defaultDialTimeout = 5 * time.Second 37 defaultDialKeepAlive = 30 * time.Second 38 ) 39 40 // etcd environment values 41 const ( 42 Endpoints = "endpoints" 43 PathPrefix = "path_prefix" 44 CoreDNSPath = "coredns_path" 45 ClientCert = "client_cert" 46 ClientCertKey = "client_cert_key" 47 48 EnvEtcdEndpoints = "MINIO_ETCD_ENDPOINTS" 49 EnvEtcdPathPrefix = "MINIO_ETCD_PATH_PREFIX" 50 EnvEtcdCoreDNSPath = "MINIO_ETCD_COREDNS_PATH" 51 EnvEtcdClientCert = "MINIO_ETCD_CLIENT_CERT" 52 EnvEtcdClientCertKey = "MINIO_ETCD_CLIENT_CERT_KEY" 53 ) 54 55 // DefaultKVS - default KV settings for etcd. 56 var ( 57 DefaultKVS = config.KVS{ 58 config.KV{ 59 Key: Endpoints, 60 Value: "", 61 }, 62 config.KV{ 63 Key: PathPrefix, 64 Value: "", 65 }, 66 config.KV{ 67 Key: CoreDNSPath, 68 Value: "/skydns", 69 }, 70 config.KV{ 71 Key: ClientCert, 72 Value: "", 73 }, 74 config.KV{ 75 Key: ClientCertKey, 76 Value: "", 77 }, 78 } 79 ) 80 81 // Config - server etcd config. 82 type Config struct { 83 Enabled bool `json:"enabled"` 84 PathPrefix string `json:"pathPrefix"` 85 CoreDNSPath string `json:"coreDNSPath"` 86 clientv3.Config 87 } 88 89 // New - initialize new etcd client. 90 func New(cfg Config) (*clientv3.Client, error) { 91 if !cfg.Enabled { 92 return nil, nil 93 } 94 cli, err := clientv3.New(cfg.Config) 95 if err != nil { 96 return nil, err 97 } 98 cli.KV = namespace.NewKV(cli.KV, cfg.PathPrefix) 99 cli.Watcher = namespace.NewWatcher(cli.Watcher, cfg.PathPrefix) 100 cli.Lease = namespace.NewLease(cli.Lease, cfg.PathPrefix) 101 return cli, nil 102 } 103 104 func parseEndpoints(endpoints string) ([]string, bool, error) { 105 etcdEndpoints := strings.Split(endpoints, config.ValueSeparator) 106 107 var etcdSecure bool 108 for _, endpoint := range etcdEndpoints { 109 u, err := xnet.ParseHTTPURL(endpoint) 110 if err != nil { 111 return nil, false, err 112 } 113 if etcdSecure && u.Scheme == "http" { 114 return nil, false, config.Errorf("all endpoints should be https or http: %s", endpoint) 115 } 116 // If one of the endpoint is https, we will use https directly. 117 etcdSecure = etcdSecure || u.Scheme == "https" 118 } 119 120 return etcdEndpoints, etcdSecure, nil 121 } 122 123 // Enabled returns if etcd is enabled. 124 func Enabled(kvs config.KVS) bool { 125 endpoints := kvs.Get(Endpoints) 126 return endpoints != "" 127 } 128 129 // LookupConfig - Initialize new etcd config. 130 func LookupConfig(kvs config.KVS, rootCAs *x509.CertPool) (Config, error) { 131 cfg := Config{} 132 if err := config.CheckValidKeys(config.EtcdSubSys, kvs, DefaultKVS); err != nil { 133 return cfg, err 134 } 135 136 endpoints := env.Get(EnvEtcdEndpoints, kvs.Get(Endpoints)) 137 if endpoints == "" { 138 return cfg, nil 139 } 140 141 etcdEndpoints, etcdSecure, err := parseEndpoints(endpoints) 142 if err != nil { 143 return cfg, err 144 } 145 146 cfg.Enabled = true 147 cfg.DialTimeout = defaultDialTimeout 148 cfg.DialKeepAliveTime = defaultDialKeepAlive 149 // Disable etcd client SDK logging, etcd client 150 // incorrectly starts logging in unexpected data 151 // format. 152 cfg.LogConfig = &zap.Config{ 153 Level: zap.NewAtomicLevelAt(zap.FatalLevel), 154 Encoding: "console", 155 } 156 cfg.Endpoints = etcdEndpoints 157 cfg.CoreDNSPath = env.Get(EnvEtcdCoreDNSPath, kvs.Get(CoreDNSPath)) 158 // Default path prefix for all keys on etcd, other than CoreDNSPath. 159 cfg.PathPrefix = env.Get(EnvEtcdPathPrefix, kvs.Get(PathPrefix)) 160 if etcdSecure { 161 cfg.TLS = &tls.Config{ 162 RootCAs: rootCAs, 163 } 164 // This is only to support client side certificate authentication 165 // https://coreos.com/etcd/docs/latest/op-guide/security.html 166 etcdClientCertFile := env.Get(EnvEtcdClientCert, kvs.Get(ClientCert)) 167 etcdClientCertKey := env.Get(EnvEtcdClientCertKey, kvs.Get(ClientCertKey)) 168 if etcdClientCertFile != "" && etcdClientCertKey != "" { 169 cfg.TLS.GetClientCertificate = func(unused *tls.CertificateRequestInfo) (*tls.Certificate, error) { 170 cert, err := tls.LoadX509KeyPair(etcdClientCertFile, etcdClientCertKey) 171 return &cert, err 172 } 173 } 174 } 175 return cfg, nil 176 }