End of Sale Notice:

F5 NGINX is announcing the End of Sale (EoS) for NGINX Controller API Management Module, effective January 1, 2024.

F5 maintains generous lifecycle policies that allow customers to continue support and receive product updates. Existing NGINX Controller API- Management customers can continue to use the product past the EoS date. License renewals are not available after September 30, 2024.

See our End of Sale announcement for more details.
End of Sale Notice:

F5 NGINX is announcing the End of Sale (EoS) for NGINX Controller Application Delivery Module, effective January 1, 2024.

F5 maintains generous lifecycle policies that allow customers to continue support and receive product updates. Existing NGINX Controller Application Delivery customers can continue to use the product past the EoS date. License renewals are not available after September 30, 2024.

See our End of Sale announcement for more details.

About Snippets

Overview

The F5 NGINX Controller Application Delivery (AD) module lets you configure NGINX directives that aren’t represented in the NGINX Controller API via “config snippets”, or “Snippets”. You can do so by using either the user interface (UI) or the Application Delivery REST API.

Caution:

When you use Snippets to customize your NGINX configuration, your changes are applied to the nginx.conf file as is. NGINX Controller does not verify that your configuration is valid before applying the snippet.

We strongly recommend verifying Snippets in a lab environment before making any changes in production.

Types of Snippets

There are five types of Snippets, which you can configure for gateways or components. This lets you add custom directives into the corresponding NGINX configuration blocks generated by the gateways and components for the associated URIs.

Note:
The uriSnippets can’t be used for TCP/UDP components.
Snippet Description Corresponding API Endpoint
httpSnippet Adds directives to the http block. Gateway
mainSnippet Adds directives to the main block. Gateway
streamSnippet Adds directives to the stream block. Gateway
uriSnippets Adds directives to the component’s server and location blocks. Component
uriSnippets Adds directives to the gateway’s server blocks. Gateway
workloadGroupSnippets Adds directives to the upstream blocks. Component

Best Practices

Gateway Partitions

It’s important to avoid adding conflicting snippets to the same context in your NGINX configuration file. We recommend that you create one stand-alone Gateway to hold the main, http, and stream snippets. Doing so lets you share the configuration for these contexts across Gateways that define the URIs (server blocks) for particular instances while reducing the risk of duplicate or conflicting settings.

NGINX Variables

NGINX configurations commonly use NGINX variables or custom variables. If you prefer to configure NGINX Controller by using the REST API, you may run into problems with variable expansion when sending JSON as part of a curl request using th -d flag. The recommended best practice for this is to reference the JSON in a data file instead of sending the string as part of the request. An alternative is to redefine the variable to itself, which allows the variable to pass through to the NGINX configuration. If you’re using the NGINX $host variable in your JSON data – represented by the <data> placeholder in the example below – you would define the variable before the curl request as follows:

host='$host' curl -s -k -H "Content-Type: application/json" -X PUT -d "<data>" https://192.168.100.10:80/

Usage Examples

Caution:
The examples provided here are intended for demonstration purposes only. We strongly recommend verifying Snippets in a lab environment before making any changes in production.

Add HTTP Strict Transport Security Headers

If you want to implement a HTTP Strict Transport Security (HSTS) policy, you can add a snippet to your gateway. For example:

{
    "metadata": {
        "name": "<gateway-name>"
    },
    "desiredState": {
        "configSnippets": {
            "uriSnippets": [
                {
                    "applicableUris": [
                        {
                            "uri": "http://172.16.0.238:81"
                        }
                    ],
                    "directives": [
                        {
                            "directive": "add_header",
                            "args": ["Strict-Transport-Security", "max-age=31536000; includeSubDomains", "always"]
                        }
                    ]
                }
            ]
        },
        "ingress": {
            "uris": {
                "http://example.com:8020": {}
            },
            "placement":  {
                "instanceRefs": [
                    {
                        "ref": "/infrastructure/locations/unspecified/instances/<instance-name>"
                    }
                ]
            }
        }
    }
}

Allow or Deny IP Addresses

You can add IP addresses to your allow- or deny-list by using the allow or deny directives in a gateway snippet. For example:

{
    "metadata": {
        "name": "<gateway-name>"
    },
    "desiredState": {
        "configSnippets": {
            "uriSnippets": [
                {
                    "applicableUris": [
                        {
                            "uri": "<gateway URI>"
                        }
                    ],
                    "directives": [
                        {
                            "directive": "deny",
                            "args": ["192.0.2.2"]
                        },
                        {
                            "directive": "allow",
                            "args": ["192.0.2.1/24"]
                        },
                                                {
                            "directive": "allow",
                            "args": ["2001:0db8::/32"]
                        },
                                                {
                            "directive": "deny",
                            "args": ["all"]
                        }
                    ]
                }
            ]
        },
        "ingress": {
            "uris": {
                "http://example.com:8020": {}
            },
            "placement":  {
                "instanceRefs": [
                    {
                        "ref": "/infrastructure/locations/unspecified/instances/<instance-name>"
                    }
                ]
            }
        }
    }
}

