一文秒懂!腾讯云ES HTTPS 集群访问通信最佳实践

作者:吴容,腾讯云Elasticsearch高级开发工程师

Elasticsearch提供了多种数据访问安全的方式,如用户名密码校验、api_key等。但是依然无法保障数据传输过程中的安全性问题。而HTTPS协议,则是一种以安全为目的的HTTP通道,在HTTP的基础上通过传输加密和身份认证等机制来保障数据传输过程中的安全性。

本文将基于腾讯云ES集群环境,演示Beats、Logstash、Kibana和Java Client等客户端访问连接开启了HTTPS协议的ES集群。

一、HTTPS集群环境准备

1、创建HTTPS协议集群

首先我们在腾讯云ES控制台创建出一个HTTPS集群,在购买页这里勾选上HTTPS协议。目前该特性目前是通过白名单支持,可提工单申请开放。

图1. 腾讯云ES购买页选择创建HTTPS协议的ES集群

其中,ES集群是通过在elasticsearch.yml配置文件中设置如下参数来开启HTTPS协议的:

代码语言:javascript
复制
xpack.security.http.ssl.enabled: truexpack.security.http.ssl.keystore.path: certs/ces-certificates.p12xpack.security.http.ssl.truststore.path: certs/ces-certificates.p12

2、获取pem证书文件

集群创建成功后,提工单让腾讯云ES研发提供访问HTTPS集群所需要的鉴权证书文件。此时腾讯云ES侧会提供如下两个证书文件:

文件名称

用途

client-certificates.pem

用于Beats Logstash、Java Client连接ES集群

server-certificates.pem

用于KIbana连接ES集群

下面将详细介绍Beats、Logstash、Kibana和Java等客户端连接HTTPS集群的配置方式。

二、Beats输出到HTTPS集群

1、CVM Metriceat输出到ES

我们首先在腾讯云CVM控制台创建一个和ES集群同VPC下的CVM,创建好后,将得到的pem鉴权文件上传到该CVM上,这里的存放路径为:/usr/local/service/https-certs。随后到腾讯云ES控制台的Beats管理页,创建一个Metricbeat:

图2. 创建Metricbeat

其中,最核心的步骤是在metricbeat.yml配置文件中进行如下配置。

代码语言:javascript
复制
output.elasticsearch:  hosts: ["https://ES-VIP:9200"]  username: "elastic"  password: "changeme"  ssl.certificate_authorities: ["/usr/local/service/https-certs/client-certificates.pem"]  ssl.verification_mode: certificate

配置信息说明:

配置项

说明

hosts

ES集群的VIP,如https://10.0.X.29:9200,以https开头

username/password

ES集群的用户名密码

ssl.certificate_authorities

连接HTTPS集群所需的pem鉴权证书文件路径

ssl.verification_mode

服务器证书认证模式,有四种模式,分别是full,strict,certificate和none。我们这里以certificate模式进行认证,即只认证CA证书,不认证主机名信息。详情可参考官方文档。

配置完成后,即可在ES集群中看到自动创建了一个metricbeat-7.14.20-*开头的索引,到此CVM中Metricbeat连接HTTPS的ES集群配置完成。

图3. ES集群中自动创建了metricbeat相关的索引

2、TKE Filebeat日志采集器输出到ES

TKE Filebeat日志采集器输出到HTTPS的ES集群流程和CVM的metricBeat输出一样,首先我们将pem文件上传到创建TKE集群时自动创建的Worker所在的CVM节点上,如/var/log/https-certs目录下。

图4. 创建TKE集群

client-certificates.pem文件存放位置:

图5. 拷贝pem文件到Worker节点

随后我们在腾讯云ES控制新建Fliebeat采集器,这里选择TKE日志采集:

图6. 创建Filebeat TKE日志采集器

下面进行Filebeat采集器的基本信息配置,如版本号选择,采集器输出ES集群选择,如图7、8所示:

图7. 配置Filbeat采集器的输出端集群

图8. 配置Filebeat采集TKE容器日志集群

