diff --git a/administration/kosli_capture/getting_started.md b/administration/kosli_capture/getting_started.md new file mode 100644 index 00000000..74e70ff0 --- /dev/null +++ b/administration/kosli_capture/getting_started.md @@ -0,0 +1,69 @@ +--- +title: "Getting started with Kosli Capture" +sidebarTitle: "Getting started" +description: "Learn how to configure Kosli Capture for your organization" +tag: "BETA" +--- + + +Kosli Capture is still in active development. Its capabilities and configuration format may change, and onboarding is done together with Kosli's Customer Success team. + + +Kosli 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 Kosli Capture you need to grant permissions to Kosli's cloud account and enable Kosli Capture within your Kosli org. + +## Overview + +Getting started with Kosli Capture involves two steps. + + + + Create an IAM role in your cloud accounts specifically for Kosli Capture. + + + Enable Kosli Capture for your Kosli org, and the regular snapshots will appear in Kosli. + + + + + + +## Prepare your environment + +In order for Kosli 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 Kosli 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 [Kosli Capture Security](/administration/kosli_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 Kosli Capture. Similarly, if you delete the CloudFormation stack we will be notified and know that the account is no longer to be included. + +## Enable Kosli Capture + +When you have created the IAM role, using the CloudFormation template, you can activate Kosli Capture within the Kosli user-interface. Kosli Capture runs on a five-minute schedule, and once you have enabled it, Kosli 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 Kosli Capture Managed 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 Kosli Capture skips over that resource. + + +## Prepare your environment + +In order for Kosli Capture to reach into your GCP cloud, to discover your Kubernetes clusters, 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 Google Cloud account. + +To simplify this process, Kosli has created a Terraform configuration that contains a role with the minimum set of permissions needed by Kosli Capture. The role can be used by Kosli and is protected by ensuring the `principalSet` supplied contains the specific Kosli Capture role that was 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 [Kosli Capture Security](/administration/kosli_capture/security) for the trust policy and the full set of permissions the role needs. +When used, you need 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 Kosli Capture. Similarly, if you delete the CloudFormation stack we will be notified and know that the account is no longer to be included. + +## Enable Kosli Capture + +When you have created the role and added the account details to Kosli, you can activate Kosli Capture within the Kosli user-interface. Kosli Capture runs on a five-minute schedule, and once you have enabled it, Kosli 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 Kosli Capture Managed snapshot, for example a specific 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 Kosli Capture skips over that resource. + + + diff --git a/administration/kosli_capture/overview.md b/administration/kosli_capture/overview.md new file mode 100644 index 00000000..8065d234 --- /dev/null +++ b/administration/kosli_capture/overview.md @@ -0,0 +1,59 @@ +--- +title: "Kosli Capture Managed Service" +sidebarTitle: "Kosli Capture" +description: "Learn how the Kosli Capture Managed Service snapshots your cloud environments from Kosli's infrastructure, with no software to install." +tag: "BETA" +--- + + +Kosli Capture is still in active development. Its capabilities and configuration format may change, and onboarding is done together with Kosli's Customer Success team. + + +Kosli 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 Kosli 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. Kosli 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 Kosli Capture](/administration/kosli_capture/getting_started). + +## Overview + +Kosli Capture connects to your cloud accounts using permissions that you manage. You configure Kosli Capture by activating it for different services, and Kosli Capture uses the permissions to regularly reach into your estate and record snapshots, sending the data into your Kosli organization. Kosli Capture uses details about your infrastructure, such as the name of an ECS cluster, to build environments within Kosli. + + +Diagram showing Kosli 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 +Kosli 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 Kosli Capture job runs under a role +scoped to your organization alone, so a worker running for another customer cannot reach your cloud +account. Kosli Capture holds no customer data; snapshots go straight to Kosli through the same ingest +path as your existing pipelines. See [Kosli Capture Security](/administration/kosli_capture/security) +for the isolation model and the full list of permissions. + +## Hands-off operation + +Kosli Capture has been designed to operate with no on-going support from you. Once the initial security permissions have been created, Kosli Capture will continue to operate in a headless mode. As your cloud infrastructure changes over time, Kosli 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 + +Kosli Capture finds all supported resources within your AWS accounts, and determines which Kosli environment should hold the snapshots. Kosli Capture will create physical environments for you inside Kosli. + +Kosli Capture can [filter out resources based on AWS tags](/administration/kosli_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, Kosli Capture automatically detects the changes. Because Kosli 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 + +Kosli 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 Kosli 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, Kosli Capture generates a snapshot within Kosli. + + +Diagram of the four steps Kosli 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/kosli_capture/security.md b/administration/kosli_capture/security.md new file mode 100644 index 00000000..bd06e37f --- /dev/null +++ b/administration/kosli_capture/security.md @@ -0,0 +1,350 @@ +--- +title: Kosli Capture - Security +sidebarTitle: Security +description: "Learn about the security of Kosli Capture" +tag: "BETA" +--- + + +Kosli Capture is still in active development. Its capabilities and configuration format may change, and onboarding is done together with Kosli's Customer Success team. + + +## Kosli Capture permissions + +The Kosli 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 Kosli 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 Kosli 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 Kosli +Capture to snapshot. + +Kosli Capture runs in AWS and reaches your project through +[Workload Identity Federation](https://cloud.google.com/iam/docs/workload-identity-federation). Kosli +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 Kosli 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 Kosli 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. + + +Kosli 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 Kosli Capture will snapshot." +} + +variable "kosli_aws_account_id" { + type = string + description = <<-EOT + The AWS account in which Kosli 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 Kosli 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." + } +} + +# Kosli 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 = "Kosli Capture" + description = <<-EOT + Read-only access for Kosli 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 = "Kosli 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 Kosli 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 = "Kosli Capture" + description = "Impersonated by Kosli 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 Kosli 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 Kosli 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 = "Kosli Capture" + description = "Read-only GKE inventory for Kosli 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 + +Kosli Capture runs as a shared, autoscaled service, but each job runs under a role that is scoped to +one customer: + +* A Kosli 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 Kosli +Capture Managed service is running. The ExternalId acts as a shared secret between Kosli and you, so that only +Kosli Capture is permitted to assume the role. + +Kosli Capture itself does not hold any customer data. Snapshots taken by Kosli Capture are +immediately sent to Kosli through the same ingest path as your existing pipelines. + +## Changing security permissions + +If the IAM role assumed by Kosli Capture is removed or edited within your cloud accounts, Kosli +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..f41e2efe 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 + +[Kosli Capture Managed Service](/administration/kosli_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..f68f48c5 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": "Kosli Capture", + "pages": [ + "administration/kosli_capture/overview", + "administration/kosli_capture/getting_started", + "administration/kosli_capture/security" + ] + } ] }, { diff --git a/getting_started/environments.md b/getting_started/environments.md index ed8723ff..6e9bfb0c 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 the [Kosli Capture Managed Service](/administration/kosli_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/kosli-capture-4-steps.png b/images/administration/kosli-capture-4-steps.png new file mode 100644 index 00000000..3190ceca Binary files /dev/null and b/images/administration/kosli-capture-4-steps.png differ diff --git a/images/administration/kosli-capture-overview.png b/images/administration/kosli-capture-overview.png new file mode 100644 index 00000000..965cbe00 Binary files /dev/null and b/images/administration/kosli-capture-overview.png differ