Hi, there

I'm devoting myself to implementing the authentication capability in
APISIX Ingress Controller, and below is my proposal.

Background

Authentication and Authorization are required on the Kubernetes
Ingress layer, which is similar to API Gateway. However, due to the
lack of some APISIX Consumer alike stuff, APISIX Ingress Controller
cannot leverage any Authentication plugins provided by APISIX.

That’s why an appropriate way needs to be developed to make up for
this drawback.

Research

The standard Ingress isn’t involve anything about Authentication, thus
every Ingress Controllers implement it in different ways.

Kong

As an API Gateway solution, Kong Ingress Controller implements the
KongConsumer CRD,  which maps it to the Consumer object in Kong.

apiVersion: configuration.konghq.com/v1
kind: KongConsumer
metadata:
  name: <object name>
  namespace: <object namespace>
  annotations:
    kubernetes.io/ingress.class: <controller ingress class, "kong" by default>
username: <user name>
custom_id: <custom ID>

All KongConsumer resources will be watched by Kong Ingress Controller
and used when authenticating.

Ingress Nginx

The Authentication is weak in Ingress Nginx,only basic-auth and digest
authentication are supported. It is implemented by specifying
annotations in Ingress.

nginx.ingress.kubernetes.io/auth-type: [basic|digest]

This annotation specifies the authentication type.

nginx.ingress.kubernetes.io/auth-secret: secretName

And this annotation specifies the Kubernetes Secret name which stores
the username and password.

apiVersion: v1
data:
  auth: Zm9vOiRhcHIxJE9GRzNYeWJwJGNrTDBGSERBa29YWUlsSDkuY3lzVDAK
kind: Secret
metadata:
  name: basic-auth
  namespace: default
type: Opaque
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress-with-auth
  annotations:
    nginx.ingress.kubernetes.io/auth-type: basic
    nginx.ingress.kubernetes.io/auth-secret: basic-auth
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - path: /
        backend:
          serviceName: http-svc
          servicePort: 80

How Consumers Work in APISIX

Consumers are always used with Authentication, you need to configure a
consumer (with username) and enable several plugins on it, then also
enabling these (auth) plugins in the Route. APISIX searches the
consumer according to the auth-type and its key (headers or query
strings).

Frankly, it also works if we just copy this way to APISIX Ingress
Controller, but the configure is not so clean and easy. As we first
need some kinds of stuff to create the Consumer, then enable (APISIX)
plugins on it, and enabling these plugins on ApisixRoute again. We
need an easier way.

ApisixConsumer

ApisixConsumer is necessary as we have to create the consumer.

apiVersion: apisix.apache.org/v2alpha1
kind: ApisixConsumer
metadata:
  name: <consumer-name>
  namespace: <consumer-namespace>
  labels:
    group: 111
spec:
  authParameter:
    basicAuth:
      valueRef: # exclusive with value
        kind: secret
        name: <secret-name>
        namespace: <secret-namespace>
      value: # exclusive with valueRef
        username: aaa
        password: xxx
    keyAuth:
      key: xxxx # exclusive with keyRef
      keyRef: # exclusive with key
        kind: secret
        name: <secret-name>
        namespace: <secret-namespace>

Currently, only fields under authParameter are concerned (other
features like limit-rate, can be put under rateLimiting field and so
on), which represents authentications, more auth types can be
implemented there, values are kind-dependent, for instance, basic auth
needs a username and password configuration in value or importing from
a secret (exclusively), in valueRef.

All ApisixConsumers will be watched by APISIX Ingress Controller, and
corresponding Consumer objects will be created, which name is
concatenated by the namespace and name (so it can be unique).

All authentication configurations will be translated to the
corresponding underlying plugins.

You may wonder that why not just using the native plugin way here.
This is because the fields above (e.g. password in basic auth) are
sensitive and just configuring literally is not so safe, a better way
is fetching them from Kubernetes secret, also, the native plugin
configuring is too flexible to manage, it might results in some
security issues (since validation is not so strong so far).

ApisixRoute

On the other hand, authentication should be enabled in ApisixRoute, of
course, it can be supported by configuring the plugins field:

apiVersion: apisix.apache.org/v2alpha1
kind: ApisixRoute
metadata:
  name: test
spec:
  http:
  - name: rule1
    match:
      paths:
      - /*
    backend:
      serviceName: httpbin
      servicePort: 8080
    plugins:
    - name: basic-auth
      enable: true

Considering some capabilities like consumer restriction (ACL) and a
unifying way for management, native fields will be better here.

apiVersion: apisix.apache.org/v2alpha1
kind: ApisixRoute
metadata:
  name: test
spec:
  http:
  - name: rule1
    match:
      paths:
      - /*
    backend:
      serviceName: httpbin
      servicePort: 8080
    authentication:
      enable: true
      type: keyAuth
      keyAuth:
        header: Authorization
      acl:
        allowedConsumers:
          selector:
            group: 111
        deniedConsumers:
          selector:
            group: 222
        rejectedCode: 503

Just enabling the required authentication way in ApisixRoute and
configuring the ACL (consumer restriction) on demand.

In practice, people won’t configure more than one authentication
method in a Route, so here the authentication type must be configured,
any extra configurations can be put under another field which name is
the same as the authentication type (e.g. configurations for keyAuth
is under keyAuth field).

As for ACL, Kuberentes typical label-selector is applied as the
consumer list might be huge. In this case, it’s not easy to maintain
such an ApisixRoute object.

All the authentications and ACL will be translated to the underlying
APISIX plugins.

Thanks!
Chao Zhang
GitHub: https://github.com/tokers

Reply via email to