web-dev-qa-db-ja.com

NTLMを使用するWindows認証を備えたnginxリバースプロキシ

NTLMを使用するWindows認証でリバースプロキシを実行できるかどうか誰でも知っていますか?これに関する例は見つかりません。 more_set_headersフィールドの値は何ですか?

location / {
            proxy_http_version      1.1;
            proxy_pass_request_headers on;
            proxy_set_header        Host            $Host;
            proxy_set_header        X-Real-IP       $remote_addr;
            proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;


            more_set_input_headers  'Authorization: $http_authorization';

            proxy_set_header  Accept-Encoding  "";

            proxy_pass              http://Host/;
            proxy_redirect          default;
            #This is what worked for me, but you need the headers-more mod
            more_set_headers        -s 401 'WWW-Authenticate: Basic realm="Host.local"';
}

ホストに直接アクセスした場合、リバースプロキシでアクセスした場合、認証は毎回失敗します。

12
matheus

NginxでNTLMパススルーを有効にするには-

upstream http_backend {
    server 2.3.4.5:80;
    keepalive 16;
}
server {
    ...
    location / {
       proxy_pass http://http_backend/;
       proxy_http_version 1.1;
       proxy_set_header Connection "";
    ...
    }
 }

- ラモン

14
Fizz

私の知る限り、これは現在nginxでは不可能です。少し前に自分で詳しく調べました。基本的な問題は、NTLM認証では後続のリクエストで同じソケットを使用する必要があるが、プロキシはそれを行わないことです。 nginx開発チームがこの動作に何らかのサポートを提供するまで、これを処理する方法は、リバースプロキシ自体で認証を使用することでした。私は現在、Apache 2.2、mod_proxy、mod_auth_sspiを使用してこれを行っています(完璧ではありませんが、動作します)。幸運を!申し訳ありません、nginx、私はあなたを愛していますが、私たちは本当にこの一般的なユースケースのためにいくつかの助けを使うことができました。

4
Tony Schwartz

それ以来、私はこのための別の解決策を考え出しました。これは、NTLMを実行するnginxと同じではありません(nginxチームがこれを実装する場合、これは素晴らしいことです)。しかし、今のところ、私がやっていることは私たちにとってうまくいきます。

暗号化されたcookieを使用するluaコードをいくつか作成しました。暗号化されたCookieには、ユーザーのID、ユーザーが認証された時間、ユーザーが認証に使用したIPアドレスが含まれます。参考のためにここに添付します。洗練されていませんが、おそらく、それを使用して独自の同様のスキームを開発できます。

基本的に、それがどのように機能するかです:

  1. Cookieが利用できない場合、または有効期限が切れているか無効である場合、nginxはクライアントのIPアドレスを渡してバックエンドIISアプリケーションにサービス呼び出し(事前認証)を行い、クライアントをIIS Webにリダイレクトします「Windows認証」をオンにしているアプリケーション。バックエンドIISアプリケーションの事前認証サービスがGUIDを生成し、そのGUIDのエントリをデータベースに格納し、このGUIDが認証されようとしていることを示すフラグを設定します。
  2. ブラウザはnginxによってGUIDを渡して認証アプリにリダイレクトされます。
  3. IISアプリはWindows認証を介してユーザーを認証し、そのGUIDのdbレコードとクライアントIPアドレスをユーザーIDと認証された時間で更新します。
  4. IISアプリは、クライアントを元のリクエストにリダイレクトします。
  5. nginx luaコードはこの呼び出しをインターセプトし、IISアプリに再度バックドアサービスコールを行い(認証後)、ユーザーIDと認証時間を要求します。この情報は暗号化されたCookieに設定され、ブラウザに送信されます。リクエストの通過が許可され、REMOTE_USERが一緒に送信されます。
  6. ブラウザーによる後続のリクエストはCookieを渡し、nginx luaコードは有効なCookieを確認し、REMOTE_USERリクエストヘッダーを渡すことでリクエストを直接プロキシします(もちろん、再度認証する必要はありません)。

access.lua:

local enc     = require("enc");
local strings = require("strings");
local dkjson  = require("dkjson");


function beginAuth()
    local headers = ngx.req.get_headers();
    local contentTypeOriginal = headers["Content-Type"];
    print( contentTypeOriginal ); 
    ngx.req.set_header( "Content-Type", "application/json" );
    local method = ngx.req.get_method();
    local body = "";
    if method == "POST" then
        local requestedWith = headers["X-Requested-With"];
        if requestedWith ~= nil and requestedWith == "XMLHttpRequest" then
            print( "bailing, won't allow post during re-authentication." );
            ngx.exit(ngx.HTTP_GONE); -- for now, we are NOT supporting a post for re-authentication.  user must do a get first.  cookies can't be set on these ajax calls when redirecting, so for now we can't support it.
            ngx.say("Reload the page.");
            return;
        else
            print( "Attempting to handle POST for request uri: " .. ngx.var.uri );
        end
        ngx.req.read_body();
        local bodyData = ngx.req.get_body_data();
        if bodyData ~= nil then
            body = bodyData;
        end
    end
    local json = dkjson.encode( { c = contentTypeOriginal, m = method, d = body } );
    local origData = enc.base64encode( json );
    local res = ngx.location.capture( "/preauth", { method = ngx.HTTP_POST, body = "{'clientIp':'" .. ngx.var.remote_addr .. "','originalUrl':'" .. ngx.var.FrontEndProtocol .. ngx.var.Host .. ngx.var.uri .. "','originalData':'" .. origData .. "'}" } );
    if contentTypeOriginal ~= nil then
        ngx.req.set_header( "Content-Type", contentTypeOriginal );
    else
        ngx.req.clear_header( "Content-Type" );
    end
    if res.status == 200 then
        ngx.header["Access-Control-Allow-Origin"] = "*";
        ngx.header["Set-Cookie"] = "pca=guid:" .. enc.encrypt( res.body ) .. "; path=/"
        ngx.redirect( ngx.var.authurl .. "auth/" .. res.body );
    else
        ngx.exit(res.status);
    end
