在前面的章節中咱們記錄了 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 功能正常運做的基礎,因此仍是很是有必要掌握這一問題的。