In general, for security purposes, a virtual server's listening scope, as derived by its destination IP address and port combination (among other things), should be kept as narrow as possible to avoid the possibility of it picking up traffic it is not intended to process.
Two separate virtual servers, one listening on port 80 and the other listening on port 443, make life easier as only the port 80 virtual needs an iRule or local traffic policy to unconditionally do the redirect. (Local traffic policies generally perform better.) For example, below are my two virtual server definitions, the default pool for the port 443 virtual, and the local traffic policy on the port 80 virtual server. Notice the HTTP virtual server has no default pool (doesn't need one as any traffic to this virtual will be unconditionally redirected by the local traffic policy. Notice the HTTPS virtual server does have a default pool. You don't need a client-ssl and server-ssl profile on the HTTPS virtual server if you don't have any processing that requires visibility into the unencrypted layer 7 payload, such as might be required if using an F5 Advanced WAF application security policy or other iRule or local traffic policy:
******* VIRTUAL SERVER LISTENING ON PORT 80 *******
ltm virtual http_vs_103 {
creation-time 2021-08-05:13:11:58
destination 10.10.4.103:http
ip-protocol tcp
last-modified-time 2021-08-05:13:11:58
mask 255.255.255.255
policies {
devcentral_test_http_redirect { }
}
profiles {
http { }
tcp { }
}
serverssl-use-sni disabled
source 0.0.0.0/0
translate-address enabled
translate-port enabled
vs-index 8
}
******* LOCAL TRAFFIC POLICY FOR PORT 80 VIRTUAL ********
ltm policy devcentral_test_http_redirect {
controls { forwarding }
last-modified 2021-08-05:13:11:19
requires { http }
rules {
perform_redirect {
actions {
0 {
http-reply
redirect
location "tcl: https://[getfield [HTTP::host] : 1][HTTP::uri]"
}
}
}
}
status published
strategy first-match
}
******* VIRTUAL SERVER LISTENING ON PORT 443 *******
ltm virtual https_vs_103 {
creation-time 2021-08-05:13:12:28
destination 10.10.4.103:https
ip-protocol tcp
last-modified-time 2021-08-05:13:12:28
mask 255.255.255.255
pool https_pool
profiles {
clientssl {
context clientside
}
serverssl {
context serverside
}
tcp { }
}
serverssl-use-sni disabled
source 0.0.0.0/0
translate-address enabled
translate-port enabled
vs-index 9
}
******* LOAD BALANCING POOL FOR PORT 443 VIRTUAL *******
ltm pool https_pool {
members {
172.16.20.1:https {
address 172.16.20.1
}
172.16.20.2:https {
address 172.16.20.2
}
172.16.20.3:https {
address 172.16.20.3
}
}
}
Mayur's suggestion is good if you want to combine the two ports - 80 (HTTP) and 443 (HTTPS) on one virtual server using a port list that contains two ports: 80 and 443. (Port lists are defined under "Shared Objects" in the BIG-IP system's GUI, also known as the Configuration utility.) But it does make things a little more complex as you will also have to conditionally disable SSL on the connection if it is over port 80. An iRule works better for this. For example:
***** IRULE ON SINGLE VIRTUAL LISTENING FOR BOTH 443 AND *) *******
when CLIENT_ACCEPTED {
set ssl_on true
SSL::enable
if { [TCP::local_port] equals 80 } {
set ssl_on false
}
}
when HTTP_REQUEST {
if { !$ssl_on } {
HTTP::respond 301 -version 1.1 Location "https://[HTTP::host][HTTP::uri]" Connection "close"
}
}
******* VIRTUAL SERVER LISTENING ON BOTH 443 AND 80 *******
ltm virtual http_and_https_vs {
creation-time 2021-08-05:12:12:31
ip-protocol tcp
last-modified-time 2021-08-05:12:29:52
pool https_pool
profiles {
clientssl {
context clientside
}
http { }
serverssl {
context serverside
}
tcp { }
}
rules {
devcentral_test_http_redirect
}
serverssl-use-sni disabled
traffic-matching-criteria http_and_https_vs_VS_TMC_OBJ
translate-address enabled
translate-port enabled
vs-index 7
}
******* TRAFFIC MATCHING CRITERIA FOR SINGLE VIRTUAL SERVER *******
ltm traffic-matching-criteria http_and_https_vs_VS_TMC_OBJ {
destination-address-inline 10.10.4.102
destination-port-list http_and_https
protocol tcp
source-address-inline 0.0.0.0
}
******* PORT LIST DEFINITION *******
net port-list /Common/http_and_https {
ports {
80 { }
443 { }
}
}
Finally, in Stephan's suggestion, note that load balancing information, such as LB::server port, is not always available yet at the CLIENT_ACCEPTED and HTTP_REQUEST events, assuming an HTTP-type profile is also assigned to the virtual server. When an HTTP profile is assigned, TCP connection setup on the server-side is delayed until after the first HTTP request is received and whatever conditional logic you have defined for the HTTP_REQUEST event (such as in an iRule or local traffic policy) have run. This allows an iRule or local traffic policy to be involved in selecting the pool, pool member, or node to used during a subsequent load balancing decision. You would also want to check the destination port sent by the client (TCP::local_port) not the destination port of the pool member selected by load balancing (LB::server port), as the latter can be different from the former. For example, the client might connect to the virtual server on port 443 (standard browser behavior when using "https://") but the pool members could be configured with alternate non-standard secure ports, such as 8443 (or whatever the server admin's chose).
One final note, if you are sure your client's never connect to the virtual server using a URL that directly specifies the port (for example, www.f5trn.com:80/...) you can change the redirects to just "https://[HTTP::host][HTTP::uri]" and ditch the getfield command parts, which causes additional overhead.