PanOS Docs

Advanced Customization and Development

Advanced techniques for customizing PanOS, modifying kernel, extending functionality, and building upon PanOS

Advanced Customization and Development

Overview

PanOS is fully customizable. You can modify almost everything:

  • Linux kernel configuration
  • Rootfs contents and structure
  • Boot process and init script
  • Node.js version
  • Application packages
  • System utilities

Kernel Customization

Changing Kernel Version

Edit 1-build.sh:

# Line 8 (current):
KERNEL_VERSION="6.6.15"

# Change to:
KERNEL_VERSION="6.5.0"    # Older stable
KERNEL_VERSION="6.7.0"    # Newer
KERNEL_VERSION="5.15.0"   # LTS (very stable)

Available versions: https://www.kernel.org/

Then rebuild:

./1-build.sh

Modifying Kernel Configuration

Edit the kernel .config in 1-build.sh:

Find section:

cat > .config << 'EOF'
# This section defines all kernel options
EOF

Add features (example: enable USB support):

# Find and modify
CONFIG_USB=y                    # Enable USB
CONFIG_USB_EHCI_HCD=y          # USB 2.0 support
CONFIG_USB_OHCI_HCD=y          # USB 1.1 support
CONFIG_USB_STORAGE=y           # USB mass storage

Remove features (example: disable networking):

# Comment out
# CONFIG_NET=y                 # Disable networking
# CONFIG_INET=y                # Disable TCP/IP

Kernel Modules (Advanced)

Enable module support for dynamic loading:

# In .config, add:
CONFIG_MODULES=y               # Enable module support
CONFIG_MODULE_UNLOAD=y         # Allow unloading modules

Then build and load modules:

# Inside PanOS
modprobe <module-name>         # Load module
lsmod                          # List loaded modules
modinfo <module-name>          # Get module info

Kernel Size Optimization

Remove unnecessary features to reduce kernel size:

# In .config, disable:
CONFIG_DEBUG_INFO=n            # Remove debug symbols
CONFIG_DEBUG_KERNEL=n          # Disable kernel debugging
CONFIG_FRAME_POINTER=n         # Disable frame pointers

This can reduce kernel size by 30-40%.

Rootfs Customization

Add Custom Tools

Before building, add binaries to rootfs:

Edit 1-build.sh, function setup_rootfs():

# After busybox installation, add:
# Example: Add curl tool
wget -q https://github.com/curl/curl/releases/download/curl-7_85_0/curl-7.85.0_1-linux64-generic.tar.gz
tar -xzf curl-* -C "${ROOTFS_DIR}/bin"
rm -f curl-*.tar.gz

Custom Init Script

Edit the init script in 1-build.sh:

Find:

cat > "${ROOTFS_DIR}/init" << 'INIT_EOF'
#!/bin/busybox sh
...
INIT_EOF

Customize:

cat > "${ROOTFS_DIR}/init" << 'INIT_EOF'
#!/bin/busybox sh

# Custom initialization
echo "Custom PanOS Init Starting..."

# Mount filesystems
busybox mount -t proc proc /proc
busybox mount -t sysfs sysfs /sys
busybox mount -t devtmpfs devtmpfs /dev
busybox mount -t tmpfs tmpfs /tmp

# Create devices
[ -e /dev/console ] || busybox mknod /dev/console c 5 1
[ -e /dev/tty ] || busybox mknod /dev/tty c 5 0
[ -e /dev/null ] || busybox mknod /dev/null c 1 3

# Custom setup
export MY_VAR="custom_value"
echo "Custom variable: $MY_VAR"

# Your automation here
if [ -f /root/autostart.sh ]; then
  . /root/autostart.sh
fi

# Start shell
exec /bin/sh
INIT_EOF

Persistent Root Filesystem

Make changes to running system persist between boots:

Warning: Requires disk setup, more complex.

Concept:

# Instead of initramfs (RAM)
# Use actual disk with overlayfs

initramfs (lower read-only layer)

overlayfs

persistent /dev/sda1 (upper writable layer)

/mnt/root (merged view)

Not covered in basic guide - see advanced overlay techniques.

Node.js Customization

Change Node.js Version

Edit 1-build.sh, search for Node.js download:

# Current
NODEJS_VERSION="24.0.0"

# Change to
NODEJS_VERSION="20.10.0"      # LTS
NODEJS_VERSION="22.0.0"       # Current
NODEJS_VERSION="18.17.0"      # Older LTS

Check available versions: https://nodejs.org/dist/

Rebuild:

./1-build.sh

Build Node.js from Source

For ARM or other architectures:

# Add to 1-build.sh
cd /tmp
wget https://github.com/nodejs/node/archive/v24.0.0.tar.gz
tar -xzf v24.0.0.tar.gz
cd node-24.0.0

./configure --prefix=/usr
make -j$(nproc)
make install

# Node now built locally

Pre-install npm Packages

Add packages to global npm during build:

# In rootfs creation section:
/bin/npm install -g \
  express \
  lodash \
  typescript \
  --prefix /node_modules_global

