about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/access.go26
-rw-r--r--src/docker.go201
-rw-r--r--src/http.go68
-rw-r--r--src/main.go18
-rw-r--r--src/structs.go5
5 files changed, 318 insertions, 0 deletions
diff --git a/src/access.go b/src/access.go
new file mode 100644
index 0000000..6de2c05
--- /dev/null
+++ b/src/access.go
@@ -0,0 +1,26 @@
+package main
+
+import (
+	"math/rand"
+	"strings"
+	"time"
+)
+
+func newAccessCode(length int) string {
+	// seed the random numbergenerator
+	rand.Seed(time.Now().UnixNano())
+
+	// define the alphabet
+	chars := []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
+		"abcdefghijklmnopqrstuvwxyz" +
+		"0123456789")
+
+	// build the accesscode
+	var b strings.Builder
+	for i := 0; i < length; i++ {
+		b.WriteRune(chars[rand.Intn(len(chars))])
+	}
+	str := b.String()
+
+	return str
+}
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
+}
diff --git a/src/http.go b/src/http.go
new file mode 100644
index 0000000..248eea4
--- /dev/null
+++ b/src/http.go
@@ -0,0 +1,68 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"os"
+	"strings"
+	"text/template"
+
+	"github.com/gorilla/mux"
+)
+
+var (
+	port      *int     // port the http server listens on
+	usernames []string // list of usernames
+)
+
+// Credentials stores user credentials
+type Credentials struct {
+	Username   string
+	Accesscode string
+	Hostname   string
+}
+
+func registerHTTPFlags() {
+	port = flag.Int("port", 8081, "The port the http server should listen on")
+}
+
+func setupHTTPServer() http.Server {
+	r := mux.NewRouter()
+
+	r.HandleFunc("/", indexHandler)
+	return http.Server{
+		Addr:    fmt.Sprintf("0.0.0.0:%d", *port),
+		Handler: r,
+	}
+}
+
+// Host of the index file
+func indexHandler(w http.ResponseWriter, r *http.Request) {
+	t := template.New("")
+	t, err := t.ParseFiles("./hosted/index.html")
+	if err != nil {
+		log.Println(err)
+		return
+	}
+
+	content := Content{
+		Hostname: os.Getenv("HOSTNAME"),
+	}
+
+	t.ExecuteTemplate(w, "index", content)
+}
+
+func readFileToReponse(w http.ResponseWriter, path string) {
+	requestedFile := strings.Replace(path, "..", "", -1)
+
+	contents, readError := ioutil.ReadFile(fmt.Sprintf("hosted/%s", requestedFile))
+
+	if readError != nil {
+		w.Write([]byte(fmt.Sprintf("unable to read %s", requestedFile)))
+	} else {
+		w.Write([]byte(contents))
+	}
+}
diff --git a/src/main.go b/src/main.go
new file mode 100644
index 0000000..57007e8
--- /dev/null
+++ b/src/main.go
@@ -0,0 +1,18 @@
+package main
+
+import (
+	"flag"
+	"log"
+)
+
+func main() {
+	// Set up flags
+	registerHTTPFlags()
+	flag.Parse()
+
+	// Set up the http server
+	log.Printf("Running the HTTP server on port %d", *port)
+	httpServer := setupHTTPServer()
+
+	log.Fatalln(httpServer.ListenAndServe())
+}
diff --git a/src/structs.go b/src/structs.go
new file mode 100644
index 0000000..cdc682a
--- /dev/null
+++ b/src/structs.go
@@ -0,0 +1,5 @@
+package main
+
+type Content struct {
+	Hostname string
+}