Amphorae 与 Octavia Worker 的安全通信实现
2019-03-23 23:17 云物互联 阅读(1097) 评论(0) 编辑 收藏 举报前言
在前面的章节中我们记录了 LoadBalancer、Listener、Pool、Member 等等 Octavia 核心资源对象的创建流程,本篇我们在此之上继续讨论处于 LB Management Network 上的 Amphorae 虚拟机是如何与处于 OpenStack Management Network 上的 Octavia Worker 进行安全通信的。
为什么 Octavia 需要自建 CA 证书?
首先我们提出一个问题:为什么 Octavia 需要自建 CA 而不使用 OpenStack 的通用认证体系?
答案是:For production use the ca issuing the client certificate and the ca issuing the server certificate need to be different so a hacker can’t just use the server certificate from a compromised amphora to control all the others.
简而言之,Octavia 自建 CA 证书主要有两个必要:
- amphora-agent 没有加入 OpenStack 鉴权体系,需要证书来保证通讯安全
- 防止恶意用户利用 amphora 作为 “肉鸡” 攻击 OpenStack 的内部网络
基于自建 CA 实现的 SSL 通信
Octavia 提供了自动化脚本通过 OpenSSL 指令来创建 CA 中心并自签发 CA 根证书。执行下述指令即可完成:
$ source /opt/rocky/octavia/bin/create_certificates.sh /etc/octavia/certs/ /opt/rocky/octavia/etc/certificates/openssl.cnf
NOTE:自签发即自己担保自己,用自己的私钥对自己的 CSR 进行签发。只有顶级认证角色才会自签发,所以也称为根证书,本质是签发服务器证书的公钥。
所谓 CA,在操作系统上的载体只是一个文件目录(Directory),包含了各类型秘钥的证书。CA 在信任系统中充当第三方信托机构的角色,提供证书签发和管理服务,可以有效解决非对称加密系统中常见的中间人攻击问题。更多关于 CA 中心为内容可以参考《使用 OpenSSL 自建 CA 并签发证书》,这里不再赘述。
Octavia 自建的 CA 中心:
$ ll /etc/octavia/certs/
total 44
-rw-r--r-- 1 stack stack 1294 Oct 26 12:51 ca_01.pem
-rw-r--r-- 1 stack stack 989 Oct 26 12:51 client.csr
-rw-r--r-- 1 stack stack 1708 Oct 26 12:51 client.key
-rw-r--r-- 1 stack stack 4405 Oct 26 12:51 client-.pem
-rw-r--r-- 1 stack stack 6113 Oct 26 12:51 client.pem
-rw-r--r-- 1 stack stack 71 Oct 26 12:51 index.txt
-rw-r--r-- 1 stack stack 21 Oct 26 12:51 index.txt.attr
-rw-r--r-- 1 stack stack 0 Oct 26 12:51 index.txt.old
drwxr-xr-x 2 stack stack 20 Oct 26 12:51 newcerts
drwx------ 2 stack stack 23 Oct 26 12:51 private
-rw-r--r-- 1 stack stack 3 Oct 26 12:51 serial
-rw-r--r-- 1 stack stack 3 Oct 26 12:51 serial.old
- newcerts dir:存放 CA 签署(颁发)过的数字证书
- private dir:存放 CA 的私钥
- serial file:存放证书序列号(e.g. 01),每新建一张证书,序列号会自动加 1
- index.txt file:存放证书信息
- ca_01.pem PEM file:CA 证书文件
- client.csr file:Server CSR 证书签名请求文件
- client.key file:Server 私钥文件
- client-.pem:PEM 编码的 Server 证书文件
- client.pem:结合了 client-.pem 和 client.key 的文件
列举 Octavia 与 CA 认证相关的配置项:
- 应用于 Create Amphora Flow 中的 TASK:GenerateServerPEMTask,生成 Amphora 私钥并签发 Amphora 证书。
[certificates]
ca_private_key_passphrase = foobar
ca_private_key = /etc/octavia/certs/private/cakey.pem
ca_certificate = /etc/octavia/certs/ca_01.pem
- 应用于 Octavia Worker 的 AmphoraAPIClient,拿着 CA 根证书(是 Amphora 证书的公钥,可以解开 Amphora 证书得到 Amphora 的公钥)和 Amphora 证书向 amphora-agent 发起 SSL 通信。
[haproxy_amphora]
server_ca = /etc/octavia/certs/ca_01.pem
client_cert = /etc/octavia/certs/client.pem
- 应用于 Task:CertComputeCreate,指定 CA 根证书的路径
[controller_worker]
client_ca = /etc/octavia/certs/ca_01.pem
Amphora Agent 启动加载证书
首先看为 Amphorae 生成证书的代码实现:
# /opt/rocky/octavia/octavia/controller/worker/tasks/cert_task.py
class GenerateServerPEMTask(BaseCertTask):
"""Create the server certs for the agent comm
Use the amphora_id for the CN
"""
def execute(self, amphora_id):
cert = self.cert_generator.generate_cert_key_pair(
cn=amphora_id,
validity=CERT_VALIDITY)
return cert.certificate + cert.private_key
Octavia Certificates 功能模块提供了 local_cert_generator(default)
和 anchor_cert_generator
两种证书生成器,通过配置项 [certificates] cert_generator
选用。
# /opt/rocky/octavia/octavia/certificates/generator/local.py
@classmethod
def generate_cert_key_pair(cls, cn, validity, bit_length=2048,
passphrase=None, **kwargs):
pk = cls._generate_private_key(bit_length, passphrase)
csr = cls._generate_csr(cn, pk, passphrase)
cert = cls.sign_cert(csr, validity, **kwargs)
cert_object = local_common.LocalCert(
certificate=cert,
private_key=pk,
private_key_passphrase=passphrase
)
return cert_object
上述 LocalCertGenerator.generate_cert_key_pair
Method 的语义是:
- 生成 Amphora 私钥
- 生成 Amphora 证书签名请求(CSR)
- 向 CA 中心申请签署 Amphora证书
属于常规的证书创建流程,与 create_certificates.sh 脚本的区别在于,Octavia Certificates 应用了 cryptography python 库而非 OpenSSL 来实现。
TASK:GenerateServerPEMTask 最终 return 了 Amphora 私钥和证书,然后实现 TASK:CertComputeCreate 将这些文件注入到 Amphora 虚拟机。登录 Amphora 即可查看这些文件,路径记录在配置文件中:
# /etc/octavia/amphora-agent.conf
[amphora_agent]
# Octavia Worker 的证书
agent_server_ca = /etc/octavia/certs/client_ca.pem
# Amphora 的私钥和证书
agent_server_cert = /etc/octavia/certs/server.pem
Gunicorn HTTP Server 启动时就会将证书文件加载, 加载证书的 options 如下:
options = {
'bind': bind_ip_port,
'workers': 1,
'timeout': CONF.amphora_agent.agent_request_read_timeout,
'certfile': CONF.amphora_agent.agent_server_cert,
'ca_certs': CONF.amphora_agent.agent_server_ca,
'cert_reqs': True,
'preload_app': True,
'accesslog': '/var/log/amphora-agent.log',
'errorlog': '/var/log/amphora-agent.log',
'loglevel': 'debug',
}
AmphoraAPIClient 发送证书请求
class AmphoraAPIClient(object):
def __init__(self):
super(AmphoraAPIClient, self).__init__()
...
self.session = requests.Session()
self.session.cert = CONF.haproxy_amphora.client_cert
self.ssl_adapter = CustomHostNameCheckingAdapter()
self.session.mount('https://', self.ssl_adapter)
...
def request(self, method, amp, path='/', timeout_dict=None, **kwargs):
...
LOG.debug("request url %s", path)
_request = getattr(self.session, method.lower())
_url = self._base_url(amp.lb_network_ip) + path
LOG.debug("request url %s", _url)
reqargs = {
'verify': CONF.haproxy_amphora.server_ca,
'url': _url,
'timeout': (req_conn_timeout, req_read_timeout), }
reqargs.update(kwargs)
headers = reqargs.setdefault('headers', {})
...
上述代码是 requests 库启用 HTTPS 请求的常规实现:
self.session.cert
:上传 Octavia Worker 私钥和证书,用于 Amphora 发起的 SSL 通信reqargs = {'verify': CONF.haproxy_amphora.server_ca, ...}
:携带 Amphora 证书,向 Amphora 发起 SSL 通信
小结
梳理 Octavia 建立 SSL 通信的步骤:
- 创建 Amphora 的过程中 Octavia Worker 会首先生成 Amphora 的私钥,并且向 CA 中心申请签发 Amphora 证书(内含 Amphora 公钥),此时 Amphora 的私钥、证书都会准备好
- Octavia Worker 通过 Config Driver 将 Amphora 的私钥、Amphora 的证书作为 user data 注入到 Amphora 虚拟机。
- Amphora 虚拟机上运行的 amphora-agent Web Server 启动时 Flask app 就会加载 Amphora 的私钥和证书,并启用 HTTPS 通讯协议。
- Octavia Worker 的 AmphoraAPIClient 首次想 amphora-agent 发送请求时,首先会下载 Amphora 证书,然后与自己手上的 CA 根证书解密出 Amphora 的公钥,然后再与 Amphora 的私钥进行匹配。
- 若匹配成功,则建立 SSL 安全通信。
NOTE: 同样的 Amphora 如果希望向 Octavia Worker 自动发起建立 SSL 通信,那么 Amphora 就需要拿着 Octavia Worker 的证书进行访问。所以 Octavia 的证书也同样会被注入到 Amphora。
最后
本篇是《OpenStack Rocky Octavia 实现与分析》系列的第四篇,主要讨论 Amphora 是如何与 Octavia Worker 建立双向的 SSL 安全通信的问题。实话说,Octavia 在解决这个问题的时候并不那么清晰明了,从命名到代码的实现都弥漫着混乱的氛围,需要细细梳理才得以清晰。并且 Octavia 和 Amphora 能够健康通信又是 LBaaS 功能正常运作的基础,所以还是非常有必要掌握这一问题的。