这篇文章是https://www.elastic.co/blog/playing-http-tricks-nginx的部分翻译,对像我这样的小白学习nginx和elasticsearch有些许帮助,ps:翻译好难。。。e文好的同学还是去看原文吧。。。
Elasticsearch默认是完全暴露在互联网上的RESTful服务接口。 这样做的好处是:web开发人员可以很快熟悉它的API,很容易使用curl命令或者浏览器来查询数据。也非常容易使用各种编程语言封装对应的接口。 Elasticsearch的RESTful接口是基于http开发的,所以它与现有的网络架构兼容性很好。
现在很多软件使用RESTful服务作为基础架构,这样做的一大好处是你可以很容易插入新功能和修改已有功能。
Nginx是一个高性能的反向代理服务。许多大型PHP项目也会使用nginx为静态文件加速。这篇文章使用基础的nginx配置为elasticsearch加上验证和授权功能。 运行Nginx为Elasticsearch做一个端口映射,我们可以非常简单的这样配置:
http {
server {
listen 8080;
location / {
proxy_pass http://localhost:9200;
}
}
}
当访问http://localhost:8080的时候,相当于访问http://localhost:9200
这种配置当然没什么大用,聪明的读者会发现了nginx其实已经做了些工作,比如记录了每次请求的日志。
使用nginx保持elasticsearch连接。这么做可以减少elasticsearch因每次请求都需要连接~断开的压力,节省资源。
更多配置请参考https://gist.github.com/karmi/b0a9b4c111ed3023a52d
events {
worker_connections 1024;
}
http {
upstream elasticsearch {
server 127.0.0.1:9200;
keepalive 15;
}
server {
listen 8080;
location / {
proxy_pass http://elasticsearch;
proxy_http_version 1.1;
proxy_set_header Connection "Keep-Alive";
proxy_set_header Proxy-Connection "Keep-Alive";
}
}
}
启动nginx
$ nginx -p $PWD/nginx/ -c $PWD/nginx_keep_alive.conf
没有持久化连接的时候,每次请求elasticsearch数据opened connections都会增加:
$ curl 'localhost:9200/_nodes/stats/http?pretty' | grep total_opened
# "total_opened" : 13
$ curl 'localhost:9200/_nodes/stats/http?pretty' | grep total_opened
# "total_opened" : 14
# ...
使用nginx持久化连接后,opened connections每回都一样:
$ curl 'localhost:8080/_nodes/stats/http?pretty' | grep total_opened
# "total_opened" : 15
$ curl 'localhost:9200/_nodes/stats/http?pretty' | grep total_opened
# "total_opened" : 15
# ...
对这个配置做一个非常小的改动,我们就可以分配requests到不同的elasticsearch节点上:
events {
worker_connections 1024;
}
http {
upstream elasticsearch {
server 127.0.0.1:9200;
server 127.0.0.1:9201;
server 127.0.0.1:9202;
keepalive 15;
}
server {
listen 8080;
location / {
proxy_pass http://elasticsearch;
proxy_http_version 1.1;
proxy_set_header Connection "Keep-Alive";
proxy_set_header Proxy-Connection "Keep-Alive";
}
}
}
这里我们添加了两个节点在upstream中。Nginx会自动分配(轮询)request到不同的节点中。
$ curl localhost:8080 | grep name
# "name" : "Silver Fox",
$ curl localhost:8080 | grep name
# "name" : "G-Force",
$ curl localhost:8080 | grep name
# "name" : "Helleyes",
$ curl localhost:8080 | grep name
# "name" : "Silver Fox",
# ...
这么做可以将请求分散给所有节点,增加节点只需在upstream中增加url,然后重启nginx
关于更高级的负载均衡功能,请参考Load Balancing with NGINX and NGINX Plus
(注意,elasticsearch本身有负载均衡机制。)
默认情况下,elasticsearch不采取认证机制,因为它不是设计在开放的网络环境当中,当你打开9200端口,你的数据,集群都不是安全的。
通常保护elasticsearch集群是通过VPN,防火墙等手段限制。但是如果你希望在外网连接elasticsearch集群,就只能通过用户密码来加以认证了。
可以使用nginx来实现用户认证:
events {
worker_connections 1024;
}
http {
upstream elasticsearch {
server 127.0.0.1:9200;
}
server {
listen 8080;
auth_basic "Protected Elasticsearch";
auth_basic_user_file passwords;
location / {
proxy_pass http://elasticsearch;
proxy_redirect off;
}
}
}
可以使用很多工具来生成密码,这里使用openssl:
$ printf "john:$(openssl passwd -crypt s3cr3t)\n" > passwords
运行nginx:
$ nginx -p $PWD/nginx/ -c $PWD/nginx_http_auth_basic.conf
普通请求会被拒绝:
$ curl -i localhost:8080
# HTTP/1.1 401 Unauthorized
# ...
输入帐号密码后,就可以建立连接了:
$ curl -i john:s3cr3t@localhost:8080
# HTTP/1.1 200 OK
# ...
现在可以关闭9200端口,只留8080端口对外开放了,想要连接集群必须得通过验证。
如果没有授权机制,只要登录,就可以对集群中任何事情,更改或删除数据,查看内部数据,甚至关闭集群。
通过非常小的改动,我们就可以拒绝通过RESTful命令关闭集群。
location / {
if ($request_filename ~ _shutdown) {
return 403;
break;
}
proxy_pass http://elasticsearch;
proxy_redirect off;
}
重启nginx:
$ nginx -p $PWD/nginx/ -c $PWD/nginx_http_auth_deny_path.conf
尝试调用关闭集群,将会返回拒绝信息:
$ curl -i -X POST john:s3cr3t@localhost:8080/_cluster/nodes/_shutdown
# HTTP/1.1 403 Forbidden
# ....
也可以配置允许某些请求,拒绝其余的请求,通过两个location来实现:
events {
worker_connections 1024;
}
http {
upstream elasticsearch {
server 127.0.0.1:9200;
}
server {
listen 8080;
auth_basic "Protected Elasticsearch";
auth_basic_user_file passwords;
location ~* ^(/_cluster|/_nodes) {
proxy_pass http://elasticsearch;
proxy_redirect off;
}
location / {
return 403;
break;
}
}
}
这样,请求/_cluster 和 /_nodes将通过,其他的会被拒绝:
$ curl -i john:s3cr3t@localhost:8080/
HTTP/1.1 403 Forbidden
# ...
$ curl -i john:s3cr3t@localhost:8080/_cluster/health
# HTTP/1.1 200 OK
# ...
$ curl -i john:s3cr3t@localhost:8080/_nodes/stats
HTTP/1.1 200 OK
# ...
现在我们想用基本认证保护elasticsearch但是允许ping命令发送HEAD请求到/起到监视集群状态的作用。
events {
worker_connections 1024;
}
http {
upstream elasticsearch {
server 127.0.0.1:9200;
}
server {
listen 8080;
location / {
error_page 590 = @elasticsearch;
error_page 595 = @protected_elasticsearch;
set $ok 0;
if ($request_uri ~ ^/$) {
set $ok "${ok}1";
}
if ($request_method = HEAD) {
set $ok "${ok}2";
}
if ($ok = 012) {
return 590;
}
return 595;
}
location @elasticsearch {
proxy_pass http://elasticsearch;
proxy_redirect off;
}
location @protected_elasticsearch {
auth_basic "Protected Elasticsearch";
auth_basic_user_file passwords;
proxy_pass http://elasticsearch;
proxy_redirect off;
}
}
}
首先,我们定义两个状态代码:590-不需要验证,595-需要验证,使用nginx的location功能,两个location都指向同一个集群,但是其中一个需要认证。
然后我们设置一个变量$ok,默认值0,当进入请求是/,$ok变成01.如果请求是HEAD$ok变成012。
如果$ok是012,返回590状态码,换句话说,不需验证,其他情况返回595,需要验证。
$ curl -i -X HEAD localhost:8080
# HTTP/1.1 200 OK
# ...
$ curl -i localhost:8080
# HTTP/1.1 401 Unauthorized
# ...
$ curl -i john:s3cr3t@localhost:8080
# HTTP/1.1 200 OK
# ...
到这里,我们已经有了一个简单的认证机制。如果我们需要一个更宽泛的认证机制,基于角色的,不如说像下面这样的:
events {
worker_connections 1024;
}
http {
upstream elasticsearch {
server 127.0.0.1:9200;
}
# Allow HEAD / for all
#
server {
listen 8080;
location / {
return 401;
}
location = / {
if ($request_method !~ "HEAD") {
return 403;
break;
}
proxy_pass http://elasticsearch;
proxy_redirect off;
}
}
# Allow access to /_search and /_analyze for authenticated "users"
#
server {
listen 8081;
auth_basic "Elasticsearch Users";
auth_basic_user_file users;
location / {
return 403;
}
location ~* ^(/_search|/_analyze) {
proxy_pass http://elasticsearch;
proxy_redirect off;
}
}
# Allow access to anything for authenticated "admins"
#
server {
listen 8082;
auth_basic "Elasticsearch Admins";
auth_basic_user_file admins;
location / {
proxy_pass http://elasticsearch;
proxy_redirect off;
}
}
}
点用openssl命令生成认证文件:
$ printf "user:$(openssl passwd -crypt user)\n" > users
$ printf "admin:$(openssl passwd -crypt admin)\n" > admins
现在,所有用户都能ping集群,但是做不了其他操作:
$ curl -i -X HEAD localhost:8080
# HTTP/1.1 200 OK
$ curl -i -X GET localhost:8080
# HTTP/1.1 403 Forbidden
user用户可以执行search,analyze请求,其他的不可以:
$ curl -i localhost:8081/_search
# HTTP/1.1 401 Unauthorized
# ...
$ curl -i user:user@localhost:8081/_search
# HTTP/1.1 200 OK
# ...
$ curl -i user:user@localhost:8081/_analyze?text=Test
# HTTP/1.1 200 OK
# ...
$ curl -i user:user@localhost:8081/_cluster/health
# HTTP/1.1 403 Forbidden
# ...
admin用户可以做任何操作:
$ curl -i admin:admin@localhost:8082/_search
# HTTP/1.1 200 OK
# ...
$ curl -i admin:admin@localhost:8082/_cluster/health
# HTTP/1.1 200 OK
# ...
这样轻松解决了基于多角色的授权,这么做的代价是:每一种角色都使用不同的端口。