创建好Filebeat TKE容器日志采集器后,随后我们在TKE集群的详情页,找到配置管理中的ConfigMap,然后找到对应beats的config文件:

图9. TKE配置管理页

点击“编辑yaml”后对output.elasticsearch配置项进行修改,如下图10所示:

图10. 编辑output.elasticsearch配置信息

具体配置信息如下:

代码语言:javascript
复制
output.elasticsearch:    hosts: ['https://ES-VIP:9200']    username: "elastic"    ssl.certificate_authorities: ["/var/log/https-certs/client-certificates.pem"]    ssl.verification_mode: "certificate"    password: "changeme"    indices:      - index: "filebeat-tke-%{+yyyy.MM.dd}"        when.equals:          tke_collector_target_name: "logs_to_https_es"

然后在TKE集群中的工作负载中,找到DaemonSet,然后再到对应beats的DaemonSet,点击进去,对它的pod进行销毁重建。

图11. TKE DaemonSet管理页

进入到Pod管理页,选择对应的Pod销毁重建。

图12. 选择对应Pod销毁重建

销毁重建后,Pod的运行状态变成Running,如图13所示。 

注:如果多次对pod进行销毁重建,仍是红色Running,有可能是beats的yml配置出现问题,可以在日志里面查看具体问题,或者重新检查一下yml配置是否错误。

图13. 销毁重建后的Pod正常运行

随后我们到ES集群中,可以看到自动创建了以filebeat-tke-*开头的索引。表明TKE日志顺利输出到HTTPS集群中了。

图14. ES集群中自动创建TKE日志采集器相关索引

三、Logstash输出到HTTPS集群

腾讯云Logstash是一款全托管的产品,因此我们首先需要在腾讯云Logstash控制台将pem文件以扩展文件方式进行上传,如下图15所示。 

图15. 腾讯云Logstash控制台上传pem文件

随后我们新建一个管道,在管道的Config配置编辑框里配置上ES的连接信息和证书路径。

图16. 配置Logstash的管道Config

管道详细配置文本信息如下:

代码语言:javascript
复制
output {  elasticsearch {      hosts => ["https://ES-VIP:9200"]      user => "elastic"      password => "changeme"      ssl => true      cacert => "/usr/local/service/logstash/extended-files/client-certificates.pem"      ssl_certificate_verification => false  }}

其中cacert即可我们上一步上传的扩展文件pem的路径,固定路径为:/usr/local/service/logstash/extended-files/client-certificates.pem。点击保存并部署管道后,就会在管道列表里可以看到我们刚刚新建出来的管道信息了,此时状态为运行中。

图17. Logstash管道列表页

这时候我们再到HTTPS集群中就可以看到有数据从input的集群中写入进来了。

四、Kibana连接HTTPS集群

腾讯云ES集群默认自带Kibana访问能力,因此一般情况下,客户是不需要对Kibana进行任何配置的。这里介绍自建Kibana连接HTTPS集群的配置方式。和Beats、Logstash等使用的客户端鉴权证书不一样,Kibana使用的是server-certificates.pem,腾讯云ES侧生成证书命令如下:

代码语言:javascript
复制
openssl pkcs12 -in ces-certificates.p12 -password pass:xxxxxx -nokeys -cacerts -out server-certificates.pem

由于前面我们已经拿到了pem证书文件。因此,我们将server-certificates.pem文件拷贝到Kibana所在节点的如下路径:/usr/local/service/https-certs。然后修改kibana.yml配置文件如下:

代码语言:javascript
复制
elasticsearch.hosts: ["https://ES-VIP:9200"]elasticsearch.username: "elastic"elasticsearch.password: "changeme"elasticsearch.ssl.verificationMode: certificateelasticsearch.ssl.certificateAuthorities: ["/usr/local/service/https-certs/server-certificates.pem"]xpack.encryptedSavedObjects.encryptionKey: "dfed624ca4014135f61804440536xxxx"xpack.fleet.registryUrl: "https://epr.elastic.co"

‍配置项说明:elasticsearch.ssl.certificateAuthorities:pem文件的存放路径。elasticsearch.ssl.verificationMode:证书鉴权模式,certificate 采用只鉴权CA证书,不鉴权主机名称的模式。xpack.fleet.registryUrl:(非必选配置)Fleet集成模块需要访问的公网仓库,这里需要Kibana节点具备公网访问能力。xpack.encryptedSavedObjects.encryptionKey:value可以通过bin/kibana-encryption-keys generate 命令获得。 

图18. 生成Kibana安全密钥

重新启动Kibana后即可正常访问HTTPS的ES集群了。

五、Java Client连接HTTPS集群

本文档演示在Spring项目中访问HTTPS集群的配置方式,首先需要添加如下maven依赖:

代码语言:javascript
复制
      <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-client</artifactId><version>7.10.1</version></dependency><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.10.1</version></dependency><dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>7.10.1</version></dependency>

‍随后我们在ElasticsearchConfig自定义类中实现对Elasticsearch连接的配置。

代码语言:javascript
复制
@Configurationpublic class ElasticsearchConfig {  @Value("${es.username}")  private String userName;  @Value("${es.password}")  private String password;  @Value("${es.scheme}")  private String scheme;  @Value("${es.domain}")  private String domain;  @Value("${es.https.cert}")  private String certFile;  @Value("${es.port}")  private int port;  private RestHighLevelClient client;  @Bean  public RestHighLevelClient EsConnectInit() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {      final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();      credentialsProvider.setCredentials(AuthScope.ANY,new UsernamePasswordCredentials(userName,password));      RestClientBuilder builder;      if (scheme.equals("https")) {          Path caCertificatePath = Paths.get(certFile);          System.out.println(certFile);          CertificateFactory factory = CertificateFactory.getInstance("X.509");          Certificate trustedCa;          try(InputStream is = Files.newInputStream(caCertificatePath)) {              trustedCa = factory.generateCertificate(is);          }          KeyStore trustStore = KeyStore.getInstance("pkcs12");          trustStore.load(null,null);          trustStore.setCertificateEntry("ca",trustedCa);          SSLContextBuilder sslContextBuilder = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustStrategy() {              @Override              public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {                  return true;              }          });          final SSLContext sslContext = sslContextBuilder.build();          final HostnameVerifier hostnameVerifier = new HostnameVerifier() {              public boolean verify(String hostname, SSLSession session) {                  return true;              }          };          builder = RestClient.builder(new HttpHost(domain, port, scheme)).setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {              @Override              public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) {                  requestConfigBuilder.setConnectTimeout(-1);                  requestConfigBuilder.setSocketTimeout(-1);                  requestConfigBuilder.setConnectionRequestTimeout(-1);                  return requestConfigBuilder;              }          }).setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {              @Override              public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {                  httpClientBuilder.disableAuthCaching();                  httpClientBuilder.setSSLHostnameVerifier(hostnameVerifier);                  return httpClientBuilder.setSSLContext(sslContext)                          .setDefaultCredentialsProvider(credentialsProvider);
              }          });      } else {            builder = RestClient.builder(new HttpHost(domain, port, scheme))                  .setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {                      @Override                      public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) {                      requestConfigBuilder.setConnectTimeout(-1);                          requestConfigBuilder.setSocketTimeout(-1);                          requestConfigBuilder.setConnectionRequestTimeout(-1);                          return requestConfigBuilder;                      }                  }).setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {                      @Override                      public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {                           httpClientBuilder.disableAuthCaching();                          return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);                      }                  });      }      client = new RestHighLevelClient(builder);      return client;  }}

‍其中Elasticsearch的配置信息如下:

代码语言:javascript
复制
es.scheme=httpses.port=9200es.domain=9.10.1.X  #es vipes.username=elastices.password=changemees.https.cert=/usr/local/services/certs/client-certificates.pem

‍通过如上配置后,即可在Spring项目中成功连接上开启了HTTPS协议的ES集群了。

扫码加入 Elasticsearch 技术社区👇

推荐阅读

关注腾讯云大数据公众号

邀您探索数据的无限可能

点击“阅读原文”,了解相关产品最新动态

↓↓↓