diff --git a/cmd/api-errors.go b/cmd/api-errors.go index 13a072c3e..1f7b277aa 100644 --- a/cmd/api-errors.go +++ b/cmd/api-errors.go @@ -29,7 +29,7 @@ import ( minio "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/tags" - "github.com/minio/minio/cmd/config/etcd/dns" + "github.com/minio/minio/cmd/config/dns" "github.com/minio/minio/cmd/crypto" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/auth" diff --git a/cmd/bucket-handlers.go b/cmd/bucket-handlers.go index 14f58b00d..63517151b 100644 --- a/cmd/bucket-handlers.go +++ b/cmd/bucket-handlers.go @@ -33,7 +33,7 @@ import ( "github.com/minio/minio-go/v7/pkg/set" "github.com/minio/minio-go/v7/pkg/tags" "github.com/minio/minio/cmd/config" - "github.com/minio/minio/cmd/config/etcd/dns" + "github.com/minio/minio/cmd/config/dns" "github.com/minio/minio/cmd/crypto" xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/cmd/logger" diff --git a/cmd/common-main.go b/cmd/common-main.go index 5639c5df4..d9bac566f 100644 --- a/cmd/common-main.go +++ b/cmd/common-main.go @@ -33,7 +33,6 @@ import ( "github.com/minio/cli" "github.com/minio/minio-go/v7/pkg/set" "github.com/minio/minio/cmd/config" - "github.com/minio/minio/cmd/config/etcd/dns" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/auth" "github.com/minio/minio/pkg/certs" @@ -286,14 +285,6 @@ func handleCommonEnvVars() { os.Unsetenv(config.EnvAccessKeyOld) os.Unsetenv(config.EnvSecretKeyOld) } - - url, user, pwd, ok := env.LookupEnv(config.EnvDNSWebhook) - if ok { - globalDNSConfig, err = dns.NewOperatorDNS(url, user, pwd) - if err != nil { - logger.Fatal(err, "Unable to fetch the value of "+config.EnvDNSWebhook) - } - } } func logStartupMessage(msg string) { diff --git a/cmd/config-current.go b/cmd/config-current.go index f2a673f5b..2416f8801 100644 --- a/cmd/config-current.go +++ b/cmd/config-current.go @@ -25,8 +25,8 @@ import ( "github.com/minio/minio/cmd/config/api" "github.com/minio/minio/cmd/config/cache" "github.com/minio/minio/cmd/config/compress" + "github.com/minio/minio/cmd/config/dns" "github.com/minio/minio/cmd/config/etcd" - "github.com/minio/minio/cmd/config/etcd/dns" xldap "github.com/minio/minio/cmd/config/identity/ldap" "github.com/minio/minio/cmd/config/identity/openid" "github.com/minio/minio/cmd/config/notify" @@ -321,6 +321,19 @@ func lookupConfigs(s config.Config, setDriveCount int) { } } + if dnsURL, dnsUser, dnsPass, ok := env.LookupEnv(config.EnvDNSWebhook); ok { + globalDNSConfig, err = dns.NewOperatorDNS(dnsURL, + dns.Authentication(dnsUser, dnsPass), + dns.RootCAs(globalRootCAs)) + if err != nil { + if globalIsGateway { + logger.FatalIf(err, "Unable to initialize remote webhook DNS config") + } else { + logger.LogIf(ctx, fmt.Errorf("Unable to initialize remote webhook DNS config %w", err)) + } + } + } + etcdCfg, err := etcd.LookupConfig(s[config.EtcdSubSys][config.Default], globalRootCAs) if err != nil { if globalIsGateway { @@ -342,19 +355,25 @@ func lookupConfigs(s config.Config, setDriveCount int) { } } - if len(globalDomainNames) != 0 && !globalDomainIPs.IsEmpty() && globalEtcdClient != nil && globalDNSConfig == nil { - globalDNSConfig, err = dns.NewCoreDNS(etcdCfg.Config, - dns.DomainNames(globalDomainNames), - dns.DomainIPs(globalDomainIPs), - dns.DomainPort(globalMinioPort), - dns.CoreDNSPath(etcdCfg.CoreDNSPath), - ) - if err != nil { - if globalIsGateway { - logger.FatalIf(err, "Unable to initialize DNS config") - } else { - logger.LogIf(ctx, fmt.Errorf("Unable to initialize DNS config for %s: %w", - globalDomainNames, err)) + if len(globalDomainNames) != 0 && !globalDomainIPs.IsEmpty() && globalEtcdClient != nil { + if globalDNSConfig != nil { + // if global DNS is already configured, indicate with a warning, incase + // users are confused. + logger.LogIf(ctx, fmt.Errorf("DNS store is already configured with %s, not using etcd for DNS store", globalDNSConfig)) + } else { + globalDNSConfig, err = dns.NewCoreDNS(etcdCfg.Config, + dns.DomainNames(globalDomainNames), + dns.DomainIPs(globalDomainIPs), + dns.DomainPort(globalMinioPort), + dns.CoreDNSPath(etcdCfg.CoreDNSPath), + ) + if err != nil { + if globalIsGateway { + logger.FatalIf(err, "Unable to initialize DNS config") + } else { + logger.LogIf(ctx, fmt.Errorf("Unable to initialize DNS config for %s: %w", + globalDomainNames, err)) + } } } } diff --git a/cmd/config/etcd/dns/etcd_dns.go b/cmd/config/dns/etcd_dns.go similarity index 94% rename from cmd/config/etcd/dns/etcd_dns.go rename to cmd/config/dns/etcd_dns.go index 371be9c83..24f05d5d0 100644 --- a/cmd/config/etcd/dns/etcd_dns.go +++ b/cmd/config/dns/etcd_dns.go @@ -26,9 +26,8 @@ import ( "strings" "time" - "github.com/minio/minio-go/v7/pkg/set" - "github.com/coredns/coredns/plugin/etcd/msg" + "github.com/minio/minio-go/v7/pkg/set" "go.etcd.io/etcd/v3/clientv3" ) @@ -214,6 +213,11 @@ func (c *CoreDNS) DeleteRecord(record SrvRecord) error { return nil } +// String stringer name for this implementation of dns.Store +func (c *CoreDNS) String() string { + return "etcdDNS" +} + // CoreDNS - represents dns config for coredns server. type CoreDNS struct { domainNames []string @@ -223,13 +227,13 @@ type CoreDNS struct { etcdClient *clientv3.Client } -// Option - functional options pattern style -type Option func(*CoreDNS) +// EtcdOption - functional options pattern style +type EtcdOption func(*CoreDNS) // DomainNames set a list of domain names used by this CoreDNS // client setting, note this will fail if set to empty when // constructor initializes. -func DomainNames(domainNames []string) Option { +func DomainNames(domainNames []string) EtcdOption { return func(args *CoreDNS) { args.domainNames = domainNames } @@ -237,14 +241,14 @@ func DomainNames(domainNames []string) Option { // DomainIPs set a list of custom domain IPs, note this will // fail if set to empty when constructor initializes. -func DomainIPs(domainIPs set.StringSet) Option { +func DomainIPs(domainIPs set.StringSet) EtcdOption { return func(args *CoreDNS) { args.domainIPs = domainIPs } } // DomainPort - is a string version of server port -func DomainPort(domainPort string) Option { +func DomainPort(domainPort string) EtcdOption { return func(args *CoreDNS) { args.domainPort = domainPort } @@ -253,14 +257,14 @@ func DomainPort(domainPort string) Option { // CoreDNSPath - custom prefix on etcd to populate DNS // service records, optional and can be empty. // if empty then c.prefixPath is used i.e "/skydns" -func CoreDNSPath(prefix string) Option { +func CoreDNSPath(prefix string) EtcdOption { return func(args *CoreDNS) { args.prefixPath = prefix } } // NewCoreDNS - initialize a new coreDNS set/unset values. -func NewCoreDNS(cfg clientv3.Config, setters ...Option) (Store, error) { +func NewCoreDNS(cfg clientv3.Config, setters ...EtcdOption) (Store, error) { etcdClient, err := clientv3.New(cfg) if err != nil { return nil, err diff --git a/cmd/config/etcd/dns/operator_dns.go b/cmd/config/dns/operator_dns.go similarity index 66% rename from cmd/config/etcd/dns/operator_dns.go rename to cmd/config/dns/operator_dns.go index 0583c8f13..3d323acea 100644 --- a/cmd/config/etcd/dns/operator_dns.go +++ b/cmd/config/dns/operator_dns.go @@ -25,39 +25,39 @@ import ( "net" "net/http" "net/url" + "strconv" "time" "github.com/dgrijalva/jwt-go" "github.com/minio/minio/cmd/config" + xhttp "github.com/minio/minio/cmd/http" ) var ( defaultOperatorContextTimeout = 10 * time.Second - // ErrNotImplemented - Indicates no entries were found for the given key (directory) + // ErrNotImplemented - Indicates the functionality which is not implemented ErrNotImplemented = errors.New("The method is not implemented") - globalRootCAs *x509.CertPool ) -// RegisterGlobalCAs register the global root CAs -func RegisterGlobalCAs(CAs *x509.CertPool) { - globalRootCAs = CAs -} +func (c *OperatorDNS) addAuthHeader(r *http.Request) error { + if c.username == "" || c.password == "" { + return nil + } -func (c *OperatorDNS) addAuthHeader(r *http.Request) (*http.Request, error) { claims := &jwt.StandardClaims{ ExpiresAt: int64(15 * time.Minute), - Issuer: c.Username, + Issuer: c.username, Subject: config.EnvDNSWebhook, } token := jwt.NewWithClaims(jwt.SigningMethodHS512, claims) - ss, err := token.SignedString([]byte(c.Password)) + ss, err := token.SignedString([]byte(c.password)) if err != nil { - return r, err + return err } r.Header.Set("Authorization", "Bearer "+ss) - return r, nil + return nil } func (c *OperatorDNS) endpoint(bucket string, delete bool) (string, error) { @@ -67,11 +67,7 @@ func (c *OperatorDNS) endpoint(bucket string, delete bool) (string, error) { } q := u.Query() q.Add("bucket", bucket) - if delete { - q.Add("delete", "true") - } else { - q.Add("delete", "false") - } + q.Add("delete", strconv.FormatBool(delete)) u.RawQuery = q.Encode() return u.String(), nil } @@ -88,16 +84,16 @@ func (c *OperatorDNS) Put(bucket string) error { if err != nil { return err } - req, err = c.addAuthHeader(req) - if err != nil { + if err = c.addAuthHeader(req); err != nil { return err } resp, err := c.httpClient.Do(req) if err != nil { - if err := c.Delete(bucket); err != nil { - return err + if derr := c.Delete(bucket); derr != nil { + return derr } } + xhttp.DrainBody(resp.Body) if resp.StatusCode != http.StatusOK { return fmt.Errorf("request to create the service for bucket %s, failed with status %s", bucket, resp.Status) } @@ -116,14 +112,14 @@ func (c *OperatorDNS) Delete(bucket string) error { if err != nil { return err } - req, err = c.addAuthHeader(req) - if err != nil { + if err = c.addAuthHeader(req); err != nil { return err } resp, err := c.httpClient.Do(req) if err != nil { return err } + xhttp.DrainBody(resp.Body) if resp.StatusCode != http.StatusOK { return fmt.Errorf("request to delete the service for bucket %s, failed with status %s", bucket, resp.Status) } @@ -158,43 +154,68 @@ func (c *OperatorDNS) Get(bucket string) (srvRecords []SrvRecord, err error) { return nil, ErrNotImplemented } +// String stringer name for this implementation of dns.Store +func (c *OperatorDNS) String() string { + return "webhookDNS" +} + // OperatorDNS - represents dns config for MinIO k8s operator. type OperatorDNS struct { httpClient *http.Client Endpoint string - Username string - Password string + rootCAs *x509.CertPool + username string + password string +} + +// OperatorOption - functional options pattern style for OperatorDNS +type OperatorOption func(*OperatorDNS) + +// Authentication - custom username and password for authenticating at the endpoint +func Authentication(username, password string) OperatorOption { + return func(args *OperatorDNS) { + args.username = username + args.password = password + } +} + +// RootCAs - add custom trust certs pool +func RootCAs(CAs *x509.CertPool) OperatorOption { + return func(args *OperatorDNS) { + args.rootCAs = CAs + } } // NewOperatorDNS - initialize a new K8S Operator DNS set/unset values. -func NewOperatorDNS(endpoint, user, pwd string) (Store, error) { - if endpoint == "" || user == "" || pwd == "" { +func NewOperatorDNS(endpoint string, setters ...OperatorOption) (Store, error) { + if endpoint == "" { return nil, errors.New("invalid argument") } + args := &OperatorDNS{ - Username: user, - Password: pwd, Endpoint: endpoint, - httpClient: &http.Client{ - Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, - DialContext: (&net.Dialer{ - Timeout: 3 * time.Second, - KeepAlive: 5 * time.Second, - }).DialContext, - ResponseHeaderTimeout: 3 * time.Second, - TLSHandshakeTimeout: 3 * time.Second, - ExpectContinueTimeout: 3 * time.Second, - TLSClientConfig: &tls.Config{ - RootCAs: globalRootCAs, - }, - // Go net/http automatically unzip if content-type is - // gzip disable this feature, as we are always interested - // in raw stream. - DisableCompression: true, + } + for _, setter := range setters { + setter(args) + } + args.httpClient = &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 3 * time.Second, + KeepAlive: 5 * time.Second, + }).DialContext, + ResponseHeaderTimeout: 3 * time.Second, + TLSHandshakeTimeout: 3 * time.Second, + ExpectContinueTimeout: 3 * time.Second, + TLSClientConfig: &tls.Config{ + RootCAs: args.rootCAs, }, + // Go net/http automatically unzip if content-type is + // gzip disable this feature, as we are always interested + // in raw stream. + DisableCompression: true, }, } - return args, nil } diff --git a/cmd/config/etcd/dns/store.go b/cmd/config/dns/store.go similarity index 98% rename from cmd/config/etcd/dns/store.go rename to cmd/config/dns/store.go index aa0bfda21..98c044be5 100644 --- a/cmd/config/etcd/dns/store.go +++ b/cmd/config/dns/store.go @@ -24,4 +24,5 @@ type Store interface { List() (map[string][]SrvRecord, error) DeleteRecord(record SrvRecord) error Close() error + String() string } diff --git a/cmd/config/etcd/dns/types.go b/cmd/config/dns/types.go similarity index 100% rename from cmd/config/etcd/dns/types.go rename to cmd/config/dns/types.go diff --git a/cmd/generic-handlers.go b/cmd/generic-handlers.go index 209d866a2..2eb4f35a1 100644 --- a/cmd/generic-handlers.go +++ b/cmd/generic-handlers.go @@ -24,7 +24,7 @@ import ( "github.com/minio/minio-go/v7/pkg/set" humanize "github.com/dustin/go-humanize" - "github.com/minio/minio/cmd/config/etcd/dns" + "github.com/minio/minio/cmd/config/dns" "github.com/minio/minio/cmd/crypto" xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/cmd/http/stats" diff --git a/cmd/globals.go b/cmd/globals.go index b43f94173..91eb0fb07 100644 --- a/cmd/globals.go +++ b/cmd/globals.go @@ -26,7 +26,7 @@ import ( humanize "github.com/dustin/go-humanize" "github.com/minio/minio/cmd/config/cache" "github.com/minio/minio/cmd/config/compress" - "github.com/minio/minio/cmd/config/etcd/dns" + "github.com/minio/minio/cmd/config/dns" xldap "github.com/minio/minio/cmd/config/identity/ldap" "github.com/minio/minio/cmd/config/identity/openid" "github.com/minio/minio/cmd/config/policy/opa" diff --git a/cmd/object-api-utils.go b/cmd/object-api-utils.go index 3e455be67..e55aa1b6e 100644 --- a/cmd/object-api-utils.go +++ b/cmd/object-api-utils.go @@ -38,7 +38,7 @@ import ( "github.com/klauspost/readahead" "github.com/minio/minio-go/v7/pkg/s3utils" "github.com/minio/minio/cmd/config/compress" - "github.com/minio/minio/cmd/config/etcd/dns" + "github.com/minio/minio/cmd/config/dns" "github.com/minio/minio/cmd/config/storageclass" "github.com/minio/minio/cmd/crypto" xhttp "github.com/minio/minio/cmd/http" diff --git a/cmd/object-handlers.go b/cmd/object-handlers.go index d547af05d..4c378f3e6 100644 --- a/cmd/object-handlers.go +++ b/cmd/object-handlers.go @@ -37,7 +37,7 @@ import ( "github.com/minio/minio-go/v7/pkg/credentials" "github.com/minio/minio-go/v7/pkg/encrypt" "github.com/minio/minio-go/v7/pkg/tags" - "github.com/minio/minio/cmd/config/etcd/dns" + "github.com/minio/minio/cmd/config/dns" "github.com/minio/minio/cmd/config/storageclass" "github.com/minio/minio/cmd/crypto" xhttp "github.com/minio/minio/cmd/http" diff --git a/cmd/server-main.go b/cmd/server-main.go index 6d7980765..fc2f201d4 100644 --- a/cmd/server-main.go +++ b/cmd/server-main.go @@ -32,7 +32,6 @@ import ( "github.com/minio/cli" "github.com/minio/minio/cmd/config" - "github.com/minio/minio/cmd/config/etcd/dns" xhttp "github.com/minio/minio/cmd/http" "github.com/minio/minio/cmd/logger" "github.com/minio/minio/pkg/auth" @@ -130,7 +129,6 @@ func serverHandleCmdArgs(ctx *cli.Context) { // Register root CAs for remote ENVs env.RegisterGlobalCAs(globalRootCAs) - dns.RegisterGlobalCAs(globalRootCAs) globalMinioAddr = globalCLIContext.Addr diff --git a/cmd/web-handlers.go b/cmd/web-handlers.go index ae5ca118e..1e6176d6d 100644 --- a/cmd/web-handlers.go +++ b/cmd/web-handlers.go @@ -40,7 +40,7 @@ import ( miniogopolicy "github.com/minio/minio-go/v7/pkg/policy" "github.com/minio/minio-go/v7/pkg/s3utils" "github.com/minio/minio/browser" - "github.com/minio/minio/cmd/config/etcd/dns" + "github.com/minio/minio/cmd/config/dns" "github.com/minio/minio/cmd/config/identity/openid" "github.com/minio/minio/cmd/crypto" xhttp "github.com/minio/minio/cmd/http"