The offboarding checklist nobody has: IP allowlists and SSH keys

What to remove when a developer leaves, in what order, and the one list you need to have before they do.

· offboarding, access, ip-allowlist, ssh

The part the standard checklist misses

The offboarding checklist most teams have covers email, single sign-on and the laptop. HR owns it, IT runs it, and it works for the things it names.

It does not name the firewall rule for the developer’s home IP. Or the authorized_keys entry on the bastion host. Or the deploy key they created for a side script three years ago, the read-only database user they made for a dashboard, or the cloud access key that is still in their name because it was quicker than setting up a role. Those stay. Not for weeks, for years, because nobody can say which ones were theirs.

This guide is the checklist for that part. It is short, and the hard work is not the checklist itself. It is the list you need to have before the person hands in their notice.

Why you can’t do this on the day

If the first time you ask “which IPs and keys are theirs” is their last day, you will be guessing. You will scroll through a firewall rule table with forty entries and labels like office-2, tmp and dev, and you will have two bad options: leave the ones you aren’t sure about, or remove them and find out next Monday what broke.

Most teams leave them. That is the rational choice on the day, and it is how an allowlist ends up with rules for people who left in 2021.

The real work is maintaining the list beforehand, when there is no pressure and the person who added the entry is still around to say what it is for. Offboarding then becomes a lookup: filter the list by their name, and remove what comes back.

The list you need in advance

For each of the following, you want to know who it belongs to, what it is for, which environment it applies to, and when it was added:

  • Each IP address on an allowlist. Office, home, VPN exit, CI runner, the vendor who needs access to one endpoint.
  • Each SSH public key accepted anywhere. Bastion, application hosts, the git server, a managed key service if you use one.
  • Each API key and deploy key. Including the ones created for a script, a dashboard, a one-off import.
  • Each cloud credential. IAM users, access keys, service accounts someone created in their own name.

Today this usually lives in one of three places. A wiki page that was accurate when it was written and has not been touched since. A spreadsheet that one person maintains and nobody else knows about. Or nowhere at all: the cloud console’s rule table is the list, and the label column is the documentation.

Each of those fails the same way. The list is updated when someone remembers, not when the entry is created, so it drifts. Within a year it is a list of what used to be true, and on offboarding day nobody trusts it enough to act on it.

What works is making the entry and the record the same step. When someone adds an IP or a key, the owner, purpose and environment are captured at the same time, by the person adding it, because that is the only moment they are certain of the answers. Where you keep it matters less than that one rule.

The checklist

In order. Each row has what to check, where to look, and the thing teams most often miss.

Before the last day

Step What to check Where to look Common miss
Pull their entries Filter your list by their name. Note each IP, key, credential and environment. Your registry, spreadsheet or wiki; failing that, each console Entries they added under a generic label, or under a shared account
Schedule the removals Decide what goes on the day and what waits for a quiet window. The list from the step above Removing something customer-facing on a Friday afternoon
Find what only they understand Any shared system, script or key where they are the only one who knows how it works. Ask them directly; check who last touched the runbooks Treating this as a knowledge-transfer problem only, when it is also an access one

On the day

Step What to check Where to look Common miss
1. SSO and identity provider Disable the account. This ends most sessions and blocks most logins at once. Your identity provider’s admin console Apps that were connected with a local password instead of SSO
2. Cloud IAM users and access keys Delete or disable their IAM user and each access key in their name. Cloud console, IAM section; check each account or project separately A second account or project you forgot exists; keys on a shared “ci” user they created
3. SSH keys on hosts Remove their public key from each host that accepts it. authorized_keys on each host, the bastion, any managed-key service, the git server Hosts not under configuration management; a key added by hand “temporarily”
4. Firewall and allowlist rules Remove each rule for their IPs: home, mobile, any personal VPN exit. Cloud firewall or security group rules, the database allowlist, the admin panel’s IP restriction Their IP sitting in a rule that also lists other people’s, so the rule can’t just be deleted
5. VPN Revoke their VPN certificate or account. The VPN server or service A VPN config file copied to a second device
6. Third-party API keys Rotate or revoke keys issued in their name at vendors: payment, email, monitoring, DNS. Each vendor’s dashboard, filtered by user Vendors where they were the only admin, so nobody else can see the key list
7. Deploy keys and CI tokens Remove deploy keys and personal access tokens they created; check CI variables. Repository settings, CI project settings, the token list on their git account A deploy key on a repository nobody deploys from any more, still valid
8. Shared secrets they knew Rotate anything shared whose value they had: database passwords, service keys, the root password in the password manager. Your rotation runbooks Skipping this because “they wouldn’t do anything”, which is not the point