end

function completeAuth( cookie )
    local guid = enc.decrypt( string.sub( cookie, 6 ) );
    local contentTypeOriginal = ngx.header["Content-Type"];
    ngx.req.set_header( "Content-Type", "application/json" );
    local res = ngx.location.capture( "/postauth", { method = ngx.HTTP_POST, body = "{'clientIp':'" .. ngx.var.remote_addr .. "','guid':'" .. guid .. "'}" } );
    if contentTypeOriginal ~= nil then
        ngx.req.set_header( "Content-Type", contentTypeOriginal );
    else
        ngx.req.clear_header( "Content-Type" );
    end
    if res.status == 200 then
        local resJson = res.body;
        -- print( "here a1" );
        -- print( resJson );
        local resTbl = dkjson.decode( resJson );
        if resTbl.StatusCode == 0 then
            resTbl = resTbl.Result;
            local time = os.time();
            local sessionData = dkjson.encode( { u = resTbl.user, t = time, o = time } );
            ngx.header["Set-Cookie"] = "pca=" .. enc.encrypt( sessionData ) .. "; path=/"
            ngx.req.set_header( "REMOTE_USER", resTbl.user );
            if resTbl.originalData ~= nil and resTbl.originalData ~= "" then
                local tblJson = enc.base64decode( resTbl.originalData );
                local tbl = dkjson.decode( tblJson );
                if tbl.m ~= nil and tbl.m == "POST" then
                    ngx.req.set_method( ngx.HTTP_POST );
                    ngx.req.set_header( "Content-Type", tbl.c );
                    ngx.req.read_body();
                    ngx.req.set_body_data( tbl.d );
                end
            end
        else
            ngx.log( ngx.ERR, "error parsing json " .. resJson );
            ngx.exit(500);
        end
    else
        print( "error completing auth." );
        ngx.header["Set-Cookie"] = "pca=; path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; token=deleted;"
        print( res.status );
        ngx.exit(res.status);
    end
end


local cookie = ngx.var.cookie_pca;
print( cookie );
if cookie == nil then 
    beginAuth();
elseif strings.starts( cookie, "guid:" ) then
    completeAuth( cookie );
else
    -- GOOD TO GO...
    local json = enc.decrypt( cookie );
    local d = dkjson.decode( json );
    local now = os.time();
    local diff = now - d.t;
    local diffOriginal = 0;
    if d.o ~= nil then 
        diffOriginal = now - d.o;
    end
    if diff > 3600 or diffOriginal > 43200 then
        beginAuth();
    elseif diff > 300 then
        print( "regenerating new cookie after " .. tostring( diff ) .. " seconds." );
        local sessionData = dkjson.encode( { u = d.u, t = now, o = d.t } );
        ngx.header["Set-Cookie"] = "pca=" .. enc.encrypt( sessionData ) .. "; path=/"
    end
    ngx.req.set_header( "REMOTE_USER", d.u );
end

strings.lua:

local private = {};
local public = {};
strings = public;

function public.starts(String,Start)
   return string.sub(String,1,string.len(Start))==Start
end

function public.ends(String,End)
   return End=='' or string.sub(String,-string.len(End))==End
end

return strings;

enc.lua:

-- for base64, try something like: http://lua-users.org/wiki/BaseSixtyFour
local private = {};
local public = {};
enc = public;

local aeslua = require("aeslua");

private.key = "f8d7shfkdjfhhggf";

function public.encrypt( s )
    return base64.base64encode( aeslua.encrypt( private.key, s ) );
end

function public.decrypt( s )
    return aeslua.decrypt( private.key, base64.base64decode( s ) );
end

return enc;

サンプルnginx conf:

upstream dev {
    ip_hash;
    server app.server.local:8080;
}
set $authurl http://auth.server.local:8082/root/;
set $FrontEndProtocol https://;
location / {
    proxy_pass     http://dev/;
    proxy_set_header Host $Host;
    proxy_redirect default;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_set_header X-Real-IP $remote_addr;
    proxy_buffers 128 8k;
    access_by_lua_file conf/lua/app/dev/access.lua;
}
3
Tony Schwartz

わかりました、私達はnginx/openrestyのために lua code を書きました、それはいくつかの解決可能な制限と商業のnginxバージョンの必要なしでntlm逆プロキシ問題を解決します

0
broomrider