Using Istio to Secure Your Multicloud Kubernetes Applications with Zero Code Changes

7 min read

Securing multicloud applications has never been easier 

Whether your computing environment uses multiple cloud providers, a single cluster, a combination of on- and off-premise solutions, or multiple containers in one cloud, having a centralized identity management can help you to preserve existing infrastructure and avoid vendor lock-in. 

Simply put, if you are using an orchestration layer like Kubernetes with your containerized applications, you can benefit from using the App Identity and Access Adapter for an abstracted level of security with zero code changes and no redeployments.

With the App Identity and Access Adapter, you can use any OIDC-compliant identity provider to control authentication and authorization policies in all environments—including frontend and backend applications—without any change to your code or the need to redeploy your application. 

In this blog post, I will be using IBM Cloud App ID with the adapter, but you can use any other OIDC-compliant identity provider, such as Auth0.

Multicloud is the new trend

Moving to a multicloud environment is the current trend in the industry. By adopting a multicloud strategy, development teams can often find improved resiliency, performance optimization, and increased cost-efficiency. With the ability to mix and match platforms and products, it prevents vendor lock-in and, ultimately, makes an organization less dependent on a sole provider. 

Using multiple cloud providers allows for greater resiliency because the risk of having concurrent downtime across applications is reduced and latency is lowered when you choose data centers closest to your customers. With no “one-size-fits-all” cloud provider, utilizing the most appropriate cloud provider that meets specific business needs or technical requirement may also be more cost-efficient in the long run. 

Security challenges

When a multicloud strategy is in place, security can become complicated as the environment grows and diversifies. While cloud providers provide protocols and tools to ensure their offerings are safe, the development teams are still responsible for the application-level security, such as API access control with OAuth2, defending against man-in-the-middle attacks with traffic encryption, and providing mutual TLS for service access control. However, this becomes complex in a multicloud environment since you might need to define those security details for each service separately. With proper security protocols in place, those external and internal threats can be mitigated. 

Development teams have spent time making their services portable to different cloud providers, and in the same regard, the security in place should be flexible and not infrastructure-dependent. 

Using Istio with Kubernetes

Typically, an orchestration service and container management platform like Kubernetes does not have all the required security features out of the box, which means cloud-native applications using Kubernetes would need to utilize a service mesh like Istio to provide a complete and secure solution.

IBM, Google, Lyft, and the open source community launched Istio—an independent, open source service mesh that creates a network of your deployed services and offers load balancing, secure service-to-service communication, monitoring, and more.

Out of the box, Istio only provides mutual TLS and basic JWT validation. By extending its telemetry and policy (Mixer) function, we can have fine-grained control of authentication, authorization, and access control for both end users and APIs using the App Identity and Access Adapter. 

The App Identity and Access Adapter

App Identity and Access Adapter is an open source project that extends Istio to control application user authentication and authorization policies across a network. Earlier, we discussed how security can become complex as a multicloud environment grows; using the App Identity and Access Adapter, however, can simplify and solve the security challenges that come with it. 

The capabilities of App Identity and Access Adapter include the following:

  • Secure your APIs and Web apps with authentication and access control policies.
    • Policies are defined at the application level and remain consistent throughout diverse protocols and runtimes, regardless what language your application is written in.
  • Configurable to work with any OAuth2/OIDC-compliant identity providers, such as IBM Cloud App ID.
    • To further customize your security needs, you can use multiple OIDC providers.
  • Protect your multicloud workloads with zero code changes.
    • The adapter does not require any changes to your code in order to enforce the rules and policies. Once a policy is defined or changed, it will automatically be applied without the need of deploying your services again.

The App Identity and Access Adapter is installed into a cluster where it will enforce the custom policies to control identity and access management into and across the service mesh. The custom access management policies configured within the adapter are linked to a particular Kubernetes service and can be finely tuned to specific service endpoints, which I will demonstrate in the use case. 

App Identity and Access Adapter with a single cloud provider

First, let's start with a simple scenario where the applications are using a single cloud provider (as in Figure 1). Incoming traffic to the applications will be monitored by the access policies and will trigger authentication based on the policies you define. The OIDC workflows are automated with the identity provider you configured with the adapter and tokens are added to the request header. 

Figure 1. A simple scenario based on a single cloud provider.

Figure 1. A simple scenario based on a single cloud provider.

App Identity and Access Adapter in a multicloud scenario

In a more complex example, Figure 2 shows a multicloud scenario with the frontend application on a cloud provider and the backend application on a different cloud provider. The adapter can be used with private and/or public OIDC identity providers as defined by a policy. 

The frontend application that uses Cloud Provider 1 will use the OIDC flow and enforce web app protection policies. In the policies, you can define a specific resource that need protection, where unauthorized users will be detected and then redirected to authenticate. 

On the Cloud Provider 2 side, backend protection policies will automatically retrieve the tokens from the request header and validate according to the JWT Bearer Token spec and reject unauthorized requests. 

Figure 2. A multicloud scenario with separated frontend and backend applications.

Figure 2. A multicloud scenario with separated frontend and backend applications.

Use cases

Configuring your custom policies for resource access and endpoint protection is incredibly easy. This adapter is provided as a Helm chart that can be installed using Helm in your cluster. Then you'll need to define and apply the appropriate rules and policies that are Custom Resource Definitions (CRD) for your application. Voila! No code change or redeployment is necessary in order to secure your multi-cloud applications.  