After

Step What to check Where to look Common miss
Confirm nothing broke Check the services, jobs and dashboards that were near anything you removed. Monitoring, the CI history, anyone who complains A nightly job that fails quietly because it used their key
Record what was removed Write the list of what you removed and when, against their name. The ticket, or your registry’s history Not recording it, so the next person can’t tell what was done
Close the ticket Mark the offboarding done, with the record attached. Wherever the ticket lives Closing it on the day, before the re-check
Re-check a week later Look for anything that reappeared: an IP re-added by automation, a key restored from a backup or an image. The same places as steps 2, 3 and 4 above Configuration management putting the key back because the source was not updated

The order on the day is deliberate. Identity first, because it cuts off the most with one action. Cloud credentials second, because they are the broadest access that survives an SSO cut. Then hosts, then the network rules that would have let them reach those hosts, then the long tail of vendor keys and tokens. Shared secrets last, because rotation takes longer and you want the direct access gone first.

The shared-secret problem

Everything above is a removal: the entry is theirs, so it goes. Shared secrets are different. If the person knew the password to anything shared, the database, a service account, the shared admin login for a vendor, then removing them from the team does nothing to that password. It is now a rotation, not a removal.

This is where offboarding gets expensive, and where teams quietly skip steps. The fix is upstream: the fewer shared secrets the team has, the shorter this part of the checklist. Where a shared secret is unavoidable, it needs a rotation runbook already written, so that on offboarding day the step is “run the runbook” rather than “work out how to rotate this”. The guide on how often to rotate SSH keys has a template for that runbook.

Removing a rule you can’t place

At some point you will find an allowlist entry or a key that is probably theirs but might not be. No label, no record, added around when they joined. The fear of deleting it and breaking something is what keeps old entries alive for years.

Disable rather than delete. Most firewalls, security groups and SSH setups let you turn a rule off without removing it: comment out the authorized_keys line, disable the rule, set the key to inactive. Leave it disabled for two weeks. If nothing breaks and nobody asks, delete it. If something does break, you re-enable it in a minute and now you know what it is for, which you write down.

Two weeks covers a monthly job twice and the person who was on holiday. Make the disabled entries a line in the ticket so they don’t become the next unlabelled mystery.

Template: the checklist

Copy this into the offboarding ticket. One row per entry you found; add rows as needed.

# Access offboarding: <name>

**Last day:** <YYYY-MM-DD>
**Run by:** <who>
**Ticket:** <link>

## Entries found (from the registry, before the last day)

| Type          | Entry                 | Environment | Purpose         | Added     | Action | Done |
| ------------- | --------------------- | ----------- | --------------- | --------- | ------ | ---- |
| IP            | <x.x.x.x>             | <prod>      | <home>          | <YYYY-MM> | remove | [ ]  |
| SSH key       | <SHA256:… or comment> | <bastion>   | <laptop>        | <YYYY-MM> | remove | [ ]  |
| Cloud key     | <key id / user>       | <prod>      | <CI, personal>  | <YYYY-MM> | remove | [ ]  |
| API key       | <vendor, key name>    | <prod>      | <monitoring>    | <YYYY-MM> | revoke | [ ]  |
| Deploy key    | <repo, key name>      | <prod>      | <deploy script> | <YYYY-MM> | remove | [ ]  |
| Shared secret | <what>                | <prod>      | <db password>   | <YYYY-MM> | rotate | [ ]  |

## Day of, in order

- [ ] 1. SSO / identity provider account disabled
- [ ] 2. Cloud IAM users and access keys removed (accounts checked: <list>)
- [ ] 3. SSH keys removed from hosts (hosts checked: <list>)
- [ ] 4. Firewall / allowlist rules removed (rules: <list>)
- [ ] 5. VPN access revoked
- [ ] 6. Third-party API keys in their name revoked (vendors: <list>)
- [ ] 7. Deploy keys and CI tokens removed (repos: <list>)
- [ ] 8. Shared secrets they knew rotated (runbooks run: <list>)

## Disabled, not deleted (delete after <YYYY-MM-DD>)

- <entry>: <where>, <why unsure>

## After

- [ ] Services and jobs near the removals checked, nothing broken
- [ ] This record saved against <name>
- [ ] Re-check scheduled for <YYYY-MM-DD>
- [ ] Re-check done, nothing reappeared

Where to start

Do a dry run with a current team member’s entries. Pick someone, set a timer, and try to list each IP, key and credential that is theirs across your environments. If you can do it in ten minutes, your list is in good shape and the checklist above will work as written. If you can’t, that is the gap, and it is a better use of the next afternoon than anything on this page.