面向网络安全的数据工程——网络加密

95 阅读17分钟

数据工程师应使用加密来保护在各个工具之间传输的敏感日志数据。本章将介绍 TLS(Transport Layer Security,传输层安全协议) ——一种对网络连接进行加密的协议,用于防止对在途数据的窃听与篡改。

把加密用于组织内部工具之间的通信,表面看似不如公开互联网通信那样关键,但内部加密同样重要,因为它保护的是组织中最敏感的数据。若攻击者或心怀不满的员工获得了通信设备的访问权限,或能嗅探经过某网卡的数据,他们就可能在存储允许的范围内尽可能多地窃取数据。内部加密会将其捕获内容“搅乱”,使敏感数据无法被利用或修改;因此,在搭建数据管道时应始终启用加密。

我们将先创建 TLS 证书颁发机构(CA)非对称密钥已签名证书,以便本书所涉及的工具彼此建立加密连接。我们还会配置 SSH(Secure Shell) ,它为工作站与服务器之间的远程终端访问与文件传输提供加密。

TLS 组件(TLS Components)

在客户端与服务器之间建立的 TLS 连接中,至少有一方要向另一方证明身份;随后双方再把会话包裹进安全信道后传输数据。为此,TLS 需要若干核心组件:双方都信任的证书颁发机构(CA) 、用于加解密的公钥与私钥证书签名请求(CSR)以及最终生成的已签名证书

CA 是受信任的实体,负责签发可供网络服务加密数据用的已签名证书。没有 CA,就无法验证彼此身份,也就不应交换任何加密数据。可以把 CA 类比为车管所:车管所向你签发驾驶执照(类似证书),你据此在公路上合法驾驶;若被检查,则通过中央数据库进行验证。

TLS 依赖通信双方各自持有一对公钥/私钥。使用公钥加密、私钥解密的机制称为非对称加密。其要点超出本章范围,但本质是:任何人都可以持有你的公钥并用它来加密数据,但无法解密;只有你的私钥能够完成解密。

证书签名请求(CSR)包含将写入最终证书的信息(包括但不限于公钥及其相关信息、证书有效的服务器、组织与所在地等)。CSR 类似你申请驾驶执照的材料:审批者据此作出决定并签发证书。CA 根据 CSR 的内容创建已签名证书。CSR 本身不是“签名文件”;而是由 OpenSSL 生成一个新的已签名证书文件。Web 服务器使用该证书与其匹配的私钥,与浏览器建立 TLS 连接。

交互过程(Interactions)

用一个示例串起这些 TLS 组件:假设 Bob 想访问 Alice 餐馆的网站。他输入网址,浏览器完成 DNS 解析TCP 三次握手,随后开始 TLS 会话建立。浏览器首先发送 Client Hello,告知 Alice 自己支持的 TLS 版本密码套件;Alice 的服务器以 Server Hello 响应,携带已签名证书、一个随机串以及其支持的密码套件。

Bob 的浏览器拿到 Alice 的已签名证书后,会据证书中标注的 CA 进行校验。由于 Bob 的浏览器也信任该 CA,它会生成第二个随机串,称为 premaster secret(预主密钥) ,并用 Alice 的公钥将其加密后发回服务器。服务器用自己的私钥解密该预主密钥——因为消息是用 Alice 的公钥加密的,故只有 Alice能解开。

接下来,借助一些“高等数学”,Bob 的浏览器与 Alice 的服务器分别基于Alice 先前发出的随机串Bob 的预主密钥生成相同的、会话专用的对称密钥。双方随后用该对称会话密钥互发一条消息确认“握手完成”。自此,浏览器与服务器将使用这些会话密钥加密所有数据——因为对称加密通常比非对称加密更快。旁人将无法知道 Bob 在 Alice 餐馆点了什么。

双向 TLS(Mutual TLS, mTLS)

注意,上述过程里只有 Alice 的服务器发送了已签名证书。Alice 并不关心 Bob 是谁,只要他来“看菜单”即可。但如果餐馆需要核验顾客身份,Bob 也要向 Alice 出示已签名证书,双方就会建立双向 TLS(mTLS) 会话。

双方都需要认证彼此时,客户端与服务器会使用 mTLS;这与“仅由客户端(如浏览器)验证服务器”的常见情形不同。mTLS 连接中,客户端与服务器相互交换证书,且在交换加密数据前,双方都要对对方证书进行 CA 校验

