irule
650 TopicsAutomate Let's Encrypt Certificates on BIG-IP
To quote the evil emperor Zurg: "We meet again, for the last time!" It's hard to believe it's been six years since my first rodeo with Let's Encrypt and BIG-IP, but (uncompromised) timestamps don't lie. And maybe this won't be my last look at Let's Encrypt, but it will likely be the last time I do so as a standalone effort, which I'll come back to at the end of this article. The first project was a compilation of shell scripts and python scripts and config files and well, this is no different. But it's all updated to meet the acme protocol version requirements for Let's Encrypt. Here's a quick table to connect all the dots: Description What's Out What's In acme client letsencrypt.sh dehydrated python library f5-common-python bigrest BIG-IP functionality creating the SSL profile utilizing an iRule for the HTTP challenge The f5-common-python library has not been maintained or enhanced for at least a year now, and I have an affinity for the good work Leo did with bigrest and I enjoy using it. I opted not to carry the SSL profile configuration forward because that functionality is more app-specific than the certificates themselves. And finally, whereas my initial project used the DNS challenge with the name.com API, in this proof of concept I chose to use an iRule on the BIG-IP to serve the challenge for Let's Encrypt to perform validation against. Whereas my solution is new, the way Let's Encrypt works has not changed, so I've carried forward the process from my previous article that I've now archived. I'll defer to their how it works page for details, but basically the steps are: Define a list of domains you want to secure Your client reaches out to the Let’s Encrypt servers to initiate a challenge for those domains. The servers will issue an http or dns challenge based on your request You need to place a file on your web server or a txt record in the dns zone file with that challenge information The servers will validate your challenge information and notify you You will clean up your challenge files or txt records The servers will issue the certificate and certificate chain to you You now have the key, cert, and chain, and can deploy to your web servers or in our case, to the BIG-IP Before kicking off a validation and generation event, the client registers your account based on your settings in the config file. The files in this project are as follows: /etc/dehydrated/config # Dehydrated configuration file /etc/dehydrated/domains.txt # Domains to sign and generate certs for /etc/dehydrated/dehydrated # acme client /etc/dehydrated/challenge.irule # iRule configured and deployed to BIG-IP by the hook script /etc/dehydrated/hook_script.py # Python script called by dehydrated for special steps in the cert generation process # Environment Variables export F5_HOST=x.x.x.x export F5_USER=admin export F5_PASS=admin You add your domains to the domains.txt file (more work likely if signing a lot of domains, I tested the one I have access to). The dehydrated client, of course is required, and then the hook script that dehydrated interacts with to deploy challenges and certificates. I aptly named that hook_script.py. For my hook, I'm deploying a challenge iRule to be applied only during the challenge; it is modified each time specific to the challenge supplied from the Let's Encrypt service and is cleaned up after the challenge is tested. And finally, there are a few environment variables I set so the information is not in text files. You could also move these into a credential vault. So to recap, you first register your client, then you can kick off a challenge to generate and deploy certificates. On the client side, it looks like this: ./dehydrated --register --accept-terms ./dehydrated -c Now, for testing, make sure you use the Let's Encrypt staging service instead of production. And since I want to force action every request while testing, I run the second command a little differently: ./dehydrated -c --force --force-validation Depicted graphically, here are the moving parts for the http challenge issued by Let's Encrypt at the request of the dehydrated client, deployed to the F5 BIG-IP, and validated by the Let's Encrypt servers. The Let's Encrypt servers then generate and return certs to the dehydrated client, which then, via the hook script, deploys the certs and keys to the F5 BIG-IP to complete the process. And here's the output of the dehydrated client and hook script in action from the CLI: # ./dehydrated -c --force --force-validation # INFO: Using main config file /etc/dehydrated/config Processing example.com + Checking expire date of existing cert... + Valid till Jun 20 02:03:26 2022 GMT (Longer than 30 days). Ignoring because renew was forced! + Signing domains... + Generating private key... + Generating signing request... + Requesting new certificate order from CA... + Received 1 authorizations URLs from the CA + Handling authorization for example.com + A valid authorization has been found but will be ignored + 1 pending challenge(s) + Deploying challenge tokens... + (hook) Deploying Challenge + (hook) Challenge rule added to virtual. + Responding to challenge for example.com authorization... + Challenge is valid! + Cleaning challenge tokens... + (hook) Cleaning Challenge + (hook) Challenge rule removed from virtual. + Requesting certificate... + Checking certificate... + Done! + Creating fullchain.pem... + (hook) Deploying Certs + (hook) Existing Cert/Key updated in transaction. + Done! This results in a deployed certificate/key pair on the F5 BIG-IP, and is modified in a transaction for future updates. This proof of concept is on github in the f5devcentral org if you'd like to take a look. Before closing, however, I'd like to mention a couple things: This is an update to an existing solution from years ago. It works, but probably isn't the best way to automate today if you're just getting started and have already started pursuing a more modern approach to automation. A better path would be something like Ansible. On that note, there are several solutions you can take a look at, posted below in resources. Resources https://github.com/EquateTechnologies/dehydrated-bigip-ansible https://github.com/f5devcentral/ansible-bigip-letsencrypt-http01 https://github.com/s-archer/acme-ansible-f5 https://github.com/s-archer/terraform-modular/tree/master/lets_encrypt_module(Terraform instead of Ansible) https://community.f5.com/t5/technical-forum/let-s-encrypt-with-cloudflare-dns-and-f5-rest-api/m-p/292943(Similar solution to mine, only slightly more robust with OCSP stapling, the DNS instead of HTTP challenge, and with bash instead of python)23KViews6likes18CommentsHelp with iRule logging to local0.
I am trying to log an iRule, but it keeps erroring out. Can you help with what I'm missing? when HTTP_REQUEST { if { [HTTP::path] starts_with "/media" } { pool persist cookie insert log local0. ”media.com_28080 TCP_logging fired, from [IP::client_addr]" } }6.7KViews0likes9CommentsiRule to Forward Traffic Based on URL Name
Hi, I am in a situation where we have shortage of IP address and am looking for an alternative way to forward traffic to pools based on URL. I am not really good with iRule hence looking for assistance and I have seen posts based on URI, but i am looking for assistance with URL. Eg: 1 VIP and based on the url name, the traffic needs to be forwarded to Pool. Help on this is appreciatedSolved3.4KViews0likes10CommentsBIG-IP : iRule test that variable exists and has certain value
F5 BIG-IP Virtual Edition v11.4.1 (Build 635.0) LTM on ESXi What is the simplest most elegant syntax to test that a variable exists and has a certain value ? For example if I want to determine that myvariable exists and has non-zero value , is this the simplest syntax ? when HTTP_REQUEST { set myvariable 0 conditional processing ... set myvariable 1 more processing ... if {[info exists $myvariable] && [$myvariable]} { do something }3KViews0likes2CommentsiRule to decrypt and rewrite RADIUS User-Password AVP
In the RADIUS protocol, the user's cleartext password is transmitted inside Attribute-Value Pair (AVP) 2, padded with null characters as necessary, and then encrypted by the shared secret by XOR'ing it across the authenticator somehow or other. The technical details of how this works is a bit above my level of understanding as I'm not a cryptography expert. We have an infrastructure where our PAN VPN Gateway prompts a user for their username and password. In our environment, after the password, the user appends a fixed-length HOTP token from a Yubikey. The backend FreeRADIUS server has been configured to decrypt the password received, extract the fixed-length token, and perform backend checks to our LDAP and token servers. FYI, the password is encoded as PAP prior to RADIUS encryption in our setup, which is why this works; CHAP would prevent this from working. We've been having trouble with the stability of our FreeRADIUS server and we would like to leverage our much more stable Aruba ClearPass infrastructure which is load balanced globally with our GTMs and LTMs and highly stable. This also moves control of the RADIUS piece away from the systems team and onto the network team (me, specifically). Unfortunately, ClearPass doesn't have a direct mechanism to break the password from the token, and PAN doesn't have a way to transmit the token separately. This is where we would like to leverage an iRule. Basically, the way I envision this working is as such: Decrypt the password+OTP that is received from PAN using the authenticator value and shared secret Rewrite AVP 2 as just the password, encrypted by the shared secret (make sure to adjust the length of the AVP) Insert AVP 17 (which is not defined by the IEFT) with the token (ClearPass can be configured to look for this by modifying its RADIUS dictionary). Rewrite the length value at layer 7 if necessary - not sure if this would happen automatically by the F5; probably not. Ship the modified RADIUS packet to ClearPass I know how to accomplish all of this on the ClearPass side, but my dev skills are weak, I'm not very familiary with Tcl, and I don't have a solid understanding of how to encrypt/decrypt the password correctly. I've search high and low but the only solutions for decrypting the password seem to be written in languages that are even more difficult to understand like C. I obviously understand it is too much to expect someone to write the entire solution for me, but any advice on where to start would be very helpful. I think the trickiest part for me would be the encrypt/decrypt step.Solved2.8KViews0likes21CommentsHow can I preserve the original HTTP Host Header to the Backendservers?
Hi guys, I'm trying the following configuration to loadbalance a RH SSO Key cloack Cluster: clientIP -> https/443 -> (clientssl-Profile) F5-Virtual Server (serverssl-Profile) -> SNAT-IP -> tcp8443 -> Backendserver The Backendserver need to see the original client http Header, so I created a http Profile with the "Insert X-Forwarded-For"-Option enabled. Alternative I tried the following iRule: when HTTP_REQUEST { HTTP::header insert X-Forwarded-For [IP::client_addr] } Unfortunaly, both ways didn't work properly... How can I preserve the whole original HTTP Host Header trough the F5 to the Backendservers? Thank you!2.7KViews0likes3CommentsRequest client cert auth based on URL
I am trying to request client cert authentication based on select URL and it works with a whitelist only but when i use the negate in the datagroup with a datagroup including URI string values it does not work. Sounds perhaps weird but it seems that negating the datagroup with URI entries is not working properly. So when using a whitelist which should be bypassed it works and other websites are authenticated succesfully and a client certificate is being requested. In the client SSL profile we dont use the client cert authentication because we dont want the client cert authentication to be performed for all URLs, hence the SSL::renegotiate option. This seems to do the trick however while negating (not) against a list of websites that should use cert auth does not work and currently dont have the explenation for this behavior. We only have a list of websites that is using cert auth and not a full list of other websites that shouldnt use cert auth, therefor negating the websites would be the easiest solution. Is there any limitation with negating a datagroup with string values? Another side question is that we would like to perform the SSL::renegotiate and request a specific client cert from a certain CA issuer, how could we accomplish that? when HTTP_REQUEST { if {not[class match [string tolower [HTTP::uri]] contains DG_ACC_NO_CERT_AUTH] }{ #HTTP::header insert SSL_CLIENT_CERT [b64encode [SSL::cert 0]] log local0. "certificate not inserted and header SSL_CLIENT_CERT value is: [HTTP::header value SSL_CLIENT_CERT] for host [HTTP::host] and URI: [HTTP::uri]" } else { SSL::session invalidate SSL::authenticate always SSL::authenticate depth 9 SSL::cert mode request SSL::renegotiate enable SSL::renegotiate HTTP::header insert SSL_CLIENT_CERT [b64encode [SSL::cert 0]] log local0. "certificate inserted and header SSL_CLIENT_CERT value is: [HTTP::header value SSL_CLIENT_CERT] for host [HTTP::host] and URI: [HTTP::uri]" } }Solved2.6KViews0likes14CommentsSend an One Time Password (OTP) via the MessageBird SMS gateway
Problem this snippet solves: This snippet makes it possible to send an One Time Password (OTP) via the MessageBird SMS gateway. This snippet uses iRuleLX and the node.js messagebird package to interact with the MessageBird API. How to use this snippet: Prepare the BIG-IP Provision the BIG-IP with iRuleLX. Create LX Workspace: messagebird Add iRule: messagebird_irule Add Extension: messagebird_extension Add LX Plugin: messagebird_plugin -> From Workspace: messagebird Install the node.js messagebird module # cd /var/ilx/workspaces/Common/messagebird/extensions/messagebird_extension # npm install messagebird --save messagebird@2.1.4 node_modules/messagebird # irule To make it works, you need to install the irule on the Virtual Server that publish your application with APM authentication. access profile If you already have an existing access profile, you will need to modify it and include some additionnal configuration in your VPE. If you have no access profile, you can starts building your own based on the description we provide below. Configuring the Visual Policy Editor The printscreen below is a minimal Visual Policy Editor used to make MessageBird OTP Authentication works properly : For a larger version of this image please download here. Irule Event – MessageBird This is an irule event with the ID set to ‘MessageBird’. This will trigger the messagebird_irule to come into action. MessageBird Status This is an empty action with two branches. The branch named "successful" contains the following expression : expr { [mcget {session.custom.messagebird.status}] contains "successful" } Message Box This is a message box that will inform the user that there was a failure sending the One Time Password. messagebird_irule ### ### Name : messagebird_irule ### Author : Niels van Sluis, <niels@van-sluis.nl> ### Version: 20180721.001 ### Date : 2018-07-21 ### when ACCESS_POLICY_AGENT_EVENT { if { [ACCESS::policy agent_id ] eq "MessageBird" } { # Set MessageBird access key set accessKey "<ACCESS_KEY>" # Set user-friendly message which will be send prior to the OTP itself set message "Your OTP is: " # Set username used for logging purposes only set username "[ACCESS::session data get session.logon.last.username]" # Set OTP generated by BIG-IP APM which will be added to the SMS message. set generatedOTP "[ACCESS::session data get session.otp.assigned.val]" # Set phonenumber to send the SMS to set telephoneNumber "[ACCESS::session data get session.ad.last.attr.telephoneNumber]" # Set the sender of the message. This can be a telephone number (including country code) or an alphanumeric string. # In case of an alphanumeric string, the maximum length is 11 characters. set sender "MessageBird" if {[info exists username] && ($username eq "")} { log local0. "Error: username variable is empty; no OTP sent." return } if {[info exists generatedOTP] && ($generatedOTP eq "")} { log local0. "Error: generatedOTP variable is empty; no OTP sent for user $username." return } if {([info exists telephoneNumber] && $telephoneNumber eq "")} { log local0. "Error: telephoneNumber variable is empty; no OTP sent for user $username." return } set rpc_handle [ ILX::init messagebird_plugin messagebird_extension ] if {[ catch { ILX::call $rpc_handle sendOTP $generatedOTP $telephoneNumber $sender $message $accessKey } result ] } { log local0. "sendOTP failed for telephoneNumber: $telephoneNumber, ILX failure: $result" return } log local0. "MessageBird status for user $username ($telephoneNumber): $result" ACCESS::session data set session.custom.messagebird.status $result } } Code : /** *** Name : messagebird_extension *** Author : Niels van Sluis, *** Version: 20180721.001 *** Date : 2018-07-21 **/ // Import the f5-nodejs module. var f5 = require('f5-nodejs'); // Create a new rpc server for listening to TCL iRule calls. var ilx = new f5.ILXServer(); ilx.addMethod('sendOTP', function(req, res) { var generatedOTP = req.params()[0]; var telephoneNumber = req.params()[1]; var sender = req.params()[2]; var message = req.params()[3]; var accessKey = req.params()[4]; var params = { 'originator': sender, 'recipients': [ telephoneNumber ], 'body': message + generatedOTP }; var messagebird = require('messagebird')(accessKey); messagebird.messages.create(params, function (err, response) { if (err) { //console.log(err); return res.reply('failed'); } //console.log(response); return res.reply('successful'); }); }); // Start listening for ILX::call and ILX::notify events. ilx.listen(); Tested this on version: 13.02.4KViews0likes3CommentsCookie Does Not Contain The "secure" Attribute on ltm vip
Our security team reported that multiple vulnerabilities has been detected on one of VIP: 1.2.3.4 (on BIG-IP LTM v12.1.2 version.) Please refer the list as below 1.Cookie Does Not Contain The "secure" Attribute 2.Path-Based Vulnerability 3. Session Cookie Does Not Contain the "Secure" Attribute 4.Slow HTTP POST vulnerability I also Referred this below article but "I don't find any kind of persistence profile enabled and also no custom http profile exist on this mentioned VIP ". K30524234: The HTTPOnly and Secure attributes are enabled by default in the Cookie persistence profile If cookies persistence not enabled on VIP, then is it something need to look at backend server (poolmember). please confirm me Kindly help me to fix this issue Great thanks, Girish2.4KViews0likes3CommentsHow to avoid "Access policy evaluation is already in progress"
Hello, I am using the iRule below to close Outlook Web App 2013 sessions. At the first sight it works correctly and shows the F5 logoff page (/vdesk/hangup.php3). However, OWA 2013 has a javascript that performs a hidden POST to the server on the onunload event to close the session on the server side. This happens right after the session is closed by the F5 logoff page. So it automatically creates a new APM session and when the user clicks on "Click here to login again" he/she sees the message below coming from APM: "Access policy evaluation is already in progress" How can I avoid this message? I tried to do ACCESS::session remove on in response to this last hidden POST but it didn't help. I also tried to introduce some delay before redirecting the user to the F5 logout page in order to let it perform the last POST but it did not work either. when HTTP_REQUEST { Set the uri variable set uri [string tolower [HTTP::uri]] Check if the user clicked the OWA signout link and redirect to the F5 logout page if { $uri contains "/logoff.owa" || $uri contains "/logoff.aspx" } { HTTP::redirect "/vdesk/hangup.php3" } }2.1KViews0likes14Comments