Plone behind Apache 2 on FreeBSD 7 with PF
Plone behind Apache and Varnish on FreeBSD 7 with PF (UPDATE May 04, 2010)
UPDATE (MAY 04, 2010)
- An updated version of this configuration with Plone 3.3.5 and Squid can be found on plone.org at the following page:
Plone 3 - Apache2/SSL - Squid on FreeBSD 7 with PF
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
If you have not already done so, 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 you reboot, IP forwarding is enabled:
# /etc/sysctl.conf net.inet.ip.forwarding=1 net.inet.ip.fastforwarding=1 net.inet6.ip6.forwarding=1 kern.ipc.somaxconn=4096 kern.ipc.nmbclusters=32768
As an alternative, you can add the following to /etc/rc.conf
# /etc/rc.conf gateway_enable="YES"
Enable HTTP Accept Filter
Next, make sure that the HTTP Accept filter is loaded into the kernel.
You can 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 you reboot, the HTTP Accept filter kernel module is loaded.
# /boot/loader.conf accf_http_load="YES"
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 kern.ipc.shmall=32768 kern.ipc.shmmax=134217728 kern.ipc.semmap=256 net.inet.ip.forwarding=1 net.inet.ip.fastforwarding=1 net.inet6.ip6.forwarding=0 kern.ipc.somaxconn=4096 kern.ipc.nmbclusters=32768
System V "Read-Only" Semaphore Parameters
Modify the System V "Read-Only" Semaphore Parameters by adding the following to /boot/loader.conf
NOTE: You must reboot for the new values of these parameters to take effect
# /boot/loader.conf kern.ipc.semmni=256 kern.ipc.semmns=512 kern.ipc.semmnu=256 accf_http_load="YES" net.inet.tcp.syncache.hashsize=1024 net.inet.tcp.syncache.bucketlimit=100 net.inet.tcp.tcbhashsize=4096 net.inet.tcp.syncache.cachelimit=102400
PF Configuration
# /etc/pf.conf
#
## MACROS-----
ext_if="bge0"
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 127.0.0.1 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 127.0.0.1
# 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>
ServerName DOMAIN.com
ServerAlias www.DOMAIN.com
ServerAdmin info@DOMAIN.com
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(.*) \
https://DOMAIN.com/$1/manage$2 [NC,R=301,L]
RewriteRule ^/manage(.*) \
https://DOMAIN.com/manage$1 [NC,R=301,L]
RewriteRule ^/login_(.*) https://%{SERVER_NAME}/login_$1 [NE,L]
RewriteRule ^/(.*) \
http://127.0.0.1:8902/VirtualHostBase/http/%{SERVER_NAME}:80/MyPloneSite/VirtualHostRoot/$1 [P,L]
</IfModule>
<IfModule mod_proxy.c>
ProxyVia On
ProxyPass / http://127.0.0.1:8902/VirtualHostBase/http/%{SERVER_NAME}:80/MyPloneSite/VirtualHostRoot/
ProxyPassReverse / http://127.0.0.1:8902/VirtualHostBase/http/%{SERVER_NAME}:80/MyPloneSite/VirtualHostRoot/
<ProxyMatch http://127.0.0.1:*/.* >
Order deny,allow
Deny from all
Allow from X.X.X.Y
</ProxyMatch>
<LocationMatch "^[^/]">
Deny from all
</LocationMatch>
</IfModule>
</VirtualHost>
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"
ServerName DOMAIN.com
ServerAdmin info@DOMAIN.com
ErrorLog "/var/log/DOMAIN-ssl-error.log"
TransferLog "/var/log/DOMAIN-ssl-access.log"
SSLEngine on
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+SSLv3:+EXP:+eNULL
SSLCertificateFile "/usr/local/etc/apache22/ssl.crt/DOMAIN.com.crt"
SSLCertificateKeyFile "/usr/local/etc/apache22/ssl.key/DOMAIN.com.key"
<FilesMatch "\.(cgi|shtml|phtml|php)$">
SSLOptions +StdEnvVars
</FilesMatch>
<Directory "/usr/local/www/apache22/cgi-bin">
SSLOptions +StdEnvVars
</Directory>
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 ^/(.*) \
http://127.0.0.1:8902/VirtualHostBase/https/%{SERVER_NAME}:443/MyPloneSite/VirtualHostRoot/$1 [L,P]
</IfModule>
<IfModule mod_proxy.c>
ProxyVia On
ProxyPass / http://127.0.0.1:8902/VirtualHostBase/http/%{SERVER_NAME}:80/MyPloneSite/VirtualHostRoot/
ProxyPassReverse / http://127.0.0.1:8902/VirtualHostBase/http/%{SERVER_NAME}:80/MyPloneSite/VirtualHostRoot/
<ProxyMatch http://127.0.0.1:*/.* >
Order deny,allow
Deny from all
Allow from X.X.X.Y
</ProxyMatch>
<LocationMatch "^[^/]">
Deny from all
</LocationMatch>
</IfModule>
</VirtualHost>
SSH Tunnel (access ZMI via http://localhost:9999
ssh -f user@mydomain.com -L 9999:localhost:8095 -N