在安全工程语境下,mTLS 通过限制谁能向你的网络服务(包括日志基础设施)发送数据,从而抵御多种网络攻击。因此,mTLS 是零信任(zero trust)的重要组成部分:只有能证明身份的实体才能访问内部或敏感应用。先强制“信任要求”,再放行应用访问,即使入侵者掌握了正确的用户名与密码,其所能触达的范围也会被大幅限制。

mTLS 适用于内部网络与零信任环境应用于面向公众的网站或服务,否则每一位潜在客户都必须持有受信任的证书才能访问。

生成证书颁发机构(Generating a Certificate Authority)

本节你将创建自己的 根 CA中间 CA。我们会用中间 CA来为本书所有工具的 CSR 进行签名。在生产环境中,请将 CA 存放在高度安全专用于创建中间 CA的系统上;根 CA应**物理隔离(air-gapped)**并妥善上锁。在练习环境中可不必做到如此严格。

继续前,先在用作主工作站的 Ubuntu 虚拟机上创建 TLS 文件目录结构:

$ cd ~
$ mkdir -p ~/tls/{configs,keys,csr,caroot,caintermediate,certs}

接下来的 TLS 命令都在你的家目录(~)下执行,从创建根 CA开始。请保留这些 CA 文件,不要在本章结束时删除;你将在后续各章为不同工具创建证书时继续使用它们。

根 CA 配置(Root Configuration)

OpenSSL 是创建与管理 TLS 基础设施(包括 CA、密钥、CSR 与证书)的开源工具。大多数 Unix/Linux 发行版默认打包了它,数以百万计的网站都在使用。我们将通过专用的 OpenSSL 配置文件来设置 CA 选项,而不是把选项全都写在命令行上。使用配置文件便于将来更方便地吊销创建中间 CA。创建 ~/tls/configs/openssl-rootca.cnf 并写入以下内容:

###############################################################[ca]
# Section for the ca command; redirects to CA_default
default_ca      = CA_default

❷ [CA_default]
base_dir        = tls/caroot
# Cert and key this config refers to
certificate     = $base_dir/ca.cert.pem
private_key     = $base_dir/ca.key.pem
# Filepaths
new_certs_dir   = $base_dir
database        = $base_dir/index-root.txt
serial          = $base_dir/serial-root.txt
# Signed certificate specifics
default_days    = 3650
default_md      = sha512
preserve        = no
x509_extensions = ca_root
# Required to copy Subject Alt Names (SANs) from CSR into cert
copy_extensions = copy
email_in_dn     = no
unique_subject  = no
###############################################################[req]
prompt             = no
default_bits       = 4096
default_md         = sha512
distinguished_name = ca_distinguished_name
x509_extensions    = ca_root
string_mask        = utf8only
###############################################################[ca_distinguished_name]
countryName            = US
stateOrProvinceName    = MO
localityName           = St. Louis
organizationName        = Business, Inc.
organizationalUnitName = Information Technology
commonName             = Root CA
emailAddress           = none@localhost
###############################################################[signing_policy]
countryName            = optional
stateOrProvinceName    = optional
localityName           = optional
organizationName       = optional
organizationalUnitName = optional
commonName             = optional
emailAddress           = optional
###############################################################
# v3 extensions
###############################################################[ca_root]
nsComment              = OpenSSL Certificate for Root CA
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints       = critical, CA:TRUE
keyUsage               = critical, cRLSign, digitalSignature, keyCertSign
###############################################################[ca_intermediate]
nsComment              = OpenSSL Certificate for Intermediate CA
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid,issuer:always
basicConstraints       = critical, CA:TRUE, pathlen:0
keyUsage               = critical, cRLSign, digitalSignature, keyCertSign
###############################################################[client_cert]
nsComment              = OpenSSL Certificate for Clients
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid,issuer:always
basicConstraints       = CA:FALSE
keyUsage               = digitalSignature, nonRepudiation
extendedKeyUsage       = clientAuth
###############################################################[server_cert]
nsComment              = OpenSSL Certificate for Servers
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid,issuer:always
basicConstraints       = CA:FALSE
keyUsage               = digitalSignature, keyEncipherment, nonRepudiation
extendedKeyUsage       = serverAuth
###############################################################[flex_cert]
nsComment              = OpenSSL Certificate for Clients or Servers
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid,issuer:always
basicConstraints       = CA:FALSE
keyUsage               = digitalSignature, keyEncipherment, nonRepudiation
extendedKeyUsage       = clientAuth, serverAuth

说明:

  • [ca] 仅包含一个键 default_ca,指向 CA_default
  • CA_default 定义了 CA 常规操作的选项(如用 openssl ca 签 CSR)。base_dir 设为 tls/caroot(相对配置文件所在路径);证书与私钥路径指明了签名时 OpenSSL 要读取的文件;并指定新签发证书索引序列号跟踪文件的位置。此处默认有效期 10 年,并以 SHA-512 作为消息摘要算法;X.509 v3 扩展由 ca_root(❻) 节定义。copy_extensions = copy 用于将 CSR 中的 SAN(主题备用名称) 拷贝到签发证书中。
  • [req]openssl req 子命令的配置:不交互提示(prompt=no),默认 4096 位密钥,摘要算法 SHA-512,并分别引用主题 DNv3 扩展的小节。
  • ca_distinguished_name 填写组织与位置信息;signing_policy 规定 CSR 中哪些字段为必填(本配置都设为可选;若设为必填,申请者需匹配 CA 的组织/地域信息)。
  • ca_root 指明这是根 CAbasicConstraints = critical, CA:TRUE(依据 RFC 5280 要求),keyUsage 包含 keyCertSign,表明该证书可用于签发。authorityKeyIdentifier = keyid:always,issuer 要求携带颁发者的标识与 DN,便于链路校验。
  • ca_intermediate 定义中间 CA,与根 CA 不同之处在于 pathlen:0,表示其下不可再有下级 CA,仅能签服务器/用户证书。
  • client_cert/ server_cert:二者均为 CA:FALSE。前者 extendedKeyUsage=clientAuth只能发起 TLS 连接),后者 extendedKeyUsage=serverAuth只能接收 TLS 连接)。
  • flex_cert 同时包含 clientAuth, serverAuth,生成的证书既可发起也可接收 TLS 连接,便于本章与后续章节的工具使用。

根 CA 生成(Root Generation)

准备好根 CA 配置后,运行以下命令生成一个4096 位、有效期 10 年、SHA-512 摘要的根证书:

$ openssl req -x509 -config tls/configs/openssl-rootca.cnf -days 3650 -new \
-extensions ca_root -keyout tls/caroot/ca.key.pem -out \
tls/caroot/ca.cert.pem -passout pass:abcd1234

根证书无需先生成 CSR;命令会直接得到一个自签名证书。用下列命令查看其内容:

$ openssl x509 -text -noout -in ~/tls/caroot/ca.cert.pem
--snip--
Issuer: C = US, ST = MO, L = St. Louis, O = "Business, Inc.",
OU = Information Technology, CN = Root CA, emailAddress = none@localhost
Subject: C = US, ST = MO, L = St. Louis, O = "Business, Inc.",
OU = Information Technology, CN = Root CA, emailAddress = none@localhost
--snip--
X509v3 Subject Key Identifier:
04:D7:9B:FE:08:A7:98:AC:0D:1B:A6:4B:4D:9B:A0:B4:68:D3:E5:20
X509v3 Authority Key Identifier:
04:D7:9B:FE:08:A7:98:AC:0D:1B:A6:4B:4D:9B:A0:B4:68:D3:E5:20
--snip--

可以看到 IssuerSubject 相同,Authority Key IdentifierSubject Key Identifier 也一致,因此根 CA 被视为自签名

为根 CA 添加索引序列号跟踪文件,以记录它已签发的证书历史与下一个可用的序列号(通常从 00 递增):

$ touch ~/tls/caroot/index-root.txt
$ echo "00" > ~/tls/caroot/serial-root.txt

接下来,我们将复制根 CA 配置并进行修改,来生成中间 CA 的配置。

中间 CA 配置(Intermediate Configuration)

我们将使用中间 CA(有时称为签发 CA)来为各工具的专用证书签名。在生产环境中,中间 CA 作为根 CA 与用户证书之间的缓冲,以降低根 CA 暴露于泄露或毁坏的风险。要配置中间 CA,先把根 CA 的配置复制为一个新文件 openssl-intermediateca.cnf

$ cp ~/tls/configs/openssl-rootca.cnf ~/tls/configs/openssl-intermediateca.cnf

CA_defaultreqca_distinguished_name 各节中所有指向根 CA的引用,修改为指向中间 CA。除此之外,该 CA 的行为与根 CA 相同:

