ELK专题:Day9——从0开始在Elastic Stack中完成TLS加密

1. 前言

在本专题介绍到的Elastic Stack的各个场景中,一直都没有涉及到通信安全的内容。一则是因为我们这一套环境是建立在内网,相对来说比较安全;二则是不想在初期就引入太多复杂的内容。然而,在实际的生产环境中,通信安全仍然是一个很重要的议题。

在当前的这个实验环境中,这个ELK集群一直都处于一个不设防的状态,不仅是没有做任何的数据加密,连最简单的账号密码认证都没有。今天我们就来尝试一下,从0开始,一步一步地完善整个集群的安全设置。

kibana-login

2. 概念解释

2.1 TLS/SSL简介

很多时候,TLS和SSL都会被放到一起讨论,甚至会被当作是同一个东西。而事实上,SSL是一个已经过时的协议,而继任者就是TLS,其中TLS最新的是2018年发布的1.3版本。作为一个安全协议,TLS对服务端和客户端之间在认证和加密时候所使用的流程和算法进行了规范,而最常用的场景便是我们几乎每日都会使用的HTTPS了。

2.2 工作原理

TLS协议会要求参与通信的各个终端围绕X.509证书进行必要的认证和数据加密,证书里面包含一个公钥和与之关联的私钥。

而TLS连接是建立在TCP和应用层之间,所以在TLS连接之前,需要先完成TCP三次握手。当客户端向服务端发起连接请求的时候,先使用非对称加密的方法约定出一个session key,再使用session key对数据进行加密对称,大致的验证步骤如下:

  1. client向server发送client hello请求,包含协议版本号,可选的加密方法以及client random
  2. server回复server hello,选择一个client支持的加密算法,并返回一个数字证书CA和server random,同时要求client提供client的数字证书(双向验证)
  3. client校验CA是否有效,并返回自己的CA到server
  4. server校验client的CA
  5. client根据client randomserver random生成一个新的premaster scret,使用server证书里面的公钥加密后发送给server
  6. server用自己的私钥解密,得到premaster secret
  7. 后续client和server就使用premaster secret进行对称加密去交换数据,完成整个对话过程

需要说明的是,如果只是TLS单向认证,则可以跳过校验client CA的步骤。而TLS单向认证的一个典型应用,则是HTTPS。

2.3 私有CA

在x.509体系下,数字证书都是由CA(Certificate Authority)去签发,而CA又分为公共CA和私有CA。在我们这个试验环境下,都是基于内网IP进行通信的,我们只能选择使用私有CA去进行签发数字证书。因此,在为Kibana和ES等各个服务签发证书之前,我们需要先成立一个私有CA。

Luckily,在我们安装elasticsearch的时候,会附带安装一个证书管理工具elasticsearch-certutil,我们可以直接创建一个CA证书。

如下图所示,我们使用工具生成了一个证书文件elastic-stack-ca.p12,这个文件在后面的步骤里面都会用到。

create_ca

3. 配置过程

3.1 环境说明

我们依然使用这一套ELK系统,其中ES和Kibana都是单点部署:

diragram

3.2 为elasticsearch配置登录验证

3.2.1 elasticsearch.yml配置变更

  1. 停止ES和Kibana服务

  2. elasticsearch.yml配置文件中添加内容: xpack.security.enabled:true

  3. 因为在这个试验环境下,ES是单点部署,所以还需要在elasticsearch.yml添加discovery.type: single-node,这样就可以防止ES意外地和同一个局域网内的ES节点组成集群。

  4. 维持kibana服务的关闭状态,启动elasticsearch服务。在本案例中,elasticsearch.yml的内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    cluster.name: rc-application
    node.name: node-rc-1
    path.data: /data/elasticsearch
    path.logs: /var/log/elasticsearch
    network.host: 192.168.0.212
    http.port: 9200

    xpack.security.enabled: true
    discovery.type: single-node

3.2.2 为ES的内置用户创建密码

$ES_HOME目录下,自带密码工具./bin/elasticsearch-setup-passwords,可以对内置用户进行密码初始化。在本环境(elasticsearch 7.13)中,内置用户包括: elastic,apm_system,kibana,kibana_system,logstash_system,beats_system,remote_monitoring_user.

es-setup-passwords

使用过elasticsearch-setup-passwords命令后,想要再修改用户密码,只能通过Change Password API或者在Kibana进行修改.

elasticsearch设置了用户密码验证后,使用curl命令请求elasticsearch API时需要添加参数--user user:password,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
root@ES:~# curl -XGET --user elastic:123456 'http://192.168.0.212:9200/'
{
"name" : "node-rc-1",
"cluster_name" : "rc-application",
"cluster_uuid" : "y_q0vv9vQ3K7ukSdl0fO7g",
"version" : {
"number" : "7.13.4",
"build_flavor" : "default",
"build_type" : "deb",
"build_hash" : "c5f60e894ca0c61cdbae4f5a686d9f08bcefc942",
"build_date" : "2021-07-14T18:33:36.673943207Z",
"build_snapshot" : false,
"lucene_version" : "8.8.2",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}

3.2.3 在Kibana使用基础验证接入Elasticsearch

ES的安全选项开启后,kibana作为用户连接到ES的时候也需要携带验证信息,根据官方文档的指引,kibna使用kibana_system用户读取ES中的信息。而在登录kibana页面的时候,则是使用elastic用户,也即是ES的管理员账号.

  1. 我们在前面的步骤已经停止了kibana服务

  2. 在配置文件kibana.yml中添加内容:elasticsearch.username: "kibana_system"

  3. 使用自带工具kibana-keystore输入kibana_system的密码.kibana-keystore

  4. 启动kibana服务,使用用户elastic以及对应的密码登录

  5. 使用kibana.yml配置文件修改后如下:

    1
    2
    3
    4
    5
    6
    7
    8
    server.port: 5601
    server.host: "192.168.0.213"
    server.name: "rc-application-test-kibana"
    elasticsearch.hosts: "http://192.168.0.212:9200"
    logging.dest: /var/log/kibana/kibana.log
    logging.verbose: false

    elasticsearch.username: "kibana_system"

TIPS:

如果觉得配置keystore麻烦,也可以直接在kibana.yml使用明文配置elasticsearch.password,但你都来这里学习配置TLS了,还会明文配置密码吗? ;-)

3.3 为elasticsearch集群配置TLS证书

需要提前说明的是,如果是ES是以集群的方式运行,则需要在每个节点都进行一次证书的创建和配置修改。在本实验环境中,因为ES只有一个节点,所以只需要进行一次。

3.3.1 创建ES的证书

$ES_HOME路径下,执行命令./bin/elasticsearch-certutil cert --ca elastic-stack-ca.p12,在获得ES的证书elastic-certificates.p12.

3.3.2 向elasticsearch.yml添加ssl证书信息

elasticsearch.yml中添加xpack.security配置项,完整配置项如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cluster.name: rc-application
node.name: node-rc-1
path.data: /data/elasticsearch
path.logs: /var/log/elasticsearch
network.host: 192.168.0.212
http.port: 9200

xpack.security.enabled: true
discovery.type: single-node

xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.client_authentication: required
xpack.security.transport.ssl.keystore.path: /etc/elasticsearch/elastic-certificates.p12
xpack.security.transport.ssl.truststore.path: /etc/elasticsearch/elastic-certificates.p12

注意:

如果在生成证书elastic-certificates.p12的时候有使用密码,则需要使用key-store保存密码,执行下面的两条命令后,会把证书的密码保存在/etc/elasticsearch/elasticsearch.keystore

1
2
./bin/elasticsearch-keystore add xpack.security.transport.ssl.keystore.secure_password
./bin/elasticsearch-keystore add xpack.security.transport.ssl.truststore.secure_password

重启elasticsearch,并确认服务状态。

3.4 在Elasticsearch和Kibana中使用HTTPS

3.4.1 为elasticsearch服务签发https证书

  1. 停止集群中的ES和Kibana服务

  2. 再一次使用certutil工具,生成一个https证书。在这个过程中,我们需要引用在步骤2.3中创建的证书文件elastic-stack-ca.p12,操作步骤如下:

es_certutil_http

使用过程中会弹出很多疑问,在本环境中,大致选择如下:

  1. 是否创建CSR→n

  2. 是否使用已存在的CA→Y

  3. 输入CA的绝对路径,在这里使用的是/etc/elasticsearch/elastic-stack-ca.p12

  4. 输入CA的密码

  5. 设置证书有效时间,这里使用默认的5年

  6. 是否对每个节点创建证书→n,因为我只有一个ES节点

  7. 输入节点的hostname

  8. 输入节点的ip地址

  9. 校验信息准确性

  10. 选择http证书的存放路径

  11. 最后会生成一个压缩包elasticsearch-ssl-http.zip,包含如下文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Archive:  ./elasticsearch-ssl-http.zip
    Length Date Time Name
    --------- ---------- ----- ----
    0 2021-10-20 16:58 elasticsearch/
    1365 2021-10-20 16:58 elasticsearch/README.txt
    3588 2021-10-20 16:58 elasticsearch/http.p12
    850 2021-10-20 16:58 elasticsearch/sample-elasticsearch.yml
    0 2021-10-20 16:58 kibana/
    1306 2021-10-20 16:58 kibana/README.txt
    1200 2021-10-20 16:58 kibana/elasticsearch-ca.pem
    1057 2021-10-20 16:58 kibana/sample-kibana.yml
    --------- -------
    9366 8 files
  12. 把压缩包中的elasticsearch/http.p12移动到/etc/elasticsearch

  13. elasticsearch.yml添加如下内容:

    1
    2
    xpack.security.http.ssl.enabled: true
    xpack.security.http.ssl.keystore.path: /etc/elasticsearch/http.p12
  14. (可选步骤)如果http证书有设置密码,则把密码添加到keystore中

    1
    /usr/share/elasticsearch/bin/elasticsearch-keystore add xpack.security.http.ssl.keystore.secure_password
  15. 重启elasticsearch

3.4.2 加密Kibana和Elasticsearch之间的HTTP通信

  1. 把文件elasticsearch-ca.pem复制到Kibana节点中的$KBN_PATH_CONF,在本实验环境中,即/etc/kibana

  2. 修改kibana.yml,增加或修改如下内容:

    1
    2
    elasticsearch.hosts: "https://192.168.0.212:9200"
    elasticsearch.ssl.certificateAuthorities: /etc/kibana/elasticsearch-ca.pem
  3. 重启Kibana服务

3.4.3 加密Kibana和浏览器之间的HTTP通信

在上一个步骤的场景中,Kibana作为客户端向ES请求数据,而在这个步骤,Kibana转变为服务器的角色。

  1. 准备kibana的服务器证书

    1. 运行elasticsearch-certutil,创建Kibana服务的证书和私钥,默认会生成一个压缩包csr-bundle.zip

      1
      ./bin/elasticsearch-certutil csr -name kibana-server -dns 192.168.0.213
    2. 解压缩压缩包csr-bundle.zip,得到kibana-server.csrkibana-server.key

      1
      2
      3
      4
      5
      root@ES:/tmp# unzip  csr-bundle.zip   
      Archive: csr-bundle.zip
      creating: kibana-server/
      inflating: kibana-server/kibana-server.csr
      inflating: kibana-server/kibana-server.key
    3. 使用之前创建的CA根证书elastic-stack-ca.p12kibana-server.csr进行签发,得到kibana-server.crt

      1. 提取CA根证书里面的私钥和证书信息

        1
        2
        3
        4
        5
        # 提取私钥,保存为elastic-stack-ca.key
        openssl pkcs12 -in ./elastic-stack-ca.p12 -out elastic-stack-ca.key -nodes -nocerts

        # 提取证书,保存为elastic-stack-ca.crt
        openssl pkcs12 -in ./elastic-stack-ca.p12 -out elastic-stack-ca.crt -nokeys
      2. 使用CA根证书对kibana的证书进行签发,用专业术语来说,即是’Send the kibana-server.csr certificate signing request to internal CA’。

        从原理来解释,就是使用机构的私钥对Kibana服务的公钥进行加密,得到Kibana的证书。当用户需要获得Kibana服务的公钥的时候,就用机构公钥对Kibana的证书进行解密。

        1
        openssl x509 -req -sha256 -CA elastic-stack-ca.crt -CAkey elastic-stack-ca.key -CAcreateserial -in kibana-server.csr -out kibana-server.crt
  2. kibana-server.crtkibana-server.key存放到$KBN_PATH_CONF

  3. 修改kibana.yml,增加内容

    1
    2
    3
    server.ssl.enabled: true
    server.ssl.certificate: /etc/kibana/kibana-server.crt
    server.ssl.key: /etc/kibana/kibana-server.key
  4. 重启kibana服务

现在可以使用HTTPS协议访问Kibana页面了,因为这是使用自己创建的CA签发的证书,所以浏览器会提示不安全。

https_kibana

3.5 完善Metricbeat的安全配置

留坑

3.6 加密Logstash和Elasticsearch之间的流量

3.6.1 在ES创建角色logstash_write_role

  • 使用Kibana Roles UI创建

    create-logstash-write-role

  • 或使用Kibana Dev Tools创建

    logstash-write-role

    ↓ 点击展开完整POST内容 ↓
    
    POST /_security/role/logstash_write_role
    {
      "cluster": [
        "monitor",
        "manage_index_templates"
      ],
      "indices": [
        {
          "names": [
            "logstash*"
          ],
          "privileges": [
            "write",
            "create_index"
          ],
          "field_security": {
            "grant": [
              "*"
            ]
          }
        }
      ],
      "run_as": [],
      "metadata": {},
      "transient_metadata": {
        "enabled": true
      }
    }
        
        

3.6.2 在ES创建用户logstash_write

  • 使用Kibana Roles UI创建

    create-logstash-write

  • 或使用Kibana Dev Tools创建

    ↓ 点击展开完整POST内容 ↓
    
    POST /_security/user/logstash_writer
    {
      "username": "logstash_writer",
      "roles": [
        "logstash_write_role"
      ],
      "full_name": null,
      "email": null,
      "password": "123456",
      "enabled": true
    }
        
        

3.6.3 准备用于连接ES的证书

在这个步骤,使用crt格式的CA的根证书,我们在步骤3.4.3里面已经把p12格式的证书转成crt格式了,我们可以照搬使用,把证书文件elastic-stack-ca.crt放到/etc/kibana目录下。

注意要允许运行logstash服务的用户读取证书文件。

3.6.4 修改pipeline配置文件

配置示例如下:

1
2
3
4
5
6
7
8
9
10
11
input { ... }
filter { ... }
output {
elasticsearch {
hosts => ["https://192.168.0.212:9200"]
cacert => '/etc/logstash/elastic-stack-ca.crt'
index => "logstash-nginx-log-%{+YYYY.MM.dd}"
user => 'logstash_writer'
password => '123456'
}
}

以上步骤完成后,就可以重启Logstash服务验收啦。

如果过程中出现任何问题,可以参考Day2的思路,结合logstash的运行日志,调试配置文件。

3.7 Kibana和Elasticsearch之间的双向TLS认证

过于先进,不便展示。

4. 心得

在完成了整个Elastic Stack集群的性能监控之后,我就开始研究在Kibana上配置告警消息的推送。但是发现告警推送功能的部分API需要依赖TLS协议,遂花了不少时间去补课。在直接下手配置之前,还专门拾起了在大学里没学好的信息安全课程,更是写了两篇小作文,以为已经做好了准备,但是在下手配置的过程还是走了很多弯路。甚至,因为难度过高不得不留坑(2021/10/21) 。

从实用的角度出发,一个运行在内网的Elastic Stack集群不进行数据加密,我能想到的最大的隐患就是,当内网的其中一台机器被入侵后,黑客可以通过端口嗅探找到ES。对一个不设防的ES,可以轻易完成拖库操作。但这种隐患也只是在内网被入侵之后才会发生,而且前提是数据具备一定的价值才会被盯上。

但我本来创建的这套实验环境本来就不是从实用的角度出发,不是吗?(笑)

不过是想要通过不断的折腾去固化知识罢了。

5. 参考文档

Set up minimal security for Elasticsearch

Set up basic security for the Elastic Stack

Set up basic security for the Elastic Stack plus secured HTTPS traffic

配置 SSL、TLS 以及 HTTPS 来确保 Elasticsearch、Kibana、Beats 和 Logstash 的安全

Mutual TLS authentication between Kibana and Elasticsearch