Friday, April 3, 2009

Plone and Apache on FreeBSD 7 behind PF

Plone 3.2.1 and Apache 2.2 on FreeBSD 7.2 behind PF

In 2001, PF was not yet integrated into the OpenBSD kernel.  OpenBSD 3.0 soon prevailed (2001) and PF was included in the kernel.  Soon thereafter (2003), PF was incorporated into the FreeBSD 5.3 kernel.   For those who are unfamiliar, PF is a system for filtering TCP/IP traffic and providing network address translation. However; PF also provides network traffic shaping capabilities - packet prioritization, bandwidth control, and TCP/IP conditioning.   My original article from 2000 explained how to setup an IP-less bridge on an OpenBSD 2.8 server running IP Filter  with dual network interface cards. The bridge filtered traffic at the data link layer and was invisible at the internet protocol level.  PF, like IP Filter, is very powerful.  While I will not be going into how to configure an IP-less bridge, the PF configuration that follows is straightforward and easily adaptable.

This document shows an example configuration of a PF ruleset and an Apache 2.2 installation in front of a Plone 3.2.1 instance on FreeBSD 7.2.  SSH tunnelling is used for remote management of the Zope/Plone instance (i.e. ZMI).

Pre-installation Requirements

  • FreeBSD 7.2-PRERELEASE w/ PF enabled
  • Apache 2.2 from (from ports) with mod_ssl (OpenSSL 0.9.8j), mod_proxy, and mod_rewrite
  • Zope w/ Plone 3.2.1
  • Varnish HTTP Accelerator

Zope is bound to an unprivileged port on localhost.  Apache is bound as a non-privileged user to port 80 and 443 on public IP address X.X.X.Y.  Zope/Plone can be running standalone or in a ZEO Server / ZEO client configuration.  In either case, Apache will function as a reverse proxy and send http requests to Zope.

Enable IP Forwarding in the Kernel

Enable IP forwarding in the kernel:

# sysctl net.inet.ip.forwarding=1
# sysctl net.inet.ip.fastforwarding=1
# sysctl net.inet6.ip6.forwarding=1

Add the following lines to /etc/sysctl.conf so that when the host is rebooted, IP forwarding is enabled:

# /etc/sysctl.conf


As an alternative, add the following to /etc/rc.conf

# /etc/rc.conf



Enable HTTP Accept Filter

Next, make sure that the HTTP Accept filter is loaded into the kernel.

Check this by running the following command:

# kldstat

2 1 0xc0b12000 2464 accf_http.ko

If the filter is not loaded, edit /boot/loader.conf and add the following line so that when the host is rebooted, the HTTP Accept filter kernel module is loaded.

# /boot/loader.conf

Last of all, to load the module without rebooting, run the following command:

kldload accf_http


System V Shared Memory and Semaphore Parameters

Modify System V shared memory and semaphore parameters

# sysctl kern.ipc.shmall=32768
# sysctl kern.ipc.shmmax=134217728
# sysctl kern.ipc.semmap=256

Make these changes permanent by adding the following to /etc/sysctl.conf

# /etc/sysctl.conf



System V "Read-Only" Semaphore Parameters

Modify the System V "Read-Only" Semaphore Parameters by adding the following to /boot/loader.conf  

NOTE: Reboot for the new values of these parameters to take effect

# /boot/loader.conf



PF Configuration

# /etc/pf.conf

## MACROS-----
set loginterface $ext_if
local_networks = "{ a.a.a.b/24, c.c.c.d/24, e.e.e.f/26}"
internet_ports = "{80, 443}"

# Table Setup
# /etc/iface_addresses contains the following
# X.X.X.Y
# X.X.X.Z
table <iface_addresses> persist file "/etc/iface_addresses"
table <bruteforce> persist

# set Block Policy option
set block-policy return

# set Skip Filtering option on localhost
set skip on lo0

scrub in all
antispoof quick for $ext_if inet

# block ip addresses contained in bruteforce table
block in log (all, to pflog0) quick on $ext_if from <bruteforce> to any

# block and then log outgoing packets that don't have one of our IPs as the source IP
block out log (all, to pflog0) quick on $ext_if from ! <iface_addresses> to any

# block nmap scans
block in log (all, to pflog0) quick on $ext_if inet proto { tcp, udp } from any to any flags FUP/FUP

