Jwt origin auth on ingress for some hosts but not for others

I would like to enable a policy to enforce jwt origin authentication for requests hitting the ingressgateway, but only for requests for certain hosts. i.e., requests to https://secure.example.com should require jwt origin authentication, but https://www.example.com should not.

The use case is that I have services running in the mesh that serve static content for React apps which need to be accessed without origin authentication, but service calls to API endpoints running in the mesh need jwt origin authentication. Also, some of the API services need to talk to each other with just peer authentication (for periodic tasks that do not have an external origin).

I know I could add path based exclusion rules in my policies, but that doesn’t scale very well and would be hard to maintain.

One option I am considering is to create a custom ingress gateway similar to https://istio.io/blog/2019/custom-ingress-gateway/ and and only apply the Policy object requiring jwt origin authentication to traffic targeting the custom gateway, and then use the default “istio-ingressgateway” for services that do not require jwt origin authentication.

Does anyone know of a simpler way to accomplish this without using two different ingress gateways?

This is a good use case of applying JWT policy on ingressgateway on different hosts, currently the JWT policy itself doesn’t support this, the trigger_rule only supports path but not host.

I think the option you mentioned is possible, the other option that uses only 1 ingressgateway is to combine the JWT policy and RBAC policy.

In JWT policy, you can set originIsOptional to true, and in RBAC policy, you can use request.headers[:authority] and request.auth.claims to enforce the JWT requirements based on host (i.e. the :authority header)

Note, the RBAC unofficially supports to be applied on ingressgateway since istio 1.2.

I’m also working on a new task do document this more clearly and with the authorization v1beta1 policy (this deprecates the current RBAC policy) coming in 1.4, the integration should be easier.

I will try implementing the RBAC policy approach before creating another ingress gateway. Looking forward to 1.4. Thank you.

@YangminZhu Here is my attempt at implementing this. Goal is to enable RBAC only for the ingress service and to allow any access when :authority == www.example.com or keycloak.example.com, but require a valid jwt claim for all others.

Since I’m pretty new to Istio RBAC, before I role it out, any feedback you could provide as to whether I’m on the correct path would be greatly appreciated:

Require authentication with originIsOptional for istio-ingressgateway:

---
apiVersion: "authentication.istio.io/v1alpha1"                                  
kind: "Policy"                                                                  
metadata:                                                                       
  name: "ingress-jwt"
  namespace: "istio-system"
spec:
  targets:
    - name: istio-ingressgateway
  peers:
    - mtls:
        mode: STRICT
  originIsOptional: true
  origins:
    - jwt:
        issuer: https://keycloak.example.com/auth/realms/master
        jwksUri: https://keycloak.example.com/auth/realms/master/protocol/openid-connect/certs                                                 
  principalBinding: USE_ORIGIN

Enable RBAC just for istio-ingressgateway service:

---
apiVersion: "rbac.istio.io/v1alpha1"
kind: ClusterRbacConfig
metadata:
  name: default
spec:
  mode: 'ON_WITH_INCLUSION'
  inclusion:
    services: ["istio-ingressgateway.istio-system.svc.cluster.local"]

Service role allowing access to www.example.com and keycloak.example.com:

----
apiVersion: "rbac.istio.io/v1alpha1"
kind: ServiceRole
metadata:
  name: istio-ingressgateway-allow-www
  namespace: istio-system
spec:
  rules:
  - services: ["istio-ingressgateway.istio-system.svc.cluster.local"]
    methods: ["*"]
    constraints:
    - key: "request.headers[:authority]"
      values: ["www.example.com", "keycloak.example.com"]

ServiceRoleBinding connecting the above service role to all users (authenticated or not):

---
apiVersion: "rbac.istio.io/v1alpha1"
kind: ServiceRoleBinding
metadata:
  name: all-user-istio-ingressgateway-allow-www
  namespace: default
spec:
  subjects:
  - user: "*"
  roleRef:
    kind: ServiceRole
    name: "istio-ingressgateway-allow-www"

Service role allowing access to all hosts:

----
kind: ServiceRole
metadata:
  name: istio-ingressgateway-allow-all
  namespace: istio-system
spec:
  rules:
  - services: ["istio-ingressgateway.istio-system.svc.cluster.local"]
    methods: ["*"]
    constraints:
    - key: "request.headers[:authority]"
      values: ["*"]

ServiceRoleBinding connecting the above service role to authenticated users

---
apiVersion: "rbac.istio.io/v1alpha1"
kind: ServiceRoleBinding
metadata:
  name: all-user-istio-ingressgateway-allow-all
  namespace: default
spec:
  subjects:
  - properties:
      source.principal: "*"
  roleRef:
    kind: ServiceRole
    name: "istio-ingressgateway-allow-all"

Hi, @Dan_McFarlane and @YangminZhu
Although lot has changed in the Istio release and security polices since you have posted this.
But, the use-case still remains as alive as it was then.

I am trying to use same ingressgateway for jwt authn and public services in the mesh with Authorization Polices. But Struggling very badly finding Authorization polices scope (on Ingressgateway or Namespace or Workload).

Will really appreciate input from the experts here!

I am trying to use same ingressgateway for jwt authn and public services in the mesh with Authorization Polices. But Struggling very badly finding Authorization polices scope (on Ingressgateway or Namespace or Workload).

First look at the namespace of the policy, if it is the same as the root namespace (the default root namespace value is istio-system and configurable in meshconfig), it then applies to all namespaces, think of the root namespace as a special namespace to store mesh-wide policies. If not, the policy applies only to the given namespace.

Then look at the selector, if the policy has no selector at all, it applies to all workloads in the namespace (or all namespaces if it’s root namespace). If not empty, it applies to all matched pods in the namespace (or all namespaces if it’s root namespace).

See more in Istio / Security

1 Like