Installing a Connector on EKS Using Terraform

This guide is intended for admins managing a Connector in the environment

📘

You have chosen the advanced installation method

You can also easily connect AWS in Apono following this UI guide here

How to install the Connector on EKS Using Terraform

Prerequisites

  • Required CLI: terraform

Step 1 - Create Connector

Important: before you start, copy the connector Terraform params and export them in the terminal.

Step 2 - Configure Variables

variable "eks_cluster_name" {
  type    = string
  default = "PLEASE_REPLACE_WITH_CLUSTER_NAME"
}

variable "CONNECTOR_ID" {
  type    = string
  description = "Apono Connector ID"
}

variable "APONO_TOKEN" {
  type    = string
  description = "Apono authoriozatrion token"
  sensitive = true
}

variable "APONO_WEBSOCKET_URL" {
  type    = string
  description = "Apono websocket URL"
}

variable "aws_region" {
  type    = string
  default = "us-east-1"
}

variable "service_account_name" {
  type    = string
  default = "apono"
}

variable "namespace" {
  type    = string
  default = "apono"
}

Step 3 - Add necessary Terraform providers

** if you already use your own providers, you can skip this step

provider "aws" {
  alias  = "apono"
  region = var.aws_region
}

data "aws_eks_cluster" "cluster" {
  name     = var.eks_cluster_name
}

data "aws_eks_cluster_auth" "eks_cluster_auth" {
  name     = var.eks_cluster_name
}

data "aws_caller_identity" "aws_account" {
}

locals {
  aws_account_id = data.aws_caller_identity.aws_account.account_id
  oidc_provider = replace(data.aws_eks_cluster.cluster.identity[0].oidc[0].issuer, "https://", "")
  config_path = "~/.kube/config"
  config_context = "my-context" 
}

provider "kubernetes" {
  config_path = local.config_path
  config_context = local.config_context
}

provider "helm" {
  kubernetes {
    config_path = local.config_path
    config_context = local.config_context
  }
}

Run terraform init to validate it works

Step 4 - Add EKS cluster OIDC provider to your IAM

It's required that your EKS cluster OIDC provider will be added to your IAM.
This step is required only once, and you may have already done it.

data "aws_eks_cluster" "eks_cluster" {
  name = var.eks_cluster_name
}

data "tls_certificate" "tls_cert" {
  url = data.aws_eks_cluster.eks_cluster.identity[0].oidc[0].issuer
}

resource "aws_iam_openid_connect_provider" "eks-oidc-provider" {
  client_id_list  = ["sts.amazonaws.com"]
  thumbprint_list = [data.tls_certificate.tls_cert.certificates[0].sha1_fingerprint]
  url             = data.aws_eks_cluster.eks_cluster.identity[0].oidc[0].issuer
}

Step 5 - Create the Connector IAM role

The Connector is deployed using helm and requires an IAM Role to be able to access tagged ASM secrets in the future.

