文章目录

非安全的连接方式

先介绍 非安全 的连接方式。

服务端配置

CoreOS请使用第二种方法,其他 Linux 系统配置时选择以下两种方法之一

通常的配置方法

docker.servicedockerd-H 参数不能与 daemon.json 中的 hosts键值对冲突。(其他参数同理)

新建 /etc/systemd/system/docker.service.d/docker.conf 文件。

[Service]
ExecStart=
ExecStart=/usr/bin/dockerd

/etc/docker/daemon.json(下文统一简称 daemon.json)中写入以下内容

{
  "hosts":[
    "unix:///var/run/docker.sock",
    "tcp://0.0.0.0:2375"
  ]
}

该文件必须符合 json 规范写法,否则 Docker 将不能启动。

重新启动 Docker。

$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

CoreOS 官方文档提供的方法

官方文档:https://coreos.com/os/docs/latest/customizing-docker.html

新建 /etc/systemd/system/docker-tcp.socket文件

[Unit]
Description=Docker Socket for the API

[Socket]
# ListenStream=127.0.0.1:2375
ListenStream=2375
BindIPv6Only=both
Service=docker.service

[Install]
WantedBy=sockets.target

重新启动服务

$ sudo systemctl daemon-reload
$ sudo systemctl enable docker-tcp.socket
$ sudo systemctl stop docker
$ sudo systemctl start docker-tcp.socket
$ sudo systemctl start docker

注意:这种方法必须先启动 docker-tcp.socket,再启动 Docker,一定要注意启动顺序!

systemd socket 详情请查看:http://www.jinbuguo.com/systemd/systemd.socket.html

在客户端测试连接

macOS (下文中 macOS 一律代指 Docker 客户端)上使用以下命令

$ docker -H 192.168.57.110:2375 info

成功输出信息,证明客户端可以成功连接到远程的服务端。

macOS 上远程操作 CoreOS 上的 Docker 每次执行命令时必须加上 -H 参数(这样太麻烦,我们可以通过将 Docker 命令 参数 配置成 环境变量 来简化命令)。

在 macOS 上执行如下命令。

$ export DOCKER_HOST="tcp://0.0.0.0:2375"

$ docker info

这里写入的变量是临时生效的,重新登录环境变量就消失了(下文同理,之后不再赘述),让环境变量永久生效请写入 ~/.bashrc。

fish shell

本人 macOS 上使用的 shell 是 fish,这里记录一下 fish 中的操作,使用 bash 的读者请忽略 fish 相关内容。

$ set -Ux DOCKER_HOST "tcp://0.0.0.0:2375"
# 以上命令写入的环境变量是永久存在的,通过以下命令删除环境变量
$ set -Ue DOCKER_HOST

配置安全连接

官方文档:https://docs.docker.com/engine/security/https/

上面我们配置的远程连接是不安全的,只能用于测试环境中。在生产环境中需要配置 TLS 安全连接,只有拥有密钥的客户端,才能连接到远程的服务端。

服务端配置

只能使用 Linux 下的 openssl 生成密钥,macOS 下的不可以。在 CoreOS 下执行以下操作

手动执行命令生成证书(不推荐)

这一步较复杂,你可以跳过这一方法,使用容器生成证书。此方法来自 Docker 官方文档 https://docs.docker.com/engine/security/https/。

文件总览

├── ca-key.pem       # 妥善保管,连接时用不到
├── ca.pem           # clent & server
├── ca.srl           # 用不到
├── cert.pem         # client
├── client.csr       # 请求文件
├── extfile.cnf      # 配置文件
├── key.pem          # client
├── server-cert.pem  # server
├── server.csr       # 请求文件
└── server-key.pem   # server
# 生成 CA 私钥

$ openssl genrsa -aes256 -out ca-key.pem 4096

# 需要输入两次密码(自定义)

# 生成 CA 公钥

$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem

# 输入上一步中设置的密码,然后需要填写一些信息

# 下面是服务器证书生成

# 生成服务器私钥

$ openssl genrsa -out server-key.pem 4096

# 用私钥生成证书请求文件

$ openssl req -subj "/CN=localhost" -sha256 -new -key server-key.pem -out server.csr

$ echo subjectAltName = DNS:localhost,DNS:www.khs1994.com,DNS:tencent,IP:192.168.199.100,IP:192.168.57.110,IP:127.0.0.1 >> extfile.cnf

# 允许服务端哪些 IP 或 host 能被客户端连接,下文会进行测试。

# DNS 我也不是很理解,这里配置 localhost ,公共 DNS 解析的域名,/etc/hosts 文件中的列表进行测试。

$ echo extendedKeyUsage = serverAuth >> extfile.cnf

# 用 CA 来签署证书

$ openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem \
  -CAcreateserial -out server-cert.pem -extfile extfile.cnf

# 再次输入第一步设置的密码

# 下面是客户端证书文件生成

# 生成客户端私钥

$ openssl genrsa -out key.pem 4096

# 用私钥生成证书请求文件  

$ openssl req -subj '/CN=client' -new -key key.pem -out client.csr

$ echo extendedKeyUsage = clientAuth >> extfile.cnf

# 用 CA 来签署证书

$ openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem \
  -CAcreateserial -out cert.pem -extfile extfile.cnf

# 再次输入第一步设置的密码

# 删除文件,更改文件权限

$ rm -v client.csr server.csr

$ chmod -v 0400 ca-key.pem key.pem server-key.pem

$ chmod -v 0444 ca.pem server-cert.pem cert.pem

ca.pem server-cert.pem server-key.pem 三个文件移动到 /etc/docker/ 文件夹中。

使用容器生成证书(推荐)

GitHub:https://github.com/khs1994-docker/dockerd-tls