--snip--
[CA_default]
base_dir        = tls/caintermediate
# Cert and key this config refers to
certificate     = $base_dir/ca-int.cert.pem
private_key     = $base_dir/ca-int.key.pem
# Filepaths
new_certs_dir   = $base_dir
database        = $base_dir/index-intermediate.txt
serial          = $base_dir/serial-intermediate.txt
# Certificate specifics
default_days    = 3650
default_md      = sha512
x509_extensions = ca_intermediate
# Required to copy Subject Alt Names (SANs) from CSR into cert
copy_extensions = copy
email_in_dn     = no
unique_subject  = no
###############################################################
[req]
# Section for the req command
prompt             = no
default_bits       = 4096
default_md         = sha512
distinguished_name = ca_distinguished_name
x509_extensions    = ca_intermediate
string_mask        = utf8only
###############################################################
[ca_distinguished_name]
countryName            = US
stateOrProvinceName    = MO
localityName           = St. Louis
organizationName       = Business, Inc.
organizationalUnitName = Information Technology
commonName             = Intermediate CA
emailAddress           = none@localhost
--snip--

其余配置与根 CA 的配置完全一致

中间 CA 签发(Intermediate Signing)

要获得已签名的中间 CA 证书,需要先生成一个 CSR(证书签名请求) 与一对 SHA-512 密钥。运行下列命令,为中间 CA 生成名为 ca-int.csr 的 CSR 以及密钥对:

$ openssl req -config ~/tls/configs/openssl-intermediateca.cnf -new -keyout \
~/tls/caintermediate/ca-int.key.pem -out \
~/tls/caintermediate/ca-int.csr -outform PEM -passout pass:abcd1234

接着,使用根 CA 的配置 openssl-rootca.cnf 对该 CSR 进行签名,生成已签名证书 ca-int.cert.pem

$ openssl ca -batch -notext -config ~/tls/configs/openssl-rootca.cnf \
-passin pass:abcd1234 -policy signing_policy -days 3650 -extensions \
ca_intermediate -out ~/tls/caintermediate/ca-int.cert.pem -infiles \
~/tls/caintermediate/ca-int.csr

注意,上述命令包含参数 -notext,这会让 OpenSSL 仅输出证书本体;否则还会附带可通过 openssl x509 查看的人类可读文本。

检查中间证书的 issuersubject

$ openssl x509 -text -noout -in ~/tls/caintermediate/ca-int.cert.pem
--snip--
Issuer: C = US, ST = MO, L = St. Louis, O = "Business, Inc.", OU = Information Technology,
CN = Root CA,
emailAddress = none@localhost Subject: C = US, ST = MO, L = St. Louis, O = "Business, Inc.",
OU = Information
Technology, CN = Intermediate CA
--snip--
X509v3 Subject Key Identifier:   A3:A5:5D:C1:9C:C6:78:81:D9:ED:34:4C:3C:80:AC:05:73:8A:34:C6
X509v3 Authority Key Identifier: 04:D7:9B:FE:08:A7:98:AC:0D:1B:A6:4B:4D:9B:A0:B4:68:D3:E5:20
--snip--

你会看到,中间证书包含自身的 subject 与 subject key identifier,以及来自根 CA的 issuer 与 authority key identifier。

还请注意,中间 CA 的 email 并未出现在 subject 中,而是出现在根 CA 所属的 issuer 字段里。这是因为我们在根 CA 配置里设置了 email_in_dn = no,且该设置仅作用于下级证书。是否在 Subject DN 中包含邮箱,对本书中的工具没有影响;一些组织可能会出于证书追踪目的选择在 DN 中使用邮箱。

中间 CA 的 Subject Key Identifier 会成为其所签发的所有证书的 Authority Key Identifier;而它的 Subject 则会成为这些证书的 Issuer。这便是信任链(trust chain)的起点:某客户端证书由我们信任的中间 CA签发,而该中间 CA 又由受信任的根 CA签发。

为了记录中间 CA 的签发历史,还需要创建索引序列号跟踪文件:

$ touch ~/tls/caintermediate/index-intermediate.txt
$ echo "00" > ~/tls/caintermediate/serial-intermediate.txt

OpenSSL 提供了验证命令,用于检查证书签名是否由指定的签发者生成。下述命令用于验证根 CA 是否正确签发了中间 CA 证书

$ openssl verify -CAfile ~/tls/caroot/ca.cert.pem ~/tls/caintermediate/ca-int.cert.pem
~/tls/caintermediate/ca-int.cert.pem: OK

无论通过该命令,还是在互联网上进行连接校验,这一验证过程都需要完整的信任链(根 CA 及其所有下级 CA)在场。本例仅比较了中间 CA 与根 CA 的签名关系。若要验证由中间 CA 签发的证书,我们需要一个同时包含根与中间 CA的文件。

链文件(Chain Files)

CA 链文件是一个单一文件,其中包含根 CA所有中间 CA的已签名证书,以及你希望一并打包用于验证或分发的其他证书。该文件应当在你的网络内部分发给所有将与本书工具交互的用户与服务。

CA 链文件本质上是一个文本文件,顺序拼接了构成完整信任链所需的全部证书,即签名过程中的所有实体。将已签名证书合并为一个名为 ca-chain.cert.pem 的文件,放在 ~/tls/certs 目录下:

$ cat ~/tls/caroot/ca.cert.pem ~/tls/caintermediate/ca-int.cert.pem >> \
~/tls/certs/ca-chain.cert.pem

在组织内部把已签名证书返回给申请人时,附带 CA 链文件是一种体贴的做法。

为 Logstash 配置 TLS(Setting Up TLS for Logstash)

在本书中我们将大量使用 Logstash 来转换数据流、在屏幕上显示测试输出并把数据发送到数据库(第 8、9 章会深入讲解)。下面先为 Logstash 创建一对 TLS 密钥和一个 flex 证书(即可同时用于客户端与服务器的证书),以便在后续测试各类工具时把数据安全地发送给它。此后你会在需要时为每个新工具都创建 flex 证书;另外在第 6 章我们还会创建一种包含多个主机名的“通配符”证书。生产环境中应尽量根据服务需要把证书限制为 clientAuth serverAuth 之一

Flex 配置(Flex Configuration)

先创建 OpenSSL 配置文件(明显比 CA 配置短)。该文件只声明服务器身份;它不需要给其他对象签名,也不包含签名策略设置。将以下内容保存为 ~/tls/configs/openssl-flex-logstash.local.cnf

###############################################################
[req]
prompt             = no
default_bits       = 4096
default_md         = sha512
❶ default_keyfile    = tls/keys/logstash.local.flex.key.pem
distinguished_name = flex_distinguished_name
❷ req_extensions     = flex_cert
###############################################################
[flex_distinguished_name]
countryName            = US
stateOrProvinceName    = MO
localityName           = St. Louis
organizationName       = Business, Inc.
organizationalUnitName = Information Technology
❸ commonName             = Logstash Flex
emailAddress           = none@localhost
###############################################################
[flex_cert]nsComment      = OpenSSL Certificate for Client and Servers
❺ subjectAltName = @alternate_names
###############################################################[alternate_names]
DNS.1  = logstash
❼ DNS.2  = logstash.local

说明:在密钥文件名中包含 .local 以便标识该密钥对应的域名 ❶。把 req 子命令的相关选项放在单独的小节 flex_cert 中 ❷(回忆 openssl req 用于生成密钥对和 CSR)。通用名 ❸ 为了兼容某些旧浏览器,通常写服务器的完整域名(FQDN);现代浏览器则主要依赖 SAN 来判断证书用途。由于这里是实验环境,也可以像 CA 证书那样写成描述性名称。注释 ❹ 用于帮助读者理解证书用途。SAN ❺ 使用 @ 指向 alternate_names 小节中的名称数组 ❻。最好把可能用于认证的名称变体都包含进去,例如基础名 logstash 以及 FQDN logstash.local ❼。

生产环境中不要把 IP 地址写进 SAN,除非你确定它们在证书整个生命周期内不会改变。若必须包含 IP,可与主机名类似按编号写:IP.1 = 192.168.1.99IP.2 = 10.10.1.99 等。

服务器密钥与证书(Server Keys and Certificates)

为 Logstash 生成密钥对与 CSR。我们使用口令 abcd1234 保护私钥,并指定 -keyout 参数(因为私钥文件名已在配置中指定;当然也可以改为在命令行指定,OpenSSL 很灵活):

$ openssl req -config ~/tls/configs/openssl-flex-logstash.local.cnf \
-new -out ~/tls/csr/logstash.local.flex.csr -outform PEM -passout pass:abcd1234

