Kubernetes Ambassador

In this example, the Next-Gen WAF is integrated with Ambassador Edge Stack, a cloud native API gateway and ingress controller for Kubernetes, built upon Envoy proxy.

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

Integrating the Signal Sciences agent into Ambassador Edge Stack (AES)

The Signal Sciences agent can be integrated with Datawire's Ambassador Edge Stack (AES). This integration uses the underlying Envoy integration built into the agent. The agent is configured with an Envoy gRPC Listener and through AES's Filter, FilterPolicy, and LogService Kubernetes resources. Deployment and configuration is flexible. As such, this guide is designed to provide information that can be applied to your own methods of deployment.

Note that the examples in the documentation will refer to installing the latest agent version, but this is only so that the documentation examples do not fall behind. Refer to the documentation on getting and updating the agent for more details on agent versioning and how to keep the agent up-to-date.

Namespaces

By default, AES is installed into the ambassador Kubernetes namespace. The agent and any applications running behind AES do not have to run in this namespace, but you must take care during configuration to use the correct namespaces as this documentation may differ from your configuration. The following namespaces are used in this documentation:

Ambassador

  • Used for the ambassador install.
  • Used for all ambassador resources (e.g., Filter, FilterPolicy, LogService, Mapping).
  • Used for the sigsci-agent when running as a sidecar.

default

  • Used for all applications and services running behind AES.
  • Used for the agent when run in standalone mode.

Ambassador ID

Ambassador Edge Stack supports running multiple Ambassador Edge Stacks in the same cluster, without restricting a given Ambassador Edge Stack to a single namespace. This is done with the ambassador_id setting. When running multiple Ambassador Edge Stacks, care must be taken to include the correct ambassador_id value within all ambassador resources (Filter, LogService, Mapping, etc), otherwise the configuration will not be used. AES deployments only running one stack under the default ID don't need to set this value. Refer to the Ambassador ID docs for more information.

Running the agent as standalone or sidecar

The agent can run as a standalone deployment service or as a sidecar container within the AES pod. Either is fine, but running as a sidecar is easier if you are using Helm, as this is directly supported in the Helm values file. Running as a sidecar also has the advantage of scaling with AES, so this is the recommended route if you are using scaling via replica counts or autoscaling.

Installation

Installation involves two tasks: Deploying the agent configured in gRPC mode and Configuring AES to send traffic to the agent.

Deploying the agent

Deploying the agent is done by deploying the signalsciences/sigsci-agent container as a sidecar to AES or as a standalone service. The agent must be configured with its Agent Access Key and Agent Secret Key. This is typically done via a Kubernetes secret. One important point about secrets is that the secret must be in the same namespace as the pod using the secret. So, if you are running as a sidecar in the ambassador namespace, then the secret must also reside in that namespace. Refer to the agent credentials documentation for more details.

Example Secret in the ambassador namespace:

1apiVersion: v1
2kind: Secret
3metadata:
4 # Edit `my-site-name-here`
5 # and change the namespace to match that which
6 # the agent is to be deployed
7 name: sigsci.my-site-name-here
8 namespace: ambassador
9stringData:
10 # Edit these `my-agent-*-here` values:
11 accesskeyid: my-agent-access-key-id-here
12 secretaccesskey: my-agent-secret-access-key-here

Sidecar with Helm

Configuring AES with Helm is the easiest way to deploy, as the Ambassador values file already has direct support for this without having to modify an existing deployment YAML file. Refer to the AES documentation for installing with helm.

To install the agent as a sidecar, you will need to add new configuration lines to your custom values file, then install or upgrade AES with this values file. Refer to the Ambassador helm chart documentation for a reference on the values file. This will add the container with the correct configuration to the AES pod as a sidecar.

Add the following to the values YAML file:

1sidecarContainers:
2- name: sigsci-agent
3 image: signalsciences/sigsci-agent:latest
4 imagePullPolicy: IfNotPresent
5 # Configure the agent to use Envoy gRPC on port 9999
6 env:
7 - name: SIGSCI_ACCESSKEYID
8 valueFrom:
9 secretKeyRef:
10 # This secret needs added (see documentation on sigsci secrets)
11 name: sigsci.my-site-name-here
12 key: accesskeyid
13 - name: SIGSCI_SECRETACCESSKEY
14 valueFrom:
15 secretKeyRef:
16 # This secret needs added (see documentation on sigsci secrets)
17 name: sigsci.my-site-name-here
18 key: secretaccesskey
19 # Configure the Envoy to expect response data
20 - name: SIGSCI_ENVOY_EXPECT_RESPONSE_DATA
21 value: "1"
22 # Configure the Envoy gRPC listener address on any unused port
23 - name: SIGSCI_ENVOY_GRPC_ADDRESS
24 value: localhost:9999
25 ports:
26 - containerPort: 9999
27 name: grpc
28 securityContext:
29 # The sigsci-agent container should run with its root filesystem read only
30 readOnlyRootFilesystem: true
31 # Ambassador uses user 8888 by default, but the sigsci-agent container
32 # needs to run as sigsci(100)
33 runAsUser: 100
34 volumeMounts:
35 - name: sigsci-tmp
36 mountPath: /sigsci/tmp
37volumes:
38- name: sigsci-tmp
39 emptyDir: {}

Example of upgrading AES with helm:

$ helm upgrade ambassador \
--values /path/to/ambassador-sigsci_values.yaml \
--namespace ambassador \
datawire/ambassador

Alternatively, you can use Helm to render the manifest files. This makes adding the agent sidecar much easier than manually editing the YAML files. The modified deployment YAML will be in:

<output-dir>/ambassador/templates/deployment.yaml

Example of rendering the manifests with helm and applying the results:

$ helm template \
--output-dir ./manifests \
--values ./ambassador-sigsci_values.yaml \
--namespace ambassador \
datawire/ambassador
$ kubectl apply \
--recursive
--filename ./manifests/ambassador

Sidecar manually

Deploying the agent as a sidecar into the AES pod manually requires significantly more work than using Helm to render the manifests and is therefore not recommended.

You will need to modify the aes.yaml file, available at https://www.getambassador.io/yaml/aes.yaml. Append the container and volumes as described in the using Helm instructions. Refer to the AES installation guide and the Kubernetes and Envoy documentation for more details.

You will need to modify the following resource:

1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 labels:
5 product: aes
6 name: ambassador
7 namespace: ambassador
8
9 containers:
10
11 volumes:
12

The container will need to be added to the containers section and the volume to the volumes section.

Standalone

To deploy a standalone agent, you only need to add a Deployment and Service resource for the agent, as shown in the following example:

1apiVersion: v1
2kind: Service
3metadata:
4 name: sigsci-agent
5 # You may want it running in the ambassador namespace
6 #namespace: ambassador
7 labels:
8 service: sigsci-agent
9spec:
10 type: ClusterIP
11 ports:
12 - name: sigsci-agent
13 port: 9999
14 targetPort: grpc
15 selector:
16 service: sigsci-agent
17---
18apiVersion: apps/v1
19kind: Deployment
20metadata:
21 name: sigsci-agent
22 # You may want it running in the ambassador namespace
23 #namespace: ambassador
24spec:
25 replicas: 1
26 selector:
27 matchLabels:
28 service: sigsci-agent
29 template:
30 metadata:
31 labels:
32 service: sigsci-agent
33 spec:
34 containers:
35 - name: sigsci-agent
36 image: signalsciences/sigsci-agent:latest
37 imagePullPolicy: IfNotPresent
38 # Configure the agent to use Envoy gRPC on port 9999
39 env:
40 - name: SIGSCI_ACCESSKEYID
41 valueFrom:
42 secretKeyRef:
43 # This secret needs added (see documentation on sigsci secrets)
44 name: sigsci.my-site-name-here
45 key: accesskeyid
46 - name: SIGSCI_SECRETACCESSKEY
47 valueFrom:
48 secretKeyRef:
49 # This secret needs added (see documentation on sigsci secrets)
50 name: sigsci.my-site-name-here
51 key: secretaccesskey
52 # Configure the Envoy to expect response data
53 - name: SIGSCI_ENVOY_EXPECT_RESPONSE_DATA
54 value: "1"
55 # Configure the Envoy gRPC listener address on any unused port
56 - name: SIGSCI_ENVOY_GRPC_ADDRESS
57 value: 0.0.0.0:9999
58 ports:
59 - containerPort: 9999
60 name: grpc
61 securityContext:
62 # The sigsci-agent should run with its root filesystem read only
63 readOnlyRootFilesystem: true
64 volumeMounts:
65 - name: sigsci-tmp
66 mountPath: /sigsci/tmp
67 volumes:
68 - name: sigsci-tmp
69 emptyDir: {}

For more information, refer to the Kubernetes and Envoy documentation.

Sending traffic to the agent

You will need to configure three Ambassador resources for AES to send data to the agent. Refer to the Envoy configuration documentation for more detailed information on what each of these configures in the underlying Envoy install. The following guide uses the example quote service included with Ambassador.

Filter

The Filter resource is used to add the external authorization (ext_authz) filter to Envoy. This will inspect incoming requests that match the FilterPolicy.

The Signal Sciences agent requires AuthService to be defined in the Ambassador configuration, otherwise the agent will not receive request data. AuthService should be enabled by default. If requests are not being received by the agent, check that AuthService is enabled by running kubectl get authservice.

The namespace used for the auth_service configuration is the namespace the agent is deployed to. This guide uses the ambassador namespace for sidecar agents and default namespace for standalone agents. The format for the auth_service URL must be:

