如何使用 TLS/SSL 和防火墙规则保护 CoreOS 群集的安全

介绍

如果您计划在您控制之外的网络环境中运行 CoreOS 集群,例如在共享数据中心或公共互联网中,您可能已经注意到etcd通过进行未加密的 HTTP 请求进行通信。

幸运的是,etcd 支持对等 TLS/SSL 连接,这样一个群集的每个成员都被验证了,所有的通信都被加密了. 在本指南中,我们将开始提供一个简单的群集,包括三个成员,然后在每个机器上配置 HTTPS 终端和基本的防火墙。

前提条件

本指南主要基于在 本介绍 CoreOS 系统组件本指南在 DigitalOcean 上设置 CoreOS 集群中讨论的概念。

您应该熟悉etcd,fleetctl,cloud-config文件的基本知识,并生成一个发现URL。

为了创建和访问集群中的机器,您需要与您的 DigitalOcean 帐户相关联的 SSH 公共密钥。

如果您想使用 DigitalOcean API 来创建您的 CoreOS 机器,请参阅 本教程以获取有关如何生成和使用个人访问令牌的信息。

创建新发现 URL

通过访问 https://discovery.etcd.io/new?size=3在您的浏览器中,复制显示的 URL,或使用从本地机器的终端的弯曲:

1curl -w "\n" "https://discovery.etcd.io/new?size=3"

保存返回的 URL;我们将很快在我们的云配置中使用它。

写一个包含 HTTPS 配置的云配置文件

