External Ingress Authentication provides an external authentication-service for Kubernetes Ingress Controllers which allows to authenticate users and authorize requests based on group-membership and request-parameters like remote-ip, request-method and/or requested path.
External Ingress Authentication works perfectly with NGINX ingress controller via External Authentication.
- Authentication and Authorization for kubernetes ingresses.
- Internal, file-based authentication backend
- Or use your ldap-server for authentication; supports protocols
ldap://andldaps:// - Fine grained access control via flexible authorization rules - allows you to e.g. protect only some paths of the application behind your Ingress
- Centralized rule-management for all ingresses in your cluster and/or ingress-specific authorization rules
- HTTP response headers with username and matched groups for the backend.
- Brute force protection blocking too many failed authentication requests
- Log format in Plain-Text or JSON.
The easiest way to install and configure the External Ingress Authentication service in your Kubernetes cluster is to use the provided Helm chart.
Most configuration parameters are passed to the service-container via environment variables. Global authorization rules and the user- and group-definitions for htpasswd authentication must be mounted at the paths specified in the respective environment variables.
External Ingress Authentication can use an internal, file-based authentication backend and/or a ldap-server for authentication.
HtPasswd authentication is modeled analog to Apache httpd's mod_authn_file and mod_authz_groupfile using a file generated by htpasswd for users and passwords and a separate file mapping users to groups.
| Variable | Default-Value | Description |
|---|---|---|
| HTPASSWD_FILE_PATH | ./config/.htpasswd |
specifies the location of a htpasswd-file containing usernames and passwords. If empty, the htpasswd-backend will not be used. The service expect this file to be utf-8 encoded. |
| HTPASSWD_GROUP_FILE_PATH | optional | specifies the location of a file mapping the users in the htpasswd-file to groups. It has the same format as the file used by Apache httpd's mod_authz_groupfile. If no path is specified, users authenticated by the htpasswd-backend are not mapped to groups. The service expect this file to be utf-8 encoded. |
| Variable | Required/Default-Value | Description |
|---|---|---|
| LDAP_SERVER_URL | optional | specifies the uri to the ldap-server. If not specified, the ldap-backend will not be used |
| LDAP_BIND_DN | required, if ldap enabled | specifies the ldap-query used to authenticate an user. Must contain the placeholder {username} which is replaced by the username (e.g. cn={username},cn=Users,dn=example,dn=com) |
| LDAP_SEARCH_BASE | required, if ldap enabled | specifies the base-query used to search for users when determining their group membership (e.g. cn=Users,dn=example,dn=com) |
| LDAP_SEARCH_FILTER | (sAMAccountName={username}) |
specifies the filter used to query users when determing their group membership. Must contain the placeholder {username} which is replaced with the given username when authenticating |
| LDAP_MANAGER_DN | required, if ldap enabled | specifies the User-DN used to query the LDAP-server to determine group-membership (e.g. cn=manager,dn=Users,dn=example,dn.com) |
| LDAP_MANAGER_PASSWORD | required, if ldap enabled | specified the Password for the Manager-User given in LDAP_MANAGER_DN |
| Variable | Required/Default-Value | Description |
|---|---|---|
| AUTHORIZATION_RULES_PATH | optional | path to a file containing authorization rules; if none is supplied the default authorization-rule is used unless additional rules are supplied by ingress-configuration. The service expect this file to be utf-8 encoded. |
| AUTHORIZATION_INGRESS_RULES_ENABLED | false |
whether authorization rules can be provided by ingresses or not. See security considerations concerning authorization rules provided by ingresses before enabling this! |
| AUTHORIZATION_INGRESS_RULES_SECRET | required | secret key an ingress must provide when sending authorization-rules to the service. See security considerations concerning authorization rules provided by ingresses for details. If no value is provided, the service generates a random secret on every (re-)start which can not be accessed. |
| Variable | Required/Default-Value | Description |
|---|---|---|
| BRUTE_FORCE_PROTECTION_ENABLED | true |
enables/disables the brute-force-protection which prevents too many login-attempts |
| BRUTE_FORCE_PROTECTION_MAX_FAILURE_COUNT | 5 |
specifies after how many failed login attempts the IP is blocked by the protection |
| BRUTE_FORCE_PROTECTION_EXPIRATION_SECONDS | 60 |
specifies the time window within which failed login attempts are counted and for how long an IP gets blocked |
| Variable | Required/Default-Value | Description |
|---|---|---|
| TLS_CERT | optional | specifies the path to the certificate used for TLS/HTTPS. If no certificate is specified, the service communicates via unsecured HTTP |
| TLS_KEY | optional | specifies the key for the certificate specified in TLS_CERT |
| Variable | Required/Default-Value | Description |
|---|---|---|
| AUTH_CACHE_TTL_SECONDS | 15 |
specifies how long the authentication of users and selection of authorization-rules is cached within the service (see below) |
| GUNICORN_CMD_ARGS | optional | allows you to specify custom arguments for the gunicorn-server used by the service |
| LOG_LEVEL | INFO |
specifies the log-level. Valid values are ERROR, WARN, INFO, DEBUG and TRACE. |
| LOG_FORMAT | JSON |
specifies the log-format |
Authentication backends are solely used to authenticate a user's credentials and to provide information about the user's group-memberships. All further authorization-restrictions (or lack thereof) are configured by rules declared in the config-file specified in AUTHORIZATION_RULES_PATH or in the ingress-configuration.
Authorization rules are declared in the format <hosts>:<ip-ranges>:<methods>:<paths>:<users>:<groups>:<groups-operator>:<users-groups-operator>. The parts have the following meaning:
| Rule element | Default-Value | Description |
|---|---|---|
<hosts> |
** |
comma-separated list of ingress-hosts the rule applies to. Use * to match subdomains, e.g. *.example.com matches sub.example.com (but not example.com itself). The default value ** applies to all ingress-hosts |
<ip-ranges> |
** |
comma-separated list of ip-ranges. For the rule to apply, the remote-ip from which the request is made must be within the given ranges. The default value ** applies to any remote-ip |
<methods> |
** |
comma-separated list of http-methods. The rule only applies if a request is made with the given methods. The default-value ** applies to any method |
<paths> |
** |
comma-separated list of requested paths the rule applies to. The pattern is evaluated using PurePath#full_match(), so /public/** matches any path below /public/ and /downloads/* matches for any file below the path /downloads/. The defaul-value ** matches any path |
<users> |
<authenticated> |
comma-separated list of usernames. To be authorized, an authenticated user must be in this list and/or one of the groups specified (see <users-groups-operator>). To block any request to a resource, use the special value <forbidden>. To allow public/unauthenticated-access to a resource use <public>. If the list contains <forbidden>or <public>, any further settings concerning group-membership etc. are ignored. Rules containing <forbidden> or <public> must specify non-wildcard values for either ip-ranges, methods or paths. The default-value <authenticated>, which is just and alias for **, allows access for all authenticated users |
<groups> |
** |
comma-separated list of groups. To be authorized, an authenticated user must be member of one or all of the specified groups - see <groups-operator> - and possibly in the list of users (see <users-groups-operator>). The default-value ** allows access for all authenticated users |
<groups-operator> |
OR |
specifies whether an authenticated user must be member of all groups specified in <groups> (AND) or any of them (OR) |
<users-groups-operator> |
AND |
specifies whether an authenticated user match the <users> and <groups> part of the rule (AND) or only one of them (OR) |
Note that if any of the list-elements above contains the wildcards ** or <authenticated> any other element in the list is ignored (so **,value is equivalent to **).
The rule **:**:**:/public/**:<public> grants public access to anything below /public/ on any host without authenticating against the authentication backends. Requests matching this rule are authorized even in the case that no authentication backends are configured
The rule **:**:**:/system/**:<forbidden> blocks all requests to anything below /system/ on any host without authenticating against the authentication backends.
The rule **:**:**:**:<authenticated>:group1,group2 restricts access to any resource to authenticated users who are member of group1 or group2
The rule **:**:**:**:<authenticated>:group1,group2:AND restricts access to any resource or authenticated users who are member of group1 and group2
The rule **:**:POST,PUT,DELETE:/admin/**:admin,operator restricts access for modifying requests below /admin/ to the users admin or operator
The rule **:**:PUT:/admin/**:admin,operator:editors restricts access for PUT-requests below /admin/ to either admin or operator or users in the group editors
The rule **:**:DELETE:/admin/**:admin,operator:cleaners:OR:AND allows access for DELETE-requests below /admin/ only to users admin or operator if the are also in the group cleaners
The rule example.com,*.example.com:172.100.0.1/24:**:**:<public> allows public access to example.com and all direct subdomains from within the range 172.100.0.1 - 172.100.0.254
The rule example.com:**:**:**:<authenticated>:Testers,Reviewers restricts requests to example.com to users in either group Testers or Reviewers
If no other rules match (or you did not provide any), requests are authorized against the rule **:**:**:**:<authenticated>:**:OR:AND - so unless other rules are supplied, all (and only) successfully authenticated users are authorized. To use the default-rule in the authorization rules file or ingress provided authorization rules you can simple use the shortcut <authenticated> for this default rule instead of the full rule-declaration above.
To make the this fallback explicit, it is recommended to always end your rules list with <authenticated>.
The authorization file may contain multiple rules separated by any (unescaped) whitespace-character.
Thus you may either specify multiple rules on one line:
**:**:**:/public:<public> **:**:**:/admin/**:admin <authenticated>
Or one rule per line:
**:**:**:/public:<public>
**:**:**:/admin/**:admin
<authenticated>
Rules are evaluated in the order that they are provided. The first rule matching all request-parameters (ingress-hostname, remote-ip, request-method and requested-path) is used to authorize the request.
You may add comments to the rules-file. Every line starting with # is regarded as a comment.
Any of the special separator characters used in the rules definition (:, , and whitespace) can be escaped using \<character, e.g. User\ containing a \:\ colon.
After installing the service in your cluster you have to configure your Ingress-resource to use the service for external authentication. Nginx Ingress Controllor provides a simple example for this setup.
The following assumes you're using ingress-nginx 0.9.0 or newer. For a detailed description of the annotations used see nginx-ingress-controller annotations.
For simple user-authentication without additional authorization restrictions to specific groups or users, you simple add the annotation nginx.ingress.kubernetes.io/auth-url to your ingress:
---
kind: Ingress
metadata:
name: external-auth-ingress
namespace: default
annotations:
nginx.ingress.kubernetes.io/auth-url: <url to the service>/ # MUST end with /
# recommended: cache responses of the authentication-service (see below for details)
nginx.ingress.kubernetes.io/auth-cache-key: $http_authorization
nginx.ingress.kubernetes.io/auth-cache-duration: 200 401 403 1m
# optional: add this if you want to pass the name of the authenticated user to the secured service
nginx.ingress.kubernetes.io/auth-response-headers: x-user
spec:
rules:
- host: secured-service.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: secured-service
port:
number: 80To improve response-times you can configure the nginx ingress-controller to cache the responses of the authentication service so that no requests outside the ingress are required. In most use-cases enabling response-caching is recommended. To enable response-caching in the ingress, add the following annotations:
nginx.ingress.kubernetes.io/auth-cache-key: $http_authorization # uses the value of the authorization-header as cache-key
nginx.ingress.kubernetes.io/auth-cache-duration: 200 401 403 1m # caches all responses for one minuteDepending on the auth-service's configuration you can provide ingress-specific authorization rules replacing those declared in the config-file of the service.
Ingress-specific rules are sent to the service using http headers. The service cannot discern between headers sent by the ingress-controller or by a client and passed through the ingress. Thus you have to ensure that all ingresses using external ingress authentication's service send the header X-Authorization-Rules when enabling ingress-specific authorization rules.
The requirement for the secret to be sent in the header X-External-Auth-Secret alongside the authorization rules for them to take effect is mainly meant as a reminder to ensure every ingress uses the annotation nginx.ingress.kubernetes.io/auth-proxy-set-headers and a ConfigMap containing the X-Authorization-Header. Everyone who has access to the secret's value (e.g. can access Secrets or whichever resource the secret's value is stored in in your cluster) will be able to send manipulated authorization rules unless your ingresses do provide a value for X-Authorization-Rules in the ingresses configuration.
To indicate that the rules configured in the auth-services should be used by the ingress it is sufficient to sent an empty value in X-Authorization-Rules; setting the value to e.g. # use default rules is more concise and readable, though.
Non-empty, valid ingress-specific authorization rules completely replace those declared in the config-file of the service for requests passed to the service by that ingress. If none of the rules provided by the ingress match, the default authorization rule is used to authorize a request. Nonetheless it is recommended to end your rules list with <authenticated> to make that fact explicit.
To provide additional rules, you have to specify them in a ConfigMap:
---
kind: Ingress
metadata:
name: external-auth-ingress
namespace: default
annotations:
nginx.ingress.kubernetes.io/auth-url: <url to the service>/ # MUST end with /
nginx.ingress.kubernetes.io/auth-proxy-set-headers: default/auth-headers
# recommended: cache responses of the authentication-service (see below for details)
nginx.ingress.kubernetes.io/auth-cache-key: $http_authorization
nginx.ingress.kubernetes.io/auth-cache-duration: 200 401 403 1m
# optional: add this if you want to pass the name and groups of the authenticated user to the secured service
nginx.ingress.kubernetes.io/auth-response-headers: x-user, x-groups
spec:
rules:
- host: secured-service.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: secured-service
port:
number: 80
---
apiVersion: v1
kind: ConfigMap
metadata:
name: auth-headers
namespace: default
data:"
X-Authorization-Rules: "<rule1> <rule2> <authenticated>"
X-External-Auth-Secret: "<secret>"Be aware that you have to reference the ConfigMap using <namespace>/<name> in nginx.ingress.kubernetes.io/auth-proxy-set-headers - otherwise the ConfigMap will be searched in the ingresses namespace.
On successful authentication/authorization the service returns the username and the authorized groups within the HTTP-headers X-User and X-Groups which can be passed through the ingress using the annotation nginx.ingress.kubernetes.io/auth-response-headers.
The service itself caches authentication results and the selection of authorization-rules to prevent overloading the service or its authentication backends; by default this cache has a lifetime of 15 seconds; this can be changed via the environment variable AUTH_CACHE_TTL_SECONDS.
The cache stores whether credentials were valid and the group-memberships of successfully authenticated users.
Selection of the authorization rules is cached based on ingress-host, remote-ip, http-method and requested path. While the selected rule is cached, whether an user is authorized to access a resource is always re-evaluated for every request.
The service exports metrics in prometheus-format about it's operation at the endpoint /metrics. Additionally to the default-metrics collected by flask-prometheus-exporter, it exposes metrics about the authorization-requests it received per ingress-host and their status in auth_requests_total and about ips blocked by the brute-force-protection in blocked_ips_total.
Source code is licensed under MIT-License
- External Ingress Authentication is based on Another LDAP developed by @dignajar