devops
816 TopicsWeblogic JSessionID Persistence
Problem this snippet solves: Contributed by: unRuleY, Summarized by: deb Note: The previous version of this iRule contained escaped newlines following the session command, which in versions 10.0 - 10.2.0 causes TMM to core as documented in CR135937 / SOL11427. This was fixed in 10.2.1. See this related Codeshare example for details on how to take advantage of session replication on the WebLogic servers with targeted node failover in an iRule. Provides persistence on the jsessionid value found in either the URI or a cookie. When a request is received, the iRule first looks for a "jsessionid" cookie, and if not found, for a "jsessionid" parameter in the requested URI. If either is found, a persistence record is created if it doesn't already exist, or followed if it does. If neither is found, the request is load balanced according to the load balancing method applied to the virtual server and persisted based on the client's IP address. In order to ensure the second and subsequent requests follow the first, LTM must create a persistence record indicating the pool member to which the first request was load balanced. If the server is setting the jsessionid in a cookie, the persistence key value may be extracted from the server response to create the persistence record. If the server is setting the jsessionid in the URLs, source address persistence with a short timeout is recommended to track the original destination until the jsessionid is sent. How to use this snippet: To ensure a new persistence record is followed when a request is re-load balanced in a client-side Keep-Alive connection, apply a OneConnect profile to the virtual server. The iRule assumes the jsessionid is in upper case when used as a cookie name. If this isn't the case, please update the example. To persist on jsessionid, create the iRule below and create a custom Universal persistence profile, with Match Across Services enabled, that uses the iRule. Then use this custom Universal persistence profile as the Default Persistence profile on your Virtual Server. Applying a Fallback Persistence profile of type Source Address Affinity with a host mask and a short timeout (the default source_addr persistence profile will do the trick) to your Virtual Server is also recommended. Attention, if you are running firmware 11.0 - 11.2.1 and enabled "Match Across Services"! There is a bug inside. SOL14061 This iRule requires LTM v10. or higher. Code : when HTTP_REQUEST { # Log details for the request set log_prefix "[IP::client_addr]:[TCP::client_port]" log local0. "$log_prefix: Request to [HTTP::uri] with cookie: [HTTP::cookie value JSESSIONID]" # Check if there is a JSESSIONID cookie if { [HTTP::cookie "JSESSIONID"] ne "" }{ # Persist off of the cookie value with a timeout of 1 hour (3600 seconds) persist uie [string tolower [HTTP::cookie "JSESSIONID"]] 3600 # Log that we're using the cookie value for persistence and the persistence key if it exists. log local0. "$log_prefix: Used persistence record from cookie. Existing key? [persist lookup uie [string tolower [HTTP::cookie "JSESSIONID"]]]" } else { # Parse the jsessionid from the path. The jsessionid, when included in the URI, is in the path, # not the query string: /path/to/file.ext;jsessionid=1234?param=value set jsess [findstr [string tolower [HTTP::path]] "jsessionid=" 11] # Use the jsessionid from the path for persisting with a timeout of 1 hour (3600 seconds) if { $jsess != "" } { persist uie $jsess 3600 # Log that we're using the path jessionid for persistence and the persistence key if it exists. log local0. "$log_prefix: Used persistence record from path: [persist lookup uie $jsess]" } } } when HTTP_RESPONSE { # Check if there is a jsessionid cookie in the response if { [HTTP::cookie "JSESSIONID"] ne "" }{ # Persist off of the cookie value with a timeout of 1 hour (3600 seconds) persist add uie [string tolower [HTTP::cookie "JSESSIONID"]] 3600 log local0. "$log_prefix: Added persistence record from cookie: [persist lookup uie [string tolower [HTTP::cookie "JSESSIONID"]]]" } }6.2KViews2likes9CommentsF5OS using Ansible Linux Shell with remote users as iCall replacement(works with banner as well).
(AI Made picture so don't take it as 100% truth) In F5OS 1.8.0 and up remote users can automatically enter the Linux Shell if they have the correct remote group parameters and if this is system level enabled as shown in https://clouddocs.f5.com/training/community/rseries-training/html/rseries_security.html#superuser-role The superuser role if enabled can still trigger F5OS commands with "f5sh" that is similar to "tmsh" in TMOS and described in https://my.f5.com/manage/s/article/K000148922 . Code version: The code was tested on F5OS 1.8.4 rSeries 5900 Ansible Example without banner: --- - name: Restart docker container hosts: f5os gather_facts: no vars: container_name: tcpdumpd_manager tasks: - name: Restart the specified docker container ansible.builtin.shell: | docker restart {{ container_name }} become: false args: executable: /bin/bash Cronjobs also can be edited through shell Ansible playbook for scheduling transferred scripts. - name: Add cron job ansible.builtin.shell: | (crontab -l 2>/dev/null | grep -Fv '/opt/check_service.sh'; \ echo '*/5 * * * * /opt/check_service.sh') | crontab - become: false args: executable: /bin/bash Great Ansible F5OS automation article with cool examples that even has file transfer for transferring script file to the F5OS: Five Ways to Automate F5OS with Ansible: A Practical Guide | DevCentral Ansible Example with banner: The F5OS ssh banner is inserted between the username and password and that breaks the shell module as I did found out. I suspect that Ansible uses the PEXPECT Python package that spans SSH session and it is similar to TCL EXPECT and most people dealing with iRules will find this interesting. You need to add sudo in-front of each command. - name: Restart container hosts: localhost gather_facts: false tasks: - name: Run docker command ansible.builtin.expect: command: >- ssh -tt -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o ServerAliveInterval=5 -o ServerAliveCountMax=2 {{ ansible_user }}@{{ ansible_host }} responses: '(?i)password:': {{ ansible_pass }} '(?m)\$\s*$': "sudo docker restart {{ container_name }}\nexit\n" timeout: 20 echo: true delegate_to: localhost register: out Also SCP file transfer can be done through the EXPEC module. From Linux bash you can use f5sh to trigger F5OS commands. Examples are f5sh "show interface" and to chain commands f5sh "config; vlan 500; commit; exit" . If you just write f5sh, you will enter the F5os from the Linux. This is like tmsh for TMOS. Summary! This is great feature. TMOS also has cronjobs but after upgrade the cronjob is lost but not on F5OS, this is why icall scripts still are better for TMOS Tenants. iCall has the option to be triggered by logs and that in F5OS will be harder as it will need a bash script that is sitting as a background process as to monitor the logs and a cronjob can check if the script is still running as a device reboot will cause the script to stop running. The script can be transferred and started the way I have shown with RESTCONF REST-API with Ansible/AWX as the best option.166Views2likes1CommentAdvanced WAF IP Exceptions Manager
This solution introduces a dedicated Python-based GUI tool for managing IP whitelist exceptions in F5 BIG-IP Advanced WAF policies. The tool was developed to simplify a common operational workflow: searching WAF policies, adding IP or CIDR-based exceptions, checking whether an IP already exists across policies, deleting outdated exceptions, applying policy changes, and tracking actions through an activity log. The solution is especially useful in large environments where manual exception handling can create operational overhead, inconsistent policy configuration, or audit challenges. It demonstrates how focused automation can deliver practical value without requiring a large orchestration platform or full CI/CD pipeline.154Views1like0CommentsBIG-IP Report
Problem this snippet solves: Overview This is a script which will generate a report of the BIG-IP LTM configuration on all your load balancers making it easy to find information and get a comprehensive overview of virtual servers and pools connected to them. This information is used to relay information to NOC and developers to give them insight in where things are located and to be able to plan patching and deploys. I also use it myself as a quick way get information or gather data used as a foundation for RFC's, ie get a list of all external virtual servers without compression profiles. The script has been running on 13 pairs of load balancers, indexing over 1200 virtual servers for several years now and the report is widely used across the company and by many companies and governments across the world. It's easy to setup and use and only requires auditor (read-only) permissions on your devices. Demo/Preview Interactive demo http://loadbalancing.se/bigipreportdemo/ Screen shots The main report: The device overview: Certificate details: How to use this snippet: Installation instructions BigipReport REST This is the only branch we're updating since middle of 2020 and it supports 12.x and upwards. Downloads: https://github.com/net-utilities/BigIPReport/releases Documentation, installation instructions and troubleshooting: https://net-utilities.github.io/BigIPReport/ BIG-IP Report (Legacy) Older version of the report that only runs on Windows and is depending on a Powershell plugin originally written by Joe Pruitt (F5). The documentation for this will stay on loadbalancing.se. BIG-IP Report (only download this if you have v10 devices): https://github.com/net-utilities/BigIPReport/releases/download/v5.8.0/bigipreport-5.4.0-beta.zip iControl Snapin https://github.com/net-utilities/BigIPReport/releases/download/v5.8.0/f5-icontrol.zip Got issues/problems/feedback? Still have issues? Drop a comment below. We usually reply quite fast. Any bugs found, issues detected or ideas contributed makes the report better for everyone, so it's always appreciated. --- Join us on Discord: https://discord.gg/7JJvPMYahA Tested this on versions: 12, 13, 14, 15, 16, 17 (probably works on later versions too)20KViews21likes102CommentsAPM SAML IdP - SP Issuer Extraction
Problem this snippet solves: APM doesn't expose any detail about the SAML SP Issuer when authentication requests hitting APM as an IdP during an SP initiated SAMLRequest. This iRule when applied to a SAML IdP enabled virtual server will extract the assertion request, decode it and present the SAML SP Issuer ID as the session variable %{session.saml.request.issuer} within APM. How to use this snippet: This comes in real handy when performing authorisation of the resource and could help avoid having APM perform a TCP connection reset when a SAML resource isn't authorised. Code : when CLIENT_ACCEPTED { ACCESS::restrict_irule_events disable } when HTTP_REQUEST { if { [HTTP::path] equals "/saml/idp/profile/redirectorpost/sso" } { if { [HTTP::method] equals "POST" } { # Colelct POST data set content_length [HTTP::header value Content-Length] HTTP::collect $content_length } elseif { [HTTP::method] equals "GET" } { #TODO } } } when HTTP_REQUEST_DATA { set payload_data [URI::decode [HTTP::payload]] log local0. "payload=[URI::query "?$payload_data" "SAMLRequest"]" if { $payload_data contains "SAMLRequest" } { # Extract SAML request data set SAMLdata [b64decode [URI::query "?$payload_data" "SAMLRequest"]] set SAML_Issuer_loc [string first "saml:issuer" [string tolower $SAMLdata]] set SAML_Issuer_start [expr {[string first ">" $SAMLdata $SAML_Issuer_loc] + 1}] set SAML_Issuer_end [expr {[string first "<" $SAMLdata $SAML_Issuer_start] - 1}] set SAML_Issuer [string range $SAMLdata $SAML_Issuer_start $SAML_Issuer_end] if { !([ACCESS::session sid] equals "" ) } { ACCESS::session data set session.saml.request.issuer $SAML_Issuer } } } when ACCESS_SESSION_STARTED { if { [info exists SAML_Issuer] } { ACCESS::session data set session.saml.request.issuer $SAML_Issuer } } Tested this on version: 11.61.8KViews2likes8CommentsCreating a tmsh script with iControl REST and using it to restart HTTPD
Problem this snippet solves: TMSH has the ability to create tcl scripts that can be used to run multiple commands and transactions. It is rare that you will want to create on with TMSH but there are a few cases where this may be desirable. One of these is to restart HTTPD, which is difficult to do from iControl REST because the REST API is running over HTTPD and the restart will not be clean. See K13292945. This Python script creates a tmsh script, and then runs it to restart HTTPD. How to use this snippet: Syntax is <program_name.py> host user password Sample output: ./rest_script_example.py 10.155.117.12 admin admin Before httpd (pid 3186) is running... After httpd (pid 3289) is running... Code : #!/usr/bin/python #[email protected] #Makes tmsh script to restart HTTP #Syntax: host username password import json #allow python 2 and python 3 by loading the correct libraries. try: from http.client import BadStatusLine from urllib.parse import urlparse, urlencode from urllib.request import urlopen, Request from urllib.error import HTTPError except ImportError: from httplib import BadStatusLine from urlparse import urlparse from urllib import urlencode from urllib2 import urlopen, Request, HTTPError import ssl import sys import time #Internal calls will not verify certs so disable cert verification. ssl._create_default_https_context = ssl._create_unverified_context #Create request for token based authentication. This is in Bigip 12 and later: url = 'https://'+sys.argv[1]+'/mgmt/shared/authn/login' values = {'username' : sys.argv[2], 'password' : sys.argv[3], 'loginProviderName' : 'tmos'} values = json.dumps(values).encode('utf-8') Request(url,data=values) req = Request(url,data=values) req.add_header('Content-Type' , 'application/json') #Request authentication token. response = urlopen(req) #auth=result will be a json data structure. auth_result = response.read() #print (auth_result) #Json.loads makes an internal python data structure that is easier to extract auth token from json. #Now construct icontrol rest query for device-groups info. auth=json.loads(auth_result) token=(auth['token']['token']) #print(token) #Get current PID of HTTPD url = 'https://'+sys.argv[1]+'/mgmt/tm/sys/service/httpd/stats' req = Request(url) req.add_header('X-F5-Auth-Token',auth['token']['token']) response = urlopen(req,data=None) json_response=(response.read()) python_response=json.loads(json_response) print("Before") print(python_response["apiRawValues"]["apiAnonymous"]) #look for script with name to make sure that the script does not already exist url = 'https://'+sys.argv[1]+'/mgmt/tm/cli/script/example.tcl' #urllib2 raises an exception with an HTTP 404 req = Request(url) req.add_header('X-F5-Auth-Token',auth['token']['token']) try: response = urlopen(req,data=None) except HTTPError as err: if err.code==404: #print (err.code) #print("\nCreate cli script\n") #request create here url = 'https://'+sys.argv[1]+'/mgmt/tm/cli/script' req = Request(url) req.add_header('X-F5-Auth-Token',auth['token']['token']) req.add_header('Content-Type' , 'application/json') values = {"name":"example.tcl", "apiAnonymous": "proc script::init {} {\n}\n\nproc script::run {} {\n tmsh::run util bash -c 'killall -9 httpd' \n tmsh::start sys service httpd\n} \n\nproc script::help {} {\n}\n\nproc script::tabc {} {\n}\n"} values = json.dumps(values) response = urlopen(req,data=values) response_py=(json.load(response)) #print(json.dumps(response_py,sort_keys=True,indent=4)) #Now run script url = 'https://'+sys.argv[1]+'/mgmt/tm/cli/script/example.tcl' req = Request(url) req.add_header('X-F5-Auth-Token',auth['token']['token']) req.add_header('Content-Type' , 'application/json') values = {"kind":"tm:cli:script:runstate","command":"run"} values = json.dumps(values).encode('utf-8') # Killing HTTPD will abort the connection so catch the exception. try: urlopen(req,data=values) except BadStatusLine: pass #Wait for httpd to restart so you can query. time.sleep(5) #Get current PID of HTTPD after restart url = 'https://'+sys.argv[1]+'/mgmt/tm/sys/service/httpd/stats' req = Request(url) req.add_header('X-F5-Auth-Token',auth['token']['token']) response = urlopen(req,data=None) json_response=(response.read()) python_response=json.loads(json_response) print("After") print(python_response["apiRawValues"]["apiAnonymous"]) Tested this on version: 13.02KViews2likes3CommentsRestsh is now available under an Open Source license!
I am proud to announce that the complete Restsh package is now released under the GNU General Public License version 3 (GPLv3) or later. There are no hidden restrictions — we are not withholding any enterprise features. Restsh will remain actively maintained and further developed by Axians IT Security. What is Restsh? Restsh is a lightweight Bash-based shell environment for working with REST APIs from the command line. It was built for interactive use, for automation in scripts, and for robust execution in CI/CD pipelines. Restsh is a core component of the Axians Automation Framework, enabling automated management of F5 environments via GitLab CI/CD pipelines. Restsh does not replace your shell. Instead it exports a small set of environment variables and provides focused helper functions to call and parse REST APIs. Combine the power of Bash, curl, jq and Mustache templates to build reliable, repeatable workflows and automation. What can I do with it? Almost anything related to REST API automation. Restsh supports the common REST verbs and includes autocompletion for F5 and GitLab APIs. To simplify day-to-day tasks, it ships hundreds of small, focused helper scripts that wrap API endpoints — designed with the Unix principle in mind: do one thing well. These compact scripts can be piped together, filtered, or executed inside loops. For example, exporting all WAF policies from an F5 is a simple one-liner: f5.asm.policy.list -r -f ".items[].fullPath" | XARGS f5.asm.policy.export Modular design Restsh is modular and provides many functions to interact with the REST APIs of F5 BIG-IP, F5 OS-A and GitLab: F5 functions F5 OS-A functions GitLab functions Do I have to sell my soul to get it? Restsh is publicly available and can be downloaded from the official GitHub repository. Support This is the open-source, community-supported edition of Restsh. For enterprise-grade support and SLAs, Axians IT Security GmbH offers commercial support plans. Contact me to discuss options. Documentation Full documentation is available online: https://axiansitsecurity.github.io/Restsh/ Tutorials F5 AS3 management F5 certificate deployment with HashiCorp Vault F5 Declarative Onboarding (DO) F5 Declarative WAF F5 New Declarative API GitLab Pipeline647Views7likes10CommentsF5 certificate deployment with iControl REST and HashiCorp Vault
Welcome to the first article in a comprehensive series dedicated to administrating F5 BIG-IP systems through the power of the iControl REST API. This series is designed for both beginners and experienced network administrators who want to streamline their F5 management workflows and embrace infrastructure-as-code principles. Throughout this series, we will explore practical, real-world scenarios for managing F5 BIG-IP appliances programmatically. Rather than relying solely on the F5 web interface, we'll leverage the iControl REST API. To make this journey accessible and efficient, I'll be using Restsh, my open-source command-line interface tool. Restsh is purpose-built to simplify interactions with the iControl REST API, abstracting away complexity while maintaining full flexibility. Whether you're new to F5 REST API or looking to enhance your automation skills, these tutorials will provide clear, step-by-step guidance with practical examples you can implement immediately. In this workflow, you’ll create the certificate materials on the F5, have them signed by a trusted Certificate Authority (in this case, HashiCorp Vault), and then deploy the signed certificate back to the F5. This process ensures that the private key never leaves the F5. Initial configuration of Restsh See First Steps for the initial configuration of Restsh. Connect Connect to the F5 with Restsh: restsh Select your F5 1. Create a key pair and signing request (CSR) on the F5 The first step is to create a key pair and a Certificate Signing Request (CSR) on the F5. A key pair consists of a private key (which stays secret on the F5) and a public key. The CSR contains the public key and certificate details that will be sent to a Certificate Authority (CA) for signing. Below are the certificate parameters we’ll use for this example: Name on the F5: test.lab.lan Partition: Common CN: test.lab.lan Subject Alternative Names: test.lab.lan, test2.lab.lan Keytype: EC with SECP384R1 f5.cert.csr.create --cn=test.lab.lan --keytype=ec-private --curvename=secp384r1 -a test.lab.lan -a test2.lab.lan -n test.lab.lan 2. Download the signing request The next step is to download the Certificate Signing Request from the F5 to your local machine. This file contains the public key and certificate information that needs to be signed by the Certificate Authority. We’ll save it to a temporary directory for processing. f5.cert.csr.get /Common/test.lab.lan > "$RESTSH_TMP/test.lab.lan.csr" You can inspect the details of the CSR to verify it contains the correct information (domain names, key type, etc.). The cert.* commands are convenient wrapper scripts for the OpenSSL command-line tool, making certificate operations easier. cert.csr.show "$RESTSH_TMP/test.lab.lan.csr" You can also view the public key contained in the CSR: cert.csr.pubkey "$RESTSH_TMP/test.lab.lan.csr" 3. Sign the CSR with HashiCorp Vault Now we use HashiCorp Vault to sign the CSR. Vault acts as your Certificate Authority (CA) and will create a signed certificate based on your CSR. First, you authenticate with Vault using vault login, then you submit the CSR for signing. The vault write command sends your CSR to the Vault PKI backend, which signs it and returns a complete certificate. In this example, we’re using the signing backend/role pki/sign/lab-ca (adjust this to match your Vault configuration). vault login vault write -field=certificate pki/sign/lab-ca csr=@"$RESTSH_TMP/test.lab.lan.csr" > "$RESTSH_TMP/test.lab.lan.crt" After signing, you can verify the certificate details to ensure it was signed correctly and contains the expected information (domain names, validity period, key type, etc.): cert.x509.show "$RESTSH_TMP/test.lab.lan.crt" You can also view the public key in the signed certificate to confirm it matches the public key from the CSR: cert.x509.pubkey "$RESTSH_TMP/test.lab.lan.crt" 4. Upload the signed certificate to the F5 Finally, we upload the signed certificate back to the F5. This makes the certificate available for use in SSL profiles. We use the same certificate name that we used when creating the CSR, so the F5 knows to pair it with the private key we created in step 1. f5.cert.import /Common/test.lab.lan "$RESTSH_TMP/test.lab.lan.crt" You can view the imported certificate with following command: f5.cert.get /Common/test.lab.lan 5. Cleanup Cleanup is optional the files in the temporary directory will be automatically removed after Restsh finishes. However, if you want to manually remove the CSR and certificate files from your local machine, you can do so with the following command: rm "$RESTSH_TMP/test.lab.lan.csr" "$RESTSH_TMP/test.lab.lan.crt" Optionally sync the F5 cluster to ensure the new certificate is available on all cluster members. "failover" is here the name of the Sync-Failover device group. f5.cluster.config-sync failover Summary In this tutorial, we walked through the process of deploying a new certificate on an F5 device using Restsh and HashiCorp Vault. We created a key pair and CSR on the F5, signed the CSR with Vault, and then imported the signed certificate back to the F5. This workflow allows you to securely manage certificates without exposing private keys, while leveraging the power of Restsh for automation and integration with your existing infrastructure.203Views3likes0CommentsUpdate an ASM Policy Template via REST-API - the reverse engineering way
I always want to automate as many tasks as possible. I have already a pipeline to import ASM policy templates. Today I had the demand to update this base policies. Simply overwriting the template with the import tasks does not work. I got the error message "The policy template ax-f5-waf-jump-start-template already exists.". Ok, I need an overwrite tasks. Searching around does not provide me a solution, not even a solution that does not work. Simply nothing, my google-foo have deserted me. Quick chat with an AI, gives me a solution that was hallucinated. The AI answer would be funny if it weren't so sad. I had no hope that AI could solve this problem for me and it was confirmed, again. I was configuring Linux systems before the internet was widely available. Let's dig us in the internals of the F5 REST API implementation and solve the problem on my own. I took a valid payload and removed a required parameter, "name" in this case. The error response changes, this is always a good signal in this stage of experimenting. The error response was "Failed Required Fields: Must have at least 1 of (title, name, policyTemplate)". There is also a valid field named "policyTemplate". My first thought: This could be a reference for an existing template to update. I added the "policyTemplate" parameter and assigned it an existing template id. The error message has changed again. It now throws "Can't use string (\"ox91NUGR6mFXBDG4FnQSpQ\") as a HASH ref while \"strict refs\" in use at /usr/local/share/perl5/F5/ASMConfig/Entity/Base.pm line 888.". An perl error that is readable and the perl file is in plain text available. Looking at the file at line 888: The Perl code looks for an "id" field as property of the "policyTemplate" parameter. Changing the payload again and added the id property. And wow that was easy, it works and the template was updated. Final the payload for people who do not want to do reverse engineering. Update POST following payload to /mgmt/tm/asm/tasks/import-policy-template to update an ASM policy template: { "filename": "<username>~<filename>", "policyTemplate": { "id": "ox91NUGR6mFXBDG4FnQSpQ" } } Create POST following payload /mgmt/tm/asm/tasks/import-policy-template to create an ASM policy template: { "name": "<name>", "filename": "<username>~<filename>" } Hint: You must upload the template before to /var/config/rest/downloads/<username>~<filename>". Conclusion Documentation is sometimes overrated if you can read Perl. Missed I the API documentation for this endpoint and it was just a exercise for me?593Views2likes8CommentsFTP Session Logging
Problem this snippet solves: This iRule logs FTP connections and username information. By default connection mapping from client through BIG-IP to server is logged as well as the username entered by the client. Optionally you can log the entire FTP session by uncommenting the log message in CLIENT_DATA. Code : # This iRule logs FTP connections and username information. # By default connection mapping from client through BIG-IP to server is logged # as well as the username entered by the client. Optionally you can log the # entire FTP session by uncommenting the log message in CLIENT_DATA. when CLIENT_ACCEPTED { set vip [IP::local_addr]:[TCP::local_port] set user "unknown" } when CLIENT_DATA { # uncomment for full session logging #log local0. "[IP::client_addr]:[TCP::client_port]: collected payload ([TCP::payload length]): [TCP::payload]" # check if payload contains the string we want to replace if { [TCP::payload] contains "USER" } { # use a regular expression to save the user name ## regex modified by arkashik regexp "USER \(\[a-zA-Z0-9_-]+)" [TCP::payload] all user # log connection mapping from client through BIG-IP to server log local0. "FTP connection from $client. Mapped to $inside -> $node, user $user" TCP::release TCP::collect } else { TCP::release TCP::collect } } when SERVER_CONNECTED { set client "[IP::client_addr]:[TCP::client_port]" set node "[IP::server_addr]:[TCP::server_port]" set inside "[serverside {IP::local_addr}]:[serverside {TCP::local_port}]" TCP::collect } when SERVER_DATA { TCP::release clientside { TCP::collect } }1.7KViews1like5Comments