# block everything by default
block in on $ext_if all

# pass in icmp and keep state
pass in quick on $ext_if inet proto icmp all keep state

# pass in tcp traffic from localhost
pass in quick on $ext_if proto tcp from to <iface_addresses>

# pass in traffic on internet ports
pass in on $ext_if proto { tcp, udp } from any to <iface_addresses> port $internet_ports flags S/SA keep state

# throttle ssh connection attempts and block their ip if a bruteforce attempt is detected
pass in quick on $ext_if proto tcp from any to any port ssh \
flags S/SA keep state \
(max-src-conn 15, max-src-conn-rate 5/3, \
overload <bruteforce> flush global)

# allow traffic from interface addresses to localhost
pass out quick on $ext_if from <interfaces_addresses> to

# allow local network admin ip addresses
pass in on $ext_if proto { tcp, udp } from $local_networks to $ext_if

# keep state on outbound connections made from one of the ip addresses on interface
pass out on $ext_if proto tcp from any to any flags S/SA modulate state
pass out on $ext_if proto { udp, icmp } from any to any keep state

 Apache Configuration

# /usr/local/etc/apache22/httpd.conf
# ----------------------------------

Listen X.X.X.Y:80 # Default
Listen X.X.X.Z:80 # VHost
Listen X.X.X.Z:80:443 # VHost

HTTP Virtual Hosts

# /usr/local/etc/apache22/Includes/httpd-vhosts.conf
# --------------------------------------------------
NameVirtualHost X.X.X.Z:80

<VirtualHost X.X.X.Z:80>
ServerSignature On
RequestHeader set Front-End-Https "On"
ProxyRequests Off
ProxyPreserveHost On

ErrorLog "/var/log/DOMAIN-error_log"
CustomLog "/var/log/DOMAIN-access_log" common
LogLevel warn

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteLogLevel 2
RewriteRule ^/icons/ - [L]
RewriteRule ^/(.*)/manage(.*) \$1/manage$2 [NC,R=301,L]
RewriteRule ^/manage(.*) \$1 [NC,R=301,L]
RewriteRule ^/login_(.*) https://%{SERVER_NAME}/login_$1 [NE,L]
RewriteRule ^/(.*) \{SERVER_NAME}:80/MyPloneSite/VirtualHostRoot/$1 [P,L]
<IfModule mod_proxy.c>
ProxyVia On
ProxyPass /{SERVER_NAME}:80/MyPloneSite/VirtualHostRoot/
ProxyPassReverse /{SERVER_NAME}:80/MyPloneSite/VirtualHostRoot/
<ProxyMatch*/.* >
Order deny,allow
Deny from all
Allow from X.X.X.Y
<LocationMatch "^[^/]">
Deny from all

SSL Virtual Hosts

# /usr/local/etc/apache22/Includes/httpd-ssl.conf

NameVirtualHost X.X.X.Z:443

<VirtualHost X.X.X.Z:443>
DocumentRoot "/usr/local/www/apache22/data"
ErrorLog "/var/log/DOMAIN-ssl-error.log"
TransferLog "/var/log/DOMAIN-ssl-access.log"

SSLEngine on
SSLCertificateFile "/usr/local/etc/apache22/ssl.crt/"
SSLCertificateKeyFile "/usr/local/etc/apache22/ssl.key/"

<FilesMatch "\.(cgi|shtml|phtml|php)$">
SSLOptions +StdEnvVars
<Directory "/usr/local/www/apache22/cgi-bin">
SSLOptions +StdEnvVars
BrowserMatch ".*MSIE.*" \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0

CustomLog "/var/log/cfhinton-ssl_request.log" \
"%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteLogLevel 2

RewriteRule ^/(.*) \{SERVER_NAME}:443/MyPloneSite/VirtualHostRoot/$1 [L,P]
<IfModule mod_proxy.c>
ProxyVia On
ProxyPass /{SERVER_NAME}:80/MyPloneSite/VirtualHostRoot/
ProxyPassReverse /{SERVER_NAME}:80/MyPloneSite/VirtualHostRoot/
<ProxyMatch*/.* >
Order deny,allow
Deny from all
Allow from X.X.X.Y
<LocationMatch "^[^/]">
Deny from all

SSH Tunnel (access ZMI via http://localhost:9999

ssh -f -L 9999:localhost:8095 -N



No comments:

Post a Comment