1616package execcmd
1717
1818import (
19+ "errors"
1920 "fmt"
2021 "path/filepath"
2122 "strings"
2223
24+ apitask "github.com/aws/amazon-ecs-agent/agent/api/task"
25+ "github.com/aws/amazon-ecs-agent/agent/config"
26+ "github.com/aws/amazon-ecs-agent/agent/utils/endpoints"
27+ "github.com/aws/amazon-ecs-agent/ecs-agent/logger"
2328 dockercontainer "github.com/docker/docker/api/types/container"
2429)
2530
@@ -114,25 +119,114 @@ func validConfigExists(configFilePath, expectedHash string) bool {
114119
115120var GetExecAgentConfigFileName = getAgentConfigFileName
116121
117- func getAgentConfigFileName (sessionLimit int ) (string , error ) {
118- config := fmt .Sprintf (execAgentConfigTemplate , sessionLimit )
122+ // formatSSMAgentConfig creates the SSM agent configuration with the appropriate endpoints
123+ // based on whether we're in an IPv6-only environment
124+ func formatSSMAgentConfig (sessionLimit int , cfg * config.Config , task * apitask.Task ) (string , error ) {
125+ var (
126+ mgsEndpoint string
127+ ssmEndpoint string
128+ mdsEndpoint string
129+ s3Endpoint string
130+ kmsEndpoint string
131+ cwlEndpoint string
132+ err error
133+ )
134+
135+ // SSM Agent needs to use dualstack endpoints for its dependencies
136+ // if the network only supports IPv6.
137+ useDualStackEndpoints := false
138+ if task .IsNetworkModeAWSVPC () {
139+ // For awsvpc tasks, the task network is used by the SSM Agent
140+ primaryENI := task .GetPrimaryENI ()
141+ if primaryENI == nil {
142+ return "" , errors .New ("awsvpc mode task does not have a primary ENI" )
143+ }
144+ useDualStackEndpoints = primaryENI .IPv6Only ()
145+ } else {
146+ useDualStackEndpoints = cfg .InstanceIPCompatibility .IsIPv6Only ()
147+ }
148+
149+ if useDualStackEndpoints {
150+ // Resolve SSM Messages endpoint
151+ mgsEndpoint , err = endpoints .ResolveSSMMessagesDualStackEndpoint (cfg .AWSRegion )
152+ if err != nil {
153+ return "" , fmt .Errorf ("failed to resolve SSM Messages endpoint: %w" , err )
154+ }
155+
156+ // Resolve SSM endpoint
157+ ssmEndpoint , err = endpoints .ResolveSSMEndpoint (cfg .AWSRegion , true )
158+ if err != nil {
159+ return "" , fmt .Errorf ("failed to resolve SSM endpoint: %w" , err )
160+ }
161+
162+ // Resolve EC2 Messages endpoint
163+ mdsEndpoint , err = endpoints .ResolveEC2MessagesDualStackEndpoint (cfg .AWSRegion )
164+ if err != nil {
165+ return "" , fmt .Errorf ("failed to resolve EC2 Messages endpoint: %w" , err )
166+ }
167+
168+ // Resolve S3 endpoint
169+ s3Endpoint , err = endpoints .ResolveS3Endpoint (cfg .AWSRegion , true )
170+ if err != nil {
171+ return "" , fmt .Errorf ("failed to resolve S3 endpoint: %w" , err )
172+ }
173+
174+ // Resolve KMS endpoint
175+ kmsEndpoint , err = endpoints .ResolveKMSEndpoint (cfg .AWSRegion , true )
176+ if err != nil {
177+ return "" , fmt .Errorf ("failed to resolve KMS endpoint: %w" , err )
178+ }
179+
180+ // Resolve CloudWatch Logs endpoint
181+ cwlEndpoint , err = endpoints .ResolveCloudWatchLogsEndpoint (cfg .AWSRegion , true )
182+ if err != nil {
183+ return "" , fmt .Errorf ("failed to resolve CloudWatch Logs endpoint: %w" , err )
184+ }
185+
186+ logger .Info ("Using dualstack endpoints for SSM Agent in IPv6-only environment" , logger.Fields {
187+ "region" : cfg .AWSRegion ,
188+ "mgsEndpoint" : mgsEndpoint ,
189+ "ssmEndpoint" : ssmEndpoint ,
190+ "mdsEndpoint" : mdsEndpoint ,
191+ "s3Endpoint" : s3Endpoint ,
192+ "kmsEndpoint" : kmsEndpoint ,
193+ "cwlEndpoint" : cwlEndpoint ,
194+ })
195+ }
196+
197+ return fmt .Sprintf (execAgentConfigTemplate , mgsEndpoint , sessionLimit , ssmEndpoint ,
198+ mdsEndpoint , s3Endpoint , kmsEndpoint , cwlEndpoint ), nil
199+ }
200+
201+ func getAgentConfigFileName (sessionLimit int , cfg * config.Config , task * apitask.Task ) (string , error ) {
202+ // Format the SSM agent config with appropriate endpoints
203+ config , err := formatSSMAgentConfig (sessionLimit , cfg , task )
204+ if err != nil {
205+ return "" , err
206+ }
207+
208+ // Generate a hash of the config to use in the filename
119209 hash := getExecAgentConfigHash (config )
120210 configFileName := fmt .Sprintf (execAgentConfigFileNameTemplate , hash )
121- // check if config file exists already
211+
212+ // Check if config file exists already
122213 configFilePath := filepath .Join (ECSAgentExecConfigDir , configFileName )
123214 if fileExists (configFilePath ) && validConfigExists (configFilePath , hash ) {
124215 return configFileName , nil
125216 }
126- // check if config file is a dir; if true, remove it
217+
218+ // Check if config file is a dir; if true, remove it
127219 if isDir (configFilePath ) {
128220 if err := removeAll (configFilePath ); err != nil {
129221 return "" , err
130222 }
131223 }
132- // config doesn't exist; create a new one
224+
225+ // Config doesn't exist; create a new one
133226 if err := createNewExecAgentConfigFile (config , configFilePath ); err != nil {
134227 return "" , err
135228 }
229+
136230 return configFileName , nil
137231}
138232
@@ -142,8 +236,11 @@ func certsExist() bool {
142236
143237// This function creates any necessary config directories/files and ensures that
144238// the ssm-agent binaries, configs, logs, and plugin is bind mounted
145- func addRequiredBindMounts (taskId , cn , latestBinVersionDir , uuid string , sessionWorkersLimit int , hostConfig * dockercontainer.HostConfig ) error {
146- configFile , rErr := GetExecAgentConfigFileName (sessionWorkersLimit )
239+ func addRequiredBindMounts (
240+ task * apitask.Task , cn , latestBinVersionDir , uuid string , sessionWorkersLimit int ,
241+ hostConfig * dockercontainer.HostConfig , cfg * config.Config ,
242+ ) error {
243+ configFile , rErr := GetExecAgentConfigFileName (sessionWorkersLimit , cfg , task )
147244 if rErr != nil {
148245 rErr = fmt .Errorf ("could not generate ExecAgent Config File: %v" , rErr )
149246 return rErr
@@ -191,7 +288,7 @@ func addRequiredBindMounts(taskId, cn, latestBinVersionDir, uuid string, session
191288
192289 // Add ssm log bind mount
193290 hostConfig .Binds = append (hostConfig .Binds , getBindMountMapping (
194- filepath .Join (HostLogDir , taskId , cn ),
291+ filepath .Join (HostLogDir , task . GetID () , cn ),
195292 ContainerLogDir ))
196293 return nil
197294}
0 commit comments