Load the NGINX Prometheus Module

In order to use the NGINX Prometheus-njs module with NGINX Controller, you need to useload_module in the main context, js_import in the http context, and js_content in the location. NGINX Controller automatically enables the location api location /api, which is also required for metrics reporting.

After installing the module, add the following Snippets to your gateway. This will add load_module and js_import:

{
    "metadata": {
        "name": "<gateway-name>"
    },
    "desiredState": {
        "configSnippets": {
            "mainSnippet": {
                "directives": [
                    {
                        "directive": "load_module",
                        "args": ["modules/ngx_http_js_module.so"]
                    }
                ]
            },
                "httpSnippet":{
                "directives": [
                    {
                        "directive": "js_import",
                        "args": ["/usr/share/nginx-plus-module-prometheus/prometheus.js"]
                    }
                ]
            }
        },
        "ingress": {
            "uris": {
                "http://example.com:8020": {}
            },
            "placement":  {
                "instanceRefs": [
                    {
                        "ref": "/infrastructure/locations/unspecified/instances/<instance-name>"
                    }
                ]
            }
        }
    }
}

Then, you’d add a config snippet similar to the example below to your component.

{
    "metadata": {
        "name": "<gateway-name>"
    },
    "desiredState": {
        "configSnippets": {
            "uriSnippets": [
                {
                    "applicableUris": [
                        {
                            "uri": "/metrics"
                        }
                    ],
                    "directives": [
                        {
                            "directive":"js_content",
                            "args": ["prometheus.metrics"]
                        }
                    ]
                }
            ]
        },
        "ingress": {
            "uris": {
                "http://example.com:8020": {}
            },
            "placement":  {
                "instanceRefs": [
                    {
                        "ref": "/infrastructure/locations/unspecified/instances/<instance-name>"
                    }
                ]
            }
        }
    }
}

NGINX as a WebSocket Proxy

If you want to use NGINX Controller to configure NGINX as a WebSocket Proxy, you can customize your nginx.conf by using Snippets and header programmability.

In the gateway, provide an http snippet that defines the map directive and the server configuration:

{
    "metadata": {
        "name": "<gateway-name>"
    },
    "desiredState": {
        "configSnippets": {
            "httpSnippet": {
                "directives": [
                    {
                        "directive": "map",
                        "args": ["$http_upgrade", "$connection_upgrade"],
                        "block": [
                            {
                                "directive": "default",
                                "args": ["upgrade"]
                            },
                            {
                                "directive": "''",
                                "args": ["close"]
                            }
                        ]
                    }
                ]
            }
        },
        "ingress": {
            "uris": {
                "http://example.com:8020": {}
            },
            "placement":  {
                "instanceRefs": [
                    {
                        "ref": "/infrastructure/locations/unspecified/instances/<instance-name>"
                    }
                ]
            }
        }
    }
}

Then, add the two required headers to the component using requestHeaderModifications. For example:

{
    "metadata": {
        "name": "<component-name>",
    },
    "desiredState": {
        "ingress": {
            "uris": {
                "/": {}
            },
            "gatewayRefs": [
                {"ref": "/services/environments/${env}/gateways/<gateway-name>"}
            ]
        },
        "programmability": {
            "requestHeaderModifications": [
                {
                    "action": "ADD",
                    "headerName": "Upgrade",
                    "headerValue": "$http_upgrade"
                },
                {
                    "action": "ADD",
                    "headerName": "Connection",
                    "headerValue": "$connection_upgrade"
                }
            ]
        },
        "backend": {
            "workloadGroups": {
                "websocket": {
                    "uris": {
                        "http://<data-host-name>:8010": {}
                    }
                }
            }
        }
    }
}

Forward Errors Logs to Remote Syslog

If you want to forward HTML error logs to syslog, you can add the error_log directive snippet to your gateway. For example:

