Skip to main content
OCISecurityintermediate

OCI Bastion Service Guide

Securely access private resources with OCI Bastion: managed SSH sessions, port forwarding, SOCKS5 tunneling, audit logging, and IAM policies.

CloudToolStack Team22 min readPublished Mar 14, 2026

Prerequisites

  • Understanding of SSH and network tunneling concepts
  • OCI account with bastion and compute permissions

Introduction to OCI Bastion Service

The OCI Bastion service provides restricted, time-limited SSH and RDP access to target resources in private subnets without exposing them to the public internet. It eliminates the need to deploy and maintain traditional bastion hosts (jump boxes) while providing stronger security through session-level access control, automatic session expiration, and comprehensive audit logging.

Traditional bastion hosts require ongoing maintenance: patching the OS, managing SSH keys, monitoring for unauthorized access, and ensuring the bastion itself is secure. OCI Bastion is a fully managed service that handles all of these concerns. Each session is isolated, time-limited (up to 3 hours), and automatically logged to OCI Audit for compliance.

The Bastion service supports three session types: Managed SSH for direct SSH access to instances with the Oracle Cloud Agent, SSH Port Forwardingfor tunneling any TCP traffic through the bastion, and Dynamic Port Forwarding (SOCKS5) for browsing multiple resources through a single tunnel. This guide covers all three session types, security configuration, audit integration, and production best practices.

Bastion Service Pricing

The OCI Bastion service is provided at no additional charge. You only pay for the underlying compute resources of the target instances you connect to. There are no per-session fees, per-hour charges, or bandwidth costs for the bastion service itself. This makes it a cost-effective alternative to maintaining dedicated bastion host instances.

Creating a Bastion

A bastion is the gateway resource that hosts sessions. Each bastion is associated with a specific VCN subnet and can reach target resources in that subnet and any peered or routed subnets. You can create multiple bastions for different subnets or environments, and each bastion can host multiple concurrent sessions.

When creating a bastion, you specify the target subnet, the maximum session TTL (time to live), and an optional CIDR allowlist that restricts which source IP addresses can connect to sessions. The CIDR allowlist is a critical security control that limits bastion access to your corporate network, VPN, or specific administrator workstations.

bash
# Create a bastion
oci bastion bastion create \
  --compartment-id $C \
  --bastion-type "STANDARD" \
  --name "dev-bastion" \
  --target-subnet-id <private-subnet-ocid> \
  --client-cidr-block-allow-list '["203.0.113.0/24", "198.51.100.0/24"]' \
  --max-session-ttl-in-seconds 10800 \
  --wait-for-state ACTIVE

# List bastions
oci bastion bastion list \
  --compartment-id $C \
  --query 'data[].{name:name, "lifecycle-state":"lifecycle-state", "target-subnet-id":"target-subnet-id"}' \
  --output table

# Get bastion details
oci bastion bastion get \
  --bastion-id <bastion-ocid> \
  --query 'data.{name:name, "max-session-ttl":"max-session-ttl-in-seconds", "client-cidr-list":"client-cidr-block-allow-list"}'

# Update the CIDR allowlist
oci bastion bastion update \
  --bastion-id <bastion-ocid> \
  --client-cidr-block-allow-list '["203.0.113.0/24", "10.0.0.0/8"]' \
  --max-session-ttl-in-seconds 7200

Restrict the CIDR Allowlist

Never use 0.0.0.0/0 in the client CIDR allowlist for production bastions. This allows connections from any IP address on the internet, defeating the purpose of the bastion's access controls. Use the narrowest CIDR blocks possible, typically your corporate network ranges, VPN exit IPs, or specific administrator workstation IPs. For remote teams, use a VPN and allowlist only the VPN exit IP.

Managed SSH Sessions

Managed SSH sessions provide direct SSH access to compute instances that have the Oracle Cloud Agent installed and the Bastion plugin enabled. The bastion service handles key exchange and session establishment automatically. This is the simplest session type and is recommended for routine administrative access to compute instances.

The target instance must have the Bastion plugin enabled in the Oracle Cloud Agent configuration. The plugin is available for Oracle Linux, CentOS, and Ubuntu instances. The instance does not need a public IP address or any inbound security rules for SSH.

bash
# Enable the Bastion plugin on a compute instance
oci compute instance update \
  --instance-id <instance-ocid> \
  --agent-config '{"pluginsConfig": [{"name": "Bastion", "desiredState": "ENABLED"}]}'

# Create a managed SSH session
oci bastion session create-managed-ssh \
  --bastion-id <bastion-ocid> \
  --display-name "admin-ssh-session" \
  --target-resource-id <instance-ocid> \
  --target-os-username "opc" \
  --key-type "PUB" \
  --ssh-public-key-file ~/.ssh/id_rsa.pub \
  --session-ttl-in-seconds 3600 \
  --wait-for-state ACTIVE

# Get session details including the SSH command
oci bastion session get \
  --session-id <session-ocid>

# The output includes an SSH command like:
# ssh -i <privateKey> -o ProxyCommand="ssh -i <privateKey> -W %h:%p -p 22
#   <session-ocid>@host.bastion.<region>.oci.oraclecloud.com"
#   opc@<target-private-ip>

# Connect using the generated SSH command
ssh -i ~/.ssh/id_rsa \
  -o ProxyCommand="ssh -i ~/.ssh/id_rsa -W %h:%p -p 22 <session-ocid>@host.bastion.us-ashburn-1.oci.oraclecloud.com" \
  opc@10.0.1.10

# List active sessions
oci bastion session list \
  --bastion-id <bastion-ocid> \
  --session-lifecycle-state ACTIVE \
  --query 'data[].{"display-name":"display-name", "session-type":"key-details.public-key-content", "lifecycle-state":"lifecycle-state", "time-created":"time-created"}' \
  --output table

SSH Port Forwarding Sessions

Port forwarding sessions create an SSH tunnel through the bastion to a specific IP address and port on the target network. Unlike managed SSH sessions, port forwarding sessions can reach any TCP service, not just SSH. This makes them ideal for accessing databases (MySQL on port 3306, PostgreSQL on port 5432, Oracle on port 1521), web applications, and other services running on private instances.

Port forwarding sessions do not require the Oracle Cloud Agent or Bastion plugin on the target. They work with any resource that has a private IP address and a TCP service listening on the specified port, including Autonomous Database, MySQL HeatWave, and instances in other VCNs accessible through peering.

bash
# Create a port forwarding session to a database
oci bastion session create-port-forwarding \
  --bastion-id <bastion-ocid> \
  --display-name "db-tunnel" \
  --target-private-ip "10.0.2.50" \
  --target-port 1521 \
  --key-type "PUB" \
  --ssh-public-key-file ~/.ssh/id_rsa.pub \
  --session-ttl-in-seconds 3600 \
  --wait-for-state ACTIVE

# Establish the SSH tunnel (runs in background)
ssh -i ~/.ssh/id_rsa -N -L 1521:10.0.2.50:1521 \
  -p 22 <session-ocid>@host.bastion.us-ashburn-1.oci.oraclecloud.com &

# Connect to the database through the tunnel
# sqlplus admin@localhost:1521/mydb

# Port forwarding to MySQL HeatWave
oci bastion session create-port-forwarding \
  --bastion-id <bastion-ocid> \
  --display-name "mysql-tunnel" \
  --target-private-ip "10.0.2.100" \
  --target-port 3306 \
  --key-type "PUB" \
  --ssh-public-key-file ~/.ssh/id_rsa.pub \
  --session-ttl-in-seconds 3600 \
  --wait-for-state ACTIVE

# Establish tunnel and connect
ssh -i ~/.ssh/id_rsa -N -L 3306:10.0.2.100:3306 \
  -p 22 <session-ocid>@host.bastion.us-ashburn-1.oci.oraclecloud.com &

# mysql -h 127.0.0.1 -P 3306 -u admin -p

# Port forwarding to a web application
oci bastion session create-port-forwarding \
  --bastion-id <bastion-ocid> \
  --display-name "web-app-tunnel" \
  --target-private-ip "10.0.1.20" \
  --target-port 8080 \
  --key-type "PUB" \
  --ssh-public-key-file ~/.ssh/id_rsa.pub \
  --session-ttl-in-seconds 1800 \
  --wait-for-state ACTIVE

# ssh -i ~/.ssh/id_rsa -N -L 8080:10.0.1.20:8080 \
#   -p 22 <session-ocid>@host.bastion.us-ashburn-1.oci.oraclecloud.com &
# Then open http://localhost:8080 in your browser

Dynamic Port Forwarding (SOCKS5)

Dynamic port forwarding creates a SOCKS5 proxy through the bastion, allowing you to route traffic to any resource on the target network without creating individual port forwarding sessions for each service. This is particularly useful when you need to access multiple private resources during a troubleshooting session, such as browsing web interfaces of multiple services, accessing databases, and checking application health endpoints.

bash
# Create a dynamic port forwarding session
oci bastion session create-port-forwarding \
  --bastion-id <bastion-ocid> \
  --display-name "socks-proxy" \
  --target-private-ip "10.0.1.10" \
  --target-port 22 \
  --key-type "PUB" \
  --ssh-public-key-file ~/.ssh/id_rsa.pub \
  --session-ttl-in-seconds 3600 \
  --wait-for-state ACTIVE

# Create SOCKS5 proxy
ssh -i ~/.ssh/id_rsa -N -D 1080 \
  -p 22 <session-ocid>@host.bastion.us-ashburn-1.oci.oraclecloud.com &

# Configure your browser to use SOCKS5 proxy:
# Host: 127.0.0.1
# Port: 1080
# Type: SOCKS5

# Or use curl with the SOCKS5 proxy:
curl --socks5 127.0.0.1:1080 http://10.0.1.20:8080/health
curl --socks5 127.0.0.1:1080 http://10.0.1.30:3000/dashboard

Use SSH Config for Convenience

Add bastion proxy configurations to your ~/.ssh/config file for easier access. Create a Host entry for each frequently accessed private instance with the ProxyCommand pointing to the bastion. This allows you to simply runssh dev-server instead of typing the full proxy command each time. Remember to update the session OCID each time you create a new session.

Audit Logging and Compliance

Every bastion session is automatically recorded in OCI Audit, providing a complete trail of who accessed what resources, when, and for how long. Audit events capture session creation, connection attempts, session expiration, and session deletion. This information is essential for security compliance, incident investigation, and access reviews.

Audit logs for bastion sessions include the requesting user's identity, the source IP address, the target resource, the session type, and timestamps for all session lifecycle events. You can query audit logs using the OCI Console, CLI, or API, and forward them to SIEM systems for centralized security monitoring.

