I'm using Radiator with ntlm_auth (on linux) to authenticate against Active
Directory. Occasionally ntlm_auth is slow to return, causing a logjam in the
request queue and making it hard for new wireless users to authenticate. I've
seen this happen in a variety of different ways, but some of those ways can be
mitigated by using multiple instances of Radiator on each server: a single
front-end instance proxies the final inner authentication piece to one of
several back-end instances (each with its own separate ntlm_auth process).
When a stalled back-end instance becomes unstalled (after spending e.g. 6s
waiting for ntlm_auth to respond to a single request), it's likely to have a
bunch more requests already queued up, and some of those may already be old
enough that there's no point in answering them (because the front-end instance
will have already given up). We want a way for the back-end instance to
recognize and IGNORE those requests, so that we can quickly get back to
processing more recent requests.
I've figured out a first approximation of this in my config snippets below: the
front-end instance adds the current time as a custom request attribute
(X-Timestamp) before proxying it, and each back-end instance uses a hook to
compare X-Timestamp to the (new) current time and short-circuit IGNORE anything
whose X-Timestamp is older than 5 seconds. This works as desired for the
_first_ back-end instance I proxy to, but if the first one times out, then the
second back-end instance will still see the same X-Timestamp value and will
also short-circuit IGNORE. Of course I could invoke my hook with a higher
tolerance (e.g. allow up to 7s) to allow a retry to succeed, but then the first
back-end instance would waste effort answering 6s-old requests.
How can I set a new attribute value on a request _each_ time I attempt to proxy
it using AuthRADIUS and friends? I'm thinking a "PreForwardHook" would be
ideal, but I don't see anything like that currently implemented. Is there
another solution I'm not seeing, or if not, would it be possible to add such a
hook?
(note: for my use case it wouldn't matter whether the hook also gets called
between successive retries for the same Host, all I care about is that it's
called each time we switch to a new Host)
As an aside: is there any special reason that MaxTargetHosts is unique to
AuthVOLUMEBALANCE? I would think it would be equally applicable to all flavors
of AuthRADIUS (and in particular I wish it was implemented in AuthROUNDROBIN).
Thanks,
David
Front-end instance:
# proxy inner auth to local sub-instances using simple round robin
# (since they are interchangeable and stateless). IGNORE acct.
<AuthBy ROUNDROBIN>
Identifier wireless-authproxy
include %D/private/localhost.secret
# give up if sub-instance doesn't respond within this time
RetryTimeout 5
# do not retry against the same sub-instance
Retries 0
FailureBackoffTime 1
<Host 127.0.0.1>
AuthPort %{GlobalVar:radius.wireless1.authport}
</Host>
<Host 127.0.0.1>
AuthPort %{GlobalVar:radius.wireless2.authport}
</Host>
<Host 127.0.0.1>
AuthPort %{GlobalVar:radius.wireless3.authport}
</Host>
<Host 127.0.0.1>
AuthPort %{GlobalVar:radius.wireless4.authport}
</Host>
# this attribute is not in the dictionary
StripFromRequest ConvertedFromEAPMSCHAPV2
IgnoreAccounting
AddToRequest X-Client-Identifier=%{Client:Identifier},X-Timestamp=%t
</AuthBy>
Back-end instances:
<AuthBy GROUP>
AuthByPolicy ContinueWhileAccept
# ...snip...
# IGNORE requests older than wireless-authproxy RetryTimeout
<AuthBy INTERNAL>
Identifier wireless-ignoreExpired
AuthHook sub { CITES::timestamp_within("X-Timestamp", 5, @_) }
</AuthBy>
<AuthBy NTLM>
# ...snip...
</AuthBy>
</AuthBy>
# AuthHook which returns ACCEPT if request's $attrname attribute has
# an epoch timestamp value within $window seconds of the current time,
# and IGNORE otherwise. Local sub-instances can use this with AuthBy
# INTERNAL (and a suitable AuthByPolicy) to proactively drop proxied
# requests that are already too old to be worth processing.
sub timestamp_within {
my ($attrname,$window) = (shift,shift);
my ($p,$rp)=@_;
my $ts = $p->get_attr($attrname) or
&main::log($main::LOG_ERR,"Missing $attrname in timestamp_within");
my $age = time - ($ts || 0);
return $main::ACCEPT if $age >= 0 && $age <= $window;
&main::log($main::LOG_ERR,"Ignoring expired request for
".$p->getUserName().": $attrname is ${age}s old");
return $main::IGNORE;
}
_______________________________________________
radiator mailing list
[email protected]
http://www.open.com.au/mailman/listinfo/radiator