C# Docker开发(二) 打开Docker的Remote API

小柊 发表于 2017年10月28日 23时21分38秒

序、扯淡

各位大家好啊,距离上一篇发布已经有快两个月了。在九月的谜之高产之后,十月又突然消失。讲到原因大概就是又滚回去上班了,难得的周末就只想在床上趴着睡觉就是了。所以一直鸽到现在。

 

一、为什么要打开Docker的远程连接?

首先要说明的是什么情况下要打开Docker的远程连接。

默认情况下,Docker默认为了安全考虑是不会启用Remote API(通俗的说就是远程访问)功能的,Docker守护进程只会生成一个Unix套接字文件(一般默认是/var/run/docker.sock文件)来进行本地通信,不会去监听任何端口,如果您的程序不能直接访问Docker的套接字文件(/var/run/docker.sock),就需要打开Docker的Remote API功能。

 

二、打开方法

打开Docker的Remote API功能非常简单。只需要修改一下Docker的配置文件就好了。

以CentOS为例:

sudo vim /etc/sysconfig/docker

 

找到“OPTIONS=”这行,在后面的引号里加上

-H 0.0.0.0:2375

 

P.S.: Docker Remote API建议使用2375端口

然后保存退出,重启Docker守护进程:

sudo systemctl restart docker.service

 

是的,现在你就可以在其他电脑上访问Docker RemoteAPI了,如果要在电脑上访问其他主机的Docker Remote API可以这么操作:

docker -H <IP地址>:<端口号> 命令

 

IP地址就是想要访问的远程主机IP,端口号如果不填的话默认使用2375端口,如果您指定的Remote API端口号不是2375端口,就需要手动指定了。

举个例子:我要查看192.168.248.129主机上的Docker版本:

docker -H 192.168.248.129 version

 

完成。

 

三、两个问题

在上一章的过程中,很多读者可能会遇到下面两个问题:

 

1.无法访问到远程主机的Docker RemoteAPI,提示getsockopt: no route to host

 

这个问题的原因提示已经说的很清楚了,没有办法连接到主机,您首先ping一下目标机,判断两机之间是否已经正确联通。如果能ping通却提示这个问题,非常大的可能是远程主机开了防火墙(例如CentOS默认会启用firewalld),尝试关闭远程主机的防火墙或者将端口放入防火墙的放行策略后再试。

 

2.无法访问主机的Docker守护进程

大致的情况就是打开了本机的Docker RemoteAPI之后,就不能在本机上直接敲Docker命令去管理Docker守护进程了,会被提示“Cannot connect to the Docker daemon. Is the docker daemon running on this host?”,但加上-H 127.0.0.1参数就可以正常访问:

 

这个问题的原因是Docker守护进程在启动时如果没有检查到-H参数,就会默认使用Unix套接字文件,而检查到有-H参数之后,就会完全根据-H的指示启动监听。

在上面的教程中,我们只是打开了对网络端口的监听,没有加上Unix文件的监听,而Docker Client默认是通过Unix套接字文件与Docker守护进程进行通信的,Docker Client找不到守护进程创建的Unix套接字文件就误以为Docker守护进程当前没有在运行。

解决的方法就是再在刚刚的位置文件中追加参数

-H unix:///var/run/docker.sock

 

保存后和之前一样重启一下Docker守护进程即可解决问题:

 

四、为了安全:保护Docker 守护进程的套接字

事实上,当前网上很多教程在完成前面讲的内容之前,就结束了,并没有考虑这个对外的Docker RemoteAPI的安全问题。事实上如果有些居心叵测的人使用扫描器等工具找到了这个端口,在管理员配置不当的情况下可以轻松的拿到宿主机的控制权。至于怎么操作,这个问题我们放下一期再说。所以,保护Docker守护进程套接字不被攻击者未授权访问这个问题刻不容缓

事实上Docker官方已经想到了这个问题,并向我们提供了一个使用TLS认证的解决方案,方案的具体内容请见Docker官方文档《Protect the Docker daemon socket》,我在这里就直接讲一下流程。

开始之前需要注意的是我们在接下来的操作中需要使用到openssl这个软件包,如果您的电脑上没有这个软件包,请使用yum或者apt-get等命令进行下载和安装:

sudo yum install -y openssl

 

下面将假设Docker守护程序部署在IP为192.168.248.129的主机中域名docker.yanning.wang指向192.168.248.129$HOST中存储着Docker守护程序主机的DNS名称(域名,不能使用IP)黑色加粗倾斜的为键入的命令,绿色字体为命令回显:

 

首先,在Docker守护程序的主机上生成CA的公私钥:

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:

 

在上面的过程中,会被要求输入两次密码,这两个密码作为CA私钥的密码。完成后你将得到CA私钥文件:ca-key.pem

 

然后我们要创建CA的证书文件:

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) [XX]: CN

State or Province Name (full name) []: Zhejiang

Locality Name (eg, city) [Default City]: Hangzhou

Organization Name (eg, company) [Default Company Ltd]: Chunfeng Tech

Organizational Unit Name (eg, section) []: IT Dept