我们将从写一个云配置开始。在初始化每个服务器时,云配置将作为用户数据提供,为集群定义重要配置细节. 这个文件将很长,但不应该比基本集群指南中的版本复杂得多(https://andsky.com/tech/tutorials/how-to-set-up-a-coreos-cluster-on-digitalocean).我们将明确告诉fleet使用HTTPS终端,为我们的防火墙启用一个名为iptables-restore的服务,并写出配置文件,告诉etcdfleet在哪里找到SSL证书。

在本地计算机上打开终端,确保您在主目录中,并使用nano(或您最喜欢的文本编辑器)创建和打开~/cloud-config.yml:

1cd ~
2nano cloud-config.yml

粘贴以下内容,然后在etcd2节中更改https://discovery.etcd.io/token到你在最后一节声称的发现URL。

您也可以删除iptables-restore部分,如果您不想启用防火墙。

当粘贴时要小心插入时,云配置是用YAML编写的,这是对白空间敏感的。 请参阅文件中的评论,以获取有关特定行的信息,然后我们将更详细地讨论一些重要的部分。

 1[label ~/cloud-config.yml]
 2#cloud-config
 3
 4coreos:
 5  etcd2:
 6    # generate a new token for each unique cluster from https://discovery.etcd.io/new:
 7    discovery: https://discovery.etcd.io/token
 8    # multi-region deployments, multi-cloud deployments, and Droplets without
 9    # private networking need to use $public_ipv4:
10    advertise-client-urls: https://$private_ipv4:2379,https://$private_ipv4:4001
11    initial-advertise-peer-urls: https://$private_ipv4:2380
12    # listen on the official ports 2379, 2380 and one legacy port 4001:
13    listen-client-urls: https://0.0.0.0:2379,https://0.0.0.0:4001
14    listen-peer-urls: https://$private_ipv4:2380
15  fleet:
16    # fleet defaults to plain HTTP - explicitly tell it to use HTTPS on port 4001:
17    etcd_servers: https://$private_ipv4:4001
18    public-ip: $private_ipv4   # used for fleetctl ssh command
19  units:
20    - name: etcd2.service
21      command: start
22    - name: fleet.service
23      command: start
24    # enable and start iptables-restore
25    - name: iptables-restore.service
26      enable: true
27      command: start
28write_files:
29  # tell etcd2 and fleet where our certificates are going to live:
30  - path: /run/systemd/system/etcd2.service.d/30-certificates.conf
31    permissions: 0644
32    content: |
33      [Service]
34      # client environment variables
35      Environment=ETCD_CA_FILE=/home/core/ca.pem
36      Environment=ETCD_CERT_FILE=/home/core/coreos.pem
37      Environment=ETCD_KEY_FILE=/home/core/coreos-key.pem
38      # peer environment variables
39      Environment=ETCD_PEER_CA_FILE=/home/core/ca.pem
40      Environment=ETCD_PEER_CERT_FILE=/home/core/coreos.pem
41      Environment=ETCD_PEER_KEY_FILE=/home/core/coreos-key.pem
42  - path: /run/systemd/system/fleet.service.d/30-certificates.conf
43    permissions: 0644
44    content: |
45      [Service]
46      # client auth certs
47      Environment=FLEET_ETCD_CAFILE=/home/core/ca.pem
48      Environment=FLEET_ETCD_CERTFILE=/home/core/coreos.pem
49      Environment=FLEET_ETCD_KEYFILE=/home/core/coreos-key.pem

作为一个可选的步骤,您可以将云配置粘贴到 官方的 CoreOS 云配置验证器并按 验证云配置

在「nano」中,您可以用 Ctrl-X来退出, y来确认写文件,和 Enter来确认要保存的文件名。

让我们看看从cloud-init.yml中的一些特定的块,首先是fleet值:

1fleet:
2    # fleet defaults to plain HTTP - explicitly tell it to use HTTPS:
3    etcd_servers: https://$private_ipv4:4001
4    public-ip: $private_ipv4   # used for fleetctl ssh command

请注意,etcd_servers 设置为https URL. 对于简单的 HTTP 操作,这个值不需要设置. 但是,如果没有明确的配置,HTTPS 将失败($private_ipv4 是一个由 CoreOS 初始化过程理解的变量,而不是你需要改变的变量)。

然后我们进入了write_files块,值被分成一个文件系统path,permissions面具和content,其中包含一个文件的所需内容。

 1write_files:
 2  # tell etcd2 and fleet where our certificates are going to live:
 3  - path: /run/systemd/system/etcd2.service.d/30-certificates.conf
 4    permissions: 0644
 5    content: |
 6      [Service]
 7      # client environment variables
 8      Environment=ETCD_CA_FILE=/home/core/ca.pem
 9      ...
10  - path: /run/systemd/system/fleet.service.d/30-certificates.conf
11    permissions: 0644
12    content: |
13      [Service]
14      # client auth certs
15      Environment=FLEET_ETCD_CAFILE=/home/core/ca.pem
16      ...

虽然我们告诉服务在哪里找到证书文件,但我们还不能提供文件本身. 为此,我们需要知道每个CoreOS机器的私人IP地址,这只有在机器被创建后才可用。

<$>[注] 注: 在CoreOS Droplets中,在创建Droplet后,无法更改‘Cloud-config’的内容,并且在每次启动时都会重新执行该文件。

供应滴

现在我们已经定义了cloud-config.yml,我们将使用它来提供群集的每个成员。在DigitalOcean上,我们可以采取两种基本方法:通过基于Web的控制面板,或使用命令行中的cURL向DigitalOcean API进行调用。

使用数字海洋控制面板

在同一数据中心区域内创建三个新的 CoreOS 滴滴,请确保每次检查 私人网络启用用户数据

  • coreos-1
  • coreos-2
  • coreos-3

用户数据字段中,从上方粘贴cloud-config.yml的内容,确保您在文件顶部附近的发现字段中插入了发现 URL。

使用 DigitalOcean API

作为一种可以节省重复粘贴到字段的替代方法,我们可以写一个短的Bash脚本,它使用弯曲来请求从DigitalOcean API使用我们的云配置,并呼吁每一个Droplet一次。

1cd ~
2nano makecoreos.sh

粘贴并保存下面的脚本,根据您的集群所需调整区域大小字段(nyc3512mb的默认值适用于演示目的,但您可能想要一个不同的区域或更大的Dropplets用于现实世界项目):

 1[label ~/makecoreos.sh]
 2#!/usr/bin/env bash
 3
 4# A basic Droplet create request.
 5curl -X POST "https://api.digitalocean.com/v2/droplets" \
 6     -d'{"name":"'"$1"'","region":"nyc3","size":"512mb","private_networking":true,"image":"coreos-stable","user_data":
 7"'"$(cat ~/cloud-config.yml)"'",
 8         "ssh_keys":[ "'$DO_SSH_KEY_FINGERPRINT'" ]}' \
 9     -H "Authorization: Bearer $TOKEN" \
10     -H "Content-Type: application/json"

现在,让我们将环境变量 $DO_SSH_KEY_FINGERPRINT 和 $TOKEN 设置为与您的 DigitalOcean 帐户相关联的 SSH 密钥的指纹和 API Personal Access Token。

有关获取个人访问令牌和使用API的信息,请参阅 本教程

要找到与您的帐户相关联的密钥的指纹,请检查您的帐户设置的 安全性部分(https://cloud.digitalocean.com/settings/security),在 SSH 密钥下。

我们在这里使用导出,以便壳的子过程,如makecoreos.sh,能够访问变量. 无论在使用脚本时,都必须在当前壳中设置,否则API调用将失败:

1export DO_SSH_KEY_FINGERPRINT="ssh_key_fingerprint"
2export TOKEN="your_personal_access_token"

<$>[注] 注: 如果您刚刚为 API 生成了一个个人访问令牌,请记住保持方便和安全。

一旦我们为每一个所需的凭证设置了环境变量,我们就可以运行脚本来创建每一个想要的Droplet。 makecoreos.sh 使用其第一个参数来填写其 API 调用中的 `name' 字段:

1bash makecoreos.sh coreos-1
2bash makecoreos.sh coreos-2
3bash makecoreos.sh coreos-3

您应该看到 JSON 输出描述每一个新的 Droplet,并且所有三个都应该出现在控制面板中的 Droplets 列表中。

点击点击Coreos-1

无论您是否使用了控制面板或API,您现在都应该有三个运行 Droplets. 现在是注意他们的公共和私人IP的良好时机,这些IP在控制面板上可以通过单击单个 Droplet,然后点击 Settings链接来获取。

请确保您的 SSH 密钥被添加到您的本地 SSH 代理:

1eval $(ssh-agent)
2ssh-add

在 DigitalOcean 控制面板中找到 coreos-1 的公共 IP 地址,并连接到启用 SSH 代理转发:

1ssh -A core@coreos-1_public_ip

第一次登录到群集中的任何成员时,我们可能会收到来自systemd的错误消息:

1[secondary_label Output]
2CoreOS stable (766.5.0)
3Failed Units: 1
4  iptables-restore.service

这表明防火墙尚未配置,目前无视此消息是安全的(如果您选择在云配置中不启用防火墙,您将不会看到错误消息。

在我们担心防火墙之前,让我们在集群的每个成员上看到etcd2实例彼此交谈。

使用CFSSL生成自签证书

CFSSL是由CloudFlare发布的TLS/SSL证书的工具包,在这篇文章写作时,它是CoreOS维护者选择的工具,用于生成自签证书,而不是OpenSSL和现在被贬值的etcd-ca

在本地机器上安装CFSSL

CFSSL 需要从源头安装一个工作 Go 安装,请参阅 此安装 Go 指南

确保您的$GOPATH设置正确并添加到您的$PATH,然后使用go get来安装cfssl命令:

1export GOPATH=~/gocode
2export PATH=$PATH:$GOPATH/bin
3go get -u github.com/cloudflare/cfssl/cmd/cfssl
4go get -u github.com/cloudflare/cfssl/...

作为一种替代方法,可以从 pkg.cfssl.org获取预先构建的二进制。

1mkdir -p ~/bin
2export PATH=$PATH:~/bin

然后使用curl来获取您平台的cfsslcfssljson的最新版本:

1curl -s -L -o ~/bin/cfssl https://pkg.cfssl.org/R1.1/cfssl_linux-amd64
2curl -s -L -o ~/bin/cfssljson https://pkg.cfssl.org/R1.1/cfssljson_linux-amd64

确保cfssl二进制是可执行的:

1chmod +x ~/bin/cfssl
2chmod +x ~/bin/cfssljson

生成证书权威

现在安装了cfssl命令,我们可以使用它们来生成一个自定义证书权限,我们将使用它来签署我们每个CoreOS机器的证书。

1mkdir ~/coreos_certs
2cd ~/coreos_certs

现在,在nano中创建并打开ca-config.json(或您最喜欢的文本编辑器):

1nano ca-config.json

粘贴并保存以下内容,该内容将配置cfssl如何进行签名:

 1[label ~/coreos_certs/ca-config.json]
 2{
 3    "signing": {
 4        "default": {
 5            "expiry": "43800h"
 6        },
 7        "profiles": {
 8            "client-server": {
 9                "expiry": "43800h",
10                "usages": [
11                    "signing",
12                    "key encipherment",
13                    "server auth",
14                    "client auth"
15                ]
16            }
17        }
18    }
19}

值得注意的是到期期限,目前设置为43800小时(或5年),以及客户端服务器配置文件,其中包括服务器auth客户端auth的使用。

接下来,创建并打开ca-csr.json

1nano ca-csr.json

粘贴以下内容,根据您的位置和组织所需调整CN名称数组,可以安全地使用主机输入以及地点和组织名称的虚构值:

 1[label ~/coreos_certs/ca-csr.json]
 2{
 3    "CN": "My Fake CA",
 4    "hosts": [
 5        "example.net",
 6        "www.example.net"
 7    ],
 8    "key": {
 9        "algo": "rsa",
10        "size": 2048
11    },
12    "names": [
13        {
14            "C": "US",
15            "L": "CO",
16            "O": "My Company",
17            "ST": "Lyons",
18            "OU": "Some Org Unit"
19        }
20    ]
21}

<$>[注] 如果您想将这些值与 ca-config.jsonca-csr.json 的默认值进行比较,则可以使用 cfssl 打印默认值。

1cfssl print-defaults config

对于ca-csr.json,请使用:

1cfssl print-defaults csr

美元

有了ca-csr.jsonca-config.json,生成证书权限:

1cfssl gencert -initca ca-csr.json | cfssljson -bare ca -

生成和签署 CoreOS 机器的证书

现在我们有证书权限,我们可以为CoreOS机器写默认值:

创建并打开coreos-1.json:

1nano coreos-1.json

粘贴并保存下列内容,将其调整为 coreos-1的私人IP地址(通过单个 Droplet 点击在 DigitalOcean 控制面板中可见):

 1[label ~/coreos_certs/coreos-1.json]
 2{
 3    "CN": "coreos-1",
 4    "hosts": [
 5        "coreos-1",
 6        "coreos-1.local",
 7        "127.0.0.1",
 8        "coreos-1_private_ip"
 9    ],
10    "key": {
11        "algo": "rsa",
12        "size": 2048
13    },
14    "names": [
15        {
16            "C": "US",
17            "L": "Lyons",
18            "ST": "Colorado"
19        }
20    ]
21}

最重要的部分是CN,这应该是您的主机名,以及主机阵列,必须包含所有:

  • 您的本地主机名(s)
  • 127.0.0.1
  • CoreOS 机器的私人 IP 地址(不是其面向公众的 IP)

这些将被添加到结果的证书为 subjectAltNames. etcd 连接(包括在 `127.0.0.1 上的本地循环设备)需要证书具有匹配连接主机名的 SAN。

