Operating System
CentOS
Firewall
sudo firewall-cmd --permanent --add-port=9806/tcp
sudo firewall-cmd --zone=external --add-forward-port=port=22:proto=tcp:toport=3753
sudo firewall-cmd --reloadFail2ban:
sudo fail2ban-client status <segment>
sudo fail2ban-client unban <ip>Check architecture
uname -m
lscpu
cat /etc/os-releaseSSH
RSA login
At the client:
ssh-keygen -t rsa -b 4096Copy the public key and paste it on the server.
At the server:
vi ~/.ssh/authorized_keysTo disable password login for a specific user, edit /etc/ssh/sshd_config and add the line below:
Match User <username>
PasswordAuthentication noThen restart the sshd service.
Tunneling
-N: Do not execute a remote command. Useful when you only want to forward ports.-f: Requests SSH to go to the background just before command execution.-L: Local port forwarding.
ssh -N -L 33306:localhost:3306 -i /path/to/key user@remote_hostdocker-compose
curl -L "https://github.com/docker/compose/releases/download/v2.13.0/docker-compose-linux-x86_64" -o /usr/local/bin/docker-compose
sudo curl -L "https://github.com/docker/compose/releases/download/v2.20.2/docker-compose-linux-aarch64" -o /usr/local/bin/docker-composeNote: It is recommended to use the official Docker Compose plugin instead.
Ubuntu / Debian
Create a sudo user
sudo adduser deploy
sudo usermod -aG sudo deploy
id deployVerify sudo access:
su - deploy
sudo whoamiEnable SSH private key login
On your local machine:
ssh-keygen -t ed25519 -C "deploy@server"
ssh-copy-id deploy@<server-ip>If ssh-copy-id is not available, copy the public key manually:
mkdir -p ~/.ssh
chmod 700 ~/.ssh
vi ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keysDisable username and password login
Edit /etc/ssh/sshd_config:
PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
UsePAM yesOptional: restrict SSH to the deploy user only.
AllowUsers deployTest the config before reloading SSH:
sudo sshd -t
sudo systemctl reload sshCAUTION
Open a second SSH session and confirm key-based login works before closing your current session, otherwise you can lock yourself out.
Init script
Use this script to initialize Ubuntu.
sudo apt update
sudo apt upgrade -y
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo "/swapfile swap swap defaults 0 0" | sudo tee -a /etc/fstab
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo apt install btop fzf ncdu
sudo timedatectl set-timezone Asia/Kuala_Lumpur
sudo systemctl restart cronProduction-ready recommendations
After the base setup, harden the host with a few standard settings.
Install baseline packages:
sudo apt-get install unattended-upgrades fail2ban ufwBasic firewall:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw statusEnable automatic security updates:
sudo dpkg-reconfigure -plow unattended-upgradesCreate an application user and app directory:
sudo adduser deploy
sudo mkdir -p /srv/app
sudo chown -R deploy:deploy /srv/appRecommended sysctl overrides for busy servers:
# /etc/sysctl.d/99-custom.conf
fs.file-max = 65535
net.core.somaxconn = 1024
net.ipv4.tcp_fin_timeout = 15
vm.swappiness = 10Apply:
sudo sysctl --systemSwap file
Add a swap file:
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo "/swapfile swap swap defaults 0 0" | sudo tee -a /etc/fstabAppend to /etc/fstab
/swapfile swap swap defaults 0 0
Verify
sudo swapon --show
sudo free -hExtend a volume
# List block devices
lsblk -f
# Check file system
df -T
# increase volume
sudo growpart /dev/xvda 1
# resize volume
sudo resize2fs /dev/xvda1Show directory disk usage
du -shShow total disk usage in a list
ncduList the top 50 files or folders by size
sudo du -h -a ./* | sort -hr | head -n 50Delete journal logs by time
sudo journalctl --vacuum-time=1monthDelete journal logs by size
sudo journalctl --vacuum-size=1GCheck the journal
sudo journalctl -xe
sudo journalctl -u <service name> -fTruncate a file
truncate -s 0 filenameRedirect stderr to stdout
cat test.log 2>&1Delete files older than 7 days
find ~/path -type f -mtime +7 -deleteBackup script
#!/bin/bash
# Define the backup directory
backup_dir=~/backup
# Create the backup directory if it doesn't exist
mkdir -p "$backup_dir"
# Read input line by line
while IFS= read -r item; do
if [ -d "$item" ]; then
# If item is a directory, create a corresponding directory in the backup location
mkdir -p "$backup_dir/$item"
elif [ -f "$item" ]; then
# If item is a file, move it to the backup location, preserving the directory structure
mkdir -p "$backup_dir/$(dirname "$item")"
mv "$item" "$backup_dir/$item"
else
echo "Skipping $item: not a valid file or directory"
fi
done
echo "Backup completed."Misc
Time zone
To set the time zone:
sudo timedatectl set-timezone Asia/Kuala_LumpurPartition
List disks:
fdisk -lList blocks:
lsblkFile system consistency check:
fsck
e2fsckCreate a new partition from a disk:
- Run
fdisk /dev/vdb. - Press
oto create a new empty DOS partition table. - Press
nto add a new partition. - Press
pto create a primary partition. - Press
wto write the table to disk and exit. - Press Enter until the end.
- Run
mkfs.ext4 /dev/vdb1to format the partition. - Run
mount -t ext4 /dev/vdb1 /datato mount the partition to a path.
Mount automatically after restart:
- Run
lsblk -fto get the UUID of the drive. - Append
UUID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX /mount/point filesystem type options 0 0to/etc/fstab. - Run
sudo mount -ato check for errors.
Example: UUID=a95ff46e-55e7-445f-8513-37b2ad1b19ca /data ext4 defaults 0 0
Create a partition (Way 2):
# Verify unused partition. Find the volume name
lsblk -f
# Create partition
sudo parted /dev/nvme0n1 --script mklabel gpt
sudo parted /dev/nvme0n1 --script mkpart primary ext4 0% 100%
sudo partprobe
# Verify
lsblk
# nvme0n1
# └─nvme0n1p1
#
# Format partition
sudo mkfs.ext4 /dev/nvme0n1p1
# Mount partition
sudo mkdir -p /data
sudo mount /dev/nvme0n1p1 /data
# Verify
df -h | grep /data
# Auto mount
# Get uuid
sudo blkid /dev/nvme0n1p1
# UUID="1234abcd-5678-efgh"
#
# Append to /etc/fstab
UUID=1234abcd-5678-efgh /data ext4 defaults,nofail 0 2
# Test, make sure no error
sudo mount -a
# Final sanity check
lsblk -f
# nvme0n1p1 ext4 ... /dataCreate a user group and user
Do not create the group before creating the user. The group will be created automatically.
adduser username
passwd username
addgroup groupname
# for centos, use "wheel" instead of "sudo"
usermod -aG sudo usernameAllow a user to use sudo without a password:
sudo sh -c 'echo "$(logname) ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers.d/$(logname)' && sudo chmod 440 /etc/sudoers.d/$(logname)Install Docker (legacy)
cat /etc/os-release
lscpu
uname -m
syscap info -arch
sudo apt install docker.io
sudo curl -L "https://github.com/docker/compose/releases/download/v2.21.0/docker-compose-linux-x86_64" -o /usr/local/bin/docker-composeInstall official Docker
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get updateAfter configuring it, install Docker with the command below:
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Windows
Show Wi-Fi password
netsh wlan show profiles name=\<network name\> key=clear
Proxy
# Must run in admin mode
# add proxy
netsh interface portproxy add v4tov4 listenport=2222 listenaddress=0.0.0.0 connectport=22 connectaddress=127.0.0.1
# delete proxy
netsh interface portproxy delete v4tov4 listenport=2222 listenaddress=0.0.0.0Bash
To execute a script that is not Bash, for example Node.js, you can use the header below:
#!/usr/bin/env nodefzf
git status -s | fzf --multi --preview 'git diff --color=always --unified=1000 -- {-1} 'tmux
Key binding
Ctrl + b
Window:
| Key | Description |
|---|---|
| c | Create window |
| , | Rename window |
| & | Close window |
| w | List window |
| p | Previous window |
| n | Next window |
| 0 .. 9 | Switch to window 0 - 9 |
Pane:
| Key | Description |
|---|---|
| ; | Toggle last pane |
| % | Horizontal split |
| " | Vertical split |
| { | Move pane left |
| } | Move pane right |
| Up / Down / Left / Right | Switch to pane at the direction |
| o | Next pane |
| q | Show pane number |
| q 0 .. 9 | Switch to pane number x |
| Ctrl + Up / Down / Left / Right | Resize pane |
| x | Close pane |
Config
Basic config .tmux.conf
set -g base-index 1
setw -g pane-base-index 1
setw -g mouse onAdvance config
# Plugins
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'catppuccin/tmux#v2.1.3' # See https://github.com/catppuccin/tmux/tags for additional tags
set -g default-terminal "screen-256color"
set -g base-index 1
setw -g pane-base-index 1
setw -g mouse on
# set -g window-status-format '#I:#W'
# set -g window-status-current-format '#[bold]#I:#W'
# setw -g mode-keys vi
# set -gF window-status-format "#[bg=#{@ctp_surface_1},fg=#{@ctp_fg}] ##I ##T "
# set -gF window-status-current-format "#[bg=#{@ctp_mauve},fg=#{@ctp_crust}] ##I ##T "
# Configure the catppuccin plugin
set -g @catppuccin_flavor "mocha"
set -g @catppuccin_window_status_style "rounded"
set -g @catppuccin_window_text " #W"
set -g @catppuccin_window_default_text "#W"
set -g @catppuccin_window_current_text " #W"
run ~/.tmux/plugins/tmux/catppuccin.tmux
# Make the status line pretty and add some modules
set -g status-right-length 100
set -g status-left-length 100
set -g status-left ""
set -g status-right "#{E:@catppuccin_status_application}"
#set -agF status-right "#{E:@catppuccin_status_cpu}"
set -ag status-right "#{E:@catppuccin_status_session}"
set -ag status-right "#{E:@catppuccin_status_uptime}"
#set -agF status-right "#{E:@catppuccin_status_battery}"
#Initialize TMUX plugin manager
run '~/.tmux/plugins/tpm/tpm'If you face error 127 when sourcing the tmux config, it may be due to CRLF. Run this:
find ~/.config/tmux/plugins -type f \( -name '*.tmux' -o -name '*.sh' -o -name '*.conf' \) -exec perl -pi -e 's/\r$//' {} +Curl
curl --ssl-no-revoke \
-X POST 'https://url.com' \
--header 'Accept: */*' \
--header 'Content-Type: application/json' \
--data-raw ''Oh My Zsh
Themes
Custom DST:
ZSH_THEME_GIT_PROMPT_PREFIX=" [%{$fg[green]%} "
ZSH_THEME_GIT_PROMPT_SUFFIX="%{$reset_color%}]"
ZSH_THEME_GIT_PROMPT_DIRTY="%{$fg[red]%}!"
ZSH_THEME_GIT_PROMPT_CLEAN=""
function prompt_char {
if [ $UID -eq 0 ]; then echo "%{$fg[red]%}#%{$reset_color%}"; else echo $; fi
}
PROMPT='%(?, ,%{$fg[red]%}FAIL%{$reset_color%}
)
%{$fg[magenta]%}%n%{$reset_color%}@%{$fg[yellow]%}%m%{$reset_color%}: %{$fg_bold[blue]%}%~%{$reset_color%}$(git_prompt_info)
$(prompt_char) 'FTP / SFTP
Add a new user:
sudo adduser --disabled-password sftpuser
sudo mkdir -p /home/sftpuser/.ssh
sudo touch /home/sftpuser/.ssh/authorized_keys
sudo chown -R sftpuser:sftpuser /home/sftpuser/.ssh
sudo chmod 700 /home/sftpuser/.ssh
sudo chmod 600 /home/sftpuser/.ssh/authorized_keysNote: To allow ChrootDirectory and readonly access, the folder must be owned by root.
Production-ready SFTP setup
Create a chroot-safe directory layout:
sudo adduser --disabled-password sftpuser
sudo mkdir -p /sftp/sftpuser/upload
sudo chown root:root /sftp
sudo chmod 755 /sftp
sudo chown root:root /sftp/sftpuser
sudo chmod 755 /sftp/sftpuser
sudo chown sftpuser:sftpuser /sftp/sftpuser/uploadInstall the SSH public key:
sudo mkdir -p /home/sftpuser/.ssh
sudo vi /home/sftpuser/.ssh/authorized_keys
sudo chown -R sftpuser:sftpuser /home/sftpuser/.ssh
sudo chmod 700 /home/sftpuser/.ssh
sudo chmod 600 /home/sftpuser/.ssh/authorized_keysChange /etc/ssh/sshd_config:
Match User sftpuser
ForceCommand internal-sftp
AllowUsers [email protected]
PasswordAuthentication no
ChrootDirectory /home/sftpuser
PermitTunnel no
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding noRecommended hardened variant:
Match User sftpuser
ForceCommand internal-sftp
PasswordAuthentication no
PubkeyAuthentication yes
ChrootDirectory /sftp/sftpuser
PermitTunnel no
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding noValidate and reload SSH:
sudo sshd -t
sudo systemctl reload sshLog settings
Redirect logs
Add a new config file at /etc/rsyslog.d (ensure the .conf extension is used so rsyslog recognizes it):
input(type="imuxsock" HostName="SOME-NAME" Socket="/some-path" CreatePath="on")
if $fromhost == 'SOME-NAME' then /destination-path
& stopLog rotation
Amend rsyslog in /etc/logrotate.d to rotate the log file configured in rsyslog.
/destination-path
{
rotate 30
daily
dateext
dateformat -%Y%m%d
}Better settings:
/destination-path
{
rotate 30
daily
missingok
notifempty
compress
delaycompress
dateext
dateformat -%Y%m%d
postrotate
systemctl restart rsyslog > /dev/null 2>&1 || true
endscript
}- Ensure
missingokandnotifemptyare enabled to avoid errors if the log is missing or empty. - Add
compressanddelaycompressfor efficient storage. - Include
postrotateto restart rsyslog after rotation, ensuring logs continue writing properly.
Give permission to rsyslog
Modify /etc/apparmor.d/usr.sbin.rsyslogd to grant read-write permissions to a directory that is not owned by rsyslog.
/some-path rwl,r= readw= writel= link