方法来自 CoreOS 官方文档:https://coreos.com/os/docs/latest/generate-self-signed-certificates.html

既然使用容器那就可以在任何系统运行,只要把生成的证书文件对应的放到 Docker 客户端和服务端即可。

$ git clone --depth=1 https://github.com/khs1994-docker/dockerd-tls.git
$ cd dockerd-tls

./cfssl/*.json 中配置好 CN hosts

$ docker-compose up cfssl

命令执行完毕之后在 ./cfssl/cert 文件夹中可以看到证书文件,修改文件权限。

$ chmod -v 0400 ca-key.pem key.pem server-key.pem
$ chmod -v 0444 ca.pem server-cert.pem cert.pem

ca.pem server-cert.pem server-key.pem 三个文件移动到服务端 /etc/docker/ 文件夹中。

CoreOS 请使用第二种方法,其他 Linux 系统根据上文选择的方法,这里选择对应的方法

通常的配置方法 修改 daemon.json 文件。

注意:非安全连接使用的是 2375 端口,安全连接使用的是 2376 端口。当然这是推荐的端口配置,你可以配置任何端口!

{
  "tlsverify": true,
  "tlscert": "/etc/docker/server-cert.pem",
  "tlskey": "/etc/docker/server-key.pem",
  "tlscacert": "/etc/docker/ca.pem",
  "hosts":[
    "unix:///var/run/docker.sock",
    "tcp://0.0.0.0:2376"
  ]
}

重新启动 Docker

$ sudo systemctl restart docker

CoreOS 官方文档的方法

首先需要修改 /etc/systemd/system/docker-tcp.socket 文件内容

ListenStream=2375

# 修改为

ListenStream=2376

修改 CoreOS 上的 daemon.json 文件。

{
  "tlsverify": true,
  "tlscert": "/etc/docker/server-cert.pem",
  "tlskey": "/etc/docker/server-key.pem",
  "tlscacert": "/etc/docker/ca.pem"
}

重新启动服务。

$ sudo systemctl daemon-reload
$ sudo systemctl stop docker
$ sudo systemctl restart docker-tcp.socket
$ sudo systemctl restart docker

上文已经提到了启动顺序,这里提示一下,不再赘述。

客户端远程安全连接

ca.pem cert.pem key.pem 三个文件通过 scp 下载到 macOS

在 macOS 执行以下命令,密钥路径请根据实际情况填写。

$ docker --tlsverify \
  --tlscacert=/Users/khs1994/test/ca.pem \
  --tlscert=/Users/khs1994/test/cert.pem \
  --tlskey=/Users/khs1994/test/key.pem \
  -H=192.168.57.110:2376 \
  info

把密钥放入~/.docker 文件夹中 每次操作需要跟那么多参数,太麻烦了。我们可以把 ca.pem cert.pem key.pem 三个文件放入客户端 ~/.docker 中,然后配置环境变量就可以简化命令了。

$ export DOCKER_HOST=tcp://192.168.57.110:2376 DOCKER_TLS_VERIFY=1
$ docker info

你也可以选择其他路径,请通过环境变量 DOCKER_CERT_PATH 指定。

报错详情

不使用安全连接

$ docker -H 192.168.57.110:2376 info
Get http://192.168.57.110:2376/v1.34/containers/json?all=1: malformed HTTP response "\x15\x03\x01\x00\x02\x02".
* Are you trying to connect to a TLS-enabled daemon without TLS?

在非允许列表 IP 中登录

假如远程服务器还有一个 IP 10.141.20.83 ,现在我们尝试使用这个 IP 作为服务端地址,看看客户端能否连接到。

$ docker -H 10.141.20.83:2376 info
error during connect: Get https://10.141.20.83:2376/v1.34/info: x509: certificate is valid for 192.168.57.110, 192.168.199.100, 127.0.0.1, not 10.141.20.83

在非允许列表 Host 中登录

$ docker -H localhost:2376 info
error during connect: Get https://localhost:2375/v1.34/info: x509: certificate is valid for coreos1, not localhost

fish shell

$ set -Ux DOCKER_HOST tcp://192.168.57.110:2376
$ set -Ux DOCKER_TLS_VERIFY 1
# 以上命令写入环境变量是永久存在的,通过以下命令删除环境变量
$ set -Ue DOCKER_HOST ; set -Ue DOCKER_TLS_VERIFY

服务端验证模式

  • tlsverify, tlscacert, tlscert, tlskey set: Authenticate clients

  • tls, tlscert, tlskey: Do not authenticate clients

客户端验证模式

  • tls: Authenticate server based on public/default CA pool

  • tlsverify, tlscacert: Authenticate server based on given CA

  • tls, tlscert, tlskey: Authenticate with client certificate, do not authenticate server based on given CA

  • tlsverify, tlscacert, tlscert, tlskey: Authenticate with client certificate and authenticate server based on given CA

测试远程构建 Docker 镜像

macOS 新建 demo 文件夹并进入。

我们首先建一个文本文件 test.txt

hello!

然后新建一个简单的 Dockerfile 文件

FROM busybox

COPY ./test.txt /

CMD cat /test.txt

按照前面的方法设置好环境变量,这里不再赘述。

$ docker -H 192.168.57.110:2375 --tlsverify build -t khs1994/busybox .

在远程服务端查看

SSH 登录到 CoreOS(这里为了便于理解,SSH 到远程服务器操作)。

$ docker image ls
REPOSITORY                             TAG                 IMAGE ID            CREATED             SIZE
khs1994/busybox                        latest              368d23df8500        10 seconds ago      1.13MB

我们已经查看到了镜像。

$ docker run -it --rm khs1994/busybox
hello!

运行成功。

客户端恢复原状

你如果想在 macOS 操作本地的服务端,请将上面配置的环境变量删除,这里不再赘述。