0

Problem

We run Istio on our Kubernetes cluster and we're implementing AuthorizationPolicies. We want to apply a filter on email address, an HTTP-condition only applicable to HTTP services. Our Kiali service should be an HTTP service (it has an HTTP port, an HTTP listener, and even has HTTP conditions applied to its filters), and yet the AuthorizationPolicy does not work. What gives?

Our setup

We have a management namespace with an ingressgateway (port 443), and a gateway+virtual service for Kiali. These latter two point to the Kiali service in the kiali namespace.

Both the management and kiali namespace have a deny-all policy and an allow policy to make an exception for particular users. (See AuthorizationPolicy YAMLs below.)

Authorization on the management ingress gateway works. The ingress gateway has 3 listeners, all HTTP, and HTTP conditions are created and applied as you would expect. You can visit its backend services other than Kiali if you're on the email list, and you cannot do so if you're not on the email list.

Authorization on the Kiali service does not work. It has 99 listeners (!), including an HTTP listener on its configured 20001 port and its IP, but it does not work. You cannot visit the Kiali service (due to the default deny-all policy). The Kiali service has port 20001 enabled and named 'http-kiali', so the VirtualService should be ok with that. (See YAMls for service and virtual service below).

EDIT: it was suggested that the syntax of the email values matters. I think that has been taken care of:

  • in the management namespace, the YAML below works as expected
  • in the kiali namespace, the same YAML fails to work as expected.
  • the empty brackets in the 'property(map[request.auth.claims[email]:{[brackets@test.com] []}])' message I think are the Values (present) and NotValues (absent), respectively, as per 'constructed internal model: &{Permissions:[{Properties:[map[request.auth.claims[email]:{Values:[brackets@test.com] NotValues:[]}]]}]}'
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: testpolicy-brackets
  namespace: kiali
spec:
  action: ALLOW
  rules:
  - when:
    - key: source.namespace
      values: ["brackets"]
    - key: request.auth.claims[email]
      values: ["brackets@test.com"]
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: testpolicy-yamllist
  namespace: kiali
spec:
  action: ALLOW
  rules:
  - when:
    - key: source.namespace
      values:
        - list
    - key: request.auth.claims[email]
      values:
        - list@test.com
debug   rbac    found authorization allow policies for workload [app=kiali,pod-template-hash=5c97c4bb66,security.istio.io/tlsMode=istio,service.istio.io/canonical-name=kiali,service.istio.io/canonical-revision=v1.16.0,version=v1.16.0] in kiali
debug   rbac    constructed internal model: &{Permissions:[{Services:[] Hosts:[] NotHosts:[] Paths:[] NotPaths:[] Methods:[] NotMethods:[] Ports:[] NotPorts:[] Constraints:[] AllowAll:true v1beta1:true}] Principals:[{Users:[] Names:[] NotNames:[] Group: Groups:[] NotGroups:[] Namespaces:[] NotNamespaces:[] IPs:[] NotIPs:[] RequestPrincipals:[] NotRequestPrincipals:[] Properties:[map[source.namespace:{Values:[brackets] NotValues:[]}] map[request.auth.claims[email]:{Values:[brackets@test.com] NotValues:[]}]] AllowAll:false v1beta1:true}]}
debug   rbac    generated policy ns[kiali]-policy[testpolicy-brackets]-rule[0]: permissions:<and_rules:<rules:<any:true > > > principals:<and_ids:<ids:<or_ids:<ids:<metadata:<filter:"istio_authn" path:<key:"source.principal" > value:<string_match:<safe_regex:<google_re2:<> regex:".*/ns/brackets/.*" > > > > > > > ids:<or_ids:<ids:<metadata:<filter:"istio_authn" path:<key:"request.auth.claims" > path:<key:"email" > value:<list_match:<one_of:<string_match:<exact:"brackets@test.com" > > > > > > > > > >
debug   rbac    ignored HTTP principal for TCP service: property(map[request.auth.claims[email]:{[brackets@test.com] []}])
debug   rbac    role skipped for no principals found

debug   rbac    found authorization allow policies for workload [app=kiali,pod-template-hash=5c97c4bb66,security.istio.io/tlsMode=istio,service.istio.io/canonical-name=kiali,service.istio.io/canonical-revision=v1.16.0,version=v1.16.0] in kiali
debug   rbac    constructed internal model: &{Permissions:[{Services:[] Hosts:[] NotHosts:[] Paths:[] NotPaths:[] Methods:[] NotMethods:[] Ports:[] NotPorts:[] Constraints:[] AllowAll:true v1beta1:true}] Principals:[{Users:[] Names:[] NotNames:[] Group: Groups:[] NotGroups:[] Namespaces:[] NotNamespaces:[] IPs:[] NotIPs:[] RequestPrincipals:[] NotRequestPrincipals:[] Properties:[map[source.namespace:{Values:[list] NotValues:[]}] map[request.auth.claims[email]:{Values:[list@test.com] NotValues:[]}]] AllowAll:false v1beta1:true}]}
debug   rbac    generated policy ns[kiali]-policy[testpolicy-yamllist]-rule[0]: permissions:<and_rules:<rules:<any:true > > > principals:<and_ids:<ids:<or_ids:<ids:<metadata:<filter:"istio_authn" path:<key:"source.principal" > value:<string_match:<safe_regex:<google_re2:<> regex:".*/ns/list/.*" > > > > > > > ids:<or_ids:<ids:<metadata:<filter:"istio_authn" path:<key:"request.auth.claims" > path:<key:"email" > value:<list_match:<one_of:<string_match:<exact:"list@test.com" > > > > > > > > > >
debug   rbac    ignored HTTP principal for TCP service: property(map[request.auth.claims[email]:{[list@test.com] []}])
debug   rbac    role skipped for no principals found

