We are your Digital Ally™
(Mostly) read-only devcontainers
AI

(Mostly) read-only devcontainers

Running agents inside devcontainers make sense from security perspective. But how to balance power of agents with security concenrns?

Stanislav MiklikOctober 12, 2026

We have already discussed that we preffer to run coding assistants inside devcontainers. But let's looks more in details how does it can look like.

Usefull tools

Each developer has its own tech-stack but here I would like to present my opinions and maybe few tricks that are applicable more generally.

Github CLI

Almost every developer will benefit agent able to use gh CLI tool. CI/CD actions can fail for multiple reasons like tests are flanky or just that CI tests are first to test against "real" services.

Adding it is easy as we can use devcontainer's features

  "features": {
    "ghcr.io/devcontainers/features/github-cli:1": {}
  },
  // forward token to container
  "remoteEnv": {
    "GH_TOKEN": "${localEnv:NS_GH_TOKEN}"
  },

Best is to create fine-grained personal access tokens and on your host machine to have env variable per project (NS_ prefix in our example) for multiple reasons:

  1. Least priviledge - you want to give agent only permissions that make sense for your case. I default to read-only permissions but you can add also some write permissions to specific github repo

  2. Not reusing same "variable" name for more project lower the chance that you (or your agent - we use our own skill for dev container setup) just copy setting and you accidentially share tokens you didn't intended. Simple solution is to use project prefix for variable on your host machine.

  3. Sometimes you just need different tokens for different organizations or you just work also on your personal repositories.

Docker

Even though our final deployment goes to AWS, it is usefull to be able run the code also locally. E.g. we are using Aurora DSQL but locally we just use Postgres in docker - which are mostly compatible but DSQL has some limitations we need to verify in our CI/CD.

Using it is just another devcontainer feature ghcr.io/devcontainers/features/docker-in-docker:3. Since it is running inside, you need to take care about data - which might be lost if you rebuild devcontainer.

Easiest trick is to mount data to gitignored .data directory - which lives together with your code on host machine but other alternatives are possible. You can start Postgres alongside your devcontainer (if you don't like docker in docker approach) but I strongly believe agents can do usefull stuff even with ad-hoc containers.

AWS CLI

AWS CLI is also available as a feature ghcr.io/devcontainers/features/aws-cli:1. Credentials are the trickier part.

The tempting shortcut is to mount your ~/.aws directory into the container. Don't. If you use AWS Organizations with IAM Identity Center (SSO), the cached SSO token there can get credentials for every permission set you have - including admin. The agent should have its own identity, not borrow yours.

My setup is a dedicated IAM user created directly in the project's AWS account (usually dev):

  1. IAM user has read-only permissions (see below) attached directly.
  2. Access keys are passed to the container the same way as GH_TOKEN - via per-project env variable on host machine.
  "features": {
    "ghcr.io/devcontainers/features/aws-cli:1": {}
  },
  "containerEnv": {
    "AWS_REGION": "eu-central-1"
  },
  "remoteEnv": {
    "AWS_ACCESS_KEY_ID": "${localEnv:NS_AWS_KEY_ID}",
    "AWS_SECRET_ACCESS_KEY": "${localEnv:NS_AWS_SECRET}"
  },

That's it - no config file, no profiles. Every aws call (including the agent's) runs as the read-only user. The account itself is a natural boundary - the key can't reach anything outside of it - and in CloudTrail you can easily filter what the agent did.

What does "read-only" actually mean?

The obvious choice is the AWS managed ReadOnlyAccess policy. Unfortunately it is read-only in a sense of "cannot change anything", not "cannot see anything sensitive". Among others it allows:

ActionWhat it leaks
s3:GetObjectcontent of every bucket
ssm:GetParameter*SecureString parameters (default aws/ssm key allows decryption via SSM)
lambda:GetFunctionenvironment variables (often secrets) and code download URL
ecs:DescribeTaskDefinitionenvironment variables
ec2:DescribeInstanceAttributeuser data, often with bootstrap secrets
dynamodb:Scan, athena:*your application data

Much better starting point is ViewOnlyAccess - it covers metadata only (List/Describe), which is enough to answer "what is deployed and how is it configured". On top of that I add CloudWatch Logs and metrics, because reading logs is the main reason I want the agent in AWS at all. Be aware that logs are only as safe as what your applications log - if you log tokens or PII, the agent will see them. If you want, restrict logs to specific log groups (e.g. /aws/lambda/myproject-*).

Finally, it is good an explicit Deny for the most sensitive actions as a safety net, so later policy changes can't open them by accident. Notice also ce:* - Cost Explorer API costs $0.01 per request and agent in a loop can surprise you. Similarly Logs Insights queries are charged per scanned GB.

IAM user permissions (ViewOnlyAccess + CloudWatch Logs)

Attach AWS managed policy arn:aws:iam::aws:policy/job-function/ViewOnlyAccess to the IAM user and add following inline policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadCloudWatchLogs",
      "Effect": "Allow",
      "Action": [
        "logs:Describe*",
        "logs:List*",
        "logs:Get*",
        "logs:FilterLogEvents",
        "logs:StartQuery",
        "logs:StopQuery",
        "logs:StartLiveTail",
        "logs:StopLiveTail"
      ],
      "Resource": "*"
    },
    {
      "Sid": "ReadCloudWatchMetrics",
      "Effect": "Allow",
      "Action": [
        "cloudwatch:Describe*",
        "cloudwatch:List*",
        "cloudwatch:GetMetricData",
        "cloudwatch:GetMetricStatistics"
      ],
      "Resource": "*"
    },
    {
      "Sid": "DenySensitiveReads",
      "Effect": "Deny",
      "Action": [
        "secretsmanager:GetSecretValue",
        "ssm:GetParameter",
        "ssm:GetParameters",
        "ssm:GetParametersByPath",
        "kms:Decrypt",
        "lambda:GetFunction",
        "ec2:GetPasswordData",
        "ec2:GetConsoleOutput",
        "ec2:GetConsoleScreenshot",
        "logs:Unmask",
        "iam:CreateAccessKey",
        "sts:GetFederationToken",
        "ce:*"
      ],
      "Resource": "*"
    }
  ]
}

logs:Unmask is denied so that if you use CloudWatch Logs data protection, masked values stay masked for the agent.

One more thing to keep in mind: an agent with internet access and read access is a potential exfiltration channel (think prompt injection). That's the main reason why "metadata + logs" is a much safer default than "read everything".

Conclusion

Giving an agent access to tools (or skills) can make developer experience even better (why to copy&paste error from CI when agent can read it). Mostly I preffer read-only access but in some cases it makes sense to give agent also write access. I even logged in temporarily with aws login inside dev container and ask agent to do something.

With properly limited permissions one can better navigate through sea where security incidents can come as unexpected wave or even iceberg.