aerial photography of container van lot

Amazon EKS IAM roles and policies with Terraform

Before you use or approve Amazon EKS in production you must have a security checklist. Everyone’s list is different but everyone’s listing must-have items to ensure authentication and authorization are at a minimum; in other words least privilege. Let’s explore Amazon EKS IAM roles and policies written in Terraform!

What are some suggestions to improve your Amazon EKS IAM design?

  • Start with the managed roles and policies, then review AWS CloudTrail logs to see what events or API calls actually occurs
  • Start creating your own managed IAM policies and IAM roles; one at a time
  • If possible require MFA for ensuring the user is who he/she says they are
  • Once you have validated your custom roles and policies then add conditions to your IAM policies; again one condition at a time

Before I show any code it’s important to know basic AWS IAM terminology. Let’s add Identity-based policies Resource-based policies to your vocabulary. Resource-based policies are about “what”. The Identity-based policies are about “who”. Then there’s the “action” that identity or resource can use based on the “effect”. There are dozens of EKS IAM actions available, see the Actions defined by Amazon Elastic Kubernetes Service page.

EKS Cluster Authentication

Just a reminder on how EKS cluster authentication works. Bottomline is that all permissions are essentially managed by Kubernetes Role Based Access Control (RBAC). However AWS IAM is involved.

Prerequisites

Prior to creating your EKS cluster be sure to identify which IAM role or user will be the “primary” identity to create the EKS cluster. The identity that first creates the EKS cluster will be automatically added to K8s system:masters group. Which is great, however you will not be able to visually see that identity in the ConfigMap!

Amazon EKS IAM Resource

The Amazon EKS cluster resource has the following ARN:

arn:${Partition}:eks:${Region}:${Account}:cluster/${ClusterName}

Note: Use a wildcard (“*”) if you really need to specify all clusters. Also you cannot use this resource filter pattern for certain events such as creating a new cluster. How would anyone know that? Well, take a look again at the Actions defined by Amazon Elastic Kubernetes Service page. You’ll notice in the CreateCluster action the Resource box is empty.

eks actions table
I drew a oval shape in the Resource type cell; notice cluster* isn’t listed

IAM policies based on the cluster name

Read/View all clusters: Terraform IAM example

Initially the user or role may not have any EKS permissions, so if you attempt to list all the clusters it would return an error like this.

aws eks list-clusters

An error occurred (AccessDeniedException) when calling the ListClusters operation: User: arn:aws:iam::1234567890:user/read-only-all is not authorized to perform: eks:ListClusters on resource: arn:aws:eks:us-east-2:1234567890:cluster/*

The “eks:ListClusters” Resource must not be restricted so therefore it’s in a different statement than the other actions that do allow the EKS resource ARN. See example below.

{
    "Statement": [
        {
            "Action": [
                "eks:ListUpdates",
                "eks:ListTagsForResource",
                "eks:ListNodegroups",
                "eks:ListIdentityProviderConfigs",
                "eks:ListFargateProfiles",
                "eks:ListAddons",
                "eks:DescribeCluster"
            ],
            "Effect": "Allow",
            "Resource": "arn:aws:eks:us-east-2:1234567890:cluster/*",
            "Sid": "ReadAllEKSclusters"
        },
        {
            "Action": "eks:ListClusters",
            "Effect": "Allow",
            "Resource": "*",
            "Sid": "ListAllEKSclusters"
        }
    ],
    "Version": "2012-10-17"
}

Rerun list cluster action with this newly attached policy.

aws eks list-clusters

# My results
{
    "clusters": [
        "aws001-preprod-dev-eks"
    ]
}

The “Describe cluster” action and few others will require the exact name of the cluster you want to describe and because the ARN is set to all (asterisk in the ARN) that means this policy allows describing all clusters.

aws eks describe-cluster --name aws001-preprod-dev-eks

# result

{
    "cluster": {
        "name": "aws001-preprod-dev-eks",
        "arn": "arn:aws:eks:us-east-2:1234567890:cluster/aws001-preprod-dev-eks",
        "createdAt": "2022-03-22T06:37:57.278000-04:00",
        "version": "1.21",
        "endpoint": "https://abc123456789.gr7.us-east-2.eks.amazonaws.com",
        "roleArn": "arn:aws:iam::1234567890:role/aws001-preprod-dev-eks-cluster-role",
        "resourcesVpcConfig": {
            "subnetIds": [
...
}

Read only specific cluster name

This time simply replace the last asterisk in the resource name with the cluster name, in this case I’m using Terraform so I’m passing the name via a local variable.

actions = [
      "eks:AccessKubernetesApi",
      "eks:DescribeCluster",
      "eks:ListAddons",
      "eks:ListFargateProfiles",
      "eks:ListIdentityProviderConfigs",
      "eks:ListNodegroups",
      "eks:ListTagsForResource",
      "eks:ListUpdates"
    ]

    resources = [
      "arn:aws:eks:${var.region}:${data.aws_caller_identity.current.account_id}:cluster/${local.cluster_name}",
    ]
  }
aws eks list-clusters --profile iam

An error occurred (AccessDeniedException) when calling the ListClusters operation: User: arn:aws:iam::1234567890:user/Waleed is not authorized to perform: eks:ListClusters on resource: arn:aws:eks:us-east-2:1234567890:cluster/*

Now listing all EKS clusters is not allowed.

Modify all clusters

This role should only be assigned to a few EKS/Kubernetes administrators whose highly experienced or certified to manage Kubernetes clusters in the AWS Cloud. Larger organizations may have a single administrator per cluster while others have one administrator to manage multiple.

statement {
    sid = "ModifyAllEKSclusters"

    actions = [
      "eks:AccessKubernetesApi",
      "eks:Associate*",
      "eks:Create*",
      "eks:Delete*",
      "eks:DeregisterCluster",
      "eks:Describe*",
      "eks:List*",
      "eks:RegisterCluster",
      "eks:TagResource",
      "eks:UntagResource",
      "eks:Update*"
    ]

    resources = [
      "*"
    ]
  }

  statement {
    sid = "Deny"

    # No major updates allowed in this example
    actions = [
      "eks:CreateCluster",
      "eks:DeleteCluster"
    ]

    resources = [
      "*"
    ]
  }

Modify a specific cluster

statement {
    sid = "ModifyaEKScluster"

    actions = [
      "eks:AccessKubernetesApi",
      "eks:Associate*",
      "eks:Create*",
      "eks:Delete*",
      "eks:DeregisterCluster",
      "eks:DescribeCluster",
      "eks:DescribeUpdate",
      "eks:List*",
      "eks:TagResource",
      "eks:UntagResource",
      "eks:Update*"
    ]

    resources = [
      "arn:aws:eks:${var.region}:${data.aws_caller_identity.current.account_id}:cluster/${local.cluster_name}",
    ]
  }

  statement {
    sid = "ModifyaEKSclusterResource"

    actions = [
      "eks:DescribeNodegroup",
      "eks:DescribeFargateProfile",
      "eks:DescribeIdentityProviderConfig",
      "eks:DescribeAddon"
    ]

    resources = [
      "arn:aws:eks:${var.region}:${data.aws_caller_identity.current.account_id}:cluster/${local.cluster_name}",
      "arn:aws:eks:${var.region}:${data.aws_caller_identity.current.account_id}:nodegroup/${local.cluster_name}/*/*",
      "arn:aws:eks:${var.region}:${data.aws_caller_identity.current.account_id}:addon/${local.cluster_name}/*/*",
      "arn:aws:eks:${var.region}:${data.aws_caller_identity.current.account_id}:identityproviderconfig/${local.cluster_name}/*/*/*",
      "arn:aws:eks:${var.region}:${data.aws_caller_identity.current.account_id}:fargateprofile/${local.cluster_name}/*/*"
    ]
  }

  # These actions don't use the 'cluster' resource type
  statement {
    sid = "Modify"

    actions = [
      "eks:RegisterCluster",
      "eks:DisassociateIdentityProviderConfig"
    ]

    resources = [
      "*",
    ]
  }

  statement {
    sid = "Deny"

    # No major updates allowed in this example
    actions = [
      "eks:CreateCluster",
      "eks:DeleteCluster"
    ]

    resources = [
      "*"
    ]
  }

