Browse code

Merge pull request #37684 from thaJeztah/add_remote_api_warning

Add warning if REST API is accessible through an insecure connection

Tibor Vass authored on 2018/08/22 08:52:37
Showing 2 changed files
... ...
@@ -68,6 +68,7 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
68 68
 		Isolation:          daemon.defaultIsolation,
69 69
 	}
70 70
 
71
+	daemon.fillAPIInfo(v)
71 72
 	// Retrieve platform specific info
72 73
 	daemon.fillPlatformInfo(v, sysInfo)
73 74
 	daemon.fillDriverInfo(v)
... ...
@@ -171,6 +172,32 @@ func (daemon *Daemon) fillSecurityOptions(v *types.Info, sysInfo *sysinfo.SysInf
171 171
 	v.SecurityOptions = securityOptions
172 172
 }
173 173
 
174
+func (daemon *Daemon) fillAPIInfo(v *types.Info) {
175
+	const warn string = `
176
+         Access to the remote API is equivalent to root access on the host. Refer
177
+         to the 'Docker daemon attack surface' section in the documentation for
178
+         more information: https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface`
179
+
180
+	cfg := daemon.configStore
181
+	for _, host := range cfg.Hosts {
182
+		// cnf.Hosts is normalized during startup, so should always have a scheme/proto
183
+		h := strings.SplitN(host, "://", 2)
184
+		proto := h[0]
185
+		addr := h[1]
186
+		if proto != "tcp" {
187
+			continue
188
+		}
189
+		if !cfg.TLS {
190
+			v.Warnings = append(v.Warnings, fmt.Sprintf("WARNING: API is accessible on http://%s without encryption.%s", addr, warn))
191
+			continue
192
+		}
193
+		if !cfg.TLSVerify {
194
+			v.Warnings = append(v.Warnings, fmt.Sprintf("WARNING: API is accessible on https://%s without TLS client verification.%s", addr, warn))
195
+			continue
196
+		}
197
+	}
198
+}
199
+
174 200
 func hostName() string {
175 201
 	hostname := ""
176 202
 	if hn, err := os.Hostname(); err != nil {
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"fmt"
6 6
 	"testing"
7 7
 
8
+	"github.com/docker/docker/internal/test/daemon"
8 9
 	"github.com/docker/docker/internal/test/request"
9 10
 	"gotest.tools/assert"
10 11
 	is "gotest.tools/assert/cmp"
... ...
@@ -40,3 +41,26 @@ func TestInfoAPI(t *testing.T) {
40 40
 		assert.Check(t, is.Contains(out, linePrefix))
41 41
 	}
42 42
 }
43
+
44
+func TestInfoAPIWarnings(t *testing.T) {
45
+	d := daemon.New(t)
46
+
47
+	client, err := d.NewClient()
48
+	assert.NilError(t, err)
49
+
50
+	d.StartWithBusybox(t, "--iptables=false", "-H=0.0.0.0:23756", "-H=unix://"+d.Sock())
51
+	defer d.Stop(t)
52
+
53
+	info, err := client.Info(context.Background())
54
+	assert.NilError(t, err)
55
+
56
+	stringsToCheck := []string{
57
+		"Access to the remote API is equivalent to root access",
58
+		"http://0.0.0.0:23756",
59
+	}
60
+
61
+	out := fmt.Sprintf("%+v", info)
62
+	for _, linePrefix := range stringsToCheck {
63
+		assert.Check(t, is.Contains(out, linePrefix))
64
+	}
65
+}