|
|
@ -0,0 +1,633 @@
|
|
|
|
|
|
|
|
A PoC Guide To Authenticating WiFi Clients Using Kerberos
|
|
|
|
|
|
|
|
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
:language: en
|
|
|
|
|
|
|
|
:date: 2023-02-11
|
|
|
|
|
|
|
|
:tags: poc, network, guide
|
|
|
|
|
|
|
|
:slug: kerberos-wifi-poc-rst
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.. FIXME: remove "-rst" from slug
|
|
|
|
|
|
|
|
TODO: I hate myself for letting this indent using spaces.
|
|
|
|
|
|
|
|
FIXME: - links with ``…``
|
|
|
|
|
|
|
|
- multiline links
|
|
|
|
|
|
|
|
- anchors
|
|
|
|
|
|
|
|
- footnotes
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
I have been recently helping someone setting up a WiFi which would authenticate
|
|
|
|
|
|
|
|
users using a centrally deployed Kerberos. It turned out to be a bit painful
|
|
|
|
|
|
|
|
with lots of dead ends, so this is meant to save time anyone reading this.[^If
|
|
|
|
|
|
|
|
anyone can even find this post, that is…]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.. TODO: video link
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Disclaimer: I definitely cannot claim in-depth understanding of what is
|
|
|
|
|
|
|
|
happening. Please take anything here with a grain of salt, I might be wrong.
|
|
|
|
|
|
|
|
Also, as the title says, this is just a PoC setup. It works, but is very much
|
|
|
|
|
|
|
|
hacked together, not nice, probably not secure, etc. I discuss security
|
|
|
|
|
|
|
|
improvements at the end of the post.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
I also do not inherently say that you need all the steps written below. I only
|
|
|
|
|
|
|
|
document, what ended up working for me in the end, so there might be some
|
|
|
|
|
|
|
|
extraneous settings that are not really used.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
While this post contains a few storytelling parts and feels like being written
|
|
|
|
|
|
|
|
chronologically, it was written post hoc and occasionally explains stuff I did
|
|
|
|
|
|
|
|
not know when experimenting with this. I tried to help the reader undestand
|
|
|
|
|
|
|
|
what is happening more than to describe how I got there.[^Also, I pretty much
|
|
|
|
|
|
|
|
just randomly searched the web and tried snippets, the important part is how I
|
|
|
|
|
|
|
|
eventually understood what I had done.]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
In other words, this should be understood as "RADIUS and Kerberos setup for the
|
|
|
|
|
|
|
|
impatient by an impatient". I didn't find anything, so I wrote it myself.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
What we want in the end
|
|
|
|
|
|
|
|
=======================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Obviously, the goal is to have some WiFi end devices[^I will try to avoid the
|
|
|
|
|
|
|
|
word "client" in order not to confuse it with a RADIUS client, which is the
|
|
|
|
|
|
|
|
WiFi access point.]. They connect to an access point (AP) and authenticate with
|
|
|
|
|
|
|
|
their username and password using IEEE 802.1X (a.k.a WPA-Enterprise). The
|
|
|
|
|
|
|
|
credentials then get verified using the central Kerberos, and if they are
|
|
|
|
|
|
|
|
correct, the end device is allowed to use the network.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The 802.1X standard (or maybe only its WPA variation) uses authentication using
|
|
|
|
|
|
|
|
RADIUS, which is unfortunately different from Kerberos, so there is a RADIUS
|
|
|
|
|
|
|
|
server in the middle which forwards the credentials to Kerberos. (This has an
|
|
|
|
|
|
|
|
unfortunate security deficiency of RADIUS server handling cleartext
|
|
|
|
|
|
|
|
passwords).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.. TODO: overall architecture scheme
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
I do not dive into the rest of the network configuration like IP addressing
|
|
|
|
|
|
|
|
scheme and assignment, DNS etc. While in my testing a DHCP setup was involved,
|
|
|
|
|
|
|
|
it is out of scope for this post.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Terminology
|
|
|
|
|
|
|
|
-----------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.. TODO: radius server, what secrets do we use, …, what authentication/links are between the components.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Technologies used
|
|
|
|
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- A laptop with a WiFi adapter which supports AP mode (look for "Supported
|
|
|
|
|
|
|
|
interface modes" in ``iw phy``)
|
|
|
|
|
|
|
|
- Arch Linux (various versions), Debian bullseye
|
|
|
|
|
|
|
|
- hostapd for AP serving
|
|
|
|
|
|
|
|
- FreeRADIUS for RADIUS server
|
|
|
|
|
|
|
|
- MIT Kerberos (krb5) as a test kerberos setup
|
|
|
|
|
|
|
|
- Random WiFi devices
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
In my PoC deployment most of this runs on localhost, but it should not be hard
|
|
|
|
|
|
|
|
to distribute services to different machines.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.. this feels so wrong…
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.. _Setup:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A note about setup and testing
|
|
|
|
|
|
|
|
--------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
I decided to migrate to VM halfway through, when I realized I would need to
|
|
|
|
|
|
|
|
deploy my own Kerberos realm. I had the hostapd + cleartext in RADIUS working
|
|
|
|
|
|
|
|
on bare metal Arch and tested with a few devices, so that seemed trustworthy
|
|
|
|
|
|
|
|
enough to do the rest of the testing only using ``radtest`` against FreeRADIUS
|
|
|
|
|
|
|
|
and either cleartext passwords or Kerberos in the VM.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
I first set it up on Arch, but it did not work, so I tried whether Debian would
|
|
|
|
|
|
|
|
have more-working configuration out-of-box. It didn't, but now I know the
|
|
|
|
|
|
|
|
differences between the systems. So the final deployment is with a Debian VM.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
In the end, I ended up pointing the bare-metal hostapd on Arch to the
|
|
|
|
|
|
|
|
virtualized RADIUS server to test it end-to-end.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
I am stripping my IP addresses, both for security reasons and because my
|
|
|
|
|
|
|
|
network setup is actually more complicated than this, so it would be confusing.
|
|
|
|
|
|
|
|
If you can run everything on localhost (either bare-metal, or with some kind of
|
|
|
|
|
|
|
|
passing your WiFi adapter to the VM), I believe it should run fine.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Hostapd: The easy part of the setup
|
|
|
|
|
|
|
|
===================================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Installing hostapd is simple, it is probably just ``hostapd`` in the
|
|
|
|
|
|
|
|
repositories. (Yes, one package even for Debian…)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Setting up hostapd is quite straight forward: set WiFi SSID and let it
|
|
|
|
|
|
|
|
forward request to the RADIUS server. Its `default configuration
|
|
|
|
|
|
|
|
file <http://w1.fi/cgit/hostap/tree/hostapd/hostapd.conf>`__ is quite well
|
|
|
|
|
|
|
|
documented in comments, my only changes to ``hostapd.conf``. are::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ssid=testX
|
|
|
|
|
|
|
|
auth_algs=3
|
|
|
|
|
|
|
|
ieee8021x=1
|
|
|
|
|
|
|
|
eapol_version=2
|
|
|
|
|
|
|
|
eap_message=hello
|
|
|
|
|
|
|
|
wpa=2
|
|
|
|
|
|
|
|
wpa_key_mgmt=WPA-EAP
|
|
|
|
|
|
|
|
wpa_pairwise=TKIP CCMP
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# RADIUS config
|
|
|
|
|
|
|
|
auth_server_addr=127.0.0.1
|
|
|
|
|
|
|
|
auth_server_port=1812
|
|
|
|
|
|
|
|
auth_server_shared_secret=testing123
|
|
|
|
|
|
|
|
# Optionally also set acct_server_{addr,port,shared_secret}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Arch has also changed some paths compared to the official tarball, but that
|
|
|
|
|
|
|
|
seems unimportant.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Side note: Authenticating against hostapd directly
|
|
|
|
|
|
|
|
--------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Hostapd is actually capable of performing basic authentication itself. While it
|
|
|
|
|
|
|
|
AFAIK cannot forward any credentials to other services, for basic testing this
|
|
|
|
|
|
|
|
is sufficient.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The simpler option is classic pre-shared key::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auth_algs=1
|
|
|
|
|
|
|
|
wpa_key_mgmt=WPA-PSK
|
|
|
|
|
|
|
|
wpa_passphrase=TheVerySecurePreSharedKey
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
If we want to authenticate by name and password, it can also do that. Instead
|
|
|
|
|
|
|
|
of setting ``auth_server_*`` settings, generate a CA key, a private key and a
|
|
|
|
|
|
|
|
certificate for it (so that the CA certifies the server certificate), set paths
|
|
|
|
|
|
|
|
and direct hostapd to the user file::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# This should probably be different file. I have no idea what I did and whether this is any secure. PoC stuff :-)
|
|
|
|
|
|
|
|
ca_cert=/etc/hostapd/server.pem
|
|
|
|
|
|
|
|
server_cert=/etc/hostapd/server.pem
|
|
|
|
|
|
|
|
private_key=/etc/hostapd/server.key
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
eap_user_file=/etc/hostapd/hostapd.eap_user
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The key generation is out of scope for this post, mostly because I struggled
|
|
|
|
|
|
|
|
with it too much and then stole the keys from the FreeRADIUS anyway. You can
|
|
|
|
|
|
|
|
try running various commands from
|
|
|
|
|
|
|
|
`ArchWiki <https://wiki.archlinux.org/title/OpenSSL#Usage>`__ and maybe be more
|
|
|
|
|
|
|
|
successful.[^Learning OpenSSL syntax seemed like too much work to me, even
|
|
|
|
|
|
|
|
though it is quite useful :-)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The
|
|
|
|
|
|
|
|
```hostapd.eap_user`` <http://w1.fi/cgit/hostap/tree/hostapd/hostapd.eap_user>`__
|
|
|
|
|
|
|
|
file contains some pre-defined credentials, they probably work, I do not know
|
|
|
|
|
|
|
|
half the authentication mechanisms. For authenticating as ``hello:world`` I ended
|
|
|
|
|
|
|
|
up with this (I found this somewhere on the internet, sorry, I cannot probably find source)::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"hello" PEAP [ver=0]
|
|
|
|
|
|
|
|
"hello" MSCHAPV2 "world" [2]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
At this point, the WiFi should be running, visible and it should be possible to connect.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FreeRADIUS basic deployment
|
|
|
|
|
|
|
|
===========================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Installing the base of FreeRADIUS is also simple, in both distributions it is
|
|
|
|
|
|
|
|
sufficient to install the ``freeradius`` package and it pulls any dependencies.
|
|
|
|
|
|
|
|
You also need to bootstrap certificates, which Debian does during the
|
|
|
|
|
|
|
|
installation, on Arch you need to run the ``/etc/raddb/certs/bootstrap`` script
|
|
|
|
|
|
|
|
yourself (which is about `the only thing ArchWiki tells
|
|
|
|
|
|
|
|
you <https://wiki.archlinux.org/title/FreeRADIUS>`__).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
You could now run the bundled systemd service (and on Debian it runs by
|
|
|
|
|
|
|
|
default), but for debugging it is
|
|
|
|
|
|
|
|
`recommended <https://wiki.freeradius.org/guide/Getting-Started>`__ to run
|
|
|
|
|
|
|
|
``radiusd -X`` in console and see its output. (Also see `Caveat 1 <Caveats_>`__.)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
To test RADIUS, you can just follow the `Getting
|
|
|
|
|
|
|
|
Started <https://wiki.freeradius.org/guide/Getting-Started>`__ guide, but I'll try
|
|
|
|
|
|
|
|
to sum it up: First, add your user in ``/etc/raddb/users`` (I put it somewhere around
|
|
|
|
|
|
|
|
the line with the example "bob" user)::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
clear Cleartext-Password := "password"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Again, this file is quite heavily commented, which helps a bit.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Then we need to tell FreeRADIUS to allow connecting from the outside. The
|
|
|
|
|
|
|
|
default configuration in ``/etc/raddb/clients.conf`` probably has it uncommented::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
client localhost {
|
|
|
|
|
|
|
|
ipaddr = 127.0.0.1
|
|
|
|
|
|
|
|
secret = testing123
|
|
|
|
|
|
|
|
# Possibly other settings, but e.g. localhost_ipv6 does not have anything more configured, so this might be sufficient.
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Note that the secret is shared with the "client", i.e. the authenticator, hostapd for us.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
You can now test the setup with ``radtest clear password 127.0.0.1 0 testing123``
|
|
|
|
|
|
|
|
– it should end with the line "Received Access-Accept". (The "0" parameter
|
|
|
|
|
|
|
|
should be a "nas-port-number", zero worked for me… Given the comments for port
|
|
|
|
|
|
|
|
in ``/etc/raddb/clients.conf``, it might just look up the 1812 port in
|
|
|
|
|
|
|
|
``/etc/services``.)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
With this and hostapd setup, the ``clear:password`` credentials should also work for accessing the WiFi.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
What is happening
|
|
|
|
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
At this point, I think that it is reasonable to understand a few bits of
|
|
|
|
|
|
|
|
``radiusd -X`` output and configuration.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Configuration basics
|
|
|
|
|
|
|
|
````````````````````
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The configuration is split across multiple files, but really the main one is
|
|
|
|
|
|
|
|
just ``radiusd.conf``, which contains core configuration and then includes the
|
|
|
|
|
|
|
|
rest:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- The configuration of each "site" (instance) is in ``sites-enabled/``
|
|
|
|
|
|
|
|
- Each module has its configuration in ``mods-enabled/``, but for some of them
|
|
|
|
|
|
|
|
(e.g. files) it is split also to ``mods-config/``
|
|
|
|
|
|
|
|
- Known clients are defined in ``clients.conf``
|
|
|
|
|
|
|
|
- The ``users`` file is just a symlink to ``mods-config/files/authorize``
|
|
|
|
|
|
|
|
- I did not need to care for the other files included
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
How a user gets authenticated depends on the site configuration, and is
|
|
|
|
|
|
|
|
performed in several phases:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1. Authorize phase (and the ``authorize { }`` block) describes, which modules
|
|
|
|
|
|
|
|
should try matching the request. If the request matches for some module, it
|
|
|
|
|
|
|
|
gets an ``Auth-Type`` assigned, which seems to be an abstract way of
|
|
|
|
|
|
|
|
describing a method of authentication.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
If no module matches, FreeRADIUS falls back to rejecting the client. (I do
|
|
|
|
|
|
|
|
not know whether this can be changed.)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
In our case, we want ``files`` to try to match.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2. Depending on the Auth-Type, the relevant module from ``authenticate`` block is
|
|
|
|
|
|
|
|
used to perform the authentication.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Modules in both sections can return one of several results like ``ok``,
|
|
|
|
|
|
|
|
``notfound``, ``reject``, ``fail``, etc. (for example see
|
|
|
|
|
|
|
|
`rlm_krb5 <https://networkradius.com/doc/3.0.10/raddb/mods-available/krb5.html>`__
|
|
|
|
|
|
|
|
description).[^Sometimes, the modules are prefixed with ``rlm_``. As I understand it, the
|
|
|
|
|
|
|
|
module is really called like ``rlm_files`` and has a corresponding ``.so`` in
|
|
|
|
|
|
|
|
``/lib/freeradius``, but for most of the configuration the prefix is dropped.]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Some modules do not implement ``authorize`` section (rlm_krb5), some do not
|
|
|
|
|
|
|
|
implement ``authenticate`` (files). If I understand this correctly, some modules
|
|
|
|
|
|
|
|
cannot match requests and some cannot verify the passwords. The files module is
|
|
|
|
|
|
|
|
quite interesting in this manner, since it seems to only add the password as a
|
|
|
|
|
|
|
|
hint and leaves the verification itself to PAP (or maybe some other module
|
|
|
|
|
|
|
|
depending on authentication method, e.g. ``radtest -t …``)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
There are other sections relevant for proxying and accounting, I did not care
|
|
|
|
|
|
|
|
about those.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The configuration itself is written in "unlang" which seems to be a DSL to
|
|
|
|
|
|
|
|
describe the sequences outlined above.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
By default, there are two sites enabled: the default one, and an
|
|
|
|
|
|
|
|
"inner-tunnel". The latter is used for EAP and similar protocols, which tunnel
|
|
|
|
|
|
|
|
the request to another server. I think the configuration should be quite
|
|
|
|
|
|
|
|
similar for both sites, but it allows for differences.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The radiusd output
|
|
|
|
|
|
|
|
``````````````````
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
At the start, radiusd just reads its config and dumps it to console. The
|
|
|
|
|
|
|
|
semi-important parts of the dumped configuration:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- ``main`` → ``security`` contains user and group which FreeRADIUS setuid's into,
|
|
|
|
|
|
|
|
so it does not run as root.
|
|
|
|
|
|
|
|
- At one point, all the ``Auth-Type``'s are created.
|
|
|
|
|
|
|
|
- Sometime later, the ``files`` module gets loaded::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Loaded module rlm_files
|
|
|
|
|
|
|
|
# Loading module "files" from file /etc/freeradius/3.0/mods-enabled/files
|
|
|
|
|
|
|
|
files {
|
|
|
|
|
|
|
|
filename = "/etc/freeradius/3.0/mods-config/files/authorize"
|
|
|
|
|
|
|
|
acctusersfile = "/etc/freeradius/3.0/mods-config/files/accounting"
|
|
|
|
|
|
|
|
preproxy_usersfile = "/etc/freeradius/3.0/mods-config/files/pre-proxy"
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- And then the module gets "instantiated"::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Instantiating module "files" from file /etc/freeradius/3.0/mods-enabled/files
|
|
|
|
|
|
|
|
reading pairlist file /etc/freeradius/3.0/mods-config/files/authorize
|
|
|
|
|
|
|
|
reading pairlist file /etc/freeradius/3.0/mods-config/files/accounting
|
|
|
|
|
|
|
|
reading pairlist file /etc/freeradius/3.0/mods-config/files/pre-proxy
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
We will later see similar lines for the krb5 module, but it is not enabled
|
|
|
|
|
|
|
|
yet.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
After the initialization, FreeRADIUS is "Ready to process requests". The
|
|
|
|
|
|
|
|
handling of each request has its sequence number in parentheses at the start of
|
|
|
|
|
|
|
|
each line.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
When handling the request, we can read:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- The request itself::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(0) Received Access-Request Id 109 from 127.0.0.1:46187 to 127.0.0.1:1812 length 75
|
|
|
|
|
|
|
|
(0) User-Name = "clear"
|
|
|
|
|
|
|
|
(0) User-Password = "password"
|
|
|
|
|
|
|
|
(0) NAS-IP-Address = 127.0.1.1
|
|
|
|
|
|
|
|
(0) NAS-Port = 0
|
|
|
|
|
|
|
|
(0) Message-Authenticator = 0x01fc48c3e3a30442c018651db62ddcce
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- The processing of the ``authorize`` phase::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(0) # Executing section authorize from file /etc/freeradius/3.0/sites-enabled/default
|
|
|
|
|
|
|
|
…
|
|
|
|
|
|
|
|
(0) files: users: Matched entry clear at line 91
|
|
|
|
|
|
|
|
(0) [files] = ok
|
|
|
|
|
|
|
|
…
|
|
|
|
|
|
|
|
(0) [pap] = updated
|
|
|
|
|
|
|
|
(0) } # authorize = updated
|
|
|
|
|
|
|
|
(0) Found Auth-Type = PAP
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
We see that the ``Auth-Type`` got set to PAP (Password authentication protocol)
|
|
|
|
|
|
|
|
- The processing of ``authenticate`` (Why it is described as "group" I do not know)::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(0) # Executing group from file /etc/freeradius/3.0/sites-enabled/default
|
|
|
|
|
|
|
|
(0) Auth-Type PAP {
|
|
|
|
|
|
|
|
(0) pap: Login attempt with password
|
|
|
|
|
|
|
|
(0) pap: Comparing with "known good" Cleartext-Password
|
|
|
|
|
|
|
|
(0) pap: User authenticated successfully
|
|
|
|
|
|
|
|
(0) [pap] = ok
|
|
|
|
|
|
|
|
(0) } # Auth-Type PAP = ok
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Auth-Type was PAP, so the relevant section matched, the only module there matched the password and we are in.
|
|
|
|
|
|
|
|
- The response::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(0) Sent Access-Accept Id 109 from 127.0.0.1:1812 to 127.0.0.1:46187 length 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- Inbetween some sections which I did not need to care about.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
If the credentials in our setup are not correct, either the PAP rejects a bad
|
|
|
|
|
|
|
|
password, or files do not even match a bad name and an Auth-Type is not set.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Kerberos
|
|
|
|
|
|
|
|
========
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Here starts the fun and ugliness. I did not have any working Kerberos realm,
|
|
|
|
|
|
|
|
nor was in a position to persuade admins of one to trust my machine with
|
|
|
|
|
|
|
|
handling user's credentials. (I do not know, whether I really need to handle
|
|
|
|
|
|
|
|
the passwords in cleartext, but given how Kerberos tickets are created, I think
|
|
|
|
|
|
|
|
I do. And AFAIK EAP cannot tunnel Kerberos protocol.) So we end up deploying
|
|
|
|
|
|
|
|
our custom realm.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Installation: ``krb5`` package on Arch, ``krb5-kdc`` and ``krb5-admin-server`` on
|
|
|
|
|
|
|
|
Debian. Debian's installation scripts ask some questions and then generate the
|
|
|
|
|
|
|
|
config file, but I ended up following
|
|
|
|
|
|
|
|
`ArchWiki <https://wiki.archlinux.org/title/Kerberos>`__ anyway.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
My ``/etc/krb5.conf``::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[libdefaults]
|
|
|
|
|
|
|
|
default_realm = TEST.LEDOIAN.CZ
|
|
|
|
|
|
|
|
[realms]
|
|
|
|
|
|
|
|
TEST.LEDOIAN.CZ = {
|
|
|
|
|
|
|
|
admin_server = localhost
|
|
|
|
|
|
|
|
kdc = localhost
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
I left the definition of other realms in place in the Debian deployment, they should not interfere.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Creating the realm (with various prompts, just commands)::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# kdb5_util -r TEST.LEDOIAN.CZ create -s
|
|
|
|
|
|
|
|
# systemctl enable --now krb5-kdc krb5-kadmind
|
|
|
|
|
|
|
|
# kadmin.local
|
|
|
|
|
|
|
|
kadmin.local: addprinc krbuser@TEST.LEDOIAN.CZ
|
|
|
|
|
|
|
|
kadmin.local: addprinc -randkey host/localhost@TEST.LEDOIAN.CZ
|
|
|
|
|
|
|
|
kadmin.local: ktadd host/localhost@TEST.LEDOIAN.CZ
|
|
|
|
|
|
|
|
# kinit krbuser
|
|
|
|
|
|
|
|
# klist
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The ``klist`` command should show that we got a valid ticket. Thus is the
|
|
|
|
|
|
|
|
deployment tested as working. Note that the keytab used is by default
|
|
|
|
|
|
|
|
``/etc/krb5.keytab``, which is probably not meant for FreeRADIUS to use. And in
|
|
|
|
|
|
|
|
fact, the ``freerad`` user does not have permissions to read it, yet…
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Also, we are abusing the host principal. That is probably bad, but for PoC
|
|
|
|
|
|
|
|
deployment it will suffice (sadly)…
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Hooking Kerberos into FreeRADIUS
|
|
|
|
|
|
|
|
================================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Now this part is really awful, ugly, nasty, etc. You have been warned…
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
We will need to get quite creative, since most other people just use either
|
|
|
|
|
|
|
|
ntlm_auth, or LDAP, since the use-case is connecting to a Windows Active
|
|
|
|
|
|
|
|
Directory. The `wiki page for
|
|
|
|
|
|
|
|
rlm_krb5 <https://wiki.freeradius.org/modules/Rlm_krb5>`__ is quite unhelpful,
|
|
|
|
|
|
|
|
really, and the documentation `at
|
|
|
|
|
|
|
|
networkradius <https://networkradius.com/doc/3.0.10/raddb/mods-available/krb5.html>`__,
|
|
|
|
|
|
|
|
while being a bit longer, only contains fragments of hints of what needs to be done, like
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
> In order to use Kerberos authentication, the administrator must manually set ``control:Auth-Type := krb5``.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Where the hell should I put that?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The simple part
|
|
|
|
|
|
|
|
---------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Enabling the krb5 module seems like a good idea::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cd mods-enabled
|
|
|
|
|
|
|
|
ln -s ../mods-available/krb5
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The module has no configuration in ``mods-config``, so we edit
|
|
|
|
|
|
|
|
``mods-available/krb5`` directly. The comments
|
|
|
|
|
|
|
|
`there <https://github.com/FreeRADIUS/freeradius-server/blob/v3.0.x/raddb/mods-available/krb5>`__
|
|
|
|
|
|
|
|
are quite helpful luckily, so we just change two lines to semi-reasonable values::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
krb5 {
|
|
|
|
|
|
|
|
# …
|
|
|
|
|
|
|
|
keytab = /etc/krb5.keytab
|
|
|
|
|
|
|
|
service_principal = host/localhost
|
|
|
|
|
|
|
|
# …
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Also, as the wiki page said, we tweak ``authenticate`` block in the default site::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
authenticate {
|
|
|
|
|
|
|
|
Auth-Type Kerberos {
|
|
|
|
|
|
|
|
krb5
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
# … the rest of values
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
But it did not work, no Auth-Type was found when authorizing, so no running of
|
|
|
|
|
|
|
|
``authenticate`` block. And there was that ``control:Auth-Type`` stuff, which
|
|
|
|
|
|
|
|
should be somewhere?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The awful hacks
|
|
|
|
|
|
|
|
---------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
So, naturally, I ended up permuting various options ("rlm_krb5" vs. just "krb5"
|
|
|
|
|
|
|
|
vs. "Kerberos", different options in the ``users`` file), most of which either
|
|
|
|
|
|
|
|
did not load (``radiusd`` refused to start) or did not use the intended paths, so
|
|
|
|
|
|
|
|
the Kerberos database was not contacted. I kind of knew that I need to force
|
|
|
|
|
|
|
|
FreeRADIUS to set Auth-Type to krb5 (not really, read on), but I had no idea
|
|
|
|
|
|
|
|
how. I found `a stub wiki page about
|
|
|
|
|
|
|
|
Auth-Type <https://wiki.freeradius.org/config/Auth-Type>`__ which mostly says that
|
|
|
|
|
|
|
|
I should not want to set it directly, so I disregarded that.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
What helped, was a snippet on `deploying FreeRADIUS with
|
|
|
|
|
|
|
|
FreeIPA <https://www.freeipa.org/page/Using_FreeIPA_and_FreeRadius_as_a_RADIUS_based_software_token_OTP_system_with_CentOS/RedHat_7>`__,
|
|
|
|
|
|
|
|
which was setting the ``control:Auth-Type`` for LDAP.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Long story short: the following nasty trick worked: The ``users`` file can
|
|
|
|
|
|
|
|
specify action for fallback (``DEFAULT``), which I succesfully abused just by
|
|
|
|
|
|
|
|
putting ``DEFAULT`` by itself towards the end of the file (line 200 for me). No
|
|
|
|
|
|
|
|
options, no filters, just nothing, catch-all.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Now we have a known module that will match, so we use the FreeIPA snippet in
|
|
|
|
|
|
|
|
the ``authorize`` section by putting the following just after the ``files`` directive::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# authorize { …
|
|
|
|
|
|
|
|
files
|
|
|
|
|
|
|
|
if ((ok || updated) && User-Password) {
|
|
|
|
|
|
|
|
update {
|
|
|
|
|
|
|
|
control:Auth-Type := Kerberos
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
# … }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
If I understand this, when the flow finishes processing ``files`` module, it
|
|
|
|
|
|
|
|
checks whether the current status is ``ok`` or ``updated`` and the request
|
|
|
|
|
|
|
|
contained a password, and if so, updates Auth-Type to be Kerberos. While this
|
|
|
|
|
|
|
|
means that we will not be able to use any other users from the ``users`` file, we
|
|
|
|
|
|
|
|
reach the correct ``authenticate`` block and use ``krb5`` module!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
I think that in the end, if you only intend to authenticate Kerberos users,
|
|
|
|
|
|
|
|
this hack does not get much in the way. And you still can restrict the
|
|
|
|
|
|
|
|
``DEFAULT`` rule to only match specific suffixes (or maybe other rules) to limit
|
|
|
|
|
|
|
|
its reach.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Progress: the error message has changed
|
|
|
|
|
|
|
|
---------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Of course at this point I was oblivious to the fact that ``radiusd`` setuid's to
|
|
|
|
|
|
|
|
``freerad`` user, since the documentation said about "user running radiusd", so
|
|
|
|
|
|
|
|
now I got a Permission denied in the ``krb5`` module. One ``strace`` and ``cat
|
|
|
|
|
|
|
|
/proc/XXXX/status`` later, I became aware, so the last bit was::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apt install acl
|
|
|
|
|
|
|
|
setfacl -m u:freerad:r /etc/krb5.keytab
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Did I say this was a really hacky PoC deployment in a VM? Isn't this just
|
|
|
|
|
|
|
|
awful? But it works, at least with ``radtest``…
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EAP and a few more tweaks in configurations
|
|
|
|
|
|
|
|
===========================================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The last issue is that EAP does not specify PAP as valid mechanism, so we
|
|
|
|
|
|
|
|
cannot use that directly. So while ``radtest`` happily uses that and hands the
|
|
|
|
|
|
|
|
password over to FreeRADIUS (PAP is the default method), which can do Kerberos
|
|
|
|
|
|
|
|
stuff to authenticate it, with EAP we need to get a bit creative. EAP-PWD looks
|
|
|
|
|
|
|
|
promising by the abbreviation, but alas – see `Caveat 5 <Caveats_>`__.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(If FreeRADIUS knows the password, as is the case with Cleartext-Password, it
|
|
|
|
|
|
|
|
can do many of the challenge-response methods of verifying shared knowledge of
|
|
|
|
|
|
|
|
password without sharing it. That is the reason this worked on my bare-metal
|
|
|
|
|
|
|
|
configuration, but does not work with Kerberos anymore.)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Luckily, my Android phone has died and at this point I only had a laptop with
|
|
|
|
|
|
|
|
IWD to test my setup, so the only thing I could do was reading ```iwd.network``
|
|
|
|
|
|
|
|
manpage <https://git.kernel.org/pub/scm/network/wireless/iwd.git/tree/src/iwd.network.rst>`__.
|
|
|
|
|
|
|
|
I noticed that when using TTLS[^`Not to be confused with Twinkle, Twinkle,
|
|
|
|
|
|
|
|
Little Star <https://en.wikipedia.org/wiki/TTLS>`__ :-)], it is allowed to use
|
|
|
|
|
|
|
|
``Tunneled-PAP`` in the second phase.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
I think I needed to do similar tweaks to ``sites-enabled/inner-tunnel`` on the
|
|
|
|
|
|
|
|
FreeRADIUS side, add the external hostapd to ``clients.conf`` and set correct IP
|
|
|
|
|
|
|
|
in ``hostapd.conf``, and my final config for IWD (``/var/lib/iwd/testX.8021x``) is
|
|
|
|
|
|
|
|
like this::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[Settings]
|
|
|
|
|
|
|
|
AutoConnect=false
|
|
|
|
|
|
|
|
Hidden=false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[Security]
|
|
|
|
|
|
|
|
EAP-Method=TTLS
|
|
|
|
|
|
|
|
EAP-Identity=anonymous
|
|
|
|
|
|
|
|
EAP-TTLS-Phase2-Method=Tunneled-PAP
|
|
|
|
|
|
|
|
EAP-TTLS-Phase2-Identity=krbuser
|
|
|
|
|
|
|
|
EAP-TTLS-Phase2-Password=pw
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
IWD complains in a log a bit, but it succeeds eventually::
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EAP server tried method 4 while client was configured for method 21
|
|
|
|
|
|
|
|
EAP completed with eapSuccess
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
It worked, so I didn't bother to read ``radiusd`` output.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Final deployment differences from stock config (patches or whole configs)
|
|
|
|
|
|
|
|
=========================================================================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Again, a disclaimer: it is PoC deployment. It should work, you should not use
|
|
|
|
|
|
|
|
it. It is insecure, horrible and may kill your puppy. Do not apply directly to
|
|
|
|
|
|
|
|
a production server, this is for your information only; yada yada yada…
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Also, the patches may contain extraneous information and have been tweaked to
|
|
|
|
|
|
|
|
not contain any real IP addresses. Please use your brain (or at least eyes and
|
|
|
|
|
|
|
|
read `above <Setup_>`__ for the rationale). These files do not describe complete
|
|
|
|
|
|
|
|
deployment (else this post would not be needed).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Sometimes, the patches are created against a template (if Debian generates the
|
|
|
|
|
|
|
|
final config from it).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Here goes. (Patches are ended with ``.patch``)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.. TODO: create links, add patches
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- FreeRADIUS (on Debian):
|
|
|
|
|
|
|
|
- ```/etc/freeradius/3.0/clients.conf`` <>`__
|
|
|
|
|
|
|
|
- ```/etc/freeradius/3.0/sites-enabled/default`` <>`__
|
|
|
|
|
|
|
|
- ```/etc/freeradius/3.0/sites-enabled/inner-tunnel`` <>`__
|
|
|
|
|
|
|
|
- ```/etc/freeradius/3.0/mods-enabled/krb5`` <>`__ (symlinked from ``../mods-available/krb5``)
|
|
|
|
|
|
|
|
- ```/etc/freeradius/3.0/users`` <>`__ (or wherever that symlink leads)
|
|
|
|
|
|
|
|
- Kerberos (on Debian)
|
|
|
|
|
|
|
|
- ```/etc/krb5.conf`` <>`__
|
|
|
|
|
|
|
|
- hostapd (on Arch)
|
|
|
|
|
|
|
|
- ```/etc/hostapd/hostapd.conf`` <>`__
|
|
|
|
|
|
|
|
- IWD config file (on an Arch WiFi device)
|
|
|
|
|
|
|
|
- ```/var/lib/iwd/testX.8021x`` <>`__
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Caveats
|
|
|
|
|
|
|
|
=============================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1. Differences between distros: while FreeRADIUS configuration resides at
|
|
|
|
|
|
|
|
``/etc/raddb`` in Arch, it is located at ``/etc/freeradius/3.0`` in Debian. The
|
|
|
|
|
|
|
|
directory structure seems to be the same. Likewise, the server binary is
|
|
|
|
|
|
|
|
``radiusd`` on Arch and ``freeradius`` on Debian.
|
|
|
|
|
|
|
|
2. Debian packaging shenanigans: while the configuration for FreeRADIUS's
|
|
|
|
|
|
|
|
rlm_krb module is bundled in the ``freeradius-config`` package (pulled by
|
|
|
|
|
|
|
|
``freeradius``), the module itself is in ``freeradius-krb5``. So you can enable
|
|
|
|
|
|
|
|
the config even without having the module. Arch bundles everything in the
|
|
|
|
|
|
|
|
``freeradius`` package.
|
|
|
|
|
|
|
|
3. RADIUS realms have nothing to do with Kerberos realms.
|
|
|
|
|
|
|
|
4. The ``users`` file allows you to specify ``Auth-Type``, but I either do not
|
|
|
|
|
|
|
|
understand its syntax (which might not be unlang), or it just does nothing –
|
|
|
|
|
|
|
|
it did not assign ``Auth-Type`` for me with the default user.
|
|
|
|
|
|
|
|
5. EAP-PWD does not send password, it is a cryptographic way to just prove
|
|
|
|
|
|
|
|
knowledge of it. This makes it unusable for us, since FreeRADIUS does not
|
|
|
|
|
|
|
|
know any passwords – Kerberos does.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Security considerations and improvement tips
|
|
|
|
|
|
|
|
============================================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A few tips on how to make the deployment more useful, secure, production-ready etc.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- Use reasonable secrets, not the defaults. This is true for all parts: Kerberos realm, RADIUS clients, user passwords.
|
|
|
|
|
|
|
|
- Use reasonable permissions for files.
|
|
|
|
|
|
|
|
- Authenticate the RADIUS by pinning the TLS certificate in the WiFi network configuration. (Both IWD and wpa_supplicant can definitely do that.) Otherwise, if someone creates a rogue AP, they get to know the passwords in clear, because we are tunnelling PAP.
|
|
|
|
|
|
|
|
- Use a dedicated Kerberos keytab and principal for FreeRADIUS, do not abuse the host one. (hint: ``kadmin.local -t keytab``)
|
|
|
|
|
|
|
|
- Secure access to FreeRADIUS as much as possible. AFAIK Kerberos expects that passwords are only known to Kerberos AS and the other party.
|
|
|
|
|
|
|
|
- Think whether you really cannot avoid handling and sending cleartext passwords (possibly by not using Kerberos in the first place). If you can find a way to avoid that with Kerberos, please let me know. Also, `solve the real issue <https://en.wikipedia.org/wiki/XY_problem>`__.
|
|
|
|
|
|
|
|
- Try understand yourself, what is happening and what is security-critical. Understand, how this relates to your threat model, what are the risks etc.
|
|
|
|
|
|
|
|
- Read the documentation, do not be like me, especially if you are deploying this to production. (I only need a PoC and will not be the one deploying this for real.)
|
|
|
|
|
|
|
|
- Do not use that horrible hack with default user. Find some better way of matching requests that should be authenticated using Kerberos.
|
|
|
|
|
|
|
|
- Do not blindly believe a random guy on the internet. (I might be wrong, misguided and/or malicious, and you would never know; in all cases I would present this as trying to help you.)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
References
|
|
|
|
|
|
|
|
==========
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Other links
|
|
|
|
|
|
|
|
===========
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- Somehow, I avoided unlang documentation before writing this post: `<https://freeradius.org/radiusd/man/unlang.html>`__
|