Add warning if REST API is accessible through an insecure connection
| ... | ... |
@@ -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 |
+} |