authentik icon indicating copy to clipboard operation
authentik copied to clipboard

Whitelist a network to bypass auth?

Open LP0101 opened this issue 3 years ago • 4 comments

Hello! I'm using Authentik with a proxy provider with domain forward auth. Is it possible to set a network to bypass auth entirely? I'd like to define a CIDR range (ie: 192.168.10.0/24) to be allowed in without auth, while still requiring it for everything else.

LP0101 avatar Jul 15 '22 22:07 LP0101

Yeah this would be awesome to have!

I'm using Zabbix for monitoring a lot of urls, it would be much easier if I could just tell authentik to let everything from my Zabbix-host through without asking for username/password.

yeyeoke avatar Aug 04 '22 20:08 yeyeoke

The question is how though. With multiple users, which should be selected to bypass the authentication?

For some applications I'm hosting I have similar requirements (e.g. authenticated when accessing remotely, but freely accessible from within my home network). As this felt more like a routing issue than an authentication issue I've solved this at Reverse-Proxy level using Traefik:

  - 'traefik.http.routers.app1.rule=Host(`app1.${DOMAIN}`)'
  - 'traefik.http.routers.app1.entrypoints=https'
  - 'traefik.http.routers.app1.middlewares=authentik@docker' # Externally accessible through Authentik
  - 'traefik.http.services.app1.loadbalancer.server.port=80'
  # Additional LOCAL freely exposed routing:
  - 'traefik.http.routers.app1_local.rule=Host(`app1.${DOMAIN}`) && HeadersRegexp(`X-Real-Ip`, `^(192\.168\.1\.\d+)`)'
  - 'traefik.http.routers.app1_local.entrypoints=https'
  - 'traefik.http.routers.app1_local.middlewares=local-ipwhitelist@file'
  - 'traefik.http.routers.app1_local.priority=100'

RoboMagus avatar Aug 05 '22 07:08 RoboMagus

Well. In my use case there shouldn't be any authentication at all for the selected adress/range, it should simply never ask for a username/password.

I've done something similar for some applications right now where I've exposed a separate endpoint in Nginx that doesn't require authentication, but it would be easier to manage it all using authentik.

yeyeoke avatar Aug 05 '22 09:08 yeyeoke

You could manage this at the policy level, but I do agree with @RoboMagus that it makes more sense to configure it in the reverse proxy. Though configuring it in authentik could save you some time I suppose.

Create a policy that matches by subnet; set the pending_user context variable to whatever service account you want; and add another policy to every relevant stage binding to not show it if the IP is within the subnet or if pending_user is set, your choice.

As long as there are no interactive flows and the client runs JavaScript, it should be fine, but do note that since authentik flows are not designed to be accessed by non-humans with non-interactive browsers, you may still run into issues. Would still recommend configuring this at the proxy/routing level.

sevmonster avatar Sep 14 '22 12:09 sevmonster

I'm looking for the exact same, bypass auth when client is in LAN.

I'm sadly not able to do it at routing/proxy level since I apply the authentik traefik middleware at the entrypoint level and there is no way to remove a middleware from a router, only alternative if to not add the middleware at entrypoint level and add it manually on each router trying to not forget one. That's not a solution for me so I need to make the middleware allow auth transparently when coming from LAN.

I did understand what was proposed, I'm only not sure how to do it. I suspect my way of setting the pending user is wrong, but I couldn't find how to properly do it. Right now, I created an expression policy calles ip-lan-policy containing this :

condition = ak_client_ip in ip_network('10.0.0.0/8')
if condition:
    if not ('pending_user' in context):
        context['pending_user']='lanuser'
return condition

