Docker 守护进程+远程连接+安全访问 (完整收藏版)

本文根据官方资料实践而成,同时对于本人操作时遇到的问题加以整理总结.通过本文,你应该能够实现通过自定义证书实现对远程Docker主机的安全访问和控制.尊重劳动成果,未经本人允许不得转载.

一 Docker服务端,客户端和CA证书

默认情况下,Docker通过非联网的Unix套接字运行。它还可以选择使用HTTP套接字进行通信。

这里有三个角色,Docker服务端Docker客户端CA签名的证书

  • Docker服务端:对应运行Docker守护程序的主机
    • 通过开放端口(一般为2376)支持远程连接
    • 通过指定tlsverify标志并将Docker的tlscacert标志指向可信的CA证书来启用TLS以实现安全访问(仅允许由该CA签名的证书进行身份验证的客户端连接)
  • Docker客户端:即默认情况下的Docker主机
    • 当使用证书连接时,仅能连接到具有该CA签名的证书的服务器
  • CA签名的证书
    • 作用:服务端和客户端证书都只对应一份信任列表,信任列表里是服务端的信息(如ip或域名等等),服务端持有服务端证书,仅接受持有客户端证书的主机访问(这里可以再加上其他的限制,详情见下文)

注意:

  • Docker服务端也可以不开启TLS验证,不过这样子很不安全,生产环境下应当尽量避免.如果只是试验性的,可以指定关闭TLS验证,但只对特定主机开放,方法见后文/etc/docker/daemon.json配置相关

  • 如果Docker服务端没有开启TLS验证,则Docker客户端不需要使用证书连接.但如果客户端不使用证书连接开启了TLS验证的服务端,则会报错,如下:

Get http://远程主机ip:2376/v1.38/version: net/http: HTTP/1.x transport connection broken: malformed HTTP response "\x15\x03\x01\x00\x02\x02".
* Are you trying to connect to a TLS-enabled daemon without TLS?
  • 如果Docker客户端连接时使用的证书内不含目的主机的信息,则会提示对方主机不在证书信任列表内,访问失败

二 使用OpenSSL创建CA和服务端密钥key

注意:将以下示例中的所有$HOST实例替换为Docker守护程序主机的域名(DNS name)(由于我没有配域名,所以直接用主机ip代替了,有影响的地方我会再做提醒)

以下步骤在Docker服务端进行:

步骤1:生成CA私钥ca-key.pem

  • 说明:
    ca-key.pem是一个临时文件,最后可以删除。
  • 指令:
openssl genrsa -aes256 -out ca-key.pem 4096

例子如下,需要设置密码并验证

扫描二维码关注公众号,回复: 2227733 查看本文章
$ openssl genrsa -aes256 -out ca-key.pem 4096
Generating RSA private key, 4096 bit long modulus
............................................................................................................................................................................................++
........++
e is 65537 (0x10001)
Enter pass phrase for ca-key.pem:
Verifying - Enter pass phrase for ca-key.pem:

步骤2:使用CA私钥生成自签名CA证书ca.pem

  • 说明:生成证书时,通过-days 365设置证书的有效期。单位为天,默认情况下为30天。有了CA证书后,就可以创建服务器密钥和证书签名请求(CSR)了
  • 指令:
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
  • 例子如下,需要验证密码并输入信息,注意”Common Name”设置为服务器所在主机的域名(我只填了Country Name 为CN,Province Name为Guangdong,后面直接回车也没关系,反正也只是自己用的证书)
$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
Enter pass phrase for ca-key.pem:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:Queensland
Locality Name (eg, city) []:Brisbane
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Docker Inc
Organizational Unit Name (eg, section) []:Sales
Common Name (e.g. server FQDN or YOUR name) []:$HOST
Email Address []:[email protected]

步骤3:生成服务器私钥server-key.pem和证书签名请求server-csr

  • 说明:
    CSR:Certificate Signing Request,证书签名请求,server-csr是一个临时文件,生成server-cert.pem以后,可以删除。
    这里分两小步
    • 第一步生成服务器私钥server-key.pem
    • 第二步使用服务器私钥另加CN信息生成证书签名请求server-csr.pem
  • 如下(注意这里的CN信息对应的是服务器所在主机域名,如果没有的话也没关系,可以通过下一步的extfile.cnf配置IP地址连接):
$ openssl genrsa -out server-key.pem 4096
Generating RSA private key, 4096 bit long modulus
.....................................................................++
.................................................................................................++
e is 65537 (0x10001)

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

步骤4:编写extfile.cnf(重要)

  • 说明:这个文件用于指定下一步生成签名证书的一些属性配置,这里我们主要用到两个属性,如果要其他要求(如限制指定ip范围的客户端才能连接)的可以看OpenSSL x509v3_config文档

    • subjectAltName
      主题备选名称,是有点像上一步生成server.csr时所用的选项-subj "/CN=$HOST"的东西,这个更像一个说明补充,这里可以填信任的DNS域名和主机IP等等,因为没有域名,所以我只填了Docker服务器的主机IP
      另外,需要特别注意,这里对应生成的是一份信任列表,这里所说的信任是对服务端的信任,所以填的是服务端的信息(如域名,IP),我之前看到有文章说这里的列表是客户端的列表,只有在列表中的客户端才能访问服务器,这种说法是错误,在使用证书连接到服务器时,会报错说服务器IP不在信任列表中( 如远程主机ip为ip3,证书的信任列表为ip1和ip2时,若使用该证书访问远程主机,则会报错x509: certificate is valid for ip1, ip2, not ip3)
    • extendedKeyUsage
      扩展密钥用法,此扩展包含一个用法列表,用于指示证书公钥可用于的目的
  • 如下
    1 将Docker守护程序密钥的扩展使用属性设置为仅用于服务器身份验证
    2 将域名(DNS Name)$HOST和IP为10.10.10.20(私有地址,用于局域网登录)和127.0.0.1(本地地址,用于本机登录)和公网ip(用于远程登录)
    注意:
    事实上只配一个公网IP就行了,其他的视实际需求而定,本机的话一般不那么麻烦,通过docker本身的配置即可使用本机控制,这里配127.0.0.1主要是为了测试是否能连通,如果只配了私有地址则只有本局域网可访问,安全性更高
