diff --git a/administration/cloud_capture/getting_started.md b/administration/cloud_capture/getting_started.md new file mode 100644 index 00000000..c4dccecd --- /dev/null +++ b/administration/cloud_capture/getting_started.md @@ -0,0 +1,75 @@ +--- +title: "Getting started with Cloud Capture" +sidebarTitle: "Getting started" +description: "Learn how to configure Cloud Capture for your organization" +tag: "BETA" +--- + + +Cloud Capture is still in active development. Its capabilities and configuration format may change, and onboarding is done together with Kosli's Customer Success team. + + +Cloud Capture is a managed service that runs on Kosli's infrastructure and connects to your cloud platform to observe the resources deployed there. To get set up with Cloud Capture you need to grant permissions to Kosli's cloud account and enable Cloud Capture within your Kosli org. + +## Overview + +Getting started with Cloud Capture involves two steps. + + + + Create an IAM role in your cloud accounts specifically for Cloud Capture. + + + Enable Cloud Capture for your Kosli org, and the regular snapshots will appear in Kosli. + + + + + + +### Prepare your environment + +In order for Cloud Capture to reach into your AWS cloud, to discover your ECS clusters and Lambdas, you need to grant permission to Kosli to do so. This requires the creation of an IAM role that Kosli can assume; the role will exist within your AWS account. + +To simplify this process, Kosli has created a CloudFormation template that contains a role with the minimum set of permissions needed by Cloud Capture. The role can be assumed by Kosli and is protected by an external Id; each organization within Kosli has its own external Id. The CloudFormation template can be downloaded from the Settings page for your organization within the Kosli UI. + +If you would rather create the role yourself, see [Cloud Capture Security](/administration/cloud_capture/security) for the trust policy and the full set of permissions the role needs. + +The CloudFormation template can be deployed within an AWS account, or can be attached to an AWS Organizational Unit (OU) as a StackSet; this latter option ensures the correct IAM permissions are rolled out to all AWS accounts within the OU. + +When used, the CloudFormation template will send your AWS account id to Kosli, so that we are automatically notified that your account is ready to be included in Cloud Capture. Similarly, if you delete the CloudFormation stack we will be notified and know that the account is no longer to be included. + +### Enable Cloud Capture + +When you have created the IAM role, using the CloudFormation template, you can activate Cloud Capture within the Kosli user-interface. Cloud Capture runs on a five-minute schedule, and once you have enabled it, Cloud Capture will pick up your environment the next time it runs - you should see environments and snapshots appearing within a few minutes. + +### Excluding resources + +If there are resources you do not wish to include within a Cloud Capture snapshot, for example an ECS cluster that you consider to be out of scope, you can add a tag to it indicating that the item should be skipped. Adding a tag with the name `kosli.capture` and the value `false` will ensure that Cloud Capture skips over that resource. + + + + +GCP support is coming soon + + +### Prepare your environment + +In order for Cloud Capture to reach into your GCP project, to discover your Kubernetes clusters, you need to grant permission to Kosli to do so. This requires a service account in your project that Cloud Capture can impersonate through workload identity federation; the service account, and the workload identity pool that guards it, exist within your own project. + +To simplify this process, Kosli has created a Terraform configuration that creates that service account, a custom role holding the minimum set of permissions needed by Cloud Capture, and a workload identity pool that accepts only the Kosli-side role created for your organization. The Terraform can be downloaded from the Settings page for your organization within the Kosli UI. + +If you would rather create the role yourself, see [Cloud Capture Security](/administration/cloud_capture/security) for the trust policy and the full set of permissions the role needs. + +GCP has no equivalent of the CloudFormation "phone-home" feature, so the Terraform configuration emits the two values Kosli needs — the workload identity provider and the service account email — as outputs. Share them with Kosli once the deployment completes; you can read them at any time with `gcloud infra-manager deployments describe`. See [Cloud Capture Security](/administration/cloud_capture/security) for the deployment command. + +### Enable Cloud Capture + +When you have created the role and added the account details to Kosli, you can activate Cloud Capture within the Kosli user-interface. Cloud Capture runs on a five-minute schedule, and once you have enabled it, Cloud Capture will pick up your environment the next time it runs - you should see environments and snapshots appearing within a few minutes. + +### Excluding resources + +Support for excluding resources is coming soon. + + + diff --git a/administration/cloud_capture/overview.md b/administration/cloud_capture/overview.md new file mode 100644 index 00000000..974de9b6 --- /dev/null +++ b/administration/cloud_capture/overview.md @@ -0,0 +1,59 @@ +--- +title: "Cloud Capture" +sidebarTitle: "Cloud Capture" +description: "Learn how Cloud Capture, a managed service, snapshots your cloud environments from Kosli's infrastructure, with no software to install." +tag: "BETA" +--- + + +Cloud Capture is still in active development. Its capabilities and configuration format may change, and onboarding is done together with Kosli's Customer Success team. + + +Cloud Capture is a managed service that runs on Kosli's infrastructure and connects to your cloud platform to observe the resources deployed there. You grant Cloud Capture a set of permissions, and it uses them to run a `kosli snapshot` every few minutes against the infrastructure you have allowed it to scan. + +Kosli also supports reporting from your own cloud accounts by running the Kosli CLI on a schedule. Cloud Capture inverts this, with Kosli running the regular [snapshots](/getting_started/environments) so there is no software for you to install. + +To set it up for your organization, see [Getting started with Cloud Capture](/administration/cloud_capture/getting_started). + +## Overview + +Cloud Capture connects to your cloud accounts using permissions that you manage. You configure Cloud Capture by activating it for different services, and Cloud Capture uses the permissions to regularly reach into your estate and record snapshots, sending the data into your Kosli organization. Cloud Capture uses details about your infrastructure, such as the name of an ECS cluster, to build environments within Kosli. + + +Diagram showing Cloud Capture, inside Kosli, sending queries to and receiving snapshots from three customer cloud accounts, then passing the data to the Kosli API and database + + +## Security + +The security of your cloud infrastructure is the primary driver behind the internal architecture of +Cloud Capture. You grant a read-only IAM role in your account, protected by an external ID that acts +as a shared secret between Kosli and you. On Kosli's side, each Cloud Capture job runs under a role +scoped to your organization alone, so a worker running for another customer cannot reach your cloud +account. Cloud Capture holds no customer data; snapshots go straight to Kosli through the same ingest +path as your existing pipelines. See [Cloud Capture Security](/administration/cloud_capture/security) +for the isolation model and the full list of permissions. + +## Hands-off operation + +Cloud Capture has been designed to operate with no on-going support from you. Once the initial security permissions have been created, Cloud Capture will continue to operate in a headless mode. As your cloud infrastructure changes over time, Cloud Capture will continue to find resources without you needing to do anything; your application teams do not need to take any action in order to onboard their products and services into Kosli. + +## Finding resources + +Cloud Capture finds all supported resources within your AWS accounts, and determines which Kosli environment should hold the snapshots. Cloud Capture will create physical environments for you inside Kosli. + +Cloud Capture can [filter out resources based on AWS tags](/administration/cloud_capture/getting_started#excluding-resources). + +As your cloud environment evolves, such as the addition of new AWS ECS clusters or the retirement of existing Lambdas, Cloud Capture automatically detects the changes. Because Cloud Capture creates physical environments as needed, when your infrastructure changes, Kosli will keep up. No changes to the configuration created during the initial setup are required. + +## Multiple AWS accounts + +Cloud Capture can operate across multiple AWS regions and accounts, allowing you to snapshot development, QA, pre-production, and production workloads with the same process. + +## Operation + +When Cloud Capture runs against one of your cloud accounts, it starts by gaining temporary credentials for the role you have created. It then uses these credentials to find resources to snapshot, such as finding all of your AWS ECS clusters. For each resource it identifies, Cloud Capture generates a snapshot within Kosli. + + +Diagram of the four steps Cloud Capture follows in a customer AWS account: assume the IAM role using the external ID, receive temporary STS credentials, find the ECS clusters, then snapshot the clusters + + diff --git a/administration/cloud_capture/security.md b/administration/cloud_capture/security.md new file mode 100644 index 00000000..e4cc5ef1 --- /dev/null +++ b/administration/cloud_capture/security.md @@ -0,0 +1,350 @@ +--- +title: Cloud Capture - Security +sidebarTitle: Security +description: "Learn about the security of Cloud Capture" +tag: "BETA" +--- + + +Cloud Capture is still in active development. Its capabilities and configuration format may change, and onboarding is done together with Kosli's Customer Success team. + + +## Cloud Capture permissions + +The Cloud Capture managed service uses the public cloud APIs to extract information about your cloud environments. In order to do this, you need to provide Kosli with an IAM role inside your cloud environment that allows access to these APIs. The role is created and managed by you. The role must contain the relevant assume-trust policy to allow Kosli to access your account and must contain the correct permissions for Kosli to find and snapshot your resources. + + + +Kosli publishes a CloudFormation template, for use in AWS, showing the permissions needed. The template is publicly accessible and can be used directly within an `aws cloudformation create-stack` call. + +The CloudFormation template we share with you includes a "phone-home" feature that notifies Kosli when a CloudFormation stack has been built from it; this allows us to pick up the AWS AccountId for the account in which you have used the CloudFormation template without you needing to do anything. This automation is especially useful when you deploy the template as a StackSet within an Organizational Unit. + +If you wish to build an IAM role for Cloud Capture without using our published CloudFormation template, the permissions needed are those shown below. The role must contain an Assume Role policy that allows the Kosli account to access the resources, and must include an externalId. Both of these values are supplied by Kosli and cannot be derived; you cannot construct the trust policy without them. + +### Assume role + +The IAM role defined within the CloudFormation template includes an "assume role" policy granting permission from Kosli. This appears as: + +```yaml + KosliCaptureAccessRole: + Type: AWS::IAM::Role + Properties: + RoleName: !Ref RoleName + Description: >- + Read-only access for Cloud Capture SDLC compliance evidence collection. + Managed by CloudFormation; do not edit in place. + MaxSessionDuration: 3600 + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Sid: AllowKosliToAssumeWithExternalId + Effect: Allow + Principal: + AWS: !Ref TrustedPrincipalArn + Action: sts:AssumeRole + Condition: + StringEquals: + sts:ExternalId: !Ref ExternalId +``` + +### All permissions needed + +The IAM role defined within the Cloudformation template includes a number of IAM policy statements, granting read-only access to some AWS APIs. The statements are: + +```yaml +Statement: + + # How Capture finds what to snapshot. Discovery lists the ECS + # clusters in the account and reads each cluster's tags from the + # same DescribeClusters call. + # + # Worth knowing for a security review: these are inventory calls + # and none of them returns application data. DescribeTaskDefinition + # is the widest - a task definition holds the container image, the + # command, and any environment variables written into the + # definition itself in plain text. Values injected from Secrets + # Manager or Parameter Store are named there rather than resolved, + # so what comes back is the reference and not the secret. + - Sid: EcsInventory + Effect: Allow + Action: + - ecs:DescribeCapacityProviders + - ecs:DescribeClusters + - ecs:DescribeContainerInstances + - ecs:DescribeServices + - ecs:DescribeTaskDefinition + - ecs:DescribeTasks + - ecs:ListClusters + - ecs:ListContainerInstances + - ecs:ListServices + - ecs:ListTagsForResource + - ecs:ListTaskDefinitionFamilies + - ecs:ListTaskDefinitions + - ecs:ListTasks + Resource: "*" + + - Sid: LambdaInventory + Effect: Allow + Action: + - lambda:GetFunctionConfiguration + - lambda:GetPolicy + - lambda:ListAliases + - lambda:ListFunctions + - lambda:ListTags + - lambda:ListVersionsByFunction + Resource: "*" + + # lambda:GetFunction returns a pre-signed URL to the deployment + # package. That is source-code access, so it is denied outright. + - Sid: NeverDownloadFunctionCode + Effect: Deny + Action: + - lambda:GetFunction + - lambda:GetLayerVersion + Resource: "*" +``` + + + + + +GCP support is coming soon + + +Kosli publishes a Terraform configuration, for use with +[Infrastructure Manager](https://cloud.google.com/infrastructure-manager/docs), showing the +permissions needed to snapshot Google Kubernetes Engine (GKE) clusters. Infrastructure Manager runs +Terraform as a managed service inside your project, so there is no state file or Terraform install +for you to manage. Deploy the configuration once in each project that holds clusters you want Cloud +Capture to snapshot. + +Cloud Capture runs in AWS and reaches your project through +[Workload Identity Federation](https://cloud.google.com/iam/docs/workload-identity-federation). Cloud +Capture exchanges its own short-lived AWS credentials for a short-lived Google token, then +impersonates a service account that you create. No service account key is ever created or shared. + +The trust has two parts, mirroring the AWS assume-role policy and external ID. The workload identity +pool accepts credentials only from the Kosli AWS account, which is the counterpart of the principal +in the trust policy. Within that account it accepts only the Kosli-side IAM role that is dedicated +to your organization, which is the counterpart of the external ID. Every Cloud Capture job runs +under the role for the organization it is working for, so a job for another Kosli customer presents +a different role name and is refused by your pool, even if that customer gave Kosli your provider +and service account instead of their own. The role name is part of the credential that AWS signs +and Google verifies, so it cannot be forged by the caller. + +GCP has no equivalent of the CloudFormation "phone-home" feature, so the configuration emits the +values Kosli needs as Terraform outputs. Share them with Kosli after the deployment completes; you +can read them at any time with `gcloud infra-manager deployments describe`. + +If you wish to grant access for Cloud Capture without using our published configuration, the +resources needed are those shown below. The pool must accept only the Kosli AWS account and, within +it, only the Kosli-side role for your organization, and only that role may impersonate the service +account. The specific permissions needed to find and snapshot GKE clusters are in the custom role. + + +Cloud Capture connects to each cluster's Kubernetes API from Kosli's infrastructure, so the +cluster's control plane endpoint must be reachable from outside your VPC. A cluster whose control +plane has a private endpoint only cannot be snapshotted. + + +### Workload identity federation + +The Terraform configuration creates a workload identity pool, an AWS provider within it that trusts +the Kosli-side role for your organization, and a service account that only that role may +impersonate. This appears as: + +```hcl +variable "project_id" { + type = string + description = "The GCP project that holds the GKE clusters Cloud Capture will snapshot." +} + +variable "kosli_aws_account_id" { + type = string + description = <<-EOT + The AWS account in which Cloud Capture runs, supplied by Kosli. It differs + per customer because more than one Kosli account serves customers. There + is no default and no value you can derive yourself. + EOT + + validation { + condition = can(regex("^[0-9]{12}$", var.kosli_aws_account_id)) + error_message = "Must be the 12-digit AWS account id issued to you by Kosli." + } +} + +variable "kosli_role_name" { + type = string + description = <<-EOT + The name of the IAM role, in the Kosli AWS account, that Cloud Capture + uses when working for your organization. Supplied by Kosli. Each Kosli + organization has its own role, so this value is the counterpart of the + AWS external ID: only jobs run on your behalf can obtain a token from + your project. + EOT + + validation { + condition = can(regex("^[A-Za-z0-9+=,.@_-]{1,64}$", var.kosli_role_name)) + error_message = "Must be the IAM role name issued to you by Kosli." + } +} + +# Cloud Capture runs in AWS. Workload Identity Federation lets it exchange its +# own short-lived AWS credentials for a short-lived Google token, so no service +# account key is ever created, stored or shared. +resource "google_iam_workload_identity_pool" "kosli_capture" { + project = var.project_id + workload_identity_pool_id = "kosli-capture" + display_name = "Cloud Capture" + description = <<-EOT + Read-only access for Cloud Capture SDLC compliance evidence collection. + Managed by Infrastructure Manager; do not edit in place. + EOT +} + +resource "google_iam_workload_identity_pool_provider" "kosli_aws" { + project = var.project_id + workload_identity_pool_id = google_iam_workload_identity_pool.kosli_capture.workload_identity_pool_id + workload_identity_pool_provider_id = "kosli-aws" + display_name = "Cloud Capture (AWS)" + + aws { + account_id = var.kosli_aws_account_id + } + + attribute_mapping = { + "google.subject" = "assertion.arn" + "attribute.account" = "assertion.account" + "attribute.aws_role" = "assertion.arn.extract('assumed-role/{role}/')" + } + + # Two checks, mirroring the AWS trust policy. The account check is the + # counterpart of the trust policy principal: only credentials issued by the + # Kosli account are accepted, however the token reaches Google. The role + # check is the counterpart of the external ID: within that account, only the + # Kosli-side role dedicated to your organization is accepted. A Cloud Capture + # job for another customer runs under a different role and is refused here. + attribute_condition = join(" && ", [ + "attribute.account == \"${var.kosli_aws_account_id}\"", + "attribute.aws_role == \"${var.kosli_role_name}\"", + ]) +} + +resource "google_service_account" "kosli_capture" { + project = var.project_id + account_id = "kosli-capture" + display_name = "Cloud Capture" + description = "Impersonated by Cloud Capture to snapshot GKE clusters. Managed by Infrastructure Manager." +} + +# Only the Kosli-side role for your organization may impersonate the service +# account. The pool's attribute condition already refuses every other identity; +# naming the role here as well keeps the binding correct even if that condition +# is later loosened. +resource "google_service_account_iam_member" "kosli_impersonation" { + service_account_id = google_service_account.kosli_capture.name + role = "roles/iam.workloadIdentityUser" + member = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.kosli_capture.name}/attribute.aws_role/${var.kosli_role_name}" +} + +# Kosli needs these two values to connect. Infrastructure Manager shows them +# with `gcloud infra-manager deployments describe`. +output "kosli_capture_provider" { + description = "Full resource name of the workload identity provider; give this to Kosli." + value = google_iam_workload_identity_pool_provider.kosli_aws.name +} + +output "kosli_capture_service_account" { + description = "Email of the service account Cloud Capture impersonates; give this to Kosli." + value = google_service_account.kosli_capture.email +} +``` + +### GKE permissions + +The Terraform configuration grants the service account a custom role containing read-only GKE +permissions. GKE enforces these permissions both on the GKE API and on the Kubernetes API of each +cluster, so no Kubernetes RBAC objects need to be created inside your clusters. The role is: + +```hcl +# How Capture finds what to snapshot. Discovery lists the GKE clusters in the +# project and reads each cluster's labels, endpoint and CA certificate from +# the same clusters.get call. +# +# Worth knowing for a security review: GKE checks the container.* permissions +# below when Cloud Capture calls the Kubernetes API, so this role is also the +# Kubernetes RBAC. None of it returns application data. pods.list is the +# widest - a pod spec holds the container image, the command, and any +# environment variables written into the spec itself in plain text. Values +# taken from a Secret or ConfigMap are named there rather than resolved, and +# this role grants no access to Secrets or ConfigMaps, so what comes back is +# the reference and not the secret. +resource "google_project_iam_custom_role" "kosli_capture" { + project = var.project_id + role_id = "kosliCapture" + title = "Cloud Capture" + description = "Read-only GKE inventory for Cloud Capture SDLC compliance evidence collection." + + permissions = [ + # Discovery + "container.clusters.get", + "container.clusters.list", + + # Snapshot: the Kubernetes API calls that kosli snapshot k8s makes + "container.namespaces.get", + "container.namespaces.list", + "container.pods.get", + "container.pods.list", + ] +} + +resource "google_project_iam_member" "kosli_capture" { + project = var.project_id + role = google_project_iam_custom_role.kosli_capture.id + member = google_service_account.kosli_capture.member +} +``` + +### Deploying with Infrastructure Manager + +Run the following from the directory containing the configuration. Infrastructure Manager runs +Terraform as the service account you name, so that service account needs permission to enable APIs +and to create workload identity pools, service accounts, custom roles and project IAM bindings. + +```bash +gcloud infra-manager deployments apply \ + projects//locations//deployments/kosli-capture \ + --service-account=projects//serviceAccounts/@.iam.gserviceaccount.com \ + --local-source=. \ + --input-values=project_id=,kosli_aws_account_id=,kosli_role_name= +``` + + + + +## How Kosli isolates customers + +Cloud Capture runs as a shared, autoscaled service, but each job runs under a role that is scoped to +one customer: + +* A Cloud Capture worker picks up a job for your organization and assumes the role in your account + using your externalId. A worker running for a different customer is unable to read the externalId + for your cloud account. +* When the job finishes, the temporary credentials for your account are discarded. A worker holding + credentials for your cloud account has no path to anyone else's account. +* The ExternalId lives in Kosli's Parameter Store and is readable only by the Kosli-side role for your + organization. The shared task role cannot read any customer's ExternalId. Separation is enforced + by IAM, not by application code. + +The trust policy on the role in your account limits access to the AWS account in which the Cloud +Capture is running. The ExternalId acts as a shared secret between Kosli and you, so that only +Cloud Capture is permitted to assume the role. + +Cloud Capture itself does not hold any customer data. Snapshots taken by Cloud Capture are +immediately sent to Kosli through the same ingest path as your existing pipelines. + +## Changing security permissions + +If the IAM role assumed by Cloud Capture is removed or edited within your cloud accounts, Cloud +Capture will fail to operate correctly. The failure will be detected and the account will be +removed from the list of accounts captured. diff --git a/administration/managing_environments/overview.md b/administration/managing_environments/overview.md index 5727f0b6..0d151755 100644 --- a/administration/managing_environments/overview.md +++ b/administration/managing_environments/overview.md @@ -64,6 +64,10 @@ terraform import kosli_environment.my_environment production The `type` in your Terraform configuration must exactly match the type of the existing environment in Kosli. A mismatch will cause import errors or misconfiguration. +### Automatically creating physical environments + +[Cloud Capture](/administration/cloud_capture/overview) snapshots the supported resources it finds in your cloud accounts and creates physical environments as needed. + ## Managing logical environments Logical environments group physical environments into a combined view — useful for representing a full production tier across multiple runtimes. diff --git a/config/navigation.json b/config/navigation.json index 3405dc4d..a90b25fd 100644 --- a/config/navigation.json +++ b/config/navigation.json @@ -73,7 +73,15 @@ "administration/managing_custom_attestation_types/overview" ] }, - "administration/managing_tags" + "administration/managing_tags", + { + "group": "Cloud Capture", + "pages": [ + "administration/cloud_capture/overview", + "administration/cloud_capture/getting_started", + "administration/cloud_capture/security" + ] + } ] }, { diff --git a/getting_started/environments.md b/getting_started/environments.md index ed8723ff..41780dad 100644 --- a/getting_started/environments.md +++ b/getting_started/environments.md @@ -55,7 +55,7 @@ Currently, the following environment types are supported: - Azure Web Apps and Function Apps - Google Cloud Run (services and jobs) -You can report environment snapshots manually using the `kosli snapshot [...]` commands for testing. For production use, however, you would configure the reporting to happen automatically on regular intervals, e.g. via a cron job or scheduled CI job, or on certain events. +You can report environment snapshots manually using the `kosli snapshot [...]` commands for testing. For production use, however, you would configure the reporting to happen automatically on regular intervals, e.g. via a cron job or scheduled CI job, or on certain events. Kosli can also report these snapshots for you, using [Cloud Capture](/administration/cloud_capture/overview). You can follow one of the tutorials below to setup automatic snapshot reporting for your environment: - [Kubernetes environment reporting](/tutorials/report_k8s_envs) diff --git a/images/administration/cloud-capture-4-steps.png b/images/administration/cloud-capture-4-steps.png new file mode 100644 index 00000000..3190ceca Binary files /dev/null and b/images/administration/cloud-capture-4-steps.png differ diff --git a/images/administration/cloud-capture-overview.png b/images/administration/cloud-capture-overview.png new file mode 100644 index 00000000..965cbe00 Binary files /dev/null and b/images/administration/cloud-capture-overview.png differ