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

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

How to install the Connector on EKS

Prerequisites

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

  • required providers: aws, tls
  • replace eks_cluster_name variable
  • or you can use terraform apply -var="eks_cluster_name={{PLEASE_REPLACE_ME}}"
variable "eks_cluster_name" {
  type    = string
  default = "{{PLEASE_REPLACE_ME}}"
}

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
}

Deploying the Connector

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

  • used providers: aws, kubernetes, helm
  • replace aws_region,eks_cluster_name,service_account_name,namespace variables
  • or you can use terraform apply -var="aws_region={{PLEASE_REPLACE_ME}}" -var="eks_cluster_name={{PLEASE_REPLACE_ME}}" -var="service_account_name={{PLEASE_REPLACE_ME}}" -var="namespace={{PLEASE_REPLACE_ME}}"
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 = "{{PLEASE_REPLACE_ME}}"
}

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

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

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

locals {
  connector_helm_repo = "https://apono-io.github.io/apono-helm-charts/"
  connector_helm_chart = "apono-connector"
  connector_helm_chart_version = "2.0.0"
  connector_image_repo = "aponoio/apono-connector"
  connector_image_tag = "1.2.10"
}

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

data "aws_eks_cluster" "eks_cluster" {
  provider = aws.apono
  name     = var.eks_cluster_name
}

data "aws_eks_cluster_auth" "eks_cluster_auth" {
  provider = aws.apono
  name     = var.eks_cluster_name
}

data "aws_caller_identity" "aws_account" {
  provider = aws.apono 
}

locals {
  aws_account_id = data.aws_caller_identity.aws_account.account_id
  oidc_provider = replace(data.aws_eks_cluster.eks_cluster.identity[0].oidc[0].issuer, "https://", "")
}

provider "kubernetes" {
  alias                  = "apono"
  host                   = data.aws_eks_cluster.eks_cluster.endpoint
  cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks_cluster.certificate_authority.0.data)
  token                  = data.aws_eks_cluster_auth.eks_cluster_auth.token
}

provider "helm" {
  alias = "apono"
  kubernetes {
    host                   = data.aws_eks_cluster.eks_cluster.endpoint
    cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks_cluster.certificate_authority.0.data)
    token                  = data.aws_eks_cluster_auth.eks_cluster_auth.token
  }
}


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

resource "helm_release" "apono-connector" {
  provider         = helm.apono
  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 = "image.repository"
    value = local.connector_image_repo
  }

  set {
    name = "image.tag"
    value = local.connector_image_tag
  }


  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}-${local.connector_id}"
  }

  set {
    name  = "apono.token"
    value = local.apono_token
  }

  set {
    name  = "apono.url"
    value = local.apono_websocket_url
  }

  set {
    name  = "apono.websocketSsl"
    value = local.apono_websocket_ssl
  }

  set {
    name  = "apono.connectorId"
    value = local.connector_id
  }
}

// IAM role and access policy for apono tagged secrets
resource "aws_iam_role" "apono-connector" {
  provider = aws.apono
  name     = "${var.service_account_name}-${local.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 s3-iam-role resources
  inline_policy {
    name = "s3-list-policy"
    policy = jsonencode({
      Version = "2012-10-17"
      Statement = [
        {
          Effect = "Allow"
          Action = ["s3:GetBucketTagging", "s3:ListAllMyBuckets", "s3:ListBucket", "s3:GetBucketLocation"]
          Resource = "arn:aws:s3:::*"
        },
      ]
    })
  }
  
  // for mysql-rds resources
  inline_policy {
    name = "rds-list-policy"
    policy = jsonencode({
      Version = "2012-10-17"
      Statement = [
        {
          Effect = "Allow"
          Action = ["rds:DescribeDBInstances", "rds:ListTagsForResource"]
          Resource = "arn:aws:rds:*:*:db:*"
        },
      ]
    })
  }

  // for ssm-iam-role resources
  inline_policy {
    name = "ssm-params-list-policy"
    policy = jsonencode({
      Version = "2012-10-17"
      Statement = [
        {
          Effect = "Allow"
          Action = ["ssm:GetParameters*"]
          Resource = "arn:aws:ssm:*:*:parameter/*"
        },
        {
          Effect = "Allow"
          Action = ["ssm:DescribeParameters"]
          Resource = "*"
        },
      ]
    })
  }

  // for iam manipulation
  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 = "apono-read-resource-tags"
    policy = jsonencode({
      Version = "2012-10-17"
      Statement = [{
        "Effect": "Allow",
        "Action": [ "tag:GetResources" ],
        "Resource": "*"
      }]
    })
  }
  
  inline_policy {
    name = "ec2-policy"
    policy = jsonencode({
      Version = "2012-10-17"
      Statement = [{
        "Effect": "Allow",
        "Action": [ "ec2:DescribeInstances","ec2:DescribeTags","ec2:AssociateIamInstanceProfile" ],
        "Resource": "*"
      }]
    })
  }
}

Validate the Connector is Connected

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