Skip to main content
Formal streamlines access and control for any Kubernetes clusters by providing:
  • Single Sign-On (SSO) integration with providers like Okta
  • Granular role-based access controls with MFA support
  • Session recording for compliance
  • Automatic cluster discovery (for EKS)

Requirements

To connect to a Kubernetes cluster through the Connector, you need the following:
  • The Formal Desktop App running and authenticated. You can find more information on how to achieve this in the documentation of the Desktop App.
  • At least one Kubernetes cluster linked to the Formal Connector as a Resource, with its Native User set. When creating the Kubernetes Resource, set the Resource name to the cluster name.
The Formal Desktop App will automatically update your local kubeconfig file and change the current context.

Configuration

Resource

When creating a Kubernetes resource, use the Kubernetes API endpoint URL with port 443 as the target port. The resource name must match the target cluster. This is a crucial step specific to Kubernetes resources: the name is used by the Connector to identify which cluster to target when a user tries to access a cluster.

Native Users

When users connect via the Formal Connector, the Connector uses the native credentials of the cluster, which are defined through the Native Users configuration. The Connector supports two kinds of Native Users for Kubernetes:
  1. IAM (standard): The Formal Connector will use the AWS IAM in the current context. For instance, for ECS Fargate, it will be the task role.
  2. Kubeconfig: Formal Connector can use a kubeconfig file. You can set the kubeconfig as an environment variable using KUBECONFIG. If not set, it will default to ~/.kube/config.

AWS IAM permissions

When using IAM (standard) for the Native User, the Connector instance will needs specific AWS IAM permissions to fetch to the upstream Kubernetes cluster configuration:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["eks:DescribeCluster", "sts:GetCallerIdentity"],
      "Resource": "*"
    }
  ]
}
If the Connector is deployed in an AWS EKS cluster, you can grant these permissions by following these steps:
  1. Create an IAM Role for Service Account (IRSA) for the Connector
  2. Create a Kubernetes Service Account (SA) with the IRSA annotation
  3. Create the required AWS IAM policy above
  4. Attach the AWS IAM policy to the IRSA
  5. Attach the SA to the Connector pod with serviceAccountName
These steps are detailed in the AWS documentation and demonstrated in our example Terraform deployment.

Upstream Kubernetes permissions

When connected to the target Kubernetes cluster, the Connector inherits the permissions defined by its token’s username and groups (e.g. the matching ClusterRoleBinding and ClusterRole). If the credentials have limited permissions, the Formal app will show the access logs correctly but users will see errors when attempting kubectl commands. For example:
$ kubectl get pods
E0526 10:28:09.839098   25809 memcache.go:265] couldn't get current server API group list: the server has asked for the
client to provide credentials
E0526 10:28:10.715348   25809 memcache.go:265] couldn't get current server API group list: the server has asked for the
client to provide credentials
E0526 10:28:11.805740   25809 memcache.go:265] couldn't get current server API group list: the server has asked for the
client to provide credentials
E0526 10:28:12.912758   25809 memcache.go:265] couldn't get current server API group list: the server has asked for the
client to provide credentials
E0526 10:28:13.788519   25809 memcache.go:265] couldn't get current server API group list: the server has asked for the
client to provide credentials
error: You must be logged in to the server (the server has asked for the client to provide credentials)
On the opposite, if the permissions are set correctly, users will be able to run kubectl commands according to the Connector identity on the Kubernetes cluster. For example, for a ClusterRole that allows reading pods but not nodes:
$ kubectl get pods
No resources found in default namespace.
$ kubectl get nodes
Error from server (Forbidden): nodes is forbidden: User "arn:aws:sts::123456789012:assumed-role/formal-connector-20240101000000000000000001/1234567890123456789" cannot list resource "nodes" in API group "" at the cluster scope
As seen in the last error of the previous example, the Connector identity is used for all commands. If you want the end user identity to propagate, you should use impersonation (recommended) or synchronize end users and native users.

Upstream Kubernetes permissions (AWS EKS)

If your target Kubernetes resource is an AWS EKS cluster, you need to:
  • Allow your Connector role to connect to the EKS cluster with an Access Entry Configuration
  • Define its permissions with an Access Entry Policy Association thats links your previously created IRSA with the cluster access policy of your choice (e.g. arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy).

Example Terraform deployment

For a complete example of deploying the Formal Connector in an EKS cluster that controls access to that cluster, see our example Terraform configuration. This example demonstrates:
  • Deploying the Formal Connector on AWS EKS
  • Setting up AWS IAM roles and policies for EKS API access
  • Configuring the Kubernetes cluster access permissions for the Connector

Impersonation

User impersonation provides two key benefits:
  1. Improved audit logs: Actions in Kubernetes logs are attributed to individual users rather than showing all Formal-mediated actions under a single service account.
  2. Granular access control: Users’ permissions are restricted to match what they would have when connecting directly to the cluster.
For more information about user impersonation, refer to the Kubernetes documentation.

Kubernetes RBAC

To allow user impersonation, the target Kubernetes cluster must have a specific RBAC configuration and allow the impersonate verb. While the cluster-admin role includes impersonation permissions by default, custom roles require explicit configuration. Here is an example ClusterRole and ClusterRoleBinding that will allow a Connector to impersonate end users:

    Formal policy

    The Connector decides whether to impersonate users based on policies. This policy will grant the calling user access to resources defined by the groups in input.user.groups rather than the access level defined by the native user.
    package formal.v2
    
    import future.keywords.if
    
    session := { "action": "impersonate", "user": input.user.username, "groups": input.user.groups } if {
      input.datastore.technology == "kubernetes"
    }
    
    When using impersonation, the Formal Connector adds two headers to the request made:
    • Impersonate-User: The user specified in the policy under user.
    • Impersonate-Group: The list of groups in the policy under groups. If using RBAC, it should match the groups used in your Kubernetes cluster.

    Policy Evaluation

    Formal supports the following policy evaluation stages for Kubernetes:
    • Session: Evaluate and enforce policies at connection time

    Other policies

    Additionally, you can apply access restrictions to Kubernetes Resources, similar to any other Resource. This includes the implementation of two additional filters, among others:
    1. Kind: allow or block access to a specific Kubernetes Kind (e.g. secret)
    2. Namespace: allow or block access to a specific Kubernetes Namespace

    MFA for kubectl exec

    This policy requires users to use MFA to run kubectl exec.
    package formal.v2
    
    import future.keywords.if
    
    session := { "action": "mfa" } if {
      input.subresource == "exec"
    }
    

    Block kube-* namespaces

    The following policy blocks every query to the Connector for namespaces that start with kube-.
    package formal.v2
    
    import future.keywords.if
    
    session := { "action": "block", "type": "block_with_formal_message" } if {
      startswith(input.namespace, "kube-")
    }
    

    Recordings

    The Formal Connector records every session. The session recordings can be accessed and reviewed in the Session application.