Networking Deep Dive - SSH and SCP for Secure DevOps Workflows

Rajratan GaikwadRajratan Gaikwad
13 min read

Hello, fellas! After exploring networking fundamentals and the OSI Model, we'll explore two essential tools in every DevOps engineer's toolkit: SSH (Secure Shell) and SCP (Secure Copy Protocol).

These protocols are the backbone of secure system administration, remote server management, and file transfers in modern DevOps workflows. Whether you're deploying applications, managing infrastructure, or automating tasks, mastering SSH and SCP is crucial for maintaining security while ensuring seamless operations.

In this blog post, we'll explore SSH key management, secure file transfers with SCP/SFTP, and advanced concepts like SSH tunneling and port forwarding. Each topic will include real-world DevOps use cases and examples to reinforce your understanding.

I know some of these concepts might feel overwhelming at first, but don’t worry—this blog is just to help you grasp the theory. In future posts, we’ll get hands-on with practical activities to make everything crystal clear.

So, let's get started on securing your DevOps workflows!


Introduction

🔹 SSH (Secure Shell) allows secure remote access to servers, crucial for DevOps tasks like deployment, automation, and troubleshooting.
🔹 SCP (Secure Copy Protocol) is used to transfer files between local and remote systems securely.
🔹 Port Forwarding & Tunneling enhances security by routing traffic through encrypted SSH channels.


1️⃣ SSH Key Management for Automated Deployments

What is SSH Key Authentication?

SSH keys are used for passwordless authentication, allowing secure access to remote servers without manually entering passwords. This is essential for automated deployments in DevOps.

Understanding SSH Keys

SSH key pairs consist of two parts:

  1. Private Key: This is your secret key, which stays on your local machine. Never share it with anyone.

  2. Public Key: This key can be shared freely and is placed on servers you want to access.

When you connect to a server, your private key and the server's public key are used in a cryptographic handshake that verifies your identity without transmitting your private key or password.


Setting Up SSH Key-Based Authentication

Step 1: Generate SSH Key Pair

Run the following command on your local machine (Linux/Mac/WSL/VS Code terminal):

# Generate a new 4096-bit RSA key pair
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

# You'll be prompted to specify a file location (default is ~/.ssh/id_rsa)
# You'll also be asked to set a passphrase (recommended for security)
  • -t rsa → Uses the RSA encryption algorithm

  • -b 4096 → Creates a stronger 4096-bit key

  • -C "your_email@example.com" → Adds a comment for identification

Press Enter to save the key to ~/.ssh/id_rsa and leave the passphrase empty for automation.


Step 2: Copy SSH Key to Remote Server

Use ssh-copy-id (or manually append the key) to allow access without passwords:

# Copy your public key to a server
ssh-copy-id -i ~/.ssh/id_rsa.pub ubuntu@192.168.1.100

If ssh-copy-id is unavailable, manually add the key:

# First, display and copy your public key
cat ~/.ssh/id_rsa.pub

# Connect to the server and add the key
ssh ubuntu@192.168.1.100
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "ssh-rsa AAAA..." >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

DevOps Use Cases

DevOps Use Case 1: CI/CD Pipeline with SSH Keys

In a typical CI/CD pipeline, you need to deploy code to servers automatically. Here's how you might set it up with GitHub Actions and SSH keys:

  1. Generate a deployment key:

     ssh-keygen -t rsa -b 4096 -C "github-actions-deploy" -f ~/.ssh/github_actions
    
  2. Add the public key to your deployment server:

     ssh-copy-id -i ~/.ssh/github_actions.pub ubuntu@your-server-ip
    
  3. Add the private key as a secret in GitHub Actions:

    • Go to your GitHub repository

    • Navigate to Settings > Secrets

    • Add a new secret named SSH_PRIVATE_KEY with the content of your private key

  4. Create a GitHub Actions workflow file:

     name: Deploy to Production
    
     on:
       push:
         branches: [ main ]
    
     jobs:
       deploy:
         runs-on: ubuntu-latest
         steps:
           - uses: actions/checkout@v3
    
           - name: Set up SSH
             uses: webfactory/ssh-agent@v0.5.4
             with:
               ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
    
           - name: Deploy to server
             run: |
               ssh -o StrictHostKeyChecking=no ubuntu@your-server-ip '
                 cd /var/www/myapp &&
                 git pull &&
                 npm install &&
                 pm2 restart app
               '
    

This setup allows your CI/CD pipeline to deploy code securely without storing passwords in your codebase.