{
    "metadata": {
        "name": "<gateway-name>"
    },
    "desiredState": {
        "configSnippets": {
            "httpSnippet": {
                "directives": [
                    {
                        "directive": "error_log",
                        "args": ["syslog:server=<data-host-name>", "debug"]
                    }
                ]
            }
        },
        "ingress": {
            "uris": {
                "http://example.com:8000": {}
            },
            "placement":  {
                "instanceRefs": [
                    {
                        "ref": "/infrastructure/locations/unspecified/instances/<instance-name>"
                    }
                ]
            }
        }
    }
}
Note:
The error_log and accesslog directives can appear at various block levels (main, http, stream, server, location, etc.). NGINX Controller adds these directives to control logging to the local file. When using Snippets to add additional logging capabilities, the inner blocks override the outer block definitions. For example, if you enable remote logging for errors at the main level, and you add an error_log directive to a server or location block that uses local logging, the local logging configuration overrides the remote logging configured at the main level.

Manage IPv6 Addresses

You can use Snippets to manage IPv6 addresses for HTTP and TCP/UDP use cases. IPv6 address management is supported in both Gateway and Component Snippets.

  • Be sure to set the reuseport option for all IPv6 listen directives. Failure to do so can cause bind errors.
  • NGINX Controller’s post-processing logic removes the reuseport option in certain cases. This is a known issue when the IPv6 port matches an IPv4 port and the IPv4 listen directive does not specify an IP address (in other words, a wildcard IP). To change the IPv6 listen directive’s IP address, remove the Snippet, then re-add the Snippet with a new IPv6 address.

If you need to support IPv6 addresses for the NGINX listen directive, you can use a snippet similar to the ones shown below to achieve it.

HTTP Gateway with IPv6

For HTTP, use the Gateway URI Snippets block to add an IPv6 listen directive to the server blocks.

{
  "metadata": {
    "name": "<gateway-name>"
  },
  "desiredState": {
    "configSnippets": {
      "uriSnippets": [
        {
          "directives": [
            {
              "directive": "listen",
              "args": [
                "[::]:80",
                "reuseport"
              ]
            },
            {
              "directive": "listen",
              "args": [
                "[<IPv6-address>]:80",
                "reuseport"
              ]
            }
          ]
        }
      ]
    },
    "ingress": {
      "placement": {
        "instanceRefs": [
          {
            "ref": "/infrastructure/locations/unspecified/instances/<instance-name>"
          }
        ]
      },
      "uris": {
        "http://example.com:80": {}
      }
    }
  }
}
Note:
You cannot add IPv6 listen directives to a server block when the FQDN is defined in the Component URI (for example, http://{FQDN}/{PATH}).

TCP/UDP Component with IPv6

For TCP/UDP, use the Component URI Snippets block to add an IPv6 listen directive to the server blocks.

TCP Component
{
  "metadata": {
    "name": "<component-name>"
  },
  "desiredState": {
    "configSnippets": {
      "uriSnippets": [
        {
          "directives": [
            {
              "directive": "listen",
              "args": [
                "[::]:9090",
                "reuseport"
              ]
            }
          ]
        }
      ]
    },
    "backend": {
      "workloadGroups": {
        "wg": {
          "uris": {
            "tcp://<workload-host>:9090": {}
          },
        }
      }
    },
    "componentType": "TCPUDP",
    "ingress": {
      "gatewayRefs": [
        {
          "ref": "/services/environments/<env-name>/gateways/<gateway-name>"
        }
      ],
      "uris": {
        "tcp://*:9090": {}
      }
    }
  }
}
UDP Component
{
  "metadata": {
    "name": "<component-name>"
  },
  "desiredState": {
    "configSnippets": {
      "uriSnippets": [
        {
          "directives": [
            {
              "directive": "listen",
              "args": [
                "[<IPv6-Address>]:9053",
                "udp",
                "reuseport"
              ]
            }
          ]
        }
      ]
    },
    "backend": {
      "workloadGroups": {
        "wg": {
          "uris": {
            "udp://<workload-host>:9053": {}
          }
        }
      }
    },
    "componentType": "TCPUDP",
    "ingress": {
      "gatewayRefs": [
        {
          "ref": "/services/environments/<env-name>/gateways/<gateway-name>"
        }
      ],
      "uris": {
        "udp://*:9053": {}
      }
    }
  }
}

IPv6-only Server Block

To add an IPv6-only server block, define the entire block in the Gateway HTTP or the Stream Snippets block.

UI Config

Add listen directives with parameters in URI Snippets. To learn more about what the listen directive does and what parameters it accepts, refer to the following topics:

Note:
The reuseport parameter creates an individual listening socket for each worker process. See reuseport option.

Extend App Security with Snippets

When adding NGINX Controller App Security to your components, you can use Snippets to add NGINX App Protect directives that aren’t represented in the NGINX Controller API. You can also use Snippets to tune your NGINX App Protect WAF performance.

Refer to Extend App Security with Snippets for more information and examples.