Common Name (eg, your name or your server's hostname) []: docker.yanning.wang

Email Address []:

 

这次要求输入的东西就比较多了。首先是CA私钥文件的密码(就是上一步输入的密码),然后下面要输入的分别是:

Country Name (2 letter code) [XX] 使用国际标准组织(ISO)国码格式,填写2个字母的国家代号。中国请填写CN。
State or Province Name (full name) [] 省份,比如填写Zhejiang
Locality Name (eg, city) [Default City] 城市,比如填写Hangzhou
Organization Name (eg, company) [Default Company Ltd] 组织单位,比如填写公司名称的拼音
Organizational Unit Name (eg, section) [] 组织单位名称,比如填写IT Dept
Common Name (eg, your name or your server's hostname) [] 行使 SSL 加密的网站地址。请注意这里并不是单指您的域名,而是直接使用 SSL 的网站名称 例如:pay.abc.com。 一个网站这里定义是:

abc.com 是一个网站;

www.abc.com 是另外一个网站;

pay.abc.com 又是另外一个网站。

Email Address [] 邮件地址,可以不填

 

完成后,您可以得到您的CA公钥文件:ca.pam。

 

完成上面的步骤之后,我们有了一个CA,接下来我们就可以用它来创建服务器密钥和证书签名请求:

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

 

接下来使用我们的CA签署公钥

因为可以通过IP地址和DNS名称进行TLS连接,所以需要在创建证书时指定。 例如,允许仅192.168.248.1和192.168.248.140的使用证书:

echo subjectAltName = DNS:$HOST,IP:192.168.248.1,IP:192.168.248.140 > extfile.cnf

如果不需要指定IP地址,则去掉上面命令中的IP参数。

 

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=192.168.248.129

Getting CA Private Key

Enter pass phrase for ca-key.pem:

 

上面的命令又会要求输入CA私钥的密码。

 

对于客户端认证,创建客户端密钥和证书签名请求:

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

 

要使密钥适合客户端身份验证,请创建一个扩展名配置文件:

echo extendedKeyUsage = clientAuth > extfile.cnf

 

现在签发私钥:

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:

 

依旧,需要输入CA私钥的密码。

 

现在,我们生成了cert.pem(客户端证书)和server-cert.pem(服务端证书),为了安全起见,需要删除刚刚的两个证书签名请求:

rm -v client.csr server.csr

已删除"client.csr"

已删除"server.csr"

 

另外,为了保护私钥文件不被他人破坏或读取,需要设置只读(0400)属性:

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

mode of "ca-key.pem" changed from 0664 (rw-rw-r--) to 0400 (r--------)

mode of "key.pem" changed from 0664 (rw-rw-r--) to 0400 (r--------)

mode of "server-key.pem" changed from 0664 (rw-rw-r--) to 0400 (r--------)

 

至于证书文件是可以全组只读的,但还是要删除写入权限以防止被破坏:

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

mode of "ca.pem" changed from 0664 (rw-rw-r--) to 0444 (r--r--r--)

mode of "server-cert.pem" changed from 0664 (rw-rw-r--) to 0444 (r--r--r--)

mode of "cert.pem" changed from 0664 (rw-rw-r--) to 0444 (r--r--r--)

 

(可选)将服务端证书和私钥整理到一个合适的位置(笔者在这里将放到/etc/docker/cert.d/remoteapi/目录下)

sudo mkdir /etc/docker/certs.d/remoteapi

sudo cp ca.pem server-cert.pem server-key.pem /etc/docker/certs.d/remoteapi/

 

修改Docker守护程序的配置文件,使Docker守护程序仅接受来自提供所信任CA证书的客户端的连接:

sudo vim /etc/sysconfig/docker

 

在OPTIONS最后插入一下参数:

--tlsverify --tlscacert=/etc/docker/certs.d/remoteapi/ca.pem --tlscert=/etc/docker/certs.d/remoteapi/server-cert.pem --tlskey=/etc/docker/certs.d/remoteapi/server-key.pem -H=0.0.0.0:2376

 

P.S.: 带TLS认证的Docker Remote API建议使用2376端口

 

重启Docker守护进程以更新配置:

sudo systemctl restart docker.service

 

完成。

 

五、测试带TLS认证的Docker RemoteAPI

如果要访问带有TLS认证的Docker RemoteAPI,需要有刚刚第四步中生成的以下三个文件

ca.pem

cert.pem

key.pem

 

使用以下命令访问带TLS认证的Docker RemoteAPI

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

 

命令比之前麻烦很多。不过后面就使用C#来远程访问RemoteAPI了。

 

六、生成Windows用的PFX文件

到上面为止,我们用的都是pem格式的证书和密钥文件。然而在之后的C#连接Docker Remote API中,C#并不能使用pem文件进行远程连接,只能使用pfx文件。

生成pfx文件只需要进入刚刚生成证书和私钥文件的文件夹下,使用下面的命令进行生成:

openssl pkcs12 -export -inkey key.pem -in cert.pem -out key.pfx

Enter Export Password:

Verifying - Enter Export Password:

 

会要求输入pfx证书文件的密码。输入密码并确认之后就可以在当前目录中找到之后C#连接Docker Remote API用的key.pfx文件了。

 

七、最后

这次的博客可能写的比较乱,请各位看官原谅。在下一期,我将讲解如何在C#项目中使用Docker.DotNet程序包进行Docker交互控制,尽请期待。

 

 

 

 

小柊

2017年10月28日 23:10:51

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注