DEV Community

Paramanand Mallik
Paramanand Mallik

Posted on

I built an AWS access recertification engine that actually enforces the decision

The access you revoked in your last review is probably still live.

I know how that sounds, but it is how most access recertification actually works. A tool generates a list, an owner clicks approve or revoke, the cycle gets marked complete, and then nothing touches the real resources. The review produces a record. The permissions stay exactly where they were. You attested to a state that was never made true.

That gap bothered me for a long time, so I built something to close it and open-sourced it on AWS's aws-samples org. It is called VIGIL.

The core idea

A normal review answers one question: should this access still exist? The owner says no, a ticket gets filed, and maybe someone actions it next quarter. Between the decision and the change, the risk just sits there.

I wanted the decision and the change to be the same step. So VIGIL does four things:

  1. It discovers resources by their owner tag and works out who actually has access to each one.
  2. It asks the owner to keep, trim, or remove that access.
  3. It applies that decision on the live resource.
  4. It records what happened in a way you can later prove.

The part I care about most: scoped enforcement

The lazy way to revoke someone's access to one bucket is to detach their policies. That nukes their access to everything, and it is how you cause an incident while trying to improve security.

VIGIL never does that. If the access came from a bucket policy, it removes just that principal, or just the specific actions, from that bucket's policy. If the access came from the principal's own IAM policy, it adds a resource-scoped explicit Deny instead of touching shared policy, so nothing else the principal can do is affected. In practice it can remove only s3:PutObject for one principal on one bucket and leave everything else alone.

If a change cannot be made safely and narrowly, it raises a ticket instead of guessing. I would rather it do nothing than do something broad.

Making enforcement durable

Enforcement is not a synchronous call inside the API. The API validates the decision, records it, and drops it on an SQS queue. A separate worker picks it up, is idempotent, retries on failure, and dead-letters anything it cannot complete. So the API stays fast, and a change is never lost halfway through.

Evidence you can hand to an auditor

Every decision and every change is written to an append-only trail where each record is hash-linked to the one before it, so tampering is detectable. You can optionally mirror that into an S3 Object Lock (WORM) bucket so the records cannot be altered or deleted for a retention period. And because the engine snapshots the before-state, any change can be rolled back.

When an auditor asks what changed and whether it actually held, that is the answer, not a spreadsheet someone signed.

Extending it

Connectors for S3 buckets, IAM users, IAM roles, and EC2 instances ship today. A connector is just four methods: snapshot, revoke, modify, rollback. Adding RDS, SNS, SQS, or Secrets Manager is one connector, not a change to the engine.

It is fully serverless: Lambda, SQS, DynamoDB, Cognito, SES, and API Gateway, with a documented REST API so you can put your own UI in front of it.

Try it

VIGIL is built to a production specification: durable and idempotent enforcement on SQS with a dead letter queue, scoped changes that never over-revoke, least-privilege IAM per function, hash-chained evidence with optional WORM storage, rollback, and a passing test suite. The repo ships an AWS SAM template, a quick start, a developer guide, and a guide for writing your own connector:

https://github.com/aws-samples/sample-identity-recertification-vigil

Clone it, point it at your owner-tagged resources, and run a cycle. As with any security tooling you bring into your environment, review it against your own standards before production. Feedback and connector contributions are very welcome.

Top comments (0)