diff --git a/builder/openstack/access_config.go b/builder/openstack/access_config.go index 10b4fd02e..5d9c2b39b 100644 --- a/builder/openstack/access_config.go +++ b/builder/openstack/access_config.go @@ -3,11 +3,14 @@ package openstack import ( "crypto/tls" "fmt" - "net/http" "os" + "crypto/x509" + "io/ioutil" + "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" + "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/packer/template/interpolate" ) @@ -24,6 +27,9 @@ type AccessConfig struct { Insecure bool `mapstructure:"insecure"` Region string `mapstructure:"region"` EndpointType string `mapstructure:"endpoint_type"` + CACertFile string `mapstructure:"cacert"` + ClientCertFile string `mapstructure:"cert"` + ClientKeyFile string `mapstructure:"key"` osClient *gophercloud.ProviderClient } @@ -53,6 +59,15 @@ func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error { if c.Username == "" { c.Username = os.Getenv("SDK_USERNAME") } + if c.CACertFile == "" { + c.CACertFile = os.Getenv("OS_CACERT") + } + if c.ClientCertFile == "" { + c.ClientCertFile = os.Getenv("OS_CERT") + } + if c.ClientKeyFile == "" { + c.ClientKeyFile = os.Getenv("OS_KEY") + } // Get as much as possible from the end ao, _ := openstack.AuthOptionsFromEnv() @@ -85,14 +100,37 @@ func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error { return []error{err} } + tls_config := &tls.Config{} + + if c.CACertFile != "" { + caCert, err := ioutil.ReadFile(c.CACertFile) + if err != nil { + return []error{err} + } + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + tls_config.RootCAs = caCertPool + } + // If we have insecure set, then create a custom HTTP client that // ignores SSL errors. if c.Insecure { - config := &tls.Config{InsecureSkipVerify: true} - transport := &http.Transport{TLSClientConfig: config} - client.HTTPClient.Transport = transport + tls_config.InsecureSkipVerify = true + } + + if c.ClientCertFile != "" && c.ClientKeyFile != "" { + cert, err := tls.LoadX509KeyPair(c.ClientCertFile, c.ClientKeyFile) + if err != nil { + return []error{err} + } + + tls_config.Certificates = []tls.Certificate{cert} } + transport := cleanhttp.DefaultTransport() + transport.TLSClientConfig = tls_config + client.HTTPClient.Transport = transport + // Auth err = openstack.Authenticate(client, ao) if err != nil { diff --git a/website/source/docs/builders/openstack.html.md b/website/source/docs/builders/openstack.html.md index 85c60d797..f042921f5 100644 --- a/website/source/docs/builders/openstack.html.md +++ b/website/source/docs/builders/openstack.html.md @@ -76,9 +76,15 @@ builder. server in. If this isn't specified, the default enforced by your OpenStack cluster will be used. This may be required for some OpenStack clusters. +- `cacert` (string) - Custom CA certificate file path. + If ommited the OS_CACERT environment variable can be used. + - `config_drive` (boolean) - Whether or not nova should use ConfigDrive for cloud-init metadata. +- `cert` (string) - Client certificate file path for SSL client authentication. + If omitted the OS_CERT environment variable can be used. + - `domain_name` or `domain_id` (string) - The Domain name or ID you are authenticating with. OpenStack installations require this if identity v3 is used. Packer will use the environment variable `OS_DOMAIN_NAME` or `OS_DOMAIN_ID`, if set. @@ -102,6 +108,9 @@ builder. - `insecure` (boolean) - Whether or not the connection to OpenStack can be done over an insecure connection. By default this is false. +- `key` (string) - Client private key file path for SSL client authentication. + If ommited the OS_KEY environment variable can be used. + - `metadata` (object of key/value strings) - Glance metadata that will be applied to the image.