Skip to main content

Envoy Proxy front end using Authelia

Q: So what is Authelia and why would I want to do this?
A: Add 2FA to an existing web service, behind Envoy Proxy as an edge proxy, with the backend unaware.

Authelia is an open-source authentication and authorization server providing 2-factor authentication and single sign-on (SSO) for your applications via a web portal. It acts as a companion of reverse proxies like nginx, Traefik or HAProxy to let them know whether queries should pass through. Unauthenticated users are redirected to Authelia Sign-in portal instead.

Documentation is available at https://docs.authelia.com.

The Solution

So the proxy authentication hook that is in Envoy doesn't match what is required for Authelia to work, but reading the manual gets us to a working configuration. What we need to do is process the request on the way in, call the Authelia service and if we don't get back an "OK" redirect the request to the Authelia service.

The Lua hook to the rescue to allow processing, adding headers and calling another service in the middle of processing the request.

         - name: envoy.filters.http.lua
           typed_config:
             "@type": type.googleapis.com/envoy.config.filter.http.lua.v2.Lua
             inline_code: |

 When the request comes in we build a GET request to send to Authelia, and based on the result allow the request to go through. If we get a 302or a 401 from Authelia then return the Authelia response to this request.
If we get a 200 and user is set then tuck it away in a header.

Sample code snippet

 - name: listener_1
   address:
     socket_address: { address: ************, port_value: 443 }
   listener_filters:
   - name: "envoy.filters.listener.tls_inspector"
     typed_config: {}
   filter_chains:
   - filter_chain_match:
       server_names: ["************"]
     transport_socket:
       name: envoy.transport_sockets.tls
       typed_config:
         "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
         common_tls_context:
           tls_certificates:
           - certificate_chain: { filename: "************.cert" }
             private_key: { filename: "************.key" }
           tls_params:
             tls_minimum_protocol_version: TLSv1_2
     filters:
     - name: envoy.filters.network.http_connection_manager
       typed_config:
         "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
         use_remote_address: true
         stat_prefix: ingress_http
         route_config:
           virtual_hosts:
           - name: default
             domains: "*"
             routes:
             - match: { prefix: "/" }
               route: { cluster: my_cluster }
           response_headers_to_add:
           - header:
               key: "Strict-Transport-Security"
               value: "max-age=31536000; includeSubDomains"
             append: true      
                    
         http_filters:
         - name: envoy.filters.http.lua
           typed_config:
             "@type": type.googleapis.com/envoy.config.filter.http.lua.v2.Lua
             inline_code: |
               function envoy_on_request(request_handle)
                  
                 local request_headers = request_handle:headers()  
                  
                 local auth_headers = {}                   
                  
                 auth_headers[":method"] = "GET"
                 auth_headers["X-Original-URL"] = "https://************.com"..request_headers:get(":path")
                 auth_headers[":path"] = "/api/verify?rd=https://************.com"
                 auth_headers[":authority"] = "auth-service"
                 auth_headers["cookie"] = request_headers:get("cookie")                                
                  
                 local headers, body = request_handle:httpCall(
                   "auth-service",
                   auth_headers,
                   "nothing here",
                   5000
                 )
                  
                 local status = headers[":status"]
                 local user   = headers["remote-user"]
                  
                 if ( status == "302" or status == "401" ) then                      
                    request_handle:logDebug("LUA:redirect 302")
                    request_handle:respond(headers,body)
                 end
                  
                 if status ~= "200" then                      
                    request_handle:respond(
                      {[":status"] = "500"},"authservice error"
                    )
                 else
                    if (user ~= nil or user ~= '') then
                       request_handle:headers():add("x-webobjects-remote-user", user)
                    end
                 end
                                    
                end


         - name: envoy.filters.http.router
           typed_config: {}

 

Want to comment or know more then please...

Contact Me