Posts Tagged ‘source port’

How to Restrict a Program to Use Only a Specific Port Range for Outgoing Connections (Linux)

Wednesday, October 22nd, 2025

In some advanced system administration and security scenarios, you might want to restrict a specific program (or a user) to only use a specific range of source ports when making outbound connections. This can be important for:

  • Securing network access per service
  • Enforcing port-based identity
  • Making firewall rules easier to manage
  • Avoiding source port collisions in NAT environments

This article explains how to achieve this using iptables, cgroups, and kernel-level ephemeral port controls, with practical, copy-paste-ready examples.

What meant by "Port Range Restriction"?

When a program connects to an external server (e.g., with curl or a custom app), the kernel assigns a source port — typically from a dynamic range like 32768–60999. If you want to limit a specific app or user to only use, say, source ports 50000–51000 for all its outbound connections, you have to implement that at the firewall or kernel level.

Option 1: Using iptables with the owner Module (Per-User Limiting)

This is the most straightforward way if the program runs as a known user or UID.

Example: Allow only source ports 50000–51000 for myuser

# Block all outgoing TCP traffic from 'myuser' outside the port range

# iptables -A OUTPUT -p tcp -m owner –uid-owner myuser ! –sport 50000:51000 -j DROP

 

# Optional: for UDP

# iptables -A OUTPUT -p udp -m owner –uid-owner myuser ! –sport 50000:51000 -j DROP

Note: This will not stop the application from running — it just ensures the kernel drops packets that don't meet your policy.

Option 2: Using cgroups with net_cls + iptables (Per-Process Control)

For more precise control, especially in multi-user systems or containers, you can use Linux cgroups to classify traffic per process.

Step-by-step:

  1. Create a net_cls cgroup:

# mkdir -p /sys/fs/cgroup/net_cls/myapp

# echo 0x100001 | sudo tee /sys/fs/cgroup/net_cls/myapp/net_cls.classid

  1. Assign your process to the cgroup:

# Replace <PID> with the actual process ID

# echo <PID> | sudo tee /sys/fs/cgroup/net_cls/myapp/tasks

  1. Add iptables rule:

# iptables -A OUTPUT -m cgroup –cgroup 0x100001 -p tcp ! –sport 50000:51000 -j DROP


This gives you fine-grained per-process port restrictions.


Optional: Set Ephemeral Port Range (Globally or Per Namespace)

Linux picks ephemeral ports from a default system-wide range (32768–60999). You can limit this globally (not recommended in shared systems) or within a network namespace.

To view the current range:

# cat /proc/sys/net/ipv4/ip_local_port_range

To change the range:

# echo "50000 51000" | sudo tee /proc/sys/net/ipv4/ip_local_port_range

Warning: This affects all processes. Better used inside isolated environments (e.g., containers or netns).

Run the App with Explicit bind() to Source Port

If you control the source code of the app, the cleanest method is to explicitly bind to the desired source port(s) before making the connection.

Python Example:

import socket

 

s = socket.socket()

s.bind(('', 50001))  # bind to specific source port

s.connect(('example.com', 443))

Many apps and libraries (like curl, wget, netcat, python requests, etc.) don't expose source port binding — in those cases, control must be done externally.

Logging Violations

To log connection attempts from disallowed source ports:
 

# iptables -A OUTPUT -p tcp -m owner –uid-owner myuser ! –sport 50000:51000 \

  -j LOG –log-prefix "PORT VIOLATION: " –log-level 4
 

Containerized or Isolated Environments: Use Namespaces or Docker

For Docker or custom network namespaces, you can:

  • Use a private ephemeral port range
  • Use iptables inside the namespace
  • Isolate the entire network stack for maximum safety

Docker Example:

 # docker run -d –name limited_app –net=custom_net -p 50000-51000:50000-51000 myimage

Then configure the app inside the container to bind or use that port range.

Important Limitations

  • iptables owner match works only for UIDs, not process names.
  • If the app doesn’t bind explicitly, the kernel still picks a port — make sure the global range or netns is set correctly.
  • Some apps (e.g. Java, browsers) may use multiple ephemeral ports simultaneously — test carefully.
  • Not all programs allow setting source port via command line.

Recommended Best Practices

Let’s say you want to allow only ports 50000–51000 for a service called myapp:

  1. Create a dedicated system user:

# sudo useradd -r -s /sbin/nologin myapp

  1. Run the program as this user:

# sudo -u myapp /path/to/myapp

  1. Set the desired port range system-wide (optional):

# echo "50000 51000" | sudo tee /proc/sys/net/ipv4/ip_local_port_range

  1. Add iptables rule:

# iptables -A OUTPUT -p tcp -m owner –uid-owner myapp ! –sport 50000:51000 -j DROP

  1. (Optional) Add logging:

# iptables -A OUTPUT -p tcp -m owner –uid-owner myapp ! –sport 50000:51000 -j LOG –log-prefix "MYAPP PORT BLOCKED: "

  1. Make sure the app binds or connects correctly from that range.

Final Thoughts

Restricting outbound source port ranges per application or user is an underused but powerful technique to lock down systems, especially in:

  • High-security environments
  • Multi-tenant servers
  • NAT/firewall-managed networks
  • IDS/IPS-aware setups


You can start with simple iptables rules for users, and later adopt cgroups or container namespaces for deeper isolation.

Force specific source ports for a app like curl

To force specific source ports in general-purpose tools like curl or nc, try using iptables SNAT (with care), or consider writing wrappers/scripts that handle binding via LD_PRELOAD or custom socket logic.

