Skip to content

09 3 security

Authorization Policy💣

Authorization is answering the access control portion of the question. Is an authenticated principal allowed to perform an action on an object? Can User A send a GET request to path /hello to Service A? Remember that certain actions may still be prohibited even if we authenticate a principal.

Your company ID card might be valid and authentic. However, you won’t be able to use it to enter the offices of a different company. If we continue with the customs officer metaphor from before, authorization is similar to a visa stamp in your passport.

It is essential to have both authentication and authorization. Without one or the other, it will not be beneficial. For proper access control, we need both.

For example: If we only authenticate the principals and don’t authorize them, they can do anything and perform any actions on any objects. Conversely, suppose we authorize a request but don’t authenticate it. In that case, we can pretend to be someone else and perform any actions on objects again.

Armed with an authenticated principal, we can now decide to restrict access based on that. To do that, we use an AuthorizationPolicy. The AuthorizationPolicy resource is where we can use the principle from the PeerAuthentication policies and the RequestAuthentication policy. Suppose we attempt to write policies based on peer or service identities. In that case, we can use the principals’ field, and if we are making decisions based on the users, we will use request principals.

This example applies the AuthorizationPolicy to all workloads matching the app=prod label. The second part of the resource is where we define the rules and say that we allow calls from a source with any requestPrincipal set. We aren’t checking for any specific principal here, just that the principal is set. With this AuthorizationPolicy and the RequestAuthentication policy, we guarantee that only authenticated requests will reach the prod workloads.

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: require-jwt
  namespace: default
spec:
  selector:
    matchLabels:
      app: prod
  rules:
  - from:
    - source:
        requestPrincipals: ["*"]

The previous example was simple, but we can write rules in the authorization policies in multiple ways. We have seen the from field in the last example. With from, you can define a list of source identities, namespaces, and principals allowed to call the services to which we’ve applied the policy.

This example will allow calls from services that use the workload service account, are coming from the prod namespace, and have the requesting principal set to tetrate.io/nauticalmike.

rules:
- from:
  - source:
      principals: ["cluster.local/ns/default/sa/workload"]
  - source:
      namespaces: ["prod"]
  - source:
      requestPrincipals: ["tetrate.io/nauticalmike"]

All these sources, principals, request principals, namespaces, and IP blocks also have negative matches, so we could write notPrincipals or notNamespaces to include a list of negative matches.

For example: If we want to apply the rules to all the namespaces except the prod namespace, we could write notNamespaces: ["prod"].

The second field is the to field. This field is where we can specify what paths, methods, ports, or hosts we use when making the calls to the service. This example shows that we can call DELETE on the /logs path, call GET on the /data path, and allow ports 3000 or 5000 for requests from request.host:

rules:
- from:
  - ...
  to:
  - operation:
      methods: ["DELETE"]
      paths: ["/logs*"]
  - operation:
      methods: ["GET"]
      paths: ["/data"]
  - operation:
      hosts: ["request.host"]
      ports: ["3000", "5000"]

Like with the from field, we can also write negative matches.

For example: notMethods, notPath, and notHosts.

The when field permits us to specify various conditions depending on the attributes of the request, such as headers, source IP, remote IP, namespaces, principals, destination ports, and connection SNI.

This example shows that we can only make the calls from and to when the request has a valid JWT token that was issued by accounts.google.com when the my-header contains some-value and the request is coming from the foo namespace:

rules:
- from:
  to:
  when:
    - key: request.auth.claims[iss]
      values: ["https://accounts.google.com"]
    - key: request.headers[my-header]
      values: ["some-value"]
    - key: source.namespace
      value: ["foo"]
    ...

Once we’ve written the rules, we can configure the action. We can either ALLOW or DENY the requests matching those rules. Additional supported actions are CUSTOM and AUDIT.

The CUSTOM action specifies our custom extension to handle the request. You must configure the custom extension in the MeshConfig. An example of this would be if you wanted to integrate a custom external authorization system to delegate the auth decisions. The CUSTOM action is experimental, which might break or change in future Istio versions.

The AUDIT action audits a request that matches the rules. If the request matches the rules, the AUDIT action will trigger logging that request. This action does not affect where the request is allowed or denied. Only DENY, ALLOW, and CUSTOM actions can do that. A sample scenario for when you could use the AUDIT action is when you are migrating your workloads from Permissive to STRICT mTLS mode.

spec:
  action: DENY
  rules:
  - from:
    to:
    when:
    ...

In summary, services get their identity through x.509 certificates from service accounts in Kubernetes. To manage the communication between services, use the PeerAuthentication resource. You can choose between Permissive or STRICT mutual TLS mode settings here.

The system saves specific metadata about the authenticated service, such as the principal name. It can later use to impose access control. Utilize the RequestAuthentication resource to verify the identity of users using JWT tokens. Once users have verified their identity, the system uses the data in the JWT token to control their access.

After obtaining the authenticated principal, which can be a service or a user, we can utilize an AuthorizationPolicy to determine which services have permission to access the workloads and establish restrictions for methods and paths.

Next💣

Next, we will focus on the security lab.