Kubernetes Agent + Ingress Controller + Module

In this example, the Signal Sciences agent is installed as a Docker sidecar, communicating with a Signal Sciences native module for NGINX installed on an ingress-nginx Kubernetes ingress controller.

Integrating the Signal Sciences agent into an ingress controller

In addition to installing the Next-Gen WAF per application, it is also possible to install the Next-Gen WAF into a Kubernetes ingress controller that will receive all external traffic to your applications. Doing this is similar to installing into an application with a Signal Sciences module:

  • Install and configure the Signal Sciences module into the ingress controller.
  • Add the sigsci-agent container to the ingress pod and mount a sigsci-agent volume.
  • Add an emptyDir{} volume as a place for the sigsci-agent to write temporary data.

Kubernetes NGINX ingress controller

The Kubernetes NGINX Ingress Controller is an NGINX based implementation for the ingress API. Next-Gen WAF supports a native module for NGINX. This enables you to easily wrap the existing ingress-nginx controller to install the Signal Sciences module.

Wrap the base nginx-ingress-controller to install the Signal Sciences module

Wrapping the nginx-ingress-controller is done by using the base controller and installing the Signal Sciences native NGINX module. Our sigsci-nginx-ingress-controller repository contains two examples of this.

A prebuilt container can be pulled from Docker Hub with: docker pull signalsciences/sigsci-nginx-ingress-controller:latest

Installation

There are two methods for installing:

Prerequisites

Copy the agent keys for the site that you want the agent to be able to access. You will use the agent keys when configuring the Signal Sciences Agent package.

Install via Helm using overrides

The following steps cover installing sigsci-nginx-ingress-controller + sigsci-agent via the official ingress-nginx charts with an override file.

  1. Add the ingress-nginx repository:

    $ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
  2. In the sigsci-values.yaml file, add the agent keys as SIGSCI_ACCESSKEYID and SIGSCI_SECRETACCESSKEY.

  3. Install with the release name my-ingress in the default namespace:

    $ helm install -f values-sigsci.yaml my-ingress ingress-nginx/ingress-nginx

    You can specify a namespace with -n flag:

    $ helm install -n NAMESPACE -f values-sigsci.yaml my-ingress ingress-nginx/ingress-nginx
  4. After a few minutes, the agent will be listed in your Next-Gen WAF console.

  5. Create an Ingress resource. This step will vary depending on setup and supports a lot of configurations. Official documentation can be found regarding Basic usage - host based routing.

    Here is an example Ingress file:

    1apiVersion: networking.k8s.io/v1
    2kind: Ingress
    3metadata:
    4 annotations:
    5 kubernetes.io/ingress.class: nginx
    6 nginx.ingress.kubernetes.io/rewrite-target: /
    7 name: hello-kubernetes-ingress
    8 #namespace: SET THIS IF NOT IN DEFAULT NAMESPACE
    9spec:
    10 rules:
    11 - host: example.com
    12 http:
    13 paths:
    14 - pathType: Prefix
    15 path: /testpath
    16 backend:
    17 service:
    18 name: NAME OF SERVICE
    19 port:
    20 number: 80

Helm upgrade with override file

  1. In the sigsci-values.yaml file, update the sigsci-nginx-ingress-controller to the latest version to update the ingress-nginx charts:

    1controller:
    2 # Replaces the default nginx-controller image with a custom image that contains the Signal Sciences Nginx Module
    3 image:
    4 repository: signalsciences/sigsci-nginx-ingress-controller
    5 tag: "latest"
    6 pullPolicy: IfNotPresent
  2. Run helm upgrade with the override file. This example is running helm upgrade against the my-ingress release created in the previous section:

    $ helm upgrade -f sigsci-values.yaml my-ingress ingress-nginx/ingress-nginx

    or

    $ helm upgrade -f sigsci-nginxinc-values.yaml my-ingress ingress-nginx/ingress-nginx

    If ingress is not in default namespace, use -n to specify namespace:

    $ helm upgrade -n NAMESPACE -f sigsci-values.yaml my-ingress ingress-nginx/ingress-nginx

    or

    $ helm upgrade -n NAMESPACE -f sigsci-nginxinc-values.yaml my-ingress ingress-nginx/ingress-nginx

