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 radiator@open.com.au http://www.open.com.au/mailman/listinfo/radiator