From f550b6832fcf74364e9bf267f57ef29ed87091c6 Mon Sep 17 00:00:00 2001 From: saikiranakula-amzn Date: Mon, 24 Oct 2022 23:09:18 +0000 Subject: [PATCH] gMSA support on Linux - capability advertisement *changes to ecs-init to establish communication of agent with credentials-fetcher daemon *changes to agent to advertise gMSA capability on Linux --- README.md | 1 + agent/app/agent_capability.go | 8 +++ agent/app/agent_capability_test.go | 26 +++++++++ agent/app/agent_capability_unix.go | 4 -- agent/app/agent_capability_unix_test.go | 10 ---- agent/app/agent_capability_windows.go | 8 --- agent/app/agent_capability_windows_test.go | 26 --------- agent/config/config_unix.go | 2 +- agent/config/parse.go | 7 +++ agent/config/parse_linux.go | 35 +++++++++++- agent/config/parse_linux_test.go | 51 +++++++++++++++++ agent/config/parse_windows.go | 5 -- ecs-init/config/common.go | 28 +++++++++ ecs-init/config/common_test.go | 18 ++++++ ecs-init/docker/docker.go | 66 +++++++++++++++++++++- ecs-init/docker/docker_test.go | 50 ++++++++++++++++ 16 files changed, 289 insertions(+), 56 deletions(-) create mode 100644 agent/config/parse_linux_test.go diff --git a/README.md b/README.md index ac2611534fc..50566c69934 100644 --- a/README.md +++ b/README.md @@ -203,6 +203,7 @@ additional details on each available environment variable. | `ECS_ENABLE_GPU_SUPPORT` | `true` | Whether you use container instances with GPU support. This parameter is specified for the agent. You must also configure your task definitions for GPU. For more information | `false` | `Not applicable` | | `HTTP_PROXY` | `10.0.0.131:3128` | The hostname (or IP address) and port number of an HTTP proxy to use for the Amazon ECS agent to connect to the internet. For example, this proxy will be used if your container instances do not have external network access through an Amazon VPC internet gateway or NAT gateway or instance. If this variable is set, you must also set the NO_PROXY variable to filter Amazon EC2 instance metadata and Docker daemon traffic from the proxy. | `null` | `null` | | `NO_PROXY` | | The HTTP traffic that should not be forwarded to the specified HTTP_PROXY. You must specify 169.254.169.254,/var/run/docker.sock to filter Amazon EC2 instance metadata and Docker daemon traffic from the proxy. | `null` | `null` | +| `CREDENTIALS_FETCHER_HOST` | `unix:///var/credentials-fetcher/socket/credentials_fetcher.sock` | Used to create a connection to the [credentials-fetcher daemon](https://github.com/aws/credentials-fetcher); to support gMSA on Linux. The default is fine for most users, only needs to be modified if user is configuring a custom credentials-fetcher socket path, ie, [CF_UNIX_DOMAIN_SOCKET_DIR](https://github.com/aws/credentials-fetcher#default-environment-variables). | `unix:///var/credentials-fetcher/socket/credentials_fetcher.sock` | Not Applicable | ### Persistence diff --git a/agent/app/agent_capability.go b/agent/app/agent_capability.go index 141cd40a343..601879cdf24 100644 --- a/agent/app/agent_capability.go +++ b/agent/app/agent_capability.go @@ -304,6 +304,14 @@ func (agent *ecsAgent) appendDockerDependentCapabilities(capabilities []*ecs.Att return capabilities } +func (agent *ecsAgent) appendGMSACapabilities(capabilities []*ecs.Attribute) []*ecs.Attribute { + if agent.cfg.GMSACapable { + return appendNameOnlyAttribute(capabilities, attributePrefix+capabilityGMSA) + } + + return capabilities +} + func (agent *ecsAgent) appendLoggingDriverCapabilities(capabilities []*ecs.Attribute) []*ecs.Attribute { knownVersions := make(map[dockerclient.DockerVersion]struct{}) // Determine known API versions. Known versions are used exclusively for logging-driver enablement, since none of diff --git a/agent/app/agent_capability_test.go b/agent/app/agent_capability_test.go index 700053c49eb..657ab3ccec5 100644 --- a/agent/app/agent_capability_test.go +++ b/agent/app/agent_capability_test.go @@ -1416,3 +1416,29 @@ func TestAppendAndRemoveAttributes(t *testing.T) { Name: aws.String("cap-2"), }) } + +func TestAppendGMSACapabilities(t *testing.T) { + var inputCapabilities []*ecs.Attribute + var expectedCapabilities []*ecs.Attribute + + expectedCapabilities = append(expectedCapabilities, + []*ecs.Attribute{ + { + Name: aws.String(attributePrefix + capabilityGMSA), + }, + }...) + + agent := &ecsAgent{ + cfg: &config.Config{ + GMSACapable: true, + }, + } + + capabilities := agent.appendGMSACapabilities(inputCapabilities) + + assert.Equal(t, len(expectedCapabilities), len(capabilities)) + for i, expected := range expectedCapabilities { + assert.Equal(t, aws.StringValue(expected.Name), aws.StringValue(capabilities[i].Name)) + assert.Equal(t, aws.StringValue(expected.Value), aws.StringValue(capabilities[i].Value)) + } +} diff --git a/agent/app/agent_capability_unix.go b/agent/app/agent_capability_unix.go index f7c2b318f5c..51b43935dba 100644 --- a/agent/app/agent_capability_unix.go +++ b/agent/app/agent_capability_unix.go @@ -200,10 +200,6 @@ func (agent *ecsAgent) appendFirelensConfigCapabilities(capabilities []*ecs.Attr return appendNameOnlyAttribute(capabilities, attributePrefix+capabilityFirelensConfigS3) } -func (agent *ecsAgent) appendGMSACapabilities(capabilities []*ecs.Attribute) []*ecs.Attribute { - return capabilities -} - func (agent *ecsAgent) appendIPv6Capability(capabilities []*ecs.Attribute) []*ecs.Attribute { return appendNameOnlyAttribute(capabilities, attributePrefix+taskENIIPv6AttributeSuffix) } diff --git a/agent/app/agent_capability_unix_test.go b/agent/app/agent_capability_unix_test.go index 956d386e160..0faf8905f42 100644 --- a/agent/app/agent_capability_unix_test.go +++ b/agent/app/agent_capability_unix_test.go @@ -913,16 +913,6 @@ func TestFirelensConfigCapabilitiesUnix(t *testing.T) { assert.Contains(t, capabilities, &ecs.Attribute{Name: aws.String(attributePrefix + capabilityFirelensConfigS3)}) } -func TestAppendGMSACapabilities(t *testing.T) { - var inputCapabilities []*ecs.Attribute - - agent := &ecsAgent{} - - capabilities := agent.appendGMSACapabilities(inputCapabilities) - assert.Equal(t, len(inputCapabilities), len(capabilities)) - assert.EqualValues(t, capabilities, inputCapabilities) -} - func TestAppendFSxWindowsFileServerCapabilities(t *testing.T) { var inputCapabilities []*ecs.Attribute diff --git a/agent/app/agent_capability_windows.go b/agent/app/agent_capability_windows.go index 749b0c3a6a5..77f34138e4b 100644 --- a/agent/app/agent_capability_windows.go +++ b/agent/app/agent_capability_windows.go @@ -100,14 +100,6 @@ func (agent *ecsAgent) appendFirelensConfigCapabilities(capabilities []*ecs.Attr return capabilities } -func (agent *ecsAgent) appendGMSACapabilities(capabilities []*ecs.Attribute) []*ecs.Attribute { - if agent.cfg.GMSACapable { - return appendNameOnlyAttribute(capabilities, attributePrefix+capabilityGMSA) - } - - return capabilities -} - func (agent *ecsAgent) appendEFSVolumePluginCapabilities(capabilities []*ecs.Attribute, pluginCapability string) []*ecs.Attribute { return capabilities } diff --git a/agent/app/agent_capability_windows_test.go b/agent/app/agent_capability_windows_test.go index 46c98cd592d..63925124929 100644 --- a/agent/app/agent_capability_windows_test.go +++ b/agent/app/agent_capability_windows_test.go @@ -225,32 +225,6 @@ func TestSupportedCapabilitiesWindows(t *testing.T) { } } -func TestAppendGMSACapabilities(t *testing.T) { - var inputCapabilities []*ecs.Attribute - var expectedCapabilities []*ecs.Attribute - - expectedCapabilities = append(expectedCapabilities, - []*ecs.Attribute{ - { - Name: aws.String(attributePrefix + capabilityGMSA), - }, - }...) - - agent := &ecsAgent{ - cfg: &config.Config{ - GMSACapable: true, - }, - } - - capabilities := agent.appendGMSACapabilities(inputCapabilities) - - assert.Equal(t, len(expectedCapabilities), len(capabilities)) - for i, expected := range expectedCapabilities { - assert.Equal(t, aws.StringValue(expected.Name), aws.StringValue(capabilities[i].Name)) - assert.Equal(t, aws.StringValue(expected.Value), aws.StringValue(capabilities[i].Value)) - } -} - func TestAppendGMSACapabilitiesFalse(t *testing.T) { var inputCapabilities []*ecs.Attribute var expectedCapabilities []*ecs.Attribute diff --git a/agent/config/config_unix.go b/agent/config/config_unix.go index 8f6b8019609..8886a92ce31 100644 --- a/agent/config/config_unix.go +++ b/agent/config/config_unix.go @@ -100,7 +100,7 @@ func DefaultConfig() Config { PollingMetricsWaitDuration: DefaultPollingMetricsWaitDuration, NvidiaRuntime: DefaultNvidiaRuntime, CgroupCPUPeriod: defaultCgroupCPUPeriod, - GMSACapable: false, + GMSACapable: parseGMSACapability(), FSxWindowsFileServerCapable: false, RuntimeStatsLogFile: defaultRuntimeStatsLogFile, EnableRuntimeStats: BooleanDefaultFalse{Value: NotSet}, diff --git a/agent/config/parse.go b/agent/config/parse.go index ba37b28a926..f8f018f3d41 100644 --- a/agent/config/parse.go +++ b/agent/config/parse.go @@ -27,6 +27,13 @@ import ( cnitypes "github.com/containernetworking/cni/pkg/types" ) +const ( + // envSkipDomainJoinCheck is an environment setting that can be used to skip + // domain join check validation. This is useful for integration and + // functional-tests but should not be set for any non-test use-case. + envSkipDomainJoinCheck = "ZZZ_SKIP_DOMAIN_JOIN_CHECK_NOT_SUPPORTED_IN_PRODUCTION" +) + func parseCheckpoint(dataDir string) BooleanDefaultFalse { checkPoint := parseBooleanDefaultFalseConfig("ECS_CHECKPOINT") if dataDir != "" { diff --git a/agent/config/parse_linux.go b/agent/config/parse_linux.go index 5ab45d3cf1e..71f542c2014 100644 --- a/agent/config/parse_linux.go +++ b/agent/config/parse_linux.go @@ -7,7 +7,7 @@ // not use this file except in compliance with the License. A copy of the // License is located at // -// httpaws.amazon.com/apache2.0/ +// http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either @@ -18,10 +18,43 @@ package config import ( "errors" + "os" "strings" + + "github.com/aws/amazon-ecs-agent/agent/utils" + "github.com/cihub/seelog" ) func parseGMSACapability() bool { + envStatus := utils.ParseBool(os.Getenv("ECS_GMSA_SUPPORTED"), true) + if envStatus { + // Check if domain join check override is present + skipDomainJoinCheck := utils.ParseBool(os.Getenv(envSkipDomainJoinCheck), false) + if skipDomainJoinCheck { + seelog.Infof("Skipping domain join validation based on environment override") + return true + } + + // check if the credentials fetcher socket is created and exists + // this env variable is set in ecs-init module + if credentialsfetcherHostDir := os.Getenv("CREDENTIALS_FETCHER_HOST_DIR"); credentialsfetcherHostDir != "" { + _, err := os.Stat(credentialsfetcherHostDir) + if err != nil { + if os.IsNotExist(err) { + seelog.Errorf("CREDENTIALS_FETCHER_HOST_DIR not found, was looking for %v", err) + return false + } + } + // returns true if the container instance is domain joined + // this env variable is set in ecs-init module + isDomainJoined := utils.ParseBool(os.Getenv("ECS_DOMAIN_JOINED_LINUX_INSTANCE"), false) + + if !isDomainJoined { + seelog.Error("gMSA on linux requires domain joined instance. Did not find expected env var ECS_DOMAIN_JOINED_LINUX_INSTANCE=true") + } + return isDomainJoined + } + } return false } diff --git a/agent/config/parse_linux_test.go b/agent/config/parse_linux_test.go new file mode 100644 index 00000000000..cabc2596195 --- /dev/null +++ b/agent/config/parse_linux_test.go @@ -0,0 +1,51 @@ +//go:build linux && unit +// +build linux,unit + +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file 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 config + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseGMSACapabilitySupported(t *testing.T) { + t.Setenv("ECS_GMSA_SUPPORTED", "True") + t.Setenv("ECS_DOMAIN_JOINED_LINUX_INSTANCE", "True") + t.Setenv("CREDENTIALS_FETCHER_HOST_DIR", "/var/run") + + assert.True(t, parseGMSACapability()) +} + +func TestParseGMSACapabilityNonDomainJoined(t *testing.T) { + t.Setenv("ECS_GMSA_SUPPORTED", "True") + t.Setenv("ECS_DOMAIN_JOINED_LINUX_INSTANCE", "False") + + assert.False(t, parseGMSACapability()) +} + +func TestParseGMSACapabilityUnsupported(t *testing.T) { + t.Setenv("ECS_GMSA_SUPPORTED", "False") + + assert.False(t, parseGMSACapability()) +} + +func TestSkipDomainJoinCheckParseGMSACapability(t *testing.T) { + t.Setenv("ECS_GMSA_SUPPORTED", "True") + t.Setenv("ZZZ_SKIP_DOMAIN_JOIN_CHECK_NOT_SUPPORTED_IN_PRODUCTION", "True") + + assert.True(t, parseGMSACapability()) +} diff --git a/agent/config/parse_windows.go b/agent/config/parse_windows.go index 6040cb5d27c..c9c6dec403b 100644 --- a/agent/config/parse_windows.go +++ b/agent/config/parse_windows.go @@ -28,11 +28,6 @@ import ( ) const ( - // envSkipDomainJoinCheck is an environment setting that can be used to skip - // domain join check validation. This is useful for integration and - // functional-tests but should not be set for any non-test use-case. - envSkipDomainJoinCheck = "ZZZ_SKIP_DOMAIN_JOIN_CHECK_NOT_SUPPORTED_IN_PRODUCTION" - // envSkipWindowsServerVersionCheck is an environment setting that can be used // to skip the windows server version check. This is useful for testing and // should not be set for any non-test use-case. diff --git a/ecs-init/config/common.go b/ecs-init/config/common.go index d0acc580be2..746e5e894a6 100644 --- a/ecs-init/config/common.go +++ b/ecs-init/config/common.go @@ -93,6 +93,17 @@ const ( // DefaultRegionEnvVar is the environment variable for specifying the default AWS region to use. DefaultRegionEnvVar = "AWS_DEFAULT_REGION" + + // ECSGMSASupportEnvVar indicates that the gMSA is supported + ECSGMSASupportEnvVar = "ECS_GMSA_SUPPORTED" + + // CredentialsFetcherHostEnvVar is the environment variable that specifies the location of the credentials-fetcher daemon socket. + CredentialsFetcherHostEnvVar = "CREDENTIALS_FETCHER_HOST" + + // this socket is exposed by credentials-fetcher (daemon for gMSA support on Linux) + // defaultCredentialsFetcherSocketPath is set to /var/credentials-fetcher/socket/credentials_fetcher.sock + // in case path is not passed in the env variable + DefaultCredentialsFetcherSocketPath = "/var/credentials-fetcher/socket/credentials_fetcher.sock" ) // partitionBucketRegion provides the "partitional" bucket region @@ -212,6 +223,23 @@ func DockerUnixSocket() (string, bool) { return "/var/run", false } +// CredentialsFetcherUnixSocketHostPath returns the credentials fetcher daemon socket endpoint and whether it reads from CredentialsFetcherEnvVar +func CredentialsFetcherUnixSocket() string { + if credentialsFetcherHost := os.Getenv(CredentialsFetcherHostEnvVar); strings.HasPrefix(credentialsFetcherHost, UnixSocketPrefix) { + return strings.TrimPrefix(credentialsFetcherHost, UnixSocketPrefix) + } + + return DefaultCredentialsFetcherSocketPath +} + +// HostCredentialsFetcherPath() returns the daemon socket location if it is available +func HostCredentialsFetcherPath() (string, bool) { + if credentialsFetcherHost := CredentialsFetcherUnixSocket(); len(credentialsFetcherHost) > 0 { + return credentialsFetcherHost, true + } + return "", false +} + // CgroupMountpoint returns the cgroup mountpoint for the system func CgroupMountpoint() string { return cgroupMountpoint diff --git a/ecs-init/config/common_test.go b/ecs-init/config/common_test.go index a86ae325d8d..658c4448b5c 100644 --- a/ecs-init/config/common_test.go +++ b/ecs-init/config/common_test.go @@ -253,3 +253,21 @@ func TestAgentRunningInExternal(t *testing.T) { defer os.Unsetenv(ExternalEnvVar) assert.True(t, RunningInExternal()) } + +func TestCredentialsFetcherUnixSocketWithoutCredentialsFetcherHost(t *testing.T) { + credentialsFetcherUnixSocketSourcePath := CredentialsFetcherUnixSocket() + + if credentialsFetcherUnixSocketSourcePath != "/var/credentials-fetcher/socket/credentials_fetcher.sock" { + t.Error("CredentialsFetcherUnixSocket() should be \"/var/credentials-fetcher/socket\"") + } + +} + +func TestCredentialsFetcherUnixSocketWithCredentialsFetcherHost(t *testing.T) { + t.Setenv("CREDENTIALS_FETCHER_HOST", "unix:///foo/bar") + + credentialsFetcherUnixSocketSourcePath := CredentialsFetcherUnixSocket() + if credentialsFetcherUnixSocketSourcePath != "/foo/bar" { + t.Error("CredentialsFetcherUnixSocket() should be \"/foo/bar\"") + } +} diff --git a/ecs-init/docker/docker.go b/ecs-init/docker/docker.go index f43e54bfcb1..ced28c5bdbd 100644 --- a/ecs-init/docker/docker.go +++ b/ecs-init/docker/docker.go @@ -18,6 +18,7 @@ import ( "encoding/json" "io" "os" + "os/exec" "path/filepath" "strings" "sync" @@ -49,7 +50,6 @@ const ( // in case /var/run/docker.sock is deleted and recreated outside the container defaultDockerEndpoint = "/var/run" defaultDockerSocketPath = "/var/run/docker.sock" - // networkMode specifies the networkmode to create the agent container networkMode = "host" // usernsMode specifies the userns mode to create the agent container @@ -298,11 +298,22 @@ func (c *client) getContainerConfig(envVarsFromFiles map[string]string) *godocke envVariables["SSL_CERT_DIR"] = certDir } + // env variable is only available if the gmsa is enabled on linux + credentialsFetcherHost, ok := config.HostCredentialsFetcherPath() + if ok && credentialsFetcherHost != "" { + envVariables["CREDENTIALS_FETCHER_HOST_DIR"] = credentialsFetcherHost + } + // merge in platform-specific environment variables for envKey, envValue := range getPlatformSpecificEnvVariables() { envVariables[envKey] = envValue } + if isDomainJoined() { + // set the environment variable to true if the container instance is domain joined + envVariables["ECS_DOMAIN_JOINED_LINUX_INSTANCE"] = "true" + } + for key, val := range envVarsFromFiles { envVariables[key] = val } @@ -430,6 +441,14 @@ func (c *client) getHostConfig(envVarsFromFiles map[string]string) *godocker.Hos binds = append(binds, gpu.GPUInfoDirPath+":"+gpu.GPUInfoDirPath) } } + + if key == config.ECSGMSASupportEnvVar && val == "true" { + // only bind if the env variable gmsa support is set to true and the path to bind exists + credentialsfetcherSocketBind, volumeExists := getCredentialsFetcherSocketBind() + if volumeExists { + binds = append(binds, credentialsfetcherSocketBind) + } + } } binds = append(binds, getDockerPluginDirBinds()...) @@ -440,6 +459,22 @@ func (c *client) getHostConfig(envVarsFromFiles map[string]string) *godocker.Hos return createHostConfig(binds) } +// getCredentialsFetcherSocketBind returns the corresponding bind for credentials fetcher socket. +func getCredentialsFetcherSocketBind() (string, bool) { + CredentialsFetcherUnixSocketHostPath := config.CredentialsFetcherUnixSocket() + CredentialsFetcherUnixSocketAgentPath := CredentialsFetcherUnixSocketHostPath + + // check whether the path to the credentials fetcher socket exists + _, err := os.Stat(CredentialsFetcherUnixSocketHostPath) + if err != nil { + if os.IsNotExist(err) { + return "", false + } + } + + return CredentialsFetcherUnixSocketHostPath + ":" + CredentialsFetcherUnixSocketAgentPath, true +} + // getDockerSocketBind returns the bind for Docker socket. // Value for the bind is as follow: // 1. DOCKER_HOST (as in os.Getenv) not set: source /var/run, dest /var/run @@ -541,3 +576,32 @@ func (c *client) StopAgent() error { } return err } + +// isDomainJoined is used to validate if container instance is part of a valid active directory. +func isDomainJoined() bool { + realmPath, err := exec.LookPath("realm") + if err != nil { + log.Errorf("realmd is not installed on the instance") + return false + } + + if len(realmPath) == 0 { + log.Errorf("could not path of realm on the instance") + return false + } + + realmCommmand := realmPath + " list" + realmCmdOutput, err := exec.Command("bash", "-c", realmCommmand).Output() + if err != nil { + log.Errorf("failed to read realm info") + return false + } + + realmOutputStr := strings.TrimSpace(string(realmCmdOutput)) + if !strings.Contains(realmOutputStr, "realm-name") { + log.Errorf("couldn't not find realm name") + return false + } + + return true +} diff --git a/ecs-init/docker/docker_test.go b/ecs-init/docker/docker_test.go index 73ecb1b0ea2..f37d11079a4 100644 --- a/ecs-init/docker/docker_test.go +++ b/ecs-init/docker/docker_test.go @@ -985,3 +985,53 @@ func TestDefaultIsPathValid(t *testing.T) { }) } } + +func TestGetCredentialsFetcherSocketBind(t *testing.T) { + testCases := []struct { + name string + credentialsFetcherHostFromEnv string + credentialsFetcherHostFromConfigFile string + expectedBind string + }{ + { + name: "No Credentials Fetcher host from env", + credentialsFetcherHostFromEnv: "", + credentialsFetcherHostFromConfigFile: "dummy", + expectedBind: "/var/credentials-fetcher/socket/credentials_fetcher.sock:/var/credentials-fetcher/socket/credentials_fetcher.sock", + }, + { + name: "Invalid Credentials Fetcher host from env", + credentialsFetcherHostFromEnv: "invalid", + credentialsFetcherHostFromConfigFile: "dummy", + expectedBind: "/var/credentials-fetcher/socket/credentials_fetcher.sock:/var/credentials-fetcher/socket/credentials_fetcher.sock", + }, + { + name: "Credentials Fetcher from env, no Credentials Fetcher from config file", + credentialsFetcherHostFromEnv: "unix:///var/credentials-fetcher/socket/credentials_fetcher.sock", + credentialsFetcherHostFromConfigFile: "", + expectedBind: "/var/credentials-fetcher/socket/credentials_fetcher.sock:/var/credentials-fetcher/socket/credentials_fetcher.sock", + }, + { + name: "Credentials Fetcher from env, invalid Credentials Fetcher from config file", + credentialsFetcherHostFromEnv: "unix:///var/credentials-fetcher/socket/credentials_fetcher.sock", + credentialsFetcherHostFromConfigFile: "invalid", + expectedBind: "/var/credentials-fetcher/socket/credentials_fetcher.sock:/var/credentials-fetcher/socket/credentials_fetcher.sock", + }, + { + name: "Credentials Fetcher host from env, Credentials Fetcher from config file", + credentialsFetcherHostFromEnv: "unix:///var/credentials-fetcher/socket/credentials_fetcher.sock.1", + credentialsFetcherHostFromConfigFile: "unix:///var/credentials-fetcher/socket/credentials_fetcher.sock.1", + expectedBind: "/var/credentials-fetcher/socket/credentials_fetcher.sock.1:/var/credentials-fetcher/socket/credentials_fetcher.sock.1", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + os.Setenv("CREDENTIALS_FETCHER_HOST", tc.credentialsFetcherHostFromEnv) + defer os.Unsetenv("CREDENTIALS_FETCHER_HOST") + + bind, _ := getCredentialsFetcherSocketBind(map[string]string{"CREDENTIALS_FETCHER_HOST": tc.credentialsFetcherHostFromConfigFile}) + assert.Equal(t, tc.expectedBind, bind) + }) + } +}