Skip to content

Instantly share code, notes, and snippets.

@jeffersfp
Last active May 25, 2025 18:36
Show Gist options
  • Save jeffersfp/feaaa6212f1fa771d347e93fc19a6ecd to your computer and use it in GitHub Desktop.
Save jeffersfp/feaaa6212f1fa771d347e93fc19a6ecd to your computer and use it in GitHub Desktop.
Scrip to connect to OCI Compute Instance through Bastion service

OCI Bastion SSH Connection Script Documentation

Overview

The oci-bastion-ssh-connect.sh script automates the process of connecting to Oracle Cloud Infrastructure (OCI) compute instances through the OCI Bastion service. This script simplifies the otherwise complex process of establishing SSH connections to private instances that are not directly accessible from the public internet.

Prerequisites

  1. OCI CLI: Must be installed and configured on your system.
  2. SSH Keys: A valid SSH key pair (public and private keys).
  3. OCI Bastion Service: A configured Bastion service in your OCI environment.
  4. Configuration File: A .env file with the required parameters.

Configuration

The script uses a .env file for configuration. Create this file in the same directory as the script with the following parameters:

# Configuration - Update these values with your actual information
BASTION_ID=ocid1.bastion.oc1.region.xxxx          # Your Bastion service OCID
INSTANCE_ID=ocid1.instance.oc1.region.xxxx        # Target instance OCID
PRIVATE_KEY=$HOME/.ssh/id_ed25519                 # Path to your SSH private key
PUBLIC_KEY=$HOME/.ssh/id_ed25519.pub              # Path to your SSH public key
TARGET_IP=10.0.1.xxx                              # Private IP of target instance
TARGET_PORT=22                                    # SSH port (usually 22)
USERNAME=ubuntu                                   # Username on target instance
REGION=region-name                                # Your OCI region (e.g., us-ashburn-1)
SESSION_TTL=10800                                 # Session time-to-live in seconds (3 hours)
SESSION_MIN_REMAINING_SECONDS=600                 # Min remaining time (10 minutes)

Features

  1. Session Management:

    • Automatically checks for existing active Bastion sessions
    • Calculates and displays remaining session time
    • Offers to reuse existing sessions or create new ones
  2. User Experience:

    • Visual progress indicators (spinner)
    • Informative emojis and formatted messages
    • Clean error handling and exit management
  3. Security:

    • Automatic session cleanup on exit
    • Secure handling of SSH credentials
    • Session time limit enforcement

Usage

  1. Clone the repository containing the script.
  2. Create a .env file with your configuration.
  3. Make the script executable:
    chmod +x oci-bastion-connect.sh
  4. Run the script:
    ./oci-bastion-connect.sh

Workflow

  1. The script checks for existing active Bastion sessions.
  2. If a valid session exists, it offers to reuse it.
  3. If no session exists or the user declines to reuse an existing one, it creates a new session.
  4. The script waits for the session to become active, displaying a spinner.
  5. Once active, it establishes an SSH connection to the target instance.
  6. When the user exits the SSH session (Ctrl+C), the script cleans up by deleting the Bastion session.

Error Handling

  • Session creation failures: The script will display an error message if it cannot create a Bastion session.
  • Session activation timeout: If a session takes too long to activate, the script will still attempt to connect.
  • Interruption handling: Pressing Ctrl+C will properly clean up resources and exit.

Notes

  • The script works with both Linux and macOS (handles date command differences).
  • Session time-to-live (TTL) defaults to 3 hours (10800 seconds).
  • Minimum session time remaining defaults to 10 minutes (600 seconds).
#!/usr/bin/env bash
# oci-connect.sh - Script to connect to an OCI instance through Bastion service
# Usage: ./oci-connect.sh
source .env
# Function to show a spinning indicator
spinner() {
local pid=$1
local delay=0.1
local spinstr='|/-\'
while ps -p $pid > /dev/null; do
local temp=${spinstr#?}
printf " [%c] " "$spinstr"
local spinstr=$temp${spinstr%"$temp"}
sleep $delay
printf "\b\b\b\b\b\b"
done
printf " \b\b\b\b"
}
# Function to clean up on exit or interruption
cleanup() {
echo -e "\n\nExiting..."
if [ ! -z "$SESSION_ID" ]; then
echo "Deleting session $SESSION_ID"
oci bastion session delete --session-id $SESSION_ID
fi
exit 0
}
# Function to check for existing active sessions and calculate remaining time
check_existing_sessions() {
echo "πŸ” Checking for existing active sessions..."
EXISTING_SESSIONS=$(oci bastion session list \
--bastion-id "$BASTION_ID" \
--session-lifecycle-state ACTIVE \
--all \
--query "data[*].{id:id,\"time-created\":\"time-created\"}" \
--raw-output 2>/dev/null)
if [ -z "$EXISTING_SESSIONS" ] || [ "$EXISTING_SESSIONS" = "[]" ]; then
echo "βœ… No existing active sessions found."
return 1
fi
# Parse JSON to get sessions and their creation times
echo "ℹ️ Found existing active session(s):"
# Extract session IDs and creation times
SESSION_COUNT=$(echo "$EXISTING_SESSIONS" | grep -c "id")
if [ $SESSION_COUNT -gt 0 ]; then
# Just grab the first active session
SESSION_OCID=$(echo "$EXISTING_SESSIONS" | grep '"id"' | head -1 | cut -d'"' -f4)
TIME_CREATED=$(echo "$EXISTING_SESSIONS" | grep '"time-created"' | head -1 | cut -d'"' -f4)
# Calculate remaining time
CREATED_TIMESTAMP=$(date -d "$TIME_CREATED" +%s 2>/dev/null || date -j -f "%Y-%m-%dT%H:%M:%S.%Z" "$TIME_CREATED" +%s 2>/dev/null)
CURRENT_TIMESTAMP=$(date +%s)
ELAPSED_SECONDS=$((CURRENT_TIMESTAMP - CREATED_TIMESTAMP))
REMAINING_SECONDS=$((SESSION_TTL - ELAPSED_SECONDS))
# Set default minimum session time to 10 minutes (600 seconds) if not defined
: ${SESSION_MIN_REMAINING_SECONDS:=600}
if [ $REMAINING_SECONDS -le ${SESSION_MIN_REMAINING_SECONDS} ]; then
echo "⚠️ Session has expired or is about to expire."
return 1
fi
REMAINING_HOURS=$((REMAINING_SECONDS / 3600))
REMAINING_MINUTES=$(( (REMAINING_SECONDS % 3600) / 60 ))
echo " Session ID: $SESSION_OCID"
echo " Remaining time: $REMAINING_HOURS hours and $REMAINING_MINUTES minutes"
# Ask user if they want to reuse the existing session
read -p "πŸ“ Would you like to reuse this existing session? (y/n): " REUSE_SESSION
if [[ "$REUSE_SESSION" =~ ^[Yy]$ ]]; then
return 0
else
return 1
fi
fi
return 1
}
# Set up the cleanup trap
trap cleanup SIGINT SIGTERM
echo "=============================================="
echo " OCI Bastion SSH Connection Script"
echo "=============================================="
echo ""
# Check for existing sessions first
if check_existing_sessions; then
echo "βœ… Reusing existing session: $SESSION_OCID"
else
# Step 1: Create the Bastion session
echo "πŸ”‘ Creating new Bastion session..."
SESSION_ID=$(oci bastion session create-managed-ssh \
--bastion-id "$BASTION_ID" \
--ssh-public-key-file "$PUBLIC_KEY" \
--session-ttl $SESSION_TTL \
--target-resource-id "$INSTANCE_ID" \
--target-port "$TARGET_PORT" \
--target-os-username "$USERNAME" \
--query 'data.id' \
--raw-output)
if [ -z "$SESSION_ID" ]; then
echo "❌ Failed to create session. Check your configuration and try again."
exit 1
fi
SESSION_OCID="$SESSION_ID"
# If the session ID doesn't already include the region prefix, add it
if [[ $SESSION_ID != ocid1.bastionsession.oc1.$REGION* ]]; then
SESSION_ID="ocid1.bastionsession.oc1.$REGION.$SESSION_ID"
fi
echo "βœ… Session created successfully!"
echo " Session ID: $SESSION_OCID"
echo " Session will be valid for $((SESSION_TTL/3600)) hours"
echo ""
# Step 2: Wait for session to become active
echo "⏳ Waiting for session to become active..."
# Start a spinner in the background
(sleep 0.5; while :; do for c in / - \\ \|; do echo -en "\r$c"; sleep 0.2; done; done) &
SPINNER_PID=$!
# Poll for session status until it becomes ACTIVE
MAX_ATTEMPTS=40 # Maximum number of attempts before giving up
ATTEMPT=0
SESSION_STATUS=""
while [ "$SESSION_STATUS" != "ACTIVE" ] && [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
ATTEMPT=$((ATTEMPT+1))
# Get the current session status
SESSION_STATUS=$(oci bastion session get --session-id "$SESSION_OCID" --query 'data."lifecycle-state"' --raw-output 2>/dev/null)
# If we got a status, check it
if [ ! -z "$SESSION_STATUS" ]; then
if [ "$SESSION_STATUS" = "ACTIVE" ]; then
break
elif [ "$SESSION_STATUS" = "FAILED" ] || [ "$SESSION_STATUS" = "DELETED" ]; then
echo -e "\r❌ Session creation failed with status: $SESSION_STATUS"
kill $SPINNER_PID &>/dev/null
cleanup
exit 1
fi
fi
# Wait before polling again
sleep 3
done
# Kill the spinner
kill $SPINNER_PID &>/dev/null
if [ "$SESSION_STATUS" = "ACTIVE" ]; then
echo -e "\rβœ… Session is now active"
else
echo -e "\r⚠️ Timed out waiting for session to become active. Attempting to connect anyway..."
fi
echo ""
fi
# Step 3: Connect to the instance through the Bastion
echo "πŸ”Œ Connecting to $USERNAME@$TARGET_IP through Bastion..."
echo " Press Ctrl+C to exit when finished"
echo "=============================================="
echo ""
# Build and execute the SSH command
SSH_CMD="ssh -i $PRIVATE_KEY ProxyCommand=\"ssh -i $PRIVATE_KEY -W %h:%p $SESSION_ID@host.bastion.$REGION.oci.oraclecloud.com\" $USERNAME@$TARGET_IP"
echo "Executing: $SSH_CMD"
eval $SSH_CMD
# Once the SSH session ends, run the cleanup function
cleanup
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment