介绍
如果您计划在您无法控制的网络环境中运行 CoreOS 集群,例如在共享数据中心内或通过公共互联网,您可能已经注意到 etcd 通过发出未加密的 HTTP 请求进行通信。 可以通过在集群中的每个节点上配置 IPTables 防火墙来降低这种行为的风险,但一个完整的解决方案最好使用加密的传输层。
幸运的是,etcd 支持点对点 TLS/SSL 连接,因此集群的每个成员都经过身份验证,所有通信都被加密。 在本指南中,我们将首先配置一个包含三个成员的简单集群,然后在每台机器上配置 HTTPS 端点和基本防火墙。
先决条件
本指南主要基于 CoreOS 系统组件介绍 和 在 DigitalOcean 上设置 CoreOS 集群的指南中讨论的概念。
您应该熟悉 etcd、fleetctl、cloud-config 文件和生成发现 URL 的基础知识。
为了创建和访问集群中的机器,您需要一个与您的 DigitalOcean 帐户关联的 SSH 公钥。 有关在 DigitalOcean 中使用 SSH 密钥的详细信息,请参见此处。
如果您想使用 DigitalOcean API 创建您的 CoreOS 机器,请参阅 本教程 以获取有关如何生成和使用具有写入权限的个人访问令牌的信息。 API 的使用是可选的,但从长远来看可能会节省您的时间,尤其是在您预期构建更大的集群时。
生成新的发现 URL
通过在浏览器中访问 https://discovery.etcd.io/new?size=3 并复制显示的 URL,从 discovery.etcd.io 检索新的发现 URL ,或通过在本地计算机上的终端使用 curl:
curl -w "\n" "https://discovery.etcd.io/new?size=3"
保存返回的网址; 我们很快就会在我们的 cloud-config 中使用它。
编写包含 HTTPS 配置的 Cloud-Config 文件
我们将从编写 cloud-config 开始。 cloud-config 将在初始化每个服务器时作为 用户数据 提供,定义集群的重要配置细节。 这个文件会很长,但不会比 基本集群指南 中的版本复杂得多。 我们将明确告诉 fleet 使用 HTTPS 端点,为我们的防火墙启用名为 iptables-restore 的服务,并写出配置文件告诉 etcd 和 fleet 在哪里查找 SSL 证书。
在本地机器上打开一个终端,确保你在你的主目录中,然后使用 nano(或你喜欢的文本编辑器)创建并打开 ~/cloud-config.yml:
cd ~ nano cloud-config.yml
粘贴以下内容,然后将 etcd2 部分中的 https://discovery.etcd.io/token 更改为您在上一部分中声明的发现 URL。
如果您不想启用防火墙,也可以删除 iptables-restore 部分。
粘贴时要小心压痕。 cloud-config 是用 YAML 编写的,它对空格很敏感。 有关特定行的信息,请参阅文件中的注释,然后我们将更详细地介绍一些重要部分。
~/cloud-config.yml
#cloud-config
coreos:
etcd2:
# generate a new token for each unique cluster from https://discovery.etcd.io/new:
discovery: https://discovery.etcd.io/token
# multi-region deployments, multi-cloud deployments, and Droplets without
# private networking need to use $public_ipv4:
advertise-client-urls: https://$private_ipv4:2379,https://$private_ipv4:4001
initial-advertise-peer-urls: https://$private_ipv4:2380
# listen on the official ports 2379, 2380 and one legacy port 4001:
listen-client-urls: https://0.0.0.0:2379,https://0.0.0.0:4001
listen-peer-urls: https://$private_ipv4:2380
fleet:
# fleet defaults to plain HTTP - explicitly tell it to use HTTPS on port 4001:
etcd_servers: https://$private_ipv4:4001
public-ip: $private_ipv4 # used for fleetctl ssh command
units:
- name: etcd2.service
command: start
- name: fleet.service
command: start
# enable and start iptables-restore
- name: iptables-restore.service
enable: true
command: start
write_files:
# tell etcd2 and fleet where our certificates are going to live:
- path: /run/systemd/system/etcd2.service.d/30-certificates.conf
permissions: 0644
content: |
[Service]
# client environment variables
Environment=ETCD_CA_FILE=/home/core/ca.pem
Environment=ETCD_CERT_FILE=/home/core/coreos.pem
Environment=ETCD_KEY_FILE=/home/core/coreos-key.pem
# peer environment variables
Environment=ETCD_PEER_CA_FILE=/home/core/ca.pem
Environment=ETCD_PEER_CERT_FILE=/home/core/coreos.pem
Environment=ETCD_PEER_KEY_FILE=/home/core/coreos-key.pem
- path: /run/systemd/system/fleet.service.d/30-certificates.conf
permissions: 0644
content: |
[Service]
# client auth certs
Environment=FLEET_ETCD_CAFILE=/home/core/ca.pem
Environment=FLEET_ETCD_CERTFILE=/home/core/coreos.pem
Environment=FLEET_ETCD_KEYFILE=/home/core/coreos-key.pem
作为可选步骤,您可以将 cloud-config 粘贴到 官方 CoreOS Cloud Config Validator 并按 Validate Cloud-Config。
保存文件并退出。 在nano中,可以通过Ctrl-X退出,y确认写入文件,Enter确认文件名来完成保存。
让我们看一下 cloud-init.yml 中的一些特定块。 首先,fleet 值:
fleet:
# fleet defaults to plain HTTP - explicitly tell it to use HTTPS:
etcd_servers: https://$private_ipv4:4001
public-ip: $private_ipv4 # used for fleetctl ssh command
请注意,etcd_servers 设置为 https URL。 对于普通的 HTTP 操作,不需要设置此值。 但是,如果没有显式配置,HTTPS 将失败。 ($private_ipv4 是 CoreOS 初始化过程理解的变量,不是你需要改变的。)
接下来我们来到 write_files 块。 值分为文件系统 path、permissions 掩码和 content,其中包含文件的所需内容。 在这里,我们指定 etcd2 和 fleet 服务的 systemd 单元文件应该设置指向我们将生成的 TLS/SSL 证书的环境变量:
write_files:
# tell etcd2 and fleet where our certificates are going to live:
- path: /run/systemd/system/etcd2.service.d/30-certificates.conf
permissions: 0644
content: |
[Service]
# client environment variables
Environment=ETCD_CA_FILE=/home/core/ca.pem
...
- path: /run/systemd/system/fleet.service.d/30-certificates.conf
permissions: 0644
content: |
[Service]
# client auth certs
Environment=FLEET_ETCD_CAFILE=/home/core/ca.pem
...
虽然我们告诉服务在哪里可以找到证书文件,但我们自己还不能提供这些文件。 为此,我们需要知道每台 CoreOS 机器的私有 IP 地址,该地址仅在机器创建后可用。
注意: 在CoreOS Droplet 上,cloud-config 的内容在Droplet 创建后不能更改,每次开机都会重新执行文件。 您应该避免将 write-files 部分用于您计划在构建集群后修改的任何配置,因为它将在下次启动 Droplet 时重置。
提供液滴
现在我们已经定义了 cloud-config.yml,我们将使用它来配置集群的每个成员。 在 DigitalOcean 上,我们可以采用两种基本方法:通过基于 Web 的控制面板,或从命令行使用 cURL 调用 DigitalOcean API。
使用 DigitalOcean 控制面板
在同一数据中心区域内创建三个新的 CoreOS Droplet。 确保每次都勾选 Private Networking 和 Enable User Data。
- coreos-1
- coreos-2
- coreos-3
在 User Data 字段中,从上方粘贴 cloud-config.yml 的内容,确保您已将发现 URL 插入到文件顶部附近的 discovery 字段中。
使用 DigitalOcean API
作为一种可以节省重复粘贴到字段中的替代方法,我们可以编写一个简短的 Bash 脚本,它使用 curl 通过我们的 cloud-config 从 DigitalOcean API 请求一个新的 Droplet,并调用它一次每个液滴。 使用 nano(或您选择的文本编辑器)打开一个名为 makecoreos.sh 的新文件:
cd ~ nano makecoreos.sh
粘贴并保存以下脚本,根据您的集群的需要调整 region 和 size 字段(nyc3 和 512mb 的默认值可用于演示目的,但您可能需要不同的区域或更大的 Droplets 用于实际项目):
~/makecoreos.sh
#!/usr/bin/env bash
# A basic Droplet create request.
curl -X POST "https://api.digitalocean.com/v2/droplets" \
-d'{"name":"'"$1"'","region":"nyc3","size":"512mb","private_networking":true,"image":"coreos-stable","user_data":
"'"$(cat ~/cloud-config.yml)"'",
"ssh_keys":[ "'$DO_SSH_KEY_FINGERPRINT'" ]}' \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json"
现在,让我们将环境变量 $DO_SSH_KEY_FINGERPRINT 和 $TOKEN 分别设置为与您的 DigitalOcean 帐户和 API 个人访问令牌关联的 SSH 密钥的指纹。
有关获取个人访问令牌和使用 API 的信息,请参阅本教程。
为了找到与您的帐户关联的密钥的指纹,请检查 帐户设置 的安全部分,在 SSH 密钥 下。 它将采用 公钥指纹 的形式,类似于 43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8。
我们在这里使用 export 以便 shell 的子进程,如 makecoreos.sh,将能够访问变量。 每次使用脚本时都必须在当前 shell 中设置,否则 API 调用将失败:
export DO_SSH_KEY_FINGERPRINT="ssh_key_fingerprint" export TOKEN="your_personal_access_token"
注意: 如果您刚刚为 API 生成了个人访问令牌,请记住将其保存在方便和安全的地方。 在首次创建时向您显示后无法检索它,并且任何拥有令牌的人都可以控制您的 DigitalOcean 帐户。
一旦我们为每个所需的凭据设置了环境变量,我们就可以运行脚本来创建每个所需的 Droplet。 makecoreos.sh 在其对 API 的调用中使用其第一个参数来填写 name 字段:
bash makecoreos.sh coreos-1 bash makecoreos.sh coreos-2 bash makecoreos.sh coreos-3
您应该看到描述每个新 Droplet 的 JSON 输出,并且所有三个都应该出现在控制面板中的 Droplet 列表中。 他们可能需要几秒钟才能完成启动。
登录到 coreos-1
无论您使用的是控制面板还是 API,您现在都应该拥有三个正在运行的 Droplet。 现在是记录他们的公共和私有 IP 的好时机,可以通过单击控制面板中的单个 Droplet,然后单击 Settings 链接来使用它们。 生成证书和配置防火墙时,将需要每个 Droplet 的私有 IP 地址。
让我们测试一个 Droplet。 确保您的 SSH 密钥已添加到本地 SSH 代理:
eval $(ssh-agent) ssh-add
在DigitalOcean控制面板中找到coreos-1的公网IP,开启SSH代理转发连接:
ssh -A core@coreos-1_public_ip
首次登录集群的任何成员时,我们可能会收到来自 systemd 的错误消息:
OutputCoreOS stable (766.5.0) Failed Units: 1 iptables-restore.service
这表明防火墙尚未配置。 目前,忽略此消息是安全的。 (如果您选择不在 cloud-config 中启用防火墙,您将不会看到错误消息。 您以后可以随时启用 iptables-restore 服务。)
在我们担心防火墙之前,让我们让集群的每个成员上的 etcd2 实例相互通信。
使用 CFSSL 生成自签名证书
CFSSL 是 CloudFlare 发布的用于处理 TLS/SSL 证书的工具包。 在撰写本文时,它是 CoreOS 维护者选择的用于生成自签名证书的工具,优于 OpenSSL 和现已弃用的 etcd-ca。
在本地计算机上安装 CFSSL
CFSSL 需要有效的 Go 安装才能从源代码安装。 请参阅 本指南以安装 Go。
确保您的 $GOPATH 设置正确并添加到您的 $PATH,然后使用 go get 安装 cfssl 命令:
export GOPATH=~/gocode export PATH=$PATH:$GOPATH/bin go get -u github.com/cloudflare/cfssl/cmd/cfssl go get -u github.com/cloudflare/cfssl/...
作为一种替代方法,可以从 pkg.cfssl.org 检索预构建的二进制文件。 首先确保 ~/bin 存在并且在您的路径中:
mkdir -p ~/bin export PATH=$PATH:~/bin
然后使用 curl 为您的平台检索 cfssl 和 cfssljson 的最新版本:
curl -s -L -o ~/bin/cfssl https://pkg.cfssl.org/R1.1/cfssl_linux-amd64 curl -s -L -o ~/bin/cfssljson https://pkg.cfssl.org/R1.1/cfssljson_linux-amd64
确保 cfssl 二进制文件是可执行的:
chmod +x ~/bin/cfssl chmod +x ~/bin/cfssljson
生成证书颁发机构
现在已经安装了 cfssl 命令,我们可以使用它们来生成自定义证书颁发机构,我们将使用该证书颁发机构为我们的每台 CoreOS 机器签署证书。 让我们首先创建并输入一个新目录来存储这些文件:
mkdir ~/coreos_certs cd ~/coreos_certs
现在,在 nano(或您喜欢的文本编辑器)中创建并打开 ca-config.json:
nano ca-config.json
粘贴并保存以下内容,用于配置 cfssl 将如何进行签名:
~/coreos_certs/ca-config.json
{
"signing": {
"default": {
"expiry": "43800h"
},
"profiles": {
"client-server": {
"expiry": "43800h",
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
]
}
}
}
}
这里值得注意的是 expiry,当前设置为 43800 小时(或 5 年),以及 client-server 配置文件,其中包括 server auth 和 client auth 用法. 对于点对点 TLS,我们需要这两者。
接下来,创建并打开 ca-csr.json。
nano ca-csr.json
粘贴以下内容,根据您的位置和组织的需要调整 CN 和 names 数组。 对 hosts 条目以及地点和组织名称使用虚构值是安全的:
~/coreos_certs/ca-csr.json
{
"CN": "My Fake CA",
"hosts": [
"example.net",
"www.example.net"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "CO",
"O": "My Company",
"ST": "Lyons",
"OU": "Some Org Unit"
}
]
}
如果要将这些与 ca-config.json 和 ca-csr.json 的默认值进行比较,可以使用 cfssl 打印默认值。 对于 ca-config.json,使用:
cfssl print-defaults config
对于 ca-csr.json,使用:
cfssl print-defaults csr
在 ca-csr.json 和 ca-config.json 就位后,生成证书颁发机构:
cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
为 CoreOS 机器生成和签署证书
现在我们有了证书颁发机构,我们可以为 CoreOS 机器编写默认值:
创建并打开coreos-1.json:
nano coreos-1.json
粘贴并保存以下内容,将其调整为 coreos-1 的私有 IP 地址(通过单击单个 Droplet 在 DigitalOcean 控制面板中可见):
~/coreos_certs/coreos-1.json
{
"CN": "coreos-1",
"hosts": [
"coreos-1",
"coreos-1.local",
"127.0.0.1",
"coreos-1_private_ip"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "Lyons",
"ST": "Colorado"
}
]
}
最重要的部分是 CN,它应该是您的主机名,以及 hosts 数组,它必须包含所有:
- 您的本地主机名
127.0.0.1- CoreOS 机器的私有 IP 地址(不是面向公众的 IP)
这些将作为 subjectAltNames 添加到生成的证书中。 etcd 连接(包括到 127.0.0.1 的本地环回设备)要求证书具有与连接主机名匹配的 SAN。
如果需要,您还可以更改 names 数组以反映您的位置。 同样,为地名使用虚构值是安全的。
对剩余的每台机器重复此过程,使用适当的 hosts 条目创建匹配的 coreos-2.json 和 coreos-3.json。
注意: 如果您想查看 coreos-1.json 的默认值,可以使用 cfssl:
cfssl print-defaults csr
现在,对于每台 CoreOS 机器,生成一个签名证书并将其上传到正确的机器:
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client-server coreos-1.json | cfssljson -bare coreos chmod 0644 coreos-key.pem scp ca.pem coreos-key.pem coreos.pem core@coreos-1_public_ip:
这将创建三个文件(ca.pem、coreos-key.pem 和 coreos.pem),确保密钥文件的权限正确,然后通过 scp 将它们复制到 [ X149X]core 在 coreos-1 上的主目录。
对剩余的每台机器重复此过程,请记住,每次调用该命令都会覆盖之前的一组证书文件:
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client-server coreos-2.json | cfssljson -bare coreos chmod 0644 coreos-key.pem scp ca.pem coreos-key.pem coreos.pem core@coreos-2_public_ip:
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client-server coreos-3.json | cfssljson -bare coreos chmod 0644 coreos-key.pem scp ca.pem coreos-key.pem coreos.pem core@coreos-3_public_ip:
检查 coreos-1 上的 etcd2 功能
有了证书,我们应该能够在 coreos-1 上运行 fleetctl。 首先,通过 SSH 登录:
ssh -A core@coreos-1_public_ip
接下来,尝试列出集群中的所有机器:
fleetctl list-machines
您应该会看到列出的每台机器的标识符及其私有 IP 地址:
OutputMACHINE IP METADATA 7cb57440... 10.132.130.187 - d91381d4... 10.132.87.87 - eeb8726f... 10.132.32.222 -
如果 fleetctl 无限期挂起,则可能需要重新启动集群。 退出到本地机器:
exit
使用 SSH 向每台 CoreOS 机器发送 reboot 命令:
ssh core@coreos-1_public_ip 'sudo reboot' ssh core@coreos-2_public_ip 'sudo reboot' ssh core@coreos-3_public_ip 'sudo reboot'
稍等片刻,重新连接到 coreos-1,然后再次尝试 fleetctl。
在集群成员上配置 IPTables 防火墙
有了证书,本地网络上的其他机器应该不可能控制您的集群或从 etcd2 中提取值。 尽管如此,如果可能的话,减少可用的攻击面是一个好主意。 为了限制我们的网络暴露,我们可以为每台机器添加一些简单的防火墙规则,阻止来自集群中对等方以外的来源的大多数本地网络流量。
请记住,如果我们在 cloud-config 中启用了 iptables-restore 服务,我们将在首次登录 CoreOS 机器时看到 systemd 错误消息:
OutputCoreOS stable (766.5.0) Failed Units: 1 iptables-restore.service
这让我们知道,虽然服务已启用,但 iptables-restore 未能正确加载。 我们可以使用 systemctl 来诊断:
systemctl status -l iptables-restore
Output● iptables-restore.service - Restore iptables firewall rules Loaded: loaded (/usr/lib64/systemd/system/iptables-restore.service; enabled; vendor preset: disabled) Active: failed (Result: exit-code) since Wed 2015-11-25 00:01:24 UTC; 27min ago Process: 689 ExecStart=/sbin/iptables-restore /var/lib/iptables/rules-save (code=exited, status=1/FAILURE) Main PID: 689 (code=exited, status=1/FAILURE) Nov 25 00:01:24 coreos-2 systemd[1]: Starting Restore iptables firewall rules... Nov 25 00:01:24 coreos-2 systemd[1]: iptables-restore.service: Main process exited, code=exited, status=1/FAILURE Nov 25 00:01:24 coreos-2 systemd[1]: Failed to start Restore iptables firewall rules. Nov 25 00:01:24 coreos-2 iptables-restore[689]: Can't open /var/lib/iptables/rules-save: No such file or directory Nov 25 00:01:24 coreos-2 systemd[1]: iptables-restore.service: Unit entered failed state. Nov 25 00:01:24 coreos-2 systemd[1]: iptables-restore.service: Failed with result 'exit-code'.
这里有很多信息,但最有用的一行是包含 iptables-restore[689] 的那一行,它是 systemd 试图与其进程 ID 一起运行的进程的名称。 这是我们经常会发现失败服务的实际错误输出的地方。
防火墙无法恢复,因为虽然我们在 cloud-config 中启用了 iptables-restore,但我们还没有为它提供包含我们所需规则的文件。 我们本可以在创建 Droplet 之前完成此操作,但无法知道在创建 Droplet 之前将为其分配哪些 IP 地址。 现在我们知道了每个私有 IP,我们可以编写一个规则集。
在编辑器中打开一个新文件,粘贴以下内容,并将 coreos-1_private_ip、coreos-2_private_ip 和 coreos-3_private_ip 替换为每台 CoreOS 机器的私有 IP 地址。 您可能还需要调整 Accept all TCP/IP traffic... 下面的部分,以反映您打算从集群提供的公共服务,尽管此版本应该可以很好地用于演示目的。
/var/lib/iptables/rules-save
*filter :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] # Accept all loopback (local) traffic: -A INPUT -i lo -j ACCEPT # Accept all traffic on the local network from other members of # our CoreOS cluster: -A INPUT -i eth1 -p tcp -s coreos-1_private_ip -j ACCEPT -A INPUT -i eth1 -p tcp -s coreos-2_private_ip -j ACCEPT -A INPUT -i eth1 -p tcp -s coreos-3_private_ip -j ACCEPT # Keep existing connections (like our SSH session) alive: -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT # Accept all TCP/IP traffic to SSH, HTTP, and HTTPS ports - this should # be customized for your application: -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT # Accept pings: -A INPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT -A INPUT -p icmp -m icmp --icmp-type 3 -j ACCEPT -A INPUT -p icmp -m icmp --icmp-type 11 -j ACCEPT COMMIT
将以上内容复制到剪贴板,登录 coreos-1,然后使用 CoreOS 上的默认文本编辑器 Vim 打开 rules-save:
ssh -A core@coreos-1_public_ip
sudo vim /var/lib/iptables/rules-save
进入编辑器后,输入 :set paste 并按 Enter 确保关闭自动缩进,然后按 i 进入插入模式并粘贴防火墙规则. 按 Esc 退出插入模式,按 :wq 写入文件并退出。
警告: 确保文件的最后一行有一个尾随换行符,否则 IPTables 可能会因语法错误而失败,尽管文件中的所有命令都显示正确。
最后,确保文件具有适当的权限(用户可读写,组和世界只读):
sudo chmod 0644 /var/lib/iptables/rules-save
现在我们应该准备好再次尝试该服务:
sudo systemctl start iptables-restore
如果成功,systemctl 将静默退出。 我们可以通过两种方式检查防火墙的状态。 首先,通过使用 systemctl status:
sudo systemctl status -l iptables-restore
其次,列出当前的 iptables 规则本身:
sudo iptables -v -L
我们使用 -v 选项来获得详细输出,这将使我们知道给定规则适用于哪个接口。
一旦您确信 coreos-1 上的防火墙已配置,请注销:
exit
接下来,重复此过程以在 coreos-2 和 coreos-3 上安装 /var/lib/iptables/rules-save。
结论
在本指南中,我们定义了一个包含三个成员的基本 CoreOS 集群,为每个成员提供 TLS/SSL 证书以进行身份验证和传输安全,并使用防火墙阻止来自本地数据中心网络上其他 Droplet 的连接。 这有助于缓解在共享网络上使用 CoreOS 所涉及的许多基本安全问题。
从这里开始,您可以应用 本系列其余关于 CoreOS 入门的技术来定义和管理服务。