From 7e540d41587aeefbbc17acbd7d5b8ae92bd55245 Mon Sep 17 00:00:00 2001 From: Stan Lagun Date: Mon, 14 Aug 2017 19:05:33 -0700 Subject: [PATCH] TLS parameters support for OpenStack TLS parameters can be passed through the following environment variables: * OPENSTACK_CA_FILE - CA file path * OPENSTACK_CERT_FILE - cert file path * OPENSTACK_KEY_FILE - key file path. Either both or none of cert/key must be specified * OPENSTACK_TLS_SERVER_NAME - (optional) expected CN of the server certificate if differs from domain in the URL * OPENSTACK_TLS_INSECURE - if set to yes|true|1 disables validation of the server certificate Code that loads tls.Config from environment variables was extracted into dedicated package so that it could be reused by different providers --- pkg/tlsutils/tlsconfig.go | 85 +++++++++++++++++++++++++++++++++++++++ provider/designate.go | 23 +++++++++++ 2 files changed, 108 insertions(+) create mode 100644 pkg/tlsutils/tlsconfig.go diff --git a/pkg/tlsutils/tlsconfig.go b/pkg/tlsutils/tlsconfig.go new file mode 100644 index 000000000..d2e0fb741 --- /dev/null +++ b/pkg/tlsutils/tlsconfig.go @@ -0,0 +1,85 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tlsutils + +import ( + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "io/ioutil" + "os" + "strings" +) + +// CreateTLSConfig creates tls.Config instance from TLS parameters passed in environment variables with the given prefix +func CreateTLSConfig(prefix string) (*tls.Config, error) { + caFile := os.Getenv(fmt.Sprintf("%s_CA_FILE", prefix)) + certFile := os.Getenv(fmt.Sprintf("%s_CERT_FILE", prefix)) + keyFile := os.Getenv(fmt.Sprintf("%s_KEY_FILE", prefix)) + serverName := os.Getenv(fmt.Sprintf("%s_TLS_SERVER_NAME", prefix)) + isInsecureStr := strings.ToLower(os.Getenv(fmt.Sprintf("%s_TLS_INSECURE", prefix))) + isInsecure := isInsecureStr == "true" || isInsecureStr == "yes" || isInsecureStr == "1" + tlsConfig, err := newTLSConfig(certFile, keyFile, caFile, serverName, isInsecure) + if err != nil { + return nil, err + } + return tlsConfig, nil +} + +func newTLSConfig(certPath, keyPath, caPath, serverName string, insecure bool) (*tls.Config, error) { + if certPath != "" && keyPath == "" || certPath == "" && keyPath != "" { + return nil, errors.New("either both cert and key or none must be provided") + } + var certificates []tls.Certificate + if certPath != "" { + cert, err := tls.LoadX509KeyPair(certPath, keyPath) + if err != nil { + return nil, fmt.Errorf("could not load TLS cert: %s", err) + } + certificates = append(certificates, cert) + } + roots, err := loadRoots(caPath) + if err != nil { + return nil, err + } + + return &tls.Config{ + Certificates: certificates, + RootCAs: roots, + InsecureSkipVerify: insecure, + ServerName: serverName, + }, nil +} + +// loads CA cert +func loadRoots(caPath string) (*x509.CertPool, error) { + if caPath == "" { + return nil, nil + } + + roots := x509.NewCertPool() + pem, err := ioutil.ReadFile(caPath) + if err != nil { + return nil, fmt.Errorf("error reading %s: %s", caPath, err) + } + ok := roots.AppendCertsFromPEM(pem) + if !ok { + return nil, fmt.Errorf("could not read root certs: %s", err) + } + return roots, nil +} diff --git a/provider/designate.go b/provider/designate.go index 4f4a7a50b..3e0d5a3cb 100644 --- a/provider/designate.go +++ b/provider/designate.go @@ -18,8 +18,11 @@ package provider import ( "fmt" + "net" + "net/http" "os" "strings" + "time" log "github.com/Sirupsen/logrus" "github.com/gophercloud/gophercloud" @@ -29,6 +32,7 @@ import ( "github.com/gophercloud/gophercloud/pagination" "github.com/kubernetes-incubator/external-dns/endpoint" + "github.com/kubernetes-incubator/external-dns/pkg/tlsutils" "github.com/kubernetes-incubator/external-dns/plan" ) @@ -132,6 +136,25 @@ func createDesignateServiceClient() (*gophercloud.ServiceClient, error) { if err != nil { return nil, err } + + tlsConfig, err := tlsutils.CreateTLSConfig("OPENSTACK") + if err != nil { + return nil, err + } + + transport := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).DialContext, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + TLSClientConfig: tlsConfig, + } + client.ProviderClient.HTTPClient.Transport = transport log.Infof("Found OpenStack Designate service at %s", client.Endpoint) return client, nil }