From LSM to Netfilter in OCI: Demystifying SELinux and Opening Ports on Ubuntu Minimal 24.04
Back to blog

From LSM to Netfilter in OCI: Demystifying SELinux and Opening Ports on Ubuntu Minimal 24.04

9/15/2026 · 3 min · Infrastructure

Managing Linux servers often reveals that what seems like a simple permission or networking problem is actually the result of kernel-level architectural decisions and cloud provider configurations. Recently, I tackled two real-world scenarios on Ubuntu 24.04 (Noble Numbat) (specifically on Oracle Cloud Infrastructure (OCI) instances) that demonstrate why understanding kernel internals is far more effective than running commands blindly.

Below is the technical breakdown of both challenges: managing LSM (Linux Security Modules) by demystifying SELinux on Ubuntu, and resolving the classic issue where a port in TCP_LISTEN refuses to answer external connections.


Scenario 1: SELinux on Ubuntu: understanding the kernel and lsms#

In the Debian and Ubuntu ecosystem, the default LSM has always been AppArmor. SELinux typically becomes relevant when explicitly installed via packages (selinux-basics, selinux-policy-default) or when using specialized custom cloud images.

How SELinux operates in the kernel#

SELinux enforces mandatory access control by associating security labels with resources via extended attributes (xattr) on filesystem inodes (security.selinux). Whenever a process attempts a sensitive syscall (such as open(), bind(), or execve()), the kernel invokes the security_compute_av hook. If the system is in Enforcing mode, it queries the AVC (Access Vector Cache) to validate the context transition.

When disabling or softening SELinux, there are three distinct levels of intervention:

1. Runtime disabling (no reboot required)#

To immediately stop policy enforcement without rebooting, switch to Permissive mode:

sudo setenforce 0

In this mode, the kernel still generates audit logs in the audit subsystem or dmesg, but allows all syscalls to proceed:

getenforce
# Expected output: Permissive

2. Persistent configuration via file#

To preserve permissive or disabled behavior across reboots, edit /etc/selinux/config:

# /etc/selinux/config
SELINUX=permissive
SELINUXTYPE=default

Technical note: In modern kernels (Linux 5.6+), setting SELINUX=disabled in this file only disables policy loading in userspace; the underlying SELinux kernel hooks remain registered. To deactivate the module entirely, boot parameters must be modified.

3. Disabling via kernel parameters (GRUB)#

To completely disable SELinux initialization and ensure AppArmor acts as the primary LSM without hook contention, edit /etc/default/grub:

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash selinux=0 lsm=landlock,lockdown,yama,integrity,apparmor,bpf"

Update GRUB and reboot:

sudo update-grub
sudo reboot

After rebooting, verify the active LSM order:

cat /sys/kernel/security/lsm
# Expected output: landlock,lockdown,yama,integrity,apparmor,bpf

Scenario 2: Network Port Blocking on Ubuntu Minimal 24.04 (OCI)#

The second scenario is a common pitfall in Oracle Cloud: you deploy a service (such as a Docker container, Node.js backend, or reverse proxy), verify the port is in LISTEN status, configure the ingress rule in the VCN console, but external requests consistently time out.

1. Verifying the local socket#

Ensure the application process is bound to the wildcard interface:

sudo ss -tulpn | grep 8088

2. The OCI Ubuntu minimal iptables trap#

Ubuntu 24.04 Minimal images on OCI include pre-configured iptables rule sets loaded via netfilter-persistent. At the bottom of the INPUT chain, a default rule drops any packet not explicitly accepted earlier:

REJECT all -- anywhere anywhere reject-with icmp-host-prohibited

If you execute sudo iptables -A INPUT -p tcp --dport 8088 -j ACCEPT, the -A (append) flag places your rule below the REJECT line. The packet hits the rejection rule before reaching your accept directive.

Inserting the rule correctly#

Insert the rule at the top of the chain using -I INPUT 1:

# Insert rule at position 1 of the INPUT chain
sudo iptables -I INPUT 1 -p tcp --dport 8088 -j ACCEPT

# Save immediately to preserve across reboots
sudo netfilter-persistent save

3. Inspecting the Netfilter backend (iptables-nft)#

On Ubuntu 24.04, iptables defaults to the nftables translation layer (iptables-nft). Inspect active chains along with packet counters:

sudo iptables -L INPUT -n -v --line-numbers

Monitor the pkts column while triggering a connection test from an external machine:

nc -zv -w 3 <OCI_PUBLIC_IP> 8088

If the pkts counter increments, the packet successfully cleared the host firewall and reached the application socket. To explore cloud-edge ingress rules and subnet routing in detail, review the guide on why opening ports in Oracle Cloud fails.


Conclusion: production takeaways#

Working with cloud instances requires treating the operating system as an integrated security and networking stack:

  1. Avoid competing LSMs: On Ubuntu, retain AppArmor as the primary module unless specific Type Enforcement policies are mandatory.
  2. Respect Netfilter chain ordering: On OCI Ubuntu instances, always insert custom rules at the top with -I INPUT 1 and persist them using netfilter-persistent save.
  3. Verify the full network path: Always audit the three distinct layers, Cloud VCN Security Lists / NSGs, host kernel Netfilter rules, and application socket binding (0.0.0.0).

Was this article helpful?

Leave a quick reaction to help prioritize future technical guides:

CC BY-NC

This post is licensed under CC BY-NC.

Comments

Join the discussion below.

0 comments