For a complete list of Amazon EKS actions see the original documentation.

Amazon EKS resources ARN

Use these various ARN patterns to constrain permissions for certain teams.

TypeARN pattern
clusterarn:aws:eks:${Region}:${Account}:cluster/${ClusterName}
nodegrouparn:aws:eks:${Region}:${Account}:nodegroup/${ClusterName}/${NodegroupName}/${UUID}
addonarn:aws:eks:${Region}:${Account}:addon/${ClusterName}/${AddonName}/${UUID}
fargateprofilearn:aws:eks:${Region}:${Account}:fargateprofile/${ClusterName}/${FargateProfileName}/${UUID}
identityproviderconfigarn:aws:eks:${Region}:${Account}:identityproviderconfig/${ClusterName}/${IdentityProviderType}/${IdentityProviderConfigName}/${UUID}
EKS resource ARN patterns

EKS IAM Condition Keys

In addition to filtering EKS permissions with the resource name(s), you can further filter down by using keys.

KeyTypeDescription
aws:RequestTag/${TagKey}stringUse this to ensure tags are present in the “request”/create call. Basically before creating new resources.
aws:ResourceTag/${TagKey}stringUse this to find resources that have the tags already. It’s for existing resources with tags.
aws:TagKeysArrayOfStringSimilar to aws:RequestTag/${TagKey} but it’s a list of tag keys, instead of just one.
eks:clientIdstringThe “clientId” value in the associateIdentityProviderConfig call
eks:issuerUrlstringThe “issuerUrl” value in the associateIdentityProviderConfig call
{
            "Sid": "TagEKSWithTheseTags",
            "Effect": "Allow",
            "Action": [
                "eks:CreateCluster",
                "eks:TagResource"
            ],
            "Resource": "*",
            "Condition": {
                "StringEqualsIfExists": {
                    "aws:RequestTag/environment": [
                        "development",
                        "sandbox"
                    ],
                    "aws:RequestTag/jobfunction": "DevOps"
                },
                "ForAllValues:StringEquals": {
                    "aws:TagKeys": [
                        "environment",
                        "jobfunction"
                    ]
                }
            }
        }

Other IAM policies

EKS Console Admin policy: This permission will allow full read and write to the Configuration tab on the EKS console. The Resources and the Overview tabs requires Kubernetes RBAC permissions.

data "aws_iam_policy_document" "console_admin" {

  statement {
    sid = "admin"

    actions = [
      "eks:*"
    ]

    resources = [
      "*"
    ]
  }

  statement {
    sid = "console"

    effect = "Allow"
    actions = [
      "iam:PassRole"
    ]

    resources = [
      "*"
    ]

    condition {
      test     = "StringEquals"
      variable = "iam:PassedToService"
      values   = ["eks.amazonaws.com"]
    }
  }
}

Update a Kubernetes cluster version: This policy will only allow to update just the Kubernetes Cluster version. In the Terraform example below, updating the cluster version is only allowed when the EKS cluster has a tag of “environment” and a value of “sandbox”; the EKS cluster that’s in the current account and region of course.

data "aws_iam_policy_document" "cluster_version" {

  statement {
    sid = "admin"

    actions = [
      "eks:UpdateClusterVersion"
    ]

    resources = [
      "arn:aws:eks:${var.region}:${data.aws_caller_identity.current.account_id}:cluster/*"
    ]

    condition {
      test     = "StringEquals"
      variable = "aws:ResourceTag/environment"
      values   = ["sandbox"]
    }
  }
}

EKS Service-linked roles

The following table shows all the service-linked roles that are automatically created when you create the cluster and its components.

ComponentRole nameService URLIAM PolicyIAM Policy ARN
EKS Cluster roleAWSServiceRoleForAmazonEKSeks.amazonaws.comAmazonEKSServiceRolePolicyarn:aws:iam::aws:policy/aws-service-role/AmazonEKSServiceRolePolicy
EKS node groupsAWSServiceRoleForAmazonEKSNodegroupeks-nodegroup.amazonaws.comAWSServiceRoleForAmazonEKSNodegrouparn:aws:iam::aws:policy/aws-service-role/AWSServiceRoleForAmazonEKSNodegroup
EKS Fargate profilesAWSServiceRoleForAmazonEKSForFargateeks-fargate.amazonaws.comAmazonEKSForFargateServiceRolePolicyarn:aws:iam::aws:policy/aws-service-role/AmazonEKSForFargateServiceRolePolicy
EKS ConnectorAWSServiceRoleForAmazonEKSConnectoreks-connector.amazonaws.comAmazonEKSConnectorServiceRolePolicyarn:aws:iam::aws:policy/aws-service-role/AmazonEKSConnectorServiceRolePolicy
EKS service-linked roles