此命令会生成密钥对和 Logstash 的 CSR。使用中间 CA配置对该 CSR 进行签名:

$ openssl ca -batch -notext -config ~/tls/configs/openssl-intermediateca.cnf -passin \
pass:abcd1234 -policy signing_policy -extensions flex_cert -out \
~/tls/certs/logstash.local.flex.cert.pem -infiles ~/tls/csr/logstash.local.flex.csr

查看新证书,确认各项是否正确:

$ openssl x509 -text -noout -in ~/tls/certs/logstash.local.flex.cert.pem
--snip--
Issuer: C = US, ST = MO, L = St. Louis, O = "Business, Inc.", OU = Information Technology,
CN = Intermediate CA
Subject: C = US, ST = MO, L = St. Louis, O = "Business, Inc.", OU = Information Technology,
CN = Logstash Flex
--snip--
X509v3 Subject Key Identifier: C6:92:DF:FD:66:0F:B2:F3:57:04:F4:9E:1E:29:94:EF:17:15:23:DE
X509v3 Authority Key Identifier: A3:A5:5D:C1:9C:C6:78:81:D9:ED:34:4C:3C:80:AC:05:73:8A:34:C6
--snip--

可以看到:签发者/授权键标识来自中间 CA,而 Subject/Subject Key Identifier 对应 Logstash。

最后验证证书签名(注意:下面这个命令应写成一行):

$ openssl verify -CAfile ~/tls/certs/ca-chain.cert.pem ~/tls/certs/logstash.local.flex.cert.pem
~/tls/certs/logstash.local.flex.cert.pem: OK

至此,你已拥有 Logstash 的密钥与已签名证书。

目录准备(Directory Preparation)

现在可以在 Logstash 的输入上启用 TLS 了。先切到 ~/Downloads 目录(若不存在先创建),用 wget 从 Elastic 官网下载 64 位 Logstash(将 X.Y.Z 替换为最新版本),再用 tar 解压:

$ mkdir ~/Downloads
$ cd ~/Downloads
$ wget https://artifacts.elastic.co/downloads/logstash/logstash-X.Y.Z-linux-x86_64.tar.gz
$ tar xvzf logstash-X.Y.Z-linux-x86_64.tar.gz

为以后少敲字,可以把解压后的版本目录重命名为 logstash,进入该目录,并创建两个子目录用于放置证书与流水线配置:

$ mv logstash-X.Y.Z logstash
$ cd logstash
$ mkdir {certs,conf.d}

把 CA 链、Logstash 的已签名证书与私钥复制到 Logstash 目录下:

$ cp ~/tls/certs/ca-chain.cert.pem ~/Downloads/logstash/certs/
$ cp ~/tls/certs/logstash.local.flex.cert.pem ~/Downloads/logstash/certs/
$ cp ~/tls/keys/logstash.local.flex.key.pem ~/Downloads/logstash/certs/

接下来创建 Logstash 的简短配置。

Logstash 配置(Logstash Configuration)

Beats 是 Elastic 的一组数据采集器(包括 FilebeatWinlogbeat 等,第 4、5 章会介绍)。下面这份 Logstash 配置通过 TLS 接收来自 Beats 的数据。创建 conf.d/beats-mtls.conf 并加入如下内容;把其中的用户名 j 改成你自己的家目录名:

input {
    beats {
        port => 5044
        ssl_enabled => true
        ssl_client_authentication => "required"
        ssl_certificate => "/home/j/Downloads/logstash/certs/logstash.local.flex.cert.pem"
        ssl_key => "/home/j/Downloads/logstash/certs/logstash.local.flex.key.pem"
        ssl_key_passphrase => "abcd1234"
        ssl_certificate_authorities => ["/home/j/Downloads/logstash/certs/ca-chain.cert.pem"]
    }
    syslog {
        port => 5514
    }
}
filter {
    # 我们稍后会在这里做字段转换等处理。
}
output {
    stdout {codec => rubydebug {metadata => true}}
}

这份配置使用 TLS 在屏幕上打印 Beatssyslog 数据。input 中包含两个输入:beatssyslogbeats 段指定了默认 TCP 端口 5044 并启用 SSL/TLS;强制双向 TLS 客户端认证,并指定已签名证书私钥私钥口令CA 链文件syslog 段监听端口 5514

