前言
在之前的一篇 集中式日志分析平台 - ELK Stack - 安全解决方案 X-Pack 里讲解了官方收费安全套件,官方套件很强大啊,但是完全收费。业界比较有名的一款开源产品 SearchGuard 却是部分收费,专注于 ES 的安全加固以及顺带做做 ELK 栈的 security,功能包括:
和认证和授权有关的:
- 支持基础 Http 认证;
- 支持 LDAP 和 AD,可以用于认证和授权的用户角色信息元数据集中式存储;(收费)
- 支持 Kerberos 双边认证;(收费)
- 支持 JWT;(收费)
- 支持代理认证,可以很方便的实现 SSO 系统对接;
ES 行(document)和列(field)级别的安全访问控制;
日志审计功能;(收费)
和 ELK 栈其他组件有关的:
- 和 Kibana 完美整合,包括添加了 x-pack 插件的情况下,支持 Kerberos 认证、代理认证、JWT、用户名/密码登录认证、Https 加密、对 ES 访问的 ACL 等,并且可以自定义登录首页样式;
- 支持 Kibana 的 visualization、dashboard 的多租户模式;(收费)
- 和 logstash 完美整合,支持 logstash 可以通过 Https 访问 ES;
- 和 x-pack monitoring 整合;
- 支持 Tribe nodes 模式,也就是说支持多个 ES 集群的级联查询;
- 支持 indices 的快照和恢复操作,默认只有 admin 用户具备权限;
- 可以和开源 ES 报警组件 ElastAlert 整合;
本文描述的是如何把关键性的几个免费功能整合到我们的 ELK 栈里去,涵盖了:
- 如何以脚本的形式 quickstart 整合 search guard 至 elasticsearch (Https & indices 粒度 ACL);
- 如何让 kibana 访问安全 elasticsearch 集群;
- 如何让 logstash 访问安全 elasticsearch 集群;
- 一个简单的链路调试例子;
Integration with ES(Https & indices 粒度 ACL)
假设我们已经有一个 ELK 集群(部署方式请参考我之前的文章),它的节点分布是这样的(版本统一为 5.2.1):
IP | Role |
---|---|
172.16.134.34 | ES/Logstash/Kibana |
172.16.134.37 | ES/Logstash/Zookeeper/Kafka/Filebeat |
服务的安装路径统一为 /home/admin/server/${SERVICE_NAME}
,日志的统一路径为 /home/admin/logs/${SERVICE_NAME}
。
互联网联通情况下的安装:
/home/admin/server/elasticsearch-current/bin/elasticsearch-plugin install -b com.floragunn:search-guard-5:5.2.1-12
请按照您自己的 es 版本进行版本选择:https://github.com/floragunncom/search-guard/wiki
离线环境下的安装:
在可以访问互联网的机器上下载 https://oss.sonatype.org/content/repositories/releases/com/floragunn/search-guard-5/5.2.1-12/search-guard-5-5.2.1-12.zip,然后上传至内网环境。
/home/admin/server/elasticsearch-current/bin/elasticsearch-plugin install -b file:///home/admin/soft/search-guard-5-5.2.1-12.zip
运行命令进行脚本式初始化:
cd /home/admin/server/elasticsearch-current/plugins/search-guard-5/tools
chmod +x install_demo_configuration.sh
./install_demo_configuration.sh
完成后会在 config
目录下生成测试用的 3 个 jks 文件:
-
truststore.jks
—根 CA; -
keystore.jks
—节点证书; -
kirk.jks
—运行sgadmin
必备的管理员证书;
完成后也会自动设置一些默认配置至 elasticsearch.yml
中:
######## Start Search Guard Demo Configuration ########
searchguard.ssl.transport.keystore_filepath: keystore.jks
searchguard.ssl.transport.truststore_filepath: truststore.jks
searchguard.ssl.transport.enforce_hostname_verification: false
searchguard.ssl.http.enabled: true
searchguard.ssl.http.keystore_filepath: keystore.jks
searchguard.ssl.http.truststore_filepath: truststore.jks
searchguard.authcz.admin_dn:
- CN=kirk,OU=client,O=client,L=test, C=de
cluster.name: searchguard_demo
network.host: 0.0.0.0
但是如果现在重新启动 es 可能会报错,因为原先的配置里涵盖了 cluster.name
network.host
等配置,报错可能如下:
Exception in thread "main" ElasticsearchParseException[duplicate settings key [cluster.name] found at line number [21], column number [15], previous value [lw-daily], current value [search
guard_demo]]
这里 lw-daily 是我们默认的集群名称。
我们需要删除相应报错配置项,然后重启 es:
######## Start Search Guard Demo Configuration ########
searchguard.ssl.transport.keystore_filepath: keystore.jks
searchguard.ssl.transport.truststore_filepath: truststore.jks
searchguard.ssl.transport.enforce_hostname_verification: false
searchguard.ssl.http.enabled: true
searchguard.ssl.http.keystore_filepath: keystore.jks
searchguard.ssl.http.truststore_filepath: truststore.jks
searchguard.authcz.admin_dn:
- CN=kirk,OU=client,O=client,L=test, C=de
network.host: 0.0.0.0
如果重启过程中还遇到如下报错说明 openssl 运行缺少 jar:
[2017-07-12T12:56:23,987][INFO ][c.f.s.s.DefaultSearchGuardKeyStore] Open SSL not available (this is not an error, we simply fallback to built-in JDK SSL) because of java.lang.ClassNotFoundException: org.apache.tomcat.jni.SSL
该情况需要去官方下载对应版本的 jar 包(https://github.com/floragunncom/search-guard-docs/blob/master/tls_openssl.md),比如我们的版本对应的是 http://repo1.maven.org/maven2/io/netty/netty-tcnative/1.1.33.Fork25,然后放置到 plugins/search-guard-5/
:
mv netty-tcnative-1.1.33.Fork25-linux-x86_64.jar /home/admin/server/elasticsearch-current/plugin/search-gurad-5/
修改 tools/sgadmin_demo.sh
,使其中一行匹配我们默认的集群名称:
/home/admin/server/elasticsearch-current/plugins/search-guard-5/tools/sgadmin.sh -cd /home/admin/server/elasticsearch-current/plugins/search-guard-5/sgconfig -cn lw-daily -ks /home/admin/server/elasticsearch-current/config/kirk.jks -ts /home/admin/server/elasticsearch-current/config/truststore.jks -nhnv
我们把 -cn 参数修改为了 lw-daily。
然后我们执行脚本:
./sgadmin_demo.sh
执行脚本之后会根据 plugins/search-guard-5-5.2.1-12/sgconfig
的配置初始化 search guard index,初始化的包括内部用户、角色、用户和角色的映射、权限组等等。
到这一步之后安装就算完成了,我们可以通过接口观察是否配置正确:
https://172.16.134.34:9200/_searchguard/authinfo
这里默认的用户名和密码可以选择 admin
,admin
,如果通过认证,那么就会返回如下结果:
{
user: "User [name=admin, roles=[]]",
user_name: "admin",
user_requested_tenant: null,
remote_address: "172.16.129.61:50317",
sg_roles: [
"sg_all_access",
"sg_own_index",
"sg_public"
],
sg_tenants: {
test_tenant_ro: true,
admin: true,
adm_tenant: true
},
principal: null,
peer_certificates: "0"
}
Integration with Kibana
ES 做了加固,原先的普通访问方式就歇菜了,首先是 Https 然后是 ACL,那么就会没通过认证和得到相应的权限访问资源。我们需要对 Kibana 做相应的配置:
下载 plugin:
https://github.com/floragunncom/search-guard-kibana-plugin/releases/download/v5.2.1-3/searchguard-kibana-5.2.1-3.zip
安装 plugin:
/home/admin/server/kibana-current/bin/kibana-plugin install file:///home/admin/soft/searchguard-kibana-5.2.1-3.zip
修改 kibana.yml
:
elasticsearch.url: "https://v134034.yn1.lwsite.net:9200"
server.host: "v134034.yn1.lwsite.net"
searchguard.basicauth.enabled: true
searchguard.cookie.secure: false
searchguard.cookie.name: 'searchguard_authentication'
searchguard.cookie.password: 'searchguard_cookie_default_password'
searchguard.cookie.ttl: 3600000
searchguard.session.ttl: 3600000
searchguard.session.keepalive: true
elasticsearch.username: "kibanaserver"
elasticsearch.password: "kibanaserver"
elasticsearch.ssl.verify: false
console.proxyConfig:
- match:
protocol: "https"
ssl:
verify: false
重启 kibana:
sudo kill -9 $(sudo lsof -i:5601 | awk '{print$2}' | tail -1 )
/home/admin/server/kibana-current/bin/kibana >> /home/admin/logs/kibana/kibana.log 2>&1 &
使用 kibanaserver 查看是否可以登录,密码默认为 kibanaserver。(该用户是在整合 searchguard 至 es 之后在运行 demo 脚本中生成的,同样的 logstash 用户也是)
Integration with Logstash
同样,我们需要修改 logstash ,以使其可以访问 es。以下是一个简单的示例配置,数据来自 kafka 的 stdin_test:
input {
kafka {
bootstrap_servers => 'v134037.yn1.lwsite.net:9092'
consumer_threads => 1
topics => ["stdin_test"]
type => "stdin_test"
codec => "json"
}
}
output {
elasticsearch {
hosts => ["v134034.yn1.lwsite.net:9200","v134037.yn1.lwsite.net:9200"]
manage_template => false
index => "stdin_test-%{+YYYY.MM.dd}"
document_type => "stdin_test"
ssl => true
ssl_certificate_verification => false
truststore => "/home/admin/server/elasticsearch-current/config/truststore.jks"
truststore_password => changeit
user => logstash
password => logstash
}
}
其中,/home/admin/server/elasticsearch-current/config/truststore.jk
s 也是由之前的 demo 脚本生成的。
重启 logstash 之后可以正确访问。
即使 ssl_certificate_verification 设置为 false,也必须配置 truststore ,否则会报错:
logstash unable to find valid certification path to requested target,参考 issue 链接:https://github.com/logstash-plugins/logstash-output-http/issues/21,我们必须保证 truststore 文件存在可读。
一个简单的例子
上一节我们介绍了如何整合 logstash 至 searchguard,现在我们要在 stdin_test 这个 topic 里面造些数据进行集成测试,我们当然用 filebeat 推送数据至 kafka,filebeat 部署在 172.16.134.37,配置如下:
首先是主配置文件 /home/admin/server/filebeat-current/filebeat.yml
:
filebeat:
config_dir: /home/admin/server/filebeat-current/conf.d
output.kafka:
hosts: ["v134037.yn1.lwsite.net:9092"]
topic: '%{[type]}'
partition.round_robin:
reachable_only: false
required_acks: 1
compression: gzip
max_message_bytes: 10485760
然后是对应的采集 stdin_test 的配置文件 /home/admin/server/filebeat-current/conf.d/stdin_test.yml
:
filebeat.prospectors:
- input_type: log
paths:
- /tmp/stdin_test.log
document_type: stdin_test
OK,接下来我们灌简单数据至文件:
echo "1" >> /tmp/stdin_test.log
在 Kibana 添加 index 的时候发现无法获取,排查了一下可以得知 Kafka 是有 topic 新建并且有数据的:
/home/admin/server/kafka-current/bin/kafka-topics.sh --list --zookeeper 172.16.134.37:2181
/home/admin/server/kafka-current/bin/kafka-console-consumer.sh --zookeeper 127.0.0.1:2181 --topic stdin_test --from-beginning
然后排查下 logstash DEBUG 日志:
[2017-07-13T15:06:14,376][WARN ][logstash.outputs.elasticsearch] Failed action. {:status=>403, :action=>["index", {:_id=>nil, :_index=>"stdin_test-2017.07.13", :_type=>"stdin_test", :_routing=>nil}, 2017-07-13T07:06:14.280Z %{host} {
"@timestamp":"2017-07-13T07:06:13.054Z","beat":{"hostname":"v134037.yn1","name":"v134037.yn1","version":"5.2.1"},"input_type":"log","message":"1","offset":6,"source":"/tmp/stdin_test.log","type":"stdin_test"}], :response=>{"index"=>{
"_index"=>"stdin_test-2017.07.13", "_type"=>"stdin_test", "_id"=>nil, "status"=>403, "error"=>{"type"=>"security_exception", "reason"=>"no permissions for indices:admin/create"}}}}
注意最后的报错 no permissions for indices:admin/create
,可以得知 logstash 没有对应权限进行 indices 新建。按照官方的建议,对 searchguard 的配置文件进行修改 /home/admin/server/elasticsearch-5.2.1/plugins/search-guard-5/sgconfig/sg_roles.yml
,注意只修改 sg_logstash
这个角色的配置:
...
sg_logstash:
cluster:
- indices:admin/template/get
- indices:admin/template/put
- CLUSTER_MONITOR
- CLUSTER_COMPOSITE_OPS
indices:
'logstash-*':
'*':
- CRUD
- CREATE_INDEX
'stdin_test-*': # 修改这里,添加对应权限
'*':
- CRUD
- CREATE_INDEX
'*beat*':
'*':
- CRUD
- CREATE_INDEX
...
发现修改后重启没有卵用,看到了官方的建议说需要用脚本刷下配置:
/home/admin/server/elasticsearch-current/plugins/search-guard-5/tools/sgadmin.sh -cd /home/admin/server/elasticsearch-current/plugins/search-guard-5/sgconfig -cn lw-daily -ks /home/admin/server/elasticsearch-current/config/kirk.jks -ts /home/admin/server/elasticsearch-current/config/truststore.jks -nhnv
官方说只要一个节点刷就可以全局生效,但是我发现不行,所以我是全局所有节点都进行了这个操作的。重启试下是否数据可以被 Kibana 识别,发现还是不行,这个时候看了下 Kibana 的日志发现如下报错:
[2017-07-13T15:49:28,591][INFO ][c.f.s.c.PrivilegesEvaluator] No index-level perm match for User [name=kibanaserver, roles=[]] [IndexType [index=stdin_test-2017.07.13, type=*]] [Action [indices:admin/mappings/fields/get]] [RolesCheck
ed [sg_kibana_server, sg_own_index, sg_public]]
[2017-07-13T15:49:28,591][INFO ][c.f.s.c.PrivilegesEvaluator] No permissions for {sg_public=[IndexType [index=stdin_test-2017.07.13, type=*]], sg_own_index=[IndexType [index=stdin_test-2017.07.13, type=*]], sg_kibana_server=[IndexTyp
e [index=stdin_test-2017.07.13, type=*]]}
也是权限的问题,按照和之前一样的方式修改权限:
...
sg_kibana:
cluster:
- CLUSTER_COMPOSITE_OPS_RO
indices:
'*':
'*':
- READ
- indices:admin/mappings/fields/get*
'?kibana':
'*':
- ALL
...
并且刷新后,重启 Kibana,以 kibanaro 角色登录(默认密码 kibanaro),再灌入测试数据至日志文件后,就可以正确获取 indices 数据。
小结
本文介绍了如何使用 searchguard 对 ES 进行 https 和 indices 粒度 ACL 的安全赋能,以及一个短小的例子。下一篇我将写一下如何整合 Kerberos 双边认证进行进一步加固,以及如何使用 restful API 对 ES 进行访问。
参考文献
- http://floragunncom.github.io/search-guard-docs/installation.html
- http://floragunncom.github.io/search-guard-docs/quickstart.html
- http://floragunncom.github.io/search-guard-docs/kibana.html
- http://floragunncom.github.io/search-guard-docs/logstash.html
- http://floragunncom.github.io/search-guard-docs/sgadmin.html