All Projects → github → Emissary

github / Emissary

Licence: mit
Envoy External Authorization API Bridge To SPIFFE Workload API

Programming Languages

go
31211 projects - #10 most used programming language

Emissary

This is a service that communicates with spire-agent to fetch and validate JWT-SVIDs sent to it over HTTP, usually from envoy using ext_authz.

Note: 🚧 Emissary is fully functional and well tested but should be considered alpha/experimental software. If you find any bugs please let us know by opening an issue or pull request.

How Emissary Works

Emissary uses Envoy's ext_authz filter (or another means in any proxy to perform sub-requests and act on the results, lua, etc) to allow or deny requests into and out of a service. Emissary works in two modes that are enabled dynamically by the direction of the request flow. The mode is determined by the x-emissary-mode header set by Envoy in requests to and from Emissary. Envoy also gets or sets the x-emissary-auth header which contains the JWT-SVID to authenticate the request.

Ingress Requests (requests from external services)

When a request from an external service is made Envoy receives the request and makes a subsequent request via the ext_authz filter setting the x-emissary-mode:ingress header. This tells Emissary that this is an ingress request which will require validating the JWT-SVID contained in the x-emissary-auth header in the original request. Emissary uses the contents of JWT-SVID to determine if the origin of the request is an allowed SPIFFE ID, set by EMISSARY_INGRESS_MAP. If so, Envoy forwards the request on to the service.

Egress Requests (requests to external services)

When a request from the internal service is made to an external service the internal service sends the request to Envoy setting the host header for the destination. Envoy receives the request and makes a subsequent request via the ext_authz filter setting the x-emissary-mode:egress header. This tells Emissary this is an egress request which will require fetching and setting a JWT-SVID in the x-emissary-auth header. To fetch a usable JWT-SVID Emissary uses EMISSARY_EGRESS_MAP to look up the SPIFFE ID of the destination of the request based on the host header. Envoy injects the x-emissary-auth header into the original outgoing request and forwards it.

Building, Testing and Running Emissary

For full build and test support emissary requires docker, docker-compose, go, curl and other basic build tools to be available. We vendor modules using go mod vendor, and most Go commands pre-1.14 will require the -mod=vendor flags to work.

Building

Runnable binary:

make

Docker:

make docker

Docker Compose:

BUILD_SHA=$(git rev-parse HEAD) docker-compose up

Testing

Unit tests:

make test

"Clean room" unit tests (installs go):

./script/cibuild

Docker Compose based integration tests (requires docker-compose and curl):

./script/cibuild-emissary-integration

Running

Emissary is configured completely by environment variables (full list below). A simple case to ensure your build is functioning is the following:

EMISSARY_LOG_LEVEL=debug EMISSARY_LISTENER=unix:///tmp/blah.sock EMISSARY_IDENTIFIER=spiffe://your.domain/app ./bin/emissary

Making a request to emissary using curl after running the above should result in a 403 status code:

curl -iv -GET --unix-socket /tmp/blah.sock localhost

To get a 200 status code out of emissary you will need a properly configured and running spire-agent and emissary, and supply emissary with an ingress or egress request.

For egress, if the host header contains a name that maps to a SPIFFE ID it should return a 200 response with the x-emissary-auth header containing a JWT-SVID:

curl -iv -GET --unix-socket /tmp/blah.sock -H 'x-emissary-mode:egress' -H 'Host:valid.host.net' localhost

For ingress, if x-emissary-auth contains a valid JWT-SVID with a subject that is allowed it should return a 200 response:

curl -iv -GET --unix-socket /tmp/blah.sock -H 'x-emissary-mode:ingress' -H 'x-emissary-auth: bearer validsvid' localhost

You can also make a request to the health check port, if spire-agent is running and emissary is able to talk to it a 200 will be returned otherwise it will return a 5XX depending on the condition:

curl -iv -GET localhost:9191

Examples

In the examples directory we have kubernetes, envoy and haproxy examples of how to use Emissary. It's important to note that in our examples we have configured envoy and haproxy to only perform emissary and spire based auth if an ingress x-emissary-auth header. This is to allow for easy migration and partial roll outs when not all services use emissary yet. If you would like to deny all emissary requests you will need to remove this match and acl respectively.

Configuration

It is configured completely by environment variables:

  • EMISSARY_LISTENER (optional, default unix:///run/emissary/sockets/auth.sock) - the http listener that emissary will accept requests on, supports TCP and Unix sockets
  • EMISSARY_SPIRE_SOCKET (optional, default /run/spire/sockets/agent.sock) - the socket path emissary will communicate with spire-agent over
  • EMISSARY_IDENTIFIER (required) - this is the SPIFFE ID of the application where emissary is running
  • EMISSARY_INGRESS_MAP (optional but required for ingress access) - this is a json based map of SPIFFE IDs and their ACLs, made up of paths and methods, for other applications that should be allowed to communicate with the application. The format is expected to be:
'{"spiffe://domain.test/app": [{"path":"/put","methods":["PUT"]},{"path":"/p","methods":["PATCH"]},{"path":"/g","methods":["GET"]}]}'
  • EMISSARY_EGRESS_MAP (optional but required egress access) - this is a json map of Host headers and SPIFFE IDs, this is how Emissary can figure out the name of the service that is the destination for a request. The format is expected to be:
'{"app.domain.test": "spiffe://domain.test/app"}'
  • EMISSARY_HEALTH_CHECK_LISTENER (optional, default 0.0.0.0:9191) - used for health checking emissary, makes a health check call to spire-agent and reports back
  • EMISSARY_LOG_LEVEL (optional, default info) - normal unix log levels supported
  • DOGSTATSD_ENABLED (optional, default false) - enables dogstatsd support
  • DOGSTATSD_HOST (optional, localhost) - send metrics to an endpoint
  • DOGSTATSD_PORT (optional, 28125) - send metrics to a port

Configuration Internals

Ingress Map and ACLs

EMISSARY_INGRESS_MAP configuration determines what other SPIFFE IDs have access to your application. This configuration is made up of a SPIFFE ID and a list of HTTP paths and methods that the stated application is allowed to access. Each ACL requires both a path and at least one method. This ACL is processed in order with the first path and method that match "winning". Path matching uses the configured path as a prefix. For example:

{
  "spiffe://domain.test/app": [
    {
      "path": "/pathx",
      "methods": [
        "GET"
      ]
    },
    {
      "path": "/path",
      "methods": [
        "PATCH"
      ]
    },
    {
      "path": "/pathy",
      "methods": [
        "OPTIONS"
      ]
    },
    {
      "path": "/",
      "methods": [
        "PUT"
      ]
    }
  ]
}

This configuration allows spiffe://domain.test/app to access your application under the following conditions:

  • GET to /pathx*
  • PATCH to /path*
  • OPTIONS to /pathy*
  • PUT to /*

In this configuration it is important to note that both PATCH and OPTIONS requests to /pathy* are allowed because of the PATCH /path* and OPTIONS /pathy* rules. The last rule in this case allows PUT to anything.

Contributing

Please see our contributing document if you would like to participate!

Getting help

If you have a problem or suggestion, please open an issue in this repository, and we will do our best to help. Please note that this project adheres to the Contributor Covenant Code of Conduct.

License

Emissary is licensed under the MIT license.

Authors

Emissary was designed and authored by Joe Williams, Ben Burry, Antoine Grondin and Eric Lee. It is now maintained, reviewed, and tested by Internal Interfaces team at GitHub.

Contact

If you need to reach out to the authors outside of issues and PRs in this repo please use: [email protected].

More about SPIFFE and SPIRE

SPIRE is a tool-chain for establishing trust between software systems across a wide variety of hosting platforms.

The Secure Production Identity Framework For Everyone (SPIFFE) Project defines a framework and set of standards for identifying and securing communications between web-based services.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].