Published on 00/00/0000
Last updated on 00/00/0000
Published on 00/00/0000
Last updated on 00/00/0000
Share
Share
PRODUCT
9 min read
Share
Istio claims that it helps to connect, secure, control, and observe services. In previous blogs, we talked about Istio Ingress, about connect, even more about observe, and also had a few articles about secure. But so far, we haven't really touched control.
This post tries to fill that gap by explaining Istio Access control and discussing Istio's access control model, or more specifically AuthorizationPolicies
.
Your Istio authorization policy is the framework through which access control will work. If it sounds complicated, it can be—which is why it helps to break it down into separate segments. In this article, we’ll address Istio access control, Kubernetes network policies, and the different aspects of building your own authorization policies for better security.
Istio Authorization can be used to enforce access control rules between workloads. It answers the question: who can access what, under which specific conditions? Like any other mesh configuration, authorization rules can be specified through Kubernetes CRDs. The API is quite simple, it consists of a single CRD, called AuthorizationPolicy
, but more on the YAML details later.
First, let's see how these rules are enforced in Istio. If you're reading this article, you should already be familiar with Istio's high-level architecture, but here's a (very) brief recap. Istio has a data plane, and a control plane. The data plane consists of sidecar proxies running alongside the application containers in the same pod, and they are responsible for forwarding all incoming, and outgoing traffic to the application.
On the other hand, the control plane accepts user configuration through CRDs, and - among a few other things - transforms these CRDs to Envoy configuration and delivers it to the proxies. The sidecars are Envoy proxies, and the control plane is now a single service, called istiod
.
Similarly to telemetry and traffic management, the real deal happens in the data plane. All checks are performed runtime by the Envoy proxy's authorization engine. A request is evaluated against the authorization policies when it arrives to the proxy. Then Envoy returns the result, either ALLOW
or DENY
.
For someone just getting to know Istio, it can be confusing that they may bump into blog posts about Istio access control containing mentions of CRDs like ClusterRbacConfig
, ServiceRole
, ServiceRoleBinding
. Those resources were part of the v1alpha1
API that is now completely replaced by the v1beta1
API. The new API was introduced in Istio 1.4, and from Istio 1.6, the old API is not supported anymore.
If you're looking for a migration path, I'd recommend reading the official blog post. The new model simplifies configuration (one CRD instead of three), supports ingress and egress gateways, and better aligns with the Istio configuration model, as it is applied to workloads instead of services.
When talking about AuthorizationPolicies
, we have to mention Kubernetes NetworkPolicies
, because they are quite similar in terms of what problem they are trying to solve. The Kubernetes docs define network policies as follows:
A network policy is a specification of how groups of pods are allowed to communicate with each other and other network endpoints. For more details about network policies check out our blog post, Exploring Network Policies in Kubernetes.
There's no easy answer to which one is better? because they are good at different things. Istio policy enforcement works at the application layer (L7) — that's where the Envoy proxies operate while Kubernetes network policies work at the network (L3) and transport layers (L4). Different networking solutions, like Calico, implement Kubernetes network policies These solutions run a controller that's watching NetworkPolicies
, and configures the underlying networking layer accordingly. Operating at the application layer has its advantages.
Because Envoy understands different protocols (most commonly HTTP), it allows for a rich set of attributes to base policy decisions on. A few examples are policies based on HTTP methods, URIs, or HTTP headers. A NetworkPolicy
cannot do these, because these concepts are unknown at the network and transport layers. But operating at the network layer has the advantage of being universal, since all network applications use IP. So you can apply policies regardless of the layer 7 protocol, which will be enforced in the kernel space. It's extremely fast, but not as flexible as Envoy policies.
Another difference is that NetworkPolicies
work in an additive, whitelist model. When a NetworkPolicy
selects a specific pod, that pod will reject any connections, except those that are explicitly allowed. These policies are additive, they do not conflict, and order of evaluation is irrelevant. AuthorizationPolicies
on the other hand have DENY
and ALLOW
rules as well, that complicates things a bit. But again, allows for more flexible rules.
So should you use Istio AuthorizationPolicies
over plain Kubernetes NetworkPolicies
? Well, it always depends on your use case. If you want to have a finer grained authorization model, you should go with Istio, but if your only requirement is that "pod A should only be able to communicate with pod B", then NetworkPolicies
are just as good. Or you can even use the two concepts side-by-side.
Istio authorization doesn't need to be explicitly enabled. When no AuthorizationPolicies
select a workload, all requests are allowed. To enforce access control, you have to apply at least one AuthorizationPolicy
resource.
To start experimenting with Istio and AuthorizationPolicies
, we suggest trying Backyards (now Cisco Service Mesh Manager) and get up and running with an example application in minutes. Backyards (now Cisco Service Mesh Manager) provides an Istio control panel where you can track, visualize or even manage your Istio YAML configuration.
AuthorizationPolicies
can be mesh-, namespace-, and workload-wide depending on the namespace
and the spec/selector
field. The namespace
of the resource determines the namespace where the rules will be enforced. When the spec/selector
field is omitted, the rules are namespace-wide. The selector, which is a standard Kubernetes label selector, can be used to restrict the policy to specific workload(s) in the namespace, making the policy workload-wide. Just like with the PeerAuthentication
resource, putting it in the root Istio namespace (usually istio-system
), without a selector has a special effect: these rules will be enforced mesh-wide, in all namespaces.
The following is a workload-wide policy, that applies to pods in the backyards-demo
namespace that have the app=catalog
label.
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: get-only
namespace: backyards-demo
spec:
selector:
matchLabels:
app: catalog
action: ALLOW
rules:
- to:
- operation:
methods: ["GET"]
Unlike NetworkPolicies
, AuthorizationPolicies
support both ALLOW
and DENY
actions. If any ALLOW
policies are applied to a workload, traffic is denied to that workload by default, and only those explicitly configured requests are allowed. It could be a bit confusing at first, especially since the default action is ALLOW
, so a policy like this will deny all traffic in a namespace:
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: deny-all
namespace: backyards-demo
spec: {}
While this one allows all traffic:
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: allow-any
namespace: backyards-demo
spec:
rules:
- {}
The deny policies take precedence over allow policies, so for example if there are conflicting rules, where a policy allows GET requests, and another denies them, the deny policy will be applied. When multiple policies are applied to the same workload, Istio applies them additively.
An authorization policy contains a list of rules, that describe which requests are matched, and then allowed or denied based on the action. Rules are built of three parts: sources, operations and conditions. Sources are specified in the from
field, and answer the who? question. Operations are listed in the to
field, and answer the what? question. Then, at last, conditions are described in the when
field and answer the when? question. Let's see a concrete example:
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: catalog
namespace: backyards-demo
spec:
selector:
matchLabels:
app: catalog
rules:
- from:
- source:
principals:
[
"cluster.local/ns/backyards-demo/sa/frontpage",
]
to:
- operation:
methods: ["GET"]
This AuthorizationPolicy
is applied to the catalog
workload in the backyards-demo
namespace, and while not explicitly specified, it's an ALLOW
rule, so it will deny all traffic that doesn't match the rules described here.
The rules contain a source
, which means that traffic is allowed only from a workload with the cluster.local/ns/backyards-demo/sa/frontpage
identity (service account). It also contains an operation
, that only matches GET
requests. It doesn't contain a condition
, which means match any conditions.
So to recap, the above policy allows GET requests from workloads with the cluster.local/ns/backyards-demo/sa/frontpage
identity to backyard-demo/catalog
, and denies everything else. In the example, the source is a principal, but it can be requestPrincipals
, namespaces
or ipBlocks
as well. Istio also support exclusion matching, by providing the same fields with a not
prefix. So for example notNamespaces: default
would match sources from all namespaces, except from default
.
Let's take a look at the operation
field as well: along methods
, valid matchers are hosts
, ports
, paths
and their exclusion pairs, like notHosts
. In most cases the when
field can be omitted, it's usually only used in complex scenarios, but it can be used to further customize request matching with a list of supported Istio attributes. For example the below example matches request header values:
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: header-matching
namespace: backyards-demo
spec:
selector:
matchLabels:
app: catalog
action: ALLOW
rules:
- from:
- source:
principals:
[
"cluster.local/ns/backyards-demo/sa/frontpage",
]
to:
- operation:
methods: ["GET"]
when:
- key: request.headers[version]
values: ["v1", "v2"]
Finally, take a look at a more complex rule to see how it matches requests when most fields contain multiple entries:
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: example-policy
namespace: backyards-demo
spec:
selector:
matchLabels:
app: movies
action: ALLOW
rules:
- from:
- source:
principals: ["cluster.local/ns/backyards-demo/sa/catalog"]
- source:
namespaces: ["backyards-test"]
to:
- operation:
methods: ["GET"]
paths: ["/api/v1*"]
- operation:
methods: ["POST"]
paths: ["/api/v1*"]
- from:
- source:
principals: ["cluster.local/ns/backyards-demo/sa/bookings"]
to:
- operation:
methods: ["GET"]
paths: ["/api/v1/movies*"]
when:
- key: request.auth.claims[iss]
values: ["https://accounts.banzaicloud.io"]
This final example contains two separate rules in one policy with an ALLOW
action.
app=movies
in the backyards-demo
namespaceGET
requests to /api/v1*
OR POST
requests to /api/v1*
from workloads in the backyards-test
namespace, OR from workloads with the cluster.local/ns/backyards-demo/sa/catalog
service accountGET
requests to /api/v1/movies*
from workloads with the cluster.local/ns/backyards-demo/sa/bookings
service account, when the request has a valid JWT token, issued by "https://accounts.banzaicloud.io"AuthorizationPolicy
entries for the two different rulessource.principals
, source.namespaces
, or the connection.sni
conditionhosts
, methods
and paths
operations have no effect, as well as the request_principals
field in the source section and some of the custom conditions*
, presence matching is *
and it's used to specify anything but emptysource.principals: ["*"]
, that means all authenticated requestsYou can use Istio to enforce access control between workloads in the service mesh using the AuthorizationPolicy
custom resource. This kind of access control is enforced at the application layer by the Envoy sidecar proxies. It gives the user a very powerful and flexible, yet performant way of authorization between Kubernetes workloads.
For more in the world of Istio and data movement, read our guide to Istio ingress, a simpler resource with all sorts of helpful use cases.
Get emerging insights on innovative technology straight to your inbox.
Discover how AI assistants can revolutionize your business, from automating routine tasks and improving employee productivity to delivering personalized customer experiences and bridging the AI skills gap.
The Shift is Outshift’s exclusive newsletter.
The latest news and updates on generative AI, quantum computing, and other groundbreaking innovations shaping the future of technology.