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

如果您计划运行在网络环境中CoreOS集群的控制之外,你可能会担心在ETCD纯HTTP进行通信。本指南描述启用TLS / SSL安全和CoreOS机器的基本防火墙。

介绍

如果您计划运行在网络环境中CoreOS集群以外的控制,如共享数据中心内或跨公共互联网,你可能已经注意到, etcd通过未加密的HTTP请求的通信。 通过在集群中的每个节点上配置IPTables防火墙,可以减轻该行为的风险,但是一个完整的解决方案最好使用加密的传输层。

幸运的是, etcd支持对等网络的TLS / SSL连接,从而使一个集群的每个成员被认证并且所有通信进行加密。 在本指南中,我们将开始配置具有三个成员的简单集群,然后在每台计算机上配置HTTPS端点和基本防火墙。

先决条件

本指南在很大程度上建立在讨论概念此介绍给CoreOS系统组件本指南建立在一个DigitalOcean集群CoreOS

你应该熟悉的基础知识etcdfleetctlcloud-config文件,并生成一个URL发现。

为了创建和访问集群中的计算机,您需要一个与您的DigitalOcean帐户相关联的SSH公钥。 有关使用SSH密钥与DigitalOcean的详细信息, 请参见这里

如果你想使用DigitalOcean API来创建你的CoreOS机,请参阅本教程 ,了解如何生成和使用个人访问令牌有写权限的信息。 使用API​​是可选的,但是从长远来看可以节省时间,特别是如果您预期构建更大的集群。

生成新的发现URL

检索一个新发现的URL从discovery.etcd.io,无论是通过参加https://discovery.etcd.io/new?size=3在浏览器复制URL显示,或通过使用curl你的本地计算机上从终端:

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

保存返回的URL; 我们将在使用它cloud-config不久。

编写包含HTTPS配置的Cloud-Config文件

我们将通过编写一个启动cloud-config cloud-config用户数据初始化每个服务器时,定义集群的重要配置细节提供。 该文件将是长期的,但不应风比中的版本更为复杂群集的基本指南 我们会告诉fleet明确使用HTTPS端点,使称为服务iptables-restore对我们的防火墙,并写出配置文件告诉etcdfleet在哪里可以找到SSL证书。

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

cd ~
nano cloud-config.yml

粘贴下面,然后更改https://discovery.etcd.io/tokenetcd2节你在最后一节声称发现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云配置验证 ,并按验证云的配置

保存文件并退出。 nano ,可以使用Ctrl-X做到这一点退出,y确认写入文件,并回车确认文件名保存。

让我们来看看具体的块从少数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块。 值被分成一个文件系统pathpermissions掩模,和content ,其中包含一个文件的期望的内容。 在这里,我们指定systemd单元文件的etcd2fleet服务应设置指向我们将生成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地址,这只有在机器创建后才可用。

注意:在CoreOSDroplet,中的内容cloud-config不能被创建Droplet之后改变,并且在每次启动该文件被重新执行。 你应该避免使用write-files部分您规划集群建成后修改任何配置,因为这将是今后一段时间的Droplet启动复位。

供应Droplet

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

使用DigitalOcean控制面板

在同一数据中心区域内创建三个新的CoreOS Droplet。 请务必检查专用网络 ,并启用用户数据各一次。

  • coreos-1
  • 核心-2
  • coreos-3

用户数据字段中,粘贴的内容cloud-config.yml从上面,确保你已经插入你的发现URL中discovery场附近的文件的顶部。

使用DigitalOcean API

作为一种替代方法可以节省重复粘贴到字段中,我们可以写它采用了短Bash脚本curl从我们的DigitalOcean API请求新的Dropletcloud-config ,并为每个Droplet一次调用它。 打开一个名为新文件makecoreos.shnano (或您选择的文本编辑器):

cd ~
nano makecoreos.sh

粘贴和保存下面的脚本,调整regionsize领域,希望为您的簇(的默认nyc3512mb的罚款出于演示的目的,但你可能要为现实世界的项目不同的地区或更大的Droplet):

〜/ 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在这里,以使外壳的子进程,像makecoreos.sh ,将能够访问的变量。 无论何时使用脚本,都必须在当前shell中设置这两者,否则API调用将失败:

export DO_SSH_KEY_FINGERPRINT="ssh_key_fingerprint"
export TOKEN="your_personal_access_token"

注意:如果您刚刚生成的个人访问令牌的API,记得要保持它的方便和安全。 在第一次创建时,没有办法检索它,任何拥有令牌的人都可以控制您的DigitalOcean帐户。

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

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

您应该看到JSON输出描述每个新的Droplet,所有三个应该出现在控制面板中的Droplets列表中。 可能需要几秒钟才能完成启动。

登录到coreos-1

无论是使用控制面板还是API,现在都应该有三个运行的Droplet。 现在是一个好时机,记下他们的公共和私有IP地址,这些都可以通过点击控制面板中的一个单独的Droplet,然后单击设置链接的。 在生成证书和配置防火墙时,需要使用每个Droplet的私有IP地址。

