SSH Key Management for Teams: Ansible, Vault, Rotation

When a team grows, access keys spread across servers and people, and no one knows who has what left. We set up a unified lifecycle for issuing, storing, and revoking keys turnkey: access is granted by role, revoked in minutes, and every action is logged. The access window shrinks from weeks to minutes, and rotation stops being manual work.

Our competencies:

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1365
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1330
  • B2B Advance company logo design
    B2B Advance company logo design
    776
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1077
  • AIDER company logo development
    AIDER company logo development
    1022
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1113

Why SSH key management breaks as the team grows

While there are a dozen servers and five engineers, access lives in personal directories and chat threads. A key is copied to a new machine, the old one remains in the allowlist, and no one can name the exact number of people who are currently accessing production servers.

After that, SSH key management turns into a manual ritual. Uncontrolled access issuance happens via chat: a public key is inserted into the config manually, a terminated employee remains in the list because no one deleted their line.

Forgotten public keys on servers accumulate for years — first from test environments, then from contractors, then from people who no longer work at the company. The inventory diverges from reality: some hosts have access, others do not.

Manual key rotation is postponed: it means a nightly work stoppage and the risk of breaking a release deployment. The longer it is postponed, the wider the window in which a compromised key remains valid.

Server access auditing is impossible in such a scheme — there is no source of truth showing who has access, why it was issued, and who approved it.

The root of the problem is not SSH, but the lack of a single source of truth and issuance automation. As long as keys are distributed manually, any check remains a one-off walkthrough, and rotation is an emergency measure.

We explain how to link issuance to engineer roles, move public keys into a managed registry, and make rotation planned — with Ansible and Vault.

What a unified access issuance and revocation loop provides

As long as access is issued manually, a key lives longer than the task it was issued for. An employee leaves, a contractor closes a project, but the private key remains valid — and no one can say which hosts it is registered on.

A unified loop changes this at the mechanical level: a key is issued for a term and removed with one operation, not by walking machines.

The loop keeps an inventory of accesses: who received what, to which hosts, until what date. Issuance comes down to one command — the Ansible role distributes the public part to target machines and removes manual edits on servers.

Access issuance time drops from hours of chat to minutes, and the steps for a new engineer and for a contractor stop diverging.

Access revocation is also one operation: the record is removed from the inventory, and the host configuration is brought to a state without that key. Previously, such edits went in a wave manually across every server, and on some machines the forgotten line continued to live.

A contractor's access window is set in advance and closes itself by calendar, not by someone's email saying "we're done." The key lifetime is tied to the task: when it expires, access stops working across the entire fleet, including machines no one remembered.

This eliminates a whole class of access failures — dangling keys of terminated employees and contractors, copies of private keys, access left over after a role change.

The share of manual operations per access drops to a few instead of a dozen steps: an engineer describes intent rather than editing files on servers.

Approaches to storing and issuing access keys

The method of issuing access determines what you will have to do when an engineer leaves, a machine is compromised, or a server is decommissioned.

There are exactly four options, and they differ not in convenience, but in where the private key is stored and who is responsible for revoking it.

Option When it fits Limitations
authorized_keys files on servers A fleet of several machines, a team of up to five people Keys spread as copies, revocation is a manual walkthrough of all hosts
A single secrets store You need one source of truth and a log of who received what The same static keys remain on servers, rotation is a separate task
Distribution via configuration management tools The fleet is already described declaratively, the host composition is known The secret passes through a repository or variables, the descriptions themselves need protection
Short-lived certificates The team is growing, access is issued for hours and per task A trusted certificate authority and its verification at connection time are required

Centralized access issuance is not "deploy a store," but linking a person's account, role, and key lifetime. Then revocation becomes one operation instead of searching for a file across dozens of servers, and rotation stops being a rush job.

In practice, the options are combined: a store and configuration management tools cover issuance, certificates cover access lifetime.