您也可以更改名称数组以反映您的位置,如果您想要的话。

对每个剩余的机器重复这个过程,创建一个与相应的主机条目匹配的coreos-2.jsoncoreos-3.json

<$>[注] 注: 如果您想查看 coreos-1.json 的默认值,您可以使用 cfssl:

1cfssl print-defaults csr

美元

现在,对于每个CoreOS机器,生成一个签名的证书,并将其上传到正确的机器:

1cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client-server coreos-1.json | cfssljson -bare coreos
2chmod 0644 coreos-key.pem
3scp ca.pem coreos-key.pem coreos.pem core@coreos-1_public_ip:

这将创建三个文件(ca.pem, coreos-key.pemcoreos.pem),确保权限在 keyfile 上正确,并通过 scp 将其复制到 coreos-1上的 core的主目录。

对于剩余的每个机器,重复这个过程,记住每个命令的召唤都会重写上一组证书文件:

1cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client-server coreos-2.json | cfssljson -bare coreos
2chmod 0644 coreos-key.pem
3scp ca.pem coreos-key.pem coreos.pem core@coreos-2_public_ip:
1cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client-server coreos-3.json | cfssljson -bare coreos
2chmod 0644 coreos-key.pem
3scp ca.pem coreos-key.pem coreos.pem core@coreos-3_public_ip:

