Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 56 additions & 39 deletions openstack/clientconfig/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package clientconfig

import (
"context"
"crypto/tls"
"errors"
"fmt"
"net/http"
"os"
"reflect"
"strconv"
"strings"

"github.com/gophercloud/gophercloud/v2"
Expand Down Expand Up @@ -737,43 +739,10 @@ func AuthenticatedClient(ctx context.Context, opts *ClientOpts) (*gophercloud.Pr
return openstack.AuthenticatedClient(ctx, *ao)
}

// NewServiceClient is a convenience function to get a new service client.
func NewServiceClient(ctx context.Context, service string, opts *ClientOpts) (*gophercloud.ServiceClient, error) {
cloud := new(Cloud)

// If no opts were passed in, create an empty ClientOpts.
if opts == nil {
opts = new(ClientOpts)
}

// Determine if a clouds.yaml entry should be retrieved.
// Start by figuring out the cloud name.
// First check if one was explicitly specified in opts.
var cloudName string
if opts.Cloud != "" {
cloudName = opts.Cloud
}

// Next see if a cloud name was specified as an environment variable.
envPrefix := "OS_"
if opts.EnvPrefix != "" {
envPrefix = opts.EnvPrefix
}

if v := env.Getenv(envPrefix + "CLOUD"); v != "" {
cloudName = v
}

// If a cloud name was determined, try to look it up in clouds.yaml.
if cloudName != "" {
// Get the requested cloud.
var err error
cloud, err = GetCloudFromYAML(opts)
if err != nil {
return nil, err
}
}

// PrepareTLSConfig builds a *tls.Config from environment variables and cloud
// configuration. Environment variables are checked first; cloud entry values
// override if set.
func PrepareTLSConfig(envPrefix string, cloud *Cloud) (*tls.Config, error) {
// Check if a custom CA cert was provided.
// First, check if the CACERT environment variable is set.
var caCertPath string
Expand Down Expand Up @@ -808,14 +777,62 @@ func NewServiceClient(ctx context.Context, service string, opts *ClientOpts) (*g
}

// Define whether or not SSL API requests should be verified.
// First, check if the INSECURE environment variable is set.
var insecurePtr *bool
if v := env.Getenv(envPrefix + "INSECURE"); v != "" {
insecure, err := strconv.ParseBool(v)
if err != nil {
return nil, fmt.Errorf("failed to parse %sINSECURE: %w", envPrefix, err)
}
insecurePtr = &insecure
}
// Next, check if the cloud entry sets verify (inverted to insecure).
if cloud.Verify != nil {
// Here we take the boolean pointer negation.
insecure := !*cloud.Verify
insecurePtr = &insecure
}
Comment on lines 811 to 825
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this might backwards: normally env vars take priority over file-based (clouds.yaml) configuration. What does openstacksdk do here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just following the existing logic in the code; e.g., the following that handles OS_CACERT:

	// Check if a custom CA cert was provided.
	// First, check if the CACERT environment variable is set.
	var caCertPath string
	if v := env.Getenv(envPrefix + "CACERT"); v != "" {
		caCertPath = v
	}
	// Next, check if the cloud entry sets a CA cert.
	if v := cloud.CACertFile; v != "" {
		caCertPath = v
	}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, that's backwards from how I'd have expected this to work but at least it's consistent. Thanks for the context.


tlsConfig, err := internal.PrepareTLSConfig(caCertPath, clientCertPath, clientKeyPath, insecurePtr)
return internal.PrepareTLSConfig(caCertPath, clientCertPath, clientKeyPath, insecurePtr)
}

// NewServiceClient is a convenience function to get a new service client.
func NewServiceClient(ctx context.Context, service string, opts *ClientOpts) (*gophercloud.ServiceClient, error) {
cloud := new(Cloud)

// If no opts were passed in, create an empty ClientOpts.
if opts == nil {
opts = new(ClientOpts)
}

// Determine if a clouds.yaml entry should be retrieved.
// Start by figuring out the cloud name.
// First check if one was explicitly specified in opts.
var cloudName string
if opts.Cloud != "" {
cloudName = opts.Cloud
}

// Next see if a cloud name was specified as an environment variable.
envPrefix := "OS_"
if opts.EnvPrefix != "" {
envPrefix = opts.EnvPrefix
}

if v := env.Getenv(envPrefix + "CLOUD"); v != "" {
cloudName = v
}

// If a cloud name was determined, try to look it up in clouds.yaml.
if cloudName != "" {
// Get the requested cloud.
var err error
cloud, err = GetCloudFromYAML(opts)
if err != nil {
return nil, err
}
}

tlsConfig, err := PrepareTLSConfig(envPrefix, cloud)
if err != nil {
return nil, err
}
Expand Down
65 changes: 65 additions & 0 deletions openstack/clientconfig/testing/tls_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package testing

import (
"os"
"testing"

"github.com/gophercloud/utils/v2/openstack/clientconfig"

th "github.com/gophercloud/gophercloud/v2/testhelper"
)

func TestPrepareTLSConfigInsecureEnv(t *testing.T) {
t.Run("OS_INSECURE=true", func(t *testing.T) {
os.Setenv("OS_INSECURE", "true")
defer os.Unsetenv("OS_INSECURE")

tlsConfig, err := clientconfig.PrepareTLSConfig("OS_", &clientconfig.Cloud{})
th.AssertNoErr(t, err)
th.AssertEquals(t, true, tlsConfig.InsecureSkipVerify)
})

t.Run("OS_INSECURE=false", func(t *testing.T) {
os.Setenv("OS_INSECURE", "false")
defer os.Unsetenv("OS_INSECURE")

tlsConfig, err := clientconfig.PrepareTLSConfig("OS_", &clientconfig.Cloud{})
th.AssertNoErr(t, err)
th.AssertEquals(t, false, tlsConfig.InsecureSkipVerify)
})

t.Run("OS_INSECURE unset", func(t *testing.T) {
os.Unsetenv("OS_INSECURE")

tlsConfig, err := clientconfig.PrepareTLSConfig("OS_", &clientconfig.Cloud{})
th.AssertNoErr(t, err)
th.AssertEquals(t, false, tlsConfig.InsecureSkipVerify)
})

t.Run("OS_INSECURE=invalid", func(t *testing.T) {
os.Setenv("OS_INSECURE", "invalid")
defer os.Unsetenv("OS_INSECURE")

_, err := clientconfig.PrepareTLSConfig("OS_", &clientconfig.Cloud{})
th.AssertErr(t, err)
})

t.Run("cloud.Verify overrides OS_INSECURE", func(t *testing.T) {
os.Setenv("OS_INSECURE", "true")
defer os.Unsetenv("OS_INSECURE")

cloud := &clientconfig.Cloud{Verify: &iTrue}
tlsConfig, err := clientconfig.PrepareTLSConfig("OS_", cloud)
th.AssertNoErr(t, err)
th.AssertEquals(t, false, tlsConfig.InsecureSkipVerify)
})

t.Run("custom env prefix", func(t *testing.T) {
os.Setenv("FOO_INSECURE", "true")
defer os.Unsetenv("FOO_INSECURE")

tlsConfig, err := clientconfig.PrepareTLSConfig("FOO_", &clientconfig.Cloud{})
th.AssertNoErr(t, err)
th.AssertEquals(t, true, tlsConfig.InsecureSkipVerify)
})
}