We had a Linux server that showed two successful SSH logins from an external IP overnight.
The strange part was that there were no failed password attempts before them.
The attacker got in cleanly.
So I checked the SSH logs properly.
Both sessions said:
Accepted publickey
That meant we were not dealing with a guessed password.
I pulled the fingerprint from the logs and compared it with the keys in:
/home/deploy/.ssh/authorized_keys
There was only one key there.
And it matched.
The comment on the key was:
deploy
@old-build
That name sounded familiar, so we checked our records.
The build server it belonged to had been retired months ago.
But nobody had removed its SSH key from this production server.
I checked the timestamp on authorized_keys.
The key had been sitting there since November 2025.
So this was not some new persistence the attacker had just created.
It was an old trust relationship we had forgotten about.
That was actually worse in a different way.
I checked the login history and confirmed both sessions came from the same external IP.
Then I looked for files touched during those sessions.
A backup copy of the application’s .env file had been created under /opt/api/.
That immediately raised another concern because environment files often contain things like database credentials, API keys and application secrets.
At that point we disabled the deployment account, removed the stale key, rotated the application secrets and started checking every other server that might still trust the same old build key.
The lesson from this one was simple.
Not every compromise starts with malware.
Sometimes the attacker just finds a credential or key that your own environment forgot existed.
Passwords expire.
Employees leave.
Servers get retired.
But SSH keys can quietly sit in authorized_keys for years if nobody is actually reviewing them.
That is why key rotation and access reviews matter.
The attacker did not need to break SSH.
We had already left the door open.