From 97782685fe0e8896b77b9a2590a82f2e715dace3 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Fri, 9 Sep 2016 15:37:24 +0100 Subject: [PATCH 1/2] Reland: ciao-cli: Support specifying CA file in environment or command line With this change the CA certificate file used for communicating with keystone and the controller can be specified: a.) either on the command line with -ca-file, b.) or with an envionment variable, CIAO_CA_CERT_FILE, thus avoiding the need to import the certificate into your local store. This change required switching from openstack.AuthenticatedClient() to the lower level API in order to override the transport options on the HTTP client. Reland: Adjusted to merge the provided CA file with the system certificate pool (rather than use a new empty cert pool.) Fixes #180 Signed-off-by: Rob Bradford --- ciao-cli/README.md | 16 ++++++++++------ ciao-cli/identity.go | 17 ++++++++++++++++- ciao-cli/main.go | 28 ++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 7 deletions(-) diff --git a/ciao-cli/README.md b/ciao-cli/README.md index dd004a5a7..ac99cc451 100644 --- a/ciao-cli/README.md +++ b/ciao-cli/README.md @@ -19,6 +19,8 @@ The options are: -alsologtostderr log to standard error as well as files + -ca-file string + CA Certificate -computeport int Openstack Compute API port (default 8774) -controller string @@ -61,7 +63,7 @@ Use "ciao-cli command -help" for more information about that command. ## Ciao environment variables -ciao-cli first look for Ciao specific environment variables to retrieve +ciao-cli first looks for Ciao specific environment variables to retrieve credentials and networking information: * `CIAO_CONTROLLER` exports the Ciao controller URL @@ -70,6 +72,7 @@ credentials and networking information: * `CIAO_USERNAME` exports the Ciao username * `CIAO_PASSWORD` export the Ciao password for `CIAO_USERNAME` * `CIAO_TENANT_NAME` export the Ciao tenant name for `CIAO_USERNAME` +* `CIAO_CA_CERT_FILE` (optional) use the supplied certificate as the CA All those environment variables can be set through an rc file. For example: @@ -89,11 +92,9 @@ or overridden from the `ciao-cli` command line. ## Keystone certificates -ciao-cli interact with the CIAO keystone instance over HTTPS. -As such you will have to install the keystone CA certificate locally -in order for ciao-cli to verify the keystone identity. - -CA certificate installation is a distribution specific process. For example: +ciao-cli interacts with the CIAO keystone instance over HTTPS. As such you +will need to have the keystone CA certificate available in order to make +requests. You can either install the CA certificate system-wide: * On Fedora: ```shell @@ -107,6 +108,9 @@ sudo cp keystone_ca_cert.pem /usr/local/share/ca-certificates/keystone.crt sudo update-ca-certificates ``` +Or, alternatively the CA certificate may be specified with the `-ca-file` +command line or with the `CIAO_CA_CERT_FILE` environment variable. + ## Priviledged versus non priviledged CIAO users Administrators of a CIAO cluster are privileged users that are part of the diff --git a/ciao-cli/identity.go b/ciao-cli/identity.go index b7f70456f..b5a856e37 100644 --- a/ciao-cli/identity.go +++ b/ciao-cli/identity.go @@ -17,7 +17,9 @@ package main import ( + "crypto/tls" "fmt" + "net/http" "github.com/mitchellh/mapstructure" "github.com/rackspace/gophercloud" @@ -121,7 +123,20 @@ func getScopedToken(username string, password string, projectScope string) (stri AllowReauth: true, } - provider, err := openstack.AuthenticatedClient(opt) + provider, err := openstack.NewClient(opt.IdentityEndpoint) + if err != nil { + errorf("Could not get ProviderClient %s\n", err) + return "", "", "", nil + } + + if caCertPool != nil { + transport := &http.Transport{ + TLSClientConfig: &tls.Config{RootCAs: caCertPool}, + } + provider.HTTPClient.Transport = transport + } + + err = openstack.Authenticate(provider, opt) if err != nil { errorf("Could not get AuthenticatedClient %s\n", err) return "", "", "", nil diff --git a/ciao-cli/main.go b/ciao-cli/main.go index 4a8432afb..12817292f 100644 --- a/ciao-cli/main.go +++ b/ciao-cli/main.go @@ -19,6 +19,7 @@ package main import ( "bytes" "crypto/tls" + "crypto/x509" "encoding/json" "flag" "fmt" @@ -130,6 +131,7 @@ var ( tenantID = flag.String("tenant-id", "", "Tenant UUID") tenantName = flag.String("tenant-name", "", "Tenant name") computePort = flag.Int("computeport", openstackComputePort, "Openstack Compute API port") + caCertFile = flag.String("ca-file", "", "CA Certificate") ) const ( @@ -139,8 +141,11 @@ const ( ciaoPasswordEnv = "CIAO_PASSWORD" ciaoComputePortEnv = "CIAO_COMPUTEPORT" ciaoTenantNameEnv = "CIAO_TENANT_NAME" + ciaoCACertFileEnv = "CIAO_CA_CERT_FILE" ) +var caCertPool *x509.CertPool + type queryValue struct { name, value string } @@ -198,6 +203,10 @@ func sendHTTPRequestToken(method string, url string, values []queryValue, token warningf("Skipping TLS verification\n") tlsConfig := &tls.Config{InsecureSkipVerify: true} + if caCertPool != nil { + tlsConfig.RootCAs = caCertPool + } + transport := &http.Transport{ TLSClientConfig: tlsConfig, } @@ -266,6 +275,7 @@ func getCiaoEnvVariables() { password := os.Getenv(ciaoPasswordEnv) port := os.Getenv(ciaoComputePortEnv) tenant := os.Getenv(ciaoTenantNameEnv) + ca := os.Getenv(ciaoCACertFileEnv) infof("Ciao environment variables:\n") infof("\t%s:%s\n", ciaoIdentityEnv, identity) @@ -274,6 +284,7 @@ func getCiaoEnvVariables() { infof("\t%s:%s\n", ciaoPasswordEnv, password) infof("\t%s:%s\n", ciaoComputePortEnv, port) infof("\t%s:%s\n", ciaoTenantNameEnv, tenantName) + infof("\t%s:%s\n", ciaoCACertFileEnv, ca) if identity != "" && *identityURL == "" { *identityURL = identity @@ -298,6 +309,10 @@ func getCiaoEnvVariables() { if tenant != "" && *tenantName == "" { *tenantName = tenant } + + if ca != "" && *caCertFile == "" { + *caCertFile = ca + } } func checkCompulsoryOptions() { @@ -339,6 +354,19 @@ func main() { usage() } + /* Load CA file if necessary */ + if *caCertFile != "" { + caCert, err := ioutil.ReadFile(*caCertFile) + if err != nil { + fatalf("Unable to load requested CA certificate: %s\n", err) + } + caCertPool, err = x509.SystemCertPool() + if err != nil { + fatalf("Unable to create system certificate pool: %s\n", err) + } + caCertPool.AppendCertsFromPEM(caCert) + } + /* If we're missing the tenant name let's try to fetch one */ if *tenantName == "" { *tenantName, *tenantID, err = getTenant(*identityUser, *identityPassword, *tenantID) From 53cf7ade299d37d868b02c1033342c14d0dc146c Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Tue, 20 Sep 2016 15:20:44 +0100 Subject: [PATCH 2/2] Reland: ciao-cli: Enable TLS verification when communicating with controller Previously the configuration of the "singlevm" test method prevented this from being enabled. Now this is resolved TLS can be enabled between the cli and controller. Reland: Can now be relanded now that the command like CA is merged with the system CA pool. Fixes: #544 Signed-off-by: Rob Bradford --- ciao-cli/main.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ciao-cli/main.go b/ciao-cli/main.go index 12817292f..958dfaaf3 100644 --- a/ciao-cli/main.go +++ b/ciao-cli/main.go @@ -200,8 +200,7 @@ func sendHTTPRequestToken(method string, url string, values []queryValue, token req.Header.Set("Accept", "application/json") } - warningf("Skipping TLS verification\n") - tlsConfig := &tls.Config{InsecureSkipVerify: true} + tlsConfig := &tls.Config{} if caCertPool != nil { tlsConfig.RootCAs = caCertPool