Implementation stages: from inventory to handover

The order of work is the reverse of the usual one: first we find out which accesses actually exist, and only then do we change anything.

Keys are rarely created by one person — they accumulate from tasks, experiments, and departed employees, so the real picture almost always diverges from the documentation.

  1. Access inventory. We collect all entry points: authorized_keys on servers, keys in deployment configurations, on engineers' machines, and in build systems.

    For each, we determine the owner, purpose, and date of last login — anything not confirmed by activity goes for decommissioning.

  2. Access role scheme. A role describes a task, not a person: a set of servers, lifetime, and privilege level. Access to production is not inherited from the test environment automatically.

  3. Key issuance setup. Credentials are issued by the secrets store, not by a colleague via messenger. A key receives an expiration date and is revoked automatically upon termination or role change — no need to walk servers manually.

  4. Pilot loop. We change the scheme on one group of servers and see how it lives in daily work: who could not log in, which services lacked permissions, how scheduled revocation goes.

  5. Migrating the remaining systems. We connect loops one by one, leave old keys during the transition, then remove them from authorized_keys with a check that there were no logins using them during that interval.

  6. Handover of the operations regulation. We hand the team the access lifecycle: how to request, issue, and revoke, who is responsible, how to handle incidents. We record periodic checks separately — otherwise the scheme degrades back within a couple of months.

Role, access policy, and rotation regulation templates

The result of the work is not a configured node, but a set of artifacts that the team uses to issue and revoke access on its own.

A new engineer gets a key and permissions in one role run, and an employee's departure closes access in minutes rather than days of searching through other people's records. Below is what exactly remains in your repository and documentation.

  • Role and access group descriptions: service owner, on-call, log reader — tied to specific nodes and privilege levels, not an abstract “admin / non-admin.”
  • Access policies: to whom and for what term a key is issued, what counts as a deviation from the template, who approves an exception, and how it is recorded.
  • Key issuance configurations: automation roles and templates that create the pair, distribute the public part to the required nodes, and prevent file and directory permissions from diverging.
  • Rotation regulation: key lifetime for each access class, the procedure for scheduled replacement, windows in which replacement does not break deployment, and rules for unscheduled revocation.
  • Team instructions: step-by-step scenarios — onboard a new engineer, revoke access upon termination, act when key compromise is suspected.
  • Issuance and revocation scheme: the path from request to the appearance of a public key on nodes and to a log entry — so every action can be verified after the fact.
  • Acceptance checklist and reporting: which events are recorded, how to make sure no extra access remains, and by which checks you accept the loop without our involvement.

Case study: access window shrank from weeks to minutes

A product team of 40 engineers came to us: server access was issued manually, and keys lived in chat threads, personal stores, and old work machines. There was no single registry, so when someone left, no one could say how many copies of a key existed. A new employee waited several days for access — sometimes a week, if the approver was away.

First we compiled an inventory: we went through the servers, matched public keys to people, and saw that one engineer accounted for 6–8 copies.

Then we set up a unified access registry and moved issuance to automated Ansible scenarios — a key is created for a specific person, receives an expiration date, and the private part never leaves their work machine.

Accounts and secrets are stored centrally, and permissions are issued by role rather than by copying a file through chats.

Metrics before and after

Onboarding a new engineer dropped from 3–5 business days to 15–20 minutes including permission checks. Closing access after departure — from several weeks to one command: the key is revoked from the registry and stops working on all nodes at once. The number of key copies per person fell from 6–8 to one.

We separately set up rotation: keys are changed on schedule, and this is no longer a manual operation with walking servers. The registry shows who has what access and when it was last used — and that is what regular checks are built on.

What to do if keys are already distributed across servers and people?

The transition period is not a one-time key replacement, but a mode in which old and new keys work in parallel. The migration task is not to break what already works and to get an observable signal showing that old access is no longer needed. As long as there is no such signal, any shutdown turns into a lottery with a risk of downtime.

