package main import ( "context" "encoding/json" "fmt" "io/ioutil" "log" "net/http" "os" "strings" "github.com/docker/docker/api/types" "github.com/docker/docker/client" ) const ( fixedDockerVersion = "1.38" ) var ( dockerCtx context.Context dockerCLI *client.Client // store the amount of flags each user has found userFlags map[string]int users []string // list of users ) // Statistics stores companion statistics type Statistics struct { Challenges []struct { Name string `json:"name"` FoundFlag int64 `json:"foundFlag"` FlagTries uint `json:"flagTries"` } `json:"challenges"` User string `json:"user"` } func setupContext() { if dockerCtx == nil { dockerCtx = context.Background() } } func setupDockerCLI() (err error) { os.Setenv("DOCKER_API_VERSION", "1.27") if dockerCLI == nil { dockerCLI, err = client.NewEnvClient() } return err } func listDockerContainers() { log.Println("listing the docker containers...") // setup the context and the docker cli connection setupContext() setupDockerCLI() // get a list of all networks networks, err := dockerCLI.NetworkList(context.Background(), types.NetworkListOptions{}) if err != nil { panic(err) } // get a list of all containers containers, err := dockerCLI.ContainerList(context.Background(), types.ContainerListOptions{}) if err != nil { panic(err) } // find the container names of the companion containers by iterating over // all containers adding the containers containing the string "companion" in // their names to the list of companion containers var companionContainerNames []string for _, container := range containers { if strings.Contains(container.Image, "companion") { companionContainerNames = append(companionContainerNames, container.Names[0][1:]) } } fmt.Printf("%#v\n\n", companionContainerNames) // find the IPs of the companion containers by iterating over all networks, // then iterating over all containers in the network and then testing if the // container is part of the previously generated companion-containers-list. var companionContainerIPs []string for _, network := range networks { if network.Name == "circus" { for _, container := range network.Containers { for _, name := range companionContainerNames { if container.Name == name { companionContainerIPs = append(companionContainerIPs, container.IPv4Address) } } } } } // print all ips found for _, rawip := range companionContainerIPs { // if the ip starts with 172, it's the right one! if strings.Compare(rawip[0:3], "172") == 0 { // strip the "/16" or so suffix (this might break, see the todo // below...) // TODO: strip everything after the slash and the slash ip := rawip[:len(rawip)-3] fmt.Printf("ip: %s\n\n", ip) // get the stats from the container statsJSON, err := getStats(ip, 8080) if err != nil { fmt.Println(err) } // unmarshal the json statistics into a golang struct var statsGoStruct Statistics err = json.Unmarshal([]byte(statsJSON), &statsGoStruct) if err != nil { fmt.Println("error: ", err) } // if the user is not in the users list, add the user to the users list present := false for _, user := range users { if user == statsGoStruct.User { present = true } } if present == false { users = append(users, statsGoStruct.User) } if userFlags == nil { userFlags = make(map[string]int) } // count the amount of flags each user has found userFlags[statsGoStruct.User] = 0 for _, chall := range statsGoStruct.Challenges { if chall.FoundFlag > 1 { userFlags[statsGoStruct.User]++ } } } } } // getStats makes an http request to the container with the given ip and port // returning the result of the request to the /api/getStats endpoint func getStats(containerName string, containerPort uint16) (string, error) { // define the url where the request should go url := fmt.Sprintf("http://%s:%d/api/getStats", containerName, containerPort) log.Printf("url: %s", url) // make the request resp, err := http.Get(url) if err != nil { return "", fmt.Errorf("could not make the http get request: %v", err) } defer resp.Body.Close() // read the response body body, err := ioutil.ReadAll(resp.Body) if err != nil { return "", fmt.Errorf("could not read the request body: %v", err) } // returns the stats return string(body), nil }