直接动手配置之前,必须搞清楚一个核心事实:Cassandra的节点间通信和客户端通信是两套独立的加密体系。很多人只配了客户端到集群的TLS,却忽略了节点之间数据传输依然是明文的,这等于给数据库留了后门。Cassandra的TLS配置需要同时覆盖internode_encryption和client_encryption两个模块,缺一不可。
生成证书体系:内部CA与节点证书Cassandra的TLS依赖标准的X.509证书体系。生产环境必须避免使用自签名证书的草率做法,而是要搭建内部CA。先用OpenSSL生成根密钥和根证书,这个根证书将作为集群内所有节点和客户端的信任锚点。
创建CA私钥,长度至少2048位,推荐使用RSA 4096或ECDSA算法。执行命令时务必保护好私钥文件的权限,设置umask 077确保只有当前用户可读写。
umask 077 openssl genrsa -out ca-key.pem 4096 openssl req -new -x509 -days 3650 -key ca-key.pem -out ca-cert.pem -subj "/CN=CassandraCA"
接下来为每个Cassandra节点生成独立的密钥对和证书签名请求。节点的证书必须包含正确的SAN扩展,否则Java的证书校验会直接拒绝连接。SAN中需要列出节点的IP地址和主机名,两者都要写全,因为Cassandra的gossip协议可能用IP也可能用主机名通信。
创建节点密钥和CSR时,需要准备一个openssl.cnf配置文件来指定SAN扩展。直接在命令行里拼凑参数容易出错,写进配置文件最稳妥。
[req] default_bits = 4096 prompt = no default_md = sha256 distinguished_name = dn req_extensions = req_ext [dn] CN = node1.cassandra.local [req_ext] subjectAltName = @alt_names [alt_names] DNS.1 = node1.cassandra.local DNS.2 = localhost IP.1 = 192.168.1.101 IP.2 = 127.0.0.1
用这个配置文件生成节点私钥和CSR,然后用CA证书签发节点证书。签发时同样要指定SAN扩展,否则签发出来的证书会丢失这些关键信息。
openssl genrsa -out node1-key.pem 4096 openssl req -new -key node1-key.pem -out node1.csr -config openssl.cnf openssl x509 -req -in node1.csr -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out node1-cert.pem -days 3650 -extensions req_ext -extfile openssl.cnf
客户端证书的生成流程类似,但CN和SAN应该反映客户端身份而非节点信息。如果客户端数量较多,可以考虑为每个应用或每个服务账号签发独立证书,这样在审计日志中能清晰区分访问来源。
密钥库转换:Java生态的必经之路Cassandra运行在JVM上,它只认Java KeyStore格式。必须把PEM格式的证书和密钥转换成JKS或PKCS12格式。推荐使用PKCS12,它是跨语言的标准格式,兼容性更好,而且Cassandra从3.0版本开始对PKCS12的支持已经非常成熟。
转换节点证书和私钥时,需要先把它们打包成PKCS12文件,再导入到KeyStore。注意私钥在PKCS12中必须设置密码保护,这个密码后续要写入cassandra.yaml配置。
openssl pkcs12 -export -in node1-cert.pem -inkey node1-key.pem -out node1-keystore.p12 -name cassandra-node -passout pass:changeit
信任库则存放所有受信任的CA证书,节点通过它来验证对端证书的合法性。把CA根证书导入信任库时,使用keytool命令更直接。
keytool -import -trustcacerts -alias cassandra-ca -file ca-cert.pem -keystore truststore.jks -storepass changeit -noprompt
客户端证书同理,为客户端生成独立的PKCS12密钥库,包含客户端的私钥和证书链。如果客户端也是Java应用,直接使用PKCS12或JKS格式即可;如果是Python或Go等其他语言,可以继续使用PEM格式,但Cassandra服务端只认Java KeyStore。
cassandra.yaml核心加密配置证书准备完毕后,修改cassandra.yaml是真正生效的关键步骤。配置项分散在文件的不同位置,需要逐一定位修改。先处理节点间通信加密,找到server_encryption_options段落。
server_encryption_options:
internode_encryption: all
keystore: /etc/cassandra/ssl/node1-keystore.p12
keystore_password: changeit
truststore: /etc/cassandra/ssl/truststore.jks
truststore_password: changeit
protocol: TLSv1.2
algorithm: SunX509
store_type: PKCS12
cipher_suites: [TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
require_client_auth: true
internode_encryption设置为all表示所有节点间通信都走TLS,包括gossip和streaming。protocol明确指定TLSv1.2,禁止使用TLSv1.0和TLSv1.1这些已知存在漏洞的版本。cipher_suites只列出强加密套件,避免使用包含CBC模式的套件,因为CBC模式容易受到Padding Oracle攻击。require_client_auth设为true意味着节点之间也要双向证书验证,每个节点必须用合法证书证明自己身份。
客户端加密配置在client_encryption_options段落,结构类似但有几个关键差异。
client_encryption_options:
enabled: true
optional: false
keystore: /etc/cassandra/ssl/node1-keystore.p12
keystore_password: changeit
truststore: /etc/cassandra/ssl/truststore.jks
truststore_password: changeit
protocol: TLSv1.2
algorithm: SunX509
store_type: PKCS12
cipher_suites: [TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
require_client_auth: true
optional参数控制是否允许客户端不加密连接。生产环境必须设为false,强制所有客户端连接使用TLS。如果设为true,Cassandra会接受明文连接作为降级方案,这会让加密形同虚设。require_client_auth启用双向TLS,客户端必须提供有效证书,这比仅服务端证书的单向TLS安全得多,能有效防止未授权的客户端连接。
权限设置与文件保护证书文件和密钥库文件的权限配置是容易被跳过的环节。Cassandra进程通常以cassandra用户运行,这些文件必须对该用户可读,但不能让其他用户访问。私钥文件和密钥库文件包含敏感密钥材料,权限应设为400或600,属主为cassandra用户。
chown -R cassandra:cassandra /etc/cassandra/ssl/ chmod 600 /etc/cassandra/ssl/*-key.pem chmod 600 /etc/cassandra/ssl/*.p12 chmod 600 /etc/cassandra/ssl/*.jks chmod 644 /etc/cassandra/ssl/*-cert.pem
密钥库密码同样需要保护。虽然cassandra.yaml中必须明文写入密码,但可以通过文件系统权限限制对该配置文件的访问。cassandra.yaml本身应设为640权限,仅cassandra用户和其所属组可读。更严格的做法是使用Cassandra的密钥库密码文件机制,将密码存储在独立文件中,cassandra.yaml里引用文件路径而非明文密码,不过这个特性在部分版本中支持有限,需要验证具体版本的文档。
滚动重启与验证修改完配置后不能一次性重启整个集群,必须逐节点滚动重启,否则会导致服务完全中断。先在一个非种子节点上应用新配置并重启,观察日志确认TLS握手成功,节点重新加入集群且gossip通信正常。
重启后检查system.log中的关键信息。正常的TLS握手日志会显示加密协议和套件协商结果,如果看到javax.net.ssl.SSLHandshakeException或证书校验错误,说明证书配置有问题。常见的错误包括SAN不匹配、证书过期、信任库未包含正确的CA证书等。
使用nodetool工具验证节点状态,gossipinfo命令可以查看节点间通信是否正常。
nodetool gossipinfo nodetool status
客户端连接验证可以使用cqlsh。cqlsh从3.0版本开始支持SSL连接,需要在cqlshrc配置文件中指定证书路径。
[ssl] certfile = /path/to/client-cert.pem userkey = /path/to/client-key.pem validate = true
连接时如果提示证书验证失败,检查客户端证书是否由同一个CA签发,以及客户端的SAN是否被服务端信任库认可。双向TLS环境下,服务端不仅验证客户端证书的签发链,还会检查证书是否在信任库中,两者缺一不可。
证书轮换策略证书都有有效期,过期后整个集群会瞬间瘫痪。证书轮换必须在过期前完成,而且轮换过程不能中断服务。Cassandra支持动态加载证书的机制有限,大多数版本需要重启节点才能加载新证书,这就要求轮换过程与滚动重启紧密结合。
推荐的轮换方案是提前生成新证书,在旧证书过期前至少留出两周的缓冲期。轮换时先将新证书部署到所有节点,然后逐个重启。Cassandra的TLS握手发生在连接建立时,已建立的连接不会因为证书更新而中断,滚动重启过程中集群可以继续提供服务。
更高级的做法是使用短期证书和自动化轮换,结合HashiCorp Vault或cert-manager等工具自动签发和更新证书。但这需要额外的基础设施支持,并且要与Cassandra的重启流程集成,实现自动化的滚动重启。
性能影响与优化TLS加密会带来CPU开销,尤其是在高吞吐量场景下。AES-GCM套件在现代CPU上通常有硬件加速支持,性能损耗在5%到15%之间。如果使用ECDHE密钥交换算法,握手阶段的计算开销比RSA密钥交换低很多,推荐优先选择ECDHE套件。
连接池配置也需要相应调整。TLS握手比TCP三次握手昂贵得多,频繁创建新连接会导致显著的延迟抖动。客户端应使用连接池,保持长连接,避免频繁的TLS握手。Cassandra驱动通常默认启用连接池,但需要根据实际负载调整池大小,确保连接复用率足够高。
节点间通信的TLS开销相对固定,因为节点间连接是长期保持的,握手只在节点启动或连接断开重建时发生。真正需要关注的是客户端连接模式,如果应用层每次查询都新建连接,TLS的开销会被急剧放大。
整个配置过程的核心在于证书体系的严谨性和配置项的正确性。任何一步的疏忽都可能导致加密失败或服务不可用。测试环境先完整验证,再谨慎地滚动推送到生产环境,这是唯一稳妥的路径。
