From c856c7e66e3ad29ea0491f1f4951b31deb8279ea Mon Sep 17 00:00:00 2001 From: Emile Date: Sun, 20 Oct 2019 16:53:28 +0200 Subject: functional --- src/access.go | 26 ++++++++ src/docker.go | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/http.go | 68 +++++++++++++++++++ src/main.go | 18 ++++++ src/structs.go | 5 ++ 5 files changed, 318 insertions(+) create mode 100644 src/access.go create mode 100644 src/docker.go create mode 100644 src/http.go create mode 100644 src/main.go create mode 100644 src/structs.go (limited to 'src') 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 +} -- cgit 1.4.1