EKS IAM Roles

Amazon EKS Cluster Role

The AmazonEKSClusterPolicy is required to be attached to your EKS Cluster role before you create your cluster.

resource "aws_iam_role" "eks_cluster_role" {
  name = "eks-cluster-role"
  tags = local.required_tags

  assume_role_policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "eks.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
POLICY
}

resource "aws_iam_role_policy_attachment" "eks_cluster_role" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy"
  role       = aws_iam_role.eks_cluster_role.name
}

Amazon EKS node IAM role

Each node (EC2 instance for example) uses IAM roles to make AWS API calls. Before you can create and register nodes to the EKS cluster they must have an IAM role with the following policies attached AmazonEKSWorkerNodePolicy and AmazonEC2ContainerRegistryReadOnly. We’ll add the AmazonEKS_CNI_Policy later.

locals {
  eks_node_policies = ["AmazonEC2ContainerRegistryReadOnly", "AmazonEKSWorkerNodePolicy"]
}

resource "aws_iam_role" "eks_node_role" {
  name = "eks-node-role"
  tags = local.required_tags

  assume_role_policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
POLICY
}

resource "aws_iam_role_policy_attachment" "eks_node_role" {
  for_each = toset(local.eks_node_policies)

  policy_arn = "arn:aws:iam::aws:policy/${each.value}"
  role       = aws_iam_role.eks_node_role.name
}

Amazon EKS CNI Policy

You can attach the AmazonEKS_CNI_Policy to the node role above. However, you must follow least privilege model to protect your nodes as much as possible. We’ll need to create the IAM roles for Kubernetes service account or IRSA. There are multiple steps to create it, but we’re in luck because there’s an IRSA Terraform module built by the AWS Open Source community! if you’re using IPV4. There’s an another IRSA Terraform module maintained by the community.

locals {
  addon_context = {
    aws_caller_identity_account_id = data.aws_caller_identity.current.account_id
    aws_caller_identity_arn        = data.aws_caller_identity.current.arn
    aws_eks_cluster_endpoint       = data.aws_eks_cluster.eks_cluster.endpoint
    aws_partition_id               = data.aws_partition.current.partition
    aws_region_name                = data.aws_region.current.name
    eks_oidc_issuer_url            = local.eks_oidc_issuer_url
    eks_cluster_id                 = aws_eks_cluster.this.id
    eks_oidc_provider_arn          = "arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/${local.eks_oidc_issuer_url}"
    tags                           = local.required_tags
  }
}

module "vpc_cni_irsa" {
  source = "git@github.com:aws-ia/terraform-aws-eks-blueprints.git//modules/irsa?ref=v4.2.1"

  kubernetes_namespace              = "kube-system"
  kubernetes_service_account        = "aws-node"
  create_kubernetes_namespace       = false
  create_kubernetes_service_account = false
  irsa_iam_policies                 = ["arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"]
  addon_context                     = local.addon_context
}
ComponentService URLIAM PolicyIAM Policy ARN
EKS Cluster roleeks.amazonaws.comAmazonEKSClusterPolicyarn:aws:iam::aws:policy/AmazonEKSClusterPolicy
EKS node roleec2.amazonaws.comAmazonEKSWorkerNodePolicy

AmazonEC2ContainerRegistryReadOnly
arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
EKS Fargate profileseks-fargate.amazonaws.comAmazonEKSForFargateServiceRolePolicyarn:aws:iam::aws:policy/aws-service-role/AmazonEKSForFargateServiceRolePolicy
EKS Connectoreks-connector.amazonaws.comAmazonEKSConnectorServiceRolePolicyarn:aws:iam::aws:policy/aws-service-role/AmazonEKSConnectorServiceRolePolicy
EKS service-linked roles

EKS Fargate profiles

We cannot use the node IAM role for the EKS Farget profiles, we have to create a pod execution IAM role. Kubernetes Role based access control (RBAC) will use this pod execution IAM role for authorization to AWS services, for example to pull an image from Amazon Elastic Container Registry (ECR). The code below creates the Amazon EKS pod execution IAM role with the required policy and trust settings.

resource "aws_iam_role" "eks_pod_exe_role" {
  name = "eks-fargate-pod-execution-role"
  tags = local.required_tags

  assume_role_policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "eks-fargate-pods.amazonaws.com"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
         "ArnLike": {
            "aws:SourceArn": "arn:aws:eks:${var.region}:${data.aws_caller_identity.current.account_id}:fargateprofile/${local.cluster_name}/*"
         }
      }
    }
  ]
}
POLICY
}

resource "aws_iam_role_policy_attachment" "eks_pod_exe_role" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonEKSFargatePodExecutionRolePolicy"
  role       = aws_iam_role.eks_pod_exe_role.name
}

EKS Connector

This is a read or view only feature to see your Kubernetes clusters from your other cloud providers or on-premises or running on your EC2s. This also needs a different IAM role.

# #########################################
# EKS Connector
# #########################################

data "aws_iam_policy_document" "connector" {

  statement {
    sid = "SsmControlChannel"

    actions = [
      "ssmmessages:CreateControlChannel"
    ]

    resources = [
      "arn:aws:eks:*:*:cluster/*"
    ]
  }

  statement {
    sid = "ssmDataplaneOperations"

    actions = [
      "ssmmessages:CreateDataChannel",
      "ssmmessages:OpenDataChannel",
      "ssmmessages:OpenControlChannel"
    ]

    resources = ["*"]
  }
}

resource "aws_iam_policy" "connector" {
  name   = "eks-connector"
  path   = "/"
  policy = data.aws_iam_policy_document.connector.json

  tags = {
    "Name" = "eks-connector"
  }
}
resource "aws_iam_role" "eks_connector_role" {
  name = "eks-connector-role"
  tags = local.required_tags

  assume_role_policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ssm.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
POLICY
}