agent-hostname[.namespace]:agent-port

Examples:

  • Sidecar: auth_service: localhost:9999
  • Standalone: auth_service: sigsci-agent.default:9999

Example Filter YAML:

1# Filter defines an external auth filter to send to the agent
2kind: Filter
3apiVersion: getambassador.io/v3alpha1
4metadata:
5 name: sigsci
6 namespace: ambassador
7 annotations:
8 getambassador.io/resource-changed: "true"
9spec:
10 External:
11 # Sidecar agent:
12 auth_service: localhost:9999
13 # Standalone sigsci-agent service in default namespace:
14 #auth_service: sigsci-agent.default:9999
15 path_prefix: ""
16 tls: false
17 proto: grpc
18 protocol_version: v3
19 include_body:
20 max_bytes: 8192
21 allow_partial: true
22 failure_mode_allow: true
23 timeout_ms: 100000

FilterPolicy

The FilterPolicy resource maps what paths will be inspected by the agent. You can map this to all traffic (path: /*) or subsets (path: /app1/*). However, there is a limitation that each subset must map to the same agent. This is due to a limitation on the LogService not having a path based filter like the FilterPolicy. The LogService must route all matching response data to the same agent that handled the request.

Example routing all traffic to the agent:

1# FilterPolicy defines which requests go to sigsci
2kind: FilterPolicy
3apiVersion: getambassador.io/v3alpha1
4metadata:
5 namespace: ambassador
6 name: sigsci-policy
7 annotations:
8 getambassador.io/resource-changed: "true"
9spec:
10 rules:
11 - host: "*"
12 # All traffic to the sigsci-agent
13 path: "/*"
14 filters:
15 # Use the same name as the Filter above
16 - name: sigsci
17 namespace: ambassador
18 onDeny: break
19 onAllow: continue
20 ifRequestHeader: null
21 arguments: {}

You can route subsets of traffic to the agent with multiple rules. However every rule must go to the same agent due to the limitations described above.

Example routing subsets of traffic to the agent:

1# FilterPolicy defines which requests go to the sigsci-agent
2kind: FilterPolicy
3apiVersion: getambassador.io/v3alpha1
4metadata:
5 namespace: ambassador
6 name: sigsci-policy
7 annotations:
8 getambassador.io/resource-changed: "true"
9spec:
10 rules:
11 # /app1/* and /app2/* to the sigsci-agent
12 - host: "*"
13 path: "/app1/*"
14 filters:
15 # Use the same name as the Filter above
16 - name: sigsci
17 namespace: ambassador
18 onDeny: break
19 onAllow: continue
20 ifRequestHeader: null
21 arguments: {}
22 - host: "*"
23 path: "/app2/*"
24 filters:
25 # Use the same name as the Filter above
26 - name: sigsci
27 namespace: ambassador
28 onDeny: break
29 onAllow: continue
30 ifRequestHeader: null
31 arguments: {}

LogService

The LogService resource is used to add the gRPC Access Log Service to Envoy. This will inspect the outgoing response data and record this data if a signal was detected. It is also used for anomaly signals such as HTTP_4XX and HTTP_5XX.

The namespace used for the service configuration is the namespace the agent is deployed to. This guide uses the ambassador namespace for sidecar agents and default namespace for standalone agents. The format for the service URL must be:

agent-hostname[.namespace]:agent-port

Examples:

  • Sidecar: service: localhost:9999
  • Standalone: service: sigsci-agent.default:9999

Example:

1# Configure the access log gRPC service for the response
2# NOTE: There is no policy equiv here, so all requests are sent
3apiVersion: getambassador.io/v3alpha1
4kind: LogService
5metadata:
6 namespace: ambassador
7 name: sigsci-agent
8spec:
9 # Sidecar agent
10 service: localhost:9999
11 # Standalone sigsci-agent service in default namespace:
12 #service: sigsci-agent.default:9999
13 driver: http
14 driver_config:
15 additional_log_headers:
16 ### Request headers:
17 # Required:
18 - header_name: "x-sigsci-request-id"
19 during_request: true
20 during_response: false
21 during_trailer: false
22 - header_name: "x-sigsci-waf-response"
23 during_request: true
24 during_response: false
25 during_trailer: false
26 # Recommended:
27 - header_name: "accept"
28 during_request: true
29 during_response: false
30 during_trailer: false
31 - header_name: "date"
32 during_request: false
33 during_response: true
34 during_trailer: true
35 - header_name: "server"
36 during_request: false
37 during_response: true
38 during_trailer: true
39 ### Both request/response headers:
40 # Recommended
41 - header_name: "content-type"
42 during_request: true
43 during_response: true
44 during_trailer: true
45 - header_name: "content-length"
46 during_request: true
47 during_response: true
48 during_trailer: true
49 grpc: true
50 protocol_version: v3
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.