{"id":182,"date":"2026-03-10T19:40:59","date_gmt":"2026-03-10T19:40:59","guid":{"rendered":"https:\/\/buunk.org\/?p=182"},"modified":"2026-03-10T20:00:45","modified_gmt":"2026-03-10T20:00:45","slug":"enterprise-style-wi-fi-at-home-freeradius-ldap-and-group-based-access","status":"publish","type":"post","link":"https:\/\/buunk.org\/?p=182","title":{"rendered":"Enterprise-Style Wi-Fi at Home: FreeRADIUS, LDAP, and Group-Based Access"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For a while, I\u2019ve wanted to set up a solid Wi-Fi network for devices in my homelab. Instead of just using a simple access point with a shared Wi-Fi password, I decided to go all the way: I implemented per-user authentication with FreeRADIUS and LDAP, giving me fine-grained control over who can connect and what they can access.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large is-resized is-style-default\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/buunk.org\/wp-content\/uploads\/2026\/03\/IMG_4084-1024x576.jpeg\" alt=\"\" class=\"wp-image-189\" style=\"aspect-ratio:1.7777244777189098;object-fit:cover;width:700px\" srcset=\"https:\/\/buunk.org\/wp-content\/uploads\/2026\/03\/IMG_4084-1024x576.jpeg 1024w, https:\/\/buunk.org\/wp-content\/uploads\/2026\/03\/IMG_4084-300x169.jpeg 300w, https:\/\/buunk.org\/wp-content\/uploads\/2026\/03\/IMG_4084-768x432.jpeg 768w, https:\/\/buunk.org\/wp-content\/uploads\/2026\/03\/IMG_4084-1536x864.jpeg 1536w, https:\/\/buunk.org\/wp-content\/uploads\/2026\/03\/IMG_4084-2048x1152.jpeg 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">user + password login on windows 11<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The RADIUS server itself runs in an LXC container on my production cluster, making it fully integrated with my infrastructure. This setup ensures it\u2019s always on, highly available, and easy to maintain. I also ensured that the container has access to my existing LDAP directory (<code>ou=people,dc=buunk,dc=org<\/code>), so it can verify user credentials and check group membership.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The Archer AX55 is used purely as an access point. It points to my main OPNsense router, which handles DHCP, routing, and forwards RADIUS requests to the FreeRADIUS container. Seeing a client connect for the first time is satisfying: FreeRADIUS looks up the user in LDAP, verifies the password, checks that the user is in the <code>wifi-users<\/code> group, and grants access.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"725\" height=\"561\" src=\"https:\/\/buunk.org\/wp-content\/uploads\/2026\/03\/image-1.png\" alt=\"\" class=\"wp-image-184\" style=\"width:700px\" srcset=\"https:\/\/buunk.org\/wp-content\/uploads\/2026\/03\/image-1.png 725w, https:\/\/buunk.org\/wp-content\/uploads\/2026\/03\/image-1-300x232.png 300w\" sizes=\"auto, (max-width: 725px) 100vw, 725px\" \/><figcaption class=\"wp-element-caption\">First time setting up a connection using my ldap user on my laptop<\/figcaption><\/figure>\n\n\n\n<pre class=\"wp-block-code has-background\" style=\"background-color:#282525;font-size:0.7rem\"><code>ldap: User object found at DN \"uid=jurre,ou=people,dc=buunk,dc=org\"\nldap: Processing memberOf value \"cn=wifi-users,ou=groups,dc=buunk,dc=org\" as a DN\nif (Ldap-Group == \"cn=wifi-users,ou=groups,dc=buunk,dc=org\") -&gt; TRUE\nReply-Message := \"User is in wifi-users group\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The group check policy is particularly neat. FreeRADIUS evaluates the <code>check_group<\/code> policy to ensure the user belongs to <code>wifi-users<\/code>. This allows me to separate access based on LDAP groups and makes it easy to extend later\u2014for example, to assign VLANs or enforce more granular policies. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here\u2019s a snippet from that policy:<\/p>\n\n\n\n<pre class=\"wp-block-code has-background\" style=\"background-color:#282525\"><code>policy check_group {<br>    if (Ldap-Group == \"cn=wifi-users,ou=groups,dc=buunk,dc=org\") {<br>        update reply {<br>            Reply-Message := \"User is in wifi-users group\"<br>        }<br>    }<br>}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Clients connect via the AX55, which is configured for WPA2 Enterprise. The OPNsense router handles DHCP and dynamic DNS, and I can watch the logs as authentication occurs. The EAP-PEAP tunnel is established, credentials are verified, and access is granted all while keeping passwords secure.<\/p>\n\n\n\n<pre class=\"wp-block-code has-background\" style=\"background-color:#282525\"><code>eap_peap: Session established. Decoding tunneled attributes<br>eap_peap: Success<br>eap_peap: Using saved attributes from the original Access-Accept<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Overall, this project gave my homelab Wi-Fi access that feels enterprise-grade. By combining my production cluster LXC container running FreeRADIUS, LDAP for user management, OPNsense as the main router, and the Archer AX55 as a managed access point, I now have a secure, auditable, and flexible network.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1536\" height=\"864\" src=\"https:\/\/buunk.org\/wp-content\/uploads\/2026\/03\/image-2-edited.png\" alt=\"\" class=\"wp-image-186\" style=\"width:700px\" srcset=\"https:\/\/buunk.org\/wp-content\/uploads\/2026\/03\/image-2-edited.png 1536w, https:\/\/buunk.org\/wp-content\/uploads\/2026\/03\/image-2-edited-300x169.png 300w, https:\/\/buunk.org\/wp-content\/uploads\/2026\/03\/image-2-edited-1024x576.png 1024w, https:\/\/buunk.org\/wp-content\/uploads\/2026\/03\/image-2-edited-768x432.png 768w\" sizes=\"auto, (max-width: 1536px) 100vw, 1536px\" \/><figcaption class=\"wp-element-caption\">Network authentication flow diagram<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Next, I plan to experiment with dynamic VLAN assignment based on group membership. This will give me more control over what guest accounts and IoT devices can access. For now, it\u2019s already incredibly satisfying to see per-user authentication working perfectly in my home lab environment.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you are interested in the technologies used to build this network, here are some links for more information:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>FreeRADIUS<\/strong> \u2013 RADIUS server for secure authentication: <a href=\"https:\/\/freeradius.org\">https:\/\/freeradius.org<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>LLDAP<\/strong> \u2013 Lightweight LDAP for user and group management: <a href=\"https:\/\/github.com\/lldap\/lldap\">https:\/\/github.com\/lldap\/lldap<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>OPNsense<\/strong> \u2013 Firewall and router: <a href=\"https:\/\/opnsense.org\">https:\/\/opnsense.org<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Archer AX55<\/strong> \u2013 Wi-Fi 6 access point: <a href=\"https:\/\/www.tp-link.com\/en\/home-networking\/wifi-router\/archer-ax55\/\">https:\/\/www.tp-link.com\/en\/home-networking\/wifi-router\/archer-ax55\/<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>For a while, I\u2019ve wanted to set up a solid Wi-Fi network for devices in my homelab. Instead of just using a simple access point with a shared Wi-Fi password, I decided to go all the way: I implemented per-user authentication with FreeRADIUS and LDAP, giving me fine-grained control over who can connect and what [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":189,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-182","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/buunk.org\/index.php?rest_route=\/wp\/v2\/posts\/182","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/buunk.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/buunk.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/buunk.org\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/buunk.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=182"}],"version-history":[{"count":6,"href":"https:\/\/buunk.org\/index.php?rest_route=\/wp\/v2\/posts\/182\/revisions"}],"predecessor-version":[{"id":204,"href":"https:\/\/buunk.org\/index.php?rest_route=\/wp\/v2\/posts\/182\/revisions\/204"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/buunk.org\/index.php?rest_route=\/wp\/v2\/media\/189"}],"wp:attachment":[{"href":"https:\/\/buunk.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=182"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/buunk.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=182"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/buunk.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=182"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}