Uninstall release

  1. Uninstall release my-ingress.

    $ helm uninstall my-ingress
  2. If it's not in the default namespace, use -n to specify the namespace:

    $ helm uninstall -n NAMESPACE my-ingress

Install with custom file

Integrating the Signal Sciences agent

The Signal Sciences agent can be installed as a sidecar into each pod or as a service for some specialized needs.

The recommended way of installing the Signal Sciences agent in Kubernetes is by integrating the sigsci-agent into a pod as a sidecar. This means adding the sigsci-agent as an additional container to the Kubernetes pod. As a sidecar, the agent will scale with the app/service in the pod instead of having to do this separately. However, in some situations, it may make more sense to install the sigsci-agent container as a service and scale it separately from the application.

The sigsci-agent container can be configured in various ways depending on the installation type and module being used.

You can use the preStop container hook to slow the pod's shutdown and ensure drain timeouts are met.

1preStop:
2 exec:
3 command:
4 - sleep
5 - "30"

By default, the agent prioritizes quick start up and performance readiness for preliminary inspection. However, quick startup isn't always desirable if you only want the agent to inspect traffic after loading your rules and configuration data. If you want to delay agent startup, consider configuring a startup probe.

Getting and updating the Signal Sciences agent container image

An official signalsciences/sigsci-agent container image is available from the Signal Sciences account on Docker Hub.

Alternatively, if you want to build your own image or need to customize the image, then follow the sigsci-agent build instructions.

These instructions reference the latest version of the agent with imagePullPolicy: Always, which will pull the latest agent version even if one already exist locally. This is so the documentation does not fall out of date and anyone using this will not have an agent that stays stagnant. However, this may not be what if you need to keep installations consistent or on a specific version of the agent. In these cases, you should specify an agent version. Images on Docker Hub are tagged with their versions and a list of versions is available on Docker Hub.

Whether you choose to use the latest image or a specific version, there are a few items to consider to keep the agent up-to-date:

Using the latest Signal Sciences container image

If you do choose to use the latest image, then you will want to consider how you will keep the agent up to date.

  • If you have used the imagePullPolicy: Always option, then the latest image will be pulled on each startup and your agent will continue to get updates.

  • Alternatively, you may instead choose to manually update the local cache by periodically forcing a pull instead of always pulling on startup:

    $ docker pull signalsciences/sigsci-agent:latest

    Then, use latest with imagePullPolicy: Never set in the configuration so that pulls are never done on startup (only manually as above):

    1- name: sigsci-agent
    2 image: signalsciences/sigsci-agent:latest
    3 imagePullPolicy: Never
    4 ...

Using a versioned Signal Sciences container image

To use a specific version of the agent, replace latest with the agent version (represented here by x.xx.x). You may also want to change imagePullPolicy: IfNotPresent in this case as the image should not change.

1- name: sigsci-agent
2 image: signalsciences/sigsci-agent:x.xx.x
3 imagePullPolicy: IfNotPresent
4 ...

This will pull the specified agent version and cache it locally. If you use this method, then it is recommended that you parameterize the agent image, using Helm or similar, so that it is easier to update the agent images later on.

Using a custom tag for the Signal Sciences container image

It is also possible to apply a custom tag to a local agent image. To do this, pull the agent image (by version or use latest), apply a custom tag, then use that custom tag in the configuration. You will need to specify imagePullPolicy: Never so local images are only updated manually. After doing so, you will need to periodically update the local image to keep the agent up-to-date.

For example:

$ docker pull signalsciences/sigsci-agent:latest
$ docker tag signalsciences/sigsci-agent:latest signalsciences/sigsci-agent:testing

Then use this image tag in the configuration:

1- name: sigsci-agent
2 image: signalsciences/sigsci-agent:testing
3 imagePullPolicy: Never
4...

Configuring the Signal Sciences agent container

Agent configuration is normally done via the environment. Most configuration options are available as environment variables. Environment variables names have the configuration option name all capitalized, prefixed with SIGSCI_ and any dashes (-) changed to underscores (_). For example, the max-procs option would become the SIGSCI_MAX_PROCS environment variable. For more details on what options are available, see the Agent Configuration documentation.

