“Before going deeper into kernel-level work, I went back to Linux fundamentals. This is what runs under every K8s cluster and eBPF probe — owning it isn’t optional.”

This series is a deliberate return to Linux fundamentals before going deeper into kernel-level work — CentOS, user management, SSH hardening, permissions. The stuff that runs silently under every K8s cluster and eBPF probe. Owning it isn’t optional.

Four problems this week: non-interactive user creation, temporary account expiry, disabling root SSH across multiple servers, and file permission management. All documented here as a reference.


Day 1: Non-Interactive Users#

The Task#

Create a user with a non-interactive shell — standard pattern for service accounts and system users that should never have a login session.

sudo useradd -s /usr/sbin/nologin username
sudo usermod -s /usr/sbin/nologin username

# Verify
getent passwd username

Expected output:

username:x:1001:1001::/home/username:/usr/sbin/nologin

The Failure Mode#

Ran all of this on the wrong host. Spent time wondering why validation wasn’t passing before checking hostname. Always verify your target host before running privileged commands — hostname before anything else.

# First command in any lab session
hostname

# Then connect to the right target
ssh benny@<server-ip>

Diagnostic Reference#

# Verify shell assignment
getent passwd username

# List all non-interactive accounts
grep nologin /etc/passwd

# Check current host before running anything
hostname
whoami

Day 2: Temporary Users with Account Expiry#

The Task#

Create a user with an expiration date — standard pattern for contractor access, temporary credentials, or time-bounded permissions.

ssh benny@<server-ip>

sudo useradd anita -e 2023-06-05

# Verify
id anita
chage -l anita

Expected output:

Account expires: Jun 05, 2023

useradd vs adduser#

useradd is the low-level utility — explicit flags, no interactive prompts, scriptable. adduser is a higher-level wrapper that prompts interactively. For automation, always use useradd.

Scheduled Account Locking#

# Lock an account after a time window using 'at'
echo "usermod --lock username" | at now + 20 minutes

Useful for temporary access that needs to expire automatically without relying on a human to remember to revoke it. In production, use a proper secrets manager or PAM configuration instead — at jobs don’t survive reboots and have no audit trail.

Diagnostic Reference#

# Full account expiry details
chage -l username

# List all accounts with expiry dates set
awk -F: '$8 != "" {print $1, $8}' /etc/shadow

# Manually expire an account immediately
sudo chage -E 0 username

# Unlock
sudo usermod --unlock username

Day 3: Disabling Root SSH Across Multiple Servers#

The Task#

Disable root login across all servers in the Stratos Datacenter. Direct root SSH is a basic hardening requirement — it removes the universal target account from remote attack surface and forces audit trails through named accounts with sudo.

The Manual Approach#

sudo vi /etc/ssh/sshd_config
# PermitRootLogin no → uncomment and set

sudo systemctl restart sshd

The Problem#

Multiple servers. Manual approach doesn’t scale and introduces inconsistency — one missed server, one typo, and you have an uneven security posture with no visibility into which hosts are actually hardened.

The Automated Approach#

for server in stapp01 stapp02 stapp03; do
    ssh $server "sudo sed -i 's/#PermitRootLogin no/PermitRootLogin no/' /etc/ssh/sshd_config && sudo systemctl restart sshd"
done

Consistent across all targets, auditable, repeatable. The right answer for anything that needs to be applied to a fleet.

Verify#

# Confirm the setting on each server
for server in stapp01 stapp02 stapp03; do
    echo "$server:"
    ssh $server "grep PermitRootLogin /etc/ssh/sshd_config"
done

# Test that root login is actually blocked
ssh root@stapp01
# Should return: Permission denied, please try again

Diagnostic Reference#

# Check SSH config for root login setting
grep -i permitrootlogin /etc/ssh/sshd_config

# View SSH service status and recent auth logs
sudo systemctl status sshd
sudo journalctl -u sshd -n 50

# Check auth log for failed root login attempts
sudo grep "Failed.*root" /var/log/secure

Day 4: File Permissions#

The Task#

Make /tmp/xfusioncorp.sh executable for all users on App Server 1.

ssh app1@<server-ip>

chmod 755 /tmp/xfusioncorp.sh

# Verify
ls -l /tmp/xfusioncorp.sh

Expected output:

-rwxr-xr-x 1 root root 156 Dec 15 10:23 /tmp/xfusioncorp.sh

Permission Reference#

ModeOctalBinaryMeaning
Owner7111read, write, execute
Group5101read, execute
Others5101read, execute

755 is the standard for executable scripts that everyone needs to run but only the owner should modify.

Common patterns:

# Read-only for everyone
chmod 444 file

# Owner full, others read-only
chmod 644 file

# Executable for everyone, owner-writable
chmod 755 script.sh

# Private — owner only
chmod 600 secret

# Directory with standard access
chmod 755 /path/to/dir

Diagnostic Reference#

# Check permissions
ls -l filename
stat filename

# Find files with dangerous permissions
find /tmp -perm -o+w -type f

# Full path permission audit
namei -l /tmp/xfusioncorp.sh

What I’d Do Differently#

  • Root SSH hardening: Use Ansible from the start instead of a manual loop. The loop works but has no idempotency guarantees — Ansible’s lineinfile module handles the sshd_config edit cleanly and is repeatable without side effects.
  • Temporary users: Don’t use at for account expiry in production. Use chage -E with a proper date and back it up with monitoring that alerts when accounts aren’t expired on schedule.
  • User creation: For any fleet larger than a few servers, use a directory service (LDAP, FreeIPA) instead of local user management. Local accounts don’t scale and create audit gaps.

Diagnostic Reference#

# Always first — verify your target host
hostname
whoami

# User management
getent passwd username
chage -l username
grep nologin /etc/passwd

# SSH hardening
grep -i permitrootlogin /etc/ssh/sshd_config
sudo journalctl -u sshd -n 50
sudo grep "Failed.*root" /var/log/secure

# Permissions
ls -l filename
stat filename
namei -l /path/to/file
find /tmp -perm -o+w -type f

Tags#

#Linux #Infrastructure #Security #SysAdmin #Automation


About the Author#

Elijah Udom (elijahu) is an Infrastructure & Cloud Engineer based in Lagos, Nigeria. AWS, Kubernetes, eBPF security, AI/ML infrastructure. Building in the open.


← Previous: The Quest for A+ TLS | Next: Days 5-8 →