DevOps Use Case 2: Infrastructure as Code with SSH Keys

When using tools like Terraform or Ansible to manage infrastructure, SSH keys are essential for secure automation:

  1. Create a dedicated automation key:

     ssh-keygen -t rsa -b 4096 -C "ansible-automation" -f ~/.ssh/ansible
    
  2. Use the key in your Ansible inventory:

     # inventory.ini
     [webservers]
     web1 ansible_host=192.168.1.101
     web2 ansible_host=192.168.1.102
    
     [all:vars]
     ansible_user=ubuntu
     ansible_ssh_private_key_file=~/.ssh/ansible
    
  3. Create a simple Ansible playbook:

     # deploy.yml
     ---
     - hosts: webservers
       become: yes
       tasks:
         - name: Update apt cache
           apt:
             update_cache: yes
    
         - name: Install Nginx
           apt:
             name: nginx
             state: present
    
         - name: Start Nginx
           service:
             name: nginx
             state: started
             enabled: yes
    
  4. Run your Ansible playbook:

     ansible-playbook -i inventory.ini deploy.yml
    

This setup allows you to automate infrastructure management securely across multiple servers.

SSH Key Best Practices for DevOps

For secure DevOps operations:

  1. Use strong keys: Generate 4096-bit RSA keys or Ed25519 keys.

  2. Set passphrases: Always use passphrases for keys used by humans.

  3. Use SSH agents: Use ssh-agent to avoid typing passphrases repeatedly.

  4. Rotate keys regularly: Establish a process for key rotation.

  5. Audit access: Regularly review who has access to what.

  6. Limit key scope: Use specific keys for specific purposes.


2️⃣ Securely Transfer Files with SCP & SFTP

What is SCP?

SCP (Secure Copy Protocol) is used to securely copy files between local and remote systems over SSH.


SCP for File Transfers

Copy File from Local Machine to Remote Server

scp myfile.txt user@remote-server-ip:/home/user/

Copy File from Remote Server to Local Machine

scp user@remote-server-ip:/home/user/myfile.txt .

Copy an Entire Directory

scp -r my_folder/ user@remote-server-ip:/home/user/

Summary

# Basic syntax
# scp source destination

# Copy a local file to a remote server
scp /path/to/local/file.txt ubuntu@192.168.1.100:/path/on/remote/

# Copy a remote file to local machine
scp ubuntu@192.168.1.100:/path/on/remote/file.txt /path/to/local/

# Copy directory recursively
scp -r /path/to/local/directory ubuntu@192.168.1.100:/path/on/remote/

# Use a specific identity file
scp -i ~/.ssh/id_rsa_prod /path/to/local/file.txt ubuntu@192.168.1.100:/path/on/remote/

# Copy between two remote servers (executed from your local machine)
scp -3 ubuntu@192.168.1.100:/path/file.txt ubuntu@192.168.1.101:/path/

What is SFTP? (Secure File Transfer Protocol)

SFTP is an interactive file transfer session over SSH.

Step-by-Step: Use SFTP

1️⃣ Connect to Remote Server

sftp user@remote-server-ip

2️⃣ Upload a File

put myfile.txt

3️⃣ Download a File

get remote_file.txt

4️⃣ Exit SFTP

exit

DevOps Use Cases

DevOps Use Case 1: Automated Backup System