filter 段将在第 9 章用于把字段改名为 Elastic Common Schema 并进行其他转换。output 段当前使用 rubydebug 编解码器把 JSON 美观打印到标准输出。第 6 章你会把数据从 Logstash 输出到名为 Elasticsearch 的数据库;第 8 章还会更详细地探索 Logstash 的输入与输出。

防火墙端口(Firewall Ports)

需要打开防火墙端口以允许 Logstash 流量。Ubuntu(UFW)

$ sudo ufw allow 5044/tcp
$ sudo ufw allow 5514/tcp

Rocky Linux / CentOS / RHEL(Firewalld)

$ sudo firewall-cmd --zone=public --add-port={5044,5514}/tcp --permanent
$ sudo firewall-cmd --reload

测试配置是否有语法错误或其他问题:

$ bin/logstash -f conf.d/beats-mtls.conf --config.test_and_exit

打开第二个终端,切到 Logstash 目录运行(保留第一个终端观看输出)。--config.reload.automatic 表示修改配置后无需重启即可生效:

$ bin/logstash -f conf.d/beats-mtls.conf --config.reload.automatic

至此 Logstash 已运行,你可以通过加密的 Beats明文的 syslog 向其发送数据。在接下来的章节中,你将广泛使用 Logstash 来接收、发送、请求、转换、显示并将数据加载到数据库中。

SSH

安全外壳协议(Secure Shell,SSH)在两台系统之间建立加密连接。这些连接允许管理员和自动化流程开启远程终端会话、进行文件传输,或在不受信任的网络之间创建安全隧道。与 TLS 类似,SSH 使用非对称密钥对建立通信,但与 TLS 不同的是,SSH 没有证书与 CA

第 11、12 章将介绍的 Ansible 依赖 SSH 来执行自动化远程管理。对于自身不支持加密的程序,或需要在远程网络之间传输数据的场景,SSH 也是一个很好的安全封装方式。

前向 SSH 隧道会绑定到本地端口,使得任何发送到该端口的本地数据都会被转发到远端的 IP 与端口。反向隧道则是连接到远端地址,随后允许远端服务通过该隧道把数据回送到发起端主机。SSH 还能充当 SOCKS 代理,把数据安全地通过这些隧道发送到远端主机所能访问的任意多个 IP 与端口。如果遇到无法或不便使用 TLS 连接的情况,SSH 隧道仍可为原本未受保护的数据提供加密。

启用 SSH(Enabling SSH)

OpenSSH 是一组实现 SSH 协议的工具,可用于创建安全连接,其中包括让服务器监听入站连接的组件。我们将在本书各处使用 SSH,先来安装 OpenSSH。

Ubuntu 上运行以下命令以安装 OpenSSH Server、在防火墙中放行 SSH,并重启 SSH 服务:

$ sudo apt install openssh-server
$ sudo ufw allow ssh
$ sudo systemctl restart ssh

Red Hat Enterprise Linux (RHEL)、Rocky Linux、CentOS 上执行相同任务(注意这些系统上的服务名是 sshd,不是 ssh):

$ sudo dnf install openssh-server
$ sudo firewall-cmd --zone=public --add-service=ssh --permanent
$ sudo firewall-cmd --reload
$ sudo systemctl restart sshd

现在,你的服务器已准备好接受提供用户名与密码的用户连接。接下来,我们创建 SSH 密钥对,以避免每次访问系统都输入用户名和密码。

创建 SSH 密钥(Creating SSH Keys)

密钥对既能防止对密码的暴力破解,也能改善用户体验。指定 4096 位的密钥长度,并把文件放在 ~/.ssh。我倾向在文件名中包含我的用户名 j,不过用 SSH 配置管理多个密钥名称会更好:

$ ssh-keygen -b 4096 -f ~/.ssh/j_id_rsa

命令会提示设置口令,从安全角度看这很有必要。与 TLS 文件一致,本书涉及口令的地方统一使用 abcd1234(在需要时会再次提醒)。接下来启动 SSH agent,把密钥加载到内存中,这样每次使用密钥登录时就不用再输入口令。

启动 Agent(Starting the Agent)

SSH agent 会把私钥保存在内存中,使你在使用该密钥登录服务器时无需每次都输入口令。比如使用第 11、12 章介绍的 Ansible 同时访问多台服务器时,这非常方便。

在启动 agent 之前,先通过 evalssh-agent 输出的环境变量加载到当前 shell 的环境中,以便与 agent 交互:

$ eval $(ssh-agent -s)
Agent pid 4604

现在你得到了一个进程 ID;如有需要,可用 kill -9 进程号 结束 agent。把私钥加载进 agent:

$ ssh-add ~/.ssh/j_id_rsa
Enter passphrase for /home/j/.ssh/j_id_rsa: your passphrase

你可以创建一个别名(快捷命令)把这些步骤合成一条命令,在 ~/.bashrc 中加入:

alias start-ssh='eval "$(ssh-agent -s)" && ssh-add /home/j/.ssh/j_id_rsa'

SSH agent 不会持久化;重启或开启新终端后需要重新启动。

创建配置文件(Creating Configuration Files)

SSH 配置文件用于将密钥映射到服务器,并为每台服务器定义连接相关信息(端口、用户名等,超出本章范围)。它能避免在开发、生产等不同环境服务器之间手动切换密钥与认证选项。

OpenSSH 使用两类配置文件:影响整个系统的全局配置,以及每个用户独立的用户配置。全局配置位于 /etc/ssh/ssh_config(客户端)或 /etc/sshd/sshd_config(服务器端,接受入站连接)。用户配置优先于全局配置,位于每个用户家目录下隐藏的 .ssh 目录中。命令行参数的优先级最高,会覆盖两者。

~/.ssh/config(注意 ssh 前面的点)创建一个新的用户配置文件:

$ nano ~/.ssh/config

添加如下内容;把远程主机的用户名 j 替换为你要连接的实际用户名:

Host 10.*.*.* 192.168.*.* *.local *.internal
Port 22
User j
IdentityFile ~/.ssh/j_id_rsa

Host 使用通配符(*)适配更广或动态的目标,包括 10.0.0.0/8192.168.0.0/16 网段,以及本书将使用的 .local.internal 域名。Port 22 是标准 SSH 端口;若服务监听在其他端口,可在此修改。我在示例中指定了连接用户名 j,实际使用时请按需设置不同用户名。IdentityFile 指定要使用的私钥。保存并退出。

SSH 配置可以包含多个如上所示的段落,例如分别为开发与生产服务器指定不同密钥,从而让管理员用同一条命令访问不同服务器。你也可以列出单个 IP 或主机名以便一劳永逸,比如跳板机或 DMZ 堡垒机。第 3 章会用 Git 备份此文件。

分发公钥(Distributing Public Keys)

你必须把 SSH 公钥分发到要访问的主机上。以下命令会把新的公钥复制到远程服务器 192.168.2.20,以远程用户 j 的身份;请替换为你的 IP 与用户名(如果只运行一台虚拟机,可用 localhost):

$ ssh-copy-id -i ~/.ssh/j_id_rsa.pub j@192.168.2.20

输入用户名与密码后,公钥即可生效。若此前从未连接过该服务器,当系统询问是否信任该主机指纹(唯一标识)时答 yes

未来如需更新 SSH 密钥,重复上述流程,然后在配置文件中将 IdentityFile 指向新密钥,最后粉碎删除shred)旧密钥(是的,Linux 中确有 shred 命令)。

验证连接(Verifying Connectivity)

现在你已创建 SSH 密钥与配置文件、启动了 agent,并把公钥拷贝到远程服务器。用下列命令测试安全连接(替换成目标 IP):

$ ssh 192.168.2.20

由于你已经在配置文件中指定了用户、IP 范围与私钥,并把私钥加载进了 agent,上述命令应能直接登录到远端。如果本地与远程用户名不同、且你忘记在配置中设置或更新 User,可以用 ssh user@ip 这种语法。

没有 SSH 配置文件的情况下,等价命令如下:

$ ssh -i ~/.ssh/j_id_rsa j@192.168.2.20

建议花点时间查阅网上的 SSH 加固指南。容易上手的做法包括:强制使用密钥登录禁止 root 远程登录限制连接速率在多次失败后暂时封禁用户等。

小结(Summary)

TLS 与 SSH 对于确保流水线中的数据在传输途中保持机密性至关重要。本章中,我们创建了根与中间 CA私钥已签名证书,让我们的工具能通过 TLS 通信;并用一份配置启动了 Logstash 以通过 TLS 接收 Beats。我们还创建了 SSH 密钥配置文件,以便更便捷地进行远程访问。

现在是为你的虚拟机创建快照的好时机!下一章你将安装 Git,用它来管理本书中的配置,并将其备份到远程位置。