The sigsci-agent container has a few required options that need to be configured:

  • Agent credentials (Agent Access Key and Agent Secret Key).
  • A volume to write temporary files.

Agent credentials

The sigsci-agent credentials are configured with two environment variables. These variables must be set or the agent will not start.

  • SIGSCI_ACCESSKEYID: The Agent Access Key identifies which site in the Next-Gen WAF console that the agent is configured for.
  • SIGSCI_SECRETACCESSKEY: The Agent Secret Key is the shared secret key to authenticate and authorize the agent.

Because of the sensitive nature of these values, we recommend you use the built in secrets functionality of Kubernetes. With this configuration, the agent will pull the values from the secrets data instead of reading hardcoded values into the deployment configuration. This also makes any desired agent credential rotation easier to manage by having to change them in only one place.

Use the valueFrom option instead of the value option to use the secrets functionality. For example:

1env:
2 - name: SIGSCI_ACCESSKEYID
3 valueFrom:
4 secretKeyRef:
5 # Update my-site-name-here to the correct site name or similar identifier
6 name: sigsci.my-site-name-here
7 key: accesskeyid
8 - name: SIGSCI_SECRETACCESSKEY
9 valueFrom:
10 secretKeyRef:
11 # Update my-site-name-here to the correct site name or similar identifier
12 name: sigsci.my-site-name-here
13 key: secretaccesskey

The secrets functionality keeps secrets in various stores in Kubernetes. This guide uses the generic secret store in its examples, however any equivalent store can be used. Agent secrets can be added to the generic secret store using YAML similar to the following example:

1apiVersion: v1
2kind: Secret
3metadata:
4 name: sigsci.my-site-name-here
5stringData:
6 accesskeyid: 12345678-abcd-1234-abcd-1234567890ab
7 secretaccesskey: abcdefg_hijklmn_opqrstuvwxy_z0123456789ABCD

This can also be created from the command line with kubectl such as with the following example:

$ kubectl create secret generic sigsci.my-site-name-here \
--from-literal=accesskeyid=12345678-abcd-1234-abcd-1234567890ab \
--from-literal=secretaccesskey=abcdefg_hijklmn_opqrstuvwxy_z0123456789ABCD

Additional information about Kubernetes secrets functionality can be found in the Kubernetes documentation.

Agent temporary volume

For added security, we recommended the sigsci-agent container be executed with the root filesystem mounted as read only. However, the agent still needs to write some temporary files such as the socket file for RPC communication and some periodically updated files such as geolocation data.

To accomplish this with a read only root filesystem, there needs to be a writeable volume mounted. This writeable volume can also be shared to expose the RPC socket file to other containers in the same pod.

The recommended way of creating a writeable volume is to use the builtin emptyDir volume type. This is typically configured in the volumes section of a deployment, as shown in the following example:

1volumes:
2 - name: sigsci-tmp
3 emptyDir: {}

Containers will then mount this volume at /sigsci/tmp:

1volumeMounts:
2 - name: sigsci-tmp
3 mountPath: /sigsci/tmp

The default in the official agent container image is to have the temporary volume mounted at /sigsci/tmp. If this needs to be moved for the agent container, then the following agent configuration options should also be changed from their defaults to match the new mount location:

  • rpc-address defaults to /sigsci/tmp/sigsci.sock
  • shared-cache-dir defaults to /sigsci/tmp/cache

