about summary refs log tree commit diff
path: root/src/docker.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/docker.go')
-rw-r--r--src/docker.go201
1 files changed, 201 insertions, 0 deletions
diff --git a/src/docker.go b/src/docker.go
new file mode 100644
index 0000000..e3ff19c
--- /dev/null
+++ b/src/docker.go
@@ -0,0 +1,201 @@
+package main
+
+import (
+	"context"
+	"fmt"
+	"log"
+	"os"
+	"time"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/container"
+	"github.com/docker/docker/api/types/mount"
+	"github.com/docker/docker/api/types/network"
+	"github.com/docker/docker/client"
+	"github.com/docker/go-connections/nat"
+)
+
+const (
+	fixedDockerVersion = "1.38"
+)
+
+var (
+	dockerCtx            context.Context
+	dockerCLI            *client.Client
+	currentVpnRemptePort = 1194
+)
+
+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 spawnCompanion(username string, accesscode string) {
+	log.Println("Spwaning a new companion container")
+	// setup the context and the docker cli connection
+	setupContext()
+	setupDockerCLI()
+
+	sessionSalt := generateSessionSalt()
+	vpnRemoteAddress := os.Getenv("HOSTNAME")
+
+	// get the vpnRemoteport and increment it
+	vpnRemotePort := currentVpnRemptePort
+	log.Println("vpnRemptePort ", currentVpnRemptePort)
+	currentVpnRemptePort++
+
+	log.Println("Generating the container config")
+
+	Config := &container.Config{
+		Image: "circus-companion:latest",
+		Cmd:   []string{"-username", username, "-accessCode", accesscode, "-sessionSalt", sessionSalt, "-vpnRemoteAddress", vpnRemoteAddress, "-vpnRemotePort", fmt.Sprintf("%d", vpnRemotePort)},
+		ExposedPorts: nat.PortSet{
+			"8080/tcp": struct{}{},
+		},
+		// Container labels used by traefik
+		Labels: map[string]string{
+			"traefik.enable": "true",
+			fmt.Sprintf("traefik.http.routers.%s.entrypoints", username):                              "web",
+			fmt.Sprintf("traefik.http.routers.%s.rule", username):                                     fmt.Sprintf("Host(`%s.%s`)", username, os.Getenv("HOSTNAME")),
+			fmt.Sprintf("traefik.http.middlewares.%s-https-redirect.redirectscheme.scheme", username): "https",
+			fmt.Sprintf("traefik.http.routers.%s.middlewares", username):                              fmt.Sprintf("%s-https-redirect", username),
+			fmt.Sprintf("traefik.http.routers.%s-secure.entrypoints", username):                       "websecure",
+			fmt.Sprintf("traefik.http.routers.%s-secure.rule", username):                              fmt.Sprintf("Host(`%s.%s`)", username, os.Getenv("HOSTNAME")),
+			fmt.Sprintf("traefik.http.routers.%s-secure.tls", username):                               "true",
+			fmt.Sprintf("traefik.http.routers.%s-secure.tls.certresolver", username):                  "mytlschallenge",
+			fmt.Sprintf("traefik.http.routers.%s-secure.service", username):                           fmt.Sprintf("%s", username),
+			fmt.Sprintf("traefik.http.services.%s.loadbalancer.server.port", username):                "8080",
+			"traefik.docker.network": "circus",
+		},
+	}
+
+	// configure the host
+	// this mounts the docker socket and the companion seed file into the
+	// container
+	HostConfig := &container.HostConfig{
+		Mounts: []mount.Mount{
+			{
+				Type:   mount.TypeBind,
+				Source: "/var/run/docker.sock",
+				Target: "/var/run/docker.sock",
+			},
+			{
+				Type:   mount.TypeBind,
+				Source: "/etc/companion.json",
+				Target: "/etc/companion.json",
+			},
+		},
+	}
+
+	circusNetworkID, err := getCircusNetworkID(dockerCtx, dockerCLI)
+	NetworkingConfig := &network.NetworkingConfig{
+		EndpointsConfig: map[string]*network.EndpointSettings{
+			"circus": {
+				NetworkID: circusNetworkID,
+			},
+		},
+	}
+
+	log.Println("Creating the container")
+
+	// create the docker container
+	resp, err := dockerCLI.ContainerCreate(dockerCtx, Config, HostConfig, NetworkingConfig, "")
+	if err != nil {
+		panic(err)
+	}
+
+	log.Println("Container created!")
+
+	// id of the created container
+	containerID := resp.ID
+
+	// start the docker container
+	err = dockerCLI.ContainerStart(dockerCtx, containerID, types.ContainerStartOptions{})
+	if err != nil {
+		fmt.Println(err)
+	}
+
+	log.Printf("Adding the container (%s) to the circus network", containerID)
+
+	// get the circus network id
+	circusNetworkID, err = getCircusNetworkID(dockerCtx, dockerCLI)
+	if err != nil {
+		log.Println("error: could not get CircusNetworkID", err)
+	}
+	log.Printf("circus network ID: %s", circusNetworkID)
+
+	// connect the companion container to the circus network
+	connectContainerToNetwork(containerID, circusNetworkID)
+
+	log.Println("Container added to the circus network")
+
+	// Get IP Address of that container
+	inspectJSON, err := dockerCLI.ContainerInspect(dockerCtx, containerID)
+	if err != nil {
+		log.Println("inspectJsonErr: ", err)
+	}
+
+	fmt.Println("---")
+	fmt.Printf("%#v", inspectJSON.NetworkSettings.Networks)
+	fmt.Printf("companion IP: %#v", inspectJSON.NetworkSettings.Networks["circus"].IPAddress)
+}
+
+func generateSessionSalt() string {
+	return fmt.Sprintf("%d", time.Now().UnixNano())
+}
+
+// listDockerNetworks lists the docker networks
+func listDockerNetworks(dockerCtx context.Context, dockerCLI *client.Client) ([]types.NetworkResource, error) {
+
+	// list the docker networks
+	networks, err := dockerCLI.NetworkList(dockerCtx, types.NetworkListOptions{})
+	if err != nil {
+		return nil, fmt.Errorf("network list err: %s", err)
+	}
+
+	return networks, nil
+}
+
+// searchDockerNetworkIDFromName returns the docker network ID using the given name
+// to find it.
+func searchDockerNetworkIDFromName(networks []types.NetworkResource, NetworkNameToFind string) (string, error) {
+
+	for _, network := range networks {
+		if network.Name == NetworkNameToFind {
+			return network.ID, nil
+		}
+	}
+	return "", fmt.Errorf("could not find the network id for the network %s: %s", NetworkNameToFind)
+}
+
+// connectContainerToNetwork inserts the container with the given ID into the
+// network with the given ID
+func connectContainerToNetwork(containerID string, networkID string) {
+	err := dockerCLI.NetworkConnect(dockerCtx, networkID, containerID, nil)
+	if err != nil {
+		log.Printf("Could not connect container %s to network %s: %s", containerID[:10], networkID[:10], err)
+	}
+	log.Println("connectContainerToNetwork err: ", err)
+}
+
+func getCircusNetworkID(dockerCtx context.Context, dockerCLI *client.Client) (string, error) {
+	networks, err := listDockerNetworks(dockerCtx, dockerCLI)
+	if err != nil {
+		return "", err
+	}
+
+	circusNetworkID, err := searchDockerNetworkIDFromName(networks, "circus")
+	if err != nil {
+		return "", err
+	}
+	return circusNetworkID, nil
+}