在前面的章节中咱们记录了 LoadBalancer、Listener、Pool、Member 等等 Octavia 核心资源对象的建立流程,本篇咱们在此之上继续讨论处于 LB Management Network 上的 Amphorae 虚拟机是如何与处于 OpenStack Management Network 上的 Octavia Worker 进行安全通讯的。python
首先咱们提出一个问题:为何 Octavia 须要自建 CA 而不使用 OpenStack 的通用认证体系?web
答案是: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.json
简而言之,Octavia 自建 CA 证书主要有两个必要:promise
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 中心:session
$ 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
列举 Octavia 与 CA 认证相关的配置项:app
[certificates] ca_private_key_passphrase = foobar ca_private_key = /etc/octavia/certs/private/cakey.pem ca_certificate = /etc/octavia/certs/ca_01.pem
[haproxy_amphora] server_ca = /etc/octavia/certs/ca_01.pem client_cert = /etc/octavia/certs/client.pem
[controller_worker] client_ca = /etc/octavia/certs/ca_01.pem
首先看为 Amphorae 生成证书的代码实现:svg
# /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 的语义是:
属于常规的证书建立流程,与 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', }
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 通讯的步骤:
NOTE: 一样的 Amphora 若是但愿向 Octavia Worker 自动发起创建 SSL 通讯,那么 Amphora 就须要拿着 Octavia Worker 的证书进行访问。因此 Octavia 的证书也一样会被注入到 Amphora。
本篇是《OpenStack Rocky Octavia 实现与分析》系列的第四篇,主要讨论 Amphora 是如何与 Octavia Worker 创建双向的 SSL 安全通讯的问题。实话说,Octavia 在解决这个问题的时候并不那么清晰明了,从命名到代码的实现都弥漫着混乱的氛围,须要细细梳理才得以清晰。而且 Octavia 和 Amphora 可以健康通讯又是 LBaaS 功能正常运做的基础,因此仍是很是有必要掌握这一问题的。