diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/access.go | 176 | ||||
-rw-r--r-- | src/container.go | 44 | ||||
-rw-r--r-- | src/docker.go | 25 | ||||
-rw-r--r-- | src/http.go | 39 | ||||
-rw-r--r-- | src/main.go | 5 |
5 files changed, 267 insertions, 22 deletions
diff --git a/src/access.go b/src/access.go new file mode 100644 index 0000000..6f9cd73 --- /dev/null +++ b/src/access.go @@ -0,0 +1,176 @@ +package main + +import ( + "bytes" + "errors" + "flag" + "fmt" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/network" + "github.com/docker/go-connections/nat" + "net/http" + "time" +) + +const( + vpnHostNetworkName = "vpnhostnet" +) + +var vpnContainerID string +var vpnNetworkID string +var vpnHostNetworkID string +var remoteAddress* string +var remotePort* int + +func registerAccessFlags() { + remoteAddress = flag.String("vpnRemoteAddress", "", "The remote domain name or IP the VPN will run on") + remotePort = flag.Int("vpnRemotePort", 1194, "The port the VPN should listen on") +} + +func startVPN() (err error) { + // Set up our context and Docker CLI connection + setupContext() + setupDockerCLI() + // Set up network + err = setupNetwork() + + if(err != nil) { + return err + } + + err = setupVPNHostNetwork() + + if err != nil { + return err + } + + // Create container + resp, err := dockerCli.ContainerCreate(dockerCtx, &container.Config{ + Image: "circus-vpn", + Env: []string{ + fmt.Sprintf("remoteAddress=%s", *remoteAddress), + fmt.Sprintf("remotePort=%d", *remotePort), + }, + ExposedPorts: map[nat.Port]struct{}{ + "1194/udp": {}, + }, + }, &container.HostConfig{ + Privileged: true, + PortBindings: nat.PortMap{ + "1194/udp": []nat.PortBinding{ + { + HostIP: "0.0.0.0", + HostPort: "1194", + }, + }, + }, + }, &network.NetworkingConfig{ + EndpointsConfig: map[string]*network.EndpointSettings{ + "startpoint": { + NetworkID: vpnHostNetworkID, + }, + }, + }, "") + + if err != nil { + return err + } + + // Attach container network to VPN container + err = dockerCli.NetworkConnect(dockerCtx, vpnNetworkID, resp.ID, &network.EndpointSettings{}) + 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 setupVPNHostNetwork() (error) { + setupContext() + setupDockerCLI() + + if vpnHostNetworkID == "" { + response, err := dockerCli.NetworkCreate(dockerCtx, vpnHostNetworkName, types.NetworkCreate{ + Internal: false, + }) + + if err != nil { + return err + } + + vpnHostNetworkID = 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(bytes.Trim(buffer, "\x00")), nil +} diff --git a/src/container.go b/src/container.go index 73912bf..c9a918f 100644 --- a/src/container.go +++ b/src/container.go @@ -1,39 +1,33 @@ package main import ( - "context" - "github.com/docker/docker/client" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types" "fmt" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/network" "time" ) +const ( + VPNNetworkName = "circus-vpnnet" +) + type ChallengeContainer struct { Challenge *Challenge ContainerID string 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 context and Docker CLI connection + setupContext() + setupDockerCLI() + // Set up network + err = setupNetwork() - // Set up our Docker CLI connection if there is not already one - if dockerCli == nil { - dockerCli, err = client.NewEnvClient() - - if err != nil { - return "", "", err - } + if err != nil { + return "", "", err } // Create container @@ -41,7 +35,13 @@ func (cc ChallengeContainer) startContainer() (address string, containerID strin 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 +60,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..ae50632 100644 --- a/src/main.go +++ b/src/main.go @@ -11,11 +11,16 @@ func main() { registerSessionFlags() registerCredentialsFlags() registerSeedFlags() + registerAccessFlags() flag.Parse() // 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 |