Istio security configuration combat

Last time we conducted some basic experiments on ISTIO traffic control. This time we continue to conduct experiments on security configuration through official DEMO and some scenarios designed by myself, mainly to solve the setting of secure routes in daily cluster use. This time there are the following scenarios:

  • JWT permission authentication
  • HTTP Traffic Authorization
  • TCP Traffic Authorization

Environment configuration

You can skip this step if you have configured the isTIO from Start to Drop series 2 – ISTIO Traffic Control in action. Download official DEMO

Kubectl label namespace default IStio-injection =enabled # Kubectl apply-f Samples/bookinfo/platform/kube/bookinfo yaml # initial application deployment kubectl apply - f Samples/bookinfo/networking/destination - rule - all. # yaml deployment of the default rulesCopy the code

JWT permission authentication

Environment to prepare

JWT is an HTTP request authentication scheme provided by ISTIO. We need to put JWK in YAML. To generate JWK, we first generate an RSA key

Openssl genrsa -out rsa-private-key.pem 2048 # Generate RSA key 2048 bits (not 256 bits) openssl genrsa -out rsa-private-key.pem 2048Copy the code

We then use our key to generate the corresponding JWT and public key

# need to install dependencies: pip3 install jwcrypto -i https://pypi.tuna.tsinghua.edu.cn/simple
from jwcrypto.jwk import JWK
from pathlib import Path

private_key = Path("rsa-private-key.pem").read_bytes()
jwk = JWK.from_pem(private_key)

Export the RSA Public Key
public_key = jwk.public().export_to_pem()
with open('./public.pem'.'w') as f:    Set the file object
    f.write(bytes.decode(public_key))

print("="*30)

# export JWK
jwk_bytes = jwk.public().export()
with open('./jwt.json'.'w') as f:    Set the file object
    f.write(jwk_bytes)
Copy the code

Paste the private key key and JWK that I tested

jwt.json {"e":"AQAB","kid":"Rv59tGv8jD63K0FcPKVWWOX8_y1LLlmp3mUSG_xrzfg","kty":"RSA","n":"pzQoa-R1yP2fz6kyWfeIZfCeYkVmXqK7hI1PuF8 LSUzcITZoLYcp54hGm6Qdi059RFlPTScWaPLfGOxB75RRZt7uw2FoMgH6X2Zi3KdQdytmdz3viU2G-kgdn1pRoq9wVXGomYL0myzOt7Uz8cLHvCG-pAGNQYb deQ_NeMgHmCgLSmcXjRQQfg55SPbB2t4rZM_IJs0jL-0ckEXvN1UwYFVifJ6eGquyV_bdoknkdCFH3eTpgCSXMwJGkh0JXSBQLW5hHPa2xOA90JYfTNuqfSD MBmRy2vVurbgZeJggFgSD-atP8_JXZ1oz4ZwJwlFjX0F1lkpTvomDORL7ATrtHQ"} public.pem -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApzQoa+R1yP2fz6kyWfeI ZfCeYkVmXqK7hI1PuF8LSUzcITZoLYcp54hGm6Qdi059RFlPTScWaPLfGOxB75RR Zt7uw2FoMgH6X2Zi3KdQdytmdz3viU2G+kgdn1pRoq9wVXGomYL0myzOt7Uz8cLH vCG+pAGNQYbdeQ/NeMgHmCgLSmcXjRQQfg55SPbB2t4rZM/IJs0jL+0ckEXvN1Uw YFVifJ6eGquyV/bdoknkdCFH3eTpgCSXMwJGkh0JXSBQLW5hHPa2xOA90JYfTNuq fSDMBmRy2vVurbgZeJggFgSD+atP8/JXZ1oz4ZwJwlFjX0F1lkpTvomDORL7ATrt HQIDAQAB -----END PUBLIC KEY----- rsa-private-key.pem -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEApzQoa+R1yP2fz6kyWfeIZfCeYkVmXqK7hI1PuF8LSUzcITZo LYcp54hGm6Qdi059RFlPTScWaPLfGOxB75RRZt7uw2FoMgH6X2Zi3KdQdytmdz3v iU2G+kgdn1pRoq9wVXGomYL0myzOt7Uz8cLHvCG+pAGNQYbdeQ/NeMgHmCgLSmcX jRQQfg55SPbB2t4rZM/IJs0jL+0ckEXvN1UwYFVifJ6eGquyV/bdoknkdCFH3eTp gCSXMwJGkh0JXSBQLW5hHPa2xOA90JYfTNuqfSDMBmRy2vVurbgZeJggFgSD+atP 8/JXZ1oz4ZwJwlFjX0F1lkpTvomDORL7ATrtHQIDAQABAoIBADXiwFgtM6yH2HYz YC/Qb5vy1Qh0t04ugeJxLE0ODHQeGh92ClMJ6X5d0+ubE45uyD47ziveOgGurCm7 EnDkyustU6OSA+OB8a/HLntQTMVrLkWlp0oHu0Vz8mAF2qNkiP5wd9apdq1/3ksk Uc5LaNV/xpHSkjSZA01dw2l2hcQKNkkKthcO0g836l/3Sw1YlP64YZkBn19m+Xim YDshEjoA4/vlyf8xTlgreKa/t9YhRIpM/l6h774cahF8PRQeBQELIx4UKTzqMJf2 0DxyjHLMv0Yt4HI5aXwSKEg7UR7ke3Tul4cwi/lyGfVTniQHptHMcAOXwrYhki5I pwp1DwECgYEA161tUcTsyQrZQohnRLP3srqyBkPCo9zCy74ezsDwK4AZZ8/797Gm pETiqLv9FC3mYPk1DS5P3MmCiW326FyYsQ66WmYmoSXPjHzVi8mLZxegscjm3/47 jPwJgusAK6EqNatmLkOlZml8yWrHMC8sQtAoTo1hM+wrYk3QUglMykECgYEAxna5 FUiuiClhoPqZH+NVCBvNotdaomnvjm9GRz4N4SSzzdGFba+VGi1iCRGXKqXM0G1H symZTung/h8EqDD8pRUgT1/twH9HmuoLETaB9YqZa+8nK5Gj9XsxIp1OiTLJuHo+ PdLFTze0I+SEKDrZRBRAobUZYo8t+DIFz3yNk90CgYEAvG6O9kPgxH0v+AsIfmPl 40dtxj9pTJTRtAQ1ElpK+xZ+G88AyxVxDFAK33Tu1bSMdOkFyrBNog6Ed+GVOMm9 teOyOMzKrzxDqvBd+jVqD/X6tZla7RRHnxOMk88RZQz3vdA0A/OiDGnZVnht8tEk EHOg45Bt/lk2RjrJ6QKrDoECgYB6YnAUHfPy54Ha4W5X6bpf+7U9fAvaJ/WgIiJ2 gF/SvO1cOJ5NW39Y2y+fZAeSNxgsV5dldnuh3DvwuXQHu92wd2yrRf65PEQN1dHp VXGi10tw8dN33KH9GXDdZaAunvEiH9AOE9G03ibqE1sj69ZbUxngHmt/CchRS5el saskyQKBgQChCANNAmCKMEFWvv3CBr3rk8T5jV38N2nraq9m1/rwdmtkHJFpBAF9 9N4azkSMVTAd/Rpsdu3phH55Cuv4K/dyY9Lkus4AcPXFgBMEDP6ySZNdBPZHkt29 4M8mNjSJuQ3I0dWzVAkSC6XoTJiWZou3YJ9Qqxa9CxsQJQBTwd1uYA== -----END RSA PRIVATE KEY-----Copy the code

With the JWK ready, we deploy two workloads

kubectl create ns foo
kubectl label namespace foo istio-injection=enabled
kubectl apply -f  samples/httpbin/httpbin.yaml -n foo
kubectl apply -f  samples/sleep/sleep.yaml -n foo
Copy the code

Test if you can access it

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



No problem, let’s deploy the JWT configuration.

kubectl apply -f - <<EOF apiVersion: "security.istio.io/v1beta1" kind: "RequestAuthentication" metadata: name: "jwt-example" namespace: foo spec: selector: matchLabels: app: httpbin jwtRules: - issuer: "[email protected]" jwks: | { "keys": [ { "e":"AQAB", "kid":"Rv59tGv8jD63K0FcPKVWWOX8_y1LLlmp3mUSG_xrzfg", "kty":"RSA", "n":"pzQoa-R1yP2fz6kyWfeIZfCeYkVmXqK7hI1PuF8LSUzcITZoLYcp54hGm6Qdi059RFlPTScWaPLfGOxB75RRZt7uw2FoMgH6X2Zi3KdQdytmdz3viU2 G-kgdn1pRoq9wVXGomYL0myzOt7Uz8cLHvCG-pAGNQYbdeQ_NeMgHmCgLSmcXjRQQfg55SPbB2t4rZM_IJs0jL-0ckEXvN1UwYFVifJ6eGquyV_bdoknkdCF H3eTpgCSXMwJGkh0JXSBQLW5hHPa2xOA90JYfTNuqfSDMBmRy2vVurbgZeJggFgSD-atP8_JXZ1oz4ZwJwlFjX0F1lkpTvomDORL7ATrtHQ" } ] } EOFCopy the code

JWKS is our plaintext configuration, which can be changed to jwksUri to import JSON files. Issuer is the user identifier of the configuration, which needs to be added in the JWT configuration.

And then we open itjwtThe corresponding tokens are generated through this website

Note the position marked in the red box, and the corresponding configuration is required. Exp must be configured, otherwise it will be regarded as an expired token.

An httpbin request was attempted using the wrong TOKEN

kubectl exec$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items.. metadata.name}) -c sleep -n foo -- curl"http://httpbin.foo:8000/headers" -s -o /dev/null -H "Authorization: Bearer invalidToken" -w "%{http_code}\n"
Copy the code

If 401 is incorrect, let’s try using the correct token

exportTOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJyeWFuQGxlbW9uYm94LmNvbSIsImlzcyI6InJ5YW5AbGVtb25ib3guY29tIiwiaWF0I joxNTU2MjM5MDIyLCJleHAiOjQ2ODU5ODk3MDB9.KRhZHEfL-IPGySxG3vdf_WDXdcZADni0sDg_CIZ8qbrR1q44NPNC7pB45KjGsnOMyWxUrUg1kpPTk2_g PNUs5KEUrqq9AgpYBl9lC2RhEGUIRA2jsngiYBH_fWCDw3YFfD2MtNbV6R6Ijzg4ttxxnJbYM2qIt-VNGnTYFrrkCzJeEaLkKTdo9l1u-mKyI68VpV7K003d 5E2e8r_JgbXb-S6vNg7koKRkXZXWWzsAXgdSB5hJPFj0cM9tCitGdIF3QX0xx9QV9UH35LP8L4WZr3uJjqIEGMLPn6ozdhHTivOWnvBOHEG8pGb47ntwIQF7 U1OdnuDDshG82PcP45duAA kubectlexec$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items.. metadata.name}) -c sleep -n foo -- curl"http://httpbin.foo:8000/headers" -s -o /dev/null -H "Authorization: Bearer $TOKEN" -w "%{http_code}\n"
Copy the code

The request succeeded, but JWT is not fully configured at this point. Let’s try the request without TOKEN.

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

The discovery request also succeeds because JWT only limits the correctness of the token and does not care if there is one.

HTTP Traffic Authorization

To solve this problem, we need to add another layer of restrictions on AuthorizationPolicy

kubectl delete -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: require-jwt
  namespace: foo
spec:
  selector:
    matchLabels:
      app: httpbin
  action: ALLOW
  rules:
  - from:
    - source:
       requestPrincipals: ["[email protected]/[email protected]"]
EOF
Copy the code

It is important to note that the form source configuration does not work when accessed from the ingress. It requires mTLS. If you are configuring to the ingress level without mTLS, you need to use yamL below

kubectl delete -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: require-jwt
  namespace: foo
spec:
  selector:
    matchLabels:
      app: httpbin
  action: ALLOW
  rules:
   - when:
      - key: request.auth.claims[iss]
        values: ["[email protected]"]
EOF
Copy the code

After the configuration, we will try tokenless access again

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

As you can see, error 403 is returned, trying to use the correct TOKEN

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

We can also try it on the previously configured website

kubectl apply -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "RequestAuthentication"
metadata:
  name: "jwt-reviews"
  namespace: default
spec:
  selector:
    matchLabels:
      app: productpage
  jwtRules:
  - issuer: "[email protected]"
    jwks: | { "keys": [ { "e":"AQAB", "kid":"Rv59tGv8jD63K0FcPKVWWOX8_y1LLlmp3mUSG_xrzfg", "kty":"RSA", "n":"pzQoa-R1yP2fz6kyWfeIZfCeYkVmXqK7hI1PuF8LSUzcITZoLYcp54hGm6Qdi059RFlPTScWaPLfGOxB75RRZt7uw2FoMgH6X2Zi3KdQdytmdz3viU2 G-kgdn1pRoq9wVXGomYL0myzOt7Uz8cLHvCG-pAGNQYbdeQ_NeMgHmCgLSmcXjRQQfg55SPbB2t4rZM_IJs0jL-0ckEXvN1UwYFVifJ6eGquyV_bdoknkdCF H3eTpgCSXMwJGkh0JXSBQLW5hHPa2xOA90JYfTNuqfSDMBmRy2vVurbgZeJggFgSD-atP8_JXZ1oz4ZwJwlFjX0F1lkpTvomDORL7ATrtHQ" } ] }EOF

kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: "reviews-viewer"
  namespace: default
spec:
  selector:
    matchLabels:
      app: productpage
  action: ALLOW
  rules:
  - from:
    - source:
       requestPrincipals: ["[email protected]/[email protected]"]
EOF
Copy the code

Using Chrome’s extension bwisse, you can configure the access header when accessing



When not enabled:



After the opening:



The permission configuration took effect.

TCP Traffic Authorization

Environment configuration

The TCP authorization configuration is not much different from HTTP, and the V2 version of Bookinfo-ratings-v2 is deployed first

kubectl apply -f samples/bookinfo/platform/kube/bookinfo-ratings-v2.yaml
kubectl apply -f samples/bookinfo/networking/destination-rule-all-mtls.yaml # New goal rules
kubectl apply -f samples/bookinfo/networking/virtual-service-ratings-db.yaml Update the Reviews workload to use only the V2 version of the Ratings workload
kubectl apply -f samples/bookinfo/platform/kube/bookinfo-db.yaml Deploy the MongoDB workload
Copy the code



Now, if you go through it, it already shows the book reviews in the database. Try disabling all TCP on Mongo first

kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: deny-all
spec:
  selector:
    matchLabels:
      app: mongodb
EOF

Copy the code



The Book Reviews in the lower right corner of the page shows an error message: “Ratings service is currently unavailable”, at this point the corresponding Mongo data cannot be called up, creating a separate allow rule for port 27017.

kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: bookinfo-ratings-v2
spec:
  selector:
    matchLabels:
      app: mongodb
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/default/sa/bookinfo-ratings-v2"]
    to:
    - operation:
        ports: ["27017"]
EOF
Copy the code

Refreshing the page again has successfully deployed a workload that communicates over TCP traffic and has applied grid level and workload level authorization policies to enforce access control over requests.