The NGINX ingress controller is installed with the mandatory.yaml file. This file contains a modified template of the Generic Ingress Controller Deployment. The main additions are:

  1. Change the ingress container to load the custom ingress container and add Volume mounts for socket file communication between the Module/ingress container and Agent sidecar container:

    1...
    2 containers:
    3 - name: nginx-ingress-controller
    4 image: signalsciences/sigsci-nginx-ingress-controller:latest
    5 ...
    6 volumeMounts:
    7 - name: sigsci-tmp
    8 mountPath: /sigsci/tmp
    9...
  2. Load the Signal Sciences module in the NGINX configuration file (nginx.conf) via ConfigMap:

    1kind: ConfigMap
    2apiVersion: v1
    3data:
    4 main-snippet: load_module /usr/lib/nginx/modules/ngx_http_sigsci_nxo_module-1.17.7.so;
    5 http-snippet: sigsci_agent_host unix:/sigsci/tmp/sigsci.sock;
    6metadata:
    7 name: nginx-configuration
    8 namespace: ingress-nginx
    9 labels:
    10 app.kubernetes.io/name: ingress-nginx
    11 app.kubernetes.io/part-of: ingress-nginx
  3. Add a container for the Signal Sciences agent:

    1...
    2 containers:
    3 ...
    4 # Signal Sciences agent running in default RPC mode
    5 - name: sigsci-agent
    6 image: signalsciences/sigsci-agent:latest
    7 imagePullPolicy: IfNotPresent
    8 env:
    9 - name: SIGSCI_ACCESSKEYID
    10 valueFrom:
    11 secretKeyRef:
    12 # This secret needs added (see docs on sigsci secrets)
    13 name: sigsci.my-site-name-here
    14 key: accesskeyid
    15 - name: SIGSCI_SECRETACCESSKEY
    16 valueFrom:
    17 secretKeyRef:
    18 # This secret needs added (see docs on sigsci secrets)
    19 name: sigsci.my-site-name-here
    20 key: secretaccesskey
    21 securityContext:
    22 # The sigsci-agent container should run with its root filesystem read only
    23 readOnlyRootFilesystem: true
    24 volumeMounts:
    25 # Default volume mount location for sigsci-agent writeable data (do not change mount path)
    26 - name: sigsci-tmp
    27 mountPath: /sigsci/tmp
    28...
  4. Define the volume used above:

    1...
    2 volumes:
    3 # Define a volume where sigsci-agent will write temp data and share the socket file,
    4 # which is required with the root filesystem is mounted read only
    5 - name: sigsci-tmp
    6 emptyDir: {}
    7...

Setup

The mandatory.yaml file creates the resources in the ingress-nginx namespace. If using Kubernetes Secrets to store the agent access keys, you will need to create the namespace and access keys before running the mandatory.yaml file.

  1. Set the name for the secrets for the agent keys in mandatory.yaml.

    1...
    2 env:
    3 - name: SIGSCI_ACCESSKEYID
    4 valueFrom:
    5 secretKeyRef:
    6 # This secret needs added (see docs on sigsci secrets)
    7 name: sigsci.my-site-name-here
    8 key: accesskeyid
    9 - name: SIGSCI_SECRETACCESSKEY
    10 valueFrom:
    11 secretKeyRef:
    12 # This secret needs added (see docs on sigsci secrets)
    13 name: sigsci.my-site-name-here
    14 key: secretaccesskey
    15...
  2. Pull or build the NGINX ingress + Signal Sciences Module container. Set any preferred registry and repository name, and set the image to match in mandatory.yaml:

    $ docker pull signalsciences/sigsci-nginx-ingress-controller:latest
  3. Deploy using modified Generic Deployment:

    $ kubectl apply -f mandatory.yaml
  4. Create the service to expose the Ingress Controller. The steps necessary are dependent on your cloud provider. Official instructions can be found at https://kubernetes.github.io/ingress-nginx/deploy/#environment-specific-instructions.

    Below is an example service.yaml file:

    1kind: Service
    2apiVersion: v1
    3metadata:
    4 name: ingress-nginx
    5 namespace: ingress-nginx
    6spec:
    7 externalTrafficPolicy: Cluster
    8 selector:
    9 app.kubernetes.io/name: ingress-nginx
    10 type: LoadBalancer
    11 ports:
    12 - name: http
    13 port: 80
    14 targetPort: http
    15 - name: https
    16 port: 443
    17 targetPort: https
  5. Create the Ingress Resource. Below is an example Ingress Resource:

    1apiVersion: extensions/v1
    2kind: Ingress
    3metadata:
    4 name: test-ingress
    5 namespace: ingress-nginx
    6 annotations:
    7 nginx.ingress.kubernetes.io/rewrite-target: /
    8spec:
    9 rules:
    10 - http:
    11 paths:
    12 - path: /testpath
    13 backend:
    14 serviceName: nginx
    15 servicePort: 80
Was this guide helpful?

Do not use this form to send sensitive information. If you need assistance, contact support. This form is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.