Amphorae 与 Octavia Worker 的安全通信实现
前言
在前面的章节中我们记录了 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 功能正常运作的基础,所以还是非常有必要掌握这一问题的。
Amphorae 与 Octavia Worker 的安全通信实现的更多相关文章
- Octavia 的实现与分析(OpenStack Rocky)
目录 文章目录 目录 Octavia 基本对象概念 基本使用流程 软件架构 服务进程清单 代码结构 loadbalancer 创建流程分析 network_tasks.AllocateVIP netw ...
- octavia的实现与分析(二)·原理,基本架构与基本流程
[了解] 其实说白了,Octavia就是将用户的API请求经过逻辑处理,转换成Haproxy或者Nginx的配置参数,下发到amphora虚机中. Octavia的内部实现中,逻辑流程的处理主要使用T ...
- openstack octavia的实现与分析(二)原理,架构与基本流程
[了解] 其实说白了,Octavia就是将用户的API请求经过逻辑处理,转换成Haproxy或者Nginx的配置参数,下发到amphora虚机中. Octavia的内部实现中,逻辑流程的处理主要使用T ...
- Octavia 创建 Listener、Pool、Member、L7policy、L7 rule 与 Health Manager 的实现与分析
目录 文章目录 目录 创建 Listener 创建 Pool 创建 Member CalculateDelta HandleNetworkDeltas AmphoraePostNetworkPlug ...
- Octavia 的 HTTPS 与自建、签发 CA 证书
目录 文章目录 目录 Octavia 为什么需要自建 CA 证书? GenerateServerPEMTask CertComputeCreate Amphora Agent AmphoraAPICl ...
- Octavia 创建 loadbalancer 的实现与分析
目录 文章目录 目录 从 Octavia API 看起 Octavia Controller Worker database_tasks.MapLoadbalancerToAmphora comput ...
- Octavia 项目加速 OpenStack LBaaS 落地大规模应用场景
目录 文章目录 目录 OpenStack LBaaS Octavia 软件架构 网络架构 操作对象基本概念 功能实现基本概念 Ocatvia Daemon 列表 部署 Ocatvia 手动方式集成 O ...
- octavia的实现与分析(一)·openstack负载均衡的现状与发展以及lvs,Nginx,Haproxy三种负载均衡机制的基本架构和对比
[负载均衡] 大量用户发起请求的情况下,服务器负载过高,导致部分请求无法被响应或者及时响应. 负载均衡根据一定的算法将请求分发到不同的后端,保证所有的请求都可以被正常的下发并返回. [主流实现-LVS ...
- Octavia Rocky UDP 负载均衡功能试验
目录 文章目录 目录 前言 UDP 简述 功能验证 网络拓扑 资源对象清单 验证 TS Amphorae UDP 包被非法篡改 存疑 前言 以往,Octavia 通过 HAProxy + Keepal ...
随机推荐
- java string split 怎么保留尾部空字符串
# 不保留尾部空字符串 public class QQ { public static void main(String[] args) { String str = "a,b,c,d,&q ...
- python生成器并行实例
生成器并行实例: send发送值被yield接受到赋值给baozi变量 #yield作用只是在这里保存这个值的当前状态然后返回之后在调用next,又回到yield #单纯调用next不会给yield传 ...
- springboot jpa junit测试遇到的问题
jpa在插入数据的时候,插入的对象变量user中不能包含变量,需要时确切的值,否则会出现sql解析报错 解析报错如下图
- A*算法实现(图形化表示)——C++描述
概要 A*算法是一种启发式寻路算法,BFS是一种盲目的无目标的搜索算法,相比于BFS,A*算法根据适应度构建优先队列,根据适应度值可以很好的向目标点移动,具体详情,请看搜索相关文档,我在只是实现了在无 ...
- python 异常处理(五)
异常处理&异常基类 1.处理异常 try.....except 语法: 1) try: 放可能会出现问题的代码 except: 处理错误的方式 例如: try: print(ab) #无错执 ...
- 最近老是有兄弟问我,Vue双向绑定的原理,以及简单的原生js写出来实现,我就来一个最简单的双向绑定,原生十行代码让你看懂原理
废话不多说直接看效果图 代码很好理解,但是在看代码之前需要知道Vue双向绑定的原理其实就是基于Object.defineProperty 实现的双向绑定 官方传送门 这里我们用官方的话来说Object ...
- Kubernetes 基本概念和术语
Kubernetes 基本概念和术语 Kubernetes 中大部分概念如 Node.Pod.Replication Controller. Service 等都可以看做一种 "资源对象&q ...
- jquery focus()方法 语法
jquery focus()方法 语法 作用:当元素获得焦点时,发生 focus 事件.大理石平台价格 触发focus事件语法:$(selector).focus() 将函数绑定到focus事件语法: ...
- 【PKUSC2019】线弦图【计数】【树形DP】【分治FFT】
Description 定义线图为把无向图的边变成点,新图中点与点之间右边当且仅当它们对应的边在原图中有公共点,这样得到的图. 定义弦图为不存在一个长度大于3的纯环,纯环的定义是在环上任取两个不相邻的 ...
- Springboot入门实战, 使用@Value
今天开始最简单的Springboot应用 entity.Book package com.draymonder.amor.entity; import java.util.List; import o ...