echo extendedKeyUsage = serverAuth >> extfile.cnf
echo subjectAltName = DNS:$HOST,IP:10.10.10.20,IP:127.0.0.1,IP:公网ip >> extfile.cnf

步骤5:使用CA证书生成服务器签名证书server-cert.pem

注意,上面总共生成了两大模块的文件和一个extfile.cnf配置文件,这三部分之间是彼此独立的,到了这一步,也是最后一步才真正做了整合.
这里要用到的有,(1)CA私钥ca-key.pem和CA签名文件ca.pem,(2)证书签名请求server-csr(3)配置文件extfile.cnf
指令如下:

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 x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem \
  -CAcreateserial -out server-cert.pem -extfile extfile.cnf
Signature ok
subject=/CN=your.host.com
Getting CA Private Key
Enter pass phrase for ca-key.pem:

至此,Docker服务端密钥创建完毕

三 利用CA创建客户端密钥key

注意:生成密钥需要使用CA私钥和签名文件,为简化流程,避免CA文件在服务端和客户端之间的传输,以下步骤仍在Docker服务端进行

创建客户端密钥的过程和服务端类似,CA相关已经创建好了,extfile.cnf配置文件也简单很多,具体如下

步骤1:生成客户端私钥key.pem和证书签名请求client-csr

如下

$ openssl genrsa -out key.pem 4096
Generating RSA private key, 4096 bit long modulus
.........................................................++
................++
e is 65537 (0x10001)

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

步骤2:编写扩展配置文件extfile.cnf

注意,如果是在刚才创建服务器私钥的文件夹下,应该还有原来的extfile.cnf文件,为避免覆写,可以先执行重命名

mv extfile.cnf extfile.cnf.old

创建扩展配置文件并使密钥适用于客户端身份验证的指令如下:

echo extendedKeyUsage = clientAuth >> extfile.cnf

步骤3:生成签名文件cert.pem

如下

$ openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem \
  -CAcreateserial -out cert.pem -extfile extfile.cnf
Signature ok
subject=/CN=client
Getting CA Private Key
Enter pass phrase for ca-key.pem:

四 修改文件权限

步骤1:删除两个证书签名请求文件

rm -v client.csr server.csr

步骤2:修改密钥文件权限为只由所有者读取

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

步骤3:修改证书文件权限为只读

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

五 启动Docker守护进程

启动Docker守护进程有两种方法,直接用带参指令或者修改daemon.json配置文件,另外,还有一种方式使用systemctl修改docker.service文件,这种不推荐,这里不作介绍.需要注意的是,不管是哪一种方法,只要对同一属性做了配置,都会导致冲突而启动失败.所以建议只使用一种.

注意,监听unix:///var/run/docker.sock是为了实现本机docker直接控制,监听tcp://0.0.0.0:2376表示监听2376端口所有连接,又这里开启了TLS验证,则会根据我们给定的TLS文件去做验证

服务端需要的TLS文件有CA证书ca.pem,服务端证书server-cert.pem,服务端密钥server-key.pem

下面的两种方法的介绍

1 指令启动

dockerd  --tlsverify=true \
 --tlscacert=/opt/docker-ssh/ca.pem  \
 --tlscert=/opt/docker-ssh/server-cert.pem  \
 --tlskey=/opt/docker-ssh/server-key.pem  \
 --host tcp://0.0.0.0:2376  \
 --host unix:///var/run/docker.sock

2 daemon.json配置启动

配置/etc/docker/daemon.json文件如下,注意,镜像地址与本文无关,可不配置

{
 "tlsverify": true,
  "tlscacert": "/opt/docker-ssh/ca.pem",
  "tlscert": "/opt/docker-ssh/server-cert.pem",
  "tlskey": "/opt/docker-ssh/server-key.pem",
  "hosts": ["tcp://0.0.0.0:2376","unix:///var/run/docker.sock"],
  "registry-mirrors": ["https://5ehijrnq.mirror.aliyuncs.com"]
}

然后通过systemctl正常启动,指令如下

systemctl restart docker

六 验证远程控制

客户端需要的TLS文件有CA证书ca.pem,客户端证书cert.pem,客户端密钥key.pem
连接的指令格式如下,以docker version为例,其中$HOST为远程主机ip

docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=$HOST:2376 version

结果如下图:
docker version
将version换成其他即可如在本机一样控制远程docker

七 参考

https://docs.docker.com/engine/security/https/
https://www.openssl.org/docs/manmaster/man5/x509v3_config.html
https://docs.docker.com/config/daemon/
https://docs.docker.com/engine/reference/commandline/dockerd/
https://docs.docker.com/install/linux/linux-postinstall/

猜你喜欢

转载自blog.csdn.net/alinyua/article/details/81086124