resource "aws_iam_role_policy_attachment" "eks_connector_role" {
  policy_arn = aws_iam_policy.connector.arn
  role       = aws_iam_role.eks_connector_role.name
}

To learn more about AWS managed policies, see https://docs.aws.amazon.com/eks/latest/userguide/security-iam-awsmanpol.html

To see all the code in Terraform, visit the GitHub repo.

If you don’t know how Terraform works, then jump to the Intro to Terraform guide first.

AWS KMS Customer Managed CMK with Terraform

AWS Key Management Service (KMS) is a AWS managed service that allows us to create, manage, and delete customer master keys (CMK) or simply use AWS customer managed keys for encrypting our data in the AWS cloud. From my experience with passing both the AWS Certified Security – Speciality and the AWS Certified Solutions Architect – Professional, AWS Key Management Service is a must to learn inside and out. If you can understand all of the KMS features you’ll have a better chance of passing those two exams. Let’s learn how to create and manage AWS KMS customer managed CMK with Terraform! I will also be using Terragrunt so we can follow the DRY (Don’t repeat yourself) model.

This is part two of AWS Key management service (KMS) – Part 1.

KMS key types
KMS Key types

Key properties

  • Key Id
  • Creation date
  • Description
  • Key state

Customer managed keys (CMK)

Given the name you can guess the differences right away, right? For starter, you as the customer will have to explicitly create the key with AWS CLI, AWS API, or Terraform or any other available methods. You can set the CMK policies to allow services or users to use the key. The key policy can pass the permission responsibilities to be managed by IAM policies instead of the KMS CMK key policies. A CMK can be set to enable or disable at any time to allow usage or stop the usage of the key.

Key Alias are a great way to tag and identify Customer managed CMKs. This will help the user quickly find the desired key in the AWS console or AWS CLI query results. Since rotation is possible and it can be enabled to automatically rotate on yearly basis. Key aliases helps with that by re-assigning the alias to the new key.

You can definitely delete the key, but you must be damn sure that no one or any data or services is using that key! Once the key is gone you cannot unencrypt the data that was encrypted with the deleted key! So in order to semi control this chaos AWS has enforced a scheduling delete functionality rather than immediate delete. The customer can delete any Customer managed key by scheduling a delete; the minimum number of days to schedule a delete is 7 days. Best practice is to set this for a month or more.

NOTE: Before proceeding

  • If you haven’t installed Terraform or Terragrunt then you must follow this guide
  • For Terraform deep dive explanation follow this guide

Pricing

Each customer master key (CMK) that you create in AWS Key Management Service (KMS) costs $1/month until you delete it. For the N. VA region:

  • $0.03 per 10,000 requests
  • $0.03 per 10,000 requests involving RSA 2048 keys
  • $0.10 per 10,000 ECC GenerateDataKeyPair requests
  • $0.15 per 10,000 asymmetric requests except RSA 2048
  • $12.00 per 10,000 RSA GenerateDataKeyPair requests

Learn more at https://aws.amazon.com/kms/pricing/

Create and edit KMS Keys

Terraform module: main.tf

# Creates/manages KMS CMK
resource "aws_kms_key" "this" {
  description              = var.description
  customer_master_key_spec = var.key_spec
  is_enabled               = var.enabled
  enable_key_rotation      = var.rotation_enabled
  tags                     = var.tags
  policy                   = var.policy
  deletion_window_in_days  = 30
}

# Add an alias to the key
resource "aws_kms_alias" "this" {
  name          = "alias/${var.alias}"
  target_key_id = aws_kms_key.this.key_id
}

Terraform module: vars.tf

variable description {}

# Options available
# SYMMETRIC_DEFAULT, RSA_2048, RSA_3072,
# RSA_4096, ECC_NIST_P256, ECC_NIST_P384,
# ECC_NIST_P521, or ECC_SECG_P256K1
variable key_spec {
  default = "SYMMETRIC_DEFAULT"
}

variable enabled {
  default = true
}

variable rotation_enabled {
  default = true
}

variable tags {}

variable alias {}

variable policy {}

Terragrunt KMS directory structure

terragrunt directory structure

Terraform plan

main.tf 

locals {
  admin_username = "waleed"
  account_id     = data.aws_caller_identity.current.account_id
}

provider "aws" {
  region  = var.aws_region
  profile = var.aws_cli_profile
}

terraform {
  backend "s3" {}
}

data "aws_caller_identity" "current" {}

data "aws_iam_policy_document" "ssm_key" {
  statement {
    sid       = "Enable IAM User Permissions"
    effect    = "Allow"
    actions   = ["kms:*"]
    resources = ["*"]

    principals {
      type        = "AWS"
      identifiers = ["arn:aws:iam::${local.account_id}:root"]
    }
  }

  statement {
    sid       = "Allow access for Key Administrators"
    effect    = "Allow"
    actions   = ["kms:*"]
    resources = ["*"]

    principals {
      type = "AWS"
      identifiers = [
        "arn:aws:iam::${local.account_id}:user/${local.admin_username}",
        "arn:aws:iam::${local.account_id}:role/aws-service-role/support.amazonaws.com/AWSServiceRoleForSupport",
        "arn:aws:iam::${local.account_id}:role/aws-service-role/trustedadvisor.amazonaws.com/AWSServiceRoleForTrustedAdvisor"
      ]
    }
  }

  statement {
    sid    = "Allow use of the key"
    effect = "Allow"
    actions = [
      "kms:Encrypt",
      "kms:Decrypt",
      "kms:ReEncrypt*",
      "kms:GenerateDataKey*",
      "kms:DescribeKey"
    ]
    resources = ["*"]

    principals {
      type = "AWS"
      identifiers = [
        "arn:aws:iam::${local.account_id}:user/${local.admin_username}",
        "arn:aws:iam::${local.account_id}:role/aws-service-role/support.amazonaws.com/AWSServiceRoleForSupport",
        "arn:aws:iam::${local.account_id}:role/aws-service-role/trustedadvisor.amazonaws.com/AWSServiceRoleForTrustedAdvisor"
      ]
    }
  }

  statement {
    sid    = "Allow attachment of persistent resources"
    effect = "Allow"
    actions = [
      "kms:CreateGrant",
      "kms:ListGrants",
      "kms:RevokeGrant"
    ]
    resources = ["*"]

    principals {
      type = "AWS"
      identifiers = [
        "arn:aws:iam::${local.account_id}:user/${local.admin_username}",
        "arn:aws:iam::${local.account_id}:role/aws-service-role/support.amazonaws.com/AWSServiceRoleForSupport",
        "arn:aws:iam::${local.account_id}:role/aws-service-role/trustedadvisor.amazonaws.com/AWSServiceRoleForTrustedAdvisor"
      ]
    }

    condition {
      test     = "Bool"
      variable = "kms:GrantIsForAWSResource"
      values   = ["true"]
    }
  }
}

