Forum Discussion
authentication iRule
Below is an iRule which is a customized version of the standard _sys_radius_auth iRule. The purpose of the modification is to not have to make a RADIUS call for every object i.e. the result of the authentication request is stored for an hour to take load off the RADIUS servers.
Unfortunately, this rule is generating a lot of logging messages to the point of us experiencing I/O Swap thrash. The message is the following:
May 30 08:26:37 local/tmm2 err tmm2[6301]: 01220001:3: TCL error: [RULENAME_REDACTED] - can't read "tmm_auth_http_collect_count": no such variable (reading value of variable to increment) invoked from within "incr tmm_auth_http_collect_count -1"
Can anyone assist and diagnose why we are getting this message? The "incr tmm_auth_http_collect_count -1" statement happens within the AUTH_RESULT event. The message seems to indicate that tmm_auth_http_collect_count is never instantiated. The only way this could happen is if the else statement enveloping the "set tmm_auth_http_collect_count 1" doesnt execute. However, if the else statement doesnt execute, then there is no call to auth::authenticate which to me would mean that the AUTH_RESULT event wouldn't fire.
when HTTP_REQUEST {
Save the username and password for reference in the AUTH_event
set user [HTTP::username]
set pass [HTTP::password]
set pass_hash [b64encode [md5 $pass]]
set key "$user-[virtual]-[IP::remote_addr]"
set tbl "[virtual]-radius-auth"
if {not [info exists tmm_auth_http_sids(radius)]} {
set tmm_auth_sid [AUTH::start pam default_radius]
set tmm_auth_http_sids(radius) $tmm_auth_sid
if {[info exists tmm_auth_subscription]} {
AUTH::subscribe $tmm_auth_sid
}
} else {
set tmm_auth_sid $tmm_auth_http_sids(radius)
}
Check if username/password are already in the session table
if {($user ne "") && \
([session lookup uie $key] eq $pass_hash)}{
Auth was already successful on previous request
log "HTTP_REQUEST auth found in session table for $key"
} else {
log "HTTP_REQUEST auth not found in session table for $key"
AUTH::username_credential $tmm_auth_sid $user
AUTH::password_credential $tmm_auth_sid $pass
AUTH::authenticate $tmm_auth_sid
if {not [info exists tmm_auth_http_collect_count]} {
HTTP::collect
set tmm_auth_http_successes 0
set tmm_auth_http_collect_count 1
} else {
incr tmm_auth_http_collect_count
}
}
}
when AUTH_RESULT {
if {not [info exists tmm_auth_http_sids(radius)] or \
($tmm_auth_http_sids(radius) != [AUTH::last_event_session_id]) or \
(not [info exists tmm_auth_http_collect_count])} {
return
}
if {[AUTH::status] == 0} {
incr tmm_auth_http_successes
}
If multiple auth sessions are pending and
one failure results in termination and this is a failure
or enough successes have now occurred
if {([array size tmm_auth_http_sids] > 1) and \
((not [info exists tmm_auth_http_sufficient_successes] or \
($tmm_auth_http_successes >= $tmm_auth_http_sufficient_successes)))} {
Abort the other auth sessions
foreach {type sid} [array get tmm_auth_http_sids] {
unset tmm_auth_http_sids($type)
if {($type ne "radius") and ($sid != -1)} {
AUTH::abort $sid
incr tmm_auth_http_collect_count -1
}
}
}
If this is the last outstanding auth then either
release or respond to this session
incr tmm_auth_http_collect_count -1
if {$tmm_auth_http_collect_count == 0} {
unset tmm_auth_http_collect_count
if { [AUTH::status] == 0 } {
log "AUTH_RESULT addig to session table for $key"
Add the username and password to the session table for one hour
session add uie $key $pass_hash 600
add a session entry with a indefinite timeout, but with a 600sec lifetime (force the record at lifetime)
table add -subtable $tbl $key $pass_hash indefinite 60
HTTP::release
} else {
HTTP::respond 401
}
}
}
6 Replies
- Kevin_Stewart
Employee
A few things:
1. First, can we see the rest of the logs to see the whole sequence?
2. Try putting the offending statement inside an if info exists clause.
3. While not really an issue, it doesn't appear that you're doing anything with the data that you set with the table command. - nitass
Employee
what are tcp profile (virtual server)'s and auth profile's idle timeouts?
is auth profile's idle timeout shorter than tcp profile's idle timeout? have you tried to set auth profile's idle timeout longer than tcp profile's idle timeout?
- Grey1
Nimbostratus
@Kevin
A few things:
1. First, can we see the rest of the logs to see the whole sequence?
The log is currently inundated with the message I indicated. The only other messages are ones saying that logging is being suppressed.
2. Try putting the offending statement inside an if info exists clause.
This may work, but the offending statement is from the canned _sys_auth_radius iRule and this iRule doesnt seem to generate these log messages. What about my modifications is causing this issue?
3. While not really an issue, it doesn't appear that you're doing anything with the data that you set with the table command.
The table command is commented out. It was used for debugging/testing :D
@nitass
The TCP profile timeout is 60 minutes. The auth profile timeout is 300 seconds. Can you elaborate on where you are going with this line of thinking? My understanding is that once the connection has been "authed", that connection is allowed for the duration of its life.
Thanks! - Grey1
Nimbostratus
Another line of thinking I investigated was the fact that in my iRule vs the canned _sys_auth_radius iRule, I wrap the Auth::authenticate call in an if statement, but do the Auth:: start and other related commands no matter what. My line of thinking was that MAYBE by virtue of doing an Auth::start, I automatically ensure that the AUTH_RESULT event will be fired for this connection. However, if the if/else statement results in the Auth::Authenticate call never happening, then the tmm_auth_http_collect_count variable would never be created. However, I tried to fix this by wrapping the initialization of the auth call (i.e. auth::Start etc) within the if statement to no avail. - nitass
Employee
The TCP profile timeout is 60 minutes. The auth profile timeout is 300 seconds. Can you elaborate on where you are going with this line of thinking?i have seen it is an issue in the past. it happens when auth profile idle timeout is shorter than tcp idle timeout.
ID244379 - TCL error: Rule _sys_auth_ldap - ... no such variable - Kevin_Stewart
Employee
Please try this minor modification:when RULE_INIT { set static::DEBUGLOG 1 set static::TABLENAME "RADUSERS" set static::TIMEOUT 60 } when HTTP_REQUEST { set key "[HTTP::username][b64encode [HTTP::password][HTTP::header User-Agent]]" if { $static::DEBUGLOG } { log local0. "key = [substr $key 0 30]" } if { [table lookup -subtable $static::TABLENAME $key] eq "" } { if { $static::DEBUGLOG } { log local0. "not authed yet" } if {not [info exists tmm_auth_http_sids(radius)]} { set tmm_auth_sid [AUTH::start pam default_radius] set tmm_auth_http_sids(radius) $tmm_auth_sid if {[info exists tmm_auth_subscription]} { AUTH::subscribe $tmm_auth_sid } } else { set tmm_auth_sid $tmm_auth_http_sids(radius) } AUTH::username_credential $tmm_auth_sid [HTTP::username] AUTH::password_credential $tmm_auth_sid [HTTP::password] AUTH::authenticate $tmm_auth_sid if {not [info exists tmm_auth_http_collect_count]} { HTTP::collect set tmm_auth_http_successes 0 set tmm_auth_http_collect_count 1 } else { incr tmm_auth_http_collect_count } } else { if { $static::DEBUGLOG } { log local0. "found existing auth" } } } when AUTH_RESULT { if {not [info exists tmm_auth_http_sids(radius)] or ($tmm_auth_http_sids(radius) != [AUTH::last_event_session_id]) or (not [info exists tmm_auth_http_collect_count])} { return } if {[AUTH::status] == 0} { incr tmm_auth_http_successes if { $static::DEBUGLOG } { log local0. "adding authed user to table: [substr $key 0 30]" } table add -subtable $static::TABLENAME $key $static::TIMEOUT } If multiple auth sessions are pending and one failure results in termination and this is a failure or enough successes have now occurred if {([array size tmm_auth_http_sids] > 1) and ((not [info exists tmm_auth_http_sufficient_successes] or ($tmm_auth_http_successes >= $tmm_auth_http_sufficient_successes)))} { Abort the other auth sessions foreach {type sid} [array get tmm_auth_http_sids] { unset tmm_auth_http_sids($type) if {($type ne "radius") and ($sid != -1)} { AUTH::abort $sid incr tmm_auth_http_collect_count -1 } } } If this is the last outstanding auth then either release or respond to this session incr tmm_auth_http_collect_count -1 if {$tmm_auth_http_collect_count == 0} { unset tmm_auth_http_collect_count if { [AUTH::status] == 0 } { HTTP::release } else { HTTP::respond 401 } } }
Help guide the future of your DevCentral Community!
What tools do you use to collaborate? (1min - anonymous)Recent Discussions
Related Content
* Getting Started on DevCentral
* Community Guidelines
* Community Terms of Use / EULA
* Community Ranking Explained
* Community Resources
* Contact the DevCentral Team
* Update MFA on account.f5.com