bash
# Query audit logs for bastion session events
oci audit event list \
  --compartment-id $C \
  --start-time "2026-03-14T00:00:00Z" \
  --end-time "2026-03-14T23:59:59Z" \
  --query 'data[?"data"."resource-name" | contains(@, '''bastion''')].{"event-name":"data."event-name"", "source-ip":"data."source-ip-address"", "time":"event-time", "user":"data.identity."principal-name""}' \
  --output table

# Search for specific bastion events:
# com.oraclecloud.bastion.CreateSession
# com.oraclecloud.bastion.DeleteSession
# com.oraclecloud.bastion.GetSession
# com.oraclecloud.bastion.ListSessions

# Create an event rule to notify on bastion session creation
oci events rule create \
  --compartment-id $C \
  --display-name "bastion-session-audit" \
  --is-enabled true \
  --condition '{"eventType": [
    "com.oraclecloud.bastion.CreateSession"
  ], "data": {}}' \
  --actions '{"actions": [{"actionType": "ONS", "topicId": "<security-topic-ocid>", "isEnabled": true}]}'

# Delete a session manually (before TTL expires)
oci bastion session delete \
  --session-id <session-ocid> \
  --force

# Delete a bastion
oci bastion bastion delete \
  --bastion-id <bastion-ocid> \
  --force

IAM Policies for Bastion Access

Controlling who can create bastion sessions is a critical security concern. OCI IAM policies determine which users and groups can create, manage, and use bastion sessions. Follow the principle of least privilege by granting only the minimum necessary permissions.

Separate bastion management (creating/deleting bastions) from session creation (connecting to resources). Administrators should manage bastions, while developers and operators should only be able to create sessions on existing bastions with restrictions on target resources and session duration.

bash
# Policy: Allow network admins to manage bastions
oci iam policy create \
  --compartment-id $C \
  --name "bastion-admin-policy" \
  --description "Allow network admins to manage bastions" \
  --statements '["Allow group NetworkAdmins to manage bastion-family in compartment <compartment-name>"]'

# Policy: Allow developers to create sessions only
oci iam policy create \
  --compartment-id $C \
  --name "bastion-user-policy" \
  --description "Allow developers to create bastion sessions" \
  --statements '[
    "Allow group Developers to use bastion in compartment <compartment-name>",
    "Allow group Developers to manage bastion-session in compartment <compartment-name>",
    "Allow group Developers to read instances in compartment <compartment-name>",
    "Allow group Developers to read subnets in compartment <compartment-name>",
    "Allow group Developers to read vnics in compartment <compartment-name>",
    "Allow group Developers to read vnic-attachments in compartment <compartment-name>"
  ]'

# Policy: Restrict session creation by tag (e.g., only dev environments)
oci iam policy create \
  --compartment-id $C \
  --name "bastion-env-restricted" \
  --description "Allow bastion access only for dev-tagged resources" \
  --statements '["Allow group Developers to manage bastion-session in compartment <compartment-name> where target.resource.tag.Environment.value = '\''dev'\''"]'

Production Best Practices

Implementing the Bastion service in production requires balancing security with operational convenience. Follow these best practices:

Separate Bastions by Environment: Create separate bastions for production, staging, and development environments. Use different IAM policies for each bastion, restricting production bastion access to a smaller group of senior operators.

Minimize Session TTL: Set the maximum session TTL to the shortest practical duration. For routine administration, 1 hour is typically sufficient. For emergency access, 3 hours provides enough time for incident response. Sessions that are no longer needed should be deleted manually.

Use Port Forwarding Over Managed SSH: When accessing databases and applications, use port forwarding sessions instead of SSH-ing to an instance and then connecting to the database. Port forwarding provides a more direct path and avoids leaving a shell session open on the bastion target.

Audit Review: Regularly review bastion session audit logs for unusual patterns, such as sessions created outside business hours, sessions to unexpected targets, or sessions from unknown IP addresses. Automate this review using OCI Events and Functions.

Eliminate Public IPs: Use the Bastion service as the single entry point to your private infrastructure. Remove public IP addresses from all compute instances in private subnets and close inbound SSH/RDP ports in security lists and NSGs. This dramatically reduces your attack surface.

MFA Enforcement: Require multi-factor authentication for all users who have bastion session creation permissions. Configure MFA in OCI Identity Domains and enforce it through sign-on policies for the bastion service.

OCI VCN Networking Deep DiveOCI IAM, Compartments & PoliciesOCI Security Best Practices

Key Takeaways

  1. 1Bastion eliminates the need for dedicated jump box instances with a fully managed service.
  2. 2Sessions are time-limited (up to 3 hours), automatically logged, and require explicit creation.
  3. 3Port forwarding sessions enable access to databases, web apps, and any TCP service in private subnets.
  4. 4The Bastion service is completely free with no per-session or per-hour charges.

Frequently Asked Questions

Does the Bastion service cost anything?
No, the OCI Bastion service is completely free. There are no charges for creating bastions, sessions, or data transfer through the bastion. You only pay for the target compute resources you connect to. This makes it a cost-effective replacement for dedicated bastion host instances which consume compute resources 24/7.
Can I use Bastion to access databases in private subnets?
Yes, use port forwarding sessions to tunnel database connections through the bastion. Create a session targeting the database's private IP and port (e.g., 3306 for MySQL, 5432 for PostgreSQL, 1521 for Oracle). Then establish an SSH tunnel on your local machine and connect your database client to localhost. The database does not need the Bastion plugin or Oracle Cloud Agent installed.

Written by CloudToolStack Team

Cloud engineers and architects with hands-on experience across AWS, Azure, and GCP. We write guides based on real-world production patterns, not just documentation rewrites.

Disclaimer: This guide is for educational purposes. Cloud services change frequently; always refer to official documentation for the latest information. AWS, Azure, and GCP are trademarks of their respective owners.