前言上文宜人贷蜂巢API网关技术解密之Netty使用实践提到了,API网关“承外对内”,将外部请求,转发到内部各个抓取服务。在网关中,不仅可以做鉴权、加解密、路由、限流功能;如果想了解各服务接口的调用次数、调用时间、成功次数、失败次数等接口相关的统计,都可以网关中埋点,采集相关信息,通过logstash放入elasticsearch(简称es),在kibana中或通过es的http请求获得各服务接口的统计信息。ELK框架前段时间有一个需求,我们的es需要作为一个服务提供出来。考虑到es内保存着敏感数据,需要对es添加权限管理功能。搜寻es权限管理,大概有三套方案:ELK Stack官方安全插件X-Pack Securites安全插件Search Guard自实现权限管理考虑到X-Pack Security是收费软件,自开发一个权限管理模块成本比较高,最后选用了Search Guard的社区版本,其支持的功能已经满足当前需求。Search Guard安装过程Stop Elasticsearchps -ef | grep elasticsearch | grep -v grep | awk {‘print $2’} | xargs kill -9Install Search Guard/opt/soft/elasticsearch-5.5.1-guard/bin/elasticsearch-plugin install -b com.floragunn:search-guard-5:5.5.1-14如果线上服务器不能访问外网,可以事先下载对应版本的 Search Guard,再以本地模式安装/opt/soft/elasticsearch-5.5.1-guard/bin/elasticsearch-plugin install -b file:///opt/soft/search-guard-5-5.5.1-15.zip产生证书及配置es如果在测试环境安装es,可以使用软件自带的脚本,生成CA及相应证书,并自动配置了essh /opt/soft/elasticsearch-5.5.1-guard/plugins/search-guard-5/tools/install_demo_configuration.sh但以上方式不适用于线上环境,我们的es希望在线上环境能提供权限访问控制,所以采用了search-guard-ssl生成线上证书首先,下载了search-guard-ssl项目git clone https://github.com/floragunncom/search-guard-ssl.git生成证书,可以参考官方文档详细说明建议修改证书信息及密码example-pki-scripts/etc/root-ca.confexample-pki-scripts/etc/signing-ca.conf执行证书生成脚本sh ./example.sh将相应的证书拷贝到es的config文件夹下修改es配置cluster.name: honeycomb-es-guard-5.5.1# node 1 to 5node.name: node-1 path.data: /data01/elasticsearch-guardpath.logs: /opt/logs/elasticsearch-guardnetwork.host: 0.0.0.0http.cors.enabled: truehttp.cors.allow-origin: ““thread_pool.bulk.queue_size: 50searchguard.ssl.transport.keystore_filepath: node-1-keystore.jkssearchguard.ssl.transport.keystore_password: node_essearchguard.ssl.transport.truststore_filepath: truststore.jkssearchguard.ssl.transport.truststore_password: r_ca_honeycombsearchguard.ssl.transport.enforce_hostname_verification: falsesearchguard.ssl.transport.enable_openssl_if_available: truesearchguard.ssl.transport.resolve_hostname: falsesearchguard.ssl.http.enabled: truesearchguard.ssl.http.keystore_filepath: node-1-keystore.jkssearchguard.ssl.http.keystore_password: node_essearchguard.ssl.http.truststore_filepath: truststore.jkssearchguard.ssl.http.truststore_password: r_ca_honeycombsearchguard.authcz.admin_dn: - CN=,OU=client,O=client,L=test, C=de - CN=kirk,OU=client,O=client,L=test,C=DEsearchguard.ssl.http.clientauth_mode: NONERestart Elasticsearch.su elk/opt/soft/elasticsearch-5.5.1-guard/bin/elasticsearch -dInitialise the Search Guard index by running sgadmin <span style=“font-family:monospace;font-size:14px;">/opt/soft/elasticsearch-5.5.1-guard/plugins/search-guard-5/tools/sgadmin.sh -cd /opt/soft/elasticsearch-5.5.1-guard/plugins/search-guard-5/sgconfig/ -cn honeycomb-es-guard-5.5.1 -ks /opt/soft/elasticsearch-5.5.1-guard/config/kirk-keystore.jks -kspass ki_ca_1 -ts /opt/soft/elasticsearch-5.5.1-guard/config/truststore.jks -tspass r_ca_honeycomb –nhnv</span>测试结果curl -k -u admin:admin ‘https://localhost:9200/_searchguard/authinfo?pretty’es装上Search Guard后的ELK框架kibana修改步骤安装插件/opt/soft/kibana-5.5.1-linux-x86_64-guard/bin/kibana-plugin install file:///opt/soft/searchguard-kibana-5.5.1-4.zipkibana配置server.port: 5601server.host: “0.0.0.0"elasticsearch.url: “https://localhost:9200"kibana.index: “.kibana"elasticsearch.username: “kibanaserver"elasticsearch.password: “kibanaserver"elasticsearch.ssl.verificationMode: nonestart kibana/opt/soft/kibana-5.5.1-linux-x86_64-guard/bin/kibana &访问页面logstash修改步骤logstash在按照官方文档配置,且咨询Search Guard官方后,并没有安装成功;现列出操作过程及问题现象,如有哪位读者了解,还望不吝赐教Configure logstash to use HTTPS instead of HTTP.例如,hosts => “https://…“If you want to verify the server’s certificate (optional, but recommended), you need to provide the path to the keystore containing your Root CA as well.logstash不需要验证es的证书,所以设置ssl_certificate_verification => false如果进行证书验证,需配置CA路径及密码Configure the logstash username and password.最终配置output { elasticsearch { hosts => [“https://xx.xx.xx.32:9200”] index => “%{log_project}-%{+YYYY-MM-dd}” ssl => true ssl_certificate_verification => false #truststore => “/opt/soft/elasticsearch-5.5.1-guard/config/truststore.jks” #truststore_password => r_ca_honeycomb user => logstash password => logstash }}start logstash/opt/soft/logstash-2.4.1-guard/bin/logstash -f /opt/soft/logstash-2.4.1-guard/conf/honeycomb-logstash.conf问题1. Search Guard 建议logstash不做证书验证且不需要提供truststore和truststore_password,但按操作后,进程启动成功,但仍有fail提示** WARNING ** Detected UNSAFE options in elasticsearch output configuration!** WARNING ** You have enabled encryption but DISABLED certificate verification.** WARNING ** To make sure your data is secure change :ssl_certificate_verification to true {:level=>:warn}PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target {:class=>“Manticore::ClientProtocolException”, :level=>:error}Pipeline main started2. logstash在启动后,发送数据给es时,始终报错Attempted to send a bulk request to Elasticsearch configured at ‘[“https://xx.xx.xx.32:9200”]’, but an error occurred and it failed! Are you sure you can reach elasticsearch from this machine using the configuration provided? {:error_message=>“PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target”, :error_class=>“Manticore::ClientProtocolException”, :backtrace=>["/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/manticore-0.6.0-java/lib/manticore/response.rb:37:in initialize'", "org/jruby/RubyProc.java:281:in
call’”, “/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/manticore-0.6.0-java/lib/manticore/response.rb:79:in call'", "/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/manticore-0.6.0-java/lib/manticore/response.rb:256:in
call_once’”,"/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/manticore-0.6.0-java/lib/manticore/response.rb:153:in code'", "/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/elasticsearch-transport-1.1.0/lib/elasticsearch/transport/transport/http/manticore.rb:84:in
perform_request’”,“org/jruby/RubyProc.java:281:in call'", "/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/elasticsearch-transport-1.1.0/lib/elasticsearch/transport/transport/base.rb:257:in
perform_request’”,"/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/elasticsearch-transport-1.1.0/lib/elasticsearch/transport/transport/http/manticore.rb:67:in perform_request'", "/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/elasticsearch-transport-1.1.0/lib/elasticsearch/transport/client.rb:128:in
perform_request’”, “/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/elasticsearch-api-1.1.0/lib/elasticsearch/api/actions/bulk.rb:93:in bulk'", "/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/logstash-output-elasticsearch-2.7.1-java/lib/logstash/outputs/elasticsearch/http_client.rb:53:in
non_threadsafe_bulk’”, “/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/logstash-output-elasticsearch-2.7.1-java/lib/logstash/outputs/elasticsearch/http_client.rb:38:in bulk'", "org/jruby/ext/thread/Mutex.java:149:in
synchronize’”, “/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/logstash-output-elasticsearch-2.7.1-java/lib/logstash/outputs/elasticsearch/http_client.rb:38:in bulk'", "/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/logstash-output-elasticsearch-2.7.1-java/lib/logstash/outputs/elasticsearch/common.rb:172:in
safe_bulk’”, “/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/logstash-output-elasticsearch-2.7.1-java/lib/logstash/outputs/elasticsearch/common.rb:101:in submit'", "/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/logstash-output-elasticsearch-2.7.1-java/lib/logstash/outputs/elasticsearch/common.rb:86:in
retrying_submit’”, “/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/logstash-output-elasticsearch-2.7.1-java/lib/logstash/outputs/elasticsearch/common.rb:29:in multi_receive'", "org/jruby/RubyArray.java:1653:in
each_slice’”, “/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/logstash-output-elasticsearch-2.7.1-java/lib/logstash/outputs/elasticsearch/common.rb:28:in multi_receive'", "/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/logstash-core-2.4.1-java/lib/logstash/output_delegator.rb:130:in
worker_multi_receive’”, “/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/logstash-core-2.4.1-java/lib/logstash/output_delegator.rb:114:in multi_receive'", "/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/logstash-core-2.4.1-java/lib/logstash/pipeline.rb:301:in
output_batch’”, “org/jruby/RubyHash.java:1342:in each'", "/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/logstash-core-2.4.1-java/lib/logstash/pipeline.rb:301:in
output_batch’”, “/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/logstash-core-2.4.1-java/lib/logstash/pipeline.rb:232:in worker_loop'", "/opt/soft/logstash-2.4.1-guard/vendor/bundle/jruby/1.9/gems/logstash-core-2.4.1-java/lib/logstash/pipeline.rb:201:in
start_workers’”], :level=>:error}看日志似乎是目的不可达,但在logstash机器上,通过命令测试,是可以访问的curl -k -u logstash:logstash ‘https://xx.xx.xx.32:9200/_searchguard/authinfo?pretty’另外es有打印错误日志[2017-08-24T10:39:20,834][ERROR][c.f.s.h.SearchGuardHttpServerTransport] [node-2] SSL Problem Received fatal alert: certificate_unknownjavax.net.ssl.SSLException: Received fatal alert: certificate_unknown at sun.security.ssl.Alerts.getSSLException(Alerts.java:208) ~[?:?] at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1666) ~[?:?] at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1634) ~[?:?] at sun.security.ssl.SSLEngineImpl.recvAlert(SSLEngineImpl.java:1800) ~[?:?] at sun.security.ssl.SSLEngineImpl.readRecord(SSLEngineImpl.java:1083) ~[?:?] at sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:907) ~[?:?] at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:781) ~[?:?] at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624) ~[?:1.8.0_131] at io.netty.handler.ssl.SslHandler$SslEngineType$3.unwrap(SslHandler.java:254) ~[netty-handler-4.1.11.Final.jar:4.1.11.Final] at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1156) ~[netty-handler-4.1.11.Final.jar:4.1.11.Final] at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1078) ~[netty-handler-4.1.11.Final.jar:4.1.11.Final] at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:489) ~[netty-codec-4.1.11.Final.jar:4.1.11.Final] at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:428) ~[netty-codec-4.1.11.Final.jar:4.1.11.Final] at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:265) ~[netty-codec-4.1.11.Final.jar:4.1.11.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) [netty-transport-4.1.11.Final.jar:4.1.11.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) [netty-transport-4.1.11.Final.jar:4.1.11.Final] at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) [netty-transport-4.1.11.Final.jar:4.1.11.Final] at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334) [netty-transport-4.1.11.Final.jar:4.1.11.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) [netty-transport-4.1.11.Final.jar:4.1.11.Final] at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) [netty-transport-4.1.11.Final.jar:4.1.11.Final] at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926) [netty-transport-4.1.11.Final.jar:4.1.11.Final] at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:134) [netty-transport-4.1.11.Final.jar:4.1.11.Final] at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:644) [netty-transport-4.1.11.Final.jar:4.1.11.Final] at io.netty.channel.nio.NioEventLoop.processSelectedKeysPlain(NioEventLoop.java:544) [netty-transport-4.1.11.Final.jar:4.1.11.Final] at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:498) [netty-transport-4.1.11.Final.jar:4.1.11.Final] at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:458) [netty-transport-4.1.11.Final.jar:4.1.11.Final] at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858) [netty-common-4.1.11.Final.jar:4.1.11.Final] at java.lang.Thread.run(Thread.java:748) [?:1.8.0_131][2017-08-24T10:39:21,663][ERROR][c.f.s.h.SearchGuardHttpServerTransport] [node-2] SSL Problem Received fatal alert: certificate_unknownjavax.net.ssl.SSLException: Received fatal alert: certificate_unknown at sun.security.ssl.Alerts.getSSLException(Alerts.java:208) ~[?:?] at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1666) ~[?:?] at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1634) ~[?:?] at sun.security.ssl.SSLEngineImpl.recvAlert(SSLEngineImpl.java:1800) ~[?:?] at sun.security.ssl.SSLEngineImpl.readRecord(SSLEngineImpl.java:1083) ~[?:?] at sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:907) ~[?:?] at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:781) ~[?:?] at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624) ~[?:1.8.0_131] at io.netty.handler.ssl.SslHandler$SslEngineType$3.unwrap(SslHandler.java:254) ~[netty-handler-4.1.11.Final.jar:4.1.11.Final] at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1156) ~[netty-handler-4.1.11.Final.jar:4.1.11.Final] at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1078) ~[netty-handler-4.1.11.Final.jar:4.1.11.Final] at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:489) ~[netty-codec-4.1.11.Final.jar:4.1.11.Final] at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:428) ~[netty-codec-4.1.11.Final.jar:4.1.11.Final]3、上文es的错误日志,显示es不知道证书(应该是客户端证书),但是已经配置了不做客户端认证,这又是为什么?es部分配置searchguard.ssl.http.clientauth_mode: NONE总结虽然logstash与es通信暂时没有成功,但是在尝试开通es权限的过程中,也积累了些经验和成果使用类似文中curl的方式(http请求),直接使用es服务添加了Search Guard的es如何对外提供域名访问,实现后端多点服务/路由nginx可以处理来自外部的https请求,但目前不支持https代理,即nginx不能访问后端的https服务。如果仍然使用Search Guard,可以考虑使用四层负载均衡器lvs或者硬件负载均衡器,将请求透传给后端服务器;另外知乎文章上有推荐使用Squid。参考https://floragunn.com/https://github.com/floragunnc…https://github.com/floragunnc...https://github.com/floragunnc...https://github.com/floragunnc...https://github.com/floragunnc...https://github.com/floragunnc...https://forum.nginx.org/read....https://www.zhihu.com/questio…作者:蜂巢团队宜信技术学院