OCI Security Best Practices
Secure your OCI tenancy with Cloud Guard, WAF, Vault, Bastion, security zones, and network security groups.
Prerequisites
- Understanding of OCI IAM and compartments
- Familiarity with network security concepts
OCI Security: A Defense-in-Depth Approach
Oracle Cloud Infrastructure was built with security as a foundational design principle, not an afterthought. OCI's architecture includes off-box network virtualization (isolating the host from the network), hardware root of trust, always-on encryption, and customer isolation at every layer. But even with the best platform security, your cloud posture depends on how well you configure and manage your resources.
This guide covers OCI's security services and best practices across identity management, network security, data protection, threat detection, and compliance. We will explore Cloud Guard, Web Application Firewall, OCI Vault, Bastion service, Security Zones, and how to implement a comprehensive security strategy that protects your workloads from the network perimeter to the data layer.
Shared Responsibility Model
Like all cloud providers, OCI follows a shared responsibility model. Oracle is responsible for securing the cloud infrastructure itself: physical data centers, hardware, hypervisors, and the network fabric. You are responsible for securing what you put in the cloud: IAM configurations, network rules, data encryption choices, application security, and OS patching for compute instances. Understanding this boundary is essential for a complete security strategy.
Cloud Guard
Cloud Guard is OCI's cloud security posture management (CSPM) service. It continuously monitors your tenancy for security weaknesses and misconfigurations, using Oracle-managed and custom detector recipes to identify problems and responder recipes to automatically remediate them. Cloud Guard is free for all OCI tenancies.
Cloud Guard works with three core concepts: Detectors identify potential security problems (like a public bucket or instance without OS updates),Problems are the specific issues found, and Responderstake action to fix problems (like making a bucket private or stopping a suspicious instance).
# Enable Cloud Guard for your tenancy
oci cloud-guard configuration update \
--compartment-id <tenancy-ocid> \
--reporting-region "us-ashburn-1" \
--status "ENABLED"
# Create a Cloud Guard target (scope of monitoring)
oci cloud-guard target create \
--compartment-id <tenancy-ocid> \
--display-name "tenancy-wide-monitoring" \
--target-resource-id <tenancy-ocid> \
--target-resource-type "COMPARTMENT" \
--target-detector-recipes '[{
"detectorRecipeId": "<oracle-managed-detector-recipe-ocid>"
}]' \
--target-responder-recipes '[{
"responderRecipeId": "<oracle-managed-responder-recipe-ocid>"
}]'
# List detected problems
oci cloud-guard problem list \
--compartment-id <tenancy-ocid> \
--lifecycle-state "ACTIVE" \
--query 'data.items[].{name:"resource-name", type:"resource-type", risk:"risk-level", detector:"detector-id", label:"labels"}' \
--output table
# Get problem details
oci cloud-guard problem get \
--problem-id <problem-ocid>
# Resolve a problem manually
oci cloud-guard problem update-problem-status \
--problem-id <problem-ocid> \
--status "RESOLVED" \
--comment "Reviewed and addressed the security finding"
# List responder activities (auto-remediation actions taken)
oci cloud-guard responder-activity list \
--problem-id <problem-ocid> \
--query 'data.items[].{type:"responder-type", rule:"responder-rule-name", result:"responder-execution-status"}' \
--output tableEnable Cloud Guard Immediately
Cloud Guard is free and should be enabled on every OCI tenancy from day one. Start with Oracle-managed detector and responder recipes, which cover common misconfigurations like public buckets, unrestricted security lists, instances with public IPs in private subnets, and unencrypted resources. As you become more familiar with your environment, clone the Oracle-managed recipes and customize them to match your organization's specific security requirements.
Security Zones
Security Zones are compartments with enforced security policies that prevent the creation of resources that do not meet specific security standards. When a compartment is associated with a Security Zone, OCI blocks any operation that would violate the zone's policies. This provides preventive security controls, not just detective controls.
For example, a Security Zone can enforce that all block volumes must be encrypted with customer-managed keys, all subnets must be private, all buckets must be private, and all databases must use private endpoints. These policies are enforced at the API level, so they cannot be bypassed regardless of how resources are created (Console, CLI, Terraform, or SDK).
# Create a security zone recipe (set of security policies)
oci cloud-guard security-recipe create \
--compartment-id <tenancy-ocid> \
--display-name "production-security-recipe" \
--security-policies '[
"ocid1.securitypolicy.oc1..deny-public-buckets",
"ocid1.securitypolicy.oc1..deny-public-ip-compute",
"ocid1.securitypolicy.oc1..require-encryption-cmk",
"ocid1.securitypolicy.oc1..deny-public-db-access"
]'
# Associate a compartment with a security zone
oci cloud-guard security-zone create \
--compartment-id <tenancy-ocid> \
--display-name "production-zone" \
--security-zone-recipe-id <recipe-ocid> \
--security-zone-target-id <production-compartment-ocid>
# List security zone violations (attempted policy breaches)
oci cloud-guard security-zone list \
--compartment-id <tenancy-ocid> \
--query 'data.items[].{name:"display-name", state:"lifecycle-state", compartment:"security-zone-target-id"}' \
--output tableOCI Vault (Key Management)
OCI Vault is a managed service for creating, managing, and rotating encryption keys and secrets. Vault integrates with most OCI services (Block Volumes, Object Storage, Autonomous Database, and more) to provide customer-managed encryption keys (CMKs). Using CMKs gives you full control over your encryption keys, including the ability to rotate, disable, or delete them.
# Create a vault
oci kms management vault create \
--compartment-id $C \
--display-name "production-vault" \
--vault-type "DEFAULT"
# Create a master encryption key (AES-256)
oci kms management key create \
--compartment-id $C \
--display-name "block-volume-key" \
--key-shape '{"algorithm": "AES", "length": 32}' \
--endpoint <vault-management-endpoint>
# Create an RSA key for signing/verification
oci kms management key create \
--compartment-id $C \
--display-name "signing-key" \
--key-shape '{"algorithm": "RSA", "length": 256}' \
--endpoint <vault-management-endpoint>
# Store a secret in the vault
oci vault secret create-base64 \
--compartment-id $C \
--vault-id <vault-ocid> \
--key-id <key-ocid> \
--secret-name "database-password" \
--secret-content-content "$(echo -n 'MyS3cretP@ss!' | base64)"
# Retrieve a secret
oci secrets secret-bundle get \
--secret-id <secret-ocid> \
--query 'data."secret-bundle-content".content' --raw-output | base64 -d
# Rotate a key (creates a new key version)
oci kms management key-version create \
--key-id <key-ocid> \
--endpoint <vault-management-endpoint>
# List keys in a vault
oci kms management key list \
--compartment-id $C \
--endpoint <vault-management-endpoint> \
--query 'data[].{name:"display-name", state:"lifecycle-state", algorithm:"key-shape".algorithm}' \
--output table
# Use a customer-managed key with Block Volume
oci bv volume create \
--compartment-id $C \
--availability-domain $AD \
--display-name "encrypted-volume" \
--size-in-gbs 100 \
--kms-key-id <key-ocid>Key Deletion Is Irreversible
Deleting a Vault key makes all data encrypted with that key permanently inaccessible. OCI enforces a minimum 7-day (default 30-day) waiting period before key deletion completes. During this period, you can cancel the deletion. Always ensure that no resources are still using a key before scheduling it for deletion. Consider disabling the key first and monitoring for errors before proceeding with deletion.
Web Application Firewall (WAF)
OCI WAF protects web applications from common attacks including SQL injection, cross-site scripting (XSS), bot traffic, and DDoS attacks. WAF can be deployed in front of load balancers, OCI Functions, and API Gateways. It inspects incoming HTTP/HTTPS requests and applies protection rules to block malicious traffic before it reaches your application.
# Create a WAF policy
oci waf web-app-firewall-policy create \
--compartment-id $C \
--display-name "prod-waf-policy" \
--actions '[
{
"name": "block-action",
"type": "RETURN_HTTP_RESPONSE",
"code": 403,
"body": {"type": "STATIC_TEXT", "text": "Blocked by WAF"},
"headers": []
},
{
"name": "allow-action",
"type": "ALLOW"
}
]' \
--request-protection '{
"rules": [
{
"name": "sql-injection-protection",
"type": "PROTECTION",
"actionName": "block-action",
"protectionCapabilities": [
{
"key": "941110",
"version": 1,
"collaborativeWeights": [],
"exclusions": {}
}
],
"isBodyInspectionEnabled": true
}
]
}'
# Create a WAF and associate it with a load balancer
oci waf web-app-firewall create \
--compartment-id $C \
--display-name "prod-waf" \
--backend-type "OCI_LOAD_BALANCER" \
--load-balancer-id <lb-ocid> \
--web-app-firewall-policy-id <waf-policy-ocid>
# List WAF instances
oci waf web-app-firewall list \
--compartment-id $C \
--query 'data.items[].{name:"display-name", state:"lifecycle-state", backend:"backend-type"}' \
--output tableBastion Service
The OCI Bastion service provides restricted and time-limited access to resources in private subnets without deploying and maintaining a dedicated bastion host VM. Bastion creates temporary SSH sessions that tunnel through OCI's infrastructure to reach instances that have no public IP address. Sessions have configurable time-to-live (TTL) and are fully audited.
# Create a bastion
oci bastion bastion create \
--compartment-id $C \
--bastion-type "STANDARD" \
--target-subnet-id <private-subnet-ocid> \
--name "prod-bastion" \
--client-cidr-block-allow-list '["203.0.113.0/24"]' \
--max-session-ttl-in-seconds 10800
# Create an SSH session to a specific instance
oci bastion session create-managed-ssh \
--bastion-id <bastion-ocid> \
--display-name "ssh-to-app-server" \
--key-type "PUB" \
--session-ttl-in-seconds 3600 \
--ssh-public-key-file ~/.ssh/id_rsa.pub \
--target-resource-id <instance-ocid> \
--target-os-username "opc" \
--target-resource-port 22
# Create a port forwarding session (for databases, web apps, etc.)
oci bastion session create-port-forwarding \
--bastion-id <bastion-ocid> \
--display-name "db-port-forward" \
--key-type "PUB" \
--session-ttl-in-seconds 3600 \
--ssh-public-key-file ~/.ssh/id_rsa.pub \
--target-private-ip "10.0.2.50" \
--target-resource-port 1521
# Get the SSH command to connect
oci bastion session get \
--session-id <session-ocid> \
--query 'data."ssh-metadata"."command"' --raw-output
# The command will look like:
# ssh -i <privateKey> -o ProxyCommand="ssh -i <privateKey> -W %h:%p -p 22
# ocid1.bastionsession...@host.bastion.<region>.oci.oraclecloud.com"
# opc@<private-ip>
# List active sessions
oci bastion session list \
--bastion-id <bastion-ocid> \
--query 'data[].{name:"display-name", state:"lifecycle-state", target:"target-resource-details"."target-resource-id", ttl:"session-ttl-in-seconds"}' \
--output tableBastion vs Jump Box
The OCI Bastion service is preferred over traditional jump box VMs because it has no compute cost (free service), no VM to patch and maintain, automatic session expiration, full audit logging of all sessions, and IP-based access restriction. Use Bastion for all SSH and RDP access to private subnet resources. Eliminate all jump boxes from your architecture and replace them with Bastion sessions.
Network Security: NSGs vs Security Lists
As covered in the networking guide, OCI provides two mechanisms for network-level access control. From a security best practices perspective, Network Security Groups (NSGs) are strongly preferred over Security Lists because they enable micro-segmentation, support referencing other NSGs as sources/destinations, and can be applied to individual VNICs rather than entire subnets.
# Security best practice: Create NSGs per application tier
# Web tier NSG
oci network nsg create \
--compartment-id $C \
--vcn-id <vcn-ocid> \
--display-name "web-tier-nsg"
# App tier NSG
oci network nsg create \
--compartment-id $C \
--vcn-id <vcn-ocid> \
--display-name "app-tier-nsg"
# DB tier NSG
oci network nsg create \
--compartment-id $C \
--vcn-id <vcn-ocid> \
--display-name "db-tier-nsg"
# Web tier: Allow HTTPS from internet, allow traffic to app tier
oci network nsg rules add --nsg-id <web-nsg-ocid> --security-rules '[
{"direction":"INGRESS", "protocol":"6", "source":"0.0.0.0/0", "sourceType":"CIDR_BLOCK",
"tcpOptions":{"destinationPortRange":{"min":443,"max":443}}, "description":"HTTPS from internet"},
{"direction":"EGRESS", "protocol":"6", "destination":"<app-nsg-ocid>", "destinationType":"NETWORK_SECURITY_GROUP",
"tcpOptions":{"destinationPortRange":{"min":8080,"max":8080}}, "description":"To app tier"}
]'
# App tier: Allow from web tier only, allow traffic to db tier
oci network nsg rules add --nsg-id <app-nsg-ocid> --security-rules '[
{"direction":"INGRESS", "protocol":"6", "source":"<web-nsg-ocid>", "sourceType":"NETWORK_SECURITY_GROUP",
"tcpOptions":{"destinationPortRange":{"min":8080,"max":8080}}, "description":"From web tier"},
{"direction":"EGRESS", "protocol":"6", "destination":"<db-nsg-ocid>", "destinationType":"NETWORK_SECURITY_GROUP",
"tcpOptions":{"destinationPortRange":{"min":1521,"max":1521}}, "description":"To db tier"}
]'
# DB tier: Allow from app tier only
oci network nsg rules add --nsg-id <db-nsg-ocid> --security-rules '[
{"direction":"INGRESS", "protocol":"6", "source":"<app-nsg-ocid>", "sourceType":"NETWORK_SECURITY_GROUP",
"tcpOptions":{"destinationPortRange":{"min":1521,"max":1521}}, "description":"From app tier only"}
]'Audit Logging
OCI Audit service automatically records every API call made to your tenancy, including who made the call, when, from what IP address, and what the result was. Audit logs are retained for 365 days and cannot be tampered with. These logs are essential for security investigations, compliance reporting, and understanding who changed what in your environment.
# Query audit events for the last 24 hours
oci audit event list \
--compartment-id $C \
--start-time "$(date -u -v-1d '+%Y-%m-%dT%H:%M:%SZ')" \
--end-time "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
--query 'data[].{time:"event-time", source:"source", type:"event-type", actor:"data"."identity"."principal-name"}' \
--output table
# Find all security list changes
oci audit event list \
--compartment-id $C \
--start-time "$(date -u -v-7d '+%Y-%m-%dT%H:%M:%SZ')" \
--end-time "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
--query 'data[?contains("event-type", `SecurityList`)].{time:"event-time", type:"event-type", actor:"data"."identity"."principal-name", ip:"data"."identity"."ip-address"}' \
--output table
# Find all user creation and deletion events
oci audit event list \
--compartment-id <tenancy-ocid> \
--start-time "$(date -u -v-30d '+%Y-%m-%dT%H:%M:%SZ')" \
--end-time "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
--query 'data[?contains("event-type", `CreateUser`) || contains("event-type", `DeleteUser`)].{time:"event-time", type:"event-type", actor:"data"."identity"."principal-name"}'
# Enable detailed logging for Object Storage (data events)
oci logging log-group create \
--compartment-id $C \
--display-name "security-logs"
oci logging log create \
--log-group-id <log-group-ocid> \
--display-name "object-storage-access-log" \
--log-type "SERVICE" \
--configuration '{
"source": {
"service": "objectstorage",
"resource": "<bucket-name>",
"category": "read",
"sourceType": "OCISERVICE"
},
"compartmentId": "'$C'"
}' \
--is-enabled trueOS Management and Patching
For compute instances, you are responsible for OS patching and security updates. OCI OS Management Hub provides centralized patch management, compliance reporting, and scheduled maintenance windows. It supports Oracle Linux, CentOS, and Windows Server.
# Check OS Management agent status on an instance
oci instance-agent plugin get \
--instanceagent-id <instance-ocid> \
--compartment-id $C \
--plugin-name "OS Management Service Agent"
# List available updates for an instance
oci os-management managed-instance list-available-updates \
--managed-instance-id <instance-ocid> \
--query 'data[].{name:name, type:"update-type", version:version}' \
--output table
# Install all security updates
oci os-management managed-instance install-all-updates \
--managed-instance-id <instance-ocid> \
--update-type "SECURITY"Security Best Practices Checklist
| Layer | Best Practice | OCI Service |
|---|---|---|
| Identity | Enable MFA for all users, use groups not individual grants | Identity Domains |
| Identity | Use dynamic groups for instance-to-service authentication | IAM Dynamic Groups |
| Network | Use NSGs for micro-segmentation, minimize security list rules | VCN NSGs |
| Network | Use private subnets for all backend resources | VCN Subnets |
| Network | Deploy WAF in front of all public-facing web applications | WAF |
| Access | Use Bastion service instead of jump boxes for SSH/RDP access | Bastion |
| Data | Use customer-managed keys (CMK) for encryption at rest | Vault |
| Data | Store secrets in Vault, never in code or environment variables | Vault Secrets |
| Detection | Enable Cloud Guard with Oracle-managed recipes | Cloud Guard |
| Prevention | Use Security Zones for production compartments | Security Zones |
| Audit | Enable service logging for critical resources | Logging, Audit |
| Patching | Automate OS patching with scheduled maintenance windows | OS Management Hub |
Do Not Disable Default Encryption
All OCI services encrypt data at rest by default using Oracle-managed keys. This encryption is always on and cannot be disabled. When using customer-managed keys (CMK), you gain additional control over the encryption keys but also take on the responsibility of managing key lifecycle. If you delete or disable a CMK, all data encrypted with that key becomes permanently inaccessible. Always have a key management process and rotation policy in place before using CMKs.
Key Takeaways
- 1Cloud Guard provides automated threat detection and response across your tenancy.
- 2Security Zones enforce security posture policies that prevent insecure resource configurations.
- 3OCI Vault integrates with most services for envelope encryption with customer-managed keys.
- 4Bastion service provides managed, time-limited SSH and RDP access without public IPs.
Frequently Asked Questions
What is Cloud Guard?
How do security zones differ from regular compartments?
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.