Token JWT

Esta tarea muestra cómo configurar una política de autorización de Istio para aplicar el acceso basado en un JSON Web Token (JWT). Una política de autorización de Istio admite claims JWT de tipo cadena, de tipo lista de cadenas y de tipo cadena delimitada por espacios.

Antes de empezar

Antes de comenzar esta tarea, haga lo siguiente:

  • Complete la tarea de autenticación de usuario final de Istio.

  • Lea los conceptos de autorización de Istio.

  • Instale Istio utilizando la guía de instalación de Istio.

  • Despliegue dos workloads: httpbin y curl. Despliegue estos en un namespace, por ejemplo foo. Ambos workloads se ejecutan con un proxy Envoy delante de cada uno. Despliegue el namespace y los workloads de ejemplo utilizando estos comandos:

    ZipZip
    $ kubectl create ns foo
    $ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@) -n foo
    $ kubectl apply -f <(istioctl kube-inject -f @samples/curl/curl.yaml@) -n foo
  • Verifique que curl se comunica correctamente con httpbin utilizando este comando:

    $ kubectl exec "$(kubectl get pod -l app=curl -n foo -o jsonpath={.items..metadata.name})" -c curl -n foo -- curl http://httpbin.foo:8000/ip -sS -o /dev/null -w "%{\n}"
    200