It all starts with collecting facts, not generating new keys.

From login logs, we export the tuple “fingerprint — account — server — date of last successful login.” Dead keys, keys with no logins within the agreed period, and those that access dozens of machines at once are immediately visible. Then the migration path is chosen.

Migration path When it fits Limitations
Parallel access with a window Downtime is critical, the loop is large A hard closure date is needed, otherwise the transition period drags on indefinitely
Phased, by server groups The fleet is divided by environments and roles Without a role registry, it is lost who went where
Scheduled shutdown A small loop or suspected compromise Highest risk of downtime, an on-call engineer and fast rollback are needed

Reissuing existing keys works like this: the private part never leaves the engineer's machine, the public part is distributed by configuration — Ansible delivers it to servers from the role registry.

Backward compatibility is maintained by the old key remaining in the allowlist as long as there are logins using it.

The risk of downtime is removed by rollback: the old key is returned to servers with one task, and the switch window is scheduled for working hours, when a problem is visible immediately rather than at night.

Transition-period metrics are the share of logins using new keys, the number of old keys remaining, and the forced shutdown date.

Technical concerns: compatibility, rollback, and audit

Centralized key issuance does not require updating the entire fleet in one day.

On nodes with old OpenSSH branches, the standard authorized_keys remains with a key ownership marker, while new hosts are moved to short-lived certificates — both paths work in parallel until the inventory converges.

There is one mandatory condition: managed write access to the sshd configuration and logging of every change.

Below are the concerns engineers most often raise before starting: what happens during a failure, how revocation is confirmed, and how not to mix contractor access with internal teams.

Will the scheme work on old servers?

Yes. For nodes where the configuration cannot be changed, the Ansible role distributes the key with an owner tag and expiration date — the tag later shows whose access resides on the host and when it expires.

Such nodes are marked as legacy in the inventory and enter the migration plan, but do not block the rest of the fleet.

What happens during a failure and what does rollback look like?

Before a change, a copy of the current files and configuration is taken, and the host state is taken from the inventory, not from manual edits. Rollback is re-uploading the previous version and re-verifying: minutes instead of a manual walkthrough.

If the secrets store is unavailable, issuance of new keys stops, already-issued keys continue to work, and access is not broken.

How is it confirmed that access has actually been revoked?

Not by exporting from a panel, but by checking on the hosts themselves: comparing the contents of authorized_keys and active certificates against the current list. The report shows where the key is still accepted; every discrepancy is a task with an owner and a deadline.

How is contractor access separated?

At the role level: contractor teams receive certificates with a short term and only for the agreed loop, while internal teams go through their own roles.

A contractor does not inherit an employee's permissions, and when work is completed their access is terminated with one operation. Who, when, to which hosts, and at whose request received access — all of this remains in the issuance log.

Let's discuss the task and come back with an estimate and plan

To come back with an estimate of the scope of work rather than general wishes, we need baseline infrastructure data. Describe the task in free form — that is enough to start the analysis.

The more complete the picture, the more accurate the implementation plan and the fewer clarification iterations.

  • list of servers and roles: who accesses which machines, where shared accounts remain;
  • how keys are issued now — manually, with scripts, through a shared store, or as a file in a repository;
  • key inventory: how many are in circulation, whether there are orphaned, expired, and duplicate keys;
  • whether a secrets store or centralized issuance already exists and what is stored in it;
  • which systems are in the loop: build agents, bastion hosts, clouds, network equipment;
  • rotation and audit requirements: key lifetime, who should see the access log;
  • limitations: maintenance windows, isolated segments, external contractor access.

On a call with an engineer, we will clarify the access topology, rotation procedure, and places where automation breaks against manual exceptions.

Then we will come back with an implementation plan: stages, order by loop, rollback procedure, and what is required from your side at each step.

Describe the task — we will come back with an estimate of the scope of work and an implementation plan.