Skip to content

Pass WWW-Authenticate header on 401 #33

Closed

Description

@nightah

Would it be possible to to ensure that the client also receives the WWW-Authenticate header from the subrequest response if a 401 is returned similar to ngx_http_auth_request_module?

This should ensure that if basic auth is being requested the appropriate prompt will be presented to the client.

Activity

  1. TimWolla commented on Mar 13, 2021

    @TimWolla
    Owner

    haproxy-auth-request does not send anything to the client by itself. It just performs a sub-request and sets variables. What you do with these variable is up to you.

    You should be able to achieve what you need by doing something like the following:

    http-request set-var(txn.auth) var(req.auth_response_header.www_authenticate)
    http-response set-header www-authenticate %[var(txn.auth)]
    
  2. nightah commented on Mar 15, 2021

    @nightah
    Author

    Thanks for the response @TimWolla.

    Not sure if I'm doing something wrong but it looked to me like whenever I perform a http-request deny that the header would never be assigned, is this a HAProxy limitation?

    I initially thought this may have been because we have HAProxy performing the TLS termination for the actual auth backend but I've tried reverting to a non-tls backend for the lua.auth-request with no success.

    He's my full config in case it helps:

    global
        lua-prepend-path /usr/local/etc/haproxy/?/http.lua
        lua-load /usr/local/etc/haproxy/auth-request.lua
        log stdout format raw local0 debug
    
    defaults
        mode http
        log global
        option httplog
        option forwardfor
    
    frontend fe_api
        bind *:8081 ssl crt /usr/local/etc/haproxy/haproxy.pem
    
        stats enable
        stats uri /api
        stats refresh 10s
        stats admin if LOCALHOST
    
    frontend fe_http
        bind *:8080 ssl crt /usr/local/etc/haproxy/haproxy.pem
    
        acl api-path path_beg -i /api
        acl headers-path path -i -m end /headers
        acl host-authelia-portal hdr(host) -i login.example.com:8080
        acl protected-frontends hdr(host) -m reg -i ^(?i)(admin|home|public|secure)\.example\.com
        acl protected-frontends-basic hdr(host) -m reg -i ^(?i)(singlefactor)\.example\.com
    
        http-request set-var(req.scheme) str(https) if { ssl_fc }
        http-request set-var(req.scheme) str(http) if !{ ssl_fc }
        http-request set-var(req.questionmark) str(?) if { query -m found }
        http-request set-var(req.method) str(CONNECT) if { method CONNECT }
        http-request set-var(req.method) str(GET) if { method GET }
        http-request set-var(req.method) str(HEAD) if { method HEAD }
        http-request set-var(req.method) str(OPTIONS) if { method OPTIONS }
        http-request set-var(req.method) str(POST) if { method POST }
        http-request set-var(req.method) str(TRACE) if { method TRACE }
        http-request set-var(req.method) str(PUT) if { method PUT }
        http-request set-var(req.method) str(PATCH) if { method PATCH }
        http-request set-var(req.method) str(DELETE) if { method DELETE }
        http-request set-header X-Forwarded-Method %[var(req.method)]
    
        http-request set-header X-Real-IP %[src]
        http-request set-header X-Forwarded-Proto %[var(req.scheme)]
        http-request set-header X-Forwarded-Host %[req.hdr(Host)]
        http-request set-header X-Forwarded-Uri %[path]%[var(req.questionmark)]%[query]
    
        # be_auth_request is used to make HAProxy do the TLS termination since the Lua script
        # does not know how to handle it (see https://github.com/TimWolla/haproxy-auth-request/issues/12).
        http-request lua.auth-request be_auth_request /api/verify if protected-frontends
        http-request lua.auth-request be_auth_request /api/verify?auth=basic if protected-frontends-basic
    
        http-request set-var(txn.auth) var(req.auth_response_header.www_authenticate) if protected-frontends-basic !{ var(txn.auth_response_successful) -m bool }
        http-response set-header WWW-Authenticate %[var(txn.auth)] if !{ var(txn.auth_response_successful) -m bool }
    
        http-request redirect location https://login.example.com:8080/?rd=%[var(req.scheme)]://%[base]%[var(req.questionmark)]%[query] if protected-frontends !{ var(txn.auth_response_successful) -m bool }
        http-request deny deny_status 401 if protected-frontends-basic !{ var(txn.auth_response_successful) -m bool }
    
        use_backend be_authelia if host-authelia-portal api-path
        use_backend fe_authelia if host-authelia-portal !api-path
        use_backend be_httpbin if protected-frontends headers-path
        use_backend be_mail if { hdr(host) -i mail.example.com:8080 }
        use_backend be_protected if protected-frontends || protected-frontends-basic
    
    backend be_auth_request
        mode http
        server proxy 127.0.0.1:8085
    
    listen be_auth_request_proxy
        mode http
        bind 127.0.0.1:8085
        server authelia-backend authelia-backend:9091 ssl verify none
    
    backend be_authelia
        server authelia-backend authelia-backend:9091 ssl verify none
    
    backend fe_authelia
        server authelia-frontend authelia-frontend:3000
    
    backend be_httpbin
        acl remote_user_exist var(req.auth_response_header.remote_user) -m found
        acl remote_groups_exist var(req.auth_response_header.remote_groups) -m found
        acl remote_name_exist var(req.auth_response_header.remote_name) -m found
        acl remote_email_exist var(req.auth_response_header.remote_email) -m found
        http-request set-header Remote-User %[var(req.auth_response_header.remote_user)] if remote_user_exist
        http-request set-header Remote-Groups %[var(req.auth_response_header.remote_groups)] if remote_groups_exist
        http-request set-header Remote-Name %[var(req.auth_response_header.remote_name)] if remote_name_exist
        http-request set-header Remote-Email %[var(req.auth_response_header.remote_email)] if remote_email_exist
    
        server httpbin-backend httpbin:8000
    
    backend be_mail
        server smtp-backend smtp:1080
    
    backend be_protected
        server nginx-backend nginx-backend:80

    Ideally what I'm trying to achieve with this flow is if the auth request backend responds with a 401 and the WWW-Authenticate header that this illicits the same response to the client.

    If I remove the http-request deny deny_status 401 if protected-frontends-basic !{ var(txn.auth_response_successful) -m bool } line then I can see the header present but it won't prompt the basic auth login prompt without the 401 response too.

    If I add the http-request deny deny_status 401 if protected-frontends-basic !{ var(txn.auth_response_successful) -m bool } line then I don't see the header.

  3. TimWolla commented on Mar 15, 2021

    @TimWolla
    Owner

    Not sure if I'm doing something wrong but it looked to me like whenever I perform a http-request deny that the header would never be assigned, is this a HAProxy limitation?

    This is a HAProxy limitation. The http-response rules are not evaluated for responses generated by HAProxy. Either use http-after-response set-header www-authenticate %[var(txn.auth)] or http-response deny deny_status 401 hdr www-authenticate %[var(txn.auth)] (not sure whether I got the syntax 100% correct). Both needs HAProxy 2.2+.

  4. nightah commented on Mar 16, 2021

    @nightah
    Author

    Thanks for your support @TimWolla I've managed to get this working as expected.

  5. Repository owner locked and limited conversation to collaborators on May 19, 2022
  6. converted this issue into a discussion #50 on May 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionFurther information is requested

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions