togatttiのエンジニアメモ

過度な期待はしないでください.

Kibana5.0.0に、認証、認可を実装する

概要

Kibanaを管理者以外の誰かに使わせる場合、そのユーザを認証し、閲覧できるインデックスを限定したいことがある。

Shieldという有償プラグインで認証、認可が可能らしいが、お金をかけずに実現したかった。

dev.classmethod.jp

今回は、OpenRestyを使い、認証、認可を付け加えた。

  • 認証は、NginxのBasic認証を利用する。
  • 認可は、luaで、Nginxをカスタマイズして、認証を経たユーザのHTTPリクエストを制御する。

環境

  • OS

  • Kibana5.0.0

    • この記事では、192.168.0.2で動作する。

NginxとKibanaは同一のサーバ内で動作している。

Kibanaへの直アクセス禁止

KibanaのURLへ直接アクセスされると、認証、認可が適用されないので、外部から5601へのアクセスを閉じておく。

ただし、リバースプロキシ経由のアクセスは許可する。

# iptables -A INPUT -i lo -j ACCEPT
# iptables -A OUTPUT -o lo -j ACCEPT
# iptables -A INPUT -p tcp --dport 5601 -j REJECT

OpenRestyのセットアップ

良さげなGistがあったので、参考にした。

Easy install openresty (used and tested on Ubuntu 14.04, 15.10 and 16.04) · GitHub

最新版は、本家から持ってくる。

OpenResty - Download

認証

KibanaへのNginxのリバースプロキシ設定は、記事で書いたので貼っておく。

togattti.hateblo.jp

認証は、Basic認証をつけてるだけ。

手順

必要なモジュールを入手する。

# apt install apache2-utils

ユーザにパスワードを発行する。とりあえず、sales、support、techユーザにした。

# htpasswd -c -b /etc/nginx/.htpasswd sales salespasswd
# htpasswd -b /etc/nginx/.htpasswd support supportpasswd
# htpasswd -b /etc/nginx/.htpasswd tech techpasswd
# cat /etc/nginx/.htpasswd 
sales:$apr1$BMU.bsHb$c/jXRc1T3.keiYTtmdtua/
support:$apr1$Yvlq26fj$vtrYnqrc/XW/2WSRG6vlN.
tech:$apr1$Zqt0uiD3$xWO72SD10EFYB4Fq.JEw.1

locationディレクティブに、auth_basicとauth_basic_user_fileを追加して、nginxを再起動する。

(snip)
location ~ (/app/kibana|/bundles/|/status|/elasticsearch|/plugins|/timelion|/console|/api/) {
                auth_basic "Restricted";
                auth_basic_user_file "/etc/nginx/.htpasswd";
                proxy_pass http://192.168.0.2:5601;
                proxy_set_header        Host $host;
                proxy_set_header        X-Real-IP $remote_addr;
                proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header        X-Forwarded-Proto $scheme;
                proxy_set_header        X-Forwarded-Host $http_host;
}
(snip)

認可

インデックスsales-* 、support-* 、tech-*がある場合に、ユーザは関連するインデックスしか閲覧できないようにしたい。

f:id:togattti1990:20161123155035p:plain

例えば、ユーザsalesは、インデックスsales-*を閲覧できるが、インデックスsupport-* 、tech-* は閲覧できないようにする。

f:id:togattti1990:20161123155042p:plain

手順

Luaで書いたサンプルを示す。

Kibanaが、Elasticsearchに、インデックスを要求する際、HTTPリクエストのボディ部分に、インデックス名を含む。

それを利用して、アクセス拒否するようにした。

no_allow_indexesに、ユーザ名とアクセス制限するインデックスを記述する。

ngx.var.remote_userで、認証されたユーザを取得できる。

-- /etc/nginx/custom/kibana_simple_acl.lua
local no_allow_indexes = {
    sales = {
    "support-*",
    "tech-*"
    },
    support = {
    "tech-*",
    "sales-*"
    },
    tech = {
    "sales-*",
    "support-*"
    }
}

local user_ = ngx.var.remote_user
if user_ == nil then
    ngx.header.content_type = "text/plain"
    ngx.log(ngx.STDERR, "no user.")
    ngx.status(403)
    ngx.say("403 Forbidden: You do not have access to this page.")
    return ngx.exit(403)
end

user_check = false
for user, indexes in pairs(no_allow_indexes) do
    local p = string.match(user, user_)
    ngx.log(ngx.STDERR, string.format("user: %s, user_: %s", user, user_))
    if p then
        user_check = true
        ngx.req.read_body()
        local body_data = ngx.req.get_body_data()
        if body_data == nil then return end
        for _, index in pairs(indexes) do
            local matcher = ngx.re.match(body_data, index)
            if matcher then
                ngx.log(ngx.STDERR, string.format("User does not have access to %s", index))
                return ngx.exit(403)
            end
        end
    end
end

if not user_check then
    ngx.header.content_type = "text/plain"
    ngx.log(ngx.STDERR, string.format("invalid user: %s", user_))
    ngx.status = 403
    ngx.say("403 Forbidden: You do not have access to this page.")
    return ngx.exit(403)
end

locationディレクティブに、access_by_lua_fileを追加して、再起動する。

(snip)
location ~ (/app/kibana|/bundles/|/status|/elasticsearch|/plugins|/timelion|/console|/api/) {
                auth_basic "Restricted";
                auth_basic_user_file "/etc/nginx/.htpasswd";
                access_by_lua_file /etc/nginx/custom/kibana_simple_acl.lua;
                proxy_pass http://192.168.0.2:5601;
                proxy_set_header        Host $host;
                proxy_set_header        X-Real-IP $remote_addr;
                proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header        X-Forwarded-Proto $scheme;
                proxy_set_header        X-Forwarded-Host $http_host;
}
(snip)

認証されたユーザが認可されてないインデックスを含むDiscoverやDashBoardが閲覧できないことを確認する。

f:id:togattti1990:20161123155631p:plain