Perform an initial Ubuntu Server setup and security hardening
Create a named administrator, prove key-based recovery access, patch the host, restrict SSH, enable a host firewall, and verify time and exposed services.
Turn a fresh Ubuntu Server installation into a recoverable administration baseline before deploying public workloads.
- Ubuntu Server 24.04 LTS
- OpenSSH 9.x
- Console access Keep provider, hypervisor, or physical console access until a second SSH session succeeds after every access-control change.
- Administrator key Generate an Ed25519 key on the workstation and protect its private half with a passphrase.
ssh-keygen -lf ~/.ssh/id_ed25519.pub - Network inventory Record the management source networks and every service that must remain reachable.
ip address show && ip route show - Recovery snapshot Take a provider snapshot or file-level backup before upgrading packages or changing remote access.
OneLiners never runs these steps or stores secrets. Review placeholders, versions, current state, and change-control requirements before using a command.
Full guide
What you will build
- A patched Ubuntu Server 24.04 baseline with a named, attributable administrator instead of routine use of the provider bootstrap or root account.
- A tested key-only OpenSSH policy introduced through a local drop-in while an independent console and the original session remain available.
- A default-deny UFW policy permitting SSH only from the declared management network, plus scheduled security updates, synchronized time, and an exposure inventory.
- A fresh SSH connection authenticates as the named administrator with an Ed25519 key and can use sudo, while root, password, and keyboard-interactive logins are rejected.
- Current packages are installed, any required reboot is deliberate, and unattended security updates have an observable schedule and reviewed origin policy.
- Only reviewed listeners are reachable through the host firewall; time, services, sockets, firewall state, and recovery access have concrete acceptance evidence.
Architecture
How the parts fit together
The server retains provider or physical console recovery while access moves from a bootstrap identity to a named administrator. OpenSSH reads a small local drop-in after packaged defaults, UFW enforces inbound deny with a source-restricted management exception, and Ubuntu timers maintain security updates. Each access change is validated locally and then proven through a new remote connection before the earlier path is retired.
- Capture the untouched host state and prove console recovery, DNS, routes, repository access, capacity, and the intended management source.
- Apply signed Ubuntu updates, complete any controlled reboot, and repeat service and network checks.
- Create a named administrator, install only the public key, and prove login plus sudo from a second terminal.
- Write and validate a narrow SSH drop-in, reload the service, and prove both allowed key access and rejected password access.
- Add the source-restricted SSH rule before enabling default-deny inbound policy, then test the intended path.
- Enable security-update scheduling and close with time, service, socket, firewall, and fresh-login evidence.
Assumptions
- The target is a fresh or fully inventoried Ubuntu Server 24.04 LTS host, not a production machine whose undocumented listeners can be removed casually.
- A provider console, hypervisor console, serial console, or physical keyboard remains usable throughout SSH and firewall changes.
- The workstation holds the administrator private key; only its public half reaches the server, and the host-key fingerprint is verified out of band.
- The management CIDR is stable and narrow. Dynamic addresses, VPNs, bastions, and IPv6 need an equivalent reviewed access design.
- Official or explicitly approved repositories are reachable, and the maintenance window permits a reboot if the kernel or core libraries require one.
Key concepts
- Bootstrap account
- The provider-created or installer-created identity used only long enough to establish attributable administrative access.
- OpenSSH drop-in
- A small file under sshd_config.d that changes selected settings while keeping packaged defaults maintainable.
- Effective configuration
- The final sshd policy after defaults, includes, and Match rules are evaluated; `sshd -T` displays it.
- Default deny
- A firewall stance in which inbound traffic is blocked unless an explicit reviewed rule permits it.
- Management plane
- The restricted path used by administrators, distinct from public application ingress.
- Unattended upgrade
- Ubuntu's scheduled mechanism for applying eligible security updates from approved origins.
Before you copy
Values used in this guide
{{adminUser}}Named personal or role administrator account used for attribution.
Example: adminops{{bootstrapUser}}Existing account whose reviewed authorized_keys supplies the initial public key.
Example: ubuntu{{serverAddress}}Stable server address whose SSH host-key fingerprint has been verified.
Example: server01.example.net{{managementCidr}}Narrow IPv4 or IPv6 administrator source network permitted to reach SSH.
Example: 198.51.100.32/28Security and production boundaries
- Never paste a private SSH key into the server, guide, shell history, ticket, or chat. The server needs only the public key.
- Disabling passwords and root login does not compensate for stolen keys, weak workstations, excessive sudo rights, or unverified host keys.
- The generic UFW OpenSSH application profile commonly permits every source; this guide expresses the real management boundary with an explicit CIDR.
- Treat authorized_keys, sudo policy, sshd drop-ins, repository definitions, and unattended-upgrades origins as security-sensitive configuration.
- Review IPv4 and IPv6 together. A narrow IPv4 rule does not secure an independently reachable IPv6 listener.
Stop before continuing if
- Stop before changing SSH if no independent console works or the host-key fingerprint cannot be verified.
- Do not disable an authentication method until a fresh second session proves the replacement key and sudo path.
- Do not enable UFW until the real management source and every required listener have been inventoried.
- Stop package work on signature, dependency, storage, or interrupted-dpkg errors; never bypass repository verification.
- Do not close commissioning while failed services, unsynchronized time, unexplained listeners, or a required reboot remain.
verification
Capture the starting state
Record the release, kernel, addresses, routes, listening sockets, firewall state, failed services, and pending updates before making changes.
Why this step matters
Approve the starting baseline provides a repeatable evidence checkpoint so later changes are compared against observed state instead of assumptions.
What to understand
Save release, kernel, addresses, routes, process-owned sockets, firewall status, failed units, free storage, and pending packages before making a change.
Run listener inspection with sudo because unprivileged output can omit process ownership and make an unexplained socket appear harmless.
Confirm console login, the management source observed through SSH_CONNECTION, DNS, and repository reachability.
System changes
- No intended persistent change; reads the current host state for verification.
Syntax explained
ip -brief address- Produces a compact interface and address inventory for later comparison.
ss -lntup- Lists listening TCP and UDP sockets with numeric endpoints and owning processes.
systemctl --failed- Displays units currently in failed state.
apt list --upgradable- Shows packages whose configured repositories offer newer candidates.
cat /etc/os-release && uname -r && ip -brief address && ip route && sudo ss -lntup && sudo ufw status verbose && systemctl --failed --no-pager && apt list --upgradablePRETTY_NAME="Ubuntu 24.04 LTS" State: inactive 0 loaded units listed. Listing... Done
Checkpoint: Approve the starting baseline
Continue whenUbuntu 24.04, intended addresses and routes, known listeners, and no unexplained failed unit are recorded.
Stop whenAny unexplained service, listener, route, identity, synchronization, or access failure remains.
If this step fails
The evidence is incomplete or differs between repeated runs.
Likely causeInsufficient privilege, a changing network/service, name resolution, or an unstable host is affecting observation.
date --iso-8601=secondshostnamectlsystemctl --failed --no-pager
ResolutionUse the documented privilege level, stabilize the host, and capture a second complete sample before proceeding.
Security notes
- Redact public addresses, usernames, repository names, and process details before sharing evidence externally.
Alternatives
- Capture equivalent signed evidence through approved configuration management or asset inventory.
Stop conditions
- Do not accept a checkpoint whose expected state cannot be explained.
command
Install current Ubuntu updates
Refresh official package metadata, apply available updates, and check whether the host needs a controlled reboot.
Why this step matters
Commissioning on current signed packages removes already-fixed vulnerabilities and avoids debugging later configuration against an obsolete kernel, OpenSSH, or package database.
What to understand
`apt update` verifies current repository metadata; signature and Release-file failures are blockers, not warnings to bypass.
`full-upgrade` may change dependencies. Review its plan on sensitive hosts and never interrupt dpkg while it writes package state.
If reboot-required exists, reboot under change control while console access is present, then repeat listener, route, and failed-unit checks.
System changes
- Updates installed packages, may restart services or install a kernel, and can require a controlled reboot.
Syntax explained
apt update- Downloads and verifies current indexes from configured repositories.
apt full-upgrade- Applies upgrades while allowing required dependency changes.
--yes- Accepts the already reviewed package plan non-interactively.
/var/run/reboot-required- Ubuntu marker that a reboot is required to load updated components.
sudo apt update && sudo apt full-upgrade --yes && if test -f /var/run/reboot-required; then cat /var/run/reboot-required; else echo 'No reboot required'; fi0 upgraded, 0 newly installed, 0 to remove. No reboot required
Checkpoint: Prove the patched boot
test ! -f /var/run/reboot-required && apt list --upgradable 2>/dev/null && systemctl --failed --no-pagerContinue whenNo required reboot remains, no unexpected upgrade is pending, and no service failed after patching.
Stop whenAPT reports unauthenticated repositories, dependency errors, insufficient space, interrupted dpkg, or failed services.
If this step fails
dpkg reports that it was interrupted.
Likely causeA prior package operation ended before configuration completed.
ps -ef | grep -E '[a]pt|[d]pkg'sudo dpkg --audit
ResolutionConfirm no package process is active, complete `sudo dpkg --configure -a`, repair dependencies explicitly, and rerun the update.
Security notes
- Never disable signature verification or add an unauthenticated repository to make patching succeed.
Alternatives
- Use an approved staged repository mirror and maintenance orchestrator that preserves Ubuntu signature verification.
Stop conditions
- Do not continue while package state or a required reboot makes later observations unreliable.
command
Create a named sudo administrator
Use an individual account for attribution. Set a strong local password even when routine access will use SSH keys.
Why this step matters
A named account makes actions attributable and lets the shared bootstrap path be retired without enabling routine remote root login.
What to understand
`adduser` creates the home and local password interactively; keep a strong console/sudo recovery password even after SSH passwords are disabled.
Ubuntu's sudo group grants broad privilege through packaged policy. Use separate accounts and remove departing operators promptly.
Verify UID, home, login shell, group list, and effective sudo policy before installing credentials.
System changes
- Creates a local user, home directory, password hash, primary group, and sudo-group membership.
Syntax explained
adduser- Uses Ubuntu's account-creation wrapper to establish a complete local user.
usermod --append --groups sudo- Adds sudo membership without replacing other supplementary groups.
id- Displays UID and resulting group membership.
Values stay on this page and are never sent or saved.
sudo adduser {{adminUser}} && sudo usermod --append --groups sudo {{adminUser}} && id {{adminUser}}uid=1001(adminops) gid=1001(adminops) groups=1001(adminops),27(sudo)
Checkpoint: Verify the named administrator
getent passwd {{adminUser}} && id {{adminUser}} && sudo -l -U {{adminUser}}Continue whenThe intended home and shell exist, sudo membership is present, and policy is understood.
Stop whenThe account collides, UID/home is unexpected, or sudo policy is broader than intended.
If this step fails
The user exists but sudo denies commands.
Likely causeGroup membership was not applied, an override changes policy, or the session predates the group change.
id adminopssudo -l -U adminopssudo visudo -c
ResolutionCorrect membership or validated sudo policy from bootstrap access, start a fresh login, and retest.
Security notes
- Prefer one account per human; shared administrator names weaken attribution even when keys differ.
Alternatives
- Integrate approved central identity while retaining a tested local break-glass administrator.
Stop conditions
- Do not retire bootstrap access until the new account succeeds remotely and through console recovery.
command
Install the administrator public key
Copy only a public key into the new account and enforce ownership and permissions accepted by OpenSSH.
Why this step matters
Installing only a verified public key provides cryptographic access without transferring the private credential to the server.
What to understand
Inspect the bootstrap authorized_keys first; it may include provider, rescue, or automation identities that should not be inherited.
Explicit ownership and 0700/0600 modes satisfy OpenSSH StrictModes and prevent unrelated users from modifying authorization.
Compare the installed fingerprint with the workstation key over an independent channel.
System changes
- Creates the administrator `.ssh` directory and authorized_keys with restrictive ownership and permissions.
Syntax explained
install -d -m 0700- Creates an owner-only directory.
-o user -g group- Sets ownership directly.
install -m 0600- Copies the key file as owner read/write only.
Values stay on this page and are never sent or saved.
sudo install -d -m 0700 -o {{adminUser}} -g {{adminUser}} /home/{{adminUser}}/.ssh && sudo install -m 0600 -o {{adminUser}} -g {{adminUser}} /home/{{bootstrapUser}}/.ssh/authorized_keys /home/{{adminUser}}/.ssh/authorized_keys-rw------- 1 adminops adminops 99 /home/adminops/.ssh/authorized_keys
Checkpoint: Match the installed key
sudo ssh-keygen -lf /home/{{adminUser}}/.ssh/authorized_keys && sudo namei -l /home/{{adminUser}}/.ssh/authorized_keysContinue whenThe workstation fingerprint appears and every path component has safe ownership and mode.
Stop whenAn unknown key, private-key header, unsafe permission, or fingerprint mismatch appears.
If this step fails
The source file contains several public keys.
Likely causeProvider automation or rescue identities share bootstrap authorization.
sudo ssh-keygen -lf /home/ubuntu/.ssh/authorized_keys
ResolutionCreate the new file from only the reviewed public-key line instead of copying the complete set.
Security notes
- A public-key comment is only a label; the fingerprint is the cryptographic identity.
Alternatives
- Use `ssh-copy-id` from the workstation before disabling password access, then inspect server ownership and fingerprint.
Stop conditions
- Never continue if private-key material or an unrecognized public key appears.
verification
Prove the new login in a second terminal
Keep the original session open. Confirm the key login and a sudo prompt before disabling any authentication method.
Why this step matters
A fresh second connection proves routing, host identity, key authentication, account policy, and sudo together before fallback access is reduced.
What to understand
Keep the original session and console open; commands in an old shell do not prove a new login works.
Verify the server host-key fingerprint rather than automatically accepting an unexpected key.
`sudo -v` exercises authentication and `sudo whoami` confirms the privileged identity.
System changes
- May refresh the sudo timestamp; otherwise performs identity and access checks without persistent configuration changes.
Syntax explained
ssh user@host- Creates an independent session using the selected account and server identity.
sudo -v- Validates sudo credentials without running an administrative workload.
sudo whoami- Confirms the effective privileged identity.
Values stay on this page and are never sent or saved.
ssh {{adminUser}}@{{serverAddress}} 'id && sudo -v && sudo whoami'uid=1001(adminops) gid=1001(adminops) groups=1001(adminops),27(sudo) root
Checkpoint: Keep two proven access paths
ssh {{adminUser}}@{{serverAddress}} 'id && sudo -v && sudo whoami'Continue whenThe new connection uses the named account and prints root only for the sudo command.
Stop whenHost identity is unexpected, key login fails, sudo fails, or only the original session remains.
If this step fails
The client offers the wrong key.
Likely causeAn agent holds many identities or client configuration selects another IdentityFile.
ssh -G adminops@server01.example.net | grep -i identityfilessh-add -l
ResolutionTest with IdentitiesOnly and an explicit key, then create a reviewed local Host block.
Security notes
- Never bypass an unexpected host-key warning merely because the user key appears correct.
Alternatives
- Use a second trusted workstation or bastion when policy requires independent access paths.
Stop conditions
- Do not change sshd authentication while fresh login or sudo proof is incomplete.
config
Add a minimal OpenSSH hardening drop-in
Disable direct root and password login only after the new key-based administrator has been proven. Preserve the main vendor-managed configuration.
Why this step matters
A small reviewable drop-in disables high-risk remote methods while preserving packaged configuration and a simple rollback.
What to understand
PermitRootLogin requires named authentication before privilege escalation.
PasswordAuthentication and KbdInteractiveAuthentication close different password-like paths; leaving either enabled can defeat key-only intent.
Inspect `sshd -T -C` because includes and Match blocks may alter policy for a specific connection.
System changes
- Creates `/etc/ssh/sshd_config.d/60-oneliners-hardening.conf`; the daemon is unchanged until reload.
Syntax explained
PermitRootLogin no- Rejects direct remote root authentication.
PasswordAuthentication no- Disables SSH password authentication.
KbdInteractiveAuthentication no- Disables keyboard-interactive authentication.
PubkeyAuthentication yes- Keeps public-key authentication enabled.
/etc/ssh/sshd_config.d/60-oneliners-hardening.confprintf 'PermitRootLogin no\nPasswordAuthentication no\nKbdInteractiveAuthentication no\nPubkeyAuthentication yes\n' | sudo tee /etc/ssh/sshd_config.d/60-oneliners-hardening.confPermitRootLogin no PasswordAuthentication no KbdInteractiveAuthentication no PubkeyAuthentication yes
Checkpoint: Validate proposed SSH policy
sudo sshd -t && sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication) 'Continue whenSyntax passes and effective root/password/keyboard-interactive access is disabled while public keys remain enabled.
Stop whenValidation errors, a Match rule changes intent, or the second session is unavailable.
If this step fails
`sshd -t` reports a bad option.
Likely causeA directive is misspelled, malformed, unsupported, or placed in an invalid context.
sudo sshd -tsudo nl -ba /etc/ssh/sshd_config.d/60-oneliners-hardening.conf
ResolutionCorrect or remove the drop-in from retained access and do not reload until syntax and effective policy pass.
Security notes
- Key-only login shifts risk to workstation and private-key custody; interactive keys should use passphrases.
Alternatives
- Add a tested second factor where approved PAM and recovery design support it.
Stop conditions
- Do not reload when effective policy differs from review.
warning
Validate and reload SSH without closing recovery access
A syntax error can stop new logins. Test the effective configuration, reload rather than stop the service, and open another fresh session.
Why this step matters
Validation followed by reload applies new policy to future connections without intentionally terminating established recovery sessions.
What to understand
Run `sshd -t` immediately before reload so the exact on-disk files are checked.
Reload Ubuntu's `ssh` unit rather than stopping it; retain console and the old session.
Prove allowed key login and rejected password login from a genuinely new client process.
System changes
- Reloads OpenSSH so new connections use the hardening drop-in; established sessions should remain active.
Syntax explained
sshd -t- Parses configuration and host keys without starting another daemon.
systemctl reload ssh- Requests a configuration reload through Ubuntu's service unit.
sshd -T- Prints effective server configuration after includes and defaults.
sudo sshd -t && sudo systemctl reload ssh && sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication) 'permitrootlogin no pubkeyauthentication yes passwordauthentication no kbdinteractiveauthentication no
Checkpoint: Prove allow and deny behavior
ssh {{adminUser}}@{{serverAddress}} 'echo key-login-ok'Continue whenA new key login succeeds; a separately forced password-only attempt is rejected.
Stop whenKey login fails, password/root login succeeds, or the service reports an error.
If this step fails
Reload fails after an earlier syntax pass.
Likely causeFiles changed between checks, a host key is missing, or the service has another runtime error.
sudo systemctl status ssh --no-pagersudo journalctl -u ssh --since '10 minutes ago' --no-pagersudo sshd -t
ResolutionRestore the drop-in through retained access, validate, reload, and repeat fresh-login tests.
Security notes
- Do not submit real passwords repeatedly during negative tests; force the method and confirm rejection.
Alternatives
- Test unusually complex Match policy with a temporary console-only sshd instance first.
Stop conditions
- Keep recovery sessions open until a new connection succeeds after reload.
warning
Enable a default-deny host firewall
Preview a source-restricted SSH rule, add the exact management access required, then enable UFW while the console and original SSH session remain available.
Why this step matters
Applying a source-restricted SSH exception before default deny limits exposure while avoiding lockout from enabling a firewall without an accurate management rule.
What to understand
Use the server-observed stable management CIDR rather than a guessed workstation address or broad Anywhere profile.
Review dry-run output and add the exact TCP/22 source rule before enabling UFW; application rules belong with service deployments.
Compare provider filtering and IPv6 reachability because UFW is only one layer.
System changes
- Adds the SSH rule, sets inbound deny and outbound allow defaults, enables UFW, and programs active firewall state.
Syntax explained
--dry-run- Prints generated policy without applying it.
allow from CIDR- Restricts acceptance to the declared source network.
port 22 proto tcp- Limits the exception to the SSH TCP listener.
default deny incoming- Blocks inbound traffic without an explicit allow rule.
Values stay on this page and are never sent or saved.
sudo ufw --dry-run allow from {{managementCidr}} to any port 22 proto tcp && sudo ufw allow from {{managementCidr}} to any port 22 proto tcp && sudo ufw default deny incoming && sudo ufw default allow outgoing && sudo ufw enable && sudo ufw status verboseStatus: active Default: deny (incoming), allow (outgoing) 22/tcp ALLOW IN 198.51.100.32/28
Checkpoint: Verify the management boundary
sudo ufw status numbered && ssh {{adminUser}}@{{serverAddress}} 'echo firewall-access-ok'Continue whenUFW is active with one narrow SSH rule and a fresh permitted connection succeeds.
Stop whenDry-run is surprising, the source is uncertain, required traffic is omitted, or login fails.
If this step fails
The client times out despite an apparent allow rule.
Likely causeUpstream filtering, routing, address-family mismatch, nonstandard port, or the wrong observed source blocks traffic.
sudo ufw status numberedsudo ss -lntp | grep sshdsudo journalctl -k --since '10 minutes ago' --no-pager
ResolutionUse console access to correct the smallest relevant layer and remove any broad emergency rule after proof.
Security notes
- UFW does not replace provider filtering, authentication, patching, or private management networking.
Alternatives
- Prepare and peer-review rules first, then enable them during a console-backed maintenance window.
Stop conditions
- Do not enable UFW without console access and a tested rule for the real management source.
command
Verify unattended security updates
Ubuntu 24.04 normally includes unattended-upgrades. Enable its timer and perform a dry run so failures are visible before relying on it.
Why this step matters
A verified unattended-upgrades setup reduces exposure between maintenance windows while keeping origins, exclusions, reboots, and failures observable.
What to understand
Package installation alone does not prove APT periodic configuration, timer state, or approved origins.
A dry run explains eligible packages without installing them; third-party repositories are not automatically authorized.
Choose explicit reboot and notification policy because an unexpected reboot can be operationally damaging.
System changes
- Installs unattended-upgrades when absent and enables the apt-daily-upgrade timer; dry-run does not install upgrades.
Syntax explained
systemctl enable --now- Persists and starts the timer schedule.
--dry-run- Evaluates eligible upgrades without installing them.
--debug- Shows origin, package, and policy decisions.
sudo apt install --yes unattended-upgrades && sudo systemctl enable --now apt-daily-upgrade.timer && sudo unattended-upgrade --dry-run --debugPackages that will be upgraded: 0 apt-daily-upgrade.timer enabled
Checkpoint: Prove automatic maintenance
systemctl is-enabled apt-daily-upgrade.timer && systemctl list-timers apt-daily-upgrade.timer --all && sudo unattended-upgrade --dry-run --debugContinue whenThe timer has a next run and dry-run completes using only approved origins.
Stop whenThe timer is masked, origins are unexpected, package state is broken, or reboot policy is unapproved.
If this step fails
A third-party package is ignored.
Likely causeIts repository origin is not in Allowed-Origins or Origins-Pattern.
apt-cache policysudo unattended-upgrade --dry-run --debug
ResolutionApprove a narrow origin pattern or maintain that vendor through a separate staged process.
Security notes
- Never broaden allowed origins with wildcards merely to silence a warning.
Alternatives
- Use centralized staged maintenance with canaries and explicit reboot windows.
Stop conditions
- Do not rely on automation until dry-run and timer evidence both pass.
verification
Verify synchronized time and exposed services
Accurate time is required for logs, certificates, and distributed systems. Investigate every listening socket rather than assuming it is intentional.
Why this step matters
Accurate time and an explained listener inventory are prerequisites for trustworthy logs, certificates, monitoring, and incident reconstruction.
What to understand
Confirm synchronization and the active NTP service; a plausible wall clock does not prove ongoing synchronization.
Every listener needs an owner, bind address, purpose, authentication, firewall rule, monitoring, and patch responsibility.
Investigate failed, masked, or repeatedly restarting units even when the main application is reachable.
System changes
- No intended persistent change; reads the current host state for verification.
Syntax explained
timedatectl status- Reports timezone, synchronization, and selected network-time service.
systemctl --failed- Lists units currently in failed state.
ss -lntup- Displays process-owned TCP and UDP listeners.
timedatectl status && systemctl --failed --no-pager && sudo ss -lntupSystem clock synchronized: yes NTP service: active 0 loaded units listed. LISTEN 0 4096 0.0.0.0:22
Checkpoint: Accept the commissioned baseline
Continue whenTime is synchronized, no unit is failed, every listener is documented, and UFW permits only approved flows.
Stop whenAny unexplained service, listener, route, identity, synchronization, or access failure remains.
If this step fails
The evidence is incomplete or differs between repeated runs.
Likely causeInsufficient privilege, a changing network/service, name resolution, or an unstable host is affecting observation.
date --iso-8601=secondshostnamectlsystemctl --failed --no-pager
ResolutionUse the documented privilege level, stabilize the host, and capture a second complete sample before proceeding.
Security notes
- Redact public addresses, usernames, repository names, and process details before sharing evidence externally.
Alternatives
- Capture equivalent signed evidence through approved configuration management or asset inventory.
Stop conditions
- Do not accept a checkpoint whose expected state cannot be explained.
Finish line
Verification checklist
ssh {{adminUser}}@{{serverAddress}} 'sudo true && echo access-ok'Returns access-ok from a new connection.sudo sshd -T | grep -E '^(permitrootlogin|passwordauthentication|pubkeyauthentication) 'Root and password login are disabled; public-key login is enabled.sudo ufw status verbose && timedatectl status && systemctl --failed --no-pagerFirewall is active, time is synchronized, and no service is failed.Recovery guidance
Common problems and safe checks
The named administrator receives `Permission denied (publickey)`.
Likely causeThe wrong key was copied, ownership or modes violate StrictModes, the home path differs, or a Match rule changes AuthorizedKeysFile.
sudo namei -l /home/adminops/.ssh/authorized_keyssudo sshd -T -C user=adminops,host=server01.example.net,addr=198.51.100.35 | grep authorizedkeysfilesudo journalctl -u ssh --since '10 minutes ago' --no-pager
ResolutionUse console or retained bootstrap access to install the intended public key with correct ownership and modes, then test a new verbose client connection.
A new connection times out after UFW is enabled.
Likely causeThe client address is outside the CIDR, SSH uses another port, upstream filtering differs, or only one address family was covered.
sudo ufw status numberedsudo ss -lntp | grep sshdip route get 198.51.100.35
ResolutionFrom console, add the smallest correct source and port rule or temporarily disable UFW, prove the corrected rule, then remove emergency access.
Unattended-upgrades reports held, unauthenticated, or disallowed-origin packages.
Likely causeA third-party repository, pin, package hold, broken dependency, or origin pattern is outside the approved policy.
apt-cache policyapt-mark showholdsudo grep -R --line-number 'Allowed-Origins\|Origins-Pattern' /etc/apt/apt.conf.d
ResolutionCorrect repository trust and explicit origin policy; maintain unsupported third-party packages through a separate tested process.
The clock is not synchronized or certificates appear not yet valid.
Likely causeThe selected NTP service is inactive, UDP/123 or DNS is blocked, or multiple time daemons conflict.
timedatectl timesync-statussystemctl status systemd-timesyncd --no-pagerjournalctl -u systemd-timesyncd --since '30 minutes ago' --no-pager
ResolutionChoose one approved time service, restore its network path, wait for synchronization, and reassess logs and certificates.
Reference
Frequently asked questions
Should the SSH port be changed from 22?
A non-default port can reduce scan noise but is not an authentication control. Keys, source restriction, host-key verification, rate controls, and patching matter more.
Should I disable the root account?
Disable direct remote root login, retain a tested sudo and console recovery path, and follow the platform recovery model instead of deleting access blindly.
Does UFW replace a cloud firewall?
No. Provider and host filtering are complementary boundaries. Keep their intent aligned and test both layers.
Recovery
Rollback
Recover access from the still-open SSH session or console, then revert one control at a time.
- Remove /etc/ssh/sshd_config.d/60-oneliners-hardening.conf, run sshd -t, and reload ssh if the new account cannot authenticate.
- Disable UFW from the console or delete only the blocking rule if required management traffic was omitted.
- Keep the named administrator and authorized key until another tested recovery account exists.
- Review package history in /var/log/apt/history.log before downgrading; do not blindly reverse security updates.
Evidence