devops
24141 TopicsKong API Gateway and BIG-IP on OpenShift
This article outlines the control plane and data plane for a BIG-IP + Kong API Gateway architecture. BIG-IP controls TLS termination, WAF, and ingress to cluster, and Kong performs rate limiting, authentication, and routing to endpoints.135Views2likes2CommentsUsing eBPF Filters to Capture and Inspect BIG-IP CNE CNF Traffic
Introduction In this article we go through integrated setup with both F5 BIG-IP eBPF Observability (EOB) and F5 BIG-IP Cloud-Native Edition (CNE) CNFs. In our lab we go through deploying BIG-IP EOB and start capturing client traffic within cnf-fw-01 namepsace. Capturing the traffic in this cloud-native architecture is as easy as creating the directive which automatically creates the streams and show the live capture. Step by Step deployment This video walks us through deploying the BIG-IP EOB through openshift operators hub, then creating the required directive to capture the client processed traffic and show it over BIG-IP EOB dashboard. Related Content F5 BIG-IP eBPF Observability (EOB) Deployment walkthrough | DevCentral eBPF Observability for Kubernetes & Cloud-Native Apps BIG-IP eBPF Observability (EOB) deployment walkthrough
50Views1like0CommentsWhy is decrypted Gemini traffic sent as HTTP over 443 via Explicit Proxy DLP, not https
Hello DevCentral Community, I am running F5 SSL Orchestrator (SSLO) integrated with an inline DLP device configured in HTTP Proxy (Explicit Proxy) mode. SSLO successfully decrypts outbound TLS traffic and forwards it to the DLP proxy in plaintext HTTP/1.1 format. However, I noticed a difference in how the destination port is handled depending on the target site: ChatGPT / Claude: The decrypted HTTP/1.1 request is forwarded to the DLP Proxy with the destination port changed to Port 80 (Explicit HTTP). Gemini (gemini.google.com): The decrypted HTTP/1.1 request is also forwarded as plaintext to the DLP Proxy, but the destination port remains as Port 443 (HTTP over 443). Since the DLP is receiving this decrypted traffic via an Explicit HTTP Proxy connection, why does SSLO preserve Port 443 specifically for Gemini while translating the destination port to 80 for ChatGPT and Claude? Could this be related to how SSLO handles the original HTTP CONNECT tunneling request, ALPN negotiation, or L7 HTTP Profile settings when rewriting proxy headers for Google services? Any advice on how to unify the behavior so Gemini traffic is also forwarded as Explicit HTTP over Port 80 would be greatly appreciated. Thank you! ps. The following error message appears when port remap is enabled: [OrchestratorConfigProcessor] Deployment failed for Error: [HAAwareICRDeployProcessor] Error: transaction failed:01071912:3: CLIENTSSL_HANDSHAKE event in rule (/Common/ssloS_GENERIC_HTTP.app/ssloS_GENERIC_HTTP-port_remap) requires an associated CLIENTSSL profile on the virtual-server (/Common/ssloS_GENERIC_HTTP.app/ssloS_GENERIC_HTTP-t-4).29Views0likes0CommentsObserving F5 BIG-IP CNE CNFs - V2 Metrics Aggregation walkthrough
Introduction Cloud-native distribution solves scale and resilience, but it fragments visibility. When TMM pods run across multiple Kubernetes nodes, logs and metrics scatter with them. The observer pod exists to pull that telemetry back into a single, coherent view. In a CNF deployment, multiple TMM pods run across Kubernetes nodes, each handling separate traffic slices. A coherent view of the entire dataplane requires aggregating those per-pod statistics. CNFs generate stats at high frequency across all TMM pods. Without aggregation, per-pod metric streams multiply quickly and do not compose cleanly into useful dashboard or alerting data. if you upgraded recently to CNF2.2+ you may have encountered new metrics behavior where, that's what we are covering here how V2 metrics change the metric collection and troubleshooting behavior. TODA Architecture: Four Components TODA (Telemetry, Observability, Diagnostics, and Analytics) is the stats collection and aggregation layer for CNFs. The distributed model has four roles: Component Description TMM Scraper Sidecar container in each TMM pod. Replaces tmstatsd. Serves metrics from tmctl over a gRPC response stream when requested by a Receiver. Receiver Runs as a StatefulSet. Scrapes metrics from assigned TMM Scrapers, persists them, and forwards to the Observer over gRPC with mutual TLS (mTLS). Handles metrics from terminated TMM pods so cumulative data is not lost mid-scrape. Observer Runs as a StatefulSet. The aggregation engine. Pulls from Receivers, aggregates metrics across all TMMs per table, and exports to the OTEL collector. Emits internal telemetry covering gRPC call metrics, aggregation performance, and storage state. Operator Runs as a Deployment. Orchestrates lifecycle: discovers TMM Scrapers, Receivers, and Observers; load-balances TMMs across Receivers; applies aggregation mode and collection interval settings via a ConfigMap. V1 vs. V2 CNF Metrics Choose before deploying. V1 and V2 use incompatible metric naming in Prometheus, so PromQL queries written for one will not work on the other. V1 (legacy): tmstatsd runs in each TMM pod and streams metrics directly to OTEL with no aggregation. Metric names look like: virtual_server_stat/spk-app-1-spk-app-tcp-8050-f5ing-testapp-virtual-server/clientside.bytes_out Each metric carries a tmmID attribute identifying the source pod. Six TMM pods means six separate data streams for the same virtual server. Dashboards scale poorly. V2 (current): The Receiver and Observer aggregate before export to OTEL. The equivalent metric: f5.virtual_server.clientside.received.bytes Attributes include f5.virtual_server.name, k8s.namespace.name, and observer.job.mode: aggregated. One metric, unified across all TMMs, with naming aligned to OpenTelemetry semantic conventions. Use V2 for new deployments. V1 remains only for environments not yet migrated. Deploying the Observer with Helm Install the Observer in the same namespace as your F5Ingress. Get the chart version from your CNFs software package: cd cnfinstall ls -1 tar | grep observer # f5-toda-observer-v4.56.4-0.0.15.tgz Create an observer_values.yaml. At minimum, set the image registry and storage class: image: repository: your-registry.example.com persistence: storageClassName: '' accessMode: ReadWriteOnce size: 3Gi platformType: robin fluentbit_sidecar: image: repository: your-registry.example.com fluentbit: tls: enabled: true fluentd: host: f5-toda-fluentd.cnf-gateway.svc.cluster.local. Install: helm install observer f5-toda-observer-<VERSION>.tgz -f observer_values.yaml Note: The Operator and Receivers share a volume. If they run on the same node, any StorageClass works. If Receivers are distributed across multiple nodes, use a ReadWriteMany-compatible StorageClass, NFS is the standard choice. In my lab I'm installing to a cne-core namespace instead of default namespace. Also, Make sure to update BIG-IP Controller ingress values, as below f5-tmm: ... ... observer: enabled: true image: repository: local.registry.com f5-toda-logging: enabled: true type: stdout fluentd: host: f5-toda-fluentd.cne-core.svc.cluster.local. tmstats: enabled: false Once updated upgrade your helm installation helm upgrade f5ingress f5ingress-v15.82.0-0.2.50.tgz -f deployment/values-ingress-v2.yaml -n cnf-fw-01 Now, you have all the components ready, you can reference the below steps for additional integrations with Grafana and Prometheus. Lab notes In my lab there are some commands I had to run to adjust to the openshift deployment, helm upgrade observer f5-toda-observer-5.22.10-0.2.4.tgz -n cne-core --reuse-values --set persistence.storageClassName=openebs-hostpath oc adm policy add-scc-to-user hostmount-anyuid -z f5-observer -n cne-core oc adm policy add-scc-to-user hostmount-anyuid -z f5-observer-operator -n cne-core oc adm policy add-scc-to-user hostmount-anyuid -z f5-observer-receiver -n cne-core oc secrets link f5-observer <secret> --for=pull -n cne-core oc secrets link f5-observer-operator <secret> --for=pull -n cne-core oc secrets link f5-observer-receiver <secret> --for=pull -n cne-core Wiring Prometheus and Grafana to CNF Metrics The OTEL collector exposes a Prometheus-compatible endpoint on TCP port 9090. It requires mTLS, so valid certificates must be in place before the scrape job succeeds. Step 1 — Create a Prometheus namespace and certificate: kubectl create namespace prometheus kubectl apply -f prom-certs.yaml # cert-manager Certificate manifest Step 2 — Configure Prometheus to scrape OTEL with TLS: serverFiles: prometheus.yml: scrape_configs: - job_name: bnk-otel scheme: https static_configs: - targets: - otel-collector-svc.default.svc.cluster.local:9090 tls_config: cert_file: /etc/prometheus/certs/tls.crt key_file: /etc/prometheus/certs/tls.key ca_file: /etc/prometheus/certs/ca.crt insecure_skip_verify: false server: extraVolumes: - name: prometheus-tls secret: secretName: prometheus-client-secret extraVolumeMounts: - name: prometheus-tls mountPath: /etc/prometheus/certs readOnly: true global: scrape_interval: 10s service: type: NodePort nodePort: 31929 persistentVolume: enabled: false Step 3 — Deploy via Helm: helm install prometheus oci://ghcr.io/prometheus-community/charts/prometheus \ -n prometheus --atomic -f values.yaml //Update otel config map and change line 47 to following debug: verbosity: detailed //Then add following at the end of the configmap exporters: - otlp - deb Step 4 — Verify the scrape target is healthy: curl http://<node-ip>:31929/api/v1/targets | jq Step 5 — List all CNF metrics currently ingested: curl http://<node-ip>:31929/api/v1/label/__name__/values | jq Step 6 — Run a quick query to validate data is flowing: curl "http://<node-ip>:31929/api/v1/query?query=f5_tmm_f5_pool_member_serverside_connections_count_total" | jq For Grafana, add Prometheus as a data source. F5 provides a pre-built Observer dashboard JSON on CloudDocs. The dashboard has three sections: gRPC metrics — Communication performance between Observer containers: call latency and request counts. Go Runtime metrics — Pod resource consumption: goroutine counts, heap memory, object allocation rates. Storage/Aggregation metrics — How the Observer handles dead TMM pod data. When a TMM pod terminates, the Observer runs merge operations to consolidate its metrics. This section shows whether those operations are healthy. Note, you need to update your OTEL definition to include the below //Update otel config map debug: verbosity: detailed //Then add following at the end of the configmap exporters: - otlp - deb //Apply updated OTEL configmap Once done, proceed to rollout the otel deployment oc rollout restart deployment otel-collector -n cnf-fw-01 oc logs otel-collector-6b5c9d5f89-qccqv -f | grep profile_tcp -> table: Str(profile_tcp_stat) -> table: Str(profile_tcp_stat) -> table: Str(profile_tcp_stat) -> table: Str(profile_tcp_stat) -> table: Str(profile_tcp_stat) -> table: Str(profile_tcp_stat) -> table: Str(profile_tcp_stat) -> table: Str(profile_tcp_stat) -> Name: f5.profile_tcp.accepts -> f5.profile_tcp.name: Str(tmstat_tcp) -> f5.profile_tcp.vs_name: Str(qkview_for_tmstatsd) -> observer.job.name: Str(cnf-fw-01/default-scrape-template-66456f74c4/default-job-profile_tcp_stat) -> Name: f5.profile_tcp.accepts -> f5.profile_tcp.name: Str(_mcptcp) -> f5.profile_tcp.vs_name: Str(grpc_mt_10:2) -> observer.job.name: Str(cnf-fw-01/default-scrape-template-66456f74c4/default-job-profile_tcp_stat) -> Name: f5.profile_tcp.accepts -> f5.profile_tcp.name: Str(_mcptcp) -> f5.profile_tcp.vs_name: Str(grpc_mt_4:0) -> observer.job.name: Str(cnf-fw-01/default-scrape-template-66456f74c4/default-job-profile_tcp_stat) -> Name: f5.profile_tcp.accepts -> f5.profile_tcp.name: Str(_mcptcp) -> f5.profile_tcp.vs_name: Str(_grpc_tmm_listener_9) -> observer.job.name: Str(cnf-fw-01/default-scrape-template-66456f74c4/default-job-profile_tcp_stat) -> Name: f5.profile_tcp.accepts -> f5.profile_tcp.name: Str(_cgctcp_in) Troubleshooting via Metrics V2 Now, we have better capabilities of actually monitoring traffic across multiple pods and TMMs from single location, [cloud-user@ocp-provisioner f5-cne-2.2.0]$ oc exec sts/f5-observer-receiver -n cne-core -- mdb --list | grep "/cnf-fw-01/" Defaulted container "f5-observer-receiver" out of: f5-observer-receiver, fluentbit 2026/07/15 18:12:09 INFO dialing to observer addr=0.0.0.0:8088 f5-log-ID=0612007a cnf-fw-01/tmm/f5-tmm-fcc888779-w98zh:f5-tmm:ae28ae8bf4ecb2de9f9a1d0674fb47f8acb2eab1bb46727e285e2cbcae87266b/cnf-fw-01/fw_context_stat 2026-07-15 18:11:22 763 cnf-fw-01/tmm/f5-tmm-fcc888779-w98zh:f5-tmm:ae28ae8bf4ecb2de9f9a1d0674fb47f8acb2eab1bb46727e285e2cbcae87266b/cnf-fw-01/virtual_server_stat 2026-07-15 18:11:22 3101 cnf-fw-01/tmm/f5-tmm-fcc888779-w98zh:f5-tmm:ae28ae8bf4ecb2de9f9a1d0674fb47f8acb2eab1bb46727e285e2cbcae87266b/cnf-fw-01/dns_cache_resolver_stat 2026-07-15 18:11:22 5162 cnf-fw-01/tmm/f5-tmm-fcc888779-w98zh:f5-tmm:ae28ae8bf4ecb2de9f9a1d0674fb47f8acb2eab1bb46727e285e2cbcae87266b/cnf-fw-01/profile_dns_stat 2026-07-15 18:11:22 7500 cnf-fw-01/tmm/f5-tmm-fcc888779-w98zh:f5-tmm:ae28ae8bf4ecb2de9f9a1d0674fb47f8acb2eab1bb46727e285e2cbcae87266b/cnf-fw-01/profile_tcp_stat 2026-07-15 18:11:22 972 cnf-fw-01/tmm/f5-tmm-fcc888779-w98zh:f5-tmm:ae28ae8bf4ecb2de9f9a1d0674fb47f8acb2eab1bb46727e285e2cbcae87266b/cnf-fw-01/pool_member_stat 2026-07-15 18:11:22 2156 cnf-fw-01/tmm/f5-tmm-fcc888779-w98zh:f5-tmm:ae28ae8bf4ecb2de9f9a1d0674fb47f8acb2eab1bb46727e285e2cbcae87266b/cnf-fw-01/fw_rule_stat 2026-07-15 18:11:22 1588 cnf-fw-01/tmm/f5-tmm-fcc888779-w98zh:f5-tmm:ae28ae8bf4ecb2de9f9a1d0674fb47f8acb2eab1bb46727e285e2cbcae87266b/cnf-fw-01/dos_stat 2026-07-15 18:11:22 3828 cnf-fw-01/tmm/f5-tmm-fcc888779-w98zh:f5-tmm:ae28ae8bf4ecb2de9f9a1d0674fb47f8acb2eab1bb46727e285e2cbcae87266b/cnf-fw-01/fw_container_stat 2026-07-15 18:11:22 1213 Now, let's have a closer look at one of the segments, below is the FW context [cloud-user@ocp-provisioner f5-cne-2.2.0]$ oc exec sts/f5-observer-receiver -n cne-core -- mdb --segment cnf-fw-01/tmm/f5-tmm-fcc888779-w98zh:f5-tmm:ae28ae8bf4ecb2de9f9a1d0674fb47f8acb2eab1bb46727e285e2cbcae87266b/cnf-fw-01/fw_context_stat Defaulted container "f5-observer-receiver" out of: f5-observer-receiver, fluentbit 2026/07/15 18:12:49 INFO dialing to observer addr=0.0.0.0:8088 f5-log-ID=0612007a -----BEGIN RESOURCE----- Meta: Name: fw_context_stat Unit: Annotations: k8s.namespace.name: cnf-fw-01 Labels: f5.firewall.context.context_name: cnf-fw-01-forwarding-any-virtual-server-SecureContext_vs f5.firewall.context.context_type: virtual f5.firewall.context.policy_type: 1 TTL: 0s Value: [2428 0 0 0] -----END RESOURCE----- Now, let's have a look at the virtual servers stats [cloud-user@ocp-provisioner f5-cne-2.2.0]$ oc exec sts/f5-observer-receiver -n cne-core -- mdb --segment cnf-fw-01/tmm/f5-tmm-fcc888779-w98zh:f5-tmm:ae28ae8bf4ecb2de9f9a1d0674fb47f8acb2eab1bb46727e285e2cbcae87266b/cnf-fw-01/virtual_server_stat Defaulted container "f5-observer-receiver" out of: f5-observer-receiver, fluentbit 2026/07/15 18:14:24 INFO dialing to observer addr=0.0.0.0:8088 f5-log-ID=0612007a -----BEGIN RESOURCE----- Meta: Name: virtual_server_stat Unit: Annotations: k8s.namespace.name: cnf-fw-01 Labels: f5.virtual_server.destination: 0.0.0.0 f5.virtual_server.name: cnf-fw-01-forwarding-any-virtual-server-SecureContext_vs f5.virtual_server.source: 0.0.0.0 TTL: 0s Value: [1844667 108023672 0 4 28282 69732 2542 0 0 0 0 108023672 1844587 0 4 69732 28280 2542] -----END RESOURCE----- -----BEGIN RESOURCE----- Meta: Name: virtual_server_stat Unit: Annotations: k8s.namespace.name: cnf-fw-01 Labels: f5.virtual_server.destination: 10.1.20.100 f5.virtual_server.name: cnf-fw-01-cnf-dohapp-virtual_server f5.virtual_server.source: 0.0.0.0 TTL: 0s Value: [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] -----END RESOURCE----- -----BEGIN RESOURCE----- Meta: Name: virtual_server_stat Unit: Annotations: k8s.namespace.name: cnf-fw-01 Labels: f5.virtual_server.destination: 10.1.30.100 f5.virtual_server.name: cnf-fw-01-dnsx-app-listener-virtual_server f5.virtual_server.source: 0.0.0.0 TTL: 0s Value: [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] -----END RESOURCE----- With Observer you have aggregated observer receiver to monitor and observe your CNF deployment. Conclusion As a conclusion, why would you go to V2 metrics vs V1, there are four pain points with V1 in distributed environments: Stream count; One virtual server across six TMM pods gives you six independent metric series in Prometheus, same stat, six rows, differentiated only by tmmID. That's not an observability system. That's a spreadsheet you have to reassemble manually every time you open Grafana. Data loss; A pod gets evicted mid-scrape and its cumulative counters are gone. The Receiver in the TODA pipeline holds that data in a local volume and merges it before export. Your charts stay clean. PromQL tax; In V1, every panel needs a sum() by (virtual_server) wrapper or the numbers are wrong. Not approximately wrong, it's wrong by a factor of N. V2 aggregates inside the pipeline, at the Observer, before the metric ever reaches Prometheus. One series. Use it directly. The naming; V1 embeds the VS name in the metric path. That's not how Prometheus is supposed to work, and everything downstream, like alerting rules, federation, label matchers fight it. V2 puts the VS identity where it belongs: as an attribute, f5.virtual_server.name. Short metric name, proper labels, and PromQL that actually reads like PromQL. The TODA pipeline ( TMM Scraper, Receiver, Observer ) exists specifically to close the visibility gap. The Receiver is the persistence layer. The Observer is the aggregation engine. Together they turn N pod streams into one coherent signal. If you take one thing from this: aggregation has to happen somewhere. In V1, it happens in your head, on every dashboard panel, every time. In V2, it happens in the pipeline, once, before the data leaves the cluster. That's the whole difference. Related Resources Distributed TODA for Stats Aggregation CNFs Event Logs Performance Visualization (Prometheus + Grafana) OTEL Statistics Reference CNF Log Formats Reference Debug Sidecar Overview Troubleshooting Common Errors
102Views3likes0CommentsF5 Distributed Cloud – Unit and Integration tests with Terraform
Introduction The Terraform test framework provides module authors with an integrated way to run unit and integration tests. It verifies that code changes do not introduce breaking behavior before production rollout. Terraform test prevents any risk on the existing state or infrastructure by keeping the state file in memory as an ephemeral entity. It never writes to a terraform state file, ensuring tests run completely separate from regular plan or apply workflows. The framework supports two testing models: Unit Testing: Runs a terraform plan to validate custom logic, calculations, and input conditions without provisioning real resources. This mode is fast, free, and runs entirely in memory. Integration Testing: Runs terraform apply to create temporary infrastructure, perform assertions against live resources, and automatically destroy those resources when the test completes. By default, test runs use command = apply, so integration testing creates real infrastructure and validates behavior against those deployed resources. To perform unit testing without creating infrastructure, you can override this behavior by setting the command attribute in a run block to plan. Configuration The following example shows a directory structure for terraform native tests: The main terraform configuration is based on the WAAP protected HTTP applications example: https://github.com/f5devcentral/f5-professional-services/tree/main/examples/f5-distributed-cloud/terraform/f5-xc-terraform-test. The compliance.tftest.hcl includes the logic to validate the following test cases: Check Compliance Run block Validate that a required Service Policy is inherited from the namespace if the Load Balancer is advertised on the public network and the origin server is behind a Customer Edge (CE) Security service_policy Validate that a required Service Policy is applied directly to the Load Balancer if it is advertised on the public network and the origin server is behind a CE Security service_policy Check that an App Firewall policy is applied to the HTTP Load Balancer if it is advertised on the public network Security app_firewall Verify that the Load Balancer name complies with the RFC 1035 Domain Names. Naming Governance http_load_balancer_name Verify that the HTTP Load Balancer quota is not exceeded. Resource governance quota_usage A helper module is included for managing test-specific resources such as data sources. It uses F5 Distributed Cloud Services API to get current quota usage for HTTP Load Balancers and the active namespace Service Policies: # setup module # Fetch data from a REST API data "http" "xc_quota_usage" { url = "${var.api_url}/web/namespaces/system/quota/usage" request_headers = { Accept = "application/json" } client_cert_pem = file(var.f5-xc_cert) client_key_pem = file(var.f5-xc_key) } data "http" "xc_active_sp" { url = "${var.api_url}/config/namespaces/${var.namespace}/active_service_policies" request_headers = { Accept = "application/json" } client_cert_pem = file(var.f5-xc_cert) client_key_pem = file(var.f5-xc_key) } # Use the response locals { xc_quota_usage = jsondecode(data.http.xc_quota_usage.response_body) xc_active_sp = jsondecode(data.http.xc_active_sp.response_body) } Below is the outputs.tf file for the module: output "xc_quota_usage" { value = { "HTTP" = local.xc_quota_usage.objects.http_loadbalancer.usage.current } } output "xc_active_sp" { value = local.xc_active_sp.service_policies[*].name } The variables.tf file used by the module is shown below: # setup module variables variable "tenant" { default = "<tenant_id>" } variable "api_url" { default = "https:// <tenant_name>.console.ves.volterra.io/api" } variable "f5-xc_cert" { default = "./certs/xc.crt" } variable "f5-xc_key" { default = "./certs/xc.key" } variable "namespace" { default = "default" } The main test file included in the test directory, compliance.tftest.hcl, contains the test logic within run blocks that applies a terraform “plan” or “apply” command to perform assertions on the resulting state: # compliance.tftest.hcl run "global_setup" { # This block initializes a module to fetch information required # for testing. module { source = "./tests/modules/setup" } } run "service_policy" { command = plan variables { required_service_policies = "allow-vpn-ip-demo-sp" } # Check that a required Service Policy is inherited from the # namespace if the Load Balancer is advertised on the public # network and the origin server is behind a CE assert { condition = ( module.http-lb.app_lb_default_vip == false ? true : module.origin.private_origin == false ? true : (module.http-lb.app_lb_ns_service_policies == false ? true : contains(flatten(run.global_setup.xc_active_sp), var.required_service_policies)) ) error_message = "Service Policy \"${var.required_service_policies}\" must be associated with Load Balancer ${module.http-lb.app_lb_name} or inherited from the namespace." } # Check that a required Service Policy is applied directly to the # Load Balancer if it is advertised on the public network and the # origin server is behind a CE assert { condition = ( module.http-lb.app_lb_default_vip == false ? true : module.origin.private_origin == false ? true : (module.http-lb.app_lb_ns_service_policies == true ? true : contains(flatten(module.http-lb.app_lb_active_service_policies), var.required_service_policies)) ) error_message = "Service Policy \"${var.required_service_policies}\" must be explicitly associated with Load Balancer ${module.http-lb.app_lb_name} or inherited from the namespace." } } run "app_firewall" { command = plan # Check that an App Firewall policy is applied to the HTTP Load # Balancer if it is advertised on the public network assert { condition = module.http-lb.app_lb_default_vip == false ? true : length(module.http-lb.app_lb_app_firewall) > 0 error_message = "An App Firewall must be associated with Load Balancer ${module.http-lb.app_lb_name} if advertised on Internet " } } run "http_load_balancer_name" { command = plan # Check that the Load Balancer name is correct assert { condition = can(regex("^[a-z]([-a-z0-9]*[a-z0-9])?$", local.http-lb-name)) && length(local.http-lb-name) <= 63 error_message = "The resource name must be a valid DNS-1035 label: 1-63 lower-case alphanumeric characters or '-', starting with a letter and ending with an alphanumeric character." } } run "quota_usage" { command = plan # Check that the HTTP Load Balancer quota is not exceeded assert { condition = run.global_setup.xc_quota_usage["HTTP"] <= 200 error_message = "HTTP Load Balancer quota exceeded" } } The assert blocks within each run block define conditions that must evaluate to true for the test to pass. Running the tests 1. Initialize Terraform configuration. To run the tests, the Terraform workspace needs to be initialized to configure the backend and install all providers and modules referred to in the configuration (main and test): 2. Running the initial test When the terraform test command is executed, it scans the current root directory ./ and the subdirectory ./tests/ for files with .tftest.hcl or tftest.json extensions. To overwrite the default discovery behavior, the following command line flags can be used: Behavior Flag Example Change the testing directory test -test-directory terraform test -test-directory=integration-tests Run a specific test file filter terraform test -filter=tests/validation.tftest.hcl This is the main tfvars file, used to validate the run blocks { "tenant": "<tenant_id>", "api_url": "https://<tenant_name>.console.ves.volterra.io/api", "api_p12_file": "./certs/api_credential.p12", "f5-xc_cert": "./certs/xc.crt", "f5-xc_key": "./certs/xc.key", "base": "demo-app", "namespace": "demo", "domains": ["demo-app.demo.net"], "origin_servers": [ { "origin": "1.2.3.4", "site": "", "virtual_site": "onprem-demo-vs", "network": "inside" }, { "origin": "5.6.7.8", "site": "", "virtual_site": "onprem-demo-vs", "network": "outside" } ], "environment": "prod", "waf_policy": true, "service_policy": [ { "name": "allow-vpn-ip-demo-sp", "namespace": "shared" }, { "name": "allowed-sources-demo-sp", "namespace": "demo" } ], "origin_pool_port": 80, "use_tls": false } When all the assertions in the execution block pass, the test is considered successful 3. Validation of assertions 3.1. Unit testing To perform unit testing, the tests can be executed using the command = plan attribute. Setting the command to plan forces Terraform to only generate an execution plan and validate your configuration logic without creating real cloud resources, making the process fast and safe. Example 1: The required Service Policy is not active in the namespace, the Load Balancer is configured with the default setting to apply namespace policies, it is advertised on the public network, and the origin server is behind a CE. Service Policy "allow-vpn-ip-demo-sp" service policy is not in the namespace Active Service Policies: Example 2: The required Service Policy is not associated with the Load Balancer when a specific list of Service Policies is applied, it is advertised on the public network, and the origin server is behind a CE. Service Policy "allow-vpn-ip-demo-sp" service policy is removed from the tfvars file: Example 3: Verify that an App Firewall policy is applied to the HTTP Load Balancer if it is advertised on the public network. To force this test to fail, the waf_policy variable is set to false in the tfvars file: Example 4: Verify that the HTTP load balancer name conforms with the core RFC DNS 1035 rules. To force this test to fail, a period is added to the base variables in terraform.tfvars.json: Example 5: Verify that the HTTP load balancer quota has not been exceeded. To force this test to fail, a value lower than the current quota is added to the condition: 3.2. Integration testing To perform integration tests, we can run them using the command = apply attribute. By setting the command as apply, Terraform provisions real infrastructure, runs the assertions against the live resources and then automatically destroys them. Example: The required Service Policy is not active in the namespace, the Load Balancer is configured with the default setting to apply namespace policies, it is advertised on the public network, and the origin server is behind a CE. Service Policy "allow-vpn-ip-demo-sp" service policy is not in the namespace Active Service Policies: The ephemeral resources are created: The audit log entries record the creation and deletion of resources: Conclusion The native terraform test framework offers a secure and unified way to validate F5 Distributed Cloud Services Infrastructure by operating against test-specific, short-lived resources. This lets you detect breaking changes early and use the assertions as built-in guardrails, ensuring infrastructure code quality without complex external dependencies.193Views0likes0Commentsinsert HTTP header according to a value received in Radius accounting
Hi, I'd like to know if the following is somehow achievable: I want that from Every Radius Accounting message (UDP 1813) the BIGIP will look for 3 attributes: Calling-Station-Id, Framed-IP-Address & Acct-Status-Type In case the Radius attribute Acct-Status-Type=1 (Start) the BIGIP will insert an entry to a table in which the key will be the Framed-IP-Address and the value will be the Calling-Station-Id. In case the Radius attribute Acct-Status-Type=2 (Stop) the BIGIP will remove the value of an entry in the same table (remove the value which is the calling-station-id assigned to the framed-ip-address) Then when HTTP transaction arrive from a client IP which appears as a key in the table, the BIGIP will insert the value assigned to that key in the table (i.e. the corresponding Calling-Station-Id) to an HTTP header called "MSISDN". Example: Radius accounting arrives with following attributes: Acct-Status-Type=1 Calling-Station-Id=123456789 Framed-IP-Address=1.1.1.1 An entry is inserted to a table 1.1.1.1 (key) -> 123456789 (value) HTTP request arrives from source IP 1.1.1.1 The F5 will insert a header "MSISDN: 123456789" to the HTTP request before forwarding. Radius accounting arrives with following attributes: Acct-Status-Type=2 Calling-Station-Id=123456789 Framed-IP-Address=1.1.1.1 The value is removed 1.1.1.1 (key) -> (empty) HTTP request arrives from source IP 1.1.1.1 The F5 will not insert a header (not "MSISDN: 123456789" and not "MSISDN: ") Hope it's clear. Thanks in advance, Yaniv847Views0likes11CommentsF5 Insight - SSLO Integration
Could you please advise how this section is supposed to be integrated? We have already integrated our SSLO device, but nothing is displayed in this section. If there is any documentation or knowledge base article describing the integration process, we would appreciate it if you could share it with us.Solved115Views0likes1CommentWeblogic 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.3KViews2likes9CommentsF5 rSeries: Next-Generation Fully Automatable Hardware
What is rSeries? F5 rSeries is a rearchitected, next-generation hardware platform that scales application delivery performance and automates application services to address many of today’s most critical business challenges. F5 rSeries is a key component of the F5 Application Delivery and Security Platform (ADSP). The additional benefit of automation capabilities can greatly simplify the process of deploying F5 products. A significant amount of time and resources are saved due to automation, which translates to more time to perform critical tasks. F5OS rSeries UI Demo Video Why is this important? Get more done in less time by using a highly automatable hardware platform that can deploy software solutions in seconds, not minutes or hours. Increased performance improves ROI: The rSeries platform is a high performance and highly scalable appliance with improved processing power. Running multiple versions on the same platform allows for more flexibility than previously possible. Pay-as-you-Grow licensing options that unlock more CPU resources. Key rSeries Use-Cases NetOps Automation Shorten time to market by automating network operations and offering cloud like orchestration with full stack programmability Drive app development and delivery with self-service and faster response time Business Continuity Drive consistent policies across on-prem and public cloud and across hardware and software based ADCs Build resiliency with rSeries’ superior performance and failover capabilities Future proof investments by running multiple versions of apps side-by-side; migrate applications at your own pace Cloud Migration On-Ramp Accelerate cloud strategy by adopting cloud operating models and on-demand scalability with rSeries and use that as on ramp to cloud Dramatically reduce TCO with rSeries systems; extend commercial models to migrate from hardware to software or as applications move to cloud Automation Capabilities Declarative APIs and integration with automation frameworks (Terraform, Ansible) greatly simplifies operations and reduces overhead: AS3 (Application Services 3 Extension): A declarative API that simplifies the configuration of application services. With AS3, customers can deploy and manage configurations consistently across environments. Ansible Automation: Prebuilt Ansible modules for rSeries enable automated provisioning, configuration, and updates, reducing manual effort and minimizing errors. Terraform: Organizations leveraging Infrastructure as Code (IaC) can use Terraform to define and automate the deployment of rSeries appliances and associated configurations. Example json file: Example of running the Automation Playbook: Example of the results: More information on Automation: Automating F5OS on rSeries GitHub Automation Repository Specialized Hardware Performance rSeries offers more hardware-accelerated performance capabilities with more FPGA chipsets that are more tightly integrated with TMOS. It also includes the latest Intel processing capabilities. This enhances the following: SSL and compression offload L4 offload for higher performance and reduced load on software Hardware-accelerated SYN flood protection Hardware-based protection from more than 100 types of denial-of-service (DoS) attacks Support for F5 Intelligence Services Conclusion The F5 rSeries platform addresses the modern enterprise’s need for high-performance, scalable, and efficient application delivery and security solutions. By combining cutting-edge hardware capabilities with robust automation tools and flexible migration options, rSeries empowers organizations to seamlessly transition from legacy platforms while unlocking new levels of performance and operational agility. Whether driven by the need for increased throughput, advanced multi-tenancy, the rSeries platform stands as a future-ready solution for securing and optimizing application delivery in an increasingly complex IT landscape. Related Content Cloud Docs rSeries Guide F5 rSeries Appliance Datasheet F5 VELOS: A Next-Generation Fully Automatable Platform DEMO: The Next Generation of F5 Hardware is Ready for you
1.8KViews2likes0CommentsF5OS cloud-init on 21.1 does tenants come with DO and AS3 RPM installed?
Hello Everyone, This great new feature https://techdocs.f5.com/en-us/bigip-21-1-0/big-ip-f5os-cloud-init-support-velos-rseries/cloud-init-support-velos-rseries.html is not very well described. I think F5 making a demo session or a Guide with pictures will be helpful. For example do the F5os Tenants come with RPM AS3 and DO installed by default for this to work ? Other than that it is mentioned that the DO yaml file needs to be hosted on F5OS ? Where exactly ?320Views1like6Comments