And added it to all steps in the flow : image (picture is showing wrong "Policy denies" and "Policy passes" as they are I checked "invert the result" for every policy binding.

I'm guessing the python code is just wrong, but couldn't get it working with some other slight variations...

Any help is welcome here ! Thanks in advance.

toxic0berliner avatar Oct 11 '22 08:10 toxic0berliner

I think your problem is that you want to not bypass the login stage. I am not at an authentik environment so I cannot confirm that. You could try these other options as well:

  1. If you don't need the user to be logged in and just need them to bypass the flow, change the flow Deny action to CONTINUE, and assign that policy to the flow instead of individual stages. I don't think you will be logged in since there is no user login stage even with pending_user set, though that may be what you want. You can also redirect from the flow policy instead of relying on app URLs with CONTINUE. However not being logged in may result in you being redirected to authentik for every page navigation. You might be able to just log in the user via the policy but I am not sure how to do that off the top of my head.
  2. Add a login stage as the first stage in the flow, and apply that policy only to that stage. Redirect to the desired URL after the login. Users that do not match that policy will ignore the login stage and go through the normal flow.
  3. Assign the policy to the flow instead of individual stages and set the login stage at the end as the next stage. Note: I'm not sure off the top of my head if pending_user is retained when set in stage context outside of user login stages. As of 2022.7, stage context (request.context, or just context) is copied from flow context and should be considered read-only (this is not true in practice but it is an irrelevant implementation detail beside this conversation). Because of this I believe you need to be setting request.context['flow_plan'].context['pending_user'], unless you are setting it on a user login stage in which I believe request.context['pending_user'] should be valid...

sevmonster avatar Oct 12 '22 14:10 sevmonster

Thanks @sevmonster for the insight. I believe I'm closer but still not there yet. I still believe the issue is writhing the pending user at the proper place. I even tried to write context['user'] but seems you protected it somehow and I get an arror for "inbuilt keys" or something like this.

I would realy like in fact to have the user logged in as a specific user I created for it, called it "lanuser", this way applications behind it are potentially able to use the lanuser id in the header to auth users directly.

I am vey unclear as to what the "redirect" should be to. For instance, User can access foo.example.com or bar.example.com, both will be behind traefik with an authentik middleware, and therefore be redirected to auth.example.com to first authenticate. auth.example.com is also behind the same traefik authentik middleware but for an unauthenticated url has been setup for this url, so it gets through to the default flow below :

image

This is what is in the ip-lan-policy :

ak_logger.info("TOXIC entering ip-lan-policy")
condition = ak_client_ip in ip_network('10.0.0.0/8')
if condition:
    ak_logger.debug("TOXIC condition is TRUE: user is a lan USER")
    if not ('flow_plan' in request.context):
        ak_logger.error("TOXIC No flow_plan key in request.context")
    else:
        if not ('pending_user' in request.context['flow_plan'].context):
            ak_logger.info("TOXIC setting pending_user to lanuser as it is empty in request.context['flow_plan'].context")
            request.context['flow_plan'].context['pending_user']='lanuser'
        if not ('pending_user' in request.context):
            ak_logger.info("TOXIC setting pending_user to lanuser as it is empty in request.context")
            request.context['pending_user']='lanuser'
    ak_logger.info("TOXIC pending user is "+context["pending_user"])
else:
    ak_logger.debug("TOXIC condition is FALSE: user is NOT a lan user")
ak_logger.warning("TOXIC returning from ip-lan-policy")
return condition

Sadly, in the logs later I still see this error :

{
	"auth_via": "unauthenticated",
	"event": "sending event to sentry",
	"exc": "AttributeError(\"'str' object has no attribute 'is_active'\")",
	"host": "auth.example.com",
	"level": "debug",
	"logger": "authentik.lib.sentry",
	"pid": 23,
	"request_id": "79c6b32bbe5b4f819d95befd841d26a3",
	"source_logger": null,
	"timestamp": "2022-10-15T14:31:49.009208"
}

Most probably because a few lines abofe I have still an unauthenticated user in the context :

Click to unfold and see `"username": "AnonymousUser"` at the bottom...
{
	"action": "policy_execution",
	"auth_via": "unauthenticated",
	"client_ip": "10.0.30.109",
	"context": {
		"binding": {
			"app": "authentik_policies",
			"model_name": "policybinding",
			"name": "Binding from Flow-stage binding #5 to 1fd3acc3-5f7a-4f8a-929b-bccc8836915f #1 to Policy policy-ip-lan",
			"pk": "dce68af061774fea8617d88ed820c688"
		},
		"http_request": {
			"args": {
				"query": "next=%2Fapplication%2Fo%2Fauthorize%2F%3Fclient_id%3Dnxj2OzUhgwKG2qgfIYTXcfIBUl5u3zOJX9ZtFAvs%26redirect_uri%3Dhttps%253A%252F%252Fauth.example.com%252Foutpost.goauthentik.io%252Fcallback%253FX-authentik-auth-callback%253Dtrue%26response_type%3Dcode%26scope%3Dopenid%2Bprofile%2Bemail%2Bak_proxy%26state%3D8JXGC1gOPTU0pqnNH0m096_TivitJ7aPeCCulfGyxdk"
			},
			"method": "GET",
			"path": "/api/v3/flows/executor/default-authentication-flow/"
		},
		"message": "Policy Execution",
		"policy_uuid": "9f29e9d612f4424ab8c61da89440d879",
		"request": {
			"context": {
				"flow_plan": {
					"bindings": [
						{
							"app": "authentik_flows",
							"model_name": "flowstagebinding",
							"name": "Flow-stage binding #5 to 1fd3acc3-5f7a-4f8a-929b-bccc8836915f",
							"pk": "44fdb88f893d4f9888abb5847366a495"
						},
						{
							"app": "authentik_flows",
							"model_name": "flowstagebinding",
							"name": "Flow-stage binding #10 to 1fd3acc3-5f7a-4f8a-929b-bccc8836915f",
							"pk": "71dd3e218ac5468cad977e0076aa0d75"
						},
						{
							"app": "authentik_flows",
							"model_name": "flowstagebinding",
							"name": "Flow-stage binding #20 to 1fd3acc3-5f7a-4f8a-929b-bccc8836915f",
							"pk": "fa2b739628fa496f990e443e5233eb3a"
						},
						{
							"app": "authentik_flows",
							"model_name": "flowstagebinding",
							"name": "Flow-stage binding #30 to 1fd3acc3-5f7a-4f8a-929b-bccc8836915f",
							"pk": "41a54c333e3245d3951f22eecd40597c"
						},
						{
							"app": "authentik_flows",
							"model_name": "flowstagebinding",
							"name": "Flow-stage binding #100 to 1fd3acc3-5f7a-4f8a-929b-bccc8836915f",
							"pk": "ec5e30ffefe0489890623dce0477b1dc"
						}
					],
					"context": {
						"pending_user": "lanuser"
					},
					"flow_pk": "1fd3acc35f7a4f8a929bbccc8836915f",
					"markers": [
						{
							"binding": {
								"app": "authentik_flows",
								"model_name": "flowstagebinding",
								"name": "Flow-stage binding #5 to 1fd3acc3-5f7a-4f8a-929b-bccc8836915f",
								"pk": "44fdb88f893d4f9888abb5847366a495"
							}
						},
						{
							"binding": {
								"app": "authentik_flows",
								"model_name": "flowstagebinding",
								"name": "Flow-stage binding #10 to 1fd3acc3-5f7a-4f8a-929b-bccc8836915f",
								"pk": "71dd3e218ac5468cad977e0076aa0d75"
							}
						},
						{
							"binding": {
								"app": "authentik_flows",
								"model_name": "flowstagebinding",
								"name": "Flow-stage binding #20 to 1fd3acc3-5f7a-4f8a-929b-bccc8836915f",
								"pk": "fa2b739628fa496f990e443e5233eb3a"
							}
						},
						{
							"binding": {
								"app": "authentik_flows",
								"model_name": "flowstagebinding",
								"name": "Flow-stage binding #30 to 1fd3acc3-5f7a-4f8a-929b-bccc8836915f",
								"pk": "41a54c333e3245d3951f22eecd40597c"
							}
						},
						{
							"binding": {
								"app": "authentik_flows",
								"model_name": "flowstagebinding",
								"name": "Flow-stage binding #100 to 1fd3acc3-5f7a-4f8a-929b-bccc8836915f",
								"pk": "ec5e30ffefe0489890623dce0477b1dc"
							}
						}
					]
				},
				"geoip": null,
				"pending_user": "lanuser"
			},
			"debug": false,
			"http_request": null,
			"obj": {
				"app": "authentik_flows",
				"model_name": "flowstagebinding",
				"name": "Flow-stage binding #5 to 1fd3acc3-5f7a-4f8a-929b-bccc8836915f",
				"pk": "44fdb88f893d4f9888abb5847366a495"
			},
			"user": {
				"email": "",
				"pk": 2,
				"username": "AnonymousUser"
			}
		},
		"result": {
			"log_messages": [],
			"messages": [],
			"passing": true,
			"source_binding": null,
			"source_results": []
		}
	},
	"event": "Created Event",
	"host": "auth.example.com",
	"level": "info",
	"logger": "authentik.events",
	"pid": 23,
	"request_id": "79c6b32bbe5b4f819d95befd841d26a3",
	"timestamp": "2022-10-15T14:31:48.978907",
	"user": {
		"email": "",
		"pk": 2,
		"username": "AnonymousUser"
	}
}

Just to see, I also tried using context['pending_user']='lanuser' instead of request.context['pending_user']='lanuser' but to no avail...

I know I'm missing the redirect anyway, but again, wher shall I redirect the user ? he might have come from foo.example.com or bar.example.com, I can only find this in ?next, shall I set the pending_user (how) and parse the next parameter to redirect him all in my python code for this lan-ip-policy ?

Thanks in advance for any help !

toxic0berliner avatar Oct 15 '22 14:10 toxic0berliner

request.context['pending_user'] cannot be set to a string, it has to be set to a user object. Try

request.context['pending_user'] = ak_user_by(username='my-user-name')

BeryJu avatar Oct 15 '22 14:10 BeryJu

That helps a lot, thanks @BeryJu !!!

Now I'm still not being logged in, getting "Unknown error" on the GUI, and this in the logs :

{"auth_via": "unauthenticated", "event": "No Pending user to login.", "host": "auth.example.com", "level": "debug", "logger": "authentik.flows.stage", "pid": 7634, "request_id": "f90ed9590c784332b05a6c3be1d6fe29", "stage": "lan-login-stage", "stage_view": "authentik.stages.user_login.stage.UserLoginStageView", "timestamp": "2022-10-15T15:14:28.033740"}

{"auth_via": "unauthenticated", "event": "f(exec): Stage invalid", "flow_slug": "default-authentication-flow", "host": "auth.example.com", "level": "debug", "logger": "authentik.flows.views.executor", "pid": 7634, "request_id": "f90ed9590c784332b05a6c3be1d6fe29", "timestamp": "2022-10-15T15:14:28.034007"}

A few log lines above, I do see the pending_user set with it's pk and email and everything in the context... but still when that gets logged the user object is the unauthenticated one...

I'm guessing that continuing the default flow with a pending user set isn't making authentik happy, but I can't find a way to tell authentik that after this lan-login-stage it should go directly to the end...

toxic0berliner avatar Oct 15 '22 15:10 toxic0berliner

I even tried adding a redirect to the foo.example.com as a policy on the next stage, enclosed in the same if user is on local IP, but it seems never to get called...

toxic0berliner avatar Oct 15 '22 18:10 toxic0berliner

Any other hint?

toxic0berliner avatar Oct 24 '22 10:10 toxic0berliner

I'm still unable to autologin a user from the local network, I do see that something is happening because authentication is not possible at all from LAN network while working nice from WAN ip, but I'm stuck... Last Idea I have is create a separate flow to autologin and maybe find a way in a policy to chenge to another flow ?

toxic0berliner avatar Nov 09 '22 08:11 toxic0berliner

I gave up, switched to authelia who does way less and less polished but at least I can have an easy auth bypass when on my LAN.

toxic0berliner avatar Nov 11 '22 08:11 toxic0berliner

This would be a great feature if possible. I've tried multiple suggestions in this thread, but it does not work.

Ideally we could specify a service account so Authentik is happy, and then go to the end of the flow.

MrLemur avatar Nov 17 '22 21:11 MrLemur

@toxic0berliner Sorry you weren't able to get it to work, I would have continued to help but I was occupied by other things. I too tried Authelia back in the day, but it was missing features I needed. Different tools for different jobs.

@MrLemur If you can provide any more information than "it does not work", like your specific software and use case and what exactly you have tried, there is probably a reason why it doesn't work that can be resolved.

I still personally think this is best resolved from the reverse proxy level (and I do this with my Nginx configuration actually), which may be why it has not seen more activity, but I can see a reason for it to be set up in authentik if there is some special headers or logic being configured there that are not otherwise being touched from the reverse proxy, such as login user; this does prevent duplication of work.

sevmonster avatar Mar 02 '23 22:03 sevmonster

@toxic0berliner Sorry you weren't able to get it to work, I would have continued to help but I was occupied by other things. I too tried Authelia back in the day, but it was missing features I needed. Different tools for different jobs.

@MrLemur If you can provide any more information than "it does not work", like your specific software and use case and what exactly you have tried, there is probably a reason why it doesn't work that can be resolved.

I still personally think this is best resolved from the reverse proxy level (and I do this with my Nginx configuration actually), which may be why it has not seen more activity, but I can see a reason for it to be set up in authentik if there is some special headers or logic being configured there that are not otherwise being touched from the reverse proxy, such as login user; this does prevent duplication of work.

Wow forgot about this! I ended up moving back to Authelia.

I got a workaround using some Python to set a "lanuser" for requests from certain IPs, but this did not work with apps that would not follow the Authentik Javascript redirect.

MrLemur avatar Mar 02 '23 22:03 MrLemur

Also switched to authelia here. I'm hoping authelia moves to add some stuff I would have liked from authentik especially having a GUI to set it up... I'm also pretty sure the "JavaScript redirect" would kill authentik for me too with various services integrating with API calls... Even if I got service account auto login to work based on source IP...

toxic0berliner avatar Mar 02 '23 22:03 toxic0berliner

I'm also pretty sure the "JavaScript redirect" would kill authentik for me too with various services integrating with API calls... Even if I got service account auto login to work based on source IP...

Yeah like I originally mentioned, authentik is mostly aimed at a user-interactive login experience. It does have an API but I have not done anything with that so I can't comment on it, I believe you can automate entire flows with it with a few restrictions. But I see that as overkill with other options being available. Personally I would never use authentik to protect an API, it's just not built for it. I use Nginx with static Bearer tokens for that instead.

sevmonster avatar Mar 02 '23 22:03 sevmonster

Hi. I have the same problem with bypass auth with local client ips. @sevmonster can you explain how to configure npm to bypass authentik login with local ip? i have actually a dns redirect in pi-hole to test all services before going "live".

thank you for your help. Toxo

Toxo666 avatar May 23 '23 19:05 Toxo666

I had the same problem after switching from Authelia. For those using nginx (or NPM), you can use this settings to bypass authentication based on IP address:

satisfy any;
allow 192.168.X.X/24;
deny all;

Put these lines (adjusted to your LAN address) before the auth_request for Authentik for those apps you don't need authorization at all in your LAN.

alceasan avatar Oct 09 '23 13:10 alceasan

Bypassing authentication from within authentik itself is not possible due to a valid user being required for all actions. It is possible to write a policy that detects login requests for a specified subnet and always attaches a generic user to them. With proxy providers it is also possible to bypass auth for certain subnets as seen above.

This is not something that will be natively supported in authentik due to the security implications and how fundamental of a change it would be

BeryJu avatar Apr 04 '24 16:04 BeryJu

Last I tried even the policy assigning a user for a subnet isn't working for APIs as it'll still rely on a JavaScript redirect... Other apps are filling my need but I still find sad this is getting closed and never will happen to have a real bypass possible at least on the forward-auth endpoint...

toxic0berliner avatar Apr 04 '24 17:04 toxic0berliner

The proper solution to this really is at the reverse proxy or application level. authentik is a user authentication (hence the name) framework and is not really designed, from a fundamental level, to protect APIs, provide non-interactive logins, bypass authentication, perform complex routing, or anything similar.

Implementing basic authentication and routing for APIs is an incredibly simple task with almost any reverse proxy. I do it with Nginx, RoboMagus got it working with Traefik. (N.B. I don't use Traefik, so I can't comment on anyone else's use of it.) In particular, Nginx has ngx_http_lua_module which can dynamically reach out to other endpoints and provide advanced routing functionality that could be adequately used for API authentication and routing. Even a simple PHP script would be able to do the same, either managed inside your reverse proxy (e.g. Nginx auth_request) or using CGI and redirects. You could easily query endpoints dynamically to authenticate on behalf of the request (perhaps even using authentik's API) or generate tokens yourself on the fly there. authentik really just isn't a good fit for this and most of its infrastructure is not built for it.

Of course, it should still be possible to massage any product into doing what you want, given enough elbow grease. Setting pending_user really should have worked, so there may have been some other issue at play. Perhaps due to context being split out to provide a top level read-only context for the current state of the flow, with the actual flow context being available in another object. But if everything was done correctly, and it still wasn't working right, then that could have been a bug. Not sure as I never had a use for this flow and never looked into it too deeply.

sevmonster avatar Apr 06 '24 23:04 sevmonster