I'll be using IBM Cloud App ID with the adapter, but you can use any OIDC-compliant identity provider.

What is App ID?

IBM Cloud App ID allows you to easily add authentication and authorization to your applications and APIs that run on IBM Cloud. With the service’s SDKs and APIs, developers can get a sign-in flow working in minutes, enable sign-in, and start building profiles on your app users. With App ID, your app experience can be professional, personalized, and, most importantly, secure.  

App ID is OIDC compliant, which allows it to be used with the App Identity and Access Adapter seamlessly. See the documentation to learn more about using App ID with the App Identity and Access Adapter.

Let's get started and see how our sample app can be secured using the adapter.

Sample app overview

The sample app is a simple Node.js application deployed on a Kubernetes cluster that has frontend resources and APIs we want to protect. There are a set of endpoints that, when reached, we want to execute an authorization grant code flow to obtain an access and identity token from App ID. 

Once authorized and authenticated, we will then display the user’s access token on the main home page. Our app also has APIs that we defined that will be protected by the JWT flow. It will validate token signatures using App ID's public keys. I will show you how to secure your application without any SDKs or app code changes. 

Requirements

You can also use IBM Cloud Kubernetes Service to streamline the installation of Istio as a managed add-on. Learn more about Managed Istio on IBM Cloud.

Install the adapter in your cluster

The App Identity and Access Adapter can apply an authorization and authentication policy to ensure a request can access a resource, whether in your frontend or backend applications. 

Secure the frontend

In our app, we have a protected resource that only authenticated users should have access to. We define the protected url to be https://HOST/web/home and show the ID token after a successful login. We want to secure the frontend with an OIDC authorization flow using App ID.  

Secure the frontend

1. After installing the adapter in your cluster, define an OidcClient CRD in a yaml file with the App ID credentials, like the following:

apiVersion: "security.cloud.ibm.com/v1"
kind: OidcConfig
metadata:
  name: oidc-provider-config
  namespace: sample-app
spec:
  clientId: <AppID-Client-ID>      
  clientSecret: <AppID-Client-Secret>
  discoveryUrl: https://us-south.appid.cloud.ibm.com/oauth/v4/<AppID-Tenant-ID>/.well-known/openid-configuration  

2. Create the OidcClient object:

​​​​​​​​​​​​​​kubectl apply -f path/to/OidcConfigCRD

3. Create a Policy CRD yaml file and add your path with the OIDC config:

apiVersion: security.cloud.ibm.com/v1
kind: Policy
metadata:
  name: sample-oidc-policy
  namespace: sample-app
spec:
  targets:
    -  
      serviceName: <svc-sample-app>
      paths:
        - exact: /web/home
          method: ALL
          policies:
            - policyType: oidc
              config: oidc-provider-config
            

4. Apply the CRD to start enforcing the policies:

kubectl apply -f path/to/PolicyCRD

​​​​​​​​​​Now, if you navigate to https://HOST/web/home, you will be prompted to App ID's authentication page. 

https://HOST/web/home

When the authentication completes, the browser is redirected to an implicit /oidc/callback endpoint where the adapter intercepts the request. At this point, the adapter obtains tokens from the identity provider and then redirects the user back to their originally requested URL, which now shows the access token.

app5

Secure the backend

In the backend app, we want to protect the API https://HOST/api/headers using a JWT flow with App ID. We can define a JwtConfig CRD that contains the public key resource, which is used to validate token signatures.

1. Define an JwtConfig CRD in a yaml file with the App ID public keys endpoint, like the following:

apiVersion: "security.cloud.ibm.com/v1"  
kind: JwtConfig  
metadata:    
  name:      jwt-config    
  namespace: sample-app  
spec:      
  jwksUrl: https://us-south.appid.cloud.ibm.com/oauth/v4/<AppID-Tenant-ID>/publickeys

2. Create the JwtConfig object:

kubectl apply -f path/to/JwtConfigCRD

3. Create a Policy CRD yaml file and add your path with the JWT config:

apiVersion: "security.cloud.ibm.com/v1"
kind: Policy
metadata:
  name:  sample-jwt-policy
  namespace: sample-app
spec:
  targets:
    -
      serviceName: <svc-sample-app>
      paths:
        - exact: /api/headers
          method: GET
          policies:
            - policyType: jwt
              config: jwt-config

4. Apply the CRD to start enforcing the policies:

kubectl apply -f path/to/PolicyCRD

Now if you try to reach https://HOST/api/headers, the JWT flow expects a request to contain an Authorization header with a valid access token and an optional identity token. Unauthenticated clients are returned an HTTP 401 response status with a list of the scopes that are needed to obtain authorization. If the tokens are invalid or expired, the API strategy returns an HTTP 401 response. 

With absolutely zero app code changes or redeployments, we have successfully secured our applications using the App Identity and Access Adapter. The use cases that are shown provide the basic minimum protection to your apps. For additional protection options see our docs

Have questions or feedback?

  • If you have technical questions about App ID, post your question on Stack Overflow and tag your question with ibm-appid.
  • For questions about the service and getting started instructions, use the IBM Developer Answers forum. Include the appid tag.
  • Open a support ticket in the IBM Cloud menu.
  • Reach out directly to the development team on Slack!

To get started with App ID, check it out in the IBM Cloud Catalog.

Be the first to hear about news, product updates, and innovation from IBM Cloud