mirror of
https://github.com/BagelHole/DevOps-Security-Agent-Skills.git
synced 2026-08-22 12:49:53 +02:00
V2
This commit is contained in:
@@ -9,57 +9,343 @@ metadata:
|
||||
|
||||
# Linux Administration
|
||||
|
||||
Core Linux system administration skills.
|
||||
Core Linux system administration skills for managing production servers, development environments, and infrastructure hosts across Debian/Ubuntu and RHEL/CentOS distributions.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Provisioning and maintaining Linux servers in any environment
|
||||
- Installing, updating, or removing software packages
|
||||
- Managing filesystems, disk usage, and mount points
|
||||
- Investigating runaway processes or high resource consumption
|
||||
- Scheduling recurring tasks with cron or systemd timers
|
||||
- Analyzing system and application logs for troubleshooting
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Root or sudo access on the target system
|
||||
- SSH access configured (see `ssh-configuration` skill)
|
||||
- Familiarity with a terminal text editor (vim, nano)
|
||||
- Package manager available (`apt` on Debian/Ubuntu, `dnf` on RHEL 8+/Fedora)
|
||||
|
||||
## Package Management
|
||||
|
||||
```bash
|
||||
# Debian/Ubuntu
|
||||
apt update && apt upgrade -y
|
||||
apt install nginx
|
||||
apt remove nginx
|
||||
apt autoremove
|
||||
### Debian / Ubuntu (apt)
|
||||
|
||||
# RHEL/CentOS
|
||||
dnf update
|
||||
dnf install nginx
|
||||
```bash
|
||||
# Update package index and upgrade all installed packages
|
||||
apt update && apt upgrade -y
|
||||
|
||||
# Search for a package by keyword
|
||||
apt search nginx
|
||||
|
||||
# Show detailed package info including dependencies
|
||||
apt show nginx
|
||||
|
||||
# Install a specific version of a package
|
||||
apt install nginx=1.24.0-1ubuntu1
|
||||
|
||||
# Install multiple packages in one command
|
||||
apt install -y nginx certbot python3-certbot-nginx
|
||||
|
||||
# Remove a package but keep its config files
|
||||
apt remove nginx
|
||||
|
||||
# Remove a package and purge all config files
|
||||
apt purge nginx
|
||||
|
||||
# Remove unused dependency packages
|
||||
apt autoremove -y
|
||||
|
||||
# List all installed packages
|
||||
dpkg -l | grep nginx
|
||||
|
||||
# Pin a package to prevent automatic upgrades
|
||||
cat <<'EOF' > /etc/apt/preferences.d/pin-nginx
|
||||
Package: nginx
|
||||
Pin: version 1.24.0-1ubuntu1
|
||||
Pin-Priority: 1001
|
||||
EOF
|
||||
|
||||
# Add an external repository (example: Docker CE)
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \
|
||||
> /etc/apt/sources.list.d/docker.list
|
||||
apt update
|
||||
```
|
||||
|
||||
### RHEL / CentOS / Fedora (dnf)
|
||||
|
||||
```bash
|
||||
# Update all packages
|
||||
dnf update -y
|
||||
|
||||
# Search for a package
|
||||
dnf search nginx
|
||||
|
||||
# Show package details
|
||||
dnf info nginx
|
||||
|
||||
# Install a package
|
||||
dnf install -y nginx
|
||||
|
||||
# Install a specific version
|
||||
dnf install nginx-1.24.0-1.el9
|
||||
|
||||
# Remove a package
|
||||
dnf remove nginx
|
||||
|
||||
# List installed packages
|
||||
dnf list installed | grep nginx
|
||||
|
||||
# Enable a module stream (example: Node.js 20)
|
||||
dnf module enable nodejs:20
|
||||
dnf install -y nodejs
|
||||
|
||||
# Add an external repository
|
||||
dnf install -y epel-release
|
||||
|
||||
# View repository list
|
||||
dnf repolist --all
|
||||
|
||||
# Clean cached package data
|
||||
dnf clean all
|
||||
```
|
||||
|
||||
## System Information
|
||||
|
||||
```bash
|
||||
uname -a # Kernel info
|
||||
hostnamectl # System info
|
||||
lscpu # CPU info
|
||||
free -h # Memory usage
|
||||
df -h # Disk usage
|
||||
ip addr # Network interfaces
|
||||
# Kernel and OS release
|
||||
uname -a
|
||||
cat /etc/os-release
|
||||
|
||||
# Hostname and system metadata
|
||||
hostnamectl
|
||||
|
||||
# CPU information
|
||||
lscpu
|
||||
nproc # Number of processing units
|
||||
|
||||
# Memory usage (human-readable)
|
||||
free -h
|
||||
|
||||
# Disk usage summary
|
||||
df -hT # Include filesystem type
|
||||
du -sh /var/log/* # Summarize directory sizes
|
||||
|
||||
# Network interfaces and IP addresses
|
||||
ip addr show
|
||||
ip route show # Routing table
|
||||
|
||||
# Uptime and load average
|
||||
uptime
|
||||
w # Who is logged in and load
|
||||
```
|
||||
|
||||
## Log Management
|
||||
## Filesystem Management
|
||||
|
||||
```bash
|
||||
journalctl -u nginx # Service logs
|
||||
journalctl -f # Follow logs
|
||||
tail -f /var/log/syslog # System logs
|
||||
dmesg # Kernel messages
|
||||
# List block devices and partitions
|
||||
lsblk
|
||||
fdisk -l
|
||||
|
||||
# Create a new ext4 filesystem on a partition
|
||||
mkfs.ext4 /dev/sdb1
|
||||
|
||||
# Mount a filesystem temporarily
|
||||
mount /dev/sdb1 /mnt/data
|
||||
|
||||
# Add a persistent mount via fstab
|
||||
echo '/dev/sdb1 /mnt/data ext4 defaults,noatime 0 2' >> /etc/fstab
|
||||
mount -a # Mount everything in fstab
|
||||
|
||||
# Check and repair a filesystem (unmount first)
|
||||
umount /dev/sdb1
|
||||
fsck.ext4 -y /dev/sdb1
|
||||
|
||||
# Monitor disk I/O in real time
|
||||
iostat -xz 2
|
||||
|
||||
# Find files larger than 100 MB
|
||||
find / -xdev -type f -size +100M -exec ls -lh {} \;
|
||||
|
||||
# Check inode usage (out-of-inodes can mimic out-of-disk)
|
||||
df -i
|
||||
```
|
||||
|
||||
## Process Management
|
||||
|
||||
```bash
|
||||
ps aux | grep nginx
|
||||
top / htop
|
||||
# List all processes with full details
|
||||
ps auxf
|
||||
|
||||
# Interactive process viewer (prefer htop if installed)
|
||||
top
|
||||
htop
|
||||
|
||||
# Find processes by name
|
||||
pgrep -la nginx
|
||||
|
||||
# Show process tree
|
||||
pstree -p
|
||||
|
||||
# Send graceful stop signal (SIGTERM)
|
||||
kill <pid>
|
||||
|
||||
# Force kill an unresponsive process (SIGKILL)
|
||||
kill -9 <pid>
|
||||
pgrep nginx
|
||||
|
||||
# Kill all processes matching a name
|
||||
pkill nginx
|
||||
|
||||
# Show open files for a process
|
||||
lsof -p <pid>
|
||||
|
||||
# Show which process is listening on a port
|
||||
ss -tlnp | grep :80
|
||||
lsof -i :80
|
||||
|
||||
# Run a process immune to hangups (persists after logout)
|
||||
nohup /opt/myapp/start.sh > /var/log/myapp.log 2>&1 &
|
||||
|
||||
# Limit CPU usage of a running process with cgroups v2
|
||||
systemd-run --scope -p CPUQuota=25% --unit=limit-myapp /opt/myapp/start.sh
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
## Cron Job Management
|
||||
|
||||
- Regular updates
|
||||
- Minimal installed packages
|
||||
- Proper file permissions
|
||||
- Log rotation configuration
|
||||
- Automated backups
|
||||
```bash
|
||||
# Edit the current user's crontab
|
||||
crontab -e
|
||||
|
||||
# List current user's cron jobs
|
||||
crontab -l
|
||||
|
||||
# Example crontab entries
|
||||
# ┌───── minute (0-59)
|
||||
# │ ┌───── hour (0-23)
|
||||
# │ │ ┌───── day of month (1-31)
|
||||
# │ │ │ ┌───── month (1-12)
|
||||
# │ │ │ │ ┌───── day of week (0-7, 0 and 7 = Sunday)
|
||||
# * * * * * command
|
||||
|
||||
# Run a backup every day at 2:30 AM
|
||||
30 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
|
||||
|
||||
# Run a cleanup every Sunday at midnight
|
||||
0 0 * * 0 /usr/local/bin/cleanup.sh
|
||||
|
||||
# Run a health check every 5 minutes
|
||||
*/5 * * * * /usr/local/bin/healthcheck.sh
|
||||
|
||||
# Place system-wide cron scripts in drop-in directories
|
||||
ls /etc/cron.daily/
|
||||
ls /etc/cron.weekly/
|
||||
|
||||
# Restrict cron access to specific users
|
||||
echo "deploy" >> /etc/cron.allow
|
||||
```
|
||||
|
||||
## Log Management
|
||||
|
||||
```bash
|
||||
# Follow systemd journal for a specific service
|
||||
journalctl -u nginx -f
|
||||
|
||||
# Show logs since last boot
|
||||
journalctl -b
|
||||
|
||||
# Show logs from a specific time range
|
||||
journalctl --since "2025-01-15 08:00" --until "2025-01-15 12:00"
|
||||
|
||||
# Show only error-level and above
|
||||
journalctl -p err
|
||||
|
||||
# Tail traditional syslog
|
||||
tail -f /var/log/syslog # Debian/Ubuntu
|
||||
tail -f /var/log/messages # RHEL/CentOS
|
||||
|
||||
# Kernel ring buffer messages
|
||||
dmesg -T # Human-readable timestamps
|
||||
dmesg --level=err,warn
|
||||
|
||||
# Check disk usage of log directory
|
||||
du -sh /var/log/*
|
||||
|
||||
# Configure logrotate for a custom application
|
||||
cat <<'EOF' > /etc/logrotate.d/myapp
|
||||
/var/log/myapp/*.log {
|
||||
daily
|
||||
missingok
|
||||
rotate 14
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
create 0640 myapp myapp
|
||||
sharedscripts
|
||||
postrotate
|
||||
systemctl reload myapp > /dev/null 2>&1 || true
|
||||
endscript
|
||||
}
|
||||
EOF
|
||||
|
||||
# Force a logrotate run for testing
|
||||
logrotate -f /etc/logrotate.d/myapp
|
||||
|
||||
# Centralized logging: forward journal to a remote syslog
|
||||
# In /etc/systemd/journal-upload.conf:
|
||||
# URL=http://logserver.example.com:19532
|
||||
```
|
||||
|
||||
## Networking Essentials
|
||||
|
||||
```bash
|
||||
# Test connectivity
|
||||
ping -c 4 8.8.8.8
|
||||
|
||||
# DNS lookup
|
||||
dig example.com
|
||||
nslookup example.com
|
||||
|
||||
# Trace route to host
|
||||
traceroute example.com
|
||||
|
||||
# List listening ports and associated processes
|
||||
ss -tlnp
|
||||
|
||||
# Show active connections
|
||||
ss -tunap
|
||||
|
||||
# Firewall management (UFW on Ubuntu)
|
||||
ufw allow 22/tcp
|
||||
ufw allow 80/tcp
|
||||
ufw allow 443/tcp
|
||||
ufw enable
|
||||
ufw status verbose
|
||||
|
||||
# Firewall management (firewalld on RHEL/CentOS)
|
||||
firewall-cmd --permanent --add-service=http
|
||||
firewall-cmd --permanent --add-service=https
|
||||
firewall-cmd --reload
|
||||
firewall-cmd --list-all
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Diagnostic Command | Common Fix |
|
||||
|---|---|---|
|
||||
| Disk full | `df -h` and `du -sh /var/log/*` | Clear old logs, run `logrotate -f`, remove temp files |
|
||||
| Out of inodes | `df -i` | Delete many small files, check `/tmp` and mail spools |
|
||||
| High CPU usage | `top`, `ps aux --sort=-%cpu` | Identify and restart or kill the offending process |
|
||||
| High memory / swapping | `free -h`, `vmstat 1` | Tune `vm.swappiness`, add RAM, identify memory leak |
|
||||
| Service won't start | `systemctl status <svc>`, `journalctl -u <svc>` | Check config syntax, file permissions, port conflicts |
|
||||
| DNS resolution fails | `dig @8.8.8.8 example.com`, `cat /etc/resolv.conf` | Fix nameserver entries, restart `systemd-resolved` |
|
||||
| Package dependency error | `apt --fix-broken install` or `dnf distro-sync` | Resolve held or conflicting packages |
|
||||
| SSH connection refused | `ss -tlnp \| grep 22`, `systemctl status sshd` | Ensure sshd is running and firewall allows port 22 |
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `ssh-configuration` -- Secure remote access to Linux servers
|
||||
- `user-management` -- Create and manage users, groups, and sudo
|
||||
- `systemd-services` -- Write and manage systemd unit files
|
||||
- `performance-tuning` -- Kernel and application performance optimization
|
||||
- `backup-recovery` -- Protect server data with automated backups
|
||||
|
||||
@@ -9,61 +9,357 @@ metadata:
|
||||
|
||||
# Performance Tuning
|
||||
|
||||
Optimize Linux system performance.
|
||||
Optimize Linux system performance through kernel parameter tuning, I/O scheduler selection, memory management, CPU governor configuration, and benchmarking. Covers methodology, real sysctl settings, and tool-based validation.
|
||||
|
||||
## System Monitoring
|
||||
## When to Use
|
||||
|
||||
- Server experiencing high latency, throughput bottlenecks, or resource exhaustion
|
||||
- Preparing infrastructure for high-traffic events or load tests
|
||||
- Tuning a database server, web server, or application host for production
|
||||
- Diagnosing whether a bottleneck is CPU, memory, disk I/O, or network
|
||||
- Establishing baseline performance metrics before and after changes
|
||||
- Configuring kernel parameters for containers, VMs, or bare-metal hosts
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Root or sudo access on the target system
|
||||
- `sysstat` package installed (provides `sar`, `iostat`, `mpstat`)
|
||||
- `linux-tools` or `perf` package for CPU profiling
|
||||
- Benchmarking tools: `fio` (disk), `sysbench` (CPU/memory), `iperf3` (network)
|
||||
- Baseline metrics collected before making any changes
|
||||
|
||||
## Performance Analysis Methodology
|
||||
|
||||
Always follow this order:
|
||||
|
||||
1. **Collect baseline** -- measure current performance with tools
|
||||
2. **Identify bottleneck** -- determine if CPU, memory, I/O, or network
|
||||
3. **Change one parameter** -- apply a single tuning change
|
||||
4. **Measure impact** -- re-run the same benchmark
|
||||
5. **Document** -- record the change and its effect
|
||||
6. **Iterate or revert** -- keep the change if beneficial, revert if not
|
||||
|
||||
## System Monitoring Tools
|
||||
|
||||
```bash
|
||||
top / htop # Process monitoring
|
||||
vmstat 1 # Memory statistics
|
||||
iostat -x 1 # Disk I/O
|
||||
sar -n DEV 1 # Network statistics
|
||||
perf top # CPU profiling
|
||||
# CPU and process monitoring
|
||||
top # Interactive process viewer
|
||||
htop # Enhanced interactive viewer
|
||||
mpstat -P ALL 2 # Per-CPU utilization every 2 seconds
|
||||
pidstat -u 2 # Per-process CPU usage
|
||||
|
||||
# Memory monitoring
|
||||
free -h # Memory summary
|
||||
vmstat 2 # Virtual memory stats every 2 seconds
|
||||
# Columns: r=runnable, b=blocked, si/so=swap in/out, bi/bo=block I/O
|
||||
|
||||
# Disk I/O monitoring
|
||||
iostat -xz 2 # Extended disk stats every 2 seconds
|
||||
# Key columns: %util, await (latency), r/s, w/s
|
||||
iotop -oP # Show processes doing I/O
|
||||
|
||||
# Network monitoring
|
||||
sar -n DEV 2 # Network interface stats
|
||||
ss -s # Socket summary
|
||||
nstat # Network counters
|
||||
|
||||
# CPU profiling (requires perf)
|
||||
perf top # Real-time function-level CPU profiling
|
||||
perf stat -a sleep 10 # System-wide counters for 10 seconds
|
||||
perf record -g -a sleep 30 # Record 30 seconds of call stacks
|
||||
perf report # Analyze recorded data
|
||||
|
||||
# One-liner: check all major resources
|
||||
echo "=== CPU ===" && mpstat 1 1 && echo "=== MEM ===" && free -h && echo "=== DISK ===" && iostat -x 1 1 && echo "=== NET ===" && ss -s
|
||||
```
|
||||
|
||||
## Kernel Parameters
|
||||
## Sysctl Kernel Parameter Tuning
|
||||
|
||||
### Network Tuning
|
||||
|
||||
```bash
|
||||
# /etc/sysctl.d/99-performance.conf
|
||||
vm.swappiness = 10
|
||||
# /etc/sysctl.d/60-network-performance.conf
|
||||
|
||||
# Increase the maximum socket receive/send buffer sizes
|
||||
net.core.rmem_max = 134217728
|
||||
net.core.wmem_max = 134217728
|
||||
net.core.rmem_default = 1048576
|
||||
net.core.wmem_default = 1048576
|
||||
|
||||
# TCP buffer auto-tuning (min, default, max in bytes)
|
||||
net.ipv4.tcp_rmem = 4096 1048576 134217728
|
||||
net.ipv4.tcp_wmem = 4096 1048576 134217728
|
||||
|
||||
# Increase connection backlog for high-traffic servers
|
||||
net.core.somaxconn = 65535
|
||||
net.ipv4.tcp_max_syn_backlog = 65535
|
||||
net.core.netdev_max_backlog = 65535
|
||||
|
||||
# Enable TCP fast open (client and server)
|
||||
net.ipv4.tcp_fastopen = 3
|
||||
|
||||
# Reuse TIME_WAIT sockets for new connections
|
||||
net.ipv4.tcp_tw_reuse = 1
|
||||
|
||||
# Increase the range of ephemeral ports
|
||||
net.ipv4.ip_local_port_range = 1024 65535
|
||||
|
||||
# TCP keepalive tuning (detect dead connections faster)
|
||||
net.ipv4.tcp_keepalive_time = 120
|
||||
net.ipv4.tcp_keepalive_intvl = 30
|
||||
net.ipv4.tcp_keepalive_probes = 3
|
||||
|
||||
# Disable slow start after idle (keeps congestion window open)
|
||||
net.ipv4.tcp_slow_start_after_idle = 0
|
||||
|
||||
# Enable BBR congestion control (requires kernel 4.9+)
|
||||
net.core.default_qdisc = fq
|
||||
net.ipv4.tcp_congestion_control = bbr
|
||||
```
|
||||
|
||||
### Memory Tuning
|
||||
|
||||
```bash
|
||||
# /etc/sysctl.d/60-memory-performance.conf
|
||||
|
||||
# Reduce swappiness (0-100, lower = less swap usage)
|
||||
# 10 for general servers, 1 for database servers
|
||||
vm.swappiness = 10
|
||||
|
||||
# Dirty page ratios (controls when dirty data is flushed to disk)
|
||||
# Lower values = more frequent, smaller writes (better for SSDs)
|
||||
vm.dirty_ratio = 20
|
||||
vm.dirty_background_ratio = 5
|
||||
|
||||
# For large-memory systems writing to fast storage
|
||||
# vm.dirty_ratio = 40
|
||||
# vm.dirty_background_ratio = 10
|
||||
|
||||
# Increase inotify limits (for apps watching many files)
|
||||
fs.inotify.max_user_watches = 524288
|
||||
fs.inotify.max_user_instances = 1024
|
||||
|
||||
# Maximum number of open file descriptors system-wide
|
||||
fs.file-max = 2097152
|
||||
vm.dirty_ratio = 40
|
||||
vm.dirty_background_ratio = 10
|
||||
|
||||
# Virtual memory overcommit
|
||||
# 0 = heuristic (default), 1 = always overcommit, 2 = never overcommit
|
||||
vm.overcommit_memory = 0
|
||||
|
||||
# For Redis or similar in-memory stores, use:
|
||||
# vm.overcommit_memory = 1
|
||||
|
||||
# Disable Transparent Huge Pages if it causes latency spikes (common with databases)
|
||||
# Done via boot parameter or runtime:
|
||||
# echo madvise > /sys/kernel/mm/transparent_hugepage/enabled
|
||||
```
|
||||
|
||||
## File Descriptor Limits
|
||||
### Applying Sysctl Changes
|
||||
|
||||
```bash
|
||||
# /etc/security/limits.conf
|
||||
* soft nofile 65535
|
||||
* hard nofile 65535
|
||||
* soft nproc 65535
|
||||
* hard nproc 65535
|
||||
# Apply all sysctl files
|
||||
sysctl --system
|
||||
|
||||
# Apply a specific file
|
||||
sysctl -p /etc/sysctl.d/60-network-performance.conf
|
||||
|
||||
# Set a parameter temporarily (lost on reboot)
|
||||
sysctl -w vm.swappiness=10
|
||||
|
||||
# Verify a parameter
|
||||
sysctl vm.swappiness
|
||||
sysctl net.ipv4.tcp_congestion_control
|
||||
```
|
||||
|
||||
## Disk I/O
|
||||
## I/O Scheduler Configuration
|
||||
|
||||
```bash
|
||||
# Change scheduler
|
||||
echo noop > /sys/block/sda/queue/scheduler
|
||||
# Check the current scheduler for a device
|
||||
cat /sys/block/sda/queue/scheduler
|
||||
# Output example: [mq-deadline] none kyber bfq
|
||||
|
||||
# Enable trim for SSDs
|
||||
fstrim -av
|
||||
# Set the scheduler temporarily
|
||||
echo mq-deadline > /sys/block/sda/queue/scheduler # Good for databases
|
||||
echo none > /sys/block/nvme0n1/queue/scheduler # Best for NVMe SSDs
|
||||
echo bfq > /sys/block/sda/queue/scheduler # Good for interactive desktop
|
||||
|
||||
# Scheduler recommendations:
|
||||
# NVMe SSD: none (noop) -- minimal overhead, hardware handles scheduling
|
||||
# SATA SSD: mq-deadline -- provides fairness with low latency
|
||||
# HDD: mq-deadline -- prevents starvation, good for databases
|
||||
# Desktop: bfq -- prioritizes interactive I/O
|
||||
|
||||
# Make persistent via udev rule
|
||||
cat <<'EOF' > /etc/udev/rules.d/60-io-scheduler.rules
|
||||
# Set mq-deadline for rotational (HDD) devices
|
||||
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="mq-deadline"
|
||||
# Set none for non-rotational (SSD/NVMe) devices
|
||||
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="none"
|
||||
ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/scheduler}="none"
|
||||
EOF
|
||||
|
||||
udevadm control --reload-rules
|
||||
|
||||
# Tune read-ahead for sequential workloads (database sequential scans)
|
||||
blockdev --setrahead 4096 /dev/sda # 4096 sectors = 2 MB
|
||||
|
||||
# Enable TRIM for SSDs (weekly via systemd timer)
|
||||
systemctl enable --now fstrim.timer
|
||||
fstrim -av # Manual run
|
||||
```
|
||||
|
||||
## Network Tuning
|
||||
## CPU Governor Configuration
|
||||
|
||||
```bash
|
||||
# Increase buffers
|
||||
sysctl -w net.core.rmem_max=134217728
|
||||
sysctl -w net.core.wmem_max=134217728
|
||||
# Check available governors
|
||||
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
|
||||
# Output: performance powersave schedutil
|
||||
|
||||
# Check current governor
|
||||
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
|
||||
|
||||
# Set all CPUs to performance mode (maximum frequency)
|
||||
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
|
||||
echo performance > "$cpu"
|
||||
done
|
||||
|
||||
# Set using cpupower (if installed)
|
||||
cpupower frequency-set -g performance
|
||||
|
||||
# Governor recommendations:
|
||||
# Server (production): performance -- max frequency, lowest latency
|
||||
# Server (general): schedutil -- kernel-driven dynamic scaling
|
||||
# Laptop / idle server: powersave -- minimize power consumption
|
||||
|
||||
# Make persistent via systemd service
|
||||
cat <<'EOF' > /etc/systemd/system/cpu-governor.service
|
||||
[Unit]
|
||||
Description=Set CPU governor to performance
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/bin/cpupower frequency-set -g performance
|
||||
RemainAfterExit=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl enable --now cpu-governor
|
||||
|
||||
# Disable CPU boost (turbo) if consistent latency is needed
|
||||
echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
## Benchmarking Tools
|
||||
|
||||
- Profile before optimizing
|
||||
- Change one parameter at a time
|
||||
- Monitor impact of changes
|
||||
- Document all tuning
|
||||
### fio -- Disk I/O Benchmarking
|
||||
|
||||
```bash
|
||||
# Install fio
|
||||
apt install -y fio # Debian/Ubuntu
|
||||
dnf install -y fio # RHEL/CentOS
|
||||
|
||||
# Sequential read test (simulates backup reads)
|
||||
fio --name=seq-read --ioengine=libaio --direct=1 --rw=read \
|
||||
--bs=1M --numjobs=4 --size=1G --runtime=60 --time_based --group_reporting
|
||||
|
||||
# Sequential write test
|
||||
fio --name=seq-write --ioengine=libaio --direct=1 --rw=write \
|
||||
--bs=1M --numjobs=4 --size=1G --runtime=60 --time_based --group_reporting
|
||||
|
||||
# Random read (4K blocks -- simulates database IOPS)
|
||||
fio --name=rand-read --ioengine=libaio --direct=1 --rw=randread \
|
||||
--bs=4k --numjobs=16 --iodepth=64 --size=1G --runtime=60 --time_based --group_reporting
|
||||
|
||||
# Random write (4K blocks)
|
||||
fio --name=rand-write --ioengine=libaio --direct=1 --rw=randwrite \
|
||||
--bs=4k --numjobs=16 --iodepth=64 --size=1G --runtime=60 --time_based --group_reporting
|
||||
|
||||
# Mixed random read/write (70/30 -- typical database workload)
|
||||
fio --name=mixed --ioengine=libaio --direct=1 --rw=randrw --rwmixread=70 \
|
||||
--bs=4k --numjobs=8 --iodepth=32 --size=1G --runtime=60 --time_based --group_reporting
|
||||
```
|
||||
|
||||
### sysbench -- CPU and Memory Benchmarking
|
||||
|
||||
```bash
|
||||
# Install: apt install -y sysbench (Debian) / dnf install -y sysbench (RHEL)
|
||||
|
||||
# CPU benchmark
|
||||
sysbench cpu --threads=4 --time=30 run
|
||||
|
||||
# Memory benchmark
|
||||
sysbench memory --threads=4 --time=30 --memory-block-size=1K --memory-total-size=100G run
|
||||
```
|
||||
|
||||
### iperf3 -- Network Benchmarking
|
||||
|
||||
```bash
|
||||
# Install iperf3
|
||||
apt install -y iperf3
|
||||
|
||||
# Start server on one host
|
||||
iperf3 -s
|
||||
|
||||
# Run client test from another host
|
||||
iperf3 -c <server-ip> -t 30 -P 4 # 30 seconds, 4 parallel streams
|
||||
|
||||
# Test with UDP (measure packet loss)
|
||||
iperf3 -c <server-ip> -u -b 1G -t 30
|
||||
|
||||
# Reverse mode (server sends to client)
|
||||
iperf3 -c <server-ip> -R -t 30
|
||||
```
|
||||
|
||||
## Quick-Reference Tuning Profiles
|
||||
|
||||
### Web Server (nginx/Apache)
|
||||
|
||||
```bash
|
||||
# /etc/sysctl.d/60-webserver.conf
|
||||
net.core.somaxconn = 65535
|
||||
net.ipv4.tcp_max_syn_backlog = 65535
|
||||
net.ipv4.tcp_tw_reuse = 1
|
||||
net.ipv4.tcp_fastopen = 3
|
||||
net.ipv4.ip_local_port_range = 1024 65535
|
||||
net.core.default_qdisc = fq
|
||||
net.ipv4.tcp_congestion_control = bbr
|
||||
fs.file-max = 2097152
|
||||
vm.swappiness = 10
|
||||
```
|
||||
|
||||
### Database Server (PostgreSQL/MySQL)
|
||||
|
||||
```bash
|
||||
# /etc/sysctl.d/60-database.conf
|
||||
vm.swappiness = 1
|
||||
vm.dirty_ratio = 15
|
||||
vm.dirty_background_ratio = 3
|
||||
vm.overcommit_memory = 2
|
||||
vm.overcommit_ratio = 80
|
||||
net.core.somaxconn = 4096
|
||||
fs.file-max = 2097152
|
||||
# Disable THP for databases
|
||||
# echo never > /sys/kernel/mm/transparent_hugepage/enabled
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Diagnostic Command | Common Fix |
|
||||
|---|---|---|
|
||||
| High CPU, no obvious process | `perf top`, `mpstat -P ALL 2` | Check for kernel-level issues: softirqs, interrupts |
|
||||
| High load avg, low CPU usage | `vmstat 2` (check `b` column) | I/O bottleneck: tune scheduler, check disk health |
|
||||
| System swapping heavily | `free -h`, `vmstat 2` (check si/so) | Reduce `vm.swappiness`, add RAM, find memory leak |
|
||||
| Disk latency spikes | `iostat -x 2` (check await) | Switch I/O scheduler, reduce dirty ratio, add SSD |
|
||||
| "Too many open files" error | `cat /proc/sys/fs/file-nr` | Increase `fs.file-max` and `LimitNOFILE` |
|
||||
| Network throughput low | `iperf3 -c <server>`, `ethtool` | Increase buffer sizes, enable BBR, check MTU |
|
||||
| Application timeout under load | `ss -s`, `sysctl net.core.somaxconn` | Increase `somaxconn` and `tcp_max_syn_backlog` |
|
||||
| Inconsistent latency | Check CPU governor | Set governor to `performance`, disable turbo boost |
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `linux-administration` -- General system monitoring and management
|
||||
- `systemd-services` -- Resource limits via cgroups in unit files
|
||||
- `block-storage` -- Storage-level performance (LVM, RAID, filesystems)
|
||||
- `nfs-storage` -- NFS-specific performance tuning
|
||||
|
||||
@@ -9,68 +9,300 @@ metadata:
|
||||
|
||||
# SSH Configuration
|
||||
|
||||
Secure SSH server and client configuration.
|
||||
Secure SSH server and client configuration for production environments, including key management, hardened sshd settings, bastion host architecture, tunneling, and multiplexing.
|
||||
|
||||
## Key Management
|
||||
## When to Use
|
||||
|
||||
- Setting up secure remote access to Linux or Unix servers
|
||||
- Hardening SSH daemon configuration to meet compliance requirements
|
||||
- Configuring bastion / jump hosts for private network access
|
||||
- Creating SSH tunnels for secure port forwarding
|
||||
- Managing SSH keys for teams or automated deployments
|
||||
- Troubleshooting connection, authentication, or performance issues
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- OpenSSH client installed locally (`ssh -V` to verify)
|
||||
- OpenSSH server installed on target (`sshd`)
|
||||
- Root or sudo access on the server for sshd_config changes
|
||||
- Firewall rules allowing TCP port 22 (or custom SSH port)
|
||||
|
||||
## Key Generation and Management
|
||||
|
||||
```bash
|
||||
# Generate key
|
||||
ssh-keygen -t ed25519 -C "user@example.com"
|
||||
# Generate an Ed25519 key (recommended -- fast, secure, short)
|
||||
ssh-keygen -t ed25519 -C "jane@example.com" -f ~/.ssh/id_ed25519
|
||||
|
||||
# Copy to server
|
||||
ssh-copy-id user@server
|
||||
# Generate an RSA 4096-bit key (for legacy compatibility)
|
||||
ssh-keygen -t rsa -b 4096 -C "jane@example.com" -f ~/.ssh/id_rsa_legacy
|
||||
|
||||
# Add to agent
|
||||
# Generate a key with a custom comment and no passphrase (CI/CD use only)
|
||||
ssh-keygen -t ed25519 -C "ci-deploy-key" -f ~/.ssh/ci_deploy -N ""
|
||||
|
||||
# Copy public key to a remote server
|
||||
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
|
||||
|
||||
# Manually append a public key (when ssh-copy-id is unavailable)
|
||||
cat ~/.ssh/id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
|
||||
|
||||
# List fingerprints of keys on the agent
|
||||
ssh-add -l
|
||||
|
||||
# Start the SSH agent and add a key
|
||||
eval "$(ssh-agent -s)"
|
||||
ssh-add ~/.ssh/id_ed25519
|
||||
|
||||
# Add a key with a lifetime (auto-removed after 8 hours)
|
||||
ssh-add -t 28800 ~/.ssh/id_ed25519
|
||||
|
||||
# Remove all keys from the agent
|
||||
ssh-add -D
|
||||
|
||||
# Convert an OpenSSH key to PEM format (for tools that need it)
|
||||
ssh-keygen -p -m PEM -f ~/.ssh/id_rsa_legacy
|
||||
|
||||
# Show the public key fingerprint (SHA256)
|
||||
ssh-keygen -lf ~/.ssh/id_ed25519.pub
|
||||
|
||||
# Rotate a key: generate new, deploy, then revoke old
|
||||
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_new -C "jane@example.com rotated $(date +%Y-%m)"
|
||||
ssh-copy-id -i ~/.ssh/id_ed25519_new.pub user@server
|
||||
# After verifying the new key works, remove the old public key from authorized_keys on the server
|
||||
```
|
||||
|
||||
## SSH Config (~/.ssh/config)
|
||||
## SSH Client Configuration (~/.ssh/config)
|
||||
|
||||
```
|
||||
Host production
|
||||
HostName prod.example.com
|
||||
User deploy
|
||||
IdentityFile ~/.ssh/prod_key
|
||||
Port 22
|
||||
```text
|
||||
# Global defaults applied to all hosts
|
||||
Host *
|
||||
AddKeysToAgent yes
|
||||
IdentitiesOnly yes
|
||||
ServerAliveInterval 60
|
||||
ServerAliveCountMax 3
|
||||
TCPKeepAlive yes
|
||||
Compression yes
|
||||
|
||||
# Production servers via bastion
|
||||
Host bastion
|
||||
HostName bastion.example.com
|
||||
User admin
|
||||
|
||||
Host internal
|
||||
HostName 10.0.0.5
|
||||
User admin
|
||||
User ops
|
||||
IdentityFile ~/.ssh/id_ed25519
|
||||
Port 22
|
||||
|
||||
Host prod-web-*
|
||||
User deploy
|
||||
IdentityFile ~/.ssh/id_ed25519
|
||||
ProxyJump bastion
|
||||
Port 22
|
||||
|
||||
Host prod-web-1
|
||||
HostName 10.0.1.10
|
||||
|
||||
Host prod-web-2
|
||||
HostName 10.0.1.11
|
||||
|
||||
# Staging accessed directly
|
||||
Host staging
|
||||
HostName staging.example.com
|
||||
User deploy
|
||||
IdentityFile ~/.ssh/id_ed25519_staging
|
||||
|
||||
# Database tunnel through bastion
|
||||
Host db-tunnel
|
||||
HostName 10.0.2.50
|
||||
User dba
|
||||
ProxyJump bastion
|
||||
LocalForward 5432 localhost:5432
|
||||
|
||||
# GitHub deploy key
|
||||
Host github-deploy
|
||||
HostName github.com
|
||||
User git
|
||||
IdentityFile ~/.ssh/github_deploy_key
|
||||
IdentitiesOnly yes
|
||||
|
||||
# Connection multiplexing for faster repeated connections
|
||||
Host fast-*
|
||||
ControlMaster auto
|
||||
ControlPath ~/.ssh/sockets/%r@%h-%p
|
||||
ControlPersist 600
|
||||
```
|
||||
|
||||
## Secure Server Config
|
||||
```bash
|
||||
# Create the sockets directory for multiplexing
|
||||
mkdir -p ~/.ssh/sockets
|
||||
chmod 700 ~/.ssh/sockets
|
||||
```
|
||||
|
||||
## Hardened Server Configuration (/etc/ssh/sshd_config)
|
||||
|
||||
```bash
|
||||
# /etc/ssh/sshd_config
|
||||
# /etc/ssh/sshd_config -- hardened configuration
|
||||
# -----------------------------------------------
|
||||
|
||||
# Listen on a non-default port (obscurity, not security -- combine with firewall)
|
||||
Port 22
|
||||
|
||||
# Protocol and key exchange
|
||||
Protocol 2
|
||||
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512
|
||||
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
|
||||
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
|
||||
|
||||
# Authentication
|
||||
PermitRootLogin no
|
||||
PasswordAuthentication no
|
||||
PubkeyAuthentication yes
|
||||
AuthenticationMethods publickey
|
||||
MaxAuthTries 3
|
||||
AllowUsers deploy admin
|
||||
```
|
||||
MaxSessions 5
|
||||
LoginGraceTime 30
|
||||
|
||||
## Tunneling
|
||||
# Restrict users and groups
|
||||
AllowGroups ssh-users ops-team
|
||||
# AllowUsers deploy admin
|
||||
|
||||
# Disable unused authentication methods
|
||||
ChallengeResponseAuthentication no
|
||||
KerberosAuthentication no
|
||||
GSSAPIAuthentication no
|
||||
|
||||
# Forwarding controls
|
||||
AllowTcpForwarding yes
|
||||
AllowAgentForwarding no
|
||||
X11Forwarding no
|
||||
PermitTunnel no
|
||||
|
||||
# Security hardening
|
||||
ClientAliveInterval 300
|
||||
ClientAliveCountMax 2
|
||||
UsePAM yes
|
||||
UseDNS no
|
||||
PermitEmptyPasswords no
|
||||
PermitUserEnvironment no
|
||||
|
||||
# Logging
|
||||
SyslogFacility AUTH
|
||||
LogLevel VERBOSE
|
||||
|
||||
# SFTP subsystem
|
||||
Subsystem sftp /usr/lib/openssh/sftp-server -f AUTH -l INFO
|
||||
|
||||
# Match block: restrict deploy user to SFTP only
|
||||
Match User sftponly
|
||||
ForceCommand internal-sftp
|
||||
ChrootDirectory /home/%u
|
||||
AllowTcpForwarding no
|
||||
AllowAgentForwarding no
|
||||
X11Forwarding no
|
||||
```
|
||||
|
||||
```bash
|
||||
# Local port forward
|
||||
ssh -L 8080:internal:80 bastion
|
||||
# Validate configuration before restarting
|
||||
sshd -t
|
||||
|
||||
# Remote port forward
|
||||
ssh -R 8080:localhost:80 server
|
||||
# Restart sshd to apply changes
|
||||
systemctl restart sshd
|
||||
|
||||
# SOCKS proxy
|
||||
ssh -D 1080 server
|
||||
# Always keep an existing session open while testing
|
||||
# Open a NEW terminal to verify you can still connect before closing the old one
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
## Bastion Host Setup
|
||||
|
||||
- Use ed25519 keys
|
||||
- Disable password auth
|
||||
- Use SSH agent forwarding carefully
|
||||
- Implement jump hosts/bastions
|
||||
```bash
|
||||
# On the bastion server, restrict forwarding to internal subnets only
|
||||
# /etc/ssh/sshd_config addition on bastion:
|
||||
AllowTcpForwarding yes
|
||||
PermitOpen 10.0.0.0/8:22 10.0.0.0/8:5432
|
||||
|
||||
# Disable shell access for jump-only users
|
||||
Match User jump-user
|
||||
PermitTTY no
|
||||
ForceCommand /usr/sbin/nologin
|
||||
AllowTcpForwarding yes
|
||||
|
||||
# Connect through the bastion from a client in one command
|
||||
ssh -J ops@bastion.example.com deploy@10.0.1.10
|
||||
|
||||
# Equivalent using ProxyCommand (older SSH versions)
|
||||
ssh -o ProxyCommand="ssh -W %h:%p ops@bastion.example.com" deploy@10.0.1.10
|
||||
|
||||
# Multi-hop: client -> bastion -> app-server -> db-server
|
||||
ssh -J ops@bastion,deploy@10.0.1.10 dba@10.0.2.50
|
||||
```
|
||||
|
||||
## SSH Tunneling
|
||||
|
||||
```bash
|
||||
# Local port forward: access remote service on localhost
|
||||
# Access remote PostgreSQL (10.0.2.50:5432) via bastion at localhost:5432
|
||||
ssh -L 5432:10.0.2.50:5432 ops@bastion.example.com -N
|
||||
|
||||
# Remote port forward: expose local service to the remote network
|
||||
# Make local dev server (localhost:3000) available on server port 8080
|
||||
ssh -R 8080:localhost:3000 user@server -N
|
||||
|
||||
# Dynamic SOCKS proxy: route all traffic through the server
|
||||
ssh -D 1080 user@server -N
|
||||
# Then configure browser or apps to use SOCKS5 proxy at localhost:1080
|
||||
|
||||
# Tunnel with a background process
|
||||
ssh -fN -L 5432:10.0.2.50:5432 ops@bastion.example.com
|
||||
# Find and kill the tunnel later
|
||||
ps aux | grep "ssh -fN" | grep -v grep
|
||||
kill <pid>
|
||||
|
||||
# Autossh for persistent tunnels (auto-reconnects)
|
||||
autossh -M 0 -f -N -L 5432:10.0.2.50:5432 ops@bastion.example.com \
|
||||
-o "ServerAliveInterval=30" -o "ServerAliveCountMax=3"
|
||||
```
|
||||
|
||||
## Agent Forwarding (Use with Caution)
|
||||
|
||||
```bash
|
||||
# Enable agent forwarding for a single connection
|
||||
ssh -A user@bastion
|
||||
|
||||
# From the bastion, your local keys are available to authenticate further
|
||||
ssh deploy@10.0.1.10 # Uses your local key via the agent
|
||||
|
||||
# SECURITY WARNING: Agent forwarding exposes your keys to anyone with root
|
||||
# on the intermediate host. Prefer ProxyJump instead.
|
||||
|
||||
# Safer alternative: ProxyJump does not expose the agent
|
||||
ssh -J ops@bastion deploy@10.0.1.10
|
||||
```
|
||||
|
||||
## SSH Key Restrictions in authorized_keys
|
||||
|
||||
```text
|
||||
# Restrict a key to a specific command only (backup key)
|
||||
command="/usr/local/bin/run-backup.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-ed25519 AAAA... backup@example.com
|
||||
|
||||
# Restrict a key to specific source IPs
|
||||
from="10.0.0.0/24,192.168.1.0/24" ssh-ed25519 AAAA... admin@example.com
|
||||
|
||||
# Read-only SFTP key with chroot
|
||||
command="internal-sftp",no-port-forwarding,no-pty ssh-ed25519 AAAA... sftp-upload@example.com
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Diagnostic Command | Common Fix |
|
||||
|---|---|---|
|
||||
| Connection refused | `ss -tlnp \| grep 22` on server | Ensure sshd is running; check firewall rules |
|
||||
| Permission denied (publickey) | `ssh -vvv user@server` | Verify key is in authorized_keys, permissions 600/700 |
|
||||
| Host key verification failed | `ssh-keygen -R server` | Remove stale host key; verify server identity |
|
||||
| Connection timeout | `ssh -o ConnectTimeout=5 user@server` | Check network path, security groups, NACLs |
|
||||
| Slow SSH login | Check `UseDNS` in sshd_config | Set `UseDNS no`; check reverse DNS |
|
||||
| Broken pipe / dropped sessions | Add `ServerAliveInterval 60` to config | Configure keepalive on both client and server |
|
||||
| Agent forwarding not working | `ssh-add -l` on bastion | Ensure `-A` flag used and agent has keys loaded |
|
||||
| Tunnel port already in use | `ss -tlnp \| grep <port>` | Kill existing tunnel or use a different local port |
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `linux-administration` -- General Linux system administration
|
||||
- `user-management` -- Managing the users who connect via SSH
|
||||
- `systemd-services` -- Managing sshd as a systemd service
|
||||
- `performance-tuning` -- Network tuning for SSH performance
|
||||
|
||||
@@ -9,68 +9,355 @@ metadata:
|
||||
|
||||
# Systemd Services
|
||||
|
||||
Manage system services with systemd.
|
||||
Create, manage, and monitor systemd services and timers. Covers unit file authoring, dependency management, socket activation, resource limits, journalctl log analysis, and production hardening.
|
||||
|
||||
## Service Unit File
|
||||
## When to Use
|
||||
|
||||
- Deploying an application as a managed background service
|
||||
- Replacing cron jobs with systemd timers for better logging and dependency control
|
||||
- Setting up socket activation for on-demand service startup
|
||||
- Configuring resource limits (CPU, memory, I/O) for services
|
||||
- Debugging service startup failures and runtime crashes
|
||||
- Managing service dependencies and ordering
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Linux system running systemd (most modern distributions)
|
||||
- Root or sudo access for creating system-level unit files
|
||||
- Application binary or script to run as a service
|
||||
- Understanding of the application's start/stop lifecycle
|
||||
|
||||
## Service Unit File -- Complete Example
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/myapp.service
|
||||
[Unit]
|
||||
Description=My Application
|
||||
After=network.target
|
||||
Description=MyApp Production Server
|
||||
Documentation=https://docs.example.com/myapp
|
||||
After=network-online.target postgresql.service
|
||||
Wants=network-online.target
|
||||
Requires=postgresql.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Type=notify
|
||||
User=myapp
|
||||
Group=myapp
|
||||
WorkingDirectory=/opt/myapp
|
||||
ExecStart=/opt/myapp/bin/start
|
||||
ExecStop=/opt/myapp/bin/stop
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
# Environment configuration
|
||||
EnvironmentFile=/etc/myapp/env
|
||||
Environment=NODE_ENV=production
|
||||
Environment=PORT=8080
|
||||
|
||||
# Execution
|
||||
ExecStartPre=/opt/myapp/bin/migrate --check
|
||||
ExecStart=/opt/myapp/bin/server --config /etc/myapp/config.yaml
|
||||
ExecStartPost=/opt/myapp/bin/healthcheck.sh
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
ExecStop=/opt/myapp/bin/graceful-stop.sh
|
||||
|
||||
# Restart behavior
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
StartLimitIntervalSec=300
|
||||
StartLimitBurst=5
|
||||
|
||||
# Timeouts
|
||||
TimeoutStartSec=30
|
||||
TimeoutStopSec=30
|
||||
WatchdogSec=60
|
||||
|
||||
# Security hardening
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
PrivateTmp=true
|
||||
ReadWritePaths=/var/lib/myapp /var/log/myapp
|
||||
CapabilityBoundingSet=
|
||||
AmbientCapabilities=
|
||||
|
||||
# Logging
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
SyslogIdentifier=myapp
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
## Service Management
|
||||
## Service Management Commands
|
||||
|
||||
```bash
|
||||
# Reload systemd after creating or modifying unit files
|
||||
systemctl daemon-reload
|
||||
|
||||
# Start, stop, restart a service
|
||||
systemctl start myapp
|
||||
systemctl stop myapp
|
||||
systemctl restart myapp
|
||||
|
||||
# Reload service configuration without restart (if supported)
|
||||
systemctl reload myapp
|
||||
|
||||
# Enable service to start on boot
|
||||
systemctl enable myapp
|
||||
|
||||
# Enable and start in one command
|
||||
systemctl enable --now myapp
|
||||
|
||||
# Disable and stop
|
||||
systemctl disable --now myapp
|
||||
|
||||
# Check service status
|
||||
systemctl status myapp
|
||||
journalctl -u myapp -f
|
||||
|
||||
# Check if a service is active, enabled, or failed
|
||||
systemctl is-active myapp
|
||||
systemctl is-enabled myapp
|
||||
systemctl is-failed myapp
|
||||
|
||||
# List all running services
|
||||
systemctl list-units --type=service --state=running
|
||||
|
||||
# List all failed services
|
||||
systemctl list-units --type=service --state=failed
|
||||
|
||||
# Show all properties of a service
|
||||
systemctl show myapp
|
||||
|
||||
# Show specific property values
|
||||
systemctl show myapp -p MainPID,MemoryCurrent,CPUUsageNSec
|
||||
|
||||
# Mask a service (prevent it from being started at all)
|
||||
systemctl mask myapp
|
||||
|
||||
# Unmask
|
||||
systemctl unmask myapp
|
||||
|
||||
# Reset a failed service state
|
||||
systemctl reset-failed myapp
|
||||
```
|
||||
|
||||
## Timer (Cron Replacement)
|
||||
## Timer Units (Cron Replacement)
|
||||
|
||||
### Timer File
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/backup.timer
|
||||
[Unit]
|
||||
Description=Daily backup
|
||||
Description=Daily backup timer
|
||||
|
||||
[Timer]
|
||||
OnCalendar=daily
|
||||
# Run daily at 2:30 AM
|
||||
OnCalendar=*-*-* 02:30:00
|
||||
# If the system was off at the scheduled time, run when it boots
|
||||
Persistent=true
|
||||
# Add random delay up to 15 minutes to avoid thundering herd
|
||||
RandomizedDelaySec=900
|
||||
# Associate with a specific service (defaults to same name .service)
|
||||
Unit=backup.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
```
|
||||
|
||||
## Resource Limits
|
||||
### Corresponding Service File
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/backup.service
|
||||
[Unit]
|
||||
Description=Daily backup job
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
MemoryLimit=512M
|
||||
CPUQuota=50%
|
||||
Type=oneshot
|
||||
User=backup
|
||||
ExecStart=/usr/local/bin/run-backup.sh
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
### Timer Management
|
||||
|
||||
- Use Type=notify for better tracking
|
||||
- Implement proper restart policies
|
||||
- Use timers instead of cron
|
||||
- Set resource limits
|
||||
```bash
|
||||
# Common OnCalendar expressions:
|
||||
# minutely, hourly, daily, weekly, monthly
|
||||
# *-*-* 06:00:00 Daily at 6 AM
|
||||
# Mon..Fri *-*-* 09:00 Weekdays at 9 AM
|
||||
# *:0/15 Every 15 minutes
|
||||
|
||||
# Validate calendar expressions
|
||||
systemd-analyze calendar "Mon..Fri *-*-* 09:00"
|
||||
|
||||
# List all active timers
|
||||
systemctl list-timers --all
|
||||
|
||||
# Enable and start a timer
|
||||
systemctl enable --now backup.timer
|
||||
|
||||
# Run the associated service immediately (for testing)
|
||||
systemctl start backup.service
|
||||
```
|
||||
|
||||
## Socket Activation
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/myapp.socket
|
||||
[Unit]
|
||||
Description=MyApp Socket
|
||||
|
||||
[Socket]
|
||||
ListenStream=8080
|
||||
Accept=no
|
||||
# Optionally bind to a specific IP
|
||||
# ListenStream=10.0.1.10:8080
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
```
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/myapp.service
|
||||
[Unit]
|
||||
Description=MyApp Server
|
||||
Requires=myapp.socket
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
User=myapp
|
||||
ExecStart=/opt/myapp/bin/server
|
||||
# Service receives the socket file descriptor from systemd
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
```bash
|
||||
# Enable the socket (service starts on first connection)
|
||||
systemctl enable --now myapp.socket
|
||||
|
||||
# Check socket status
|
||||
systemctl status myapp.socket
|
||||
|
||||
# List all listening sockets
|
||||
systemctl list-sockets
|
||||
```
|
||||
|
||||
## Dependency Management
|
||||
|
||||
```bash
|
||||
# Key [Unit] directives for ordering and dependencies:
|
||||
# After= Start after these units (ordering only)
|
||||
# Requires= Hard dependency -- fail if this unit cannot start
|
||||
# Wants= Soft dependency -- try to start, don't fail if unavailable
|
||||
# PartOf= Stop this unit when the parent stops
|
||||
# Conflicts= Cannot run alongside this unit
|
||||
|
||||
# Visualize the dependency tree for a service
|
||||
systemctl list-dependencies myapp
|
||||
|
||||
# Show reverse dependencies (who depends on this unit)
|
||||
systemctl list-dependencies myapp --reverse
|
||||
|
||||
# Analyze boot order for a service
|
||||
systemd-analyze critical-chain myapp.service
|
||||
```
|
||||
|
||||
## Resource Limits (cgroups v2)
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/myapp.service.d/limits.conf
|
||||
# (drop-in override file)
|
||||
[Service]
|
||||
# Memory limits
|
||||
MemoryMax=1G
|
||||
MemoryHigh=768M
|
||||
|
||||
# CPU limits
|
||||
CPUQuota=200% # Up to 2 full CPU cores
|
||||
CPUWeight=100 # Relative weight (default=100)
|
||||
|
||||
# I/O limits
|
||||
IOWeight=50
|
||||
IOReadBandwidthMax=/dev/sda 100M
|
||||
IOWriteBandwidthMax=/dev/sda 50M
|
||||
|
||||
# Process limits
|
||||
LimitNOFILE=65535
|
||||
LimitNPROC=4096
|
||||
TasksMax=512
|
||||
|
||||
# Disable OOM killer (let the app handle it)
|
||||
OOMPolicy=continue
|
||||
```
|
||||
|
||||
```bash
|
||||
# Apply drop-in overrides without editing the main unit file
|
||||
mkdir -p /etc/systemd/system/myapp.service.d/
|
||||
|
||||
cat <<'EOF' > /etc/systemd/system/myapp.service.d/limits.conf
|
||||
[Service]
|
||||
MemoryMax=1G
|
||||
CPUQuota=200%
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl restart myapp
|
||||
|
||||
# View current resource usage for a service
|
||||
systemctl status myapp # Shows Memory and CPU
|
||||
systemd-cgtop # Real-time cgroup resource usage
|
||||
|
||||
# Edit a service's overrides interactively
|
||||
systemctl edit myapp
|
||||
# This creates a drop-in file automatically
|
||||
```
|
||||
|
||||
## Journalctl Log Analysis
|
||||
|
||||
```bash
|
||||
# Follow logs for a service in real time
|
||||
journalctl -u myapp -f
|
||||
|
||||
# Show logs since last boot
|
||||
journalctl -u myapp -b
|
||||
|
||||
# Show logs for a specific time range
|
||||
journalctl -u myapp --since "2025-01-15 08:00" --until "2025-01-15 12:00"
|
||||
|
||||
# Show only error and above
|
||||
journalctl -u myapp -p err
|
||||
|
||||
# Show the last 100 lines with full messages (no truncation)
|
||||
journalctl -u myapp -n 100 --no-pager -l
|
||||
|
||||
# Show logs in JSON format (for parsing)
|
||||
journalctl -u myapp -o json-pretty --no-pager | head -50
|
||||
|
||||
# Check journal disk usage and vacuum old entries
|
||||
journalctl --disk-usage
|
||||
journalctl --rotate
|
||||
journalctl --vacuum-time=7d
|
||||
journalctl --vacuum-size=500M
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Diagnostic Command | Common Fix |
|
||||
|---|---|---|
|
||||
| Service fails to start | `systemctl status myapp`, `journalctl -u myapp -n 50` | Check ExecStart path, permissions, config syntax |
|
||||
| Service keeps restarting | `journalctl -u myapp --since "5 min ago"` | Check StartLimitBurst; look for crash in logs |
|
||||
| "Main process exited, code=exited, status=217" | `journalctl -u myapp` | User or group in unit file does not exist |
|
||||
| "Failed to set up mount namespacing" | Check ProtectSystem/PrivateTmp | Kernel too old or SELinux blocking; relax directives |
|
||||
| Timer not firing | `systemctl list-timers`, `systemctl status backup.timer` | Ensure timer is enabled; validate OnCalendar expression |
|
||||
| Service starts before dependency | Check After= and Requires= | Add `After=dependency.service` for ordering |
|
||||
| OOM killed | `journalctl -k \| grep oom`, `dmesg` | Increase MemoryMax or optimize application memory |
|
||||
| Cannot bind to port 80 | Check AmbientCapabilities | Add `CAP_NET_BIND_SERVICE` or use a higher port |
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `linux-administration` -- General system administration context
|
||||
- `performance-tuning` -- Kernel tuning and resource optimization
|
||||
- `user-management` -- Service accounts and permissions
|
||||
- `backup-recovery` -- Scheduling backups with systemd timers
|
||||
|
||||
@@ -9,61 +9,359 @@ metadata:
|
||||
|
||||
# User Management
|
||||
|
||||
Manage users, groups, and permissions.
|
||||
Manage users, groups, permissions, sudo access, PAM modules, and LDAP integration on Linux systems. Includes practical scripts for bulk user operations and access auditing.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Creating and managing local user accounts on Linux servers
|
||||
- Configuring sudo access with fine-grained privilege controls
|
||||
- Setting up group-based access control for teams
|
||||
- Integrating Linux hosts with LDAP or Active Directory for centralized auth
|
||||
- Auditing user accounts, permissions, and access patterns
|
||||
- Automating bulk user provisioning and deprovisioning
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Root or sudo access on the target system
|
||||
- `shadow-utils` package (provides useradd, usermod, etc.) -- installed by default
|
||||
- `libpam-modules` for PAM configuration
|
||||
- For LDAP: `sssd`, `realmd`, `libpam-ldapd`, or `nslcd` packages
|
||||
- For auditing: `auditd` package
|
||||
|
||||
## User Operations
|
||||
|
||||
### Creating Users
|
||||
|
||||
```bash
|
||||
# Create user
|
||||
useradd -m -s /bin/bash username
|
||||
passwd username
|
||||
# Create a user with home directory, default shell, and comment
|
||||
useradd -m -s /bin/bash -c "Jane Smith" jsmith
|
||||
|
||||
# Delete user
|
||||
userdel -r username
|
||||
# Set the user's password interactively
|
||||
passwd jsmith
|
||||
|
||||
# Modify user
|
||||
usermod -aG sudo username
|
||||
usermod -s /bin/zsh username
|
||||
# Create a user with a specific UID and primary group
|
||||
useradd -m -s /bin/bash -u 1500 -g developers -c "Deploy Account" deploy
|
||||
|
||||
# Create a system account (no home, no login shell) for running services
|
||||
useradd -r -s /usr/sbin/nologin -d /opt/myapp -c "MyApp Service Account" myapp
|
||||
|
||||
# Create a user with an expiration date (contractor access)
|
||||
useradd -m -s /bin/bash -e 2025-12-31 -c "Contractor - Bob Lee" blee
|
||||
|
||||
# Create user and add to multiple supplementary groups at creation time
|
||||
useradd -m -s /bin/bash -G docker,developers,ssh-users -c "Dev User" devuser
|
||||
```
|
||||
|
||||
### Modifying Users
|
||||
|
||||
```bash
|
||||
# Add a user to a supplementary group (preserving existing groups with -a)
|
||||
usermod -aG sudo jsmith
|
||||
usermod -aG docker,developers jsmith
|
||||
|
||||
# Change the user's login shell
|
||||
usermod -s /bin/zsh jsmith
|
||||
|
||||
# Change the user's home directory and move existing files
|
||||
usermod -d /home/jsmith-new -m jsmith
|
||||
|
||||
# Lock a user account (disable login without deleting)
|
||||
usermod -L jsmith
|
||||
|
||||
# Unlock a user account
|
||||
usermod -U jsmith
|
||||
|
||||
# Set an account expiration date
|
||||
usermod -e 2025-06-30 blee
|
||||
|
||||
# Change a user's login name
|
||||
usermod -l jsmith-new jsmith
|
||||
|
||||
# Force password change on next login
|
||||
chage -d 0 jsmith
|
||||
|
||||
# Set password aging: min 7 days, max 90 days, warn 14 days before
|
||||
chage -m 7 -M 90 -W 14 jsmith
|
||||
|
||||
# View password aging info
|
||||
chage -l jsmith
|
||||
```
|
||||
|
||||
### Deleting Users
|
||||
|
||||
```bash
|
||||
# Remove a user and their home directory
|
||||
userdel -r jsmith
|
||||
|
||||
# Remove a user but keep their home directory (for auditing)
|
||||
userdel jsmith
|
||||
|
||||
# Find and reassign files owned by a deleted user (by UID)
|
||||
find / -uid 1500 -exec chown newowner:newgroup {} \;
|
||||
```
|
||||
|
||||
## Group Management
|
||||
|
||||
```bash
|
||||
# Create group
|
||||
# Create a new group
|
||||
groupadd developers
|
||||
|
||||
# Add user to group
|
||||
usermod -aG developers username
|
||||
gpasswd -a username developers
|
||||
# Create a group with a specific GID
|
||||
groupadd -g 2000 devops
|
||||
|
||||
# Remove from group
|
||||
gpasswd -d username developers
|
||||
# Add a user to a group
|
||||
usermod -aG developers jsmith
|
||||
# Alternative using gpasswd
|
||||
gpasswd -a jsmith developers
|
||||
|
||||
# Remove a user from a group
|
||||
gpasswd -d jsmith developers
|
||||
|
||||
# Set group administrators (can add/remove members without root)
|
||||
gpasswd -A jsmith developers
|
||||
|
||||
# Delete a group
|
||||
groupdel developers
|
||||
|
||||
# List all groups a user belongs to
|
||||
groups jsmith
|
||||
id jsmith
|
||||
|
||||
# List all members of a group
|
||||
getent group developers
|
||||
|
||||
# Show all groups on the system
|
||||
cat /etc/group | cut -d: -f1 | sort
|
||||
```
|
||||
|
||||
## Sudo Configuration
|
||||
|
||||
```bash
|
||||
# /etc/sudoers.d/developers
|
||||
%developers ALL=(ALL) NOPASSWD: /usr/bin/docker
|
||||
username ALL=(ALL) NOPASSWD: ALL
|
||||
# Always edit sudoers via visudo (syntax validation prevents lockout)
|
||||
visudo
|
||||
|
||||
# Better: use drop-in files in /etc/sudoers.d/
|
||||
visudo -f /etc/sudoers.d/developers
|
||||
```
|
||||
|
||||
## File Permissions
|
||||
### /etc/sudoers.d/developers
|
||||
|
||||
```text
|
||||
# Allow the developers group to restart specific services
|
||||
%developers ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart myapp, /usr/bin/systemctl status myapp
|
||||
|
||||
# Allow a deploy user full sudo with no password
|
||||
deploy ALL=(ALL) NOPASSWD: ALL
|
||||
|
||||
# Allow ops team to run docker commands only
|
||||
%ops ALL=(ALL) NOPASSWD: /usr/bin/docker, /usr/bin/docker-compose
|
||||
|
||||
# Allow a user to run commands as a specific service account
|
||||
jsmith ALL=(myapp) NOPASSWD: /opt/myapp/bin/*
|
||||
|
||||
# Restrict to specific hosts (useful with centralized sudoers)
|
||||
jsmith dbservers=(root) /usr/bin/systemctl restart postgresql
|
||||
|
||||
# Log all sudo commands to a dedicated file
|
||||
Defaults log_output
|
||||
Defaults!/usr/bin/sudoreplay !log_output
|
||||
Defaults logfile="/var/log/sudo.log"
|
||||
|
||||
# Require password re-entry every 5 minutes (default is 15)
|
||||
Defaults timestamp_timeout=5
|
||||
|
||||
# Require password for sudo even if user has NOPASSWD elsewhere
|
||||
Defaults:jsmith !authenticate
|
||||
```
|
||||
|
||||
```bash
|
||||
chmod 755 file # rwxr-xr-x
|
||||
chmod u+x file # Add execute for user
|
||||
chown user:group file # Change ownership
|
||||
chown -R user:group dir/
|
||||
# Validate sudoers syntax without applying
|
||||
visudo -c
|
||||
|
||||
# ACLs
|
||||
setfacl -m u:user:rx file
|
||||
getfacl file
|
||||
# Check what sudo permissions a user has
|
||||
sudo -l -U jsmith
|
||||
|
||||
# Test a specific sudo command as a user
|
||||
sudo -u myapp /opt/myapp/bin/healthcheck.sh
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
## File Permissions and ACLs
|
||||
|
||||
- Use groups for access control
|
||||
- Minimal sudo privileges
|
||||
- Regular access reviews
|
||||
- Strong password policies
|
||||
```bash
|
||||
# Standard permissions
|
||||
chmod 755 /opt/myapp # rwxr-xr-x
|
||||
chmod 640 /etc/myapp.conf # rw-r-----
|
||||
chmod u+x script.sh # Add execute for owner
|
||||
chmod g+w shared-dir/ # Add write for group
|
||||
chmod o-rwx private-file # Remove all permissions for others
|
||||
|
||||
# Change ownership
|
||||
chown deploy:developers /opt/myapp
|
||||
chown -R deploy:developers /opt/myapp/ # Recursive
|
||||
|
||||
# Set the SGID bit (new files inherit group ownership)
|
||||
chmod g+s /opt/shared/
|
||||
|
||||
# Set the sticky bit (only owner can delete their files)
|
||||
chmod +t /tmp/shared/
|
||||
|
||||
# Access Control Lists (ACLs) for fine-grained control
|
||||
# Grant read-execute to a specific user on a directory
|
||||
setfacl -m u:jsmith:rx /opt/myapp/logs/
|
||||
|
||||
# Grant read-write to a group
|
||||
setfacl -m g:developers:rw /opt/shared/
|
||||
|
||||
# Set default ACL (applied to new files created in the directory)
|
||||
setfacl -d -m g:developers:rw /opt/shared/
|
||||
|
||||
# View ACLs
|
||||
getfacl /opt/shared/
|
||||
|
||||
# Remove a specific ACL entry
|
||||
setfacl -x u:jsmith /opt/myapp/logs/
|
||||
|
||||
# Remove all ACLs
|
||||
setfacl -b /opt/shared/
|
||||
```
|
||||
|
||||
## PAM Configuration
|
||||
|
||||
```bash
|
||||
# PAM config files are in /etc/pam.d/
|
||||
# Each file controls auth for a specific service (sshd, login, sudo, etc.)
|
||||
|
||||
# Enforce password complexity via pam_pwquality
|
||||
# /etc/pam.d/common-password (Debian) or /etc/pam.d/system-auth (RHEL)
|
||||
password requisite pam_pwquality.so retry=3 minlen=12 dcredit=-1 ucredit=-1 ocredit=-1 lcredit=-1
|
||||
|
||||
# Configure /etc/security/pwquality.conf
|
||||
minlen = 12
|
||||
dcredit = -1
|
||||
ucredit = -1
|
||||
ocredit = -1
|
||||
lcredit = -1
|
||||
maxrepeat = 3
|
||||
dictcheck = 1
|
||||
|
||||
# Limit concurrent logins per user
|
||||
# /etc/security/limits.conf
|
||||
jsmith hard maxlogins 3
|
||||
@developers hard maxlogins 5
|
||||
|
||||
# Lock account after 5 failed login attempts
|
||||
# /etc/pam.d/common-auth (Debian)
|
||||
auth required pam_faillock.so preauth silent deny=5 unlock_time=900
|
||||
auth required pam_faillock.so authfail deny=5 unlock_time=900
|
||||
|
||||
# View failed login attempts
|
||||
faillock --user jsmith
|
||||
|
||||
# Unlock a locked account
|
||||
faillock --user jsmith --reset
|
||||
```
|
||||
|
||||
## LDAP / Active Directory Integration
|
||||
|
||||
```bash
|
||||
# Install SSSD and realmd for AD integration (Ubuntu/Debian)
|
||||
apt install -y sssd realmd adcli sssd-tools libnss-sss libpam-sss
|
||||
|
||||
# Install SSSD and realmd (RHEL/CentOS)
|
||||
dnf install -y sssd realmd adcli sssd-tools oddjob oddjob-mkhomedir
|
||||
|
||||
# Discover and join an Active Directory domain
|
||||
realm discover corp.example.com
|
||||
realm join corp.example.com -U admin@CORP.EXAMPLE.COM
|
||||
|
||||
# Verify the join
|
||||
realm list
|
||||
|
||||
# Allow specific AD groups to log in
|
||||
realm permit -g "Linux Admins@corp.example.com"
|
||||
realm permit -g "Developers@corp.example.com"
|
||||
|
||||
# Deny all except permitted groups
|
||||
realm deny --all
|
||||
realm permit -g "Linux Admins@corp.example.com"
|
||||
|
||||
# Restart SSSD after config changes
|
||||
systemctl restart sssd
|
||||
|
||||
# Test LDAP user lookup
|
||||
id jsmith
|
||||
getent passwd jsmith
|
||||
|
||||
# Grant sudo to an AD group
|
||||
echo '%linux\ admins ALL=(ALL) ALL' > /etc/sudoers.d/ad-admins
|
||||
```
|
||||
|
||||
## Bulk User Management Scripts
|
||||
|
||||
### Bulk User Creation from CSV
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# bulk-create-users.sh
|
||||
# CSV format: username,fullname,groups,shell
|
||||
# Example: jsmith,Jane Smith,developers;docker,/bin/bash
|
||||
|
||||
CSV_FILE="${1:?Usage: $0 <users.csv>}"
|
||||
|
||||
while IFS=',' read -r username fullname groups shell; do
|
||||
# Skip header line
|
||||
[[ "$username" == "username" ]] && continue
|
||||
|
||||
if id "$username" &>/dev/null; then
|
||||
echo "SKIP: User $username already exists"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Replace semicolons with commas for -G flag
|
||||
group_list="${groups//;/,}"
|
||||
|
||||
useradd -m -s "$shell" -c "$fullname" -G "$group_list" "$username"
|
||||
# Generate a random temporary password
|
||||
temp_pass=$(openssl rand -base64 12)
|
||||
echo "$username:$temp_pass" | chpasswd
|
||||
chage -d 0 "$username" # Force password change at first login
|
||||
|
||||
echo "CREATED: $username (groups: $group_list) temp-pass: $temp_pass"
|
||||
done < "$CSV_FILE"
|
||||
```
|
||||
|
||||
### Quick Access Audit Commands
|
||||
|
||||
```bash
|
||||
# List non-system users (UID >= 1000)
|
||||
awk -F: '$3 >= 1000 && $3 < 65534 { printf "%-20s UID=%-6s Shell=%s\n", $1, $3, $7 }' /etc/passwd
|
||||
|
||||
# List users with sudo access
|
||||
getent group sudo wheel 2>/dev/null
|
||||
|
||||
# Find accounts that have never logged in
|
||||
lastlog | awk '$0 ~ /Never logged in/ { print $1 }'
|
||||
|
||||
# Find accounts with empty passwords
|
||||
awk -F: '($2 == "" || $2 == "!") { print $1 }' /etc/shadow 2>/dev/null
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Diagnostic Command | Common Fix |
|
||||
|---|---|---|
|
||||
| User cannot log in | `passwd -S username`, `faillock --user username` | Unlock account, reset password, check shell |
|
||||
| "not in sudoers" error | `sudo -l -U username` | Add user to sudo group or create sudoers.d file |
|
||||
| Group membership not applied | `id username`, `groups username` | User must log out and back in for new groups |
|
||||
| LDAP/AD user not found | `id aduser`, `sssctl user-show aduser` | Check SSSD status, clear cache: `sss_cache -E` |
|
||||
| Permission denied on file | `ls -la file`, `getfacl file` | Fix ownership/permissions, check SELinux context |
|
||||
| PAM lockout after failed attempts | `faillock --user username` | `faillock --user username --reset` |
|
||||
| Home directory not created | Check `/etc/login.defs` CREATEHOME | Use `useradd -m` or enable `pam_mkhomedir` |
|
||||
| Password policy not enforced | Check `/etc/pam.d/common-password` | Install and configure `pam_pwquality` |
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `linux-administration` -- General Linux server management
|
||||
- `ssh-configuration` -- SSH key-based authentication for managed users
|
||||
- `systemd-services` -- Service accounts and systemd user instances
|
||||
- `performance-tuning` -- Resource limits per user via cgroups and ulimits
|
||||
|
||||
@@ -9,48 +9,294 @@ metadata:
|
||||
|
||||
# Windows Server Administration
|
||||
|
||||
Windows Server management and PowerShell automation.
|
||||
Windows Server management and PowerShell automation for production workloads including IIS web hosting, Active Directory domain services, and system maintenance.
|
||||
|
||||
## Server Roles
|
||||
## When to Use
|
||||
|
||||
- Provisioning or configuring Windows Server 2019/2022 instances
|
||||
- Setting up IIS websites, application pools, and bindings
|
||||
- Managing Active Directory users, groups, and Group Policy
|
||||
- Automating administrative tasks with PowerShell
|
||||
- Reviewing Windows Event Logs for troubleshooting
|
||||
- Applying and managing Windows Updates on servers
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Administrator account on the target server
|
||||
- PowerShell 5.1+ (built-in) or PowerShell 7+ installed
|
||||
- Remote Desktop or WinRM access configured
|
||||
- Windows Server 2019 or 2022 (Desktop Experience or Server Core)
|
||||
|
||||
## PowerShell Administration Essentials
|
||||
|
||||
```powershell
|
||||
# Install IIS
|
||||
Install-WindowsFeature -Name Web-Server -IncludeManagementTools
|
||||
# Check PowerShell version
|
||||
$PSVersionTable.PSVersion
|
||||
|
||||
# Install AD DS
|
||||
# Get system information
|
||||
Get-ComputerInfo | Select-Object CsName, OsName, OsVersion, OsArchitecture
|
||||
|
||||
# List running processes sorted by CPU
|
||||
Get-Process | Sort-Object CPU -Descending | Select-Object -First 20
|
||||
|
||||
# List all services and their status
|
||||
Get-Service | Where-Object { $_.Status -eq 'Running' }
|
||||
|
||||
# Restart a service
|
||||
Restart-Service -Name W3SVC -Force
|
||||
|
||||
# Get disk space on all drives
|
||||
Get-PSDrive -PSProvider FileSystem | Select-Object Name, @{N='Used(GB)';E={[math]::Round($_.Used/1GB,2)}}, @{N='Free(GB)';E={[math]::Round($_.Free/1GB,2)}}
|
||||
|
||||
# Check uptime
|
||||
(Get-Date) - (Get-CimInstance Win32_OperatingSystem).LastBootUpTime
|
||||
|
||||
# Open firewall port
|
||||
New-NetFirewallRule -DisplayName "Allow HTTPS" -Direction Inbound -Protocol TCP -LocalPort 443 -Action Allow
|
||||
|
||||
# List firewall rules
|
||||
Get-NetFirewallRule | Where-Object { $_.Enabled -eq 'True' -and $_.Direction -eq 'Inbound' } | Select-Object DisplayName, Action
|
||||
|
||||
# Set DNS client server addresses
|
||||
Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses ("10.0.0.2","10.0.0.3")
|
||||
|
||||
# PowerShell remoting to another server
|
||||
Enter-PSSession -ComputerName server02 -Credential (Get-Credential)
|
||||
|
||||
# Run a command on multiple remote servers
|
||||
Invoke-Command -ComputerName server01,server02,server03 -ScriptBlock { Get-Service W3SVC }
|
||||
```
|
||||
|
||||
## Server Roles and Features
|
||||
|
||||
```powershell
|
||||
# List all available roles and features
|
||||
Get-WindowsFeature
|
||||
|
||||
# Install IIS with management tools
|
||||
Install-WindowsFeature -Name Web-Server -IncludeManagementTools -IncludeAllSubFeature
|
||||
|
||||
# Install Active Directory Domain Services
|
||||
Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools
|
||||
|
||||
# List installed features
|
||||
Get-WindowsFeature | Where-Object Installed
|
||||
# Install DNS Server
|
||||
Install-WindowsFeature -Name DNS -IncludeManagementTools
|
||||
|
||||
# Install DHCP Server
|
||||
Install-WindowsFeature -Name DHCP -IncludeManagementTools
|
||||
|
||||
# Install File Server with deduplication
|
||||
Install-WindowsFeature -Name FS-FileServer, FS-Data-Deduplication
|
||||
|
||||
# List installed features only
|
||||
Get-WindowsFeature | Where-Object Installed | Select-Object Name, InstallState
|
||||
|
||||
# Remove a feature
|
||||
Uninstall-WindowsFeature -Name Telnet-Client
|
||||
```
|
||||
|
||||
## System Information
|
||||
## IIS Web Server Setup
|
||||
|
||||
```powershell
|
||||
Get-ComputerInfo
|
||||
Get-Process
|
||||
Get-Service
|
||||
Get-EventLog -LogName System -Newest 50
|
||||
```
|
||||
# Import the IIS administration module
|
||||
Import-Module WebAdministration
|
||||
|
||||
## IIS Management
|
||||
# Create a new application pool
|
||||
New-WebAppPool -Name "ProductionPool"
|
||||
Set-ItemProperty IIS:\AppPools\ProductionPool -Name processModel.identityType -Value 3 # NetworkService
|
||||
Set-ItemProperty IIS:\AppPools\ProductionPool -Name managedRuntimeVersion -Value "" # No managed code (reverse proxy)
|
||||
|
||||
```powershell
|
||||
# Create website
|
||||
New-Website -Name "MyApp" -Port 80 -PhysicalPath "C:\inetpub\myapp"
|
||||
# Create a new website
|
||||
New-Website -Name "MyApp" `
|
||||
-Port 443 `
|
||||
-Protocol https `
|
||||
-PhysicalPath "C:\inetpub\myapp" `
|
||||
-ApplicationPool "ProductionPool" `
|
||||
-SslFlags 1
|
||||
|
||||
# Create app pool
|
||||
New-WebAppPool -Name "MyAppPool"
|
||||
# Add an HTTP binding that redirects to HTTPS
|
||||
New-WebBinding -Name "MyApp" -Protocol http -Port 80
|
||||
|
||||
# Start/Stop
|
||||
# Bind an SSL certificate to the HTTPS site
|
||||
$cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.Subject -like "*example.com*" }
|
||||
New-Item IIS:\SslBindings\0.0.0.0!443 -Value $cert
|
||||
|
||||
# Create a virtual directory
|
||||
New-WebVirtualDirectory -Site "MyApp" -Name "static" -PhysicalPath "C:\inetpub\static"
|
||||
|
||||
# Start, stop, and restart a site
|
||||
Start-Website -Name "MyApp"
|
||||
Stop-Website -Name "MyApp"
|
||||
Stop-Website -Name "MyApp"
|
||||
Restart-WebAppPool -Name "ProductionPool"
|
||||
|
||||
# List all websites and their state
|
||||
Get-Website | Select-Object Name, State, PhysicalPath, @{N='Bindings';E={$_.Bindings.Collection.bindingInformation}}
|
||||
|
||||
# Enable IIS logging with W3C format
|
||||
Set-WebConfigurationProperty -PSPath "IIS:\Sites\MyApp" `
|
||||
-Filter "system.webServer/httpLogging" `
|
||||
-Name "dontLog" -Value $false
|
||||
|
||||
# URL Rewrite: redirect HTTP to HTTPS (requires URL Rewrite module)
|
||||
# web.config rule:
|
||||
@'
|
||||
<rule name="HTTP to HTTPS" stopProcessing="true">
|
||||
<match url="(.*)" />
|
||||
<conditions>
|
||||
<add input="{HTTPS}" pattern="off" />
|
||||
</conditions>
|
||||
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
|
||||
</rule>
|
||||
'@
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
## Active Directory Basics
|
||||
|
||||
- Use Server Core when possible
|
||||
- Implement Windows Admin Center
|
||||
- Regular Windows Update
|
||||
- PowerShell remoting over WinRM
|
||||
- Active Directory best practices
|
||||
```powershell
|
||||
# Promote server to a new domain controller in a new forest
|
||||
Install-ADDSForest `
|
||||
-DomainName "corp.example.com" `
|
||||
-DomainNetBIOSName "CORP" `
|
||||
-InstallDns:$true `
|
||||
-SafeModeAdministratorPassword (ConvertTo-SecureString "P@ssw0rd!" -AsPlainText -Force) `
|
||||
-Force:$true
|
||||
|
||||
# Create an Organizational Unit
|
||||
New-ADOrganizationalUnit -Name "Engineering" -Path "DC=corp,DC=example,DC=com"
|
||||
|
||||
# Create a new AD user
|
||||
New-ADUser -Name "Jane Smith" `
|
||||
-SamAccountName "jsmith" `
|
||||
-UserPrincipalName "jsmith@corp.example.com" `
|
||||
-Path "OU=Engineering,DC=corp,DC=example,DC=com" `
|
||||
-AccountPassword (ConvertTo-SecureString "TempP@ss1" -AsPlainText -Force) `
|
||||
-Enabled $true `
|
||||
-ChangePasswordAtLogon $true
|
||||
|
||||
# Add user to a group
|
||||
Add-ADGroupMember -Identity "Domain Admins" -Members "jsmith"
|
||||
|
||||
# Search for users in an OU
|
||||
Get-ADUser -Filter * -SearchBase "OU=Engineering,DC=corp,DC=example,DC=com" | Select-Object Name, SamAccountName, Enabled
|
||||
|
||||
# Disable a user account
|
||||
Disable-ADAccount -Identity "jsmith"
|
||||
|
||||
# Unlock a locked-out account
|
||||
Unlock-ADAccount -Identity "jsmith"
|
||||
|
||||
# Reset a user password
|
||||
Set-ADAccountPassword -Identity "jsmith" -Reset -NewPassword (ConvertTo-SecureString "NewP@ss1" -AsPlainText -Force)
|
||||
|
||||
# List all domain controllers
|
||||
Get-ADDomainController -Filter * | Select-Object Name, IPv4Address, Site
|
||||
|
||||
# Check AD replication status
|
||||
Get-ADReplicationPartnerMetadata -Target "dc01.corp.example.com"
|
||||
repadmin /replsummary
|
||||
```
|
||||
|
||||
## Windows Update Management
|
||||
|
||||
```powershell
|
||||
# Install the PSWindowsUpdate module (from PowerShell Gallery)
|
||||
Install-Module -Name PSWindowsUpdate -Force
|
||||
|
||||
# Check for available updates
|
||||
Get-WindowsUpdate
|
||||
|
||||
# Install all available updates (auto-reboot if needed)
|
||||
Install-WindowsUpdate -AcceptAll -AutoReboot
|
||||
|
||||
# Install only critical and security updates
|
||||
Install-WindowsUpdate -Category "Security Updates","Critical Updates" -AcceptAll
|
||||
|
||||
# View update history
|
||||
Get-WUHistory | Select-Object -First 20 Title, Date, Result
|
||||
|
||||
# Schedule monthly patching via Task Scheduler
|
||||
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
|
||||
-Argument "-NoProfile -Command Install-WindowsUpdate -AcceptAll -AutoReboot"
|
||||
$trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Sunday -At 2am
|
||||
Register-ScheduledTask -TaskName "MonthlyPatching" -Action $action -Trigger $trigger -User "SYSTEM" -RunLevel Highest
|
||||
|
||||
# WSUS configuration via Group Policy (registry keys)
|
||||
# HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate
|
||||
# WUServer = http://wsus.corp.example.com:8530
|
||||
# WUStatusServer = http://wsus.corp.example.com:8530
|
||||
```
|
||||
|
||||
## Event Log Analysis
|
||||
|
||||
```powershell
|
||||
# View the 50 most recent System log errors
|
||||
Get-EventLog -LogName System -EntryType Error -Newest 50
|
||||
|
||||
# Search for specific event IDs (e.g., unexpected shutdowns = 6008)
|
||||
Get-EventLog -LogName System -InstanceId 6008
|
||||
|
||||
# Use Get-WinEvent for advanced filtering (newer cmdlet)
|
||||
Get-WinEvent -FilterHashtable @{
|
||||
LogName = 'Application'
|
||||
Level = 2 # Error
|
||||
StartTime = (Get-Date).AddDays(-1)
|
||||
} | Select-Object TimeCreated, Id, Message -First 20
|
||||
|
||||
# Search Security log for failed logons (Event ID 4625)
|
||||
Get-WinEvent -FilterHashtable @{
|
||||
LogName = 'Security'
|
||||
Id = 4625
|
||||
} | Select-Object TimeCreated, @{N='Account';E={$_.Properties[5].Value}}, @{N='Source';E={$_.Properties[19].Value}} -First 30
|
||||
|
||||
# Export events to CSV for analysis
|
||||
Get-WinEvent -FilterHashtable @{ LogName='System'; Level=1,2 } |
|
||||
Export-Csv -Path C:\Logs\system-errors.csv -NoTypeInformation
|
||||
|
||||
# Clear old event log entries (use cautiously)
|
||||
Clear-EventLog -LogName Application
|
||||
|
||||
# Set maximum log size
|
||||
Limit-EventLog -LogName Application -MaximumSize 512MB -OverflowAction OverwriteAsNeeded
|
||||
```
|
||||
|
||||
## Scheduled Tasks
|
||||
|
||||
```powershell
|
||||
# Create a scheduled task to run a script daily at 3 AM
|
||||
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
|
||||
-Argument "-NoProfile -File C:\Scripts\daily-maintenance.ps1"
|
||||
$trigger = New-ScheduledTaskTrigger -Daily -At 3am
|
||||
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -DontStopOnIdleEnd
|
||||
Register-ScheduledTask -TaskName "DailyMaintenance" -Action $action -Trigger $trigger -Settings $settings -User "SYSTEM"
|
||||
|
||||
# List all scheduled tasks
|
||||
Get-ScheduledTask | Where-Object { $_.State -ne 'Disabled' } | Select-Object TaskName, State, TaskPath
|
||||
|
||||
# Run a task immediately
|
||||
Start-ScheduledTask -TaskName "DailyMaintenance"
|
||||
|
||||
# Disable and remove a task
|
||||
Disable-ScheduledTask -TaskName "DailyMaintenance"
|
||||
Unregister-ScheduledTask -TaskName "DailyMaintenance" -Confirm:$false
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Diagnostic Command | Common Fix |
|
||||
|---|---|---|
|
||||
| IIS site returns 503 | `Get-WebAppPoolState` | Restart the application pool; check Event Log for crash |
|
||||
| High CPU on server | `Get-Process \| Sort CPU -Desc` | Identify process; check for runaway w3wp or service |
|
||||
| Disk running low | `Get-PSDrive -PSProvider FileSystem` | Clear temp files, IIS logs, Windows Update cache |
|
||||
| AD account locked out | `Search-ADAccount -LockedOut` | `Unlock-ADAccount`; find lockout source in Security log |
|
||||
| Windows Update fails | `Get-WindowsUpdate -Verbose` | Run `sfc /scannow`, reset update components |
|
||||
| Service fails to start | `Get-EventLog -LogName System -Newest 20` | Check dependencies, credentials, and port conflicts |
|
||||
| RDP connection refused | `Get-ItemProperty 'HKLM:\System\...\Terminal Server'` | Ensure RDP is enabled and firewall allows port 3389 |
|
||||
| DNS resolution fails | `Resolve-DnsName example.com` | Check DNS server settings and forwarder config |
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `linux-administration` -- Cross-platform comparison and hybrid management
|
||||
- `ssh-configuration` -- SSH access for Windows OpenSSH Server
|
||||
- `user-management` -- Parallel concepts for Linux user/group management
|
||||
- `systemd-services` -- Linux equivalent of Windows Services and Task Scheduler
|
||||
- `performance-tuning` -- Performance monitoring and optimization patterns
|
||||
|
||||
Reference in New Issue
Block a user