Skip to content

Commit e73f2c8

Browse files
authored
add the ability to connect to a TLS protected Docker engine (#230)
* add the ability to connect to a TLS protected Docker engine * fix wrong image in README * change UIFD flags to be compliant with Docker CLI
1 parent 2754aef commit e73f2c8

File tree

3 files changed

+107
-33
lines changed

3 files changed

+107
-33
lines changed

README.md

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ UI For Docker is a web interface for the Docker Remote API. The goal is to prov
1212
* Minimal dependencies - I really want to keep this project a pure html/js app.
1313
* Consistency - The web UI should be consistent with the commands found on the docker CLI.
1414

15-
### Quickstart
15+
### Quickstart
1616
1. Run: `docker run -d -p 9000:9000 --privileged -v /var/run/docker.sock:/var/run/docker.sock uifd/ui-for-docker`
1717

1818
2. Open your browser to `http://<dockerd host ip>:9000`
@@ -25,17 +25,35 @@ Bind mounting the Unix socket into the UI For Docker container is much more secu
2525

2626
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`.
2727

28-
You can use the `-e` flag to change this socket:
28+
You can use the `-H` flag to change this socket:
2929

3030
# Connect to a tcp socket:
31-
$ docker run -d -p 9000:9000 --privileged uifd/ui-for-docker -e http://127.0.0.1:2375
31+
$ docker run -d -p 9000:9000 --privileged uifd/ui-for-docker -H tcp://127.0.0.1:2375
3232

3333
### Change address/port UI For Docker is served on
3434
UI For Docker listens on port 9000 by default. If you run UI For Docker inside a container then you can bind the container's internal port to any external address and port:
3535

3636
# Expose UI For Docker on 10.20.30.1:80
3737
$ docker run -d -p 10.20.30.1:80:9000 --privileged -v /var/run/docker.sock:/var/run/docker.sock uifd/ui-for-docker
3838

39+
### Access a Docker engine protected via TLS
40+
41+
Ensure that you have access to the CA, the TLS certificate and the TLS key used to access your Docker engine.
42+
43+
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:
44+
45+
```
46+
$ docker run -d -p 9000:9000 uifd/ui-for-docker -v /path/to/certs:/certs -H tcp://my-docker-host.domain:2376 -tlsverify
47+
```
48+
49+
If you want to specify different names for the CA, certificate and public key respectively you can use the `-tlscacert`, `-tlscert` and `-tlskey`:
50+
51+
```
52+
$ docker run -d -p 9000:9000 uifd/ui-for-docker -v /path/to/certs:/certs -H tcp://my-docker-host.domain:2376 -tlsverify -tlscacert /certs/myCA.pem -tlscert /certs/myCert.pem -tlskey /certs/myKey.pem
53+
```
54+
55+
*Note*: Replace `/path/to/certs` to the path to the certificate files on your disk.
56+
3957
### Check the [wiki](https://github.com/kevana/ui-for-docker/wiki) for more info about using UI For Docker
4058

4159
### Stack
@@ -62,24 +80,24 @@ The UI For Docker code is licensed under the MIT license.
6280
Copyright (c) 2013-2016 Michael Crosby (crosbymichael.com), Kevan Ahlquist (kevanahlquist.com)
6381

6482
Permission is hereby granted, free of charge, to any person
65-
obtaining a copy of this software and associated documentation
66-
files (the "Software"), to deal in the Software without
67-
restriction, including without limitation the rights to use, copy,
68-
modify, merge, publish, distribute, sublicense, and/or sell copies
69-
of the Software, and to permit persons to whom the Software is
83+
obtaining a copy of this software and associated documentation
84+
files (the "Software"), to deal in the Software without
85+
restriction, including without limitation the rights to use, copy,
86+
modify, merge, publish, distribute, sublicense, and/or sell copies
87+
of the Software, and to permit persons to whom the Software is
7088
furnished to do so, subject to the following conditions:
7189

72-
The above copyright notice and this permission notice shall be
90+
The above copyright notice and this permission notice shall be
7391
included in all copies or substantial portions of the Software.
7492

7593
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
7694
EXPRESS OR IMPLIED,
77-
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
78-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
79-
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
80-
HOLDERS BE LIABLE FOR ANY CLAIM,
81-
DAMAGES OR OTHER LIABILITY,
82-
WHETHER IN AN ACTION OF CONTRACT,
83-
TORT OR OTHERWISE,
84-
ARISING FROM, OUT OF OR IN CONNECTION WITH
95+
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
96+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
97+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
98+
HOLDERS BE LIABLE FOR ANY CLAIM,
99+
DAMAGES OR OTHER LIABILITY,
100+
WHETHER IN AN ACTION OF CONTRACT,
101+
TORT OR OTHERWISE,
102+
ARISING FROM, OUT OF OR IN CONNECTION WITH
85103
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

dockerui.go

Lines changed: 71 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,23 @@ import (
99
"net/http/httputil"
1010
"net/url"
1111
"os"
12-
"strings"
1312
"github.com/gorilla/csrf"
1413
"io/ioutil"
1514
"fmt"
1615
"github.com/gorilla/securecookie"
16+
"crypto/tls"
17+
"crypto/x509"
1718
)
1819

1920
var (
20-
endpoint = flag.String("e", "/var/run/docker.sock", "Dockerd endpoint")
21-
addr = flag.String("p", ":9000", "Address and port to serve UI For Docker")
22-
assets = flag.String("a", ".", "Path to the assets")
23-
data = flag.String("d", ".", "Path to the data")
21+
endpoint = flag.String("H", "unix:///var/run/docker.sock", "Dockerd endpoint")
22+
addr = flag.String("p", ":9000", "Address and port to serve UI For Docker")
23+
assets = flag.String("a", ".", "Path to the assets")
24+
data = flag.String("d", ".", "Path to the data")
25+
tlsverify = flag.Bool("tlsverify", false, "TLS support")
26+
tlscacert = flag.String("tlscacert", "/certs/ca.pem", "Path to the CA")
27+
tlscert = flag.String("tlscert", "/certs/cert.pem", "Path to the TLS certificate file")
28+
tlskey = flag.String("tlskey", "/certs/key.pem", "Path to the TLS key")
2429
authKey []byte
2530
authKeyFile = "authKey.dat"
2631
)
@@ -29,6 +34,13 @@ type UnixHandler struct {
2934
path string
3035
}
3136

37+
type TLSFlags struct {
38+
tls bool
39+
caPath string
40+
certPath string
41+
keyPath string
42+
}
43+
3244
func (h *UnixHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
3345
conn, err := net.Dial("unix", h.path)
3446
if err != nil {
@@ -61,35 +73,72 @@ func copyHeader(dst, src http.Header) {
6173
}
6274
}
6375

64-
func createTcpHandler(e string) http.Handler {
65-
u, err := url.Parse(e)
76+
func createTLSConfig(flags TLSFlags) *tls.Config {
77+
cert, err := tls.LoadX509KeyPair(flags.certPath, flags.keyPath)
78+
if err != nil {
79+
log.Fatal(err)
80+
}
81+
caCert, err := ioutil.ReadFile(flags.caPath)
6682
if err != nil {
6783
log.Fatal(err)
6884
}
85+
caCertPool := x509.NewCertPool()
86+
caCertPool.AppendCertsFromPEM(caCert)
87+
tlsConfig := &tls.Config{
88+
Certificates: []tls.Certificate{cert},
89+
RootCAs: caCertPool,
90+
}
91+
return tlsConfig;
92+
}
93+
94+
func createTcpHandler(u *url.URL) http.Handler {
95+
u.Scheme = "http";
6996
return httputil.NewSingleHostReverseProxy(u)
7097
}
7198

99+
func createTcpHandlerWithTLS(u *url.URL, flags TLSFlags) http.Handler {
100+
u.Scheme = "https";
101+
var tlsConfig = createTLSConfig(flags)
102+
proxy := httputil.NewSingleHostReverseProxy(u)
103+
proxy.Transport = &http.Transport{
104+
TLSClientConfig: tlsConfig,
105+
}
106+
return proxy;
107+
}
108+
72109
func createUnixHandler(e string) http.Handler {
73110
return &UnixHandler{e}
74111
}
75112

76-
func createHandler(dir string, d string, e string) http.Handler {
113+
func createHandler(dir string, d string, e string, flags TLSFlags) http.Handler {
77114
var (
78115
mux = http.NewServeMux()
79116
fileHandler = http.FileServer(http.Dir(dir))
80117
h http.Handler
81118
)
82119

83-
if strings.Contains(e, "http") {
84-
h = createTcpHandler(e)
85-
} else {
86-
if _, err := os.Stat(e); err != nil {
120+
u, perr := url.Parse(e)
121+
if perr != nil {
122+
log.Fatal(perr)
123+
}
124+
125+
if u.Scheme == "tcp" {
126+
if flags.tls {
127+
h = createTcpHandlerWithTLS(u, flags)
128+
} else {
129+
h = createTcpHandler(u)
130+
}
131+
} else if u.Scheme == "unix" {
132+
var socketPath = u.Path
133+
if _, err := os.Stat(socketPath); err != nil {
87134
if os.IsNotExist(err) {
88-
log.Fatalf("unix socket %s does not exist", e)
135+
log.Fatalf("unix socket %s does not exist", socketPath)
89136
}
90137
log.Fatal(err)
91138
}
92-
h = createUnixHandler(e)
139+
h = createUnixHandler(socketPath)
140+
} else {
141+
log.Fatalf("Bad Docker enpoint: %s. Only unix:// and tcp:// are supported.", e)
93142
}
94143

95144
// Use existing csrf authKey if present or generate a new one.
@@ -127,7 +176,14 @@ func csrfWrapper(h http.Handler) http.Handler {
127176
func main() {
128177
flag.Parse()
129178

130-
handler := createHandler(*assets, *data, *endpoint)
179+
tlsFlags := TLSFlags{
180+
tls: *tlsverify,
181+
caPath: *tlscacert,
182+
certPath: *tlscert,
183+
keyPath: *tlskey,
184+
}
185+
186+
handler := createHandler(*assets, *data, *endpoint, tlsFlags)
131187
if err := http.ListenAndServe(*addr, handler); err != nil {
132188
log.Fatal(err)
133189
}

gruntFile.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ module.exports = function (grunt) {
265265
command: [
266266
'docker stop ui-for-docker',
267267
'docker rm ui-for-docker',
268-
'docker run --net=host -d -v /tmp/ui-for-docker:/data --name ui-for-docker ui-for-docker -d /data -e http://127.0.0.1:2374'
268+
'docker run --net=host -d -v /tmp/ui-for-docker:/data --name ui-for-docker ui-for-docker -d /data -H tcp://127.0.0.1:2374'
269269
].join(';')
270270
},
271271
cleanImages: {

0 commit comments

Comments
 (0)