Enable Apache libphp extension to interpret PHP scripts on FreeBSD 9.1

Saturday, January 12th, 2013

Enable php scripts to be interpreted / executed by PHP on freebsd
First you have to have installed and properly set up Apache from port, in my case this is Apache:

 

freebsd# pkg_info | grep -i apache
ap22-mod_fastcgi-2.4.6_3 A fast-cgi module for Apache
apache22-2.2.23_4   Version 2.2.x of Apache web server with prefork MPM.
apr-1.4.6.1.4.1_3   Apache Portability Library

I've installed it from source port /usr/ports/www/apache22, with:

freebsd# cd /usr/ports/www/apache22;
freebsd# make install clean
.....
Then to be able to start Apache from init script and make it run automatically on FBSD system reboot:

 

echo 'apache22_enable="YES"' >> /etc/rc.conf

I've also installed php5-extensions port;

freebsd# cd /usr/ports/lang/php5-extensions/
freebsd# make install clean
....
freebsd# cp -rpf /usr/local/etc/php.ini-production /usr/local/etc/php.ini

I had to select the exact Apache PHP library extensions I need, after selecting and installing, here is the list of PHP extensions installed on system:

freebsd# pkg_info | grep -i php5
php5-5.4.10         PHP Scripting Language
php5-bz2-5.4.10     The bz2 shared extension for php
php5-ctype-5.4.10   The ctype shared extension for php
php5-dom-5.4.10     The dom shared extension for php
php5-filter-5.4.10  The filter shared extension for php
php5-gd-5.4.10      The gd shared extension for php
php5-gettext-5.4.10 The gettext shared extension for php
php5-hash-5.4.10    The hash shared extension for php
php5-iconv-5.4.10   The iconv shared extension for php
php5-json-5.4.10    The json shared extension for php
php5-mbstring-5.4.10 The mbstring shared extension for php
php5-mcrypt-5.4.10  The mcrypt shared extension for php
php5-mysql-5.4.10   The mysql shared extension for php
php5-pdo-5.4.10     The pdo shared extension for php
php5-pdo_sqlite-5.4.10 The pdo_sqlite shared extension for php
php5-phar-5.4.10    The phar shared extension for php
php5-posix-5.4.10   The posix shared extension for php
php5-session-5.4.10 The session shared extension for php
php5-simplexml-5.4.10 The simplexml shared extension for php
php5-sqlite3-5.4.10 The sqlite3 shared extension for php
php5-tokenizer-5.4.10 The tokenizer shared extension for php
php5-xml-5.4.10     The xml shared extension for php
php5-xmlreader-5.4.10 The xmlreader shared extension for php
php5-xmlwriter-5.4.10 The xmlwriter shared extension for php
php5-zip-5.4.10     The zip shared extension for php
php5-zlib-5.4.10    The zlib shared extension for php

By default DirectoryIndex is not set to process index.php and .php, file extensions will not be interpreted by libphp, instead requests to .php, just opens them as plain text files.

In Apache config httpd.conf, libphp5 module should be displaying as loaded, like so:

freebsd# grep -i php5 /usr/local/etc/apache22/httpd.conf
LoadModule php5_module        libexec/apache22/libphp5.so

Next step find in /usr/local/etc/apache22/httpd.conf lines:

<IfModule dir_module>

DirectoryIndex index.html

Change

DirectoryIndex index.html

to

DirectoryIndex index.php index.html

(If you would like index.php to be processed as primary whether an Apache directory contains both .php and .html files.

After DirectoryIndex index.php, paste following;

<IfModule mod_dir.c>
    <IfModule mod_php3.c>
        <IfModule mod_php5.c>
            DirectoryIndex index.php index.php3 index.html
        </IfModule>
        <IfModule !mod_php4.c>
            DirectoryIndex index.php3 index.html
        </IfModule>
    </IfModule>
    <IfModule !mod_php3.c>
        <IfModule mod_php5.c>
            DirectoryIndex index.php index.html index.htm
        </IfModule>
        <IfModule !mod_php4.c>
            DirectoryIndex index.html
        </IfModule>
    </IfModule>
</IfModule>

Open /usr/local/etc/apache22/httpd.conf. I use vim so:

vim /usr/local/etc/apache22/httpd.conf

and press CTRL+g to go to last line of file. Last line is:

Include etc/apache22/Includes/*.conf

I press I to insert text and paste before the line:

AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phpS

AddType application/x-httpd-php .php .htm .html

And save with the usual vim esc+ :x! (exit, save and overwrite changes command).

Then restart Apache to load new settings, after testing Apache config is okay;

freebsd# apache2ctl -t
Syntax OK
freebsd# /usr/local/sbin/apachectl -k restart

To test php conduct the usual test if php is interpretting code with phpinfo(); by creating  file php_info.php and pasting inside:

<?php
phpinfo();
?>

One important note, to make here is if you try to use phpinfo(); test code like:

<?
phpinfo();
?>

You will get in your browser empty pages content – which usually appear only, if PHP execution fails. Even if you try to enable PHP errors to be displayed in browser by setting

display_errors = On in /usr/local/etc/php.ini, or configuring a separate php error_log file with setting variable error_log, i.e.:

error_log = /var/log/php_error.log

No error or warning is to be both displayed in browser or recorded in log. After consulting in irc.freenode.net #php, I was pointed out by nezZario that this unusual behavior is normal for PHP 5.4, as well as explained this behavior is controlled by var called:

Short Open Tags

To enable Short Open Tags to interpret PHP code inside <? set in /usr/local/etc/php.ini

short_open_tag = On