月度归档:2016年05月

自建 CA 和 PKI 相关内容

企业内部服务互相认证,或是为内部服务颁发 ssl 证书,包括有些 wifi 认证证书的话,一般会选择使用自建 CA 来解决,既保证了灵活性,又节约了成本。
目前有一个自建 docker registry 的需求,所以就研究了一下自建 CA 及其相关内容。因为之前对 PKI 这部分的东西了解的比较少,所以这一次花了一些时间补了一下课。

首先从最基本的认识开始,如果需要被访问的 https 站点被信任,那么这个站点的证书需要在系统的信任列表中。而证书,是分层级的。分为根证书和由根证书颁发的证书。想要站点被信任,只要信任根证书,或者是颁发的证书,都可以。

重要的是,如果信任了根证书,就相当于信任了所有由这个根证书颁发出来的证书。也就是说,在企业内部,客户端只要信任了根证书,就不用麻烦的每次信任新的颁发证书了。

第一步,需要创建一个根证书,分为两步,
1. 创建私钥,后续用来颁发证书的基础,整个体系最重要的东西。
2. 用私钥生成根证书,提供给客户端信任,同时配合私钥,用来颁发证书

1. 创建私钥:创建私钥非常简单,openssl genrsa -out /etc/pki/CA/private/cakey.pem 4096 ,就可以了。这句话里面包含了很多的信息,我一个一个讲清楚。
a) genrsa 是选择用 rsa 算法生成 key
b) 把文件生成到位置 /etc/pki/CA/private/cakey.pem,是因为在本地颁发证书时,默认使用这个位置的私钥进行颁发。这个位置,是配置在 /etc/pki/tls/openssl.cnf 文件中的,这个文件中还有很多相关内容,一会儿会把重要的配置都列出来。
c) 4096,生成多少位的 rsa key
2. 用私钥生成根证书:openssl req -new -days 3650 -x509 -key /etc/pki/CA/private/cakey.pem -out /etc/pki/CA/cacert.pem,即创建一个 x509 格式的证书,输出到 /etc/pki/CA/cacert.pem,同样的,颁发证书所需要的根证书,默认要放在这个位置,也是 /etc/pki/tls/openssl.cnf 中配置的。

两次提到 openssl.cnf,就把里面的有用的选项拿出来列一下。
1. [ ca ] 下面配置了 ca 证书相关的目录等信息,如:private_key 就是刚才的私钥位置,certificate 就是刚才证书的位置。database 是放置颁发证书的列表位置,serial 存放了当前颁发证书的序号。crl 比较重要,配置失效颁发证书的位置。default_days 默认证书有效时间。大部分配置默认就可以了,证书有效期可以改长点儿,省得麻烦。default_md 不能配置成 sha1,最少也要用 sha256,否则 chrome 会报警告。
2. [ req ] 里面的 default_md 配置了默认的 digest 算法,如果配置成 default 会默认为 sha1,如果用 sha1 来进行 digest,chrome 会报出警告。所以这里要修改成 sha256
3. [ req_distinguished_name ] 里面配置了生成证书的默认配置,为了自己生成证书省事儿,可以修改成常用的,比如 countryName_default = CN
4. [ usr_cert ] 配置了 x509_extensions 信息 nsCaRevocationUrl 比较有用,配置 crl 的地址

第二步,颁发证书,分为三步
1. 创建私钥
2. 创建 csr 用来向 ca 请求证书,包含了请求信息,和公钥
3. 把 csr 发送给 ca,颁发证书

在说明怎么颁发证书之前,先说明一下私钥、公钥、证书这些文件的存储,存储的编码方式有两种,pem 和 der,pem 是 base64 文本的(加了开头一结尾),der 是二进制的。查看 pem 文件信息可以使用 openssl [x509|req|rsa] -in xxx.pem -noout -text 就可以,x509|req|rsa 分别查看 x509, req 和 rsa 密钥内容的文件。如果是 der 的,需要在后面加上 -inform der
还有一种没用到的 pkcs12 文件,这种格式的文件可以包含多组公私钥,而且可以通过密码保护起来。和 java 中经常使用 jks 文件格式功能类似。

那么继续颁发证书
1. 创建私钥,和之前一样:openssl genrsa -out nginx.key 4096
2. 创建 csr:openssl req -new -key nginx.key -out nginx.csr
3. 颁发证书:第一次颁发证书之前,要初始化列表和序号:echo -n > /etc/pki/CA/index.txt;echo 01 > /etc/pki/CA/serial,然后把 csr 文件传到之前创建根证书的机器上,运行 openssl ca -in nginx.csr -out nginx.crt,再把 nginx.crt 文件发回给之前的机器,就可以了。
生成的 crt 文件中,不仅包含了 csr 文件中所有的信息和公钥,还包含了用 ca 私钥签名的部分,以证明是被 ca 颁发的。

