diff --git a/Dockerfile.dind b/Dockerfile.dind index bcb49ac..aa7dbf2 100644 --- a/Dockerfile.dind +++ b/Dockerfile.dind @@ -45,6 +45,10 @@ CMD cat /etc/hosts >/etc/hosts.bak && \ sed 's/^::1.*//' /etc/hosts.bak > /etc/hosts && \ sed -i "s/\DOCKER_STORAGE_DRIVER/$DOCKER_STORAGE_DRIVER/" /etc/docker/daemon.json && \ sed -i "s/\PWD_IP_ADDRESS/$PWD_IP_ADDRESS/" /etc/docker/daemon.json && \ + sed -i "s/\DOCKER_TLSENABLE/$DOCKER_TLSENABLE/" /etc/docker/daemon.json && \ + sed -i "s/\DOCKER_TLSCACERT/$DOCKER_TLSCACERT/" /etc/docker/daemon.json && \ + sed -i "s/\DOCKER_TLSCERT/$DOCKER_TLSCERT/" /etc/docker/daemon.json && \ + sed -i "s/\DOCKER_TLSKEY/$DOCKER_TLSKEY/" /etc/docker/daemon.json && \ umount /var/lib/docker && mount -t securityfs none /sys/kernel/security && \ dockerd &>/docker.log & \ while true ; do script -q -c "/bin/bash -l" /dev/null ; done diff --git a/daemon.json b/daemon.json index f007534..f625e49 100644 --- a/daemon.json +++ b/daemon.json @@ -6,5 +6,9 @@ "insecure-registries": ["127.0.0.1"], "hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2375"], "storage-driver": "DOCKER_STORAGE_DRIVER", - "dns": ["PWD_IP_ADDRESS", "8.8.8.8"] + "dns": ["PWD_IP_ADDRESS", "8.8.8.8"], + "tls": DOCKER_TLSENABLE, + "tlscacert": "DOCKER_TLSCACERT", + "tlscert": "DOCKER_TLSCERT", + "tlskey": "DOCKER_TLSKEY" } diff --git a/handlers/set_keys.go b/handlers/set_keys.go deleted file mode 100644 index 53271a6..0000000 --- a/handlers/set_keys.go +++ /dev/null @@ -1,43 +0,0 @@ -package handlers - -import ( - "encoding/json" - "log" - "net/http" - - "github.com/gorilla/mux" - "github.com/play-with-docker/play-with-docker/services" -) - -func SetKeys(rw http.ResponseWriter, req *http.Request) { - vars := mux.Vars(req) - sessionId := vars["sessionId"] - instanceName := vars["instanceName"] - - type certs struct { - ServerCert []byte `json:"server_cert"` - ServerKey []byte `json:"server_key"` - } - - var c certs - jsonErr := json.NewDecoder(req.Body).Decode(&c) - if jsonErr != nil { - log.Println(jsonErr) - rw.WriteHeader(http.StatusBadRequest) - return - } - - s := services.GetSession(sessionId) - s.Lock() - defer s.Unlock() - i := services.GetInstance(s, instanceName) - - _, err := i.SetCertificate(c.ServerCert, c.ServerKey) - - if err != nil { - log.Println(err) - rw.WriteHeader(http.StatusBadRequest) - return - } - log.Printf("Set keys for instance %s\n", instanceName) -} diff --git a/handlers/tlsproxy.go b/handlers/tlsproxy.go index 26ff520..e6194c6 100644 --- a/handlers/tlsproxy.go +++ b/handlers/tlsproxy.go @@ -9,10 +9,12 @@ import ( "strings" vhost "github.com/inconshreveable/go-vhost" + "github.com/play-with-docker/play-with-docker/services" ) func StartTLSProxy(port string) { var validProxyHost = regexp.MustCompile(`^.*pwd([0-9]{1,3}-[0-9]{1,3}-[0-9]{1,3}-[0-9]{1,3})(?:-?([0-9]{1,5}))?\..*$`) + var validAliasProxyHost = regexp.MustCompile(`^.*pwd([0-9|a-z|A-Z]*)-([0-9|a-z|A-Z]{8})(?:-?([0-9]{1,5}))?\..*$`) tlsListener, tlsErr := net.Listen("tcp", fmt.Sprintf(":%s", port)) log.Println("Listening on port " + port) @@ -37,26 +39,42 @@ func StartTLSProxy(port string) { } defer vhostConn.Close() - host := vhostConn.ClientHelloMsg.ServerName - match := validProxyHost.FindStringSubmatch(host) - if len(match) < 2 { - // Not a valid proxy host, just close connection. - return - } - var targetIP string targetPort := "443" - if len(match) == 3 { - targetPort = match[2] - } - - ip := strings.Replace(match[1], "-", ".", -1) - - if net.ParseIP(ip) == nil { - // Not a valid IP, so treat this is a hostname. + host := vhostConn.ClientHelloMsg.ServerName + match := validProxyHost.FindStringSubmatch(host) + if len(match) < 2 { + // Not a valid proxy host, try alias hosts + match := validAliasProxyHost.FindStringSubmatch(host) + if len(match) < 4 { + // Not valid, just close the connection + return + } else { + alias := match[1] + sessionPrefix := match[2] + instance := services.FindInstanceByAlias(sessionPrefix, alias) + if instance != nil { + targetIP = instance.IP + } else { + return + } + if len(match) == 4 { + targetPort = match[3] + } + } } else { - targetIP = ip + // Valid proxy host + ip := strings.Replace(match[1], "-", ".", -1) + if net.ParseIP(ip) == nil { + // Not a valid IP, so treat this is a hostname. + return + } else { + targetIP = ip + } + if len(match) == 3 { + targetPort = match[2] + } } dest := fmt.Sprintf("%s:%s", targetIP, targetPort) diff --git a/services/docker.go b/services/docker.go index 34a28b5..2d716da 100644 --- a/services/docker.go +++ b/services/docker.go @@ -244,14 +244,14 @@ func CreateInstance(session *Session, conf InstanceConfig) (*Instance, error) { // Write certs to container cert dir if len(conf.ServerCert) > 0 { - env = append(env, "DOCKER_TLSCERT=/var/run/pwd/certs/cert.pem") + env = append(env, `DOCKER_TLSCERT=\/var\/run\/pwd\/certs\/cert.pem`) } if len(conf.ServerKey) > 0 { - env = append(env, "DOCKER_TLSKEY=/var/run/pwd/certs/key.pem") + env = append(env, `DOCKER_TLSKEY=\/var\/run\/pwd\/certs\/key.pem`) } if len(conf.CACert) > 0 { // if ca cert is specified, verify that clients that connects present a certificate signed by the CA - env = append(env, "DOCKER_TLSCACERT=/var/run/pwd/certs/ca.pem") + env = append(env, `DOCKER_TLSCACERT=\/var\/run\/pwd\/certs\/ca.pem`) } if len(conf.ServerCert) > 0 || len(conf.ServerKey) > 0 || len(conf.CACert) > 0 { // if any of the certs is specified, enable TLS @@ -323,7 +323,11 @@ func CreateInstance(session *Session, conf InstanceConfig) (*Instance, error) { return nil, err } - return &Instance{Name: containerName, Hostname: cinfo.Config.Hostname, IP: cinfo.NetworkSettings.Networks[session.Id].IPAddress}, nil + return &Instance{ + Name: containerName, + Hostname: cinfo.Config.Hostname, + IP: cinfo.NetworkSettings.Networks[session.Id].IPAddress, + }, nil } func copyIfSet(content []byte, fileName, path, containerName string) error { diff --git a/services/instance.go b/services/instance.go index 4f1ae08..384f155 100644 --- a/services/instance.go +++ b/services/instance.go @@ -2,7 +2,6 @@ package services import ( "context" - "crypto/tls" "fmt" "io" "log" @@ -38,11 +37,13 @@ type Instance struct { Mem string `json:"mem"` Cpu string `json:"cpu"` Alias string `json:"alias"` + tempPorts []uint16 `json:"-"` + ServerCert []byte `json:"server_cert"` + ServerKey []byte `json:"server_key"` + CACert []byte `json:"ca_cert"` + Cert []byte `json:"cert"` + Key []byte `json:"key"` Ports UInt16Slice - tempPorts []uint16 `json:"-"` - ServerCert []byte `json:"server_cert"` - ServerKey []byte `json:"server_key"` - cert *tls.Certificate `json:"-"` } type InstanceConfig struct { @@ -51,6 +52,8 @@ type InstanceConfig struct { ServerCert []byte ServerKey []byte CACert []byte + Cert []byte + Key []byte } func (i *Instance) setUsedPort(port uint16) { @@ -65,25 +68,6 @@ func (i *Instance) setUsedPort(port uint16) { i.tempPorts = append(i.tempPorts, port) } -func (i *Instance) SetCertificate(cert, key []byte) (*tls.Certificate, error) { - i.ServerCert = cert - i.ServerKey = key - c, e := tls.X509KeyPair(i.ServerCert, i.ServerKey) - if e != nil { - return nil, e - } - i.cert = &c - - // We store sessions as soon as we set instance keys - if err := saveSessionsToDisk(); err != nil { - return nil, err - } - return i.cert, nil -} -func (i *Instance) GetCertificate() *tls.Certificate { - return i.cert -} - func (i *Instance) IsConnected() bool { return i.conn != nil @@ -119,7 +103,11 @@ func NewInstance(session *Session, conf InstanceConfig) (*Instance, error) { } instance.Alias = conf.Alias - + instance.Cert = conf.Cert + instance.Key = conf.Key + instance.ServerCert = conf.ServerCert + instance.ServerKey = conf.ServerKey + instance.CACert = conf.CACert instance.session = session if session.Instances == nil { diff --git a/services/session.go b/services/session.go index 67e987d..b52c2d2 100644 --- a/services/session.go +++ b/services/session.go @@ -1,6 +1,7 @@ package services import ( + "crypto/tls" "encoding/gob" "fmt" "log" @@ -15,6 +16,7 @@ import ( "github.com/docker/docker/api" "github.com/docker/docker/client" + "github.com/docker/go-connections/tlsconfig" "github.com/googollee/go-socket.io" "github.com/play-with-docker/play-with-docker/config" "github.com/prometheus/client_golang/prometheus" @@ -99,11 +101,27 @@ func (s *Session) SchedulePeriodicTasks() { if i.dockerClient == nil { // Need to create client to the DinD docker daemon + // We check if the client needs to use TLS + var tlsConfig *tls.Config + if len(i.Cert) > 0 && len(i.Key) > 0 { + tlsConfig = tlsconfig.ClientDefault() + tlsConfig.InsecureSkipVerify = true + tlsCert, err := tls.X509KeyPair(i.Cert, i.Key) + if err != nil { + log.Println("Could not load X509 key pair: %v. Make sure the key is not encrypted", err) + continue + } + tlsConfig.Certificates = []tls.Certificate{tlsCert} + } + transport := &http.Transport{ DialContext: (&net.Dialer{ Timeout: 1 * time.Second, KeepAlive: 30 * time.Second, }).DialContext} + if tlsConfig != nil { + transport.TLSClientConfig = tlsConfig + } cli := &http.Client{ Transport: transport, } @@ -313,14 +331,6 @@ func LoadSessionsFromDisk() error { for _, i := range s.Instances { // wire the session back to the instance i.session = s - - if i.ServerCert != nil && i.ServerKey != nil { - _, err := i.SetCertificate(i.ServerCert, i.ServerKey) - if err != nil { - log.Println(err) - return err - } - } } // Connect PWD daemon to the new network