在 coreos-1 上检查 etcd2 功能

有了证书,我们应该能够在 coreos-1上运行fleetctl

1ssh -A core@coreos-1_public_ip

接下来,尝试列出群集中的所有机器:

1fleetctl list-machines

您应该看到列出的每个机器的标识符以及其私人IP地址:

1[secondary_label Output]
2MACHINE		IP		METADATA
37cb57440...	10.132.130.187	-
4d91381d4...	10.132.87.87	-
5eeb8726f...	10.132.32.222	-

如果「fleetctl」暂停无限期,则可能需要重新启动集群。

1exit

使用 SSH 向每个 CoreOS 机器发送重启命令:

1ssh core@coreos-1_public_ip 'sudo reboot'
2ssh core@coreos-2_public_ip 'sudo reboot'
3ssh core@coreos-3_public_ip 'sudo reboot'

等待几分钟,重新连接到 coreos-1,然后再试试fleetctl

在集群成员中配置 IPTables 防火墙

有了证书,本地网络上的其他机器将无法控制您的集群或从etcd2中提取值。然而,如果可能的话,最好减少可用的攻击面积。

请记住,如果我们在cloud-config中启用了iptables-restore服务,我们会在首次登录CoreOS机时看到systemd错误消息:

1[secondary_label Output]
2CoreOS stable (766.5.0)
3Failed Units: 1
4  iptables-restore.service

这让我们知道,虽然该服务已启用,但iptables-restore无法正确加载,我们可以使用systemctl来诊断这种情况:

1systemctl status -l iptables-restore
 1[secondary_label Output]
 2 iptables-restore.service - Restore iptables firewall rules
 3   Loaded: loaded (/usr/lib64/systemd/system/iptables-restore.service; enabled; vendor preset: disabled)
 4   Active: failed (Result: exit-code) since Wed 2015-11-25 00:01:24 UTC; 27min ago
 5  Process: 689 ExecStart=/sbin/iptables-restore /var/lib/iptables/rules-save (code=exited, status=1/FAILURE)
 6 Main PID: 689 (code=exited, status=1/FAILURE)
 7
 8Nov 25 00:01:24 coreos-2 systemd[1]: Starting Restore iptables firewall rules...
 9Nov 25 00:01:24 coreos-2 systemd[1]: iptables-restore.service: Main process exited, code=exited, status=1/FAILURE
