func handleCredentials(apiVersion, subpath string, c *ec2metaproxy.ContainerService, w http.ResponseWriter, r *http.Request) { resp, err := instanceServiceClient.RoundTrip(NewGET(baseURL + "/" + apiVersion + "/meta-data/iam/security-credentials/")) if err != nil { log.Error("Error requesting creds path for API version ", apiVersion, ": ", err) w.WriteHeader(http.StatusInternalServerError) return } resp.Body.Close() if resp.StatusCode != http.StatusOK { w.WriteHeader(resp.StatusCode) return } clientIP := remoteIP(r.RemoteAddr) role, err := c.RoleForIP(clientIP) if err != nil { log.Error(clientIP, " ", err) http.Error(w, "An unexpected error getting container role", http.StatusInternalServerError) return } roleName := role.Arn.RoleName() if len(subpath) == 0 { w.Write([]byte(roleName)) } else if !strings.HasPrefix(subpath, roleName) || (len(subpath) > len(roleName) && subpath[len(roleName)-1] != '/') { // An idiosyncrasy of the standard EC2 metadata service: // Subpaths of the role name are ignored. So long as the correct role name is provided, // it can be followed by a slash and anything after the slash is ignored. w.WriteHeader(http.StatusNotFound) } else { creds, err := json.Marshal(&MetadataCredentials{ Code: "Success", LastUpdated: role.LastUpdated, Type: "AWS-HMAC", AccessKeyID: role.Credentials.AccessKey, SecretAccessKey: role.Credentials.SecretKey, Token: role.Credentials.Token, Expiration: role.Credentials.Expiration, }) if err != nil { log.Error("Error marshaling credentials: ", err) w.WriteHeader(http.StatusInternalServerError) } else { w.Write(creds) } } }
func (t *ContainerService) syncContainers() { log.Info("Synchronizing state with running docker containers") apiContainers, err := t.docker.ListContainers(docker.ListContainersOptions{ All: false, // only running containers Size: false, // do not need size information Limit: 0, // all running containers Since: "", // not applicable Before: "", // not applicable }) if err != nil { log.Error("Error listing running containers: ", err) return } containerIPMap := make(map[string]*ContainerInfo) containerIDMap := make(map[string]string) for _, apiContainer := range apiContainers { container, err := t.docker.InspectContainer(apiContainer.ID) if err != nil { log.Error("Error inspecting container: ", apiContainer.ID, ": ", err) continue } shortContainerID := apiContainer.ID[:6] containerIP := container.NetworkSettings.IPAddress roleArn, roleErr := getRoleArnFromEnv(container.Config.Env, t.defaultRoleArn) if roleArn.Empty() && roleErr == nil { roleErr = fmt.Errorf("No role defined for container %s: image=%s", shortContainerID, container.Config.Image) } log.Infof("Container: id=%s image=%s role=%s", shortContainerID, container.Config.Image, roleArn) containerIPMap[containerIP] = &ContainerInfo{ ContainerID: apiContainer.ID, ShortContainerID: shortContainerID, SessionName: generateSessionName(container), LastUpdated: time.Time{}, Error: roleErr, RoleArn: roleArn, } containerIDMap[apiContainer.ID] = containerIP } t.containerIPMap = containerIPMap t.containerIDMap = containerIDMap }
func main() { kingpin.CommandLine.Help = "Docker container EC2 metadata service." kingpin.Parse() defer log.Flush() configureLogging(*verboseOpt) auth, err := aws.GetAuth("", "", "", time.Time{}) if err != nil { panic(err) } containerService := ec2metaproxy.NewContainerService(dockerClient(), *defaultRole, auth) // Proxy non-credentials requests to primary metadata service http.HandleFunc("/", logHandler(func(w http.ResponseWriter, r *http.Request) { match := credsRegex.FindStringSubmatch(r.URL.Path) if match != nil { handleCredentials(match[1], match[2], containerService, w, r) return } proxyReq, err := http.NewRequest(r.Method, fmt.Sprintf("%s%s", baseURL, r.URL.Path), r.Body) if err != nil { log.Error("Error creating proxy http request: ", err) http.Error(w, "An unexpected error occurred communicating with Amazon", http.StatusInternalServerError) return } copyHeaders(proxyReq.Header, r.Header) resp, err := instanceServiceClient.RoundTrip(proxyReq) if err != nil { log.Error("Error forwarding request to EC2 metadata service: ", err) http.Error(w, "An unexpected error occurred communicating with Amazon", http.StatusInternalServerError) return } defer resp.Body.Close() copyHeaders(w.Header(), resp.Header) w.WriteHeader(resp.StatusCode) if _, err := io.Copy(w, resp.Body); err != nil { log.Warn("Error copying response content from EC2 metadata service: ", err) } })) log.Critical(http.ListenAndServe(*serverAddr, nil)) }