Template Overview

Template overview of the NGINX Management Suite App Delivery Manager module.

Overview

The App Delivery Manager module uses templates for each of the main NGINX blocks to control the exact format of the generated NGINX configuration. App Delivery Manager uses a set of default built-in templates whenever a new environment is created. These templates provide basic features needed by all app developers when deploying their apps.

These templates can be copied and customized to a degree to support use cases not provided by the built-in templates. All NGINX directives, including directives defined by the rich set of community-provided dynamic modules, are available for use by these custom templates. The additional use cases and enhancements can either be hardcoded into the templates themselves or, by using variables, be made available to the app developers as part of the App Delivery Manager API (a feature called customExtensions).

This diagram depicts a simplified view of the App Delivery Manager config generation process.

App Delivery Manager Objects

  1. REST API request is processed.
    • Request is validated.
    • Business logic is applied.
    • Template inputs are generated.
  2. Generated template input is validated against the schema.
  3. Base and use case templates are executed using the input generated in step 1.
  4. Generated config is merged with the base config and config from other modules to generate the final config. This config is pushed to the instance groups.
Note:
  • App Delivery Manager templates only control the snippet of config generated by the App Delivery Manager module.
  • The config generated by the App Delivery Manager module is merged with the base config and config from other modules to generate the final config.
  • In the final config, the order of the directives within a server, location, map, match, upstream etc. blocks will match what is in the template.
  • In the final config, the order of main, http, stream, server, upstream, match, map, etc…directives will match what is in the template. But the line number of where the App Delivery Manager config starts will be different and cannot be controlled via App Delivery Manager templates.

Template types

Built-in templates are installed under the /etc/nms/adm/templates directory. Any custom templates must also be placed under this directory. The content of the template file uses GO Templates syntax. Templates are of two types.

  1. Base templates
  2. Use case templates

Base templates

Base templates contain the logic for generating the core part of the NGINX config, like server blocks, location blocks, and upstream blocks. These are in the /etc/nms/adm/templates/base directory. Every API resource (Gateways, Web components, and TCPUDP components) that generates NGINX config is associated with a template for config generation.

API resource Template
Gateways <name>-gateway.tmpl
Web components <name>-web-component.tmpl, <name>-web-component-locations.tmpl
TCP UDP components <name>-tcp-udp-component.tmpl
Warning:
  • We recommend not updating the built-in templates, as these will potentially change from one release to the next. App Delivery Manager will enforce this via MD5 checks built into the code.

Gateway

A Gateway config is generated using the Gateway base template. File name suffix -gateway.tmpl is used to identify this template.

Web component

Web components use two base templates to generate the config.

  • Web component locations template - Generates the locations that are injected into Gateway server blocks based on the Gateway references
  • Web component template - All other configs, except locations.

File name suffixes -web-component-locations.tmpl and -web-component.tmpl are used to identify these templates.

TCPUDP component

A TCPUDP component config is generated using the TCPUDP component base template. File name suffix -tcp-udp-component.tmpl is used to identify this template.

Instance Group

An Instance-group template renders the final config snippet. The file name built-in-v1-instance-group.tmpl is hardcoded and cannot be changed. This template cannot be configured per environment.

Note:
The suffix and the extension of the template file are hidden in web interface/API. E.g. The base template file my-new-base-gateway.tmpl will be referred to using the name my-new-base. -gateway.tmpl is not visible in the web interface/API.

Use case templates

Use case templates contain:

  • The logic for implementing additional use cases that add directives.
  • Block directives (if, match, split_clients…).
  • Arguments to directives generated by the base templates.

Use case templates can be used to layer on any NGINX config on what is generated by the base templates. The changes are additive only, and use cases cannot remove the config added by the base templates. Unlike a base template which is a single file, templates for a use case are grouped together in a directory under /etc/nms/adm/templates/usecases. This directory contains all the artifacts needed to implement the use case:

  • Template files.
  • Schema files for validation and UI definition.
  • Documentation.
Warning:
  • We recommend not implementing use cases in base templates.
  • To create a custom base template, copy one of the built-in templates and replace the prefix built-in-v1 with a unique name.
  • Using custom base templates may break the compatibility with built-in use cases.

Folder name convention

The folder name matches the name of the use case. After a use case folder is added, the folder’s name can be used to enable the use case on an Environment.

File name conventions

A template name suffix follows the same convention as in base templates. For example, a use case template with a -gateway suffix will be invoked from a Gateway base template.

The template name prefix determines the injection point of the config. The injection point is where the directives from this template will be placed inside the config generated by the base template. For example., a use case template named main-gateway.tmpl will be invoked when the line {{.ExecUseCaseTemplates "main"}} is invoked in the Gateway base template. The use case template is executed only if the use case is enabled on the Environment.


