{"id":20567804,"url":"https://github.com/isometry/f5-totp","last_synced_at":"2026-03-19T16:25:16.616Z","repository":{"id":68239085,"uuid":"118280406","full_name":"isometry/f5-totp","owner":"isometry","description":"TOTP for F5 APM","archived":false,"fork":false,"pushed_at":"2018-01-20T21:01:57.000Z","size":81,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-05-29T08:08:04.151Z","etag":null,"topics":["f5","f5-apm","irule","rfc-6238","totp"],"latest_commit_sha":null,"homepage":null,"language":"Tcl","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/isometry.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-01-20T21:01:30.000Z","updated_at":"2022-09-21T19:04:47.000Z","dependencies_parsed_at":"2023-03-27T21:48:33.846Z","dependency_job_id":null,"html_url":"https://github.com/isometry/f5-totp","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/isometry/f5-totp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/isometry%2Ff5-totp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/isometry%2Ff5-totp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/isometry%2Ff5-totp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/isometry%2Ff5-totp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/isometry","download_url":"https://codeload.github.com/isometry/f5-totp/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/isometry%2Ff5-totp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28880225,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-29T10:31:27.438Z","status":"ssl_error","status_checked_at":"2026-01-29T10:31:01.017Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["f5","f5-apm","irule","rfc-6238","totp"],"created_at":"2024-11-16T04:48:33.907Z","updated_at":"2026-01-29T15:32:04.989Z","avatar_url":"https://github.com/isometry.png","language":"Tcl","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TOTP for F5 APM\n\n## Overview\n\nThis APM iRule implements the [RFC 6238 TOTP: Time-Based One-Time Password Algorithm](https://tools.ietf.org/html/rfc6238) for use with F5 APM access policies; for example, with an F5 SSL-VPN implementation.\n\n#### Features\n - Configurable storage backend (LDAP, Active Directory or Data Group List)\n - Per-user TOTP algorithm, digits and period\n - Configurable clock-drift tolerance and rate-limiting\n - Differentiation between users with and without TOTP metadata\n\n## Configuration\n\n### totp_apm iRule\n\nCreate a new _Local Traffic ➡︎ iRules ➡︎ iRule List_ iRule named `totp_apm`, containing the entire [totp_apm.tcl](https://github.com/isometry/f5-totp/blob/master/totp_apm.tcl) TCL script, to be customised for your chosen backend, as below.\n\n#### Backend configuration\n\nThe backend used for storage of user metadata is selected by modifying the value of the `totp_key_storage` in the `totp_apm` iRule ([totp_apm.tcl](https://github.com/isometry/f5-totp/blob/master/totp_apm.tcl#L7)).\n\nAll backends expect the metadata to be in the following format:\n```\nALGORITHM SECRET DIGITS PERIOD\n```\nwhere\n * `ALGORITHM` is `sha1`, `sha256` or `sha512` (lowercase);\n * `SECRET` is the user's TOTP secret (a Base32-encoded string with length an integer multiple of 8 characters, see below);\n * `DIGITS` is the number of digits expected from the user (normally `6` or `8`);\n * `PERIOD` is the frequency at which the code changes (normally `30` or `60` seconds).\n\nFor both `ldap` and `ad` backends, the metadata is expected to be accessible in an attribute of the user object, the attribute name configurable via [`totp_key_ldap_attr`](https://github.com/isometry/f5-totp/blob/master/totp_apm.tcl#L10) or [`totp_key_ad_attr`](https://github.com/isometry/f5-totp/blob/master/totp_apm.tcl#L13) variables, respectively, and both defaulting to `totp_auth_key`.\n\nThe Data Group List backend requires a String-type datagroup, configurable via the [`totp_key_dg`](https://github.com/isometry/f5-totp/blob/master/totp_apm.tcl#L16) variable (default: `totp_auth_keys`), with the key being the user's username and the value being the users' TOTP metadata.\n\n#### TOTP Secrets\n\nUser secrets take the form of a Base32-encoded random value with length a multiple of 8 characters. They can be easily generated using `pyotp`:\n\n```\npip3 install pyotp\npython3 -c \"import pyotp; print(pyotp.random_base32(32))\n```\n\nThese are normally communicated in the form of a QR Code encoding of an `otpauth://` string unique to the user:\n\n```\notpauth://totp/USERNAME@DOMAIN?secret=SECRET\u0026algorithm=ALGORITHM\u0026digits=DIGITS\u0026period=PERIOD\u0026issuer=DOMAIN\n```\n\nNote: `ALGORITHM` is all uppercase in the `otpauth://` string.\n\n##### otpauth example\n\n```\notpauth://totp/user@example.com?secret=FYNLSPTBTQPZJYEMB3QOZLDW34ZWX7TD\u0026algorithm=SHA256\u0026digits=8\u0026period=30\u0026issuer=example.com\n```\n\n#### Clock-drift tolerance, rate-limiting and lockout\n\n * Client clock-drift tolerance (the width of the window of codes that will be accepted) is configured via [`totp_tolerance`](https://github.com/isometry/f5-totp/blob/master/totp_apm.tcl#L52) (default = `1`, )\n * TOTP rate-limiting lockout are configured via [`totp_lockout_period`](https://github.com/isometry/f5-totp/blob/master/totp_apm.tcl#L54) (default = `90` seconds) and [`totp_lockout_rate`](https://github.com/isometry/f5-totp/blob/master/totp_apm.tcl#L56) (default = `3`)\n\nNote: already used codes and lockout state are stored with the [`totp_used_codes_table`](https://github.com/isometry/f5-totp/blob/master/totp_apm.tcl#L58) and [`totp_lockout_state_table`](https://github.com/isometry/f5-totp/blob/master/totp_apm.tcl#L59) tables.\n\n### Access Policy\n\nThe associated access policy consists of three main blocks:\n 1. retrieve the user's TOTP metadata from the configured backend via an _iRule Event_ ([`get_totp_key`](https://github.com/isometry/f5-totp/blob/master/totp_apm.tcl#L3)), \n 2. prompt for the user's code via a _Logon Page_,\n 3. verify the supplied code via another _iRule Event_ ([`check_totp_code`](https://github.com/isometry/f5-totp/blob/master/totp_apm.tcl#L45)).\n\n![TOTP Macro](https://raw.github.com/isometry/f5-totp/master/totp_macro.png)\n\nThe two iRule Event agents can also be integrated into alternate login workflows, for example prompting all users for a TOTP code alongside their main credentials.\n\n#### Creating the Access Policy\n\n 1. Add a new _Macro_ named `TOTP` to the relevant APM _Access Policy_ through the _Visual Policy Editor_ (VPE).\n\n 2. Within the TOTP Macro, add a _General Purpose ➡︎ iRule Event_ block with _Name_ `Get TOTP Key` and _ID_ `get_totp_key`; add a _Branch Rule_ named `Key Found` with the _Expression_ `mcget -secure {session.custom.totp.key}`.\n\n    The `fallback` branch from the `Get TOTP Key` block will be followed by any user for whom TOTP metadata cannot be found; associate with either the `Allow` or `Deny` terminal dependent on local policy.\n\n 3. On the `Key Found` branch add a _Logon ➡︎ Logon Page_ block named `TOTP Code Page`. _Field 1_ must be configured with _Type_ = `Text`, _Post Variable Name_ = `totp_code` and _Session Variable Name_ = `totp_code` (customisable via [`totp_code_form_field`](https://github.com/isometry/f5-totp/blob/master/totp_apm.tcl#L49)). All other fields should have _Type_ = `None`. Customise _Form Header Text_, _Logon Page Input Field #1_ and _Logon Button_ text as appropriate.\n\n 4. On the `fallback` branch of the `TOTP Code Page` block, add another _General Purpose ➡︎ iRule Event_ block with _Name_ `Check TOTP Code` and _ID_ `check_totp_code`.\n\n 5. On the `fallback` branch of the `Check TOTP Code` block, add a _General Purpose ➡︎ Logging_ block with name `Log TOTP Result`. Set an appropriate _Log Message_ (e.g. \"TOTP Verification Complete\") and a _Custom_ entry with value `session.custom.totp.result`; add a _Branch Rule_ named `Success` with the _Expression_ `expr {[mcget {session.custom.totp.result}] eq \"success\"}`.\n\n    The `Success` branch from the `Check TOTP Code` block will be followed only when the user supplied code matches that calculated from their metadata, and should normally be associated with the `Allow` terminal. `fallback` should be associated with the `Deny` terminal.\n\n 6. Plumb the `TOTP` macro in immediately after the primary authentication block or macro (tested with _AD Auth_ and _SAML Auth_).\n\n#### tmsh\n\nVia `tmsh`, assuming configuration within the `VPN` partition for an access policy named `vpn`, configuration will resemble the following:\n\n```\napm policy agent irule-event totp_act_irule_event_ag {\n    id get_totp_key\n    partition VPN\n}\napm policy agent irule-event totp_act_irule_event_1_ag {\n    id check_totp_code\n    partition VPN\n}\napm policy agent logon-page totp_act_logon_page_ag {\n    customization-group totp_act_logon_page_ag\n    field-type2 none\n    partition VPN\n    post-var-name1 totp_code\n    sess-var-name1 totp_code\n}\napm policy policy-item totp_act_irule_event {\n    agents {\n        totp_act_irule_event_ag {\n            type irule-event\n        }\n    }\n    caption \"Get TOTP Key\"\n    color 1\n    item-type action\n    partition VPN\n    rules {\n        {\n            caption \"Key Found\"\n            expression \"mcget -secure {session.custom.totp.key}\"\n            next-item totp_act_logon_page\n        }\n        {\n            caption fallback\n            next-item totp_ter_out\n        }\n    }\n}\napm policy policy-item totp_act_irule_event_1 {\n    agents {\n        totp_act_irule_event_1_ag {\n            type irule-event\n        }\n    }\n    caption \"Check TOTP Code\"\n    color 1\n    item-type action\n    partition VPN\n    rules {\n        {\n            caption fallback\n            next-item totp_act_logging\n        }\n    }\n}\napm policy policy-item totp_act_logging {\n    agents {\n        totp_act_logging_ag {\n            type logging\n        }\n    }\n    caption \"Log TOTP Result\"\n    color 1\n    item-type action\n    partition VPN\n    rules {\n        {\n            caption Success\n            expression \"expr {[mcget {session.custom.totp.result}] eq \\\"success\\\"}\"\n            next-item totp_ter_out\n        }\n        {\n            caption fallback\n            next-item totp_ter_totp_verification_failed\n        }\n    }\n}\napm policy policy-item totp_act_logon_page {\n    agents {\n        totp_act_logon_page_ag {\n            type logon-page\n        }\n    }\n    caption \"TOTP Code Page\"\n    color 1\n    item-type action\n    partition VPN\n    rules {\n        {\n            caption fallback\n            next-item totp_act_irule_event_1\n        }\n    }\n}\napm policy policy-item totp_ent_in {\n    caption In\n    color 1\n    partition VPN\n    rules {\n        {\n            caption fallback\n            next-item totp_act_irule_event\n        }\n    }\n}\napm policy policy-item totp_ter_out {\n    caption Allow\n    color 1\n    item-type terminal-out\n    partition VPN\n}\napm policy policy-item totp_ter_totp_verification_failed {\n    caption Deny\n    color 2\n    item-type terminal-out\n    partition VPN\n}\napm policy access-policy totp {\n    caption TOTP\n    default-ending totp_ter_out\n    items {\n        totp_act_irule_event { }\n        totp_act_irule_event_1 { }\n        totp_act_logging { }\n        totp_act_logon_page { }\n        totp_ent_in { }\n        totp_ter_out {\n            priority 8\n        }\n        totp_ter_totp_verification_failed {\n            priority 9\n        }\n    }\n    partition VPN\n    start-item totp_ent_in\n    type macro\n}\n```\n\nThis is then referenced as a macro within the `vpn` access policy:\n\n```\napm policy access-policy vpn {\n    …\n    macros { … /VPN/totp … }\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fisometry%2Ff5-totp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fisometry%2Ff5-totp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fisometry%2Ff5-totp/lists"}