eas
(pronounced eez
) is primarily focused on lowering the barrier to
using various authentication schemes in a kubernetes environment (but it works
with any reverse proxy supporting external/forward auth). eas
can be
deployed once and protect many services using disperse authentication methods
and providers. The goal is to make enabling authentication as easy as:
- generating a new
config_token
(see below) - configuring the reverse proxy to use the service for external authentication
- benefit
Various authentication plugins are supported. Within a single
config_token
you can enable as many as you would like which results in a
pipeline of authentication mechanisms being invoked. The first plugin to result
in a 2XX
response code will allow the request to be serviced. If all
plugins fail, then by default the result from the final plugin defined in the
config_token
will be returned to the client. You can however alter that on
a service-by-service basis by setting the fallback_plugin=plugin index
(0
indexed) parameter on the authentication URL.
- htpasswd
- LDAP
- OpenID Connect
- oauth2
- request param
- request header
- request js
- jwt
- firebase jwt
- works with any proxy server (traefik, nginx, ambassador, istio, envoy, etc) that supports forward/external auth
- works with any
OpenID Connect
/oauth2
provider (tested predominantly withkeycloak
but it should be agnostic) - only requires 1 installation to service any number of providers/configurations/vhosts/domains
- passes tokens to the backing service via headers
- automatically refreshes tokens
- server-side
config_token
s CONFIG_TOKENS
If running multiple instances (HA) you will need a shared cache/store (see redis below). You only really need redis if:
- You are running HA
- You are using the
oidc
oroauth2
plugins
Refer to the HOWTO for a more detailed overview.
-
eas
must be able to accessOIDC Provider
-
user-agent
must be able to accessOIDC Provider
-
user-agent
must be able to accessproxy
-
user-agent
must be able to accesseas
(ifredirect_uri
is directly pointing toeas
service/oauth/callback
endpoint) -
proxy
must be able to accesseas
-
proxy
must sendX-Forwarded-Host
(localhost:8000) toeas
in sub-request -
proxy
must sendX-Forwarded-Uri
(/anything/foo/bar?test=foo) toeas
in sub-request -
proxy
must sendX-Forwarded-Proto
(http) toeas
in sub-request -
proxy
should sendX-Forwarded-Method
(GET) toeas
in sub-request -
proxy
must return non2XX
responses fromeas
to browser -
proxy
may forward2XX
auth headerX-Id-Token
to backing service -
proxy
may forward2XX
auth headerX-Userinfo
to backing service -
proxy
may forward2XX
auth headerX-Access-Token
to backing service -
proxy
may forward2XX
auth headerAuthorization
to backing service
EAS_CONFIG_TOKEN_SIGN_SECRET="foo" \
EAS_CONFIG_TOKEN_ENCRYPT_SECRET="bar" \
EAS_ISSUER_SIGN_SECRET="super secret" \
EAS_ISSUER_ENCRYPT_SECRET="blah" \
EAS_COOKIE_SIGN_SECRET="hello world" \
EAS_COOKIE_ENCRYPT_SECRET="something" \
EAS_SESSION_ENCRYPT_SECRET="baz" \
EAS_CONFIG_TOKEN_STORES='{}' \
EAS_LOG_LEVEL="info" \
EAS_PORT=8080 \
node src/server.js
docker run -d --name eas -p 8080:8080 \
-e EAS_CONFIG_TOKEN_SIGN_SECRET="foo" \
-e EAS_CONFIG_TOKEN_ENCRYPT_SECRET="bar" \
-e EAS_ISSUER_SIGN_SECRET="super secret" \
-e EAS_ISSUER_ENCRYPT_SECRET="blah" \
-e EAS_COOKIE_SIGN_SECRET="hello world" \
-e EAS_COOKIE_ENCRYPT_SECRET="something" \
-e EAS_SESSION_ENCRYPT_SECRET="baz" \
-e EAS_CONFIG_TOKEN_STORES='{}' \
-e EAS_LOG_LEVEL="info" \
-e EAS_PORT=8080 \
travisghansen/external-auth-server
A helm
chart is supplied in the repo directly. Reviewing
values.yaml is highly
recommended as examples are provided for common use-cases.
helm repo add eas https://travisghansen.github.io/external-auth-server
helm repo update
helm upgrade \
--install \
--namespace=external-auth-server \
\
--set configTokenSignSecret=<random> \
--set configTokenEncryptSecret=<random> \
--set issuerSignSecret=<random> \
--set issuerEncryptSecret=<random> \
--set cookieSignSecret=<random> \
--set cookieEncryptSecret=<random> \
--set sessionEncryptSecret=<random> \
--set logLevel="info" \
\
--set redis-ha.enabled=true \
--set redis-ha.auth=true \
--set redis-ha.redisPassword=53c237 \
\
--set storeOpts.store=ioredis \
--set storeOpts.password=53c237 \
--set storeOpts.name=mymaster \
--set storeOpts.sentinels[0].host=eas-redis-ha-announce-0 \
--set storeOpts.sentinels[0].port=26379 \
--set storeOpts.sentinels[1].host=eas-redis-ha-announce-1 \
--set storeOpts.sentinels[1].port=26379 \
--set storeOpts.sentinels[2].host=eas-redis-ha-announce-2 \
--set storeOpts.sentinels[2].port=26379 \
--set storeOpts.keyPrefix="eas:" \
\
--set ingress.enabled=true \
--set ingress.hosts[0]=eas.example.com \
--set ingress.paths[0]=/ \
eas eas/external-auth-server
# please edit the values in bin/generate-config-token.js to your situation
# ie: issuer disovery URL, client_id, client_secret, etc
# also make sure to use the same secrets used when launching the server
EAS_CONFIG_TOKEN_SIGN_SECRET="foo" \
EAS_CONFIG_TOKEN_ENCRYPT_SECRET="bar" \
node bin/generate-config-token.js
# alternatively you may use the following to create tokens
# files can be either json or yaml
cat config-token.json | docker run --rm -i -e EAS_CONFIG_TOKEN_SIGN_SECRET=foo -e EAS_CONFIG_TOKEN_ENCRYPT_SECRET=bar travisghansen/external-auth-server generate-config-token
cat config-token.json | EAS_CONFIG_TOKEN_SIGN_SECRET=foo EAS_CONFIG_TOKEN_ENCRYPT_SECRET=bar npm run generate-config-token
# See full examples in the ./examples/ directory
# particularly nginx has some particular requirements
# NOTE: run over https in production
# NOTE: take care to NOT authenticate `eas` with itself (this is particularly
# possible to happen in service mesh scenarios), whatever tool you use should
# ensure access to the `eas` service bypasses authentication thereby avoiding
# recursive behavior
# traefik
address = http://<eas server ip>:8080/verify?config_token=<token output from above>
# nginx (see examples/nginx.conf)
proxy_pass "http://<eas server ip>:8080/verify?redirect_http_code=401&config_token=<token output from above>";
# ingress-nginx (see examples/ingress-nginx.yaml)
# nginx-ingress-controller (see examples/nginx-ingress-controller.yaml)
# traefik ingress
ingress.kubernetes.io/auth-type: forward
ingress.kubernetes.io/auth-url: "https://eas.example.com/verify?config_token=CONFIG_TOKEN_HERE"
ingress.kubernetes.io/auth-response-headers: X-Userinfo, X-Id-Token, X-Access-Token, Authorization
# ambassador (see file in examples directory)
# istio (see file in examples directory)
# haproxy-ingress (see file in examples directory)
# contour (see file in examples directory)
# envoy (see file in examples directory)
Configure the external auth URL to point to the services /verify
endpoint. The URL supports the following query params:
config_token=the encrypted configuration token
redirect_http_code=code
(only use with nginx to overcome external auth module limitations (should be set to401
), otherwise omitted)fallback_plugin=plugin index
if all plugins fail authentication which plugin response should be returned to the client
If your provider does not support wildcards you may expose eas
directly and
set the config_token
redirect_uri
to the eas
service at the
/oauth/callback
path.
EAS_SSL_CERT
path to ssl cert file to enable httpsEAS_SSL_KEY
path to ssl key file to enable httpsEAS_GRPC_ADDRESS
the address to start the grpc server on (default is0.0.0.0
)EAS_GRPC_PORT
port the grpc server is bound to (default is50051
)EAS_GRPC_SSL_CERT
path to ssl cert file to enable httpsEAS_GRPC_SSL_KEY
path to ssl key file to enable httpsEAS_ALLOW_EVAL
allow for potentially unsafe execution of untrusted code (enablesrequest_js
andjs
query engine)EAS_ALLOW_PLAIN_SERVER_SIDE_TOKENS
allows server-side token to be unsigned (ie: store plain json/yaml in the store(s))
Support for sentinel, see bin/generate-store-opts.js
with further options.
EAS_STORE_OPTS='{"store":"ioredis","host":"localhost"}'
No support for sentinel currently, see bin/generate-store-opts.js
with further options.
EAS_STORE_OPTS='{"store":"redis","host":"localhost"}'