ssm-key.tf : It’s best practice to have each type of key in its own terraform file. In this example this key is for SSM. You can create keys for any services possible in your region!

Notice the source is only pulling the ‘dev’ branch. Once tested and verified the source branch would change in each environment.

module "ssm" {
  source = "git@github.com:S-Waleed/terraform-aws-kms-module.git"

  description = "KMS key for System Manager"
  alias       = "ssm"
  policy      = data.aws_iam_policy_document.ssm_key.json

  tags = {
    Name  = "ssm"
    Owner = "wsarwari"
  }
}
# module.ssm.aws_kms_alias.this will be created
  + resource "aws_kms_alias" "this" {
      + arn            = (known after apply)
      + id             = (known after apply)
      + name           = "alias/ssm"
      + target_key_arn = (known after apply)
      + target_key_id  = (known after apply)
    }

# module.ssm.aws_kms_key.this will be created
  + resource "aws_kms_key" "this" {
      + arn                      = (known after apply)
      + customer_master_key_spec = "SYMMETRIC_DEFAULT"
      + deletion_window_in_days  = 30
      + description              = "KMS key for System Manager"
      + enable_key_rotation      = true
      + id                       = (known after apply)
      + is_enabled               = true
      + key_id                   = (known after apply)
      + key_usage                = "ENCRYPT_DECRYPT"
      + policy                   = jsonencode(
            {
              + Statement = [
                  + {
                      + Action    = "kms:*"
                      + Effect    = "Allow"
                      + Principal = {
                          + AWS = "arn:aws:iam::111122223334:root"
                        }
                      + Resource  = "*"
                      + Sid       = "Enable IAM User Permissions"
                    },
                  + {
                      + Action    = "kms:*"
                      + Effect    = "Allow"
                      + Principal = {
                          + AWS = [
                              + "arn:aws:iam::111122223334:user/waleed",
                              + "arn:aws:iam::111122223334:role/aws-service-role/trustedadvisor.amazonaws.com/AWSServiceRoleForTrustedAdvisor",
                              + "arn:aws:iam::111122223334:role/aws-service-role/support.amazonaws.com/AWSServiceRoleForSupport",
                            ]
                        }
                      + Resource  = "*"
                      + Sid       = "Allow access for Key Administrators"
                    },
                  + {
                      + Action    = [
                          + "kms:ReEncrypt*",
                          + "kms:GenerateDataKey*",
                          + "kms:Encrypt",
                          + "kms:DescribeKey",
                          + "kms:Decrypt",
                        ]
                      + Effect    = "Allow"
                      + Principal = {
                          + AWS = [
                              + "arn:aws:iam::111122223334:user/waleed",
                              + "arn:aws:iam::111122223334:role/aws-service-role/trustedadvisor.amazonaws.com/AWSServiceRoleForTrustedAdvisor",
                              + "arn:aws:iam::111122223334:role/aws-service-role/support.amazonaws.com/AWSServiceRoleForSupport",
                            ]
                        }
                      + Resource  = "*"
                      + Sid       = "Allow use of the key"
                    },
                  + {
                      + Action    = [
                          + "kms:RevokeGrant",
                          + "kms:ListGrants",
                          + "kms:CreateGrant",
                        ]
                      + Condition = {
                          + Bool = {
                              + kms:GrantIsForAWSResource = "true"
                            }
                        }
                      + Effect    = "Allow"
                      + Principal = {
                          + AWS = [
                              + "arn:aws:iam::111122223334:user/waleed",
                              + "arn:aws:iam::111122223334:role/aws-service-role/trustedadvisor.amazonaws.com/AWSServiceRoleForTrustedAdvisor",
                              + "arn:aws:iam::111122223334:role/aws-service-role/support.amazonaws.com/AWSServiceRoleForSupport",
                            ]
                        }
                      + Resource  = "*"
                      + Sid       = "Allow attachment of persistent resources"
                    },
                ]
              + Version   = "2012-10-17"
            }
        )
      + tags                     = {
          + "Name"  = "ssm"
          + "Owner" = "wsarwari"
        }
    }

Plan: 2 to add, 0 to change, 0 to destroy.

After applying the code you should see the key in Key Management Service.

kms key created

Enable and disable CMKs

Simple, just update the “enabled” variable to false! Plan and apply.

module "ssm" {
  source = "git@github.com:S-Waleed/terraform-aws-kms-module.git"

  description = "KMS key for System Manager"
  alias       = "ssm"
  ...
  enabled     = false

  ....
}

Automatic rotation

Another easy change with Terraform! By default I have set it to rotate the key in the custom module I created above. Now if you want to disable automatic rotation then all you have to do is set the variable rotation_enabled to false.

module "ssm" {
  source = "git@github.com:S-Waleed/terraform-aws-kms-module.git"

  description = "KMS key for System Manager"
  alias       = "ssm"
  ...
  rotation_enabled     = false

  ....
}

Key Alias

Keys are identified by randomly generated GUIDs. It’s best to create in alias for each key so it can be easily identified by everyone. Aliases are also easy to create and update in Terraform. In the custom module above I have added the ability to create the key alias right after the key is provisioned. The key alias is also passed as a variable as shown below.

module "ssm" {
  source = "git@github.com:S-Waleed/terraform-aws-kms-module.git"

  description = "KMS key for System Manager"
  alias       = "ssm"
  ...
}
Type of CMKCan view CMK metadataCan manage CMKUsed only for my AWS accountAutomatic rotation
Customer managed CMKYesYesYesOptional. Every 365 days (1 year).
AWS managed CMKYesNoYesRequired. Every 1095 days (3 years).
AWS owned CMKNoNoNoVaries
This chart is from the AWS documentation

Key Policy

Each key must have a policy that allows or denies the key to be used by users or services. In the design that I have created allows you to give each key a policy. Let’s breakdown each statement of the key. 

Enable IAM User Permissions

statement {
    sid       = "Enable IAM User Permissions"
    effect    = "Allow"
    actions   = ["kms:*"]
    resources = ["*"]

    principals {
      type        = "AWS"
      identifiers = ["arn:aws:iam::${local.account_id}:root"]
    }
  }

This statement allows all users and services in this account to execute all KMS actions on this key. It’s best practice to follow up on this open statement by creating an IAM policy to restrict the key usage. The identifiers in the principles section can be other AWS accounts.

Allow access for Key Administrators

statement {
    sid       = "Allow access for Key Administrators"
    effect    = "Allow"
    actions   = ["kms:*"]
    resources = ["*"]

    principals {
      type = "AWS"
      identifiers = [
        "arn:aws:iam::${local.account_id}:user/${local.admin_username}",
        "arn:aws:iam::${local.account_id}:role/aws-service-role/support.amazonaws.com/AWSServiceRoleForSupport",
        "arn:aws:iam::${local.account_id}:role/aws-service-role/trustedadvisor.amazonaws.com/AWSServiceRoleForTrustedAdvisor"
      ]
    }
  }

This statement allows selected individual users and IAM roles to fully manage this key. This statement must exist for every single key. Without this statement you will absolutely lose access and management of this key.

Allow use of the key

statement {
    sid    = "Allow use of the key"
    effect = "Allow"
    actions = [
      "kms:Encrypt",
      "kms:Decrypt",
      "kms:ReEncrypt*",
      "kms:GenerateDataKey*",
      "kms:DescribeKey"
    ]
    resources = ["*"]

    principals {
      type = "AWS"
      identifiers = [
        "arn:aws:iam::${local.account_id}:user/${local.admin_username}",
        "arn:aws:iam::${local.account_id}:role/aws-service-role/support.amazonaws.com/AWSServiceRoleForSupport",
        "arn:aws:iam::${local.account_id}:role/aws-service-role/trustedadvisor.amazonaws.com/AWSServiceRoleForTrustedAdvisor"
      ]
    }
  }

This statement is specifically for the usage of the key. If you do not provide the statement Enable IAM User Permissions then you must include this statement; Otherwise this key will not be usable by anyone besides the key administrators.

Allow attachment of persistent resources

statement {
    sid    = "Allow attachment of persistent resources"
    effect = "Allow"
    actions = [
      "kms:CreateGrant",
      "kms:ListGrants",
      "kms:RevokeGrant"
    ]
    resources = ["*"]

    principals {
      type = "AWS"
      identifiers = [
        "arn:aws:iam::${local.account_id}:user/${local.admin_username}",
        "arn:aws:iam::${local.account_id}:role/aws-service-role/support.amazonaws.com/AWSServiceRoleForSupport",
        "arn:aws:iam::${local.account_id}:role/aws-service-role/trustedadvisor.amazonaws.com/AWSServiceRoleForTrustedAdvisor"
      ]
    }

    condition {
      test     = "Bool"
      variable = "kms:GrantIsForAWSResource"
      values   = ["true"]
    }
  }

This statement allows listing, creating, and revoking grants for the key by the principals identified in the statement.

Deleting the key

Do not delete production keys! You must be 100% sure no service or data has ever used the key you are able to delete. Once a key is deleted it’s not possible to restore! You cannot decrypt data that was encrypted with the key that you delete. In Terraform you can delete the key by deleting the code or commenting out the code, then Terraform plan and apply. Or if you want to keep the code and just want to remove the resource from AWS then execute Terragrunt destroy. This destroy will also release the alias so it can be reused. 

kms key pending deletion
Key deletion


Status is now pending deletion. It will delete this key 30 days from the day of the destroy. 

Stop KMS key deletion

If you decide to not delete it then on the AWS console you can select the key then click on Key actions. Finally select Cancel key deletion. This option is only available before the deletion date. 

Cancel key deletion
Key actions

Read more about deleting KMS customer managed keys.

KMS Multi-Region Keys

Take a look at Terraform AWS KMS Multi-Region Keys.

As always if you see any errors, mistakes, have suggestions or questions please comment below. Don’t forget to like, share, and subscribe below for more! 

AWS managed CMKs

I talked about AWS managed customer master keys in my previous post here.

Complete Code

Get the complete module from https://github.com/masterwali/terraform-aws-kms-module.

AWS Key management service (KMS) – Part 1

AWS Key Management Service (AWS KMS) is a AWS service that allows us to encrypt and decrypt our data with a Customer Master Key (CMKs) in the AWS cloud. As as result of passing both the AWS Certified Security – Speciality and the AWS Certified Solutions Architect – Professional exams I feel that it’s critical that you know AWS KMS inside and out. If you can understand all of the AWS KMS features I believe you’ll have a better chance of passing. This deep dive (with infographic) will be several different posts about AWS Key Management Service (AWS KMS).

AWS Key Management Service (AWS KMS) one of the most critical service to secure your AWS account and all of its data. KMS provides security that has to be put in place before new services and data come in. A lot of other AWS services will need KMS keys so it is best practice to set it up before starting other services like EC2, S3, Cloudtrail, Lambda, and so on.

Intro to CMKs

There are different types of AWS Key Management Service (AWS KMS) keys to allow us to encrypt and decrypt our data with a Customer Master Key (CMKs). AWS managed CMK and customer managed CMK. AWS KMS can support both symmetric or asymmetric keys. By default none of these key types are created when the account is created. Each AWS managed key is automatically created when you select to encrypt a certain service. The CMK type must be created by you before it’s available for use. Lets deep dive into each of these types and the Terraform code to creating them.

KMS key types
KMS Key types

CMK properties

  • Key Id
  • Creation date
  • Description
  • Key state

Symmetric CMK

A symmetric key is a 256-bit encryption key that never leaves AWS KMS unencrypted. It’s a single key that’s used for both encryption and decryption. More about symmetric keys here. When you create a KMS key this the default key type. This type of key requires valid AWS credentials to use. This means if the users requires encryption without AWS credentials then it’s an asymmetric key type. Symmetric keys are the better options for most cases. This key type cannot be signed or verified.

