Using the authenticate function on rlm_python
Hi there, I am trying to write a python script to be used by the python module of Freeradius. My main goal is to send an Access-Challenge to the user so that he would reply to that challenge. On perl, usually this «Response-Packet-Type» is configured on RAD_CHECK. But how do we send this in Python? I tried this: def authenticate(p): print("*** authenticate ***") print("") radiusd.radlog(radiusd.L_INFO, "*** log call in authenticate ***") print("") print(p) print(radiusd.config) print("") reply = ( ('Reply-Message', 'Hello from rlm_python'), ('State', 'test'), ('Response-Packet-Type', 'Access-Challenge') ) # config = ( ('Response-Packet-Type' , 'Acccess-Challenge'), ) config = ( ('Cleartext-Password' , '123'), ) return (radiusd.RLM_MODULE_HANDLED, reply,None) But without any success. Any clues of what to do here? Best, Francis -- Francis Augusto Medeiros-Logeay Oslo, Norway
On Nov 14, 2024, at 3:29 PM, Francis Augusto Medeiros-Logeay via Freeradius-Users <freeradius-users@lists.freeradius.org> wrote:
I am trying to write a python script to be used by the python module of Freeradius.
My main goal is to send an Access-Challenge to the user so that he would reply to that challenge.
Ok.
On perl, usually this «Response-Packet-Type» is configured on RAD_CHECK. But how do we send this in Python?
It's almost the same thing. See the documentation in mods-available/python3. Set pass_all_vps = yes And then modify the "config" list. Alan DeKok.
On 14 Nov 2024, at 21:59, Alan DeKok <aland@deployingradius.com> wrote:
On Nov 14, 2024, at 3:29 PM, Francis Augusto Medeiros-Logeay via Freeradius-Users <freeradius-users@lists.freeradius.org> wrote:
I am trying to write a python script to be used by the python module of Freeradius.
My main goal is to send an Access-Challenge to the user so that he would reply to that challenge.
Ok.
On perl, usually this «Response-Packet-Type» is configured on RAD_CHECK. But how do we send this in Python?
It's almost the same thing. See the documentation in mods-available/python3.
Set
pass_all_vps = yes
And then modify the "config" list.
Thanks a lot, Alan. It works! I was wondering if there’s any example on how to handle threads here. The thing is that I’ll be using python there to handle push notifications, which might be blocking. I’m not being able to have concurrent authentication requests. according to the documentation, threading seems to be supported, but I can’t figure out how. Best, Francis
On Nov 15, 2024, at 3:28 AM, Francis Augusto Medeiros-Logeay via Freeradius-Users <freeradius-users@lists.freeradius.org> wrote:
Thanks a lot, Alan. It works!
Good to hear.
I was wondering if there’s any example on how to handle threads here.
No. The server has it's own thread pool. You shouldn't start new threads in Python.
The thing is that I’ll be using python there to handle push notifications, which might be blocking. I’m not being able to have concurrent authentication requests. according to the documentation, threading seems to be supported, but I can’t figure out how.
If the API is blocking, there is very little you can do to fix that. The server *must* wait for the reply, which means... waiting for the reply. The only solution here for v3 is to increase the number of threads in radiusd.conf. See "thread pool". Set the maximum to a large number, like 100, or even more if you need it. The only negative side effect is that the server will use more memory. One of the large delays in v4 is changing the entire model to non-blocking. The Python module doesn't yet support it, but the rest of the server does. This increases performance quite a bit. Alan DeKok.
On 15 Nov 2024, at 13:37, Alan DeKok <aland@deployingradius.com> wrote:
I was wondering if there’s any example on how to handle threads here.
No. The server has it's own thread pool. You shouldn't start new threads in Python.
Thanks Alan! Before reading your message, I tried with threads, and it seemed better to handle my specific situation, but I don’t want to do anything that’s non-kosher here. I just thought it was ok to use threads here due the reference to threading.local() on the documentation.
The thing is that I’ll be using python there to handle push notifications, which might be blocking. I’m not being able to have concurrent authentication requests. according to the documentation, threading seems to be supported, but I can’t figure out how.
If the API is blocking, there is very little you can do to fix that. The server *must* wait for the reply, which means... waiting for the reply.
The only solution here for v3 is to increase the number of threads in radiusd.conf. See "thread pool". Set the maximum to a large number, like 100, or even more if you need it. The only negative side effect is that the server will use more memory.
That’s the thing: I have the default configuration, which is five servers. But even a second connection doesn’t execute concurrently. If the first hangs, the second still waits. Is there any configuration for the python module to make it more compliant to this? What I am trying here is to emulate a bit the behaviour of the NPS radius, which handles push notifications. Those are blocking, so I don’t want to return `OK` before the user has approved the authentication on his mobile. Best, Francis
On Nov 15, 2024, at 8:17 AM, Francis Augusto Medeiros-Logeay <r_f@med-lo.eu> wrote:
Before reading your message, I tried with threads, and it seemed better to handle my specific situation, but I don’t want to do anything that’s non-kosher here. I just thought it was ok to use threads here due the reference to threading.local() on the documentation.
You can use threads in python, but they won't help. The server has it's own thread pool. Each request is processed through one thread at a time, until it finishes. This means that if the Python module calls a blocking API, then it blocks that thread. Adding more Python pthreads won't help, because the main *FreeRADIUS* thread is blocked.
That’s the thing: I have the default configuration, which is five servers. But even a second connection doesn’t execute concurrently. If the first hangs, the second still waits. Is there any configuration for the python module to make it more compliant to this?
If you're using the python3 module, it will create one Python interpreter, but will create sub-interpreters for each FreeRADIUS thread. I suspect the issue may be either that the Python API you're using has a global lock, or else you're running into the main Python lock. So... this is really a Python problem. TBH, unless the *only* way to interact with that API is through a Python library, you're better off just using the basic server features. We've done many, many, installations, and the only time we need Perl / Python is when we have to use an external API, and the only tools available are Perl / Python.
What I am trying here is to emulate a bit the behaviour of the NPS radius, which handles push notifications. Those are blocking, so I don’t want to return `OK` before the user has approved the authentication on his mobile.
Exactly. Blocking is the only way to implement this in v3. Alan DeKok.
On 15 Nov 2024, at 14:42, Alan DeKok <aland@deployingradius.com> wrote:
On Nov 15, 2024, at 8:17 AM, Francis Augusto Medeiros-Logeay <r_f@med-lo.eu> wrote:
Before reading your message, I tried with threads, and it seemed better to handle my specific situation, but I don’t want to do anything that’s non-kosher here. I just thought it was ok to use threads here due the reference to threading.local() on the documentation.
You can use threads in python, but they won't help. The server has it's own thread pool. Each request is processed through one thread at a time, until it finishes. This means that if the Python module calls a blocking API, then it blocks that thread.
Adding more Python pthreads won't help, because the main *FreeRADIUS* thread is blocked.
I see. Thanks for that.
That’s the thing: I have the default configuration, which is five servers. But even a second connection doesn’t execute concurrently. If the first hangs, the second still waits. Is there any configuration for the python module to make it more compliant to this?
If you're using the python3 module, it will create one Python interpreter, but will create sub-interpreters for each FreeRADIUS thread. I suspect the issue may be either that the Python API you're using has a global lock, or else you're running into the main Python lock.
It could be, but when I tested concurrency, all I did was a print («Will block now») While True: time.sleep(5) print («waiting») Just to check if another request would print «Will block now», which it never did. The only way I got that to happen was to fork into another thread before the print.
So... this is really a Python problem.
TBH, unless the *only* way to interact with that API is through a Python library, you're better off just using the basic server features. We've done many, many, installations, and the only time we need Perl / Python is when we have to use an external API, and the only tools available are Perl / Python.
Maybe this isn’t the only way - it’s just the easiest way for me to do it as I’m not so familiar with other approaches. I need basically to: - send a request to the RestAPI to ask if the user will have a push notification or if he’ll use TOTP - That info comes on a json - if it’s totp, send an access-challenge and then process the password sent back - if it’s push, send an api call to the server, poll it to know if the user has replied, and then send a final call to check what was the result of the user’s action. The third step is blocking, as you see. Do you suggest any other tool/scripting to deal with this?
What I am trying here is to emulate a bit the behaviour of the NPS radius, which handles push notifications. Those are blocking, so I don’t want to return `OK` before the user has approved the authentication on his mobile.
Exactly. Blocking is the only way to implement this in v3.
I’ll see if I find out what could be blocking my threads. Best, Francis
On Nov 15, 2024, at 3:12 PM, Francis Augusto Medeiros-Logeay <r_f@med-lo.eu> wrote:
It could be, but when I tested concurrency, all I did was a
print («Will block now») While True: time.sleep(5) print («waiting»)
Just to check if another request would print «Will block now», which it never did. The only way I got that to happen was to fork into another thread before the print.
If your'e running in debug mode, it's single threaded. That explains it.
Maybe this isn’t the only way - it’s just the easiest way for me to do it as I’m not so familiar with other approaches. I need basically to:
- send a request to the RestAPI to ask if the user will have a push notification or if he’ll use TOTP - That info comes on a json
The server has a REST module which can do some json work...
I’ll see if I find out what could be blocking my threads.
Check if the server is running single-threaded. Alan DeKOk.
On 15 Nov 2024, at 21:36, Alan DeKok <aland@deployingradius.com> wrote:
On Nov 15, 2024, at 3:12 PM, Francis Augusto Medeiros-Logeay <r_f@med-lo.eu> wrote:
It could be, but when I tested concurrency, all I did was a
print («Will block now») While True: time.sleep(5) print («waiting»)
Just to check if another request would print «Will block now», which it never did. The only way I got that to happen was to fork into another thread before the print.
If your'e running in debug mode, it's single threaded. That explains it.
I was, but I also tested with running radiusd as a service. Got the same results. Somehow the python module is blocking other threads.
Maybe this isn’t the only way - it’s just the easiest way for me to do it as I’m not so familiar with other approaches. I need basically to:
- send a request to the RestAPI to ask if the user will have a push notification or if he’ll use TOTP - That info comes on a json
The server has a REST module which can do some json work...
I took a look at it. The thing about it is that I had the feeling it requires too much predefined data. I didn’t see a lot of conditionals there, but I’ll read more.
I’ll see if I find out what could be blocking my threads.
Check if the server is running single-threaded.
Is there a special way to do it? What I do is that I send two authenticate requests, and see at the second never gets processed until the first one returns something to the client. Best, Francis
On Nov 16, 2024, at 4:53 AM, Francis Augusto Medeiros-Logeay <r_f@med-lo.eu> wrote:
I was, but I also tested with running radiusd as a service. Got the same results. Somehow the python module is blocking other threads
The Python interpreter has a global lock (GIL). So I suspect that's it.
Is there a special way to do it? What I do is that I send two authenticate requests, and see at the second never gets processed until the first one returns something to the client.
See radiusd.conf, "thread pool" If Python doesn't work, maybe try Perl. Alan DeKok,
__ Francis Augusto Medeiros-Logeay Oslo, Norway Sent from a mobile device / Enviado a partir de dispositivo móvel
On 16 Nov 2024, at 14:51, Alan DeKok <aland@deployingradius.com> wrote:
On Nov 16, 2024, at 4:53 AM, Francis Augusto Medeiros-Logeay <r_f@med-lo.eu> wrote:
I was, but I also tested with running radiusd as a service. Got the same results. Somehow the python module is blocking other threads
The Python interpreter has a global lock (GIL). So I suspect that's it.
Yup, I’ve seen that in the past people mentioned that.
Is there a special way to do it? What I do is that I send two authenticate requests, and see at the second never gets processed until the first one returns something to the client.
See radiusd.conf, "thread pool"
If Python doesn't work, maybe try Perl.
To be honest, I was thinking of biting the bullet and do it with a combination of unlang and rest and using exec in bash for polling the server. Would you recommend this approach instead of doing it in Perl? Best, Francis
On 16 Nov 2024, at 15:10, Francis Augusto Medeiros-Logeay via Freeradius-Users <freeradius-users@lists.freeradius.org> wrote:
__ Francis Augusto Medeiros-Logeay Oslo, Norway Sent from a mobile device / Enviado a partir de dispositivo móvel
On 16 Nov 2024, at 14:51, Alan DeKok <aland@deployingradius.com> wrote:
On Nov 16, 2024, at 4:53 AM, Francis Augusto Medeiros-Logeay <r_f@med-lo.eu> wrote:
I was, but I also tested with running radiusd as a service. Got the same results. Somehow the python module is blocking other threads
The Python interpreter has a global lock (GIL). So I suspect that's it.
Yup, I’ve seen that in the past people mentioned that.
Is there a special way to do it? What I do is that I send two authenticate requests, and see at the second never gets processed until the first one returns something to the client.
See radiusd.conf, "thread pool"
If Python doesn't work, maybe try Perl.
To be honest, I was thinking of biting the bullet and do it with a combination of unlang and rest and using exec in bash for polling the server.
Would you recommend this approach instead of doing it in Perl?
Just an add on: I tried Perl. It worked pretty well with concurrent threads. I’d like to try with the rest module as well (I started another thread about it), though. But at least I know that the problem I was facing is due python and that with Perl I get the functionality I was looking for. Thanks a lot for the help along the way! Best, Francis
participants (2)
-
Alan DeKok -
Francis Augusto Medeiros-Logeay