17

我已将我的 elasticsearch 服务器放在提供基本身份验证的 Apache 反向代理后面。

直接从浏览器向 Apache 进行身份验证工作正常。但是,当我使用 Kibana 3 访问服务器时,我收到身份验证错误。

显然是因为没有身份验证标头与 Kibana 的 Ajax 调用一起发送。

我在 Kibana 供应商目录中的 elastic-angular-client.js 中添加了以下内容,以实现快速而肮脏的身份验证。但由于某种原因,它不起作用。

$http.defaults.headers.common.Authorization = 'Basic ' + Base64Encode('user:Password');

在 Kibana 中实施基本身份验证的最佳方法和位置是什么?

/*! elastic.js - v1.1.1 - 2013-05-24
 * https://github.com/fullscale/elastic.js
 * Copyright (c) 2013 FullScale Labs, LLC; Licensed MIT */

/*jshint browser:true */
/*global angular:true */
'use strict';

/* 
Angular.js service wrapping the elastic.js API. This module can simply
be injected into your angular controllers. 
*/
angular.module('elasticjs.service', [])
  .factory('ejsResource', ['$http', function ($http) {

  return function (config) {
    var

      // use existing ejs object if it exists
      ejs = window.ejs || {},

      /* results are returned as a promise */
      promiseThen = function (httpPromise, successcb, errorcb) {
        return httpPromise.then(function (response) {
          (successcb || angular.noop)(response.data);
          return response.data;
        }, function (response) {
          (errorcb || angular.noop)(response.data);
          return response.data;
        });
      };

    // check if we have a config object
    // if not, we have the server url so
    // we convert it to a config object
    if (config !== Object(config)) {
      config = {server: config};
    }

    // set url to empty string if it was not specified
    if (config.server == null) {
      config.server = '';
    }

    /* implement the elastic.js client interface for angular */
    ejs.client = {
      server: function (s) {
        if (s == null) {
          return config.server;
        }

        config.server = s;
        return this;
      },
      post: function (path, data, successcb, errorcb) {
        $http.defaults.headers.common.Authorization = 'Basic ' + Base64Encode('user:Password');
        console.log($http.defaults.headers);
        path = config.server + path;
        var reqConfig = {url: path, data: data, method: 'POST'};
        return promiseThen($http(angular.extend(reqConfig, config)), successcb, errorcb);
      },
      get: function (path, data, successcb, errorcb) {
        $http.defaults.headers.common.Authorization = 'Basic ' + Base64Encode('user:Password');
        path = config.server + path;
        // no body on get request, data will be request params
        var reqConfig = {url: path, params: data, method: 'GET'};
        return promiseThen($http(angular.extend(reqConfig, config)), successcb, errorcb);
      },
      put: function (path, data, successcb, errorcb) {
        $http.defaults.headers.common.Authorization = 'Basic ' + Base64Encode('user:Password');
        path = config.server + path;
        var reqConfig = {url: path, data: data, method: 'PUT'};
        return promiseThen($http(angular.extend(reqConfig, config)), successcb, errorcb);
      },
      del: function (path, data, successcb, errorcb) {
        $http.defaults.headers.common.Authorization = 'Basic ' + Base64Encode('user:Password');
        path = config.server + path;
        var reqConfig = {url: path, data: data, method: 'DELETE'};
        return promiseThen($http(angular.extend(reqConfig, config)), successcb, errorcb);
      },
      head: function (path, data, successcb, errorcb) {
        $http.defaults.headers.common.Authorization = 'Basic ' + Base64Encode('user:Password');
        path = config.server + path;
        // no body on HEAD request, data will be request params
        var reqConfig = {url: path, params: data, method: 'HEAD'};
        return $http(angular.extend(reqConfig, config))
          .then(function (response) {
          (successcb || angular.noop)(response.headers());
          return response.headers();
        }, function (response) {
          (errorcb || angular.noop)(undefined);
          return undefined;
        });
      }
    };

    return ejs;
  };
}]);

更新 1:我实施了 Matts 的建议。但是,服务器返回一个奇怪的响应。似乎授权标头不起作用。这可能与我在端口 81 上运行 Kibana 而在 8181 上运行弹性搜索这一事实有关吗?

OPTIONS /solar_vendor/_search HTTP/1.1
Host: 46.252.46.173:8181
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: de-de,de;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Origin: http://46.252.46.173:81
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization,content-type
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache

这是回应

HTTP/1.1 401 Authorization Required
Date: Fri, 08 Nov 2013 23:47:02 GMT
WWW-Authenticate: Basic realm="Username/Password"
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 346
Connection: close
Content-Type: text/html; charset=iso-8859-1

更新 2:在这些 Kibana 文件中使用修改后的标头更新了所有实例

root@localhost:/var/www/kibana# grep -r 'ejsResource(' .

./src/app/controllers/dash.js:      $scope.ejs = ejsResource({server: config.elasticsearch, headers: {'Access-Control-Request-Headers': 'Accept, Origin, Authorization', 'Authorization': 'Basic XXXXXXXXXXXXXXXXXXXXXXXXXXXXX=='}});
./src/app/services/querySrv.js:    var ejs = ejsResource({server: config.elasticsearch, headers: {'Access-Control-Request-Headers': 'Accept, Origin, Authorization', 'Authorization': 'Basic XXXXXXXXXXXXXXXXXXXXXXXXXXXXX=='}});
./src/app/services/filterSrv.js:    var ejs = ejsResource({server: config.elasticsearch, headers: {'Access-Control-Request-Headers': 'Accept, Origin, Authorization', 'Authorization': 'Basic XXXXXXXXXXXXXXXXXXXXXXXXXXXXX=='}});
./src/app/services/dashboard.js:    var ejs = ejsResource({server: config.elasticsearch, headers: {'Access-Control-Request-Headers': 'Accept, Origin, Authorization', 'Authorization': 'Basic XXXXXXXXXXXXXXXXXXXXXXXXXXXXX=='}});

并像这样为反向代理修改了我的 vhost conf

<VirtualHost *:8181>

ProxyRequests Off
ProxyPass / http://127.0.0.1:9200/
ProxyPassReverse / https://127.0.0.1:9200/

    <Location />
        Order deny,allow
        Allow from all
        AuthType Basic
        AuthName “Username/Password”
        AuthUserFile /var/www/cake2.2.4/.htpasswd
        Require valid-user

    Header always set Access-Control-Allow-Methods "GET, POST, DELETE, OPTIONS, PUT"
    Header always set Access-Control-Allow-Headers "Content-Type, X-Requested-With, X-HTTP-Method-Override, Origin, Accept, Authorization"
    Header always set Access-Control-Allow-Credentials "true"
    Header always set Cache-Control "max-age=0"
    Header always set Access-Control-Allow-Origin *

    </Location>

ErrorLog ${APACHE_LOG_DIR}/error.log

</VirtualHost>

Apache 发回新的响应标头,但请求标头在某处似乎仍然是错误的。身份验证不起作用。

请求标头

OPTIONS /solar_vendor/_search HTTP/1.1
Host: 46.252.26.173:8181
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: de-de,de;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Origin: http://46.252.26.173:81
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization,content-type
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache

响应标头

HTTP/1.1 401 Authorization Required
Date: Sat, 09 Nov 2013 08:48:48 GMT
Access-Control-Allow-Methods: GET, POST, DELETE, OPTIONS, PUT
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-HTTP-Method-Override, Origin, Accept, Authorization
Access-Control-Allow-Credentials: true
Cache-Control: max-age=0
Access-Control-Allow-Origin: *
WWW-Authenticate: Basic realm="Username/Password"
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 346
Connection: close
Content-Type: text/html; charset=iso-8859-1

解决方案: 在做了更多研究之后,我发现这绝对是关于 CORS 的配置问题。有很多关于该主题的帖子,但似乎为了解决我的问题,有必要在 apache 上进行一些非常精细的配置,并确保从浏览器发送正确的内容。

所以我重新考虑了策略并找到了一个更简单的解决方案。只需修改 vhost 反向代理配置以将 elastisearch 服务器和 kibana 移动到同一 http 端口上。这也为 Kibana 增加了更好的安全性。

这就是我所做的:

<VirtualHost *:8181>

ProxyRequests Off

ProxyPass /bigdatadesk/ http://127.0.0.1:81/bigdatadesk/src/
ProxyPassReverse /bigdatadesk/ http://127.0.0.1:81/bigdatadesk/src/

ProxyPass / http://127.0.0.1:9200/
ProxyPassReverse / https://127.0.0.1:9200/


    <Location />
        Order deny,allow
        Allow from all
        AuthType Basic
        AuthName “Username/Password”
        AuthUserFile /var/www/.htpasswd
        Require valid-user
    </Location>


ErrorLog ${APACHE_LOG_DIR}/error.log

</VirtualHost>
4

3 回答 3

10

这是一个完美的解决方案:

https://github.com/fangli/kibana-authentication-proxy

不仅支持 basicAuth 后端,还支持客户端的 GoogleOAuth 和 BasicAuth。如果有效,请给个star,谢谢。

于 2013-12-20T05:26:10.633 回答
7

在 Kibana 中,将现有的 elastic-angular-client.js 替换为可以在此处找到的最新版本。然后,在 Kibana 代码中替换以下所有实例:

$scope.ejs = ejsResource(config.elasticsearch);

$scope.ejs = ejsResource({server: config.elasticsearch, headers: {'Access-Control-Request-Headers': 'accept, origin, authorization', 'Authorization': 'Basic ' + Base64Encode('user:Password')}});

这应该就是你所需要的。

更新:

是否为 CORS 配置了 apache?看到这个

Header always set Access-Control-Allow-Methods "GET, POST, DELETE, OPTIONS, PUT"
Header always set Access-Control-Allow-Headers "Content-Type, X-Requested-With, X-HTTP-Method-Override, Origin, Accept, Authorization"
Header always set Access-Control-Allow-Credentials "true"
Header always set Cache-Control "max-age=0"
Header always set Access-Control-Allow-Origin *
于 2013-11-08T22:23:52.677 回答
1

你是对的,因为这是一个 CORS 问题。Kibana 3 使用 CORS 与 ElasticSearch 进行通信。

为了使 HTTP Authentication Headers 和 Cookies 与 Kibana CORS 请求一起发送,您需要做两件事:

一:在您的 Kibana config.js 文件中,找到定义 ElasticSearch 服务器的设置:

elasticsearch: "http://localhost:9200",

这需要更改为:

elasticsearch: {server: "http://localhost:9200", withCredentials: true},

如果服务器能够接收到,这将告诉 Kibana 发送 Authentication 标头和 cookie。

二:接下来,您需要进入 ElasticSearch 配置文件(主机服务器上的 elasticsearch.yml;我的位于 CentOS7 服务器上的 /etc/elasticsearch/elasticsearch.yml )。在此文件中,您将找到“网络和 HTTP”部分。您将需要找到显示以下内容的行:

#http.port: 9200

取消注释此行并将端口更改为您希望 ElasticSearch 运行的端口。我选择了 19200。然后对#transport.tcp.port: 9300设置执行相同的操作。我再次选择了 19300。

最后,在本节的末尾(为了组织起见,您也可以简单地将以下内容附加到文件末尾)添加:

http.cors.allow-origin: http://localhost:8080 http.cors.allow-credentials: true http.cors.enabled: true

您可以将上述源地址更改为您的 Web 服务器为 Kibana 提供服务的任何位置。或者,您可以简单地/.*/匹配所有来源,但这是不可取的。

现在保存 elasticsearch.yml 文件并重新启动 elasticsearch 服务器。如果请求通过身份验证,您的反向代理应配置为在端口 9200 上运行并指向 19200。

警告的话,如果您使用 Cookie 来验证请求,则应确保在反向代理配置中将 HTTP OPTIONS 方法列入白名单,因为只有 GET、PUT、POST 和 DELETE 请求包含 cookie。我还没有测试 OPTIONS 是否也包含 Authentication 标头,但它可能与 cookie 的情况相同。如果 OPTIONS 请求也无法通过,Kibana 将无法正常运行。

将反向代理配置为将任何以 _shutdown 结尾的请求列入黑名单也是一个好主意,因为在大多数情况下,通过外部请求不需要此命令。

于 2015-02-15T19:41:58.900 回答