Why this matters
Oracle Cloud opens a few doors by default. A new Virtual Cloud Network (VCN) comes with a default security list that allows SSH on TCP 22 from 0.0.0.0/0 and ICMP from anywhere. If an instance has a public IP and its subnet routes to an Internet Gateway, those rules are a live path from the internet to the host. Public load balancers, public Kubernetes API endpoints, and Autonomous Database public endpoints repeat the pattern.
Closing a port is not one switch. In OCI, ingress is allowed by security lists at the subnet level and by network security groups (NSGs) at the VNIC level. Traffic is allowed if either one permits it. You can remove a rule from the security list and still leave the port open through an NSG. That union is why people think a port is closed when it is not.
Public endpoints deserve the same treatment. A private subnet with a NAT gateway can still download updates and call external APIs, but it cannot accept inbound connections. A public load balancer, a public OKE service, or a database with a public endpoint adds a listener you have to own, patch, and monitor. Fewer public addresses means fewer things to watch.
Before you start
Make an inventory before you change rules. In the OCI Console, check Networking, Virtual Cloud Networks for security lists, NSGs, and route tables. Check Compute for public IPs, Load Balancers for public and private load balancers, Container Engine for Kubernetes for services of type LoadBalancer, and Databases for Autonomous Database access type. The OCI CLI can export the same data into a file you review with app owners.
Decide which services must stay public. A web front end on 443 through a load balancer and WAF is a normal choice. SSH, RDP, database ports, and admin consoles usually are not. Write down each intended public listener, the source CIDR, and the owner. That list becomes your test plan.
Check permissions and timing. You need rights to manage security lists, NSGs, public IPs, load balancers, and the resources behind them. Use a maintenance window for changes that can drop sessions. Keep a copy of the old rules, or manage them in Terraform so you can revert with a plan and apply.
How to do it
Work from the outside in. Find every public address and listener, remove rules that no longer have an owner, then move workloads to private subnets where they only need outbound access. Change one layer at a time and test after each step.
At the subnet and VNIC layers, edit security list ingress rules and every NSG attached to the instance, load balancer, or OKE worker node. Remove 0.0.0.0/0 and ::/0 entries for ports you do not serve publicly. Keep a narrow rule for the load balancer or bastion subnet if a backend needs it. If an NSG and a security list both allow a port, removing one is not enough.
For public endpoints, change the endpoint type where the service allows it. Make a load balancer private if it only serves internal clients. Set an Autonomous Database to a private endpoint, or keep the public endpoint with a narrow access control list. For OKE, set the service annotation for an internal load balancer instead of a public one. Use OCI Bastion for SSH instead of a public IP on port 22.
- List public IPs and listeners. In the OCI Console, open Networking, Public IP Addresses and note every reserved and ephemeral public IP. Then check Load Balancers, OKE services, and database endpoints for public listeners. Match each one to an owner and a business need.
- Remove broad ingress rules. Open each VCN, then Security Lists, and delete ingress rules that allow 0.0.0.0/0 or ::/0 to admin ports. Repeat in Network Security Groups for every VNIC. Keep only the ports and source CIDRs that the service needs.
- Move instances to private subnets. Put application and database instances in private subnets with a route to a NAT gateway for outbound traffic and a service gateway for OCI services. Remove the public IP from the VNIC or reserved public IP. Do not route the private subnet to an Internet Gateway.
- Make load balancers private or narrow. Set the load balancer to private if internal clients are the only users. If it must stay public, keep only 80 and 443 listeners, put WAF in front, and restrict backend health check and app ports to the load balancer subnet or NSG.
- Lock down OKE and database endpoints. For OKE, set service.beta.kubernetes.io/oci-load-balancer-internal to true and recreate the service so the public load balancer is removed. For Autonomous Database, switch Access Type to private endpoint, or use a public endpoint with a tight access control list.
- Replace public SSH with OCI Bastion. Create a bastion in a private subnet, set the target subnet and a narrow CIDR allowlist, and start a managed SSH session. Remove the port 22 ingress rule from the instance security list and NSG. Use session types that fit the task, such as managed SSH or port forwarding.
- Add logging and guardrails. Turn on VCN Flow Logs for subnets that face the internet and send them to Logging. Use Cloud Guard and Security Zones to flag new public IPs or public buckets. Set a review date for every remaining public listener.
How to check it worked
Test from outside the VCN, not from a host inside it. From a separate network, run a TCP check against each public IP and port, for example nc -vz 22 or nmap -Pn -p 22,3389,5432 . A refused or timed-out connection is the result you want for closed ports. Check IPv6 addresses too if the VCN uses IPv6.
Inside the instance, run ss -tulpn to list listening sockets. Compare that list with the ingress rules. A service can listen on 0.0.0.0 but still be unreachable if no rule allows it. If it is reachable and you did not expect it, check both the security list and every NSG on the VNIC.
In the OCI Console, confirm the public IP list has no address for private instances. Check that private subnets have no route to an Internet Gateway, only NAT and service gateways. Open each load balancer and confirm the listener list and visibility. For OKE, run kubectl get svc and look for EXTERNAL-IP. For Autonomous Database, confirm the access type and allowed CIDRs. Use VCN Flow Logs to see accepted traffic after the change, and recheck after the next deployment because a new service can create a public load balancer without anyone editing the VCN.
Mistakes to avoid
Removing a security list rule but leaving the NSG rule is a common miss. The reverse happens too. Treat the two layers as one control and review both for every VNIC. Also check IPv6 rules, which often sit next to IPv4 rules and get missed.
Do not remove the NAT gateway or service gateway while you close inbound ports. Private instances still need outbound access for package updates, backups, and API calls. If you remove the Internet Gateway route, keep a path for the workloads that need the internet through NAT.
Do not leave admin ports open to a wide CIDR because a bastion feels slow to set up. A public SSH rule on 22 is a standing invitation, and key-only auth does not make it private. Use OCI Bastion, a VPN, or a zero trust proxy, then remove the rule.
Watch for endpoints that recreate themselves. An OKE LoadBalancer service, an instance pool, or a Terraform apply can bring back a public listener after you close it. Put the desired state in code and review plans for public IPs and public load balancers. If you want an outside read before a customer security review, the Cloud security review from Cloudverg is a fixed price $6,500 and runs 2 to 3 weeks. It covers up to 5 cloud accounts or subscriptions, including public exposure checks and a ranked fix list.
Frequently asked questions
How do I close port 22 on an Oracle Cloud instance?
Remove the ingress rule for TCP 22 from 0.0.0.0/0 in the subnet security list and in every NSG on the VNIC. Then remove the public IP or move the instance to a private subnet. Use OCI Bastion for access.
Do security lists and NSGs both need to allow traffic?
No. OCI allows traffic if either the security list or the NSG permits it. That is why closing a port means checking both layers. A broad rule in one layer keeps the port open even if the other layer is clean.
How can I tell if an Oracle Cloud endpoint is public?
Check for a public IP on the VNIC, a public load balancer, or a service with a public endpoint, such as an Autonomous Database public access type or an OKE service with an external IP. In the Console, Public IP Addresses and Load Balancers give a quick list. Confirm the subnet route to an Internet Gateway.