Created
August 9, 2013 18:54
-
-
Save fishnix/6196155 to your computer and use it in GitHub Desktop.
CAS Client F5 iRule
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # yale_cas_auth iRule | |
| # | |
| # iRule to act as a CAS client for apps that can't do CAS natively | |
| # | |
| # Since the sideband itself can't do SSL, it requires an | |
| # unencrypted vserver to proxy sideband connections to | |
| # an encrypted validation service. | |
| # | |
| # CAS flow looks like this: | |
| # 1. Browser GET http://foo.yale.edu/someapp | |
| # 2. APP looks for a session, if none, 302 to CAS login | |
| # 3. Browser GET CAS login, post to login form | |
| # https://casserver/cas/login?service=http://foo.yale.edu/someapp | |
| # 4. CAS login succeeds, 302 back to the app w/ticket: | |
| # http://foo.yale.edu/someapp?ticket=xxxxxxxx | |
| # 5. APP validates the ticket by calling CAS validate | |
| # with the ticket and the service | |
| # https://casserver/cas/validate?ticket=xxxx&service=http://foo.yale.edu/someapp | |
| # 6. APP creates a session if ticket validates | |
| # | |
| # E. Camden Fisher | |
| # [email protected] | |
| # | |
| # 20121115 ECF initial revision, from test | |
| # 20121127 ECF remove return on valid session | |
| # set valid_session after ticket validation | |
| # set/remove X-SSO-NETID header for authz | |
| # | |
| # TODO: | |
| # - determine service protocol (http/https) | |
| # - SSO redirect will break for someone who's session expired, | |
| # but has a ticket in the URL for some reason | |
| # | |
| when RULE_INIT { | |
| # Set some static variables | |
| # (once at load time instead of each request) | |
| # enable/disable debug loggging | |
| set static::cas_auth_debug 0 | |
| # set the location of CAS | |
| set static::sso_server "my.cas.server.yale.edu" | |
| set static::sso_server_login "/cas/login" | |
| set static::sso_server_validate_ip "XXX.XXX.XXX.XXX" | |
| set static::sso_server_validate_port "18443" | |
| set static::sso_server_validate "/cas/validate" | |
| # set timeout + idle for cas validate | |
| set static::sso_server_validate_timeout 1000 | |
| set static::sso_server_validate_idle 1000 | |
| # set the name of the session cookie to use | |
| set static::sso_cookie "F5SESSION" | |
| # set the session timeout (reset on every access) | |
| set static::sso_session_timeout 3600 | |
| # set the lifetime of the session, never reset, forces login | |
| set static::sso_session_life 28800 | |
| # key prefix for table | |
| set static::sso_table_prefix "F5SESS-" | |
| set static::sso_authz_header "X-SSO-NETID" | |
| } | |
| when CLIENT_ACCEPTED { | |
| # setup high speed logging | |
| set hsl [HSL::open -proto UDP -pool dcsunix_syslog_hosts] | |
| } | |
| when HTTP_REQUEST { | |
| set valid_session 0 | |
| set sso_ticket_valid 0 | |
| set validate_connect_failed 0 | |
| set sso_ticket "" | |
| set sso_netid "" | |
| set sso_session_id "" | |
| set url [string tolower [HTTP::host]][HTTP::uri] | |
| set proto "http" | |
| set client_addr [IP::client_addr] | |
| set virtual_name [virtual name] | |
| if { $static::cas_auth_debug == 1 } { | |
| log local0. "Got Connection from [IP::client_addr]" | |
| log local0. "CAS Client: got URL $url" | |
| } | |
| if { [HTTP::header exists $static::sso_authz_header] }{ | |
| log local0. "Removing $static::sso_authz_header header [HTTP::header $static::sso_authz_header]... h4x?" | |
| HTTP::header remove $static::sso_authz_header | |
| } | |
| # Check for active session | |
| if { [HTTP::cookie exists $static::sso_cookie] } { | |
| # if a session cookie exists, let's work with it | |
| set sso_session_id [HTTP::cookie $static::sso_cookie] | |
| # lookup the session in our table to see if the session is valid (updates timer too) | |
| if { ($sso_session_id starts_with $static::sso_table_prefix) and [table lookup $sso_session_id] != "" } { | |
| set valid_session 1 | |
| set sso_netid [table lookup $sso_session_id] | |
| if { $static::cas_auth_debug == 1 } { log local0. "[IP::client_addr] has a valid session: $sso_session_id netid: $sso_netid" } | |
| } | |
| } | |
| # check for Ticket in the URL | |
| if { ([URI::query $url "ticket"] ne "") and ($valid_session == 0) } { | |
| if { [URI::query $url "ticket"] starts_with "ST-" } { | |
| set sso_ticket [URI::query $url "ticket"] | |
| if { $static::cas_auth_debug == 1 } { log local0. "Found CAS ticket: $sso_ticket" } | |
| # sideband to validate ticket | |
| # GET https://my.cas.server.yale.edu/cas/validate?ticket=xxxxx&service=xxxxxx | |
| # if we can get a connection | |
| if { [catch { connect -timeout 1000 -idle 30 -status conn_status $static::sso_server_validate_ip:$static::sso_server_validate_port } conn_id ] == 0 && $conn_id ne ""} { | |
| if { $static::cas_auth_debug == 1 } { log local0. "Connect returns: $conn_id and conn status: $conn_status" } | |
| set service_url [URI::encode "${proto}://[string tolower [HTTP::host]][HTTP::path]"] | |
| set validate_url "${static::sso_server_validate}?service=${service_url}&ticket=${sso_ticket}" | |
| set send_data "GET ${validate_url} HTTP/1.0\r\n\r\n" | |
| set send_bytes [send -timeout 1000 -status send_status $conn_id $send_data] | |
| if { $static::cas_auth_debug == 1 } { log local0. "Sent $send_bytes with status $send_status" } | |
| set recv_data [string trim [getfield [recv -timeout 1000 -status recv_status $conn_id] "\r\n\r\n" 2]] | |
| if { $static::cas_auth_debug == 1 } { log local0. "RECV'd with status $recv_status and data $recv_data" } | |
| close $conn_id | |
| if { $recv_data ne "" } { | |
| if { $recv_data starts_with "yes" } { | |
| set sso_netid [getfield $recv_data "\n" 2] | |
| # Log netid/client address; see RFC 3164 Section 4.1.1 - "PRI Part" for more info | |
| # http://tools.ietf.org/html/rfc3164#section-4.1.1 | |
| HSL::send $hsl "<165> Successfully validated ticket for $sso_netid from [IP::client_addr]\n" | |
| set sso_ticket_valid 1 | |
| set valid_session 1 | |
| set random_num [expr {int(rand()*1000000)}] | |
| set sso_session_id $static::sso_table_prefix[b64encode [sha256 $client_addr-$virtual_name-$random_num]] | |
| if { $static::cas_auth_debug == 1 } { log local0. "Setting SSO Session cookie to: $sso_session_id" } | |
| # Log session creation; see RFC 3164 Section 4.1.1 - "PRI Part" for more info | |
| # http://tools.ietf.org/html/rfc3164#section-4.1.1 | |
| HSL::send $hsl "<165> Setting up SSO new session for [IP::client_addr]\n" | |
| table set $sso_session_id $sso_netid $static::sso_session_timeout $static::sso_session_life | |
| } else { HTTP::respond 403 content "Unauthorized. Bad ticket." } | |
| } else { HTTP::respond 500 content "Failed to connect to validation host." } | |
| # else bail | |
| } else { | |
| if { $static::cas_auth_debug == 1 } { log local0. "Connection could not be established to $static::sso_server_validate_ip:$static::sso_server_validate_port" } | |
| HTTP::respond 500 content "Failed to connect to validation host." | |
| } | |
| } | |
| } | |
| # redirect to CAS if there is no valid session or ticket | |
| if { ($valid_session == 0) and ($sso_ticket eq "") } { | |
| set service_url [URI::encode "${proto}://[string tolower [HTTP::host]][HTTP::uri]"] | |
| set login_url "https://${static::sso_server}${static::sso_server_login}?service=${service_url}" | |
| HTTP::redirect $login_url | |
| } elseif { $valid_session == 1 } { | |
| # Let'em in | |
| if { $static::cas_auth_debug == 1 } { log local0. "Adding $static::sso_authz_header header $sso_netid ..." } | |
| HTTP::header insert $static::sso_authz_header $sso_netid | |
| } | |
| } | |
| when HTTP_RESPONSE { | |
| # If we got a valid SSO ticket, we need to setup a cookie | |
| if { ($sso_ticket_valid == 1) and ($sso_session_id ne "") } { | |
| HTTP::cookie remove $static::sso_cookie | |
| HTTP::cookie insert name $static::sso_cookie value $sso_session_id | |
| } | |
| if { $valid_session == 1 } { HTTP::header insert "X-F5-SSO" $static::sso_cookie } | |
| if {[HTTP::header exists $static::sso_authz_header]}{ HTTP::header remove $static::sso_authz_header } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment