Skip to content

Workloadattestation🔗

When an app offers a service via the app2appPort setting, this service is available for all other apps deployed on this SFH. If your service should only be available to certain apps on an SFH or the service should be aware of the identity of using app, this can be realized with the workloadattestation feature.

The basic principle is, that apps get sidecar containers deployed into their apps, through which the apps can communicate with each other. The sidecars then request their identity from a workloadattestation system called spire. The containers themselves need no knowledge about the workloadattestation at all.

Workloadattestation

HTTP Communication🔗

Following an example for the use of the workloadattestation feature with http communication is shown. All apps, that want to use the workloadattestation feature need to set the workloadAttestation.enabled property. Apps with servers then define incomingProxies (sidecars, that accept connections and proxy the request to the app`s server) and apps, that want to communicate to the server need to configure outgoingProxies (sidecars, that accept local connections and proxy them to the configured target app).

Server🔗

A basic whoami HTTP server. It has two incoming proxies. One, which can only be used by appId 1. Another one, which can be used by all other SFH Apps.

Headless Service: The sidecarPort is used for the communication instead of the sidecarApp2appPort. BUT the sidecarApp2appPort must still be set. It is best to simply specify the same port.

{
  "definitionVersion": "1.0.0",
  "workloadAttestation": {
    "enabled": true,
    "incomingProxies": [
      {
        "sidecarPort": 8080,
        "sidecarApp2AppPort": 8080,
        "targetContainerPort": 80
      },
      {
        "sidecarPort": 8081,
        "sidecarApp2AppPort": 8081,
        "targetContainerPort": 80,
        "allowedAppIds": [1]
      }
    ]
  },
  "containers": [
    {
      "image": "containous/whoami",
      "tag": "latest",
      "name": "whoami",
      "resources": {
        "requests": {
          "memory": "128M",
          "cpu": "100m"
        },
        "limits": {
          "memory": "128M",
          "cpu": "100m"
        }
      },
      "ports": [
        {
          "name": "string",
          "containerPort": 80
        }
      ]
    }
  ]
}

The server will get details on the calling party in the http header:

X-Envoy-Expected-Rq-Timeout-Ms: 15000
X-Forwarded-Client-Cert: By=spiffe://sfh.trumpf.com/aks-agents-13073474-vmss000000/inquisitive-gray-hyena-21;Hash=6df8c460da2764ed6cf20855e790ddb26074edc0143ea11256daa8f612c0bab7;URI=spiffe://sfh.trumpf.com/aks-agents-13073474-vmss000000/international-green-jellyfish-22
X-Forwarded-Proto: http
X-Request-Id: ed2e1ecf-9c36-4c91-808d-8cacefcc5cfc

A server can then react to the calling party by for example limiting access to parts of an api, if the appId encoded in the identity does not match a certain appId.

Client🔗

A simple http client, that repeately calls a webserver in the app inquisitive-gray-hyena-21 via the outgoing sidecar

from debian:latest

RUN apt update && apt install curl -y

ENTRYPOINT ["/bin/sh"]
{
  "definitionVersion": "1.0.0",
  "workloadAttestation": {
    "enabled": true,
    "outgoingProxies": [
      {
        "sidecarContainerPort": 3001,
        "targetAddress": "inquisitive-gray-hyena-21",
        "targetPort": 8080,
        "targetWorkloadId": "inquisitive-gray-hyena-21"
      }
    ]
  },
  "containers": [
    {
      "image": "curl",
      "tag": "latest",
      "name": "client",
      "command": ["/bin/sh"],
      "args": [
        "-c",
        "while true; do curl -m 8 -sS localhost:3001; sleep 10;done"
      ],
      "resources": {
        "requests": {
          "memory": "128M",
          "cpu": "100m"
        },
        "limits": {
          "memory": "128M",
          "cpu": "100m"
        }
      }
    }
  ]
}

The targetAddress and targetWorkloadId define the app to talk with. An app must be addressed by the generated name, that euqals the app's harbor project name.

The targetAddress defines the service, the proxy communicates with. The targetWorkloadId defines the Id which is checked against the server's SpiffeId.

Headless Service: The headless service must be used as the targetAddress. In this case it would be inquisitive-gray-hyena-21-headless. As targetPort the sidecarPort of the incoming Proxy should be addressed. In this case it would be also be 8080.

TCP communication🔗

In Development

For protocols other than http, the sidecar can be configured to tunnel also tcp connections. This is useful in combination with the allowedAppIds property. Without defining allowedAppIds, it is quite useless to proxy the tcp connection 😉.

Example setup:

Server🔗

{
  "definitionVersion": "1.0.0",
  "workloadAttestation": {
    "enabled": true,
    "incomingProxies": [
      {
        "sidecarPort": 4711,
        "sidecarApp2AppPort": 4711,
        "targetContainerPort": 80,
        "allowedAppIds": [1],
        "tcp": true
      }
    ]
  },
  "containers": [
    {
      "image": "debian",
      "tag": "latest",
      "name": "server",
      "command": ["/bin/sh"],
      "args": [
        "-c",
        "apt update && apt install -y ncat && netcat -l 80 -k -e /bin/cat"
      ],
      "resources": {
        "requests": {
          "memory": "128M",
          "cpu": "100m"
        },
        "limits": {
          "memory": "128M",
          "cpu": "100m"
        }
      },
      "ports": [
        {
          "name": "test",
          "containerPort": 80
        }
      ]
    }
  ]
}

Client🔗

{
  "definitionVersion": "1.0.0",
  "workloadAttestation": {
    "enabled": true,
    "outgoingProxies": [
      {
        "sidecarContainerPort": 1234,
        "targetAddress": "inquisitive-gray-hyena-21",
        "targetPort": 4711,
        "targetWorkloadId": "inquisitive-gray-hyena-21",
        "tcp": true
      }
    ]
  },
  "containers": [
    {
      "image": "debian:latest",
      "tag": "latest",
      "name": "client",
      "command": ["/bin/sh"],
      "args": [
        "-c",
        "apt update && apt install -y ncat && while true; do  (echo test; sleep 11) | netcat 127.0.0.1 1234;done"
      ],
      "resources": {
        "requests": {
          "memory": "128M",
          "cpu": "100m"
        },
        "limits": {
          "memory": "128M",
          "cpu": "100m"
        }
      }
    }
  ]
}

Using the WorkloadAttestation without sidecars🔗

Behind the hood a spire setup is attesting the workloads identities. Instead of using the convenient sidecar containers, that handle the communication with spire, a container can also use spire itself.

Just enable workloadAttestation for the app and then enable the property mountWorkloadAttestationPath in the container, that want's to talk to the spire agent.

The container then will get the socket mounted to /run/spire/sockets. More details on how to use spire can be found on the website of the spiffe project

{
  "definitionVersion": "1.0.0",
  "workloadAttestation": {
    "enabled": true
  },
  "containers": [
    {
      "image": "special",
      "tag": "latest",
      "name": "client",
      "mountWorkloadAttestationPath": true,
      "resources": {
        "requests": {
          "memory": "128M",
          "cpu": "100m"
        },
        "limits": {
          "memory": "128M",
          "cpu": "100m"
        }
      }
    }
  ]
}

A usecase for using spire directly is to communicate with a system outside of the SFH and to authenticate with the SFHs identity. With the spire framework, the app can get a key and certificate to communicate with the other system and the target server can check the cert against the spire CA, which can be requested from here