(Follows: a list of YAMLs mentioned above)

# Cluster AuthorizationPolicies
## Management namespace
Name:         default-deny-all-policy
Namespace:    management
API Version:  security.istio.io/v1beta1
Kind:         AuthorizationPolicy
Spec:
---
Name:         allow-specified-email-addresses
Namespace:    management
API Version:  security.istio.io/v1beta1
Kind:         AuthorizationPolicy
Spec:
  Action:  ALLOW
  Rules:
    When:
      Key:  request.auth.claims[email]
      Values:
        my.email@my.provider.com
---
## Kiali namespace
Name:         default-deny-all-policy
Namespace:    kiali
API Version:  security.istio.io/v1beta1
Kind:         AuthorizationPolicy
Spec:
---
Name:         allow-specified-email-addresses
Namespace:    kiali
API Version:  security.istio.io/v1beta1
Kind:         AuthorizationPolicy
Spec:
  Action:  ALLOW
  Rules:
    When:
      Key:  request.auth.claims[email]
      Values:
        my.email@my.provider.com
---
# Kiali service YAML
apiVersion: v1
kind: Service
metadata:
  labels:
    app: kiali
    version: v1.16.0
  name: kiali
  namespace: kiali
spec:
  clusterIP: 10.233.18.102
  ports:
  - name: http-kiali
    port: 20001
    protocol: TCP
    targetPort: 20001
  selector:
    app: kiali
    version: v1.16.0
  sessionAffinity: None
  type: ClusterIP
---
# Kiali VirtualService YAML
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: kiali-virtualservice
  namespace: management
spec:
  gateways:
  - kiali-gateway
  hosts:
  - our_external_kiali_url
  http:
  - match:
    - uri:
        prefix: /
    route:
    - destination:
        host: kiali.kiali.svc.cluster.local
        port:
          number: 20001

2 Answers2

1

Marking as solved: I had forgotten to apply a RequestAuthentication to the Kiali namespace.

The problematic situation, with the fix in bold:

  • RequestAuthentication on the management namespace adds a user JWT (through an EnvoyFilter that forwards requests to an authentication service)
  • AuthorizationPolicy on the management namespace checks the request.auth.claims[email]. These fields exist in the JWT and all is well.

  • RequestAuthentication on the Kiali namespace missing. I fixed the problem by adding a RequestAuthentication for the Kiali namespace, which populates the user information, which allows the AuthorizationPolicy to perform its checks on actually existing fields.

  • AuthorizationPolicy on the Kiali namespace also checks the request.auth.claims[email] field, but since there is no authentication, there is no JWT with these fields. (There are some fields populated, e.g. source.namespace, but nothing like a JWT.) Hence, user validation on that field fails, as you would expect.
  • what would be the issue at this https://stackoverflow.com/questions/63319153/istio-service-mesh-security-with-authorizationpolicy-requestauthentication ? the RequestAuthentication and AuthorizationPolicy are being ignored – Tiago Medici Aug 09 '20 at 08:47
  • Would https://github.com/oauth2-proxy/oauth2-proxy/issues/711 perhaps be relevant for you? We have encountered this as well recently: in our Istio > envoy > OAuth2 setup, OAuth2 always replies with a 200 without even going to Keycloak for the user. – Joren Zandstra Aug 12 '20 at 14:28
  • Also: in your issue you say "requests are always allowed." Do you have some oauth2 proxy logs during such login attempts? (I'm commenting on my issue because I can't comment on yours---50 reputation required, sorry.) – Joren Zandstra Aug 12 '20 at 14:30
  • having a keycloak runing on k8s cluster, should i point to the nodeport the issuer and jwksUri or the internal port ? – Tiago Medici Oct 31 '20 at 10:34
0

According to istio documentation:

Unsupported keys and values are silently ignored.

In your debug log there is:

debug   rbac    ignored HTTP principal for TCP service: property(map[request.auth.claims[email]:{[my.email@my.provider.com] []}])

As You can see there are []}] chars there that might suggest that the value got parsed the wrong way and got ignored as unsupported value.


Try to put Your values like suggested in documentation inside [""]:

request.auth.claims

Claims from the origin JWT. The actual claim name is surrounded by brackets HTTP only

key: request.auth.claims[iss]

values: ["*@foo.com"]

Hope it helps.

Piotr Malec
  • 3,429
  • 11
  • 16
  • Thanks, that might very well be it. Will update with results. – Joren Zandstra May 15 '20 at 07:44
  • Unfortunately, this does not work: both `values: - element` and `values: ["element"]` fail to work. (And `values: '["element"]'` fails validation, since the value does actually need to be an array apparently, and not a string which is itself an array literal.) – Joren Zandstra May 18 '20 at 10:54
  • Also, the same AuthorizationPolicies do work the `management` namespace... – Joren Zandstra May 18 '20 at 11:24
  • I suggest going over troubleshooting steps from istio [documentation](https://istio.io/docs/ops/common-problems/security-issues/) for authorization debugging. – Piotr Malec May 22 '20 at 11:51
  • We stumbled upon the provisional answer: I was applying an AuthorizationPolicy based on user JWT properties. It turns out, by the time you're entering Kiali the system is using mTLS, so in the management-ingressgateway sidecar to the kiali sidecar communication, there's no longer a request.auth.claims[email] like there is in the original request. Hence, the request gets denied. Thanks for the help! – Joren Zandstra May 27 '20 at 09:28