让我们测试一个Droplet。 请确保您的SSH密钥已添加到本地SSH代理:

eval $(ssh-agent)
ssh-add

找到DigitalOcean控制面板中coreos-1的公网IP地址,并与SSH代理转发功能连接:

ssh -A core@coreos-1_public_ip

在第一次登录到集群中的任何成员,我们可能会从收到一条错误消息systemd

CoreOS stable (766.5.0)
Failed Units: 1
  iptables-restore.service

这表示防火墙尚未配置。 现在,忽略此消息是安全的。 (如果你不选,让你的防火墙cloud-config ,你不会看到一个错误信息,您可以随时启用iptables-restore后的服务。)

之前我们担心的防火墙,让我们的etcd2集群互相交谈的每个成员的实例。

使用CFSSL生成自签名证书

CFSSL是使用TLS / SSL证书,通过CloudFlare的出版工作的工具包。 在写这篇文章的时候,它的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来获取最新版本cfsslcfssljson为您的平台:

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

现在,创建开放的ca-config.jsonnano (或你喜欢的文本编辑器):

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 authclient auth用途。 我们需要这两个对等TLS。

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

nano ca-csr.json

粘贴以下,调整CNnames按您的需要的位置和企业阵列。 它的安全使用虚构的值作为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.jsonca-csr.json ,可以打印使用默认值cfssl 对于ca-config.json ,使用方法:

cfssl print-defaults config

对于ca-csr.json ,使用方法:

cfssl print-defaults csr

随着ca-csr.jsonca-config.json到位,生成证书颁发机构:

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

为CoreOS计算机生成和签署证书

现在我们有一个证书颁发机构,我们可以为CoreOS机器编写默认值:

创建和打开coreos-1.json

nano coreos-1.json

粘贴并保存以下,(通过点击单个DropletDigitalOcean控制面板中可见)调整它coreos-1的私有IP地址:

〜/ 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)

这些将被添加到所得的证书作为subjectAltNamesetcd连接(包括在本地环回装置127.0.0.1 )要求证书具有SAN匹配连接主机名。

您还可以更改names数组以反映你的位置,如果需要的话。 同样,它可以安全地使用虚拟值的placenames。

重复此过程,其余每个机器,创建一个匹配的coreos-2.jsoncoreos-3.json与适当的hosts条目。

注意:如果您想看看默认值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.pemcoreos-key.pemcoreos.pem ),请确保权限是否正确的密钥文件,并通过将它们复制scp 核心的主目录上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:

检查etcd2 coreos-1上的功能

有了证书,应该能够运行fleetctlcoreos-1。 首先,通过SSH登录:

ssh -A core@coreos-1_public_ip

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

fleetctl list-machines

您应该看到列出的每台计算机的标识符及其专用IP地址:

MACHINE     IP      METADATA
7cb57440... 10.132.130.187  -
d91381d4... 10.132.87.87    -
eeb8726f... 10.132.32.222   -

如果fleetctl无限期挂起,可能需要重新启动集群是必要的。 退出到本地机器:

exit

使用SSH发送reboot命令给每个CoreOS机:

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 然而,如果可能,减少可用的攻击面是一个好主意。 为了限制我们的网络暴露,我们可以添加一些简单的防火墙规则到每个机器,阻止大多数本地网络流量从来自集群中的对等端之外的来源。

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

CoreOS stable (766.5.0)
Failed Units: 1
  iptables-restore.service

这让我们知道,虽然该服务已启用, iptables-restore未能正确加载。 我们可以通过诊断本systemctl

systemctl status -l iptables-restore
● 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运行。 这是我们经常会找到失败的服务的实际错误输出。

防火墙无法恢复,因为,当我们启用iptables-restorecloud-config ,我们还没有包含我们所期望的规则的文件,只要它。 我们可以在我们创建Droplet之前做到这一点,除了在创建Droplet之前没有办法知道将要分配哪些IP地址。 现在我们知道每个私有IP,我们可以写一个规则集。

在编辑器中打开一个新文件,粘贴以下,并更换coreos-1_private_ipcoreos-2_private_ipcoreos-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,和开放的rules-save使用的Vim ,在CoreOS默认的文本编辑器:

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

一旦在编辑器中,键入:set paste ,然后按Enter,以确保自动缩进被关闭,然后按i键进入插入模式并粘贴你的防火墙规则。 Esc键退出插入方式和:WQ写入文件并退出。

警告:确保有该文件的最后一行尾随换行,或者,正Tables可能会失败,混乱的语法错误,尽管在该文件中出现的正确的所有命令。

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

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

接下来,重复此过程,安装/var/lib/iptables/rules-savecoreos-2coreos-3。

结论

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

从这里,你可以在剩下的应用技术, 这一系列与CoreOS入门定义和管理服务。