Search This Blog

Monday, August 31, 2026

SSH Remote port mapping

SSH port forwarding (tunneling) is one of the most powerful utilities in a systems administrator's toolkit. It allows you to securely traverse firewalls, access private network services, and expose internal ports without modifying router configurations or setting up full VPNs.


Common Flag Breakdown

When creating background tunnels, these flags are commonly combined to keep the session silent, secure, and persistent:

  • -f: Requests SSH to go to the background just before command execution (prompts for credentials if needed, then detaches).
  • -N: Do not execute a remote command (useful when only forwarding ports).
  • -C: Enables gzip compression of all data transferred over the tunnel.
  • -g: Allows remote hosts on your local network to connect to local forwarded ports (binds to 0.0.0.0 instead of 127.0.0.1).
  • -p <port>: Specifies the remote SSH server daemon port if it differs from the default port 22.

1. Local Port Forwarding (-L)

Local forwarding opens a listening port on your client machine and routes incoming traffic through the SSH tunnel to a destination reachable from the remote SSH server.

ssh -C -f -N -g -L [local_bind_port]:[target_destination_ip]:[target_port] user@ssh_jump_host

Practical Scenario: Accessing an internal web dashboard (10.3.32.26:80) located behind an edge gateway (192.168.190.115):

ssh -f -N -l root -L 8500:10.3.32.26:80 192.168.190.115

Visiting http://localhost:8500 in your local browser will now tunnel directly to 10.3.32.26:80.


2. Remote (Reverse) Port Forwarding (-R)

Remote forwarding opens a listening port on the remote SSH server and directs all incoming connections back to a designated port accessible from your local system. This allows you to expose a local service to an external server.

ssh -C -f -N -g -R [remote_listen_port]:[target_destination_ip]:[target_port] user@remote_server

Practical Scenario: Forwarding traffic received on remote server 174.139.9.66:8080 directly into your local machine's HTTP service on port 80:

ssh -C -f -N -g -R 8080:127.0.0.1:80 master@174.139.9.66

(Note: For remote clients to connect to the remote port, ensure GatewayPorts yes is set in the remote server's /etc/ssh/sshd_config.)


3. Dynamic Application Forwarding / SOCKS5 Proxy (-D)

Instead of mapping a single port, dynamic forwarding allocates a local port acting as a SOCKS4/SOCKS5 proxy. Traffic sent through this proxy is automatically routed dynamically depending on the protocol and requested destination host.

ssh -C -f -N -D 1080 user@proxy_jump_host

Configure your web browser or system network settings to use SOCKS proxy 127.0.0.1:1080 to route all browsing traffic through the remote host securely.


Comparison Summary

Forwarding Mode Flag Where the Listening Port Lives Primary Use Case
Local -L Client / Local Host Accessing remote private servers or internal DBs
Remote (Reverse) -R Remote SSH Server Exposing local development services to the internet
Dynamic -D Client / Local Host Full-traffic SOCKS5 proxy via remote jump host

Putty Log filename

Automatically logging SSH and serial sessions in PuTTY is essential for auditing, troubleshooting, and compliance. Instead of manually specifying a static filename or overwriting previous session captures, PuTTY supports built-in wildcard variables to dynamically generate unique, structured log paths based on target hosts, timestamps, and connection ports.


Configuring Dynamic Log File Names

In PuTTY, navigate to Session > Logging in the left sidebar. Under Session logging, select "All session output" (or "Printable output only"), then configure the Log file name field.

When defining your path, use the & parameter prefix to automatically inject real-time session metadata into the filename.


Supported Substitution Variables

Variable Replacement Description Example Output
&H Target Host Name / IP Address web01.example.internal
&P Destination Port Number 22
&Y Current Year (4 digits) 2026
&M Current Month (2 digits) 08
&D Current Day of Month (2 digits) 31
&T Current Time (6 digits: HHMMSS) 143015

Practical Path Examples

1. Standard Host and Timestamp Template

To organize session captures by target server name and full timestamp, enter the following pattern in the Log file name box:

C:\puttylogs\&H_&Y-&M-&D_&T.log

PuTTY automatically resolves this into clean, chronological log files:

C:\puttylogs\srv-db01.internal_2026-08-31_125405.log
C:\puttylogs\192.168.1.10_2026-08-31_130522.log

2. Dedicated Port and Daily File Naming

If connecting to non-standard ports or consolidating daily sessions into one log per host:

C:\puttylogs\&H_port&P_&Y&M&D.log

Best Practice: Saving to Default Settings

To ensure every future connection is logged automatically without setting this per profile:

  1. Open PuTTY and go to Session > Logging.
  2. Set the desired logging method and dynamic file path.
  3. Select "Always append to the end of it" under What to do if the log file already exists.
  4. Click back to Session in the left pane, click Default Settings, and hit Save.

PostgreSQL Performance Tweak

PostgreSQL is renowned for ACID compliance, reliability, and high throughput, but out-of-the-box defaults are conservative. Achieving peak performance requires tuning both the database engine parameters and the underlying Linux operating system kernel.


Part 1: Linux OS Kernel Tuning with Tuned

Modern Linux distributions use tuned to dynamically manage system profiles, CPU governors, and virtual memory parameters without manual, error-prone edits to /etc/sysctl.conf.

1. Install and Enable Tuned

dnf -y install tuned
systemctl enable --now tuned

2. Create a Dedicated PostgreSQL Profile

Default profiles (like virtual-guest or power-saving modes) throttle CPU frequency and mismanage dirty buffer flushing. Create a custom profile tailored for database workloads:

mkdir -p /etc/tuned/postgresql-custom

cat << 'EOF' > /etc/tuned/postgresql-custom/tuned.conf
[main]
summary=Tuned profile optimized for PostgreSQL workloads

[bootloader]
cmdline=transparent_hugepage=never

[cpu]
governor=performance
energy_perf_bias=performance
min_perf_pct=100

[sysctl]
# Reduce swappiness to keep DB pages in RAM
vm.swappiness = 10

# Flush dirty memory pages to disk continuously to avoid checkpoint I/O spikes
vm.dirty_background_ratio = 3
vm.dirty_ratio = 10
vm.dirty_expire_centisecs = 500
vm.dirty_writeback_centisecs = 250

# Memory allocation & networking
vm.overcommit_memory = 0
net.ipv4.tcp_timestamps = 0
EOF

3. Activate the Profile

tuned-adm profile postgresql-custom
tuned-adm active

Part 2: Core PostgreSQL Configuration (postgresql.conf)

1. Memory Allocation

  • shared_buffers: Set to 25% of total system RAM for dedicated database servers.
  • effective_cache_size: Estimate how much memory is available for disk caching by the OS and PostgreSQL (typically set to 50% to 75% of total RAM).
  • work_mem: Memory allocated per sorting operation or complex hash table (start conservatively at 32MB64MB). Watch logs for temporary file creation:
    work_mem = 64MB
  • maintenance_work_mem: Memory used for maintenance operations such as VACUUM, CREATE INDEX, and foreign key checks. Set to ~10% of system RAM (up to 1GB2GB):
    maintenance_work_mem = 1GB

2. Structured Logging

PostgreSQL logging is lightweight and vital for identifying slow queries, checkpoint stalls, and temp file writes:

logging_collector = on
log_destination = 'csvlog'
log_directory = 'pg_log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_min_duration_statement = 250ms
log_temp_files = 0
log_checkpoints = on

Part 3: Schema Design & Query Anti-Patterns

  • Don't Use the DB as a Queue or Cache: Avoid putting ephemeral task queues (like Celery), session data, or frequent heartbeat counters in transactional tables.
  • Split Hot and Cold Columns: If a table contains frequently modified fields (e.g., last_active_at) alongside wide, static data, split the hot column into a separate 1-to-1 table to minimize table bloat and write amplifications.
  • Avoid Large IN (...) Clauses: Passing massive lists inside IN queries prevents efficient index scans; join against a temporary table or use unnest() with an array parameter instead.
  • Beware of Unanchored Wildcards: Queries using ILIKE '%pattern%' cannot use standard B-Tree indexes. Use Trigram (pg_trgm) GIN/GiST indexes for partial text search.

Part 4: Maintenance, Indexing & Replication

1. Monitor Table Bloat and Index Usage

Check for sequential scans and unused indexes using built-in statistics views:

-- Identify tables with heavy sequential scans
SELECT relname, seq_scan, seq_tup_read, idx_scan 
FROM pg_stat_user_tables 
WHERE seq_scan > 0 
ORDER BY seq_tup_read DESC LIMIT 10;

2. Safe Schema Migrations on Large Tables

Adding a column with a default value or adding non-concurrent indexes will take an exclusive table lock (ACCESS EXCLUSIVE), blocking all read and write traffic:

  • Always build indexes concurrently: CREATE INDEX CONCURRENTLY ...
  • Add nullable columns first, populate data in batches, and add constraints subsequently.

3. Backups vs. High Availability

  • pg_dump: Ideal for logical exports and development copies, but becomes too slow for multi-hundred gigabyte databases.
  • Physical Streaming Replication & WAL Archiving: Use tools like pgBackRest or native WAL shipping to maintain byte-for-byte read replicas and enable point-in-time recovery (PITR).
```

How to display a zenity/GUI window to the user from a root cron job

Running a background maintenance task via root's crontab is straightforward, but notifying a logged-in desktop user upon completion can be tricky. Because cron runs in an isolated, non-interactive environment without access to the user's graphical session, GUI tools like zenity, kdialog, or notify-send fail by default. To display a dialog box on the active user's desktop, you must explicitly provide the correct display and authorization parameters.


The Problem: Cron's Isolated Environment

When root runs a cron job, two critical environment variables are absent:

  • DISPLAY: Tells the graphical application which X server display to render on (usually :0.0 or :0).
  • XAUTHORITY: Points to the user's .Xauthority cookie file, which authorizes applications to connect to that X server session.

Solution 1: Run Zenity via su with Environment Variables

The standard way to show a Zenity dialog is to execute the command as the target desktop user while exporting the display and authority variables:

su username -c 'DISPLAY=:0.0 XAUTHORITY=/home/username/.Xauthority zenity --info --title="Task Completed" --text="Root cron task has finished successfully."'

(Replace username and /home/username with the actual active user and their home directory path.)


Solution 2: Dynamic User Detection Script

If you manage multi-user workstations or don't want hardcoded usernames, use a wrapper script that automatically detects the currently logged-in desktop user, finds their .Xauthority, and triggers the popup.

Save the following script to /usr/local/bin/notify_user.sh:

#!/usr/bin/env bash
set -euo pipefail

# 1. Identify the user owning the current X session
ACTIVE_USER=$(who | awk '$0 ~ /(:0|\(:0\))/ {print $1; exit}')

if [ -z "${ACTIVE_USER}" ]; then
    # Fallback: grab the first active graphical session
    ACTIVE_USER=$(who | awk '{print $1; exit}')
fi

USER_HOME=$(eval echo "~${ACTIVE_USER}")
export DISPLAY=":0.0"
export XAUTHORITY="${USER_HOME}/.Xauthority"

# 2. Execute Zenity under the active user's environment
if [ -f "${XAUTHORITY}" ]; then
    su "${ACTIVE_USER}" -c "DISPLAY=${DISPLAY} XAUTHORITY=${XAUTHORITY} zenity --info --title=\"${1:-Notification}\" --text=\"${2:-Background task completed.}\""
else
    # Modern Wayland/Desktop DBus fallback (notify-send)
    USER_ID=$(id -u "${ACTIVE_USER}")
    su "${ACTIVE_USER}" -c "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/${USER_ID}/bus notify-send \"${1:-Notification}\" \"${2:-Background task completed.}\""
fi

Make the script executable:

chmod +x /usr/local/bin/notify_user.sh

Example Cron Implementation

Call the notification script at the end of your root backup or maintenance job in /etc/crontab or sudo crontab -e:

0 2 * * * root /opt/scripts/backup.sh && /usr/local/bin/notify_user.sh "Backup Status" "Nightly system backup finished successfully."

Disable .vmem in VMware

If you run VMware Workstation or Fusion on mechanical HDDs or wear-sensitive SSDs, you may have noticed severe disk thrashing caused by background memory paging. VMware default settings often write guest memory pages to persistent .vmem files on the host disk, saturating storage I/O and causing system-wide lag.


The Solution: Force In-RAM Execution

To eliminate .vmem file generation and stop VMware from paging guest RAM to your host storage, add the following configuration block to your VM's .vmx file (or globally in settings.ini / preferences):

# Lock 100% of guest memory into physical host RAM
prefvmx.minVmMemPct = "100"

# Disable dynamic memory trimming back to host
MemTrimRate = "0"

# Prevent creation of the .vmem backing file on host disk
mainMem.useNamedFile = "FALSE"

# Disable redundant memory page sharing scans
sched.mem.pshare.enable = "FALSE"

# Automatically lock the recommended memory allocation size
prefvmx.useRecommendedLockedMemSize = "TRUE"

What Each Parameter Does

  • mainMem.useNamedFile = "FALSE": Stops VMware from allocating a full .vmem disk file matching the guest's RAM capacity.
  • prefvmx.minVmMemPct = "100": Demands that 100% of the virtual machine's provisioned RAM remains pinned in physical host memory rather than being paged out.
  • MemTrimRate = "0": Prevents the hypervisor from constantly unmapping and trimming unused guest memory, avoiding bursty host disk I/O.
  • sched.mem.pshare.enable = "FALSE": Disables transparent page sharing (TPS) scanning, eliminating unnecessary background CPU and disk comparisons.
  • prefvmx.useRecommendedLockedMemSize = "TRUE": Directs VMware to reserve and lock host RAM according to optimal memory sizing guidelines.

Implementation Note

Make sure to shut down your virtual machine completely before editing its .vmx file. Because this configuration locks the guest's entire memory space into physical host RAM, ensure your host has enough free physical memory before launching multiple VMs simultaneously.