# Copy to rootfs
cp -r /node_modules_global/* ${ROOTFS_DIR}/node_modules/

Build System Enhancement

Automated Scripts

Create helper scripts for common tasks:

rebuild-kernel.sh:

#!/bin/bash
echo "Rebuilding kernel with updated config..."
rm -rf ~/pan-os-iso/kernel-src/linux-6.6.15
./1-build.sh
echo "Complete!"

quick-test.sh:

#!/bin/bash
./1-build.sh && ./2-run.sh

Makefile Enhancements

Edit Makefile to add custom targets:

# Add to Makefile
custom-build:
	@echo "Custom build starting..."
	@./1-build.sh
	@echo "Build complete!"

test-iso:
	@qemu-system-x86_64 -cdrom ~/pan-os-iso/build/pan-os-booteable.iso

shell-in-rootfs:
	@cd ~/pan-os-iso/rootfs && /bin/sh

Then run:

make custom-build
make test-iso

System Architecture Modifications

Add Swap Space

During boot, allocate swap:

Edit init script:

# Add after filesystems mounted
dd if=/dev/zero of=/swapfile bs=1M count=256
mkswap /swapfile
swapon /swapfile
echo "Swap configured: 256 MB"

Add User Management

Create non-root user:

Edit init script:

# After initial setup
mkdir -p /home/user
echo "user:x:1000:1000::/home/user:/bin/sh" >> /etc/passwd
echo "user::1000:user" >> /etc/group
chown -R user:user /home/user
echo "User 'user' created"

Access as:

/ # su - user
user $ whoami
user

Network Configuration

Static IP configuration:

Edit init script:

# Instead of DHCP:
ip addr add 192.168.1.100/24 dev eth0
ip link set eth0 up
ip route add default via 192.168.1.1
echo "nameserver 8.8.8.8" > /etc/resolv.conf

Security Hardening

Read-Only Root Filesystem

Make rootfs immutable:

# After mounting rootfs
mount -o remount,ro /

# Only /tmp is writable
mount -t tmpfs tmpfs /tmp

Edit init script .config:

# Change from:
exec /bin/sh

# To:
mount -o remount,ro /
exec /bin/sh

Disable Unnecessary Services

Remove unneeded daemons:

# In init script, simplify to absolute minimum
# Remove any service startup
# Should only have:
# 1. Mount filesystems
# 2. Create devices
# 3. Start shell

Remove Debug Symbols

Reduce attack surface:

# In kernel config:
CONFIG_DEBUG_INFO=n
CONFIG_DEBUG_KERNEL=n

# Strip binaries:
strip -s /bin/busybox
strip -s /bin/node

Performance Tuning

Kernel Command Line Optimization

In GRUB config or boot parameters:

# Current:
linux /boot/vmlinuz console=ttyS0

# Optimized:
linux /boot/vmlinuz console=ttyS0 quiet quiet loglevel=0 \
  mitigations=off ipv6.disable=1

Parameters:

  • quiet loglevel=0: Suppress kernel messages
  • mitigations=off: Disable CPU mitigations (performance)
  • ipv6.disable=1: Disable unused IPv6

Kernel Preemption

For real-time performance:

# In kernel config:
CONFIG_PREEMPT=y              # Lower latency
CONFIG_PREEMPT_RT=y           # Real-time kernel

Cross-Compilation

Build for different architecture:

ARM64 (for ARM servers)

export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-

# Install ARM toolchain
sudo apt-get install -y gcc-aarch64-linux-gnu

# Then modify 1-build.sh for ARM64:
# Change kernel config
# Change qemu-system to qemu-system-aarch64

ARM32

export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-

sudo apt-get install -y gcc-arm-linux-gnueabihf

# Modify build scripts similarly

Extending Functionality

Add Systemd Services (Advanced)

Replace init script-based startup with systemd:

# Create /lib/systemd/system/myapp.service
[Unit]
Description=My Node.js Application
After=network.target

[Service]
Type=simple
ExecStart=/bin/node /opt/myapp/main.js
WorkingDirectory=/opt/myapp
User=root

[Install]
WantedBy=multi-user.target

# Then in init:
systemctl enable myapp
systemctl start myapp

Docker Integration

Use PanOS as base for Docker images:

FROM scratch
ADD pan-os-booteable.iso /
ENTRYPOINT ["/bin/sh"]

Development Containers

Create minimal development environment:

# Add development tools to rootfs:
# - gcc (C compiler)
# - make (build tool)
# - git (version control)
# - vim (editor)

npm install -g \
  typescript \
  webpack \
  babel-cli

Troubleshooting Custom Builds

Build fails after customization

  1. Test changes on small portion first
  2. Revert to known working state
  3. Rebuild:
    ./1-build.sh

Kernel won't compile with custom config

Try with more verbose output:

# In kernel compile section, change:
make -j$(nproc) 2>&1 | tee build.log

# Then check build.log for errors
tail -100 build.log

Boot fails after customization

  1. Check init script syntax:

    # Validate shell script
    sh -n init
  2. Verify all required commands exist:

    grep -o '[a-z]*' init | sort -u | xargs -I {} which {}
  3. Enable verbose boot:

    linux /boot/vmlinuz ... loglevel=7

Performance Benchmarking

Measure Boot Time

From power-on to shell:

# Add timestamp tracking in init
echo "[$(date +%s)] Starting init" >> /root/boot.log

# Check elapsed time
cat /root/boot.log

Memory Profiling

Inside PanOS:

/ # ps aux | grep -oP 'VSZ|RSS'
/ # free -h
/ # cat /proc/meminfo

CPU Usage

/ # top -b -n 1
/ # mpstat -P ALL
/ # perf stat node myapp.js

Documentation and Versioning

Version Control

Track your customizations:

git init ~/pan-os-iso
git add .
git commit -m "PanOS custom build v1.0"
git tag v1.0

Build Reproduction

Document exact build parameters:

# build-info.txt
Build Date: 2024-02-16
Kernel Version: 6.6.15
Node.js Version: 24.0.0
Customizations:
  - Added custom tools
  - Modified init script
  - Optimized kernel config
Total Size: 156 MB

Next Steps


Previous: 09-creating-iso.mdx

Documentation Complete - All 10 documents created!

On this page