{"id":13994613,"url":"https://github.com/buildkite/iam-ssh-agent","last_synced_at":"2025-07-22T19:33:02.624Z","repository":{"id":37178311,"uuid":"244089470","full_name":"buildkite/iam-ssh-agent","owner":"buildkite","description":"Keyless SSH Agent for IAM Entities","archived":true,"fork":false,"pushed_at":"2023-10-12T01:02:13.000Z","size":615,"stargazers_count":20,"open_issues_count":16,"forks_count":3,"subscribers_count":19,"default_branch":"master","last_synced_at":"2024-08-10T14:16:33.492Z","etag":null,"topics":["authentication","aws-sam","continuous-integration","iam","serverless","ssh-agent","ssh-keys"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/buildkite.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-03-01T04:57:12.000Z","updated_at":"2024-03-06T22:35:35.000Z","dependencies_parsed_at":"2023-10-12T10:02:32.265Z","dependency_job_id":null,"html_url":"https://github.com/buildkite/iam-ssh-agent","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buildkite%2Fiam-ssh-agent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buildkite%2Fiam-ssh-agent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buildkite%2Fiam-ssh-agent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/buildkite%2Fiam-ssh-agent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/buildkite","download_url":"https://codeload.github.com/buildkite/iam-ssh-agent/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227166820,"owners_count":17740986,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","aws-sam","continuous-integration","iam","serverless","ssh-agent","ssh-keys"],"created_at":"2024-08-09T14:02:59.334Z","updated_at":"2024-11-29T16:31:38.306Z","avatar_url":"https://github.com/buildkite.png","language":"Rust","readme":"\u003e [!IMPORTANT]\n\u003e This package is no longer actively maintained or supported.\n\n# iam-ssh-agent\n\nA replacement ssh-agent that uses the caller's IAM identity to access a list of\npermitted ssh identities.\n\n`iam-ssh-agent` is designed to be used in less trusted continuous integration\nenvironments where you want to use an ssh key to clone source control\nrepositories without providing the raw key material.\n\n`iam-ssh-agent` is split into two components: a binary that binds a unix domain\nsocket with the ssh-agent protocol, and a serverless API that uses API Gateway\nand Lambda functions to answer `list keys` and `sign data` requests.\n\n- [`/agent`](agent) a Rust crate that builds the `iam-ssh-agent`\nbinary.\n- [`/service`](service) an AWS SAM project that deploys the serverless\nbackend for the `iam-ssh-agent` binary.\n\nFor deployment instructions see the [`service` README](service/README.md#deploying).\nOnce you have successfully deployed the service you can\n[add keys](#adding-keys), [grant](#granting-access-to-keys), and\n[test](#testing-access) access.\n\nFor development and testing:\n\n- [`/client`](client) a node package used for testing the ssh-agent\nimplementation and comparing output to other ssh-agent implementations.\n\n## Protocol\n\nBy storing the ssh private keys behind a service boundary, and providing an\nauthenticated, access controlled signing service, client environments can\nauthenticate without access to the ssh private keys.\n\n![](images/protocol.png)\n\n_Generated using https://sequencediagram.org/_\n\n## Agent\n\nThe agent binary should be a near drop-in replacement for existing uses of\nssh-agent, or provide a pathway for you to remove private key material from\ncontinuous integration hosts. It should be installed in the same place you\ncurrently use `ssh`.\n\nThe agent requires an `IAM_SSH_AGENT_BACKEND_URL` environment variable. This URL\nwill be printed following a successful deploy of the [service](#service) and\nwill be formatted like `https://{api-gateway-id}.execute-api.{region}.amazonaws.com/Prod`.\n\nThe agent binary will auto discover IAM credentials in the expected places:\nenvironment variables, EC2 instance metadata, or ECS task metadata. Requests to\nthe API Gateway will be signed with these credentials and the service will\nprovide access to keys listed in the DynamoDB Permissions table for the caller's\nIAM entity.\n\nThe agent can be installed from the Debian packages attached to the\n[GitHub releases](https://github.com/buildkite/iam-ssh-agent/releases) or\nusing `cargo` to build the binary yourself. It is also published to Docker hub\nas [buildkite/iam-ssh-agent](https://hub.docker.com/r/buildkite/iam-ssh-agent).\n\n## Service\n\nAn API Gateway is configured to forward requests to two lambdas: ListIdentities\nand GetSignature. The `iam-ssh-agent` never sees the raw key material, it can\nonly ask for a list of available keys or a signature for a key it has access\nto.\n\nKeys are stored in AWS Systems Manager Parameter Store where the private keys\ncan be encrypted with a KMS key. Key permissions are stored in a DynamoDB table\nkeyed by [IAM Entity Unique ID](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-unique-ids).\n\nSee the [deploying guide](service/README.md#deploying) for instructions on how\nto deploy the service in your AWS Organization.\n\n### Adding Keys\n\nYou can add keys to AWS Systems Manager Parameter Store using the AWS CLI or\nConsole. Public and private keys are stored in separate parameters. The\nListIdentities lambda has access to the public key parameters and the\nGetSignature lambda has access to both.\n\nYou can use any hierarchy to store your public and private keys in SSM so long\nas the parameter paths end in `key.pub` and `key` respectively.\n\nExample key hierarchies:\n\n```\n# GitHub repository deploy key\n/github/keithduncan/iam-ssh-agent/key.pub\n/github/keithduncan/iam-ssh-agent/key\n\n# Machine user key\n/github/machine-user1/key.pub\n/github/machine-user1/key\n\n# GitLab Global Key\n/gitlab.company.com/global/name/key.pub\n/gitlab.company.com/global/name/key\n```\n\nThe GetSignature lambda IAM role includes a policy that permits `kms:Decrypt`\nusing the `aws/ssm` KMS key. You can store your ssh private keys in a\n`SecureString` parameter encrypted with that key to prevent unintended access to\nthe raw key material. Deploying this service to a unique AWS account also\nhelps limit access to the key material.\n\nTo generate and store an ssh key pair in the Parameter Store:\n\n```\n# Don't enter a passphrase, the private key will be encrypted using a KMS key\n$ ssh-keygen -f test-key\n\n# Store the keys in Parameter Store\n$ aws ssm put-parameter \\\n  --name /github/username/repository/key.pub \\\n  --type String \\\n  --value \"$(\u003ctest-key.pub)\"\n$ aws ssm put-parameter \\\n  --name /github/username/repository/key \\\n  --type SecureString \\\n  --value \"$(\u003ctest-key)\"\n$ aws ssm get-parameter \\\n  --name /github/username/repository/key \\\n  --output text \\\n  --query 'Parameter.ARN'\n```\n\nOnce you have added both keys to the Parameter Store, and submitted\nthe public key to the service you want to access, delete the key\nfiles from your file system. When adding public keys to a service,\ngive the key a descriptive name like the Parameter ARN printed by\nthe last command.\n\n### Granting Access to Keys\n\nOnce you have [added the keys](#adding-keys) to the parameter store, you can\ngrant IAM entities access to those keys.\n\nUse the AWS CLI to look up the Unique ID for the IAM entity that you will be\ngranting access to. The Unique IDs are not exposed in the AWS Console. For\nroles, use `get-role` and copy the `RoleId` for use in the next step:\n\n```\naws iam get-role --role-name MyRole\n{\n    \"Role\": {\n        \"Path\": \"/\",\n        \"RoleName\": \"MyRole\",\n        \"RoleId\": \"AROAXXXXXXXXXXXXXXXXX\",\n        \"Arn\": \"arn:aws:iam::{account}:role/MyRole\",\n        \"CreateDate\": \"2020-01-22T10:52:29Z\",\n        \"AssumeRolePolicyDocument\": {\n            \"Version\": \"2008-10-17\",\n            \"Statement\": [\n                {\n                    \"Effect\": \"Allow\",\n                    \"Principal\": {\n                        \"Service\": \"ecs-tasks.amazonaws.com\"\n                    },\n                    \"Action\": \"sts:AssumeRole\"\n                },\n                {\n                    \"Effect\": \"Allow\",\n                    \"Principal\": {\n                        \"AWS\": \"arn:aws:iam::{account}:root\"\n                    },\n                    \"Action\": \"sts:AssumeRole\"\n                }\n            ]\n        },\n        \"Description\": \"\",\n        \"MaxSessionDuration\": 3600,\n        \"RoleLastUsed\": {\n            \"LastUsedDate\": \"2020-03-04T04:38:00Z\",\n            \"Region\": \"us-east-1\"\n        }\n    }\n}\n```\n\nSee [IAM Identifiers Unique ID](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-unique-ids)\nfor more details on IAM Unique IDs.\n\nOnce you have the Unique ID for the entity, you can add a permission to the\nDynamoDB permissions table. You can add an item using the AWS Console or CLI:\n\n```\naws dynamodb update-item \\\n--table-name MyPermissionsTableName \\\n--key '{\"IamEntityUniqueId\":{\"S\":\"AROAXXXXXXXXXXXXXXXXX\"}}' \\\n--update-expression 'ADD #p :keys' \\\n--expression-attribute-names '{\"#p\":\"Parameters\"}' \\\n--expression-attribute-values '{\":keys\":{\"SS\":[\"/github/machine-user1/key\"]}}'\n```\n\n#### Multiple Keys\n\nAn ssh-agent with multiple keys can cause issues when authenticating to source\ncontrol services. If an IAM entity with access to multiple keys connects to\n`github.com` the ssh server will accept the first ssh key offered and\n_authenticate_ successfully but may fail _authorization_ when attempting to\nclone a repository if the key doesn't have access.\n\nInstead of using multiple keys, consider using a GitHub machine user or a GitLab\nGlobal Deploy key which allow a single key to access multiple repositories.\n\n### Granting Access to the API Gateway\n\nHow you grant access to the API Gateway to `list keys` and `sign data` is\ndetermined by whether you deployed the service to the same account as your\ncalling IAM entities or to a different account.\n\nWhen using an API Gateway deployed to the same account, you don't have to provide\nexplicit access in the IAM entity policies, the API Gateway Resource Policy is\nsufficient to permit access. An explicit deny in an IAM Policy is of course\nrespected.\n\nWhen using an API Gateway deployed to a different account (cross account IAM\naccess) you must configure access in both accounts. The API Gateway's resource\npolicy must include the calling account’s account ID (or source VPC / VPC Endpoint\nif using a Private endpoint), and the calling account’s IAM entity must be granted\nan explicit allow with an IAM Policy Statement:\n\n```json\n{\n    \"Statement\": [\n        {\n            \"Action\": \"execute-api:Invoke\",\n            \"Resource\": \"arn:aws:execute-api:{region}:{account-id}:{api-gateway-id}/Prod/*/*\",\n            \"Effect\": \"Allow\"\n        }\n    ]\n}\n```\n\nSee the [API Gateway Authorization Flow](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-authorization-flow.html)\ndocumentation for more details.\n\n### Private Endpoint Configuration\n\nWhen configuring the API Gateway with `EndpointConfiguration: PRIVATE` some\nadditional configuration may be necessary.\n\nFirst, configure a VPC Endpoint for `execute-api` in the VPC from which you want\nto access the API Gateway. Ensure the VPC Endpoint security groups allow inbound\nnetwork traffic from the security group that the `iam-ssh-agent` binary will\nexecute in.\n\nIf Private DNS is enabled, all `execute-api` requests from this VPC will be\nrouted via this endpoint. If this is not appropriate for your VPC, you can\ndisable Private DNS, [associate the API Gateway with the VPC Endpoint](https://docs.aws.amazon.com/apigateway/latest/developerguide/associate-private-api-with-vpc-endpoint.html)\n, and use the Route 53 alias DNS name for your private API gateway instead.\n\n`iam-ssh-agent` supports connecting to Private DNS Names or a Route 53 alias. It\ndoes not support Endpoint-Specific Public DNS Hostnames. See\n[How to Invoke a Private API](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-private-api-test-invoke-url.html) for details on these options.\n\nTo restrict access from inside your VPC to the `iam-ssh-agent` backend, you can\ncustomise the API Gateway Resource Policy (ensure you redeploy the\nAPI after any changes) or set a [VPC Endpoint Policy](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-vpc-endpoint-policies.html).\n\nSee the [AWS Private API troubleshooting guide](https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-private-endpoint-connection/)\nfor more tips on troubleshooting access to Private API Gateways.\n\n## Testing Access\n\nThis project can be used to grant [Buildkite](https://buildkite.com) agents\n[running on ECS](https://github.com/buildkite/on-demand) permission to use\nssh private keys to clone private source code repositories. The same pattern is\nalso applicable to init system managed virtual machines on EC2, or\nKubernetes pods on EKS.\n\nTo use the `iam-ssh-agent` service in ECS Tasks, add a\n[buildkite/iam-ssh-agent](https://hub.docker.com/r/buildkite/iam-ssh-agent)\nsidecar container to your task definition with a bind mount volume to expose the\nunix domain socket bound by `iam-ssh-agent` to the Buildkite agent container\nwhich invokes `ssh` to clone a repository.\n\nTo ensure the `iam-ssh-agent` container has booted before attempting to clone,\nthe main container uses a container dependency `DependsOn: [{\"Condition\": \"HEALTHY\", \"ContainerName\": \"ssh-agent\"}]`\ncondition to wait for the ssh-agent to boot and become healthy before starting,\nand the sidecar container defines a healthcheck which uses\n[busybox](https://www.busybox.net) to verify the socket has been bound.\n\nThe example task definition below prints the keys the task has access\nto, based on the task IAM role that was passed when scheduling the ECS task.\nThis task definition can be useful for diagnosing `ssh` access issues and\nconfirming that your ECS Task Role has access to the keys you expect. Ensure you\noverride the Task Role when scheduling this task, otherwise it won't have access\nto any keys.\n\n```yaml\nSshTaskDefinition:\n  Type: AWS::ECS::TaskDefinition\n  Properties:\n    Family: ssh-example\n    ContainerDefinitions:\n      - Name: agent\n        EntryPoint:\n          - /bin/sh\n          - -c\n        Command:\n          - ssh-add -L; ssh -vvvvT git@github.com\n        Essential: true\n        Image: buildkite/agent:3\n        LogConfiguration:\n          LogDriver: awslogs\n          Options:\n            awslogs-region: !Ref AWS::Region\n            awslogs-group: /aws/ecs/ssh\n            awslogs-stream-prefix: ecs\n        Environment:\n          - Name: SSH_AUTH_SOCK\n            Value: /ssh/socket\n        DependsOn:\n          - Condition: HEALTHY\n            ContainerName: ssh-agent\n        MountPoints:\n          - ContainerPath: /ssh\n            SourceVolume: ssh-agent\n      - Name: ssh-agent\n        Command:\n          - /usr/bin/iam-ssh-agent\n          - daemon\n          - --bind-to=/ssh/socket\n        Essential: true\n        Image: buildkite/iam-ssh-agent:latest\n        Environment:\n          - Name: IAM_SSH_AGENT_BACKEND_URL\n            Value: !Ref YourIamSshAgentBackendUrlHere\n        LogConfiguration:\n          LogDriver: awslogs\n          Options:\n            awslogs-region: !Ref AWS::Region\n            awslogs-group: /aws/ecs/ssh\n            awslogs-stream-prefix: ecs\n        HealthCheck:\n          Command:\n            - /bin/busybox\n            - test\n            - -S\n            - /ssh/socket\n        MountPoints:\n          - ContainerPath: /ssh\n            SourceVolume: ssh-agent\n    Cpu: 256\n    Memory: 512\n    NetworkMode: awsvpc\n    ExecutionRoleArn: !ImportValue agent-scheduler-ECSTaskExecutionRoleArn\n    RequiresCompatibilities:\n      - FARGATE\n    Volumes:\n      - Name: ssh-agent\n```\n\nIn my personal AWS Organization, the `iam-ssh-agent` service is deployed to a\nseparate AWS account. My ECS task role has a policy to explicitly grant access\nto the API Gateway:\n\n```yaml\nProjectRole:\n  Type: AWS::IAM::Role\n  Properties:\n    Path: /BuildkiteAgentTask/\n    RoleName: ProjectName\n    AssumeRolePolicyDocument:\n      Statement:\n      - Effect: Allow\n        Principal:\n          Service: [ecs-tasks.amazonaws.com]\n        Action: ['sts:AssumeRole']\n    Policies:\n      - PolicyName: SshAgentApi\n        PolicyDocument:\n          Statement:\n            - Effect: Allow\n              Action: execute-api:Invoke\n              Resource:\n                !Ref YourIamSshAgentApiGatewayArnHere\n```\n\nFor more details on running Buildkite agents on-demand with ECS see my\n[agent-scheduler](https://github.com/buildkite/on-demand/tree/master/agent-scheduler)\nproject.\n","funding_links":[],"categories":["Rust"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbuildkite%2Fiam-ssh-agent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbuildkite%2Fiam-ssh-agent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbuildkite%2Fiam-ssh-agent/lists"}