Asymmetric CMK

Asymmetric CMK key type is a RSA key pair (public key & a private key) that is used for encryption and decryption or signing and verification (but not both), or an elliptic curve (ECC) key pair that is used for signing and verification. The private key never leaves AWS KMS unencrypted. You can use the public key within AWS KMS by calling the AWS KMS API operations, or download the public key and use it outside of AWS KMS.

symmetric key vs asymmetric keys
Symmetric Key vs. Asymmetric Keys

AWS managed CMKs

AWS managed CMK’s are created, managed, and used on the behalf of the customer by an AWS service. A few AWS services only accept this type. As a customer you can view the keys, their policies, and track their usage via Cloudtrail. The customer cannot managed them, rotate them or modify their key policies.

AWS Key Management Service (AWS KMS)

AWS Managed keys – Pricing

There is no cost to the customer for the creation and storage of the AWS Managed CMKs. Under the free tier 20,000 requests/month calculated across all regions have no cost.

Here’s KMS pricing breakdown for the Northern Virginia region. The price does vary by region, use the pricing calculator for latest numbers.

  • $0.03 per 10,000 requests
  • $0.03 per 10,000 requests involving RSA 2048 keys
  • $0.10 per 10,000 ECC GenerateDataKeyPair requests
  • $0.15 per 10,000 asymmetric requests except RSA 2048
  • $12.00 per 10,000 RSA GenerateDataKeyPair requests

Note: Customer managed CMKs do have a cost, that’s another post. Subscribe to get notified when that’s released.

AWS MANAGED KEYS – Aliases

AWS managed CMKs have aliases already and cannot be modified. The name pattern is aws/service-name; aws/s3 or aws/ssm, etc.

KeyManager field will either be “AWS” or “Customer” to know if a key is managed by AWS or by you.

AWS MANAGED KEYS – Create

Remember the customer cannot create the AWS Managed CMKs directly. This type of a key will be available to you when you encrypt an object or resource.

By default the customer has no keys. Let’s run this command to see the list of keys.

aws kms list-keys --profile { profile_name }

{
    "Keys": []
}

Note: If you haven’t setup your AWS CLI be sure to visit this page “Setup infrastructure as code environment“. You’ll need the following IAM policy in order to list the keys.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "ReadKMSkeys",
            "Effect": "Allow",
            "Action": [
                "kms:ListKeys",
                "kms:ListKeyPolicies",
                "kms:ListRetirableGrants",
                "kms:ListAliases",
                "kms:GetKeyPolicy",
                "kms:DescribeKey"
            ],
            "Resource": "*"
        }
    ]
}
AWS Key Management Service (AWS KMS)
By default no AWS managed keys exists

Let’s say you want to encrypt an object or a bucket in S3. You will see aws/s3 key available all of sudden. Hint this is an AWS managed CMK because of the format!

AWS Key Management Service (AWS KMS)
Encrypting S3 object with AWS managed CMK

Now if we list the keys you will see the AWS managed CMK.

aws kms list-keys --profile dev

{
    "Keys": [
        {
            "KeyArn": "arn:aws:kms:us-east-1:111111111:key/2323232-2323-2424-23424a-a2324a3", 
            "KeyId": "2323232-2323-2424-23424a-a2324a3"
        }
    ]
}

Key Type: Symmetric
Origin: AWS_KMS
Key Spec: SYMMETRIC_DEFAULT
Key Usage: Encrypt and decrypt

Here’s the default and non-editable key policy for the CMK I just created above. Each AWS managed CMK policy is restricted to a single AWS service.

{
    "Version": "2012-10-17",
    "Id": "auto-s3-2",
    "Statement": [
        {
            "Sid": "Allow access through S3 for all principals in the account that are authorized to use S3",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::1111111111:root"
            },
            "Action": [
                "kms:Encrypt",
                "kms:Decrypt",
                "kms:ReEncrypt*",
                "kms:GenerateDataKey*",
                "kms:DescribeKey"
            ],
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "kms:CallerAccount": "1111111111",
                    "kms:ViaService": "s3.us-east-1.amazonaws.com"
                }
            }
        },
        {
            "Sid": "Allow direct access to key metadata to the account",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::1111111111:root"
            },
            "Action": [
                "kms:Describe*",
                "kms:Get*",
                "kms:List*"
            ],
            "Resource": "*"
        }
    ]
}

Note: The “Principal” with a value of “AWS” with the account number and “root” means that any authenticated user or service in that account can use this key.

AWS MANAGED CMKS – Enable and disable keys

Not possible with AWS managed CMKs.

AWS MANAGED CMKS – automatic rotation

Automatic rotation is enabled to rotate the key every 3 years for AWS Managed CMKs. You cannot modify this setting.

AWS MANAGED CMKS – Delete Keys

Not possible with AWS managed CMKs.

Quotas and Limits

Because the cloud services are shared with hundreds of thousands of customers, AWS has put some limits on requests and resources to ensure acceptable performance for all customers.

Resource limits

Grants for a given principal per CMK: 500

Key policy document size: 32 KB

Request limits

If you see this error

