diff --git a/api.go b/api.go index 8f7cac9..3d4a151 100644 --- a/api.go +++ b/api.go @@ -4,10 +4,8 @@ import ( "crypto/tls" "fmt" "log" - "net" "net/http" "os" - "regexp" "strings" gh "github.com/gorilla/handlers" @@ -28,7 +26,7 @@ func main() { bypassCaptcha := len(os.Getenv("GOOGLE_RECAPTCHA_DISABLED")) > 0 // Start the DNS server - dns.HandleFunc(".", handleDnsRequest) + dns.HandleFunc(".", handlers.DnsRequest) udpDnsServer := &dns.Server{Addr: ":53", Net: "udp"} go func() { err := udpDnsServer.ListenAndServe() @@ -72,6 +70,7 @@ func main() { corsRouter.HandleFunc("/sessions/{sessionId}", handlers.GetSession).Methods("GET") corsRouter.HandleFunc("/sessions/{sessionId}/instances", handlers.NewInstance).Methods("POST") corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}", handlers.DeleteInstance).Methods("DELETE") + corsRouter.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/exec", handlers.Exec).Methods("POST") r.HandleFunc("/sessions/{sessionId}/instances/{instanceName}/keys", handlers.SetKeys).Methods("POST") h := func(w http.ResponseWriter, r *http.Request) { @@ -137,85 +136,3 @@ func main() { } log.Fatal(s.ListenAndServeTLS("", "")) } - -var dnsFilter = regexp.MustCompile(`pwd[0-9]{1,3}_[0-9]{1,3}_[0-9]{1,3}_[0-9]{1,3}`) - -func handleDnsRequest(w dns.ResponseWriter, r *dns.Msg) { - if len(r.Question) > 0 && dnsFilter.MatchString(r.Question[0].Name) { - // this is something we know about and we should try to handle - question := r.Question[0].Name - domainChunks := strings.Split(question, ".") - tldChunks := strings.Split(strings.TrimPrefix(domainChunks[0], "pwd"), "-") - ip := strings.Replace(tldChunks[0], "_", ".", -1) - - m := new(dns.Msg) - m.SetReply(r) - m.Authoritative = true - m.RecursionAvailable = true - a, err := dns.NewRR(fmt.Sprintf("%s 60 IN A %s", question, ip)) - if err != nil { - log.Fatal(err) - } - m.Answer = append(m.Answer, a) - w.WriteMsg(m) - return - } else { - if len(r.Question) > 0 { - question := r.Question[0].Name - - if question == "localhost." { - log.Printf("Not a PWD host. Asked for [localhost.] returning automatically [127.0.0.1]\n") - m := new(dns.Msg) - m.SetReply(r) - m.Authoritative = true - m.RecursionAvailable = true - a, err := dns.NewRR(fmt.Sprintf("%s 60 IN A 127.0.0.1", question)) - if err != nil { - log.Fatal(err) - } - m.Answer = append(m.Answer, a) - w.WriteMsg(m) - return - } - - log.Printf("Not a PWD host. Looking up [%s]\n", question) - ips, err := net.LookupIP(question) - if err != nil { - // we have no information about this and we are not a recursive dns server, so we just fail so the client can fallback to the next dns server it has configured - w.Close() - // dns.HandleFailed(w, r) - return - } - log.Printf("Not a PWD host. Looking up [%s] got [%s]\n", question, ips) - m := new(dns.Msg) - m.SetReply(r) - m.Authoritative = true - m.RecursionAvailable = true - for _, ip := range ips { - ipv4 := ip.To4() - if ipv4 == nil { - a, err := dns.NewRR(fmt.Sprintf("%s 60 IN AAAA %s", question, ip.String())) - if err != nil { - log.Fatal(err) - } - m.Answer = append(m.Answer, a) - } else { - a, err := dns.NewRR(fmt.Sprintf("%s 60 IN A %s", question, ipv4.String())) - if err != nil { - log.Fatal(err) - } - m.Answer = append(m.Answer, a) - } - } - w.WriteMsg(m) - return - - } else { - log.Printf("Not a PWD host. Got DNS without any question\n") - // we have no information about this and we are not a recursive dns server, so we just fail so the client can fallback to the next dns server it has configured - w.Close() - // dns.HandleFailed(w, r) - return - } - } -} diff --git a/handlers/dns.go b/handlers/dns.go new file mode 100644 index 0000000..138690a --- /dev/null +++ b/handlers/dns.go @@ -0,0 +1,93 @@ +package handlers + +import ( + "fmt" + "log" + "net" + "regexp" + "strings" + + "github.com/miekg/dns" +) + +var dnsFilter = regexp.MustCompile(`pwd[0-9]{1,3}_[0-9]{1,3}_[0-9]{1,3}_[0-9]{1,3}`) + +func DnsRequest(w dns.ResponseWriter, r *dns.Msg) { + if len(r.Question) > 0 && dnsFilter.MatchString(r.Question[0].Name) { + // this is something we know about and we should try to handle + question := r.Question[0].Name + domainChunks := strings.Split(question, ".") + tldChunks := strings.Split(strings.TrimPrefix(domainChunks[0], "pwd"), "-") + ip := strings.Replace(tldChunks[0], "_", ".", -1) + + m := new(dns.Msg) + m.SetReply(r) + m.Authoritative = true + m.RecursionAvailable = true + a, err := dns.NewRR(fmt.Sprintf("%s 60 IN A %s", question, ip)) + if err != nil { + log.Fatal(err) + } + m.Answer = append(m.Answer, a) + w.WriteMsg(m) + return + } else { + if len(r.Question) > 0 { + question := r.Question[0].Name + + if question == "localhost." { + log.Printf("Not a PWD host. Asked for [localhost.] returning automatically [127.0.0.1]\n") + m := new(dns.Msg) + m.SetReply(r) + m.Authoritative = true + m.RecursionAvailable = true + a, err := dns.NewRR(fmt.Sprintf("%s 60 IN A 127.0.0.1", question)) + if err != nil { + log.Fatal(err) + } + m.Answer = append(m.Answer, a) + w.WriteMsg(m) + return + } + + log.Printf("Not a PWD host. Looking up [%s]\n", question) + ips, err := net.LookupIP(question) + if err != nil { + // we have no information about this and we are not a recursive dns server, so we just fail so the client can fallback to the next dns server it has configured + w.Close() + // dns.HandleFailed(w, r) + return + } + log.Printf("Not a PWD host. Looking up [%s] got [%s]\n", question, ips) + m := new(dns.Msg) + m.SetReply(r) + m.Authoritative = true + m.RecursionAvailable = true + for _, ip := range ips { + ipv4 := ip.To4() + if ipv4 == nil { + a, err := dns.NewRR(fmt.Sprintf("%s 60 IN AAAA %s", question, ip.String())) + if err != nil { + log.Fatal(err) + } + m.Answer = append(m.Answer, a) + } else { + a, err := dns.NewRR(fmt.Sprintf("%s 60 IN A %s", question, ipv4.String())) + if err != nil { + log.Fatal(err) + } + m.Answer = append(m.Answer, a) + } + } + w.WriteMsg(m) + return + + } else { + log.Printf("Not a PWD host. Got DNS without any question\n") + // we have no information about this and we are not a recursive dns server, so we just fail so the client can fallback to the next dns server it has configured + w.Close() + // dns.HandleFailed(w, r) + return + } + } +} diff --git a/handlers/exec.go b/handlers/exec.go new file mode 100644 index 0000000..eba4a93 --- /dev/null +++ b/handlers/exec.go @@ -0,0 +1,40 @@ +package handlers + +import ( + "encoding/json" + "log" + "net/http" + + "github.com/gorilla/mux" + "github.com/play-with-docker/play-with-docker/services" +) + +type execRequest struct { + Cmd []string `json:"command"` +} + +type execResponse struct { + ExitCode int `json:"status_code"` +} + +func Exec(rw http.ResponseWriter, req *http.Request) { + vars := mux.Vars(req) + instanceName := vars["instanceName"] + + var er execRequest + err := json.NewDecoder(req.Body).Decode(&er) + if err != nil { + rw.WriteHeader(http.StatusBadRequest) + return + } + + code, err := services.Exec(instanceName, er.Cmd) + + if err != nil { + log.Println(err) + rw.WriteHeader(http.StatusInternalServerError) + return + } + + json.NewEncoder(rw).Encode(execResponse{code}) +} diff --git a/handlers/new_session.go b/handlers/new_session.go index d4ce9b6..d8f6873 100644 --- a/handlers/new_session.go +++ b/handlers/new_session.go @@ -24,6 +24,7 @@ func NewSession(rw http.ResponseWriter, req *http.Request) { } reqDur := req.Form.Get("session-duration") + stack := req.Form.Get("stack") duration := services.GetDuration(reqDur) s, err := services.NewSession(duration) @@ -31,7 +32,7 @@ func NewSession(rw http.ResponseWriter, req *http.Request) { log.Println(err) //TODO: Return some error code } else { - + s.StackFile = stack hostname := fmt.Sprintf("%s.%s", config.PWDCName, req.Host) // If request is not a form, return sessionId in the body if req.Header.Get("X-Requested-With") == "XMLHttpRequest" { diff --git a/services/docker.go b/services/docker.go index 9bc71e2..bb64616 100644 --- a/services/docker.go +++ b/services/docker.go @@ -274,3 +274,20 @@ func CreateInstance(session *Session, dindImage string) (*Instance, error) { func DeleteContainer(id string) error { return c.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{Force: true, RemoveVolumes: true}) } + +func Exec(instanceName string, command []string) (int, error) { + e, err := c.ContainerExecCreate(context.Background(), instanceName, types.ExecConfig{Cmd: command}) + if err != nil { + return 0, err + } + err = c.ContainerExecStart(context.Background(), e.ID, types.ExecStartCheck{}) + if err != nil { + return 0, err + } + ins, err := c.ContainerExecInspect(context.Background(), e.ID) + if err != nil { + return 0, err + } + return ins.ExitCode, nil + +} diff --git a/services/session.go b/services/session.go index 3e7a7a6..67e987d 100644 --- a/services/session.go +++ b/services/session.go @@ -54,6 +54,7 @@ type Session struct { scheduled bool `json:"-"` ticker *time.Ticker `json:"-"` PwdIpAddress string `json:"pwd_ip_address"` + StackFile string `json:"-"` } func (s *Session) Lock() { diff --git a/www/assets/button.png b/www/assets/button.png new file mode 100644 index 0000000..dcea54d Binary files /dev/null and b/www/assets/button.png differ diff --git a/www/bypass.html b/www/bypass.html index 62f1037..9a47d4a 100644 --- a/www/bypass.html +++ b/www/bypass.html @@ -11,6 +11,7 @@