Installing a Connector on Kubernetes with AWS Permissions

📘

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 Kubernetes

Prerequisite

It's required that you have your Cloud Account connected to your Kubernetes Cluster
In this example we use Kube2Iam solution for kubernetes on EC2

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.
You can choose to install the Connector using either your CLI or Terraform:

CLI

  • Create a IAM role to allow Connector read access for apono tagged secrets
  • Get AWS Account
  • Replace #EKS_CLUSTER_NAME
ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text) && OIDC_PROVIDER=$(aws eks describe-cluster --name "#EKS_CLUSTER_NAME" --query "cluster.identity.oidc.issuer" --output text | sed -e "s/^https:\/\///")
  • Create Connector Role
aws iam create-role --role-name "{{K8S_SERVICE_ACCOUNT}}-{{CONNECTOR_ID}}" --assume-role-policy-document '{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Service": "ec2.amazonaws.com"
    },
    "Action": "sts:AssumeRole"
  }]
}'
  • Assign Role Policies
aws iam put-role-policy --role-name "{{K8S_SERVICE_ACCOUNT}}-{{CONNECTOR_ID}}" --policy-name "apono-tagged-secrets-access-policy" --policy-document '{
  "Version": "2012-10-17",
  "Statement": [{
      "Effect": "Allow",
      "Action": [ "secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret"],
      "Resource": "arn:aws:secretsmanager:*:'"${ACCOUNT_ID}"':secret:*",
      "Condition": { "StringEquals": {"aws:ResourceTag/apono-connector-read": "true"} }
    }]
}'

aws iam put-role-policy --role-name "{{K8S_SERVICE_ACCOUNT}}-{{CONNECTOR_ID}}" --policy-name "apono-tagged-kms-keys-access-policy" --policy-document '{  
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "kms:Sign",
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "aws:ResourceTag/apono-connector-read": "true"
                }
            }
        }
    ]
}'

aws iam put-role-policy --role-name "{{K8S_SERVICE_ACCOUNT}}-{{CONNECTOR_ID}}" --policy-name "apono-iam-policy" --policy-document '{
  "Version": "2012-10-17",
  "Statement": [{"Action":["iam:ListPolicies","iam:CreateInstanceProfile","iam:ListGroups","iam:ListInstanceProfiles"],
    "Effect":"Allow",
    "Resource":"*"},
    {"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"],
    "Effect":"Allow",
    "Resource":["arn:aws:iam::*:instance-profile/*","arn:aws:iam::*:role/*","arn:aws:iam::*:group/*","arn:aws:iam::*:user/*"]}]
}'

aws iam put-role-policy --role-name "{{K8S_SERVICE_ACCOUNT}}-{{CONNECTOR_ID}}" --policy-name "apono-read-objects-policy" --policy-document '{
  "Version": "2012-10-17",
  "Statement": [{"Action":["rds:DescribeDBInstances","rds:ListTagsForResource"],
    "Effect":"Allow",
    "Resource":"arn:aws:rds:*:*:db:*"},
    {"Action":["ssm:GetParameters*"],
    "Effect":"Allow",
    "Resource":"arn:aws:ssm:*:*:parameter/*"},
    {"Action":["ssm:DescribeParameters"],
    "Effect":"Allow",
    "Resource":"*"},
    {"Action":["s3:GetBucketTagging","s3:ListAllMyBuckets","s3:ListBucket","s3:GetBucketLocation"],
    "Effect":"Allow",
    "Resource":"arn:aws:s3:::*"}]
}'

aws iam put-role-policy --role-name "{{K8S_SERVICE_ACCOUNT}}-{{CONNECTOR_ID}}" --policy-name "apono-read-resource-tags" --policy-document '{
  "Version": "2012-10-17",
  "Statement": [{
      "Effect": "Allow",
      "Action": "tag:GetResources",
      "Resource": "*"
    }]
}'
  • Deploy Apono Connector
helm install apono-connector {{CONNECTOR_CHART_REPO}}/{{CONNECTOR_CHART_NAME}}/{{CONNECTOR_CHART_NAME}}-<<helmVersion>>.tgz \
    --namespace "{{K8S_NAMESPACE}}" \
    --set image.repository="{{CONNECTOR_IMAGE_REPO}}" \
    --set image.tag="<<connectorVersion>>" \
    --set gitSyncImage="{{GIT_SYNC_IMAGE}}" \
    --set githubAccessToken="{{GITHUB_ACCESS_TOKEN}}" \
    --set serviceAccount.name="{{K8S_SERVICE_ACCOUNT}}" \
    --set podAnnotations."iam\.amazonaws\.com/role"="{{K8S_SERVICE_ACCOUNT}}-{{CONNECTOR_ID}}" \
    --set apono.token="{{TOKEN}}" \
    --set apono.url="{{APONO_WEBSOCKET_URL}}" \
    --set apono.websocketSsl="{{APONO_WEBSOCKET_SSL}}" \
    --set apono.connectorId="{{CONNECTOR_ID}}" \
    --create-namespace

Terraform

  • Required providers: helm (v2.5.1), aws, kubernetes
locals {
  apono_token = "{{TOKEN}}"
  apono_websocket_url = "{{APONO_WEBSOCKET_URL}}"
  apono_websocket_ssl = "{{APONO_WEBSOCKET_SSL}}"
  connector_id = "{{CONNECTOR_ID}}"
  connector_helm_repo = "{{CONNECTOR_CHART_REPO}}"
  connector_helm_chart = "{{CONNECTOR_CHART_NAME}}"
  connector_helm_chart_version = "<<helmVersion>>"
  connector_image_repo = "{{CONNECTOR_IMAGE_REPO}}"
  connector_image_tag = "<<connectorVersion>>"
  git_sync_image = "{{GIT_SYNC_IMAGE}}"
  github_access_token = "{{GITHUB_ACCESS_TOKEN}}"
  service_account_name = "{{K8S_SERVICE_ACCOUNT}}"
  namespace = "{{K8S_NAMESPACE}}"
}

data "aws_caller_identity" "aws_account" {
  
}

locals {
  aws_account_id = data.aws_caller_identity.aws_account.account_id
}


// agent deployment
resource "kubernetes_namespace_v1" "apono-namespace" {
  metadata {
    name = local.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 = "image.repository"
    value = local.connector_image_repo
  }

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

  set {
    name  = "gitSyncImage"
    value = local.git_sync_image
  }

  set {
    name  = "githubAccessToken"
    value = local.github_access_token
  }

  set {
    name = "serviceAccount.name"
    value = local.service_account_name
  }
  
  set {
    name = "serviceAccount.awsRoleAccountId"
    value = local.aws_account_id
  }
  
  set {
    name = "podAnnotations."iam\.amazonaws\.com/role""
    value = "${local.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" {
  name = "${local.service_account_name}-${local.connector_id}"

  assume_role_policy = jsonencode({
    "Version" : "2012-10-17",
    "Statement" : [
      {
        "Effect" : "Allow",
        "Principal": { "Service": "ec2.amazonaws.com"},
        "Action" : "sts:AssumeRole"
      }
    ]
  })
  
  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.