Template input

Template input is the data structure passed into the template when rendered. The template uses the data in the input to generate the config.

Base template data structure

All base template inputs have a shared wrapper shown below. The structure of the data inside the Data.<versioned_input> field is determined by the type of base template being executed. For example, a Gateway template input provides data to create server blocks(generated from a Gateway API request), and a Web component template input will contain data needed to create location blocks(generated from a Web component API request).

{
	"Data": {
		"v1": {
            ....
		}
	}
}

The input is versioned. If breaking changes are made, we will temporarily continue to support older versions to maintain compatibility.

Use case template data structure

All use case template inputs have a shared wrapper shown below. The structure of the data inside the Data.<versioned_input> field is determined by the base template invoking this use case template. For example, if invoked by a Gateway template, the Data.<versioned_input> will contain the Gateway template input(same as the one passed into the base template). Field Args contains the arguments passed in from the base template. For example,.ExecUseCaseTemplates "main" "arg1" "arg2" will result in .Args containing ["arg1", "arg2"]

{
    "Data": {
        "v1": {
            .....
        }
    },
    "Args": [
        .....
    ]
}

Methods

The template input exposes methods that can be used in the templates. The following methods are available.

  • ExecUseCaseTemplates - Invoke use case template with a specified prefix.
  • JSON - Print the JSON representation of the template input.

Global functions

Global helper functions are available to make writing templates easier. The following functions are available.

  • MD5 - Computes the MD5 for a string.
  • IsNGINXIpPort - Returns true if a string format is a valid NGINX conf IP, Port combination. For example, 10.1.1.1:53, [2001::1]:6464
  • Sprig library template functions.

Expanding the App Delivery Manager API using Use Case templates.

Gateway, Web component and TCPUDP component REST APIs can be expanded to expose the use cases via the API. These APIs contain an empty object customExtensions at several places in the API. Data in customExtensions is passed to the templates verbatim via template input. Below is an example of the extension points in the Gateway API and the corresponding template input generated from the API request. In the template, the data in customExtensions can be used to augment the NGINX config with additional directives required for the use case.

API and corresponding template input

Template Schema

Adding the logic for the use case in the templates and enabling it in the Environment is insufficient. We need to expose the settings for the use case to the API/UI users and protect against malformed/malicious input. This can be done using the JSON schema associated with the base and use case templates. The JSON schema has two purposes:

  1. Validate the template input generated from an API request.
  2. UI uses the schema definitions under customExtensions to augment the UI with the use case fields.
Warning:
The web interface has limited support for the JSON schema. Please refer to the Web interface JSON schema support page.

Base template schema

Each base template can be associated with a JSON schema with the same file name, but with .json extension.

Warning:
We recommend not modifying the base template schemas.

Use case template schema

A use case can be used to expand the REST API for Gateway, Web component and TCPUDP component resources. Depending on which API the use case is exposed on, a use case directory can have four schemas.

  1. gateway.json - Validates Gateway template input and augments Gateway UI.
  2. web-component.json - Validates Web component template input and augments Web component UI, except the UI inside the URI.
  3. web-component-locations.json - Validates Web component template input and augments ONLY the fields inside the URI.
  4. tcp-udp-component.json - Validates TCPUDP component template input and augments TCPUDP component UI.

The basic format of the JSON schema for any of the templates is the following. A detailed example can be found in the built-in use cases.

{
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "properties": {
        "v1": {
            "type": "object",
            "properties": {
                "customExtensions": {
                    "type": ["object", "null"],
                    "properties": {
                    },
                    "required": []
                }
            }
        }
    }
}

Template writing best practices

Templates use data from the REST API to implement features. A bad or malicious API request can be used to inject NGINX configs via the REST API. So it is essential that we:

  • Validate all API customExtensions data using the JSON schema.
  • Use best practices while writing the template to prevent config injection.

Config injection example

    // Unsafe
    proxy_set_header {{$headerName}} {{$headerValue}};

    // Safe
    proxy_set_header '{{$headerName}}' '{{$headerValue}}';

In the first example a $headerValue of value-1; location = /injected {proxy_pass http://foobackend;} can be used to inject a malicious location block into the config.


Upgrading and Supportability

App Delivery Manager ships with built-in templates and use cases. Future App Delivery Manager versions may change the base and use case templates. Modifying them is not supported and may cause upgrades to fail. App Delivery Manager will also prevent these modifications via MD5 checks. The recommended ways of enhancing the templates are:

  • Do not modify the built-in base or use case templates.
  • Do not implement use cases in base templates.
  • If base template modification is needed (for example, to add additional injection points), create a copy of the base template and make the modifications.
  • Implement use cases in the /usecases directory.