diff --git a/README.md b/README.md index 8e15053e0..ce004a2a2 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,16 @@ The `--privileged` flag is required for hosts using SELinux. By default UI For Docker connects to the Docker daemon with`/var/run/docker.sock`. For this to work you need to bind mount the unix socket into the container with `-v /var/run/docker.sock:/var/run/docker.sock`. -You can use the `-e` flag to change this socket: +You can use the `--host`, `-H` flags to change this socket: ``` # Connect to a tcp socket: -$ docker run -d -p 9000:9000 cloudinovasi/cloudinovasi-ui -e http://127.0.0.1:2375 +$ docker run -d -p 9000:9000 cloudinovasi/cloudinovasi-ui -H tcp://127.0.0.1:2375 +``` + +``` +# Connect to another unix socket: +$ docker run -d -p 9000:9000 cloudinovasi/cloudinovasi-ui -H unix:///path/to/docker.sock ``` ### Swarm support @@ -41,7 +46,7 @@ You can access a specific view for you Swarm cluster by defining the `--swarm` f ``` # Connect to a tcp socket and enable Swarm: -$ docker run -d -p 9000:9000 cloudinovasi/cloudinovasi-ui -e http://: --swarm +$ docker run -d -p 9000:9000 cloudinovasi/cloudinovasi-ui -H tcp://: --swarm ``` *NOTE*: Due to Swarm not exposing information in a machine readable way, the app is bound to a specific version of Swarm at the moment. @@ -61,8 +66,13 @@ Ensure that you have access to the CA, the cert and the public key used to acces These files will need to be named `ca.pem`, `cert.pem` and `key.pem` respectively. Store them somewhere on your disk and mount a volume containing these files inside the UI container: ``` -# Note the access to the endpoint via https -$ docker run -d -p 9000:9000 cloudinovasi/cloudinovasi-ui -v /path/to/certs:/certs -e https://my-docker-host.domain:2376 +$ docker run -d -p 9000:9000 cloudinovasi/cloudinovasi-ui -v /path/to/certs:/certs -H https://my-docker-host.domain:2376 --tlsverify +``` + +You can also use the `--tlscacert`, `--tlscert` and `--tlskey` flags if you want to change the default path to the CA, certificate and key file respectively: + +``` +$ docker run -d -p 9000:9000 cloudinovasi/cloudinovasi-ui -v /path/to/certs:/certs -H https://my-docker-host.domain:2376 --tlsverify --tlscacert /certs/myCa.pem --tlscert /certs/myCert.pem --tlskey /certs/myKey.pem ``` *Note*: Replace `/path/to/certs` to the path to the certificate files on your disk. @@ -87,10 +97,13 @@ $ docker run -d -p 9000:9000 --privileged -v /var/run/docker.sock:/var/run/docke The following options are available for the `ui-for-docker` binary: -* `--endpoint`, `-e`: Docker deamon endpoint (default: *"/var/run/docker.sock"*) -* `--bind`, `-p`: Address and port to serve UI For Docker (default: *":9000"*) -* `--data`, `-d`: Path to the data folder (default: *"."*) -* `--certs`, `-c`: Path to the certificates used for TLS (default: *"/certs"*) -* `--assets`, `-a`: Path to the assets (default: *"."*) -* `--swarm`, `-s`: Swarm cluster support (default: *false*) +* `--host`, `-H`: Docker daemon endpoint (default: `"unix:///var/run/docker.sock"`) +* `--bind`, `-p`: Address and port to serve UI For Docker (default: `":9000"`) +* `--data`, `-d`: Path to the data folder (default: `"."`) +* `--assets`, `-a`: Path to the assets (default: `"."`) +* `--swarm`, `-s`: Swarm cluster support (default: `false`) * `--hide-label`, `-l`: Hide containers with a specific label in the UI +* `--tlsverify`: TLS support (default: `false`) +* `--tlscacert`: Path to the CA (default `/certs/ca.pem`) +* `--tlscert`: Path to the TLS certificate file (default `/certs/cert.pem`) +* `--tlskey`: Path to the TLS key (default `/certs/key.pem`) diff --git a/dockerui.go b/dockerui.go index 9d3da31c6..d2614c4c1 100644 --- a/dockerui.go +++ b/dockerui.go @@ -20,13 +20,16 @@ import ( ) var ( - endpoint = kingpin.Flag("endpoint", "Dockerd endpoint").Default("/var/run/docker.sock").Short('e').String() - addr = kingpin.Flag("bind", "Address and port to serve UI For Docker").Default(":9000").Short('p').String() - assets = kingpin.Flag("assets", "Path to the assets").Default(".").Short('a').String() - data = kingpin.Flag("data", "Path to the data").Default(".").Short('d').String() - certs = kingpin.Flag("certs", "Path to the certs").Default("/certs").Short('c').String() - swarm = kingpin.Flag("swarm", "Swarm cluster support").Default("false").Short('s').Bool() - labels = LabelParser(kingpin.Flag("hide-label", "Hide containers with a specific label in the UI").Short('l')) + endpoint = kingpin.Flag("host", "Dockerd endpoint").Default("unix:///var/run/docker.sock").Short('H').String() + addr = kingpin.Flag("bind", "Address and port to serve UI For Docker").Default(":9000").Short('p').String() + assets = kingpin.Flag("assets", "Path to the assets").Default(".").Short('a').String() + data = kingpin.Flag("data", "Path to the data").Default(".").Short('d').String() + swarm = kingpin.Flag("swarm", "Swarm cluster support").Default("false").Short('s').Bool() + tlsverify = kingpin.Flag("tlsverify", "TLS support").Default("false").Bool() + tlscacert = kingpin.Flag("tlscacert", "Path to the CA").Default("/certs/ca.pem").String() + tlscert = kingpin.Flag("tlscert", "Path to the TLS certificate file").Default("/certs/cert.pem").String() + tlskey = kingpin.Flag("tlskey", "Path to the TLS key").Default("/certs/key.pem").String() + labels = LabelParser(kingpin.Flag("hide-label", "Hide containers with a specific label in the UI").Short('l')) authKey []byte authKeyFile = "authKey.dat" ) @@ -35,6 +38,13 @@ type UnixHandler struct { path string } +type TlsFlags struct { + tls bool + caPath string + certPath string + keyPath string +} + type Config struct { Swarm bool `json:"swarm"` HiddenLabels Labels `json:"hiddenLabels"` @@ -109,20 +119,17 @@ func configurationHandler(w http.ResponseWriter, r *http.Request, c Config) { json.NewEncoder(w).Encode(c) } -func createTcpHandler(e string) http.Handler { - u, err := url.Parse(e) - if err != nil { - log.Fatal(err) - } +func createTcpHandler(u *url.URL) http.Handler { + u.Scheme = "http"; return httputil.NewSingleHostReverseProxy(u) } -func createTlsConfig(c string) *tls.Config { - cert, err := tls.LoadX509KeyPair(c + "/" + "cert.pem", c + "/" + "key.pem") +func createTlsConfig(tlsFlags TlsFlags) *tls.Config { + cert, err := tls.LoadX509KeyPair(tlsFlags.certPath, tlsFlags.keyPath) if err != nil { log.Fatal(err) } - caCert, err := ioutil.ReadFile(c + "/" + "ca.pem") + caCert, err := ioutil.ReadFile(tlsFlags.caPath) if err != nil { log.Fatal(err) } @@ -135,12 +142,9 @@ func createTlsConfig(c string) *tls.Config { return tlsConfig; } -func createTcpHandlerWithTLS(e string, c string) http.Handler { - u, err := url.Parse(e) - if err != nil { - log.Fatal(err) - } - var tlsConfig = createTlsConfig(c) +func createTcpHandlerWithTLS(u *url.URL, tlsFlags TlsFlags) http.Handler { + u.Scheme = "https"; + var tlsConfig = createTlsConfig(tlsFlags) proxy := httputil.NewSingleHostReverseProxy(u) proxy.Transport = &http.Transport{ TLSClientConfig: tlsConfig, @@ -152,24 +156,33 @@ func createUnixHandler(e string) http.Handler { return &UnixHandler{e} } -func createHandler(dir string, d string, certs string, e string, c Config) http.Handler { +func createHandler(dir string, d string, e string, c Config, tlsFlags TlsFlags) http.Handler { var ( mux = http.NewServeMux() fileHandler = http.FileServer(http.Dir(dir)) h http.Handler ) - if strings.Contains(e, "https") { - h = createTcpHandlerWithTLS(e, certs) - } else if strings.Contains(e, "http") { - h = createTcpHandler(e) - } else { - if _, err := os.Stat(e); err != nil { + u, perr := url.Parse(e) + if perr != nil { + log.Fatal(perr) + } + if u.Scheme == "tcp" { + if tlsFlags.tls { + h = createTcpHandlerWithTLS(u, tlsFlags) + } else { + h = createTcpHandler(u) + } + } else if u.Scheme == "unix" { + var socketPath = u.Path + if _, err := os.Stat(socketPath); err != nil { if os.IsNotExist(err) { - log.Fatalf("unix socket %s does not exist", e) + log.Fatalf("unix socket %s does not exist", socketPath) } log.Fatal(err) } - h = createUnixHandler(e) + h = createUnixHandler(socketPath) + } else { + log.Fatalf("Bad Docker enpoint: %s. Only unix:// and tcp:// are supported.", e) } // Use existing csrf authKey if present or generate a new one. @@ -216,7 +229,14 @@ func main() { HiddenLabels: *labels, } - handler := createHandler(*assets, *data, *certs, *endpoint, configuration) + tlsFlags := TlsFlags{ + tls: *tlsverify, + caPath: *tlscacert, + certPath: *tlscert, + keyPath: *tlskey, + } + + handler := createHandler(*assets, *data, *endpoint, configuration, tlsFlags) if err := http.ListenAndServe(*addr, handler); err != nil { log.Fatal(err) } diff --git a/gruntFile.js b/gruntFile.js index b18674a1d..3ff9bbc31 100644 --- a/gruntFile.js +++ b/gruntFile.js @@ -272,14 +272,14 @@ module.exports = function (grunt) { command: [ 'docker stop ui-for-docker', 'docker rm ui-for-docker', - 'docker run -d -p 9000:9000 -v /tmp/docker-ui:/data --name ui-for-docker ui-for-docker -e http://10.0.7.10:4000 --swarm -d /data' + 'docker run -d -p 9000:9000 -v /tmp/docker-ui:/data --name ui-for-docker ui-for-docker -H tcp://10.0.7.10:4000 --swarm -d /data' ].join(';') }, runSsl: { command: [ 'docker stop ui-for-docker', 'docker rm ui-for-docker', - 'docker run -d -p 9000:9000 -v /tmp/docker-ui:/data -v /tmp/docker-ssl:/certs --name ui-for-docker ui-for-docker -e https://10.0.7.10:2376 -d /data' + 'docker run -d -p 9000:9000 -v /tmp/docker-ui:/data -v /tmp/docker-ssl:/certs --name ui-for-docker ui-for-docker -H tcp://10.0.7.10:2376 -d /data --tlsverify' ].join(';') }, cleanImages: {