// IAM role and access policy for apono tagged secrets
resource "aws_iam_role" "apono-connector" {
  name     = "${var.service_account_name}-${var.CONNECTOR_ID}"

  assume_role_policy = jsonencode({
    "Version" : "2012-10-17",
    "Statement" : [
      {
        "Effect" : "Allow",
        "Principal" : { "Federated" : "arn:aws:iam::${local.aws_account_id}:oidc-provider/${local.oidc_provider}" },
        "Action" : "sts:AssumeRoleWithWebIdentity",
        "Condition" : {
          "StringEquals" : { "${local.oidc_provider}:sub" : "system:serviceaccount:${var.namespace}:${var.service_account_name}" }
        }
      }
    ]
  })
  
  inline_policy {
    name = "apono-tagged-keys-access-policy"

    policy = jsonencode({
      Version = "2012-10-17"
      Statement = [
        {
          Effect = "Allow"
          Action = [ "kms:Sign" ]
          Resource = "*"
          Condition = { "StringEquals": {"aws:ResourceTag/apono-connector-read": "true"} }
        },
      ]
    })
  }
  
  inline_policy {
    name = "apono-tagged-secrets-access-policy"

    policy = jsonencode({
      Version = "2012-10-17"
      Statement = [
        {
          Effect = "Allow"
          Action = [ "secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret"]
          Resource = "arn:aws:secretsmanager:*:${local.aws_account_id}:secret:*"
          Condition = { "StringEquals": {"aws:ResourceTag/apono-connector-read": "true"} }
        },
      ]
    })
  }

  // for IAM Managment
  inline_policy {
    name = "iam-access-policy"
    policy = jsonencode({
      Version = "2012-10-17"
      Statement = [
        {
            Effect = "Allow"
            Action = [
                "iam:ListPolicies",
                "iam:CreateInstanceProfile",
                "iam:ListGroups",
                "iam:ListInstanceProfiles"
            ],
            Resource = "*"
        },
        {
            Effect = "Allow"
            Action = [
                "iam:CreateInstanceProfile",
                "iam:GetRole",
                "iam:UpdateAssumeRolePolicy",
                "iam:ListRoleTags",
                "iam:TagRole",
                "iam:CreateRole",
                "iam:DeleteRole",
                "iam:AttachRolePolicy",
                "iam:PutRolePolicy",
                "iam:AddRoleToInstanceProfile",
                "iam:ListInstanceProfilesForRole",
                "iam:DetachRolePolicy",
                "iam:ListAttachedRolePolicies",
                "iam:DeleteRolePolicy",
                "iam:ListAttachedGroupPolicies",
                "iam:ListRolePolicies",
                "iam:GetRolePolicy",
                "iam:PassRole",
                "iam:GetInstanceProfile",
                "iam:CreateUser",
                "iam:CreateAccessKey",
                "iam:DeleteAccessKey",
                "iam:PutUserPolicy",
                "iam:DeleteUserPolicy",
                "iam:GetUser",
                "iam:GetUserPolicy",
                "iam:ListAttachedUserPolicies",
                "iam:ListUserPolicies",
                "iam:UpdateLoginProfile",
                "iam:ListAccessKeys",
                "iam:AttachUserPolicy",
                "iam:DetachUserPolicy",
                "iam:CreateLoginProfile"
            ]
            Resource = [
                "arn:aws:iam::*:instance-profile/*",
                "arn:aws:iam::*:role/*",
                "arn:aws:iam::*:group/*",
                "arn:aws:iam::*:user/*"
            ]
        }
      ]
    })
  }
  
  inline_policy {
    name = "ec2-policy"
    policy = jsonencode({
      Version = "2012-10-17"
      Statement = [{
        "Effect": "Allow",
        "Action": [ "ec2:DescribeInstances","ec2:DescribeTags","ec2:AssociateIamInstanceProfile" ],
        "Resource": "*"
      }]
    })
  }
}

data "aws_iam_policy" "SecurityAudit" {
  arn = "arn:aws:iam::aws:policy/SecurityAudit"
}

resource "aws_iam_role_policy_attachment" "connector-security-audit" {
  role       = "${aws_iam_role.apono-connector.name}"
  policy_arn = "${data.aws_iam_policy.SecurityAudit.arn}"
}

Step 6 - Deploy Apono Connector

locals {
  connector_helm_repo = "https://apono-io.github.io/apono-helm-charts/"
  connector_helm_chart = "apono-connector"
  connector_helm_chart_version = "<<helmVersion>>"
}

// agent deployment
resource "kubernetes_namespace_v1" "apono-namespace" {
  metadata {
    name = var.namespace
  }
}

resource "helm_release" "apono-connector" {
  name             = "apono-connector"
  repository       = local.connector_helm_repo
  chart            = local.connector_helm_chart
  version          = local.connector_helm_chart_version
  namespace        = kubernetes_namespace_v1.apono-namespace.metadata[0].name


  set {
    name = "serviceAccount.name"
    value = var.service_account_name
  }
  
  set {
    name = "serviceAccount.awsRoleAccountId"
    value = local.aws_account_id
  }
  
  set {
    name = "serviceAccount.awsRoleName"
    value = "${var.service_account_name}-${var.CONNECTOR_ID}"
  }

  set {
    name = "image.tag"
    value = "<<connectorVersion>>"
  }

  set {
    name  = "apono.token"
    value = var.APONO_TOKEN
  }

  set {
    name  = "apono.url"
    value = var.APONO_WEBSOCKET_URL
  }

  set {
    name  = "apono.connectorId"
    value = var.CONNECTOR_ID
  }
}


Validate the Connector is Connected

You can validate the Connector is installed in the Connector status page.