You have exceeded the rate at which you may call KMS. Reduce the frequency of your calls.
(Service: AWSKMS; Status Code: 400; Error Code: ThrottlingException; Request ID:

This “ThrottlingException” means you had a valid request but have passed the quota. AWS purposefully throttles your request. Use the Service Quotas console or the RequestServiceQuotaIncrease operation to request an increase.

Request quotas applies to both AWS managed and Customer managed CMKs but not AWS owned CMKs that are designed to protect your resources.

Also requests such as updating the CMK’s alias or setting it to disable have limits. If you aren’t doing large quantity changes you shouldn’t have to worry about hitting these limits. Here’s a few default limits, for a full list click here.

UpdateAlias5/second
DisableKey5/second
ListKeys100/second
Request quotas

KMS Security

  • Dedicated hardened hardware security modules (HSMs)
  • HSMs are physical devices that do not have a virtualization layer
  • Key usage is isolated within an AWS Region.
  • Multiple Amazon employees with role-specific access are required to perform administrative actions on the HSMs. There is no mechanism to export plaintext CMKs.
  • Approved for the these compliance programs; SOC 1,2,3, FedRamp, DoD Impact Level 2-6, HIPPA BAA, C5, and much more.
  • Available in the U.S GovCloud and U.S Secret regions
  • All symmetric key encrypt commands used within HSMs use the Advanced Encryption Standards (AES) 256
  • AWS KMS uses envelope encryption internally to secure confidential material between service endpoints
  • KMS does not store the data, just the keys
  • Use VPC EndPoints to avoid KMS traffic going through the internet

Encryption at Rest

  • Customer master keys (CMKs) are stored in FIPS 140-2 Level 2–compliant hardware security modules (HSMs).
  • The key material for CMKs and the encryption keys that protect the key material never leave the HSMs in plaintext form.

Encryption in Transit

  • The key material that AWS KMS generates for CMKs is never exported or transmitted in AWS KMS API operations
  • All AWS KMS API calls must be signed and be transmitted using minimum Transport Layer Security (TLS) 1.2
  • Also Calls to AWS KMS require a modern cipher suite that supports perfect forward secrecy

KMS Multi-Region Keys

This feature was publicly released in June, 2021. As the name of the feature says, this type of key is created into two different regions. The Primary region is the region where the key is initially created, then the Replicated region is the other region where the same exact key is copied to. The Key Id is the same in both regions with a Multi-Region key. Its KMS key policy, alias, and other settings can be different or the same, it’s up to you. The benefit is in the cost saving, you are only charged for one key even though the key is in two different regions.

Wrapping up KMS for now; but in the future I’ll cover AWS KMS monitoring in detail, how and what can KMS integrate with other services, cross account KMS permissions, customer managed CMKs, and much more deep dives coming soon so be sure to subscribe! Here’s part two: https://cloudly.engineer/2020/aws-kms-customer-managed-cmk-with-terraform/aws/

As always if you see any errors, mistakes, have suggestions or questions please comment below. Don’t forget to like, share, and subscribe for more!

How to pass the AWS Certified Security – Speciality

I recently took the AWS Certified Security – Speciality exam for the first time and I passed it with over 800 points! L Scale is from 100 to 1,000 and 750 is the passing mark. More about the exam here. et’s go over how to pass the AWS certified Security – Speciality certification.

Take these certifications first

Although AWS has lifted the requirements to have an associate level exam before taking a speciality exam; I on the other hand still believe having an associate level certification and understanding is very much needed to pass. In my personal experience I always advise taking the AWS SysOps Administrator over the Solutions Architect if you’re planning on taking this security speciality. Knowing the capabilities of many AWS services will help you cross out the wrong answers immediately. The AWS security training does not cover in depth capabilities.

  • Pass the AWS Cloud Practitioner
  • Pass the AWS SysOps Administrator

Experience

Nothing beats real experience. Or at least LinuxAcademy.com labs. I have been in the AWS world for more than 4 years now.

Security topics

I believe the same exam is always different for different editions and users. This is all based on my experience.

  • KMS: Know this inside and out!
    • All of its capabilities
    • Key policies/permissions/cross account
    • Usage with S3 in depth
    • Usage with all other top services like EBS, RDS, DynamoDB, etc.
    • Be able to troubleshoot issues related to permissions and missing keys, etc.
  • IAM: This is one obvious and must
    • Be able to read and write IAM policies, S3 bucket policies, KMS policies, etc.
    • Master roles for services, for role switching (how to secure it)
    • Cross account setup
  • S3
    • Replication
    • KMS integration
    • KMS cross account integration
    • Troubleshoot permission issues

This is just my top list, always use the guide and study based on that. So don’t ignore other topics.

Training tools / White papers

Exam guide

Click here

Subscribe for future updates and AWS tutorials

As always if you see any errors, mistakes, have suggestions or questions please comment below. Don’t forget to like, share, and subscribe for more! 

Your Google API’s are open to the public by default! Let’s secure them.

How is it Google API’s open to the public?

There are a lot of reasons why you might be using Google API’s. They may be for Google Maps, an iOS or Android app, or just web application. Regardless what you’re using your Google API’s for by default (if it’s autogenerated) they are unrestricted meaning if anyone sees the API keys, they can simply copy and paste in their own project and start reading and writing to! How crazy is that?! The web API JavaScript’s are extremely easy to view with the developer console of the browser. Here’s what mine looks like (with fake data of course!).

// Your web app's Firebase configuration
var firebaseConfig = {
apiKey: "adfaeafaffee-eeeeeaaa333",
authDomain: "appName-1111.firebaseapp.com",
databaseURL: "https://appName-1111.firebaseio.com",
projectId: "appName-1111",
storageBucket: "appName-1111.appspot.com",
messagingSenderId: "44444444",
appId: "2:43434:122434de"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);

How to secure Google API keys for Web

Let’s secure them by only allowing the referrers or initiated action from whitelisted domain names and app bundle ID.

  1. Navigate to https://console.developers.google.com/apis
  2. Go to your firebase project
  3. Go to credentials
  4. Under API keys, select the Browser key associated with your firebase project (should have the same key as the API key you use to initialize your firebase app.)
  5. Under Application restrictions select HTTP referrers (web sites), simply add the URL of your app.
    1. https://CloudlyEngineer.com/*
    2. http://CloudlyEngineer.com/*
    3. CloudlyEngineer.com/*
  6. Optional: If you have previously unrestricted keys prior to this change, I suggest regenerate a new key and update your JavaScript!
  7. If you’re using Firebase, be sure to add those URL’s that look like “https://appName-1111.firebaseapp.com&#8221;
  8. Save.

How to secure Google API keys for App

Now let’s secure them by only allowing the referrers or initiated action from whitelisted app bundle ID.

  1. Navigate to https://console.developers.google.com/apis
  2. Go to your firebase project
  3. Go to credentials
  4. Under API keys, select the iOS Key or Android Key associated with your firebase project (should have the same key as the API key you use to initialize your firebase app.)
  5. Under Application, restrictions select iOS apps. You can find this in Xcode and elsewhere.
    1. orgName.AppName
  6. Optional: If you have previously unrestricted keys prior to this change, I suggest regenerate a new key and update your JavaScript!
  7. Save.

What Google must do!

Update your damn starting guides to make the keys secure before initializing the keys! I know they have a checklist but as coders, we tend to keep following the technical guide more. Here’s their checklist.Â