10Nov 25 00:01:24 coreos-2 systemd[1]: Failed to start Restore iptables firewall rules.
11Nov 25 00:01:24 coreos-2 iptables-restore[689]: Can't open /var/lib/iptables/rules-save: No such file or directory
12Nov 25 00:01:24 coreos-2 systemd[1]: iptables-restore.service: Unit entered failed state.
13Nov 25 00:01:24 coreos-2 systemd[1]: iptables-restore.service: Failed with result 'exit-code'.

这里有很多信息,但最有用的行是包含iptables-restore[689]的行,这就是试图与其过程ID一起运行的过程systemd的名称,这就是我们经常找到失败服务的实际错误输出的地方。

防火墙未能恢复,因为,虽然我们在云配置中启用了iptables-restore,但我们还没有提供包含我们想要的规则的文件。

在编辑器中打开一个新的文件,粘贴以下内容,并用每个CoreOS机的私有IP地址代替coreos-1_private_ip,coreos-2_private_ipcoreos-3_private_ip

 1[label /var/lib/iptables/rules-save]
 2*filter
 3:INPUT DROP [0:0]
 4:FORWARD DROP [0:0]
 5:OUTPUT ACCEPT [0:0]
 6
 7# Accept all loopback (local) traffic:
 8-A INPUT -i lo -j ACCEPT
 9
10# Accept all traffic on the local network from other members of
11# our CoreOS cluster:
12-A INPUT -i eth1 -p tcp -s coreos-1_private_ip -j ACCEPT
13-A INPUT -i eth1 -p tcp -s coreos-2_private_ip -j ACCEPT
14-A INPUT -i eth1 -p tcp -s coreos-3_private_ip -j ACCEPT
15
16# Keep existing connections (like our SSH session) alive:
17-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
18
19# Accept all TCP/IP traffic to SSH, HTTP, and HTTPS ports - this should
20# be customized for your application:
21-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
22-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
23-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
24
25# Accept pings:
26-A INPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT
27-A INPUT -p icmp -m icmp --icmp-type 3 -j ACCEPT
28-A INPUT -p icmp -m icmp --icmp-type 11 -j ACCEPT
29COMMIT

将上述内容复制到剪辑板上,登录到 coreos-1,并使用 Vim打开规则保存,这是 CoreOS 上的默认文本编辑器:

1ssh -A core@coreos-1_public_ip
1sudo vim /var/lib/iptables/rules-save

一旦进入编辑器,键入 :set paste 并按 Enter 以确保自动插入被关闭,然后按 i 进入插入模式并粘贴您的防火墙规则。

<$>[警告] 警告: 请确保文件的最后一行上有新线条,否则IPTables可能会导致混淆的语法错误,尽管文件中的所有命令都显示正确。

最后,请确保文件具有适当的权限(为用户阅读和写入,仅为组和世界阅读):

1sudo chmod 0644 /var/lib/iptables/rules-save

现在我们应该准备好再次尝试服务:

1sudo systemctl start iptables-restore

如果成功,‘systemctl’会默默地退出,我们可以通过两种方式检查防火墙的状态,首先是使用‘systemctl status’:

1sudo systemctl status -l iptables-restore

第二,通过列出当前的iptables规则本身:

1sudo iptables -v -L

我们使用-v选项来获得无语的输出,这将让我们知道一个规则适用于哪个界面。

一旦您确信 coreos-1上的防火墙已配置,请退出:

1exit

接下来,重复此过程以在 coreos-2coreos-3 上安装 /var/lib/iptables/rules-save

结论

在本指南中,我们定义了三个成员的基本 CoreOS 集群,每个成员都提供了一个 TLS/SSL 证书用于身份验证和运输安全,并使用防火墙来阻止本地数据中心网络上的其他 Droplets 的连接,这有助于缓解在共享网络上使用 CoreOS 的许多基本安全问题。

从这里,您可以应用本系列的其余部分(https://www.digitalocean.com/community/tutorial_series/getting-started-with-coreos-2)中的技术来定义和管理服务。

Published At
Categories with 技术
comments powered by Disqus