web-dev-qa-db-fra.com

proxy inverse nginx avec authentification Windows utilisant NTLM

Tout le monde sait s'il est possible de faire un proxy inverse avec une authentification Windows utilisant NTLM? Je ne trouve aucun exemple à ce sujet. Quelles devraient être les valeurs du champ 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"';
}

Si j'accède directement à l'hôte, l'authentification réussit si j'accède avec le proxy inverse, l'authentification échoue à chaque fois. 

10
matheus

Pour activer l'authentification unique NTLM avec Nginx -

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 "";
    ...
    }
 }

- Ramon

13
Fizz

Depuis, j'ai trouvé une autre solution pour cela. Ce n’est toujours pas la même chose que faire nginx avec NTLM (ce qui sera agréable si l’équipe nginx l’implémente un jour). Mais, pour le moment, ce que je fais fonctionne pour nous.

J'ai écrit du code Lua qui utilise un cookie crypté. Le cookie crypté contient l'identifiant de l'utilisateur, l'heure à laquelle il s'est authentifié et l'adresse IP à partir de laquelle il s'est authentifié. Je joins ce matériel ici pour référence. Ce n'est pas poli, mais vous pouvez peut-être l'utiliser pour développer votre propre système similaire.

Voici comment cela fonctionne:

  1. Si le cookie n'est PAS disponible ou s'il a expiré ou est invalide, nginx passe un appel de service (pré-autorisation) vers une application dorsale IIS en transmettant l'adresse IP du client, puis redirige le client vers un IIS application Web sur laquelle "Authentification Windows" est activé. Le service de préautorisation de l'application dorsale IIS génère un GUID et stocke une entrée dans la base de données pour ce GUID ainsi qu'un indicateur indiquant que ce GUID est sur le point de être authentifié.
  2. Le navigateur est redirigé par nginx vers l'application d'authentification en passant le GUID.
  3. L'application IIS authentifie l'utilisateur via l'authentification Windows et met à jour l'enregistrement de base de données pour ce GUID et l'adresse IP du client avec l'ID utilisateur et l'heure authentifiés.
  4. L'application IIS redirige le client vers la demande d'origine. 
  5. nginx lua code intercepte cet appel et effectue à nouveau un appel de service en arrière-plan à l'application IIS (post-auth) et demande l'identifiant et l'heure de l'utilisateur authentifiés. Ces informations sont définies dans un cookie crypté et sont envoyées au navigateur. La demande est autorisée à passer et le REMOTE_USER est envoyé.
  6. les requêtes suivantes du navigateur transmettent le cookie et le code nginx lua voit le cookie valide et envoie directement la requête à un proxy (sans avoir à s'authentifier à nouveau naturellement) en transmettant l'en-tête de la requête 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;

échantillon 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

Pour autant que je sache, cela n'est actuellement pas possible avec nginx. J'ai moi-même étudié cette question en profondeur il y a quelques instants. Le problème fondamental est que l'authentification NTLM nécessitera que le même socket soit utilisé lors de la requête suivante, mais le proxy ne le fait pas. Jusqu'à ce que l'équipe de développement de nginx fournisse une sorte de support pour ce problème, la façon dont je l'ai géré était de recourir à l'authentification dans le proxy inverse lui-même. Je le fais actuellement avec Apache 2.2, mod_proxy, mod_auth_sspi (pas parfait, mais ça marche). Bonne chance! Désolé nginx, je t'aime, mais nous pourrions vraiment utiliser de l'aide pour ce cas d'utilisation courant.

3
Tony Schwartz

Ok, nous avons écrit lua code pour nginx/openresty, ce qui résout le problème de reverse proxy par ntlm avec certaines limitations pouvant être résolues et sans nécessiter de version commerciale de nginx

0
broomrider