Permitir solicitudes con JWT válido y claims de tipo lista

  1. El siguiente comando crea la política de autenticación de solicitudes jwt-example para el workload httpbin en el namespace foo. Esta política para el workload httpbin acepta un JWT emitido por testing@secure.istio.io:

    $ kubectl apply -f - <<EOF
    apiVersion: security.istio.io/v1
    kind: RequestAuthentication
    metadata:
      name: "jwt-example"
      namespace: foo
    spec:
      selector:
        matchLabels:
          app: httpbin
      jwtRules:
      - issuer: "testing@secure.istio.io"
        jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.32/security/tools/jwt/samples/jwks.json"
    EOF
  2. Verifique que una solicitud con un JWT inválido es denegada:

    $ kubectl exec "$(kubectl get pod -l app=curl -n foo -o jsonpath={.items..metadata.name})" -c curl -n foo -- curl "http://httpbin.foo:8000/headers" -sS -o /dev/null -H "Authorization: Bearer invalidToken" -w "%{\n}"
    401
  3. Verifique que una solicitud sin un JWT es permitida porque no hay política de autorización:

    $ kubectl exec "$(kubectl get pod -l app=curl -n foo -o jsonpath={.items..metadata.name})" -c curl -n foo -- curl "http://httpbin.foo:8000/headers" -sS -o /dev/null -w "%{\n}"
    200
  4. El siguiente comando crea la política de autorización require-jwt para el workload httpbin en el namespace foo. La política requiere que todas las solicitudes al workload httpbin tengan un JWT válido con requestPrincipal establecido en testing@secure.istio.io/testing@secure.istio.io. Istio construye el requestPrincipal combinando el iss y sub del token JWT con un separador / como se muestra:

    $ kubectl apply -f - <<EOF
    apiVersion: security.istio.io/v1
    kind: AuthorizationPolicy
    metadata:
      name: require-jwt
      namespace: foo
    spec:
      selector:
        matchLabels:
          app: httpbin
      action: ALLOW
      rules:
      - from:
        - source:
           requestPrincipals: ["testing@secure.istio.io/testing@secure.istio.io"]
    EOF
  5. Obtenga el JWT que establece las claves iss y sub en el mismo valor, testing@secure.istio.io. Esto hace que Istio genere el atributo requestPrincipal con el valor testing@secure.istio.io/testing@secure.istio.io:

    $ TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/release-1.32/security/tools/jwt/samples/demo.jwt -s) && echo "$TOKEN" | cut -d '.' -f2 - | base64 --decode
    {"exp":4685989700,"foo":"bar","iat":1532389700,"iss":"testing@secure.istio.io","sub":"testing@secure.istio.io"}
  6. Verifique que una solicitud con un JWT válido es permitida:

    $ kubectl exec "$(kubectl get pod -l app=curl -n foo -o jsonpath={.items..metadata.name})" -c curl -n foo -- curl "http://httpbin.foo:8000/headers" -sS -o /dev/null -H "Authorization: Bearer $TOKEN" -w "%{\n}"
    200
  7. Verifique que una solicitud sin un JWT es denegada:

    $ kubectl exec "$(kubectl get pod -l app=curl -n foo -o jsonpath={.items..metadata.name})" -c curl -n foo -- curl "http://httpbin.foo:8000/headers" -sS -o /dev/null -w "%{\n}"
    403
  8. El siguiente comando actualiza la política de autorización require-jwt para también requerir que el JWT tenga un claim llamado groups que contenga el valor group1:

    $ kubectl apply -f - <<EOF
    apiVersion: security.istio.io/v1
    kind: AuthorizationPolicy
    metadata:
      name: require-jwt
      namespace: foo
    spec:
      selector:
        matchLabels:
          app: httpbin
      action: ALLOW
      rules:
      - from:
        - source:
           requestPrincipals: ["testing@secure.istio.io/testing@secure.istio.io"]
        when:
        - key: request.auth.claims[groups]
          values: ["group1"]
    EOF
  9. Obtenga el JWT que establece el claim groups en una lista de cadenas: group1 y group2:

    $ TOKEN_GROUP=$(curl https://raw.githubusercontent.com/istio/istio/release-1.32/security/tools/jwt/samples/groups-scope.jwt -s) && echo "$TOKEN_GROUP" | cut -d '.' -f2 - | base64 --decode
    {"exp":3537391104,"groups":["group1","group2"],"iat":1537391104,"iss":"testing@secure.istio.io","scope":["scope1","scope2"],"sub":"testing@secure.istio.io"}
  10. Verifique que una solicitud con el JWT que incluye group1 en el claim groups es permitida:

    $ kubectl exec "$(kubectl get pod -l app=curl -n foo -o jsonpath={.items..metadata.name})" -c curl -n foo -- curl "http://httpbin.foo:8000/headers" -sS -o /dev/null -H "Authorization: Bearer $TOKEN_GROUP" -w "%{\n}"
    200
  11. Verifique que una solicitud con un JWT, que no tiene el claim groups es rechazada:

    $ kubectl exec "$(kubectl get pod -l app=curl -n foo -o jsonpath={.items..metadata.name})" -c curl -n foo -- curl "http://httpbin.foo:8000/headers" -sS -o /dev/null -H "Authorization: Bearer $TOKEN" -w "%{\n}"
    403

Permitir solicitudes con JWT válido y claims delimitados por espacios

Algunos claims de JWT codifican múltiples valores como una única cadena delimitada por espacios en lugar de un array JSON. El claim scope de OAuth2 es un ejemplo conocido de este patrón, definido en RFC 6749 como una lista separada por espacios.

Istio siempre trata los claims scope y permission como delimitados por espacios, dividiéndolos en valores individuales que las políticas de autorización pueden comparar. Para otros claims personalizados que usen la misma codificación, use el campo spaceDelimitedClaims en su JWTRule para incluirlos explícitamente. Sin este campo, un valor de claim personalizado como "admin editor" se compara como una única cadena exacta.

{"iss": "testing@secure.istio.io", "roles": "admin editor"}
  1. Descargue el script de generación de JWT y la clave de firma:

    $ wget --no-verbose https://raw.githubusercontent.com/istio/istio/release-1.32/security/tools/jwt/samples/gen-jwt.py
    $ wget --no-verbose https://raw.githubusercontent.com/istio/istio/release-1.32/security/tools/jwt/samples/key.pem
  2. El siguiente comando actualiza la política de autenticación de solicitudes jwt-example para declarar que el claim roles es delimitado por espacios:

    $ kubectl apply -f - <<EOF
    apiVersion: security.istio.io/v1
    kind: RequestAuthentication
    metadata:
      name: "jwt-example"
      namespace: foo
    spec:
      selector:
        matchLabels:
          app: httpbin
      jwtRules:
      - issuer: "testing@secure.istio.io"
        jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.32/security/tools/jwt/samples/jwks.json"
        spaceDelimitedClaims: ["roles"]
    EOF
  3. El siguiente comando actualiza la política de autorización require-jwt para requerir que el claim roles incluya el valor admin:

    $ kubectl apply -f - <<EOF
    apiVersion: security.istio.io/v1
    kind: AuthorizationPolicy
    metadata:
      name: require-jwt
      namespace: foo
    spec:
      selector:
        matchLabels:
          app: httpbin
      action: ALLOW
      rules:
      - from:
        - source:
           requestPrincipals: ["testing@secure.istio.io/testing@secure.istio.io"]
        when:
        - key: request.auth.claims[roles]
          values: ["admin"]
    EOF
  4. Obtenga un JWT cuyo claim roles esté establecido en la cadena delimitada por espacios "admin editor":

    $ TOKEN_ROLES=$(python3 ./gen-jwt.py ./key.pem --claims '{"roles":"admin editor"}')
  5. Verifique que una solicitud con ese JWT es permitida, porque admin está presente en el claim roles delimitado por espacios:

    $ kubectl exec "$(kubectl get pod -l app=curl -n foo -o jsonpath={.items..metadata.name})" -c curl -n foo -- curl "http://httpbin.foo:8000/headers" -sS -o /dev/null -H "Authorization: Bearer $TOKEN_ROLES" -w "%{\n}"
    200
  6. Obtenga un JWT cuyo claim roles no incluya admin:

    $ TOKEN_NO_ADMIN=$(python3 ./gen-jwt.py ./key.pem --claims '{"roles":"editor"}')
  7. Verifique que la solicitud es denegada:

    $ kubectl exec "$(kubectl get pod -l app=curl -n foo -o jsonpath={.items..metadata.name})" -c curl -n foo -- curl "http://httpbin.foo:8000/headers" -sS -o /dev/null -H "Authorization: Bearer $TOKEN_NO_ADMIN" -w "%{\n}"
    403

Limpieza

Elimine el namespace foo:

$ kubectl delete namespace foo

Si descargaste gen-jwt.py y key.pem para la sección de claims delimitados por espacios, elimínelos:

$ rm -f ./gen-jwt.py ./key.pem
¿Fue útil esta información?
¿Tienes alguna sugerencia para mejorar?

¡Gracias por tus comentarios!