Authentik with Source IP Auth Modifications

2024-11-06

Introduction

I run Authentik in my homelab for SSO. It’s actually super handy to have one set of credentials that can be used to auth to pretty much everything I host. Once I started sharing services with my partner, and then later yet with friends and family, it’s so nice to say “oh, you want access to my NetBox instance? Granted, use the same creds you use for all my services!” It also supports (although I haven’t enabled) federating auth with a source such as Google Accounts, meaning external users wouldn’t even need separate creds for my services at all. That being said, pretty much everyone who does have access is handy with a password manager so what’s one more password.

One thing I wanted to do with Authentik is have it prompt for MFA credentials (Passkey or TOTP), but only when accessed from the Internet. In other words, if I’m sitting at home, I don’t need to provide MFA creds when logging in, but I do if I’m out and about on my phone.

I laid the groundwork for this in Getting real IPs from behind ingress. After doing that, Authentik could see the real IP address of clients.

Implementation

Policy

Now, to actually wire this up in Authentik, start by going to Customization > Policies and Create a policy of type Expression Policy. Give it a name (I chose Is Internal). Then, for the expression, paste in:

internal_nets = [
    "10.0.3.0/24",
    "10.0.10.0/24",
    "10.0.11.0/24",
    "10.0.30.0/24"
]

return any([ak_client_ip in ip_network(net) for net in internal_nets])

ak_client_ip is a policy variable populated by Authentik that returns the client’s real IP address. If for some reason it can’t determine the client’s IP, it returns 255.255.255.255.

From there, it’s just Python (plus the ipaddress module from the stdlib). We check if the client’s IP is within any of the subnets we define as “internal”.

Now, hit Finish.

Binding

Now that the policy exists, we need to wire it up (which is called “binding” in Authentik). Go to Flows and Stages > Flows. Now, you need to find the Authentication flow that you’re using. If you haven’t changed any defaults in this regard, it’s most likely default-authentication-flow. If in doubt, pop an incognito window and go to your Authentik instance. The flow name will be in the URL path after /if/flow/.

Click the flow name to be brought into the flow overview page. Click the Stage Bindings tab. In here, click the > next to default-authentication-mfa-validation (or your MFA stage). Then, click Bind existing policy/group/user. Choose the policy we created in the previous section. Check Negate result1, and hit Create.


Now, go test it out! You should be able to log in from an incognito window on a machine in your internal network without getting prompted for MFA, but you should be prompted if you, say, disconnect your phone from wifi and login using mobile data.


  1. We negate the result because we want the policy check to return true (and thus cause the stage to run) if the client IP is not internal. ↩︎

Enter your instance's address