From 8b65f91699cd474563c0abacc726a3d47961a78f Mon Sep 17 00:00:00 2001 From: maride Date: Thu, 23 Aug 2018 11:46:23 +0200 Subject: Add VPN container and access --- src/access.go | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/container.go | 40 +++++++++----------- src/docker.go | 25 +++++++++++++ src/http.go | 39 +++++++++++++++++++ src/main.go | 4 ++ 5 files changed, 197 insertions(+), 22 deletions(-) create mode 100644 src/access.go create mode 100644 src/docker.go (limited to 'src') diff --git a/src/access.go b/src/access.go new file mode 100644 index 0000000..6072025 --- /dev/null +++ b/src/access.go @@ -0,0 +1,111 @@ +package main + +import ( + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types" + "time" + "errors" + "net/http" + "fmt" + "github.com/docker/docker/api/types/network" +) + +var vpnContainerID string +var vpnNetworkID string + +func startVPN() (err error) { + // Set up our context and Docker CLI connection + setupContext() + setupDockerCLI() + // Set up network + setupNetwork() + + // Create container + resp, err := dockerCli.ContainerCreate(dockerCtx, &container.Config{ + Image: "circus-vpn", + }, &container.HostConfig{ + Privileged: true, + }, &network.NetworkingConfig{ + EndpointsConfig: map[string]*network.EndpointSettings{ + "endpoint": { + NetworkID: vpnNetworkID, + }, + }, + }, "") + + if err != nil { + return err + } + + // Start container + err = dockerCli.ContainerStart(dockerCtx, resp.ID, types.ContainerStartOptions{}) + if err != nil { + return err + } + + vpnContainerID = resp.ID + + return nil +} + +func stopVPN() { + setupContext() + setupDockerCLI() + + timeout := time.Second * 5 + dockerCli.ContainerStop(dockerCtx, vpnContainerID, &timeout) + + vpnContainerID = "" +} + +func setupNetwork() (error) { + setupContext() + setupDockerCLI() + + if vpnNetworkID == "" { + response, err := dockerCli.NetworkCreate(dockerCtx, VPNNetworkName, types.NetworkCreate{ + Internal: true, + }) + + if err != nil { + return err + } + + vpnNetworkID = response.ID + } + + return nil +} + +func getCertificate() (string, error) { + if vpnContainerID == "" { + return "", errors.New("VPN container not up") + } + + // Get IP of VPN container + inspectJSON, err := dockerCli.ContainerInspect(dockerCtx, vpnContainerID) + if err != nil { + return "", err + } + + // get certificate + var certResponse *http.Response + + for i := 0; i < 10; i++ { + certResponse, err = http.Get(fmt.Sprintf("http://%s:9999/", inspectJSON.NetworkSettings.Networks[VPNNetworkName].IPAddress)) + + if err == nil { + break + } + time.Sleep(time.Second) + } + + if err != nil { + return "", err + } + + buffer := make([]byte, 1024) + certResponse.Body.Read(buffer) + + return string(buffer), nil +} diff --git a/src/container.go b/src/container.go index 73912bf..5b2075b 100644 --- a/src/container.go +++ b/src/container.go @@ -1,12 +1,15 @@ package main import ( - "context" - "github.com/docker/docker/client" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types" "fmt" "time" + "github.com/docker/docker/api/types/network" +) + +const ( + VPNNetworkName = "vpn-network" ) type ChallengeContainer struct { @@ -15,33 +18,26 @@ type ChallengeContainer struct { IP string } -var ( - dockerCtx context.Context - dockerCli *client.Client -) - // Starts the container and returns its address and containerID if successful func (cc ChallengeContainer) startContainer() (address string, containerID string, err error) { - // Set up our context if there is none already set up - if dockerCtx == nil { - dockerCtx = context.Background() - } - - // Set up our Docker CLI connection if there is not already one - if dockerCli == nil { - dockerCli, err = client.NewEnvClient() - - if err != nil { - return "", "", err - } - } + // Set up our context and Docker CLI connection + setupContext() + setupDockerCLI() + // Set up network + setupNetwork() // Create container resp, err := dockerCli.ContainerCreate(dockerCtx, &container.Config{ Image: cc.Challenge.Container, Env: []string{fmt.Sprintf("FLAG=%s", cc.Challenge.Flag)}, Tty: false, - }, nil, nil, "") + }, nil, &network.NetworkingConfig{ + EndpointsConfig: map[string]*network.EndpointSettings{ + VPNNetworkName: { + NetworkID: vpnNetworkID, + }, + }, + }, "") if err != nil { return "", "", err @@ -60,7 +56,7 @@ func (cc ChallengeContainer) startContainer() (address string, containerID strin } // Return IP, Container ID and error - return inspectJSON.NetworkSettings.IPAddress, resp.ID,nil + return inspectJSON.NetworkSettings.Networks[VPNNetworkName].IPAddress, resp.ID,nil } // Stops the container with a timeout of one second diff --git a/src/docker.go b/src/docker.go new file mode 100644 index 0000000..9bc667b --- /dev/null +++ b/src/docker.go @@ -0,0 +1,25 @@ +package main + +import ( + "github.com/docker/docker/client" + "context" +) + +var ( + dockerCtx context.Context + dockerCli *client.Client +) + +func setupContext() { + if dockerCtx == nil { + dockerCtx = context.Background() + } +} + +func setupDockerCLI() (err error) { + if dockerCli == nil { + dockerCli, err = client.NewEnvClient() + } + + return err +} \ No newline at end of file diff --git a/src/http.go b/src/http.go index 287004f..c7ad213 100644 --- a/src/http.go +++ b/src/http.go @@ -30,10 +30,12 @@ func runHTTPServer() (error) { r.HandleFunc("/login", loginPostHandler).Methods("POST") r.HandleFunc("/logout", logoutHandler).Methods("POST") r.HandleFunc("/challenges", challengesHandler).Methods("GET") + r.HandleFunc("/access", accessHandler).Methods("GET") r.HandleFunc("/api/getChallenges", getChallengesHandler).Methods("GET") r.HandleFunc("/api/submitFlag", submitFlagHandler).Methods("POST") r.HandleFunc("/api/startContainer", startContainerHandler).Methods("POST") r.HandleFunc("/api/stopContainer", stopContainerHandler).Methods("POST") + r.HandleFunc("/api/getAccess", getAccessHandler).Methods("GET") address := fmt.Sprintf(":%d", *port) return http.ListenAndServe(address, r) @@ -163,6 +165,19 @@ func challengesHandler(w http.ResponseWriter, r *http.Request) { } } +// Host the access file +func accessHandler(w http.ResponseWriter, r *http.Request) { + session, cookieNotFoundError := r.Cookie("session") + + if cookieNotFoundError != nil || !isValidSession(session.Value) { + // either no session cookie found, or it contains an invalid session token. Redirect. + http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) + } else { + // valid session token found, redirect to frontpage + readFileToResponse(w, "/access.html") + } +} + func getChallengesHandler(w http.ResponseWriter, r *http.Request) { session, cookieNotFoundError := r.Cookie("session") @@ -282,3 +297,27 @@ func stopContainerHandler(w http.ResponseWriter, r *http.Request) { stopChallengeContainer(challengeName) } } + +// Returns the configuration for the VPN +func getAccessHandler(w http.ResponseWriter, r *http.Request) { + session, cookieNotFoundError := r.Cookie("session") + + if cookieNotFoundError != nil || !isValidSession(session.Value) { + // either no session cookie found, or it contains an invalid session token. Redirect. + http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) + } else { + // valid session token found, get credentials + credentials, err := getCertificate() + errorString := "" + + if err != nil { + errorString = err.Error() + } + + jsonAnswer, _ := json.Marshal(map[string]string{ + "error": errorString, + "credentials": credentials, + }) + w.Write([]byte(jsonAnswer)) + } +} diff --git a/src/main.go b/src/main.go index 9885957..a06ee66 100644 --- a/src/main.go +++ b/src/main.go @@ -16,6 +16,10 @@ func main() { // Read challenges from file getChallengesFromSeedFile() + // Start our VPN container and network + startVPN() + defer stopVPN() + // Run HTTP server log.Fatalln(runHTTPServer()) } \ No newline at end of file -- cgit 1.4.1