多说一句,查看 csr 文件,就可以使用 openssl req -in nginx.csr -noout -text 查看内容。

这两步,主要的问题有几点:
1. 对概念不清晰,导致只能照猫画虎,比如私钥、ca、pem、csr 等乖
2. 默认的 digest 一定要修改为 sha256,sha1 在 chrome 上不被信任
3. 不知道查看文件信息的办法,两眼一码黑。只要使用 openssl [x509|req|rsa] -in xxx.pem -noout -text 就能看到生成文件相关信息,非常方便。

在 docker client 中信任 ca
docker 中增加 ca,需要在 /etc/docker/certs.d 目录中创建和 registry 一样的目录,比如 nexus.scalaone.com:33333,并把证书以扩展名 crt 放在这个目录下,docker 会按字母顺序找,直到匹配。
docker 并没有支持全局的 ca 证书,全局的 ca 证书要靠系统的证书机制。
在 centos 中,需要把 cacert.pem copy 到 /etc/pki/ca-trust/source/anchors/ 目录下,以 crt 结尾。然后执行 update-ca-trust extract 更新一下,就可以了。这里我踩了一个大坑,加完证书,一定要 service docker restart 一下,否则没用的。增加 docker 自己的证书,是不用重启的。

如果使用 nexus 3 作为 docker registry 的话,就需要把 nginx 的证书转换成 java 的 jks 文件。
直接生成一个 jks 的方法很简单,只要:keytool -genkey -keyalg RSA -dname "cn=nexus.scalaone.com,ou=scalaone,o=scalaone.com,l=china,st=shanghai,c=cn" -alias server -keypass password -keystore keystore.jks -storepass password,就可以了。
把现有的 nginx.key 和 nginx.crt 转成 jks,会麻烦一些。
1. 先把公私钥合成一个 p12 文件 openssl pkcs12 -export -in nginx.crt -inkey nginx.key -out nginx.p12
2. 用 java 的 keytool 把 p12 存到 jks 文件中 keytool -importkeystore -srckeystore nginx.p12 -srcstoretype PKCS12 -destkeystore keystore.jks -deststoretype JKS

这时,其实不能用,用 keytool -list -keystore keystore.jks 会发现,里面包含一个 PrivateKeyEntry,但名字是 1,直接给 jetty 用,是无法使用的,所以需要:
3. 把 keystore 中的 PrivateKeyEntry 1 的 alias 改成 server:keytool -changealias -keystore keystore.jks -alias 1 -destalias server -storepass password,就可以了,因为对 jks 不是很熟悉,也花了一些时间。

到这里,就需要使用 docker push 和 docker pull 把自建的 registry 使用起来了,比如 docker tag centos nexus.scalaone.com:33333/centos,然后 docker push nexus.scalaone.com:33333/centos,使用的时候也一样,docker pull nexus.scalaone.com:33333/centos
遇到一个问题,docker 1.7.1 和 nexus 3 有个 bug,不能 push,docker 更高版本就可以了。

nexus 3 和 maven repo 一样,可以配置两类的 docker registry,一种是 host,就是上面那种,另外一种就是 proxy,增加一个 docker proxy mirror,然后需要配置 docker,增加这个私有的 registry 加到的默认 registry 列表中。

加的方法如下:
配置环境 centos 7,docker 通过 systemctl 启动的

systemctl 有个机制,也是 docker 建议的(https://docs.docker.com/engine/admin/systemd/),把配置文件放在 /etc/systemd/system/docker.service.d,并以 .conf 结尾的,名字随意,就能够自动加载,主要是增加了一个 registry,我的配置如下:

registry.conf


[Service]
Environment="ADD_REGISTRY=--add-registry nexus.scalaone.com"

当下次 docker pull 的时候,就会优先从 nexus.scalaone.com 下载了,如果没有,还会继续从 docker.io 下载的。

ps:
遇到一个坑,根证书默认有效期是 30 天,玩了一段时间后,根证书过期了,这个郁闷啊。生成根证书增加参数 -days 3650。

APNManager 能够生成在 4G 环境下使用的 proxy

github:
git clone https://github.com/realityone/APNManager
cd APNManager
pip install tornado redis
python apn_manager.py

然后访问 http://xxx.xxx.xxx.xxx:8000/
页面上可以写 host port,并 generate 就可以生成在 iOS 上安装的 profile

尝试过,正常使用

ps:

国内外各租用一台机器,linode 和 aliyun
linode 安装 shadowsocks,启动了多个端口
aliyun 安装 cow 使用多个端口