Let's create a simple automated backup system using SCP:

  1. Create a backup script:

     #!/bin/bash
     # backup.sh
    
     # Configuration
     BACKUP_DIR="/var/backups/$(date +%Y-%m-%d)"
     DB_NAME="myapp_db"
     DB_USER="dbuser"
     REMOTE_SERVER="ubuntu@backup-server"
     REMOTE_PATH="/backup/storage"
    
     # Create backup directory
     mkdir -p $BACKUP_DIR
    
     # Backup database
     echo "Backing up database..."
     mysqldump -u $DB_USER -p $DB_NAME > $BACKUP_DIR/database.sql
    
     # Backup configuration files
     echo "Backing up configuration..."
     cp /etc/nginx/sites-available/* $BACKUP_DIR/
     cp /etc/php/8.1/fpm/php.ini $BACKUP_DIR/
    
     # Compress backup
     echo "Compressing backup..."
     tar -czf $BACKUP_DIR.tar.gz $BACKUP_DIR
    
     # Transfer backup to remote server
     echo "Transferring to remote server..."
     scp $BACKUP_DIR.tar.gz $REMOTE_SERVER:$REMOTE_PATH
    
     # Clean up
     echo "Cleaning up..."
     rm -rf $BACKUP_DIR
     rm $BACKUP_DIR.tar.gz
    
     echo "Backup completed successfully!"
    
  2. Make the script executable:

     chmod +x backup.sh
    
  3. Schedule it with cron:

     # Edit crontab
     crontab -e
    
     # Add a daily backup at 2 AM
     0 2 * * * /path/to/backup.sh > /var/log/backup.log 2>&1
    

This setup ensures regular, secure backups of your critical data.

DevOps Use Case 2: Application Deployment Pipeline

Here's how to create a simple deployment pipeline for a web application using SCP:

  1. Create a deployment script:

     #!/bin/bash
     # deploy.sh
    
     # Configuration
     APP_NAME="mywebapp"
     BUILD_DIR="./dist"
     SERVER_USER="ubuntu"
     SERVERS=("192.168.1.101" "192.168.1.102")
     TARGET_DIR="/var/www/html"
    
     # Build the application (example for a Node.js app)
     echo "Building application..."
     npm ci
     npm run build
    
     # Deploy to each server
     for SERVER in "${SERVERS[@]}"; do
       echo "Deploying to $SERVER..."
    
       # Create a deployment directory
       ssh $SERVER_USER@$SERVER "mkdir -p $TARGET_DIR/$APP_NAME.new"
    
       # Copy files to the new directory
       scp -r $BUILD_DIR/* $SERVER_USER@$SERVER:$TARGET_DIR/$APP_NAME.new/
    
       # Execute remote commands to finish deployment
       ssh $SERVER_USER@$SERVER "
         # Backup current version
         if [ -d $TARGET_DIR/$APP_NAME ]; then
           mv $TARGET_DIR/$APP_NAME $TARGET_DIR/$APP_NAME.old
         fi
    
         # Switch to new version
         mv $TARGET_DIR/$APP_NAME.new $TARGET_DIR/$APP_NAME
    
         # Update permissions
         chown -R www-data:www-data $TARGET_DIR/$APP_NAME
    
         # Restart web server
         systemctl restart nginx
    
         # Remove old version after successful restart
         if [ $? -eq 0 ] && [ -d $TARGET_DIR/$APP_NAME.old ]; then
           rm -rf $TARGET_DIR/$APP_NAME.old
         fi
       "
    
       echo "Deployment to $SERVER completed!"
     done
    
     echo "All deployments completed successfully!"
    
  2. Make the script executable:

     chmod +x deploy.sh
    
  3. Run the deployment manually or integrate with CI/CD:

     ./deploy.sh
    

This script builds your application and securely deploys it to multiple servers, with a backup and rollback capability.


3️⃣ SSH Tunneling and Port Forwarding

What is SSH Port Forwarding?

SSH tunneling allows secure access to remote services through an encrypted SSH connection. It’s useful when accessing databases, private applications, or internal servers securely.

Understanding SSH Tunneling

SSH tunneling creates an encrypted channel between a local port and a remote port through an SSH connection. There are three main types of tunnels:

  1. Local Port Forwarding: Forwards a local port to a remote destination.

  2. Remote Port Forwarding: Forwards a remote port to a local destination.

  3. Dynamic Port Forwarding: Creates a SOCKS proxy for flexible routing.


SSH Tunneling for Secure Access

Local Port Forwarding

Local port forwarding connects a port on your local machine to a port on a remote server or any server accessible from the remote:

# Basic syntax
# ssh -L local_port:destination_host:destination_port ssh_server

# Example: Access a remote MySQL server through SSH
ssh -L 3306:localhost:3306 ubuntu@192.168.1.100

# Example: Access a web server behind a bastion host
ssh -L 8080:internal-web-server:80 ubuntu@bastion-host

After establishing this tunnel, you can connect to localhost:3306 or localhost:8080 on your local machine, and the connection will be securely forwarded through the SSH server.

Remote Port Forwarding

Remote port forwarding is the reverse of local forwarding. It connects a port on the remote SSH server to a port on your local machine or any server accessible from your local machine:

# Basic syntax
# ssh -R remote_port:destination_host:destination_port ssh_server

# Example: Expose a local development server to a remote machine
ssh -R 8080:localhost:3000 ubuntu@192.168.1.100

# Example: Allow remote access to a local database
ssh -R 5432:localhost:5432 ubuntu@192.168.1.100

For remote port forwarding to work, you may need to enable GatewayPorts in the SSH server configuration.

Dynamic Port Forwarding (SOCKS Proxy)

Dynamic port forwarding sets up a SOCKS proxy server on your local machine that routes traffic through the SSH connection:

# Basic syntax
# ssh -D local_port ssh_server

# Example: Create a SOCKS proxy on port 1080
ssh -D 1080 ubuntu@192.168.1.100

You can then configure your applications (like browsers) to use this SOCKS proxy (localhost:1080).

Persistent SSH Tunnels

For DevOps purposes, you often need persistent tunnels that stay active even if the connection drops. The autossh tool is perfect for this:

# Install autossh
sudo apt update
sudo apt install autossh

# Create a persistent local port forward
autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -L 3306:localhost:3306 ubuntu@192.168.1.100

# Create a persistent remote port forward
autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -R 8080:localhost:3000 ubuntu@192.168.1.100

# Create a persistent SOCKS proxy
autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -D 1080 ubuntu@192.168.1.100

Creating SSH Tunnel Services

For even more reliability, you can create a systemd service to manage your tunnels:

# Create a systemd service file
sudo nano /etc/systemd/system/ssh-tunnel.service

Add the following content:

[Unit]
Description=SSH tunnel to remote server
After=network.target

[Service]
User=ubuntu
ExecStart=/usr/bin/autossh -M 0 -N -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -L 3306:localhost:3306 ubuntu@192.168.1.100
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Enable and start the service:

sudo systemctl enable ssh-tunnel.service
sudo systemctl start ssh-tunnel.service
sudo systemctl status ssh-tunnel.service

DevOps Use Cases

DevOps Use Case 1: Securely Accessing Database in Production

As a DevOps engineer, you often need to access production databases securely without exposing database ports to the public internet:

  1. Create a local port forwarding tunnel to access the database:

     ssh -L 5432:localhost:5432 ubuntu@prod-bastion-server
    
  2. Configure your database client to connect to localhost:5432:

     # For PostgreSQL
     psql -h localhost -p 5432 -U dbuser -d mydb
    
     # For MySQL
     mysql -h localhost -P 5432 -u dbuser -p mydb
    
  3. Create a script for quick access:

     #!/bin/bash
     # db-tunnel.sh
    
     echo "Creating secure tunnel to production database..."
     ssh -L 5432:db-server:5432 ubuntu@prod-bastion-server -N &
     TUNNEL_PID=$!
    
     echo "Tunnel established (PID: $TUNNEL_PID)"
     echo "Connect to the database using: psql -h localhost -p 5432 -U dbuser -d mydb"
     echo "Press Ctrl+C to close the tunnel"
    
     # Wait for user to press Ctrl+C
     trap "kill $TUNNEL_PID; echo 'Tunnel closed'; exit" INT
     wait
    

This approach ensures that:

  • The database port is never exposed to the public internet

  • All traffic is encrypted through the SSH tunnel

  • Access requires SSH keys, providing an additional layer of security

DevOps Use Case 2: Development Environment with Remote Services

When developing applications that interact with multiple services, you can use SSH tunneling to create a local development environment that connects to remote services:

  1. Create a script to set up multiple tunnels:

     #!/bin/bash
     # dev-environment.sh
    
     # Configuration
     BASTION_SERVER="ubuntu@dev-bastion"
    
     # Start all tunnels
     echo "Setting up development environment tunnels..."
    
     # Redis tunnel
     ssh -L 6379:redis-server:6379 $BASTION_SERVER -N &
     REDIS_TUNNEL_PID=$!
     echo "Redis tunnel established (PID: $REDIS_TUNNEL_PID)"
    
     # MongoDB tunnel
     ssh -L 27017:mongodb-server:27017 $BASTION_SERVER -N &
     MONGO_TUNNEL_PID=$!
     echo "MongoDB tunnel established (PID: $MONGO_TUNNEL_PID)"
    
     # ElasticSearch tunnel
     ssh -L 9200:elasticsearch-server:9200 $BASTION_SERVER -N &
     ES_TUNNEL_PID=$!
     echo "ElasticSearch tunnel established (PID: $ES_TUNNEL_PID)"
    
     # RabbitMQ tunnel
     ssh -L 5672:rabbitmq-server:5672 $BASTION_SERVER -N &
     RABBIT_TUNNEL_PID=$!
     echo "RabbitMQ tunnel established (PID: $RABBIT_TUNNEL_PID)"
    
     echo "All tunnels established!"
     echo "Your application can now connect to these services on localhost"
     echo "Press Ctrl+C to close all tunnels"
    
     # Wait for user to press Ctrl+C
     trap "kill $REDIS_TUNNEL_PID $MONGO_TUNNEL_PID $ES_TUNNEL_PID $RABBIT_TUNNEL_PID; echo 'All tunnels closed'; exit" INT
     wait
    
  2. Add tunnel information to your project documentation:

     # Development Environment Setup
    
     ## Prerequisites
     - SSH access to the development bastion server
     - SSH key configured in your SSH agent
    
     ## Setup
     1. Run the tunnel script: `./dev-environment.sh`
     2. Configure your application to use the following connections:
        - Redis: localhost:6379
        - MongoDB: localhost:27017
        - ElasticSearch: localhost:9200
        - RabbitMQ: localhost:5672
     3. Run your application normally
    

This setup allows developers to:

  • Work with their local code while connecting to shared development services

  • Avoid installing complex dependencies locally

  • Test against realistic data and configurations

  • Maintain secure, encrypted connections to all services


Best Practices for SSH and SCP in DevOps

To ensure the security and reliability of your SSH and SCP usage in DevOps workflows, follow these best practices:

Security Best Practices

  1. Use key-based authentication exclusively: Disable password authentication where possible.

  2. Implement strong key policies:

    • Use 4096-bit RSA keys or Ed25519 keys

    • Set passphrases on keys, especially for human users

    • Rotate keys regularly

  3. Configure SSH securely:

     # Edit SSH server configuration
     sudo nano /etc/ssh/sshd_config
    
     # Recommended settings
     PasswordAuthentication no
     PermitRootLogin no
     PubkeyAuthentication yes
     MaxAuthTries 3
     ClientAliveInterval 300
     ClientAliveCountMax 2
    
  4. Use SSH certificates for larger teams:

    • Implement an SSH Certificate Authority (CA)

    • Sign user keys with the CA

    • Configure servers to trust the CA

  5. Implement jump hosts/bastion hosts:

    • Limit direct SSH access to servers

    • Route all SSH connections through bastion hosts

    • Implement enhanced logging on bastion hosts

Automation Best Practices

  1. Use SSH config files for clarity and maintainability:

     # ~/.ssh/config
     Host *
       ServerAliveInterval 60
       ServerAliveCountMax 3
    
     Host prod-*
       User ubuntu
       IdentityFile ~/.ssh/id_rsa_prod
       ProxyJump bastion-prod
    
     Host staging-*
       User ubuntu
       IdentityFile ~/.ssh/id_rsa_staging
       ProxyJump bastion-staging
    
  2. Implement SSH multiplexing for performance:

     # ~/.ssh/config
     Host *
       ControlMaster auto
       ControlPath ~/.ssh/cm_%h_%p_%r
       ControlPersist 10m
    
  3. Use SSH agents for secure key management:

     # Start SSH agent
     eval $(ssh-agent)
    
     # Add keys with timeout
     ssh-add -t 8h ~/.ssh/id_rsa
    
  4. Create reusable scripts for common operations:

    • Backup scripts

    • Deployment scripts

    • Environment setup scripts

  5. Document SSH practices and requirements:

    • Key rotation procedures

    • Access request processes

    • Troubleshooting guides


Conclusion

  • SSH and SCP are essential tools for DevOps workflows.

  • Using key-based authentication enhances security and automation.

  • Tunneling and port forwarding secure access to remote services.


Well done, guys! I hope this was a smooth ride for you—haha, just kidding! I know it might have felt like a lot, with all the concepts and examples we covered. But as I mentioned earlier, this blog is here to help you grasp the theory first. With continuous practice, SSH and SCP will become second nature to you.

By mastering these tools, you'll be able to build more secure, efficient automation pipelines and infrastructure management workflows. So, keep experimenting and applying these concepts in real-world scenarios!

Next up, we’re heading into the final topic of this networking series. In our next blog post, we'll dive into "Securing Your Web Connection: SSL and HTTPS for DevOps," where we’ll explore web security fundamentals and how to implement HTTPS for your applications. Trust me, you don’t want to miss this one!

Until next time, keep coding, automating, and advancing in DevOps! 😁

Peace out ✌️

0
Subscribe to my newsletter

Read articles from Rajratan Gaikwad directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Rajratan Gaikwad
Rajratan Gaikwad

I write about the art and adventure of DevOps, making complex topics in CI/CD, Cloud Automation, Infrastructure as Code, and Monitoring approachable and fun. Join me on my DevOps Voyage, where each post unpacks real-world challenges, explores best practices, and dives deep into the world of modern DevOps—one journey at a time!