Copyright © 2005 Chatchawan Wongsiriprasert
$Id: article.sgml,v 1.2 2006/05/27 05:44:30 cws Exp $
FreeBSD is a registered trademark of the FreeBSD Foundation.
Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this document, and the FreeBSD Project was aware of the trademark claim, the designations have been followed by the “™” or the “®” symbol.
This document is a guidline for install an FreeBSD for Internet hosting. My company ,MiracleNet Group, is a web base software solution provider. Sometime we need to setup a server to host the solution for our customer which is my responsibility.
This guildline was start from notes I has been taken when I install those servers. I assume that the reader has some experience on FreeBSD and has already read the FreeBSD Handbook.
The requirement for this Internet server are:
It must be an e-mail server with virus and spam filter. The customer must be allow to add/delete an e-mail without the need to contact us.
It must support POP3/IMAP4/POP3S/IMAP4S,webmail , and e-mail relay for our customers.
It must host our customers web sites. Each customer must not be able to access files of other customers.
The customer must not be able to login on this server , except for upload and download the web pages.
First of all, please read my suggestion on Partition Layout because it is the only thing you can not change after install FreeBSD. Then, you can proceed to install FreeBSD as indicated in FreeBSD Handbook. You can download ISO images for i386 or amd64 from FreeBSD.org or mirror sites. Only the first disk ,6.0-xxx-xxx-disc1.iso, is required.
Before start the installation process. You must make up your mind about the partition layout of the hard disk because it is only the thing you can not change after install the system
Assume that you have single disk of moderate size (32GB or up) my suggestion for partition layout are:
Table 1. Partition Layout for 32GB
| Partition | Filesystem | Size | Description |
|---|---|---|---|
| a | / | 256 MB | FreeBSD handbook suggest 100 MB to this partition but for a 32GB-up disk set it 256 MB may be better. |
| b | N/A | 4-8 GB | This according to FreeBSD handbook that suggest 2-3 x RAM. Upgrade RAM is easy ,just put the new RAM module but add swap space is mean add new disk which may not be applicable in 1U RACK.Anyways, with 32GB hard disk or i386, 8 GB may be too much. |
| d | /var | 2-4 GB | Server need a lot of space on /var for logging and house keeping. Some software use /var to store temporary data by default. |
| e | /tmp | 1-2 GB | Many software and user scripts assume that /tmp is world writable. Put this directory on it own partition will prevent a runaways user process to eat up all the space on more critical partition such as / or /var. |
| f | /usr | 5-10 GB | We need this partition to store source/ports tree and do the system building. 5 GB is fine but with large hard disk (72 GB) , 10 GB will not hurt you. |
| g | /home | Rest of disk | This partition will store all user data or anything that you don't want to touch when reinstall the system. Moreover, you may want to set quota on this slice. |
After install FreeBSD 6 and ports tree from CD. You need to upgrade your system to the lastest patch to protect your system against various types of attack.
You need a pre-build program (or package in FreeBSD) to upgrade your system. The package is net/cvsup-without-gui which can be used to upgrade both source and ports tree.
For example,as the time of this writing version of 6 is 6.0 and assume that the platform is i386. The command to download and install cvsup are:
# fetch ftp://ftp.freebsd.org/pub/FreeBSD/releases/i386/6.0-RELEASE/packages/net/cvsup-without-gui-16.1h_2.tbz
cvsup-without-gui-16.1h_2.tbz 100% of 754 kB 37 kBps 00m00s
# pkg_add cvsup-without-gui-16.1h_2.tbz
Edit cvs-supfile to upgrade lastest update of FreeBSD 6.0. This
is my cvs-supfile:
#For complete list of cvsupd see CVSup Sites on FreeBSD handbook.
*default host=cvsup12.freebsd.org
*default base=/usr
*default prefix=/usr
*default release=cvs
*default delete use-rel-suffix
*default tag=RELENG_6_0
*default compress
src-all
ports-all tag=.
Run cvsup ,It will take a while to fetch both src and ports tree.
# /usr/local/bin/cvsup -L2 cvs-supfile
See Using CVSup section on FreeBSD handbook for more detail about using
cvsup. Edit your /etc/make.conf (copy the default from /usr/src/share/examples/etc/make.conf). At least change the CPUTYPE to match your machine. See gcc 3.4.4 manual for detail of each CPUTYPE. FreeBSD building system may not be able to use all CPUTYPE in gcc manual. If your CPUTYPE cause a build error, try the other one. This is the example of my make.conf:
CPUTYPE?= p4 #Use ?= not = to allow FreeBSD build process to override this value #CPUTYPE?= k8 #For Athlon64 on i386 #CPUTYPE?= athlon64 #For Athlon64 on AMD64Modify your kernel configuration. You should read Configuring the FreeBSD Kernel and /usr/src/sys/i386/conf/NOTES or /usr/src/sys/amd64/conf/NOTES for each options of the kernel. This is my kernel configuration for i386/AMD64 on my Althon64 test machine:
machine i386
#machine amd64
cpu I686_CPU
#cpu HAMMER
#options SMP # Symmetric MultiProcessor Kernel
ident GAIA-I386
#ident GAIA-AMD64
#Adjust memory limit for 4G RAM for i386
options KVA_PAGES=384 #1.5 G for kernels
options MAXDSIZ=(1536UL*1024*1024) #1.5 G for data
options MAXSSIZ=(128UL*1024*1024) #128M for stack
#Leave 896KB for code segment
options DFLDSIZ=(1536UL*1024*1024) #Set default data size to 1.5G
options SCHED_4BSD
options PREEMPTION # Enable kernel thread preemption
options INET # InterNETworking
options FFS # Berkeley Fast Filesystem
options SOFTUPDATES # Enable FFS soft updates support
options UFS_ACL # Support for access control lists
options UFS_DIRHASH # Improve performance on big directories
options MD_ROOT # MD is a potential root device
options CD9660 # ISO 9660 Filesystem
options PROCFS # Process filesystem (requires PSEUDOFS)
options PSEUDOFS # Pseudo-filesystem framework
options GEOM_GPT # GUID Partition Tables.
options COMPAT_43 # Compatible with BSD 4.3 [KEEP THIS!]
#options COMPAT_IA32 # Compatible with i386 binaries
options COMPAT_FREEBSD4 # Compatible with FreeBSD4
options COMPAT_FREEBSD5 # Compatible with FreeBSD5
options SCSI_DELAY=5000 # Delay (in ms) before probing SCSI
options KTRACE # ktrace(1) support
options SYSVSHM # SYSV-style shared memory
options SYSVMSG # SYSV-style message queues
options SYSVSEM # SYSV-style semaphores
options _KPOSIX_PRIORITY_SCHEDULING # POSIX P1003_1B real-time extensions
options KBD_INSTALL_CDEV # install a CDEV entry in /dev
options ADAPTIVE_GIANT # Giant mutex is adaptive.
#Kernel Options for PostgreSQL with large shared memory (312.5M)
options SYSVSHM #SYSV-style shared memory
options SYSVMSG #SYSV-style message queues
options SYSVSEM #SYSV-style semaphores
options SHMMAXPGS=80000
options SHMSEG=256
options SHMMNI=256
options SEMMNI=256
options SEMMNS=512
options SEMMNU=256
options SEMMAP=256
#PostgreSQL use a alot of shared memory - default is 200
options PMAP_SHPGPERPROC=512
#Firewall & NAT & DummyNet, may be needed in jail setup
options IPFIREWALL
options IPDIVERT
options IPFIREWALL_DEFAULT_TO_ACCEPT
options IPFIREWALL_VERBOSE
options IPFIREWALL_VERBOSE_LIMIT=100
options DUMMYNET
options IPFIREWALL_FORWARD
options QUOTA
device apic # I/O APIC
device pci
# Floppy drives
device fdc
# ATA and ATAPI devices
device ata
device atadisk # ATA disk drives
device ataraid # ATA RAID drives
device atapicd # ATAPI CDROM drives
device atapifd # ATAPI floppy drives
device atapist # ATAPI tape drives
options ATA_STATIC_ID # Static device numbering
# SCSI peripherals
device scbus # SCSI bus (required for SCSI)
device ch # SCSI media changers
device da # Direct Access (disks)
device sa # Sequential Access (tape etc)
device cd # CD
device pass # Passthrough device (direct SCSI access)
device ses # SCSI Environmental Services (and SAF-TE)
# atkbdc0 controls both the keyboard and the PS/2 mouse
device atkbdc # AT keyboard controller
device atkbd # AT keyboard
device psm # PS/2 mouse
device vga # VGA video card driver
device splash # Splash screen and screen saver support
# syscons is the default console driver, resembling an SCO console
device sc
device agp # support several AGP chipsets
# Power management support (see NOTES for more options)
device apm
# Add suspend/resume support for the i8254.
device pmtimer
# Serial (COM) ports
device sio # 8250, 16[45]50 based serial ports
# Parallel port
device ppc
device ppbus # Parallel port bus (required)
device lpt # Printer
device plip # TCP/IP over parallel
device ppi # Parallel port interface device
#device vpo # Requires scbus and da
# PCI Ethernet NICs that use the common MII bus controller code.
# NOTE: Be sure to keep the 'device miibus' line in order to use these NICs!
device miibus # MII bus support
device sk # SysKonnect SK-984x & SK-982x gigabit Ethernet
# Pseudo devices.
device loop # Network loopback
device random # Entropy device
device ether # Ethernet support
device sl # Kernel SLIP
device ppp # Kernel PPP
device tun # Packet tunnel.
device pty # Pseudo-ttys (telnet etc)
device md # Memory "disks"
device gif # IPv6 and IPv4 tunneling
device faith # IPv6-to-IPv4 relaying (translation)
device io
device mem
# The `bpf' device enables the Berkeley Packet Filter.
# Be aware of the administrative consequences of enabling this!
# Note that 'bpf' is required for DHCP.
device bpf # Berkeley packet filter
# USB support
device uhci # UHCI PCI->USB interface
device ohci # OHCI PCI->USB interface
device ehci # EHCI PCI->USB interface (USB 2.0)
device usb # USB Bus (required)
#device udbp # USB Double Bulk Pipe devices
device ugen # Generic
device uhid # "Human Interface Devices"
device ukbd # Keyboard
device ulpt # Printer
device umass # Disks/Mass storage - Requires scbus and da
device ums # Mouse
device uscanner # Scanners
Rebuild your world and kernel as told in the handbok.
# cd / # mergemaster -pai # cd /usr/src # make -j2 buildworld -- For dual CPU use -j4 # make -j2 buildkernel KERNCONF=XXX # make installkernel KERNCONF=XXX # cd / # mergemaster -ai -- clear temproot # cd /var/tmp/temproot # chflags noschg var/empty # find . -type l -delete # find . -empty -delete -- check the leftover files, replace or delete as you please # cd /var/tmp # rm -rf temprootIf you have the console access
# shutdown nowIf you can only has a ssh access,close as many daemons as you can except sshd and daemons spawn by kernel. This method should work for patch level upgrade (6.0 to 6.0p1), may work for minor version upgrade (6.0 to 6.1) and unlikely to work for major version upgrade (4.x to 5.x).
# cd /usr/src # make installworldBefore reboot, Set your System Configuration because some setting will be in effect only after reboot. Set them first save you another reboot. If everything is fine, it is the time to reboot your server with shutdown -r now
There are 4 system configuration files you may need to modify.
/etc/rc.conf, check that you have these 3 lines
sshd_enable="YES" sendmail_enable="NONE" syslogd_flags="-ss" firewall_enable="YES" firewall_type="/etc/ipfw.rules" #If your ISP has a reliable DNS service you can use its service, #otherwise it better to rely on ourself. #Don't forget to run : cd /etc/namedb/ && ./make-localhost named_enable="YES" quota_enable="YES" #It is a time consume job, better run it later after we got access to the system check_quotas="NO" #Don't forget to run : quotacheck -a after next reboot to create a quota file
/etc/sysctl.conf
security.bsd.see_other_uids=0 kern.coredump=0 net.inet.icmp.drop_redirect=1 net.inet.tcp.blackhole=2 net.inet.udp.blackhole=1 net.inet.ip.rtexpire=2 net.inet.ip.rtminexpire=2 kern.ipc.somaxconn=512
/boot/loader.conf
autoboot_delay="3" kern.ipc.maxsockets=81920 kern.ipc.maxsockbuf=1048576
/etc/hosts
You should swap the first 2 lines to make sure that you will get IPv4 (127.0.0.1) address for localhost instead of IPv6 (::1) because some program does not support IPv6.
127.0.0.1 localhost localhost.my.domain ::1 localhost localhost.my.domain #Our IP is 10.0.0.34 and our name is gaia.net0.intranet 10.0.0.34 gaia gaia.net0.intranet
/etc/ssh/sshd_config
#Assume that our IP is 10.0.0.34 ListenAddress 10.0.0.34:22 # Change to yes to enable built-in password authentication. # SecureCRT need this option PasswordAuthentication yes # If UseDNS is "yes" and your resolver is not work (i.e DNS server is down), # you can not log in. UseDNS no #Allow only admin to login from anywhere AllowUsers cws@* Subsystem sftp /usr/libexec/sftp-server
/etc/fstab
/dev/ad6s1g /home ufs rw,userquota,groupquota 2 2
/var/named/etc/namedb/named.conf
listen-on { 127.0.0.1; };
allow-recursion { 127.0.0.1; };
/etc/resolv.conf
nameserver 127.0.0.1
/etc/ipfw.rules
#more rules later add 65535 allow ip from any to any
It is also a good idea to change /etc/motd to something that look more legal such as
* * * * * * * * * * * * * W A R N I N G * * * * * * * * * * * * * * * THIS SYSTEM IS RESTRICTED TO AUTHORIZED USERS FOR AUTHORIZED USE ONLY. UNAUTHORIZED ACCESS IS STRICTLY PROHIBITED AND MAY BE PUNISHABLE UNDER THE COMPUTER FRAUD AND ABUSE ACT OF 1986 OR OTHER APPLICABLE LAWS. IF NOT AUTHORIZED TO ACCESS THIS SYSTEM, DISCONNECT NOW. BY CONTINUING, YOU CONSENT TO YOUR KEYSTROKES AND DATA CONTENT BEING MONITORED. ALL PERSONS ARE HEREBY NOTIFIED THAT THE USE OF THIS SYSTEM CONSTITUTES CONSENT TO MONITORING AND AUDITING. THE ADMINISTRATORS ALSO RESERVE THE RIGHT TO CANCEL OR LOCK YOUR ACCOUNT AT ANY GIVEN TIME. ALL TERMS DESCRIBED ABOVE ARE SUBJECT TO CHANGE WITHOUT ANY GIVEN NOTICE. IF YOU DO NOT AGREE TO THESE TERMS LOGOUT NOW! * * * * * * * * * * * * * W A R N I N G * * * * * * * * * * * * * * *which I copied from a web site somewhere.
This is a time to install program from ports tree. It is possible that the previous installation process may already install some ports on your system. Use pkg_delete to remove each installed ports except net/cvsup-without-gui because building this port require a lot of programs that will be never used elsewhere.
The system utilities I always install on my server are:
Table 2. System Utilities
| Port | Description | Note |
|---|---|---|
| lang/perl5.8 | Mandatory port. | |
| shells/bash | Shell for users who can login to this server. | |
| security/portaudit | Checks installed ports against a list of security vulnerabilities. | |
| sysutils/portupgrade | FreeBSD ports/packages administration and management tool suite. | Don't check BDB4 box. |
| security/bcwipe | BCWipe securely erases data from magnetic and solid-state memory. | |
| net/rsync | A network file distribution/synchronization utility. | |
| security/sudo | Allow others to run commands as root. | |
| sysutils/lsof | Lists information about open files (similar to fstat(1) ). | |
| misc/compat4x | Compatible module for application that compiled for FreeBSD 4 | Add compat4x_enable="YES" to /etc/rc.conf to enable FreeBSD 4 compatible. |
| misc/compat5x | Compatible module for application that compiled for FreeBSD 5 | Add compat5x_enable="YES" to /etc/rc.conf to enable FreeBSD 5 compatible. |
Table 3. System Utilities
| Port | Description | Note |
|---|---|---|
| database/mysql41-server | We use mysql to store administrative data. | Append the line WITH_XCHARSET=all to /etc/make.conf before install the port. This will add many international languages (such as Thai) support (search/sort) in MySQL. |
| databases/postgresql81-server | We use postgresql to store data for the application. | |
| databases/phpmyadmin | Tool to manipulate MySQL. | Install this after you install WWW server. Select all options except MYSQLI. |
| databases/phppgadmin | Tool to manipulate PostgreSQL. | Install this after you install WWW server. |
| databases/p5-DBD-mysql | MySQL driver for the Perl5 Database Interface (DBI). | Some of the perl scripts need MySQL access. |
I place my database in /home/mysql , so my /etc/rc.conf for mysql are:
mysql_enable="YES" mysql_dbdir="/home/mysql" mysql_args="--bind-address=127.0.0.1"If you want to access mysql from another machine, remove the third line. Before start mysql, you may need to set my.cnf to change mysql options:
# mkdir /home/mysql # mkdir /home/mysql/tmp # cp /usr/local/share/mysql/my-medium.cnf /home/mysql/my.cnf # chown -R mysql:mysql /home/mysqlI always set mysqld tmpdir to /home/mysql/tmp unless I have a very large /tmp on another disk. Sometime mysql use a lot of tmpdir when you run a complex query. Read mysql manual for more detail.
[mysqld] ... max_allowed_packet = 4M ... #log-bin skip-innodb tmpdir = /home/mysql/tmp #For development machine, you may need slow query log #to track a badly write SQL. long_query_time = 10 log_slow_queries = /home/mysql/slow-query.log ...Don't forget to set MySQL root password
# /usr/local/etc/rc.d/mysql-server.sh start
# mysql -u root
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2 to server version: 4.1.14
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> SET PASSWORD FOR root@localhost=PASSWORD('xxx');
Query OK, 0 rows affected (0.02 sec)
mysql>
Like mysql , I place postgresql databases in /home/pgsql. My /etc/rc.conf for postgresql are:
postgresql_enable="YES" postgresql_data="/home/pgsql/data"Use vipw to change home directory of pgsql user to /home/pgsql.
-- rsync preserve symbolic link while cp is not # rsync -a -v /usr/local/pgsql /home/ # su -m pgsql # initdb /home/pgsql/dataYou must edit /home/pgsal/data/pg_hba.conf before start postgresql
# TYPE DATABASE USER CIDR-ADDRESS METHOD # "local" is for Unix domain socket connections only local all pgsql ident sameuser local all all md5 # IPv4 local connections: host all all 127.0.0.1/32 md5 # IPv6 local connections: host all all ::1/128 md5Read PostgreSQL manual and Tuning PostgreSQL for performance for more details.
This is the change I made for my server:
shared_buffers = 30000 # min 16, at least max_connections*2, 8KB each work_mem = 32768 # min 64, size in KB max_fsm_pages = 40000 # min max_fsm_relations*16, 6 bytes each max_fsm_relations = 1000 # min 100, ~50 bytes each wal_buffers = 32 # min 4, 8KB each checkpoint_segments = 8 # in logfile segments, min 1, 16MB each effective_cache_size = 4000 # typically 8KB each #logging log_destination = 'stderr' redirect_stderr = on log_directory = 'pg_log' log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' log_rotation_age =1440 log_rotation_size = 10240 #slow query log -- enable for developer to check slow query #log_min_duration_statement = 10 #log_line_prefix = '%t [%u:%d] 'By default PostgreSQL root is pgsql or any system user that own the database files. You should create another database adminstrator account to allow postgresql user such as sa to act as database adminstrator.
# su pgsql
# psql template1
Welcome to psql 8.0.4, the PostgreSQL interactive terminal.
Type: \copyright for distribution terms
\h for help with SQL commands
\? for help with psql commands
\g or terminate with semicolon to execute query
\q to quit
template1=# CREATE USER sa WITH PASSWORD 'xxxx' CREATEDB CREATEUSER
CREATE USER
template1=#
After install WWW service, you may want to install database/phpmyadmin and database/phppgadmin to manage your databases. You must access these packages via HTTPS only because both require you to enter the database user and password on the webpage.
# cd /home/www/public_ssl # ln -s /usr/local/www/phpMyAdmin # ln -s /usr/local/www/data/phpPgAdminCopy /usr/local/www/phpMyAdmin/libraries/config.default.php to /usr/local/www/phpMyAdmin/config.inc.php and change the following lines to use http authentication:
$cfg['Servers'][0]['host'] = 'localhost'; $cfg['Servers'][0]['connect_type'] = 'socket'; $cfg['Servers'][0]['auth_type'] = 'http';
Table 4. Ports for WWW Service
| Port | Description | Note |
|---|---|---|
| www/apache13-modssl | A www server of a choice. | Append the line WITH_APACHE_MODDEFLATE=yes to /etc/make.conf install mod_deflate. |
| lang/php4 | Our main development language. | select OPENSSL box. |
| lang/php4-extension | A "meta-port" to install PHP extensions. | Append the line WITHOUT_X11=yes to /etc/make.conf before install the port. This will prevent any reference to X11 which include XBM support in GD. |
| devel/ZendOptimizer | An optimizer for PHP code. | It free but closed source. May cause a core-dump with some php extenstions. Unfortunely, the current version of ZendOptimizer (2.5.10) is not support FreeBSD AMD64. If you really want to run it you may need to enable 32bit support in the kernel and run a 32-bit version of Apache/PHP -- see Setup User WWW Site for more detail. |
| www/awstat | Free real-time logfile analyzer to get advanced web statistics. | |
| net/p5-Geo-IP | Gets country name by IP or hostname. |
I usually move apache's document root from /usr/local/www/data to /home/www/public_html for HTTP service and /home/www/public_ssl for HTTPS service. Another change I usually made to /usr/local/etc/apache/httpd.conf is remove the univeral listen line Port 80 or Listen 80 to more specified listen Listen xxx.xxx.xx.xx:80 because I need to run another apache in a jail(8). I also change a log format and logfile name.Here is a result of the command diff -u /usr/local/etc/apache/httpd.conf-dist /usr/local/etc/apache/httpd.conf and also the complete version of httpd.conf. Don't foget to create a folder to store your log file. For example:
# mkdir /var/log/httpdIf you have a lot of virtual hosts on the server, it is more preferable to move the virtual host configuration to another file and use apache Include directive to include that configuration to httpd.conf.
To enable mod_deflate, you must add the line
AddModule mod_deflate.c #The following lines can be put in .htaccess if you want #to enable deflate per directory <IfModule mod_deflate.c> DeflateEnable On DeflateMinLength 3000 DeflateCompLevel 1 DeflateProxied Off DeflateHTTP 1.0 DeflateDisableRange "MSIE 4." DeflateTypes text/plain text/html </IfModule>to httpd.conf
The last concern for httpd.conf is to remove unused modules. Read Apache modules Manual to see which module is not need for your server. Or, just remove them all ,then add the module one by one untils your site work as you want.
If you run HTTPS service, you may need to create a valid SSL certificate. There is a good doucument about Client Authentication with SSL at The FreeBSD Diary.
# sh /etc/periodic/weekly/310.locate # locate CA.pl # /usr/src/crypto/openssl/apps/CA.pl -newreq Generating a 1024 bit RSA private key .......................................++++++ ...........++++++ writing new private key to 'newreq.pem' Enter PEM pass phrase:xxxxxx Verifying - Enter PEM pass phrase:xxxxxx ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:TH State or Province Name (full name) [Some-State]:Bangkok Locality Name (eg, city) []:Phayathai Organization Name (eg, company) [Internet Widgits Pty Ltd]:MiracleNet Group Co., Ltd. Organizational Unit Name (eg, section) []:Hosting Service Common Name (eg, YOUR name) []:gaia.net0.intranet Email Address []:root@net0.intranet Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:MiracleNet Group Co., Ltd. Request (and private key) is in newreq.pem # openssl rsa < newreq.pem > newkey.pem Enter pass phrase:xxxxxx writing RSA keySend your newreq.pem to Certificate Authority for real server or sign it yourself for the test one.
If you want to sign the certificate yourself. You must create yourown Certificate Authority first (assume that we will put the CA in /home/admin/CA,then sign the certificate:
# mkdir -p /home/admin/CA # cd /home/admin/CA # /usr/src/crypto/openssl/apps/CA.pl -newca CA certificate filename (or enter to create) <ENTER> Making CA certificate ... Generating a 1024 bit RSA private key ........................++++++ ........++++++ writing new private key to './demoCA/private/cakey.pem' Enter PEM pass phrase:xxxxx2 Verifying - Enter PEM pass phrase:xxxxx2 ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:TH State or Province Name (full name) [Some-State]:Bangkok Locality Name (eg, city) []:Phayathai Organization Name (eg, company) [Internet Widgits Pty Ltd]:Miraclenet Group Co., Ltd. Organizational Unit Name (eg, section) []:Hosting Service Common Name (eg, YOUR name) []:miraclenet.co.th Email Address []:root@miraclenet.co.th # cp /home/admin/CA/newreq.pem . # /usr/src/crypto/openssl/apps/CA.pl -sign Using configuration from /etc/ssl/openssl.cnf Enter pass phrase for ./demoCA/private/cakey.pem:xxxxx2 Check that the request matches the signature Signature ok ... Certificate is to be certified until Nov 29 02:13:01 2006 GMT (365 days) Sign the certificate? [y/n]:y 1 out of 1 certificate requests certified, commit? [y/n]y Write out database with 1 new entries Data Base Updated Signed certificate is in newcert.pemAnyways, Use this self signed certificate will generate the warning message from the browser becase it don't known your Certificate Authority. To get rid of this warning, you must make the browser know your CA which can be done For firefox and opera, just copy the file demoCA/cacert.pem to the client machine,then, import it to your browser( Preferences/Advanced/Manage Certificates/Authories/Import or just put it on your web page and allow user to download and install the certificate ). For IE, change the file extension to .crt and import it with Internet Options/Contents/Publishers/Trusted Root Certification Authorities/Import.
After that, copy the signed request and key to /usr/local/etc/apache and modify your httpd.conf accordingly.
# cp newcert.pem /usr/local/etc/apache/ssl.crt/gaia.crt # cp newkey.pem /usr/local/etc/apache/ssl.key/gaia.key # cd /usr/local/etc/apache/ssl.crt/ # make -- Don't forget to edit SSLCertificateFile and SSLCertificateKeyFile -- in httpd.conf to point to new crt and keyDon't forget to add the line apache_enable="YES" to /etc/rc.conf to enable apache service.
I need to patch PHP to make serialize command run faster , see the bug report "Slow serialize on FreeBSD". To apply the patch, just download patch-ph_smart_str.h and copy the patch to ports/lang/php4/files before build the php4 port. Anyways, this patch never made it ways through php porject or FreeBSD port tree, use it with your own risk.
If you want the OPENSSL support on PHP, don't forget to add OPENSSL option when build PHP. The OPENSSL can not work when compiled as an extension.
You may need install PHP extensions only install the required extension. The less extension installed, the less problem from PHP. The extensions normally installed on my server are BCMATH, BZ2, CTYPE, CURL, GD, IMAP, MBSTRING, MCRYPT, MHASH, MYSQL , OVERLOAD, PCRE , PDF, PGSQL, POSIX , SESSION , SOCKETS, SYSVSEM,SYSVSHM,SYSVMSG,TOKENIZER,XML and ZLIB.
Don't forget to add
<IfModule mod_php4.c>
AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps
</IfModule>
to /usr/local/etc/apache/httpd.conf to automatic run php when
user access .php file.There are some dependency mismatch on FreeBSD 6.0/6.1 that cause apache start after compat5x which prevent ZendOptimizer from starting when you reboot the system. Run /sbin/rcorder to check for this problem
# rcorder /etc/rc.d/* /usr/local/etc/rc.d/* ... /etc/rc.d/yppasswdd /usr/local/etc/rc.d/apache.sh /etc/rc.d/LOGIN /usr/local/etc/rc.d/rsyncd.sh /usr/local/etc/rc.d/mysql-server.sh /usr/local/etc/rc.d/010.pgsql.sh /usr/local/etc/rc.d/000.pkgtools.sh /usr/local/etc/rc.d/000.compat5x.sh /usr/local/etc/rc.d/000.compat4x.sh ...If you see that above result, you have this problem. Edit /usr/local/etc/rc.d/apache.sh to force compat5x to start before apache.
gaia# diff -u apache.sh.org apache.sh --- apache.sh.org Sat May 20 14:04:48 2006 +++ apache.sh Sat May 20 14:04:56 2006 @@ -2,7 +2,7 @@ # $FreeBSD: ports/www/apache13-modssl/files/rcng.sh,v 1.5 2006/02/20 20:47:46 dougb Exp $ # PROVIDE: apache -# REQUIRE: DAEMON +# REQUIRE: DAEMON compat5x # BEFORE: LOGIN # KEYWORD: shutdownRerun /sbin/rcorder to recheck that apache start after compat5x.
# rcorder /etc/rc.d/* /usr/local/etc/rc.d/* ... /etc/rc.d/yppasswdd /usr/local/etc/rc.d/000.compat5x.sh /usr/local/etc/rc.d/apache.sh /etc/rc.d/LOGIN ...
Some parameters in /usr/local/etc/php.ini may need to be consider such as:
output_buffering = On
zlib.output_compression = On
register_argc_argv = Off
magic_quotes_gpc = Off #When On, It cause more problems because we don't know
#the quote come from user input or from this option.
[Zend]
zend_optimizer.optimization_level=15
zend_extension_manager.optimizer="/usr/local/lib/php/20020429/Optimizer"
zend_extension_manager.optimizer_ts="/usr/local/lib/php/20020429/Optimizer_TS"
zend_extension="/usr/local/lib/php/20020429/ZendExtensionManager.so"
zend_extension_ts="/usr/local/lib/php/20020429/ZendExtensionManager_TS.so"
Due to the volumn of log messages, we does not use syslogd to keep apache access/error log. So, we wrote rotatelog.pl to rotate logs file every midnight to prevent them grow too large. You need to put the rotatelog.pl in your crontab to run it every midnight.
Next step is to setup awstat. awstats require a configuration file which should to be placed in /usr/local/etc/awstats. There are small changes I made on /usr/local/www/awstats/cgi-bin/awstats.model.conf to create my configuration file.
awstats.gaia.conf
LogFile="bunzip2 -dc /var/log/httpd/access.log.0.bz2 |" SiteDomain="gaia.net0.intranet" HostAliases="localhost 127.0.0.1" DNSLookup=0 DirData="/home/www/public_html/stats/data" DirCgi="/stats/cgi-bin" DirIcons="/stats/icons" LoadPlugin="geoip GEOIP_STANDARD /usr/local/share/GeoIP/GeoIP.dat" UseFramesWhenCGI=0 LogFormat=1
The GeoIP database also need to update. The database version that I use is a free GeoLite Country Database which update once a month. Put this geoip_update.sh shell script in your crontab update the database.
To allow user to view the statistic, don't forget to setup a URL for awstats and setup authenticate:
# mkdir -p /home/www/apache # htpasswd -cm /home/www/apache/passwd stats # mkdir -p /home/www/public_html/stats/data # cd /home/www/public_html/stats # ln -s /usr/local/www/awstats/cgi-bin # ln -s /usr/local/www/awstats/classes # ln -s /usr/local/www/awstats/css # ln -s /usr/local/www/awstats/icons # ln -s /usr/local/www/awstats/jsCreate /home/www/public_html/stats/index.php:
<?
header("Location:/stats/cgi-bin/awstats.pl?config=gaia");
?>
Create /home/www/public_html/stats/.htaccess:
AuthType Basic AuthName "Gaia Access Statistic" AuthUserFile /home/www/apache/passwd Require user stats Options FollowSymLinksCreate /home/www/public_html/stats/cgi-bin/.htaccess to run perl script with mod_perl:
Options ExecCGI AddHandler cgi-script .plDon't forget to create a crontab entries for rotate access log ,update statistic and update GeoIP database.
#crontab -e 0 0 * * * /home/admin/bin/rotatelog.pl 10 0 * * * /home/www/public_html/stats/cgi-bin/awstats.pl -config=gaia -update 0 0 2 * * /home/admin/bin/geoip_update.sh
Mail service (SMTP/POP/IMAP) is one of the function for this server. It much support virtual mailboxs for our customers. The server must act as a mail relay for the customer and spam and virus filter are a must have features. The following table show list of ports I use to implement mail service on this server:
Table 5. Mail Service
| Port | Description | Note |
|---|---|---|
| security/courier-authlib | Courier authentication library base. | Select AUTH_MYSQL and AUTH_USERDB when build the port. |
| security/cyrus-sasl2 | RFC 2222 SASL |
Add the following lines to /etc/make.conf WITH_AUTHDAEMON= yes WITHOUT_OTP= yes WITHOUT_NTLM= yes WITHOUT_GSSAPI= yes WITH_MYSQL41=yes #If you use mysql41-serverto remove unused authentication method. |
| mail/postfix | More secure than mail/sendmail and easier to extend than mail/qmail. | Select SASL2,TLS and MySQL. Answer y to every post installation questions. |
| security/amavisd-new | Our spam and virus filter. | Remove all options (MILTER is set by default - uncheck it). |
| mail/dspam | Bayesian spam filter. |
Append the following lines to /etc/make.conf: DSPAM_OWNER=vscan DSPAM_GROUP=vscan DSPAM_HOME_OWNER=vscan DSPAM_HOME_GROUP=vscanUse default options when building the port. |
| security/clamav | a GPL anti-virus toolkit for UNIX. New version of dspam install clamav by default. You may not need to install this port manually. | No options is need. If you don't like clamav, see /usr/local/etc/amavisd.conf for another virus scanner supported by amavisd-new. |
| mail/courier-imap | Our POP3, POP3S, IMAP4 and IMAP4S server. | Select OPENSSL, TRASHQUOTA and AUTH_MYSQL. Unselect IPV6 unless you need it. |
| mail/squirrelmail | Greate web mail for small and medium size mail server. | go to /usr/port and run make search key=webmail to see another webmail in ports tree. |
The mail server that I create is not the hight-performane one. On moderate hardware (Althon64 2800 with 1GB RAM and SATA disk) it can process about 3 mails a second (180 mails per minute) which is enought for small or medium company. So , if you a looking for the hight-performance mail server , this setup may not for you.
We store our customer e-mail accounts on MySQL database to make it easier to manipulate and increase look up speed. Most of the information on this section come from Martin List-Petersen's ISP Mailserver Solution Howto.
CREATE DATABASE maildb; USE maildb; CREATE TABLE `alias` ( `email` varchar(255) NOT NULL default '', `destination` varchar(255) NOT NULL default '', `customer_id` varchar(16) NOT NULL default '', PRIMARY KEY `email` (`email`), KEY `customer_id` (`customer_id`) ) ENGINE=MyISAM;
Table 6. alias
| The originally email-address. The email can be xyz@example.com for single email or @example.com for all user in that domain. | |
| destination | The destination email-address for the email. |
| customer_id | System customer id to check record owner. If the id is removed from the system all record with customer_id will be deleted. |
CREATE TABLE `transport` ( `domain` varchar(255) NOT NULL default '', `transport` varchar(128) NOT NULL default '', `customer_id` varchar(16) NOT NULL default '', PRIMARY KEY (`domain`), KEY `customer_id` (`customer_id`) ) ENGINE=MyISAM;
Table 7. transport
| domain | Domain name of interest. |
| transport | Postfix transport type can be local: for local domain, virtual: for virtaul domain and smtp:another.mail.server if you need to forward mail for the domain to another server. |
| customer_id | System customer id to check record owner. If the id is removed from the system all record with customer_id will be deleted. |
CREATE TABLE `user` (
`email` varchar(128) NOT NULL default '',
`passwd` varchar(128) NOT NULL default '$1$X$XXX',
`name` varchar(128) NOT NULL default '',
`uid` int(6) NOT NULL default '65534',
`gid` int(6) NOT NULL default '65534',
`home` varchar(255) NOT NULL default '',
`maildir` varchar(255) NOT NULL default '',
`allow_login` enum('Y','N') NOT NULL default 'Y',
`allow_receive` enum('Y','N') NOT NULL default 'Y',
`customer_id` varchar(16) NOT NULL default '',
PRIMARY KEY (`email`),
KEY `customer_id` (`customer_id`)
) ENGINE=MyISAM;
Table 8. user
| User email address (user@domain.com). | |
| passwd | Encrypted password. Use /usr/local/sbin/userdbpw to create an encrypted password. |
| name | The users name. This is only for record keeping and it is not use by the mail system. |
| uid/gid | FreeBSD user id/group id of the mailbox owner. |
| home | The users home path. This is only for record keeping and it is not use by the mail system. |
| maildir | Path to user mailbox , for example "/home/vhost/user_x/mail/domain.com/user/". Don't remove the trailing slash or else postfix will deliver your mail to a mailspool format instead of a maildir. |
| allow_login | If it is 'N' user is not allow to access POP3/IMAP4 and SASL. |
| allow_receive | If it is 'N' , the user email is closed from receiving mail. |
| customer_id | System customer id to check record owner. If the id is removed from the system all record with customer_id will be deleted. |
We need 3 MySQL user accounts with difference privileges .
maildb -- Owner of the database can do everything to the database.
maildb_auth -- Can read every fields in user table. Use by courier-authlib.
maildb_smtp -- Can read every fields on the database except passwd. Use by postfix. The user/password of this account must store in a word readable file in /usr/local/etc/postfix ,therefore, give minimum access to this account.
GRANT USAGE ON maildb.* TO 'maildb'@'localhost' IDENTIFIED BY '*********'; GRANT ALL PRIVILEGES ON `maildb` . * TO 'maildb'@'localhost' WITH GRANT OPTION; GRANT USAGE ON maildb. * TO 'maildb_auth'@'localhost' IDENTIFIED BY '*********'; GRANT SELECT ON `maildb`.`user` TO 'maildb_auth'@'localhost'; GRANT USAGE ON maildb. * TO 'maildb_smtp'@'localhost' IDENTIFIED BY '*********'; GRANT SELECT ON `maildb`.`alias` TO 'maildb_smtp'@'localhost'; GRANT SELECT ON `maildb`.`transport` TO 'maildb_smtp'@'localhost'; GRANT SELECT ( `email` , `name` , `uid` , `gid` , `home` , `maildir` , `allow_login` , `allow_receive` , `customer_id` ) ON `maildb`.`user` TO 'maildb_smtp'@'localhost';
First append the lines to /etc/rc.conf to enable the service.
#Amavis/ClamAV/SpamAssasin clamav_clamd_enable="YES" clamav_freshclam_enable="YES" amavisd_enable="YES"Add clamav user to vscan group to enable clamd to access the amavisd filtering mail.
# vi /etc/group spamd:*:58: vscan:*:110:clamav clamav:*:106:To run dspam from amavisd-new you need to make some change to installed dspam.
# chmod u-s,a+rx /usr/local/bin/dspam # cd /var/amavis # ln -s /var/db/dspamThen edit /usr/local/etc/amavisd.conf as show below:
$mydomain = 'gaia.net0.intranet'; # a convenient default for other settings $dspam = 'dspam'; # Allow dspam #Don't forget to uncomment 'ClamAV-clamd' to enable clamav #If you want to accept .zip and .bz2, remove the comment on #[ qr'^\.(Z|gz|bz2)$' => 0 ] and #[ qr'^\.(zip|rar|arc|arj|zoo)$'=> 0 ] #Discard all filtered mail -- don't notify sender $final_virus_destiny = D_DISCARD; $final_banned_destiny = D_DISCARD; $final_spam_destiny = D_DISCARD; $final_bad_header_destiny = D_DISCARD; $recipient_delimiter = '-'; #If someting go wrong enable the following options and take a look at #/var/log/maillog and your mailheader #$log_level = 5; #$sa_tag_level_deflt = 0;You can see the result of the command diff -u amavisd.conf-dist amavisd.conf on my server here.
To setup dspam, you must cread a dspam user and database on MySQL. Give that user full access to the database and run the script in /usr/local/share/examples/dspam/mysql/mysql_objects-4.1.sql.
# mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 144 to server version: 4.1.14 Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql> CREATE DATABASE dspam; Query OK, 1 row affected (0.00 sec) mysql> GRANT USAGE ON dspam.* TO 'dspam'@'localhost' IDENTIFIED BY '*********'; Query OK, 1 row affected (0.00 sec) mysql> GRANT ALL PRIVILEGES ON `dspam` . * TO 'dspam'@'localhost' WITH GRANT OPTION; Query OK, 1 row affected (0.00 sec) mysql> USE dspam; Database changed mysql> \. /usr/local/share/examples/dspam/mysql/mysql_objects-4.1.sqlEdit /usr/local/etc/dspam.conf to add MySQL user and password:
StorageDriver /usr/local/lib/libmysql_drv.so MySQLServer /tmp/mysql.sock #MySQLPort MySQLUser dspam MySQLPass xxxxxx MySQLDb dspam #MySQLCompress true #For Relearn false negative and false positive MySQLUIDInSignature on Preference "signatureLocation=headers" #We work with amavisd-new IgnoreHeader X-Spam-Status IgnoreHeader X-Spam-Scanned IgnoreHeader X-Virus-Scanner-Result #Add the following line and take a look at /var/log/dspam/dspam.debug #if something don't work as expected #Debug vscandspam will not activate util it see about 2,000 spam/nospam mails, so you must wait for this threashold to be reach.
Edit /usr/local/etc/authlib/authmysqlrc:
MYSQL_SERVER localhost MYSQL_USERNAME maildb_auth MYSQL_PASSWORD xxxxx MYSQL_SOCKET /tmp/mysql.sock MYSQL_DATABASE maildb MYSQL_USER_TABLE user MYSQL_CRYPT_PWFIELD passwd MYSQL_LOGIN_FIELD email MYSQL_MAILDIR_FIELD maildir MYSQL_WHERE_CLAUSE allow_login='Y'Edit /usr/local/etc/authlib/authdaemonrc:
authmodulelist="authpam authmysql"Don't forget to add the line courier_authdaemond_enable="YES" to /etc/rc.conf.
For cyrus-sasl2, create the file /usr/local/lib/sasl2/smtpd.conf with content:
pwcheck_method: authdaemond authdaemond_path: /var/run/authdaemond/socketand change permission of /var/run/authdaemond to allow other to access the directory.
# chmod o+x /var/run/authdaemond
Edit the file to run postfix as mail service instead of the built-in sendmail:
#Postfix postfix_enable="YES" sendmail_enable="NONE" sendmail_flags="-bd" sendmail_outbound_enable="NO" sendmail_submit_enable="NO" sendmail_msp_queue_enable="NO" daily_clean_hoststat_enable="NO" daily_status_mail_rejects_enable="NO" daily_status_include_submit_mailq="NO" daily_submit_queuerun="NO"
Older postfix port does not come with FreeBSD startup script. If ther is no /usr/local/etc/rc.d/postfix.sh use the following one:
#!/bin/sh
# PROVIDE: postfix
# REQUIRE: NETWORKING SERVERS
# BEFORE: DAEMON
# AFTER: mysql-server
# KEYWORD: shutdown
. /etc/rc.subr
name="postfix"
rcvar=`set_rcvar`
load_rc_config ${name}
: ${postfix_enable="NO"}
command=/usr/local/sbin/postfix
pidfile=/var/spool/${name}/pid/master.pid
start_cmd="postfix_cmd start"
stop_cmd="postfix_cmd stop"
restart_cmd="postfix_cmd stop && postfix_cmd start"
echo ${pidfile}
postfix_cmd () {
case $1 in
start)
echo "Starting ${name}."
${command} start
;;
stop)
echo "Stopping ${name}."
${command} stop
;;
esac
}
run_rc_command "$1"
We need to run amavisd and let postfix smtpd use it. First remove the standard smtpd service line at the begining of the file
smtp inet n - n - - smtpdand appened the following lines to start smtpd with amavis filter. It this configuration, we don't filter the outgoing mail (127.0.0.1:smtp). Assume that the server IP is 10.0.0.34
smtp-amavis unix - - n - 2 lmtp
-o lmtp_data_done_timeout=1200
-o lmtp_send_xforward_command=yes
127.0.0.1:smtp inet n - n - - smtpd
-o content_filter=
10.0.0.34:smtp inet n - n - - smtpd
-o content_filter=smtp-amavis:[127.0.0.1]:10024
127.0.0.1:10025 inet n - n - - smtpd
-o content_filter=
-o local_recipient_maps=
-o relay_recipient_maps=
-o smtpd_restriction_classes=
-o smtpd_client_restrictions=
-o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o mynetworks=127.0.0.0/8
-o strict_rfc821_envelopes=yes
Append the following lines to the files (see postconf(5) or Postfix Site for detail of each parameter)
/usr/local/etc/postfix/main.cfinet_interfaces = 10.0.0.34, 127.0.0.1 recipient_delimiter = - home_mailbox = Maildir/ #Set size limit to 50MB mailbox_size_limit = 51200000 virtual_mailbox_limit= 51200000 mynetworks = 127.0.0.0/8,10.0.0.34/32 #MAP transport_maps=mysql:/usr/local/etc/postfix/transport.cf virtual_mailbox_base=/ virtual_mailbox_maps=mysql:/usr/local/etc/postfix/virtual_mailbox.cf virtual_alias_maps=mysql:/usr/local/etc/postfix/virtual_alias.cf virtual_uid_maps=mysql:/usr/local/etc/postfix/virtual_uid.cf virtual_gid_maps=mysql:/usr/local/etc/postfix/virtual_gid.cf #Receive name form $myhostname and any domain in transport table mydestination= $myhostname,$transport_maps #Valid local user name from local user, /etc/alias, virtual user and virtual alias local_recipient_maps=proxy:unix:passwd.byname,$alias_maps,$virtual_mailbox_maps,$virtual_alias_maps #Set .forward name forward_path = $home/.forward$recipient_delimiter$extension,$home/.forward # sasl config broken_sasl_auth_clients = yes smtpd_sasl_auth_enable = yes smtpd_sasl_local_domain = smtpd_sender_restrictions = permit_sasl_authenticated,permit_mynetworks smtpd_recipient_restrictions = permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination smtpd_sasl_security_options = noanonymous # tls config smtp_use_tls = yes smtpd_use_tls = yes smtp_tls_note_starttls_offer = yes smtpd_tls_auth_only = no smtpd_tls_key_file = /usr/local/etc/postfix/gaia.pem smtpd_tls_cert_file = /usr/local/etc/postfix/gaia.pem smtpd_tls_CAfile = /usr/local/etc/postfix/gaia.pem smtpd_tls_loglevel = 1 smtpd_tls_received_header = yes smtpd_tls_session_cache_timeout = 3600s tls_random_source = dev:/dev/urandom smtpd_tls_sender_restrictions = permit_sasl_authenticated,permit_mynetworks smtpd_tls_recipient_restrictions = permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination/usr/local/etc/postfix/transport.cf
user = maildb_smtp password = xxxx dbname = maildb table = transport select_field = transport where_field = domain socket = /tmp/mysql.sock/usr/local/etc/postfix/virtual_mailbox.cf
user = maildb_smtp password = xxxx dbname = maildb table = user select_field = maildir where_field = email additional_conditions= and allow_receive='Y' socket = /tmp/mysql.sock/usr/local/etc/postfix/virtual_alias.cf
user = maildb_smtp password = xxxx dbname = maildb table = alias select_field = destination where_field = email socket = /tmp/mysql.sock/usr/local/etc/postfix/virtual_uid.cf
user = maildb_smtp password = xxxx dbname = maildb table = user select_field = uid where_field = email socket = /tmp/mysql.sock/usr/local/etc/postfix/virtual_gid.cf
user = maildb_smtp password = xxxx dbname = maildb table = user select_field = gid where_field = email socket = /tmp/mysql.sockTo create /usr/local/etc/postfix/gaia.pem, you can use the same method from the WWW section or just use that certificate with commands:
# cp /usr/local/etc/apache/ssl.crt/gaia.crt /usr/local/etc/postfix/gaia.pem # cat /usr/local/etc/apache/ssl.key/gaia.key >> /usr/local/etc/postfix/gaia.pem # chmod og-r /usr/local/etc/postfix/gaia.pem
To use courier imap you need to change the config file in /usr/local/etc/courier-imap and create an certificate for IMAP4S and POP3S or just use the certificate from postfix.
/etc/rc.conf
#IMAP courier_authdaemond_enable="YES" courier_imap_imapd_enable="YES" courier_imap_imapd_ssl_enable="YES" courier_imap_pop3d_enable="YES" courier_imap_pop3d_ssl_enable="YES"/usr/local/etc/courier-imap/imapd
#The default is listen to all IP, remove or comment the line ADDRESS=0 #ADDRESS=0 #127.0.0.1 for SquirrelMail, 10.0.0.34 for rest of the world PORT=127.0.0.1.143,10.0.0.34.143 #Many company has an sigle external IP for their office ,so every request #from that company will alwasys come from the same IP. MAXPERIP=16/usr/local/etc/courier-imap/imapd-ssl
#SSLADDRESS=0 SSLPORT=10.0.0.34.993 MAXPERIP=16 TLS_CERTFILE=/usr/local/etc/postfix/gaia.pem/usr/local/etc/courier-imap/pop3d
#ADDRESS=0 #We use POP3 to authenticate user in our admin page. #Just connect to port 110, then send USER xxx\r\n,PASS xxx\r\n #So we listen on 127.0.0.1 PORT=127.0.0.1.110,10.0.0.34.110 MAXPERIP=16/usr/local/etc/courier-imap/pop3d-ssl
#SSLADDRESS=0 SSLPORT=10.0.0.34.995 MAXPERIP=16 TLS_CERTFILE=/usr/local/etc/postfix/gaia.pem
There are some unused authentication method /usr/local/lib/sasl2 such as
libsasldb.a libsasldb.so libsasldb.so.2 libcrammd5.a libcrammd5.so libcrammd5.so.2 libdigestmd5.a libdigestmd5.so libdigestmd5.so.2that cause a warning message in /var/log/maillog:
warning: SASL authentication failure: no user in db warning: SASL authentication failure: no secret in database warning: t42.net0.intranet[10.0.0.31]: SASL CRAM-MD5 authentication failedLeave them there cause no harm only an annoy warning. If you are sure that you don't really need them and don't want to see the warning anymore -- remove those files.
After install squirrelmail and create a symbolic link to HTTPS main page with command
# ln -s /usr/local/www/squirrelmail /home/www/public_ssl/mailSome options need to be set to speed up SquirrelMail when process large mail box:
# cd /usr/local/www/squirrelmail && ./configure
Table 9. SquirrelMail Options
| Option | Value | Description |
|---|---|---|
| Server Settings/Domain | gaia.net0.intranet | Set it to your domain name. |
| Server Settings/IMAP Settings/Server software | courier | We use courier-imap. |
| General Options/Allow server thread sort | true | Let courier-imap sort mails for us. |
| General Options/Allow server-side sorting | true | Let courier-imap sort mails for us. |
Add e-mail account to this system is simple, just follow the steps:
Create a FreeBSD user for that domain.
Create a maildir for the email address with command maildirmake. Set owner and group to the user created in previous step.
Add the domain to transport table with transport set to virtual:.
Add the user to user table with each fileld as indicated in user table.
#pw user add test -s /sbin/nologin
#mkdir -p /home/vhost/test/mail/gaia-test.net0.intranet
#maildirmake /home/vhost/test/mail/gaia-test.net0.intranet/test1
#maildirmake /home/vhost/test/mail/gaia-test.net0.intranet/test1/.~SPAM
#maildirmake /home/vhost/test/mail/gaia-test.net0.intranet/test1/.~VIRUS
#maildirmake /home/vhost/test/mail/gaia-test.net0.intranet/test1/.~Report-SPAM
#maildirmake /home/vhost/test/mail/gaia-test.net0.intranet/test1/.~Report-NOTSPAM
#chown -R test:test /home/vhost/test/mail/gaia-test.net0.intranet/test1
#echo INBOX.~SPAM > /home/vhost/test/mail/gaia-test.net0.intranet/test1/courierimapsubscribed
#echo INBOX.~VIRUS >> /home/vhost/test/mail/gaia-test.net0.intranet/test1/courierimapsubscribed
#echo INBOX.~Report-SPAM >> /home/vhost/test/mail/gaia-test.net0.intranet/test1/courierimapsubscribed
#echo INBOX.~Report-NOTSPAM >> /home/vhost/test/mail/gaia-test.net0.intranet/test1/courierimapsubscribed
#id test
uid=1002(test) gid=1002(test) groups=1002(test)
#userdbpw -md5
Password:
Reenter password:
$1$X$XXX
#mysql -u maildb -p maildb
mysql> INSERT INTO transport (domain,transport,customer_id)
-> VALUES ('gaia-test.net0.intranet','virtual:','test');
mysql> INSERT INTO user ( email,passwd,name,uid,gid,home,maildir,
-> allow_login,allow_receive,customer_id) VALUES (
-> 'test1@gaia-test.net0.intranet', '$1$X$XXX', 'Test User', '1002', '1002',
-> '/home/vhost/test/mail/gaia-test.net0.intranet/test1',
-> '/home/vhost/test/mail/gaia-test.net0.intranet/test1/', 'Y', 'Y', 'test');
There To enable user to manage their own account , I create a PHP script to manage a database part and write a small perl daemon that run as root to manage Maildir part. The PHP use fsockopen to talk to the perl daemon. Unfortunely, the script and daemon is my company property which I can not giveout without the permission from by boss.
User can access their mailbox by mail/squirrelmail or any email client such as mail/thunderbird. The FreeBSD user use their login user/password and virtual user user their email/password (i.e test1@test.intranet) to access the mailbox. User mail client need to support TLS/SASL2 if they want to relay their mail via this server.
Using SquirrelMail as a mail client give another plus. It allow me to write a plugin to allow virtual user to change their own password. The plugin need my perl daemon to run, so you can not take it and run the script. I put it to show how to write the plugin and to compile with GPL licence of SquirrelMail. You must move the plugin folder to /usr/local/www/squirrelmail/plugins and use squirrelmail configure script to enable the plugin. This is code of change_vhostpass.
I config the server to not notify user when they got spam or virus mail. The mail that identified as a spam or infested are move to the quarantine directory /var/virusmails. Every 5 minutes , I run a script to move the mail in quarantine directory to user "INBOX.~SPAM" or "INBOX.~VIRUS" and leave it there for user to check. After 7 days, another cron script is run to remove the spam/virus mail older than 7 days.
crontab -u root -e */5 * * * * /home/admin/bin/move-spam-mail.pl 2>/dev/null crontab -u test -e 0 1 * * * cd /home/vhost/test/mail && /usr/bin/find . \( -mtime +7 -a -name 'BLOCKED-*' \) -deleteEdit /usr/local/www/squirrelmail/config/config.php to show unread mail in .~SPAM and .~VIRUS folder
$default_unseen_notify = 3;
dspam has an ability to re-learn mail. To use this feature make sure that the file /usr/local/etc/dspam.conf has these two lines
MySQLUIDInSignature on Preference "signatureLocation=headers"The next step is to allow user to pass us the misclassified mails (both spam and no-spam) with full header intact, then let dspam re-learn those mails with the command:
#cat spam-mail | dspam --user vscan --class=spam --source=error #cat notspam-mail | dspam --user vscan --class=innocent --source=errorUnfortunately, there is no easy way to let user pass the original mail to us. If you use standard forward it will remove the original header. Using forward as attachment , give you a complex MIME mail that need a code to extract the original mail.
The method I use to implement on this server is .~Report-SPAM and .~Report-NOTSPAM folder for each user. SquirrelMail or IMAP user can use the move command to move spam/not-spam mail to these 2 folders with header intact. We can run cron job to scan the mail in these folder ,then run the above command on each mail.
crontab -u root -e
0 1 * * * find /home/vhost -name '.~Report-SPAM' -exec /home/admin/bin/dspam-relearn.pl spam '{}'/cur \;
0 2 * * * find /home/vhost -name '.~Report-NOTSPAM' -exec /home/admin/bin/dspam-relearn.pl innocent '{}'/cur \;
/home/admin/bin/dspam-relearn.pl
#!/usr/local/bin/perl -w
my $DSPAM_PATH = "/usr/local/bin/dspam";
my $DSPAM_FLAGS = "--user vscan --source=error";
if ( scalar(@ARGV) < 2 ||
$ARGV[0] ne "spam" && $ARGV[0] ne "innocent" )
{
die "usage: $0 spam|innocent folder_name";
}
my $mail_class = $ARGV[0];
my $PATH = $ARGV[1];
if ( !opendir(SPAM_DIR,$PATH) )
{
print STDERR "Can not open $PATH\n";
exit(1);
}
my $fname='';
my $t = time();
my @flist;
my $p;
while( ($fname=readdir(SPAM_DIR)) )
{
$p = "$PATH/$fname";
if ( -f $p )
{
my @st = stat("$PATH/$fname");
if ( $t - $st[9] > 3 )
{
push(@flist,$p);
}
}
}
closedir(SPAM_DIR);
foreach $p (@flist)
{
#relearn
`/bin/cat \"$p\" | $DSPAM_PATH $DSPAM_FLAGS --class=$mail_class 2>&1`;
unlink($p);
}
We host more than one customer on the same server, so it is very critical that each customer never be able to access file of another. There is many way to implement this restriction such as
Use FreeBSD jail(8) with real IP to host apache and sshd.
Use FreeBSD jail(8) with private IP (192.168.x.x) to host apache and use apache mod_proxy on real IP to allow the Internet to access jailed apache. For file upload use shell/scponly with WITH_SCPONLY_CHROOT=yes.
Use php safe_mode and shell/scponly with WITH_SCPONLY_CHROOT=yes.
We don't have a problem to optain an IP for each customer , so I use the first method to implement the user WWW site. There is a good document about Creating a FreeBSD Jail but it talk about creating a full blow jail. My document is about to create a minimum jail to host only sshd and appache.
After got the IP from the ISP , we need to add it to our server with the following command:
#ifconfig sk0 alias 10.0.0.35 netmask 255.255.255.255 #vi /etc/rc.conf ifconfig_sk0_alias0="inet 10.0.0.35 netmask 255.255.255.255"If you add wrong IP, the command to remove the alias is ifconfig sk0 -alias 10.0.0.35
There is a little inconvenient in jail system. You can not access a service in the main host with the loop back IP 127.0.0.1 , so any service on host (such as DNS) must listen to the host real IP. But if the service listen on the real IP, another machine can also access that server. This is why I complie IPFW support in the kernel.
/var/named/etc/namedb/named.conf
listen-on { 127.0.0.1; 10.0.0.34; };
allow-recursion { 127.0.0.1; 10.0.0.34; 10.0.0.35; };
/etc/ipfw.rules
#Bandwidth statistic -- our accounting need this information add 00100 count ip from 10.0.0.35 to not me add 00101 count ip from not me to 10.0.0.35 #Additional rules for DNS service add 50000 deny ip from not me to 10.0.0.34 53
Each of our customer got 2 FreeBSD user ids , one is a main account and another is an account that run apache. User and group in the jail is independent from the host but to reduce the headache when manage the server I add both user in the host first, then copy the uid/gid to the jail.
#pw user add www_test -s /sbin/nologin -d /nonexistent -G test #id test uid=1002(test) gid=1002(test) groups=1002(test) #id www_test uid=1003(www_test) gid=1003(www_test) groups=1003(www_test), 1002(test)
If you want sshd in the jail log the message to host system log, you must edit the host /etc/rc.conf to allow syslogd to listen to the request from the jail.
syslogd_flag="-ss -l /home/vhost/test/var/run/log"
The jailed process can not access files outside jail. You must copy all programs/files and libraries need by the programs to the jail. I write a perl script copy_dep.pl which use ldd(1) command to copy all required files.
You need not to install full FreeBSD on the jail because we do not need a full blow system on the jail. We only need the system that can run sshd(8) and www/apache13-modssl. Copy the libraries and programs from their path to the jailed system (i.e /home/vhost/test) or for configuration file, create them in the jailed system.
Table 10. Base Files for Jail
| Name | Description/Contents |
|---|---|
| /bin/sh | Standard command interpreter for the system.Many program expects that it always exists in the system. |
| /libexec/ld-elf.so.1 | Run-time link-editor |
| /var/run/ld-elf.so.hints | Run-time link-editor |
| /etc/resolv.conf |
nameserver 10.0.0.34 |
| /etc/hosts |
127.0.0.1 localhost localhost.localdomain 10.0.0.34 gaia gaia.net0.intranet 10.0.0.35 selene selene.net0.intranet |
| /etc/group |
# -- Use real group id -- copy from the host /etc/group wheel:*:0:root sshd:*:22: pgsql:*:70: mysql:*:88: nogroup:*:65533: nobody:*:65534: test:*:1002:www_test www_test:*:1003: |
| /etc/master.passwd |
# -- Use real group id -- #touch /home/vhost/test/etc/master.passwd #vipw -d /home/vhost/test/etc # -- home and shell of root account MUST be existed in the jail root:*:0:0::0:0:Charlie &:/:/bin/sh sshd:*:22:22::0:0:Secure Shell Daemon:/var/empty:/usr/sbin/nologin pgsql:*:70:70::0:0:PostgreSQL Daemon:/:/usr/sbin/nologin mysql:*:88:88::0:0:MySQL Daemon:/:/usr/sbin/nologin nobody:*:65534:65534::0:0:Unprivileged user:/nonexistent:/usr/sbin/nologin test:*:1002:1002::0:0:User &:/www_data/home:/bin/sh www_test:*:1003:1003::0:0:User &:/www_data:/usr/sbin/nologin # -- To change password use #pw -V /home/vhost/test/etc usermod test -h 0 # -- Only main user need a password |
| /etc/localtime | Copy from the host /etc/localtime |
| /dev/random /dev/null /dev/urandom |
FreeBSD 6 use devfs(8) to create a device node instead of the old mknod(8). To create device node in FreeBSD 6 you need to put the device creation command in your jail startup script. This step will need only to test the jail. FreeBSD /etc/rc.d/jail will create the devfs for you in the real run depend on the value in the host /etc/rc.conf. mkdir /home/vhost/test/dev /sbin/mount -t devfs dev /home/vhost/test/dev -- see /etc/default/devfs.rules to check rule number of devfsrules_jail /sbin/devfs -m /home/vhost/test/dev rule -s 4 applyset |
| sysctl security.jail.sysvipc_allowed=1 |
If you want to use devel/ZendOptimizer, you need to enable this sysctl(8) value when you test your jail. In the real run /etc/rc.d/jail will set it for you. |
| /tmp |
#mkdir /home/vhost/test/tmp #chgrp test /home/vhost/test/tmp #chmod g+rw /home/vhost/test/tmp |
For example:
#/home/admin/bin/copy_dep.pl /home/vhost/test /bin/sh /libexec/ld-elf.so.1 #mkdir /home/vhost/test/dev #mount -t devfs dev /home/vhost/test/dev #/sbin/devfs -m /home/vhost/test/dev rule -s 4 applyset #mkdir /home/vhost/test/etc #vi /home/vhost/test/etc/resolv.conf ... #jail -l -U root -i /home/vhost/test selene.net0.intranet 10.0.0.35 /bin/sh -- Now you should be in the jail unless there is a mising files. #/usr/sbin/sshd -Dd #/usr/local/sbin/httpd -DSSL ...
The following files are need for ssh service in a jail.
Table 11. sshd Files for Jail
| Name | Description/Contents |
|---|---|
| /usr/sbin/sshd | SSH Daemon |
| /usr/libexec/sftp-server | SFTP server subsystem |
| /bin/ls /bin/chmod /bin/mkdir /bin/rmdir /usr/bin/scp /bin/ln /bin/mv /bin/rm /usr/bin/cd /usr/bin/groups /bin/pwd /bin/echo | Basic commands for scp |
| /etc/ssh/ |
#mkdir -p /home/vhost/test/etc/ssh
#/usr/bin/ssh-keygen -t rsa1
-b 1024 -f /home/vhost/test/etc/ssh/ssh_host_key -N ''
#/usr/bin/ssh-keygen -t dsa
-f /home/vhost/test/etc/ssh/ssh_host_dsa_key -N ''
#/usr/bin/ssh-keygen -t rsa
-f /home/vhost/test/etc/ssh/ssh_host_rsa_key -N ''
#cp /etc/ssh/moduli /home/vhost/test/etc/ssh/
#cp /etc/ssh/ssh_config /home/vhost/test/etc/ssh/
|
| /etc/ssh/sshd_config |
ListenAddress 10.0.0.35:22 PasswordAuthentication yes UseDNS no #I can not make PAM work in chroot/jail UsePAM no AllowUsers test@* Subsystem sftp /usr/libexec/sftp-server |
| /var/empty |
#mkdir -p /home/vhost/test/var/empty #chflags schg /home/vhost/test/var/empty |
| /etc/auth.conf /var/log/wtmp /var/log/lastlog |
#cp /etc/auth.conf /home/vhost/test/etc #mkdir -p /home/vhost/test/var/log #touch /home/vhost/test/var/log/wtmp #touch /home/vhost/test/var/log/lastlog |
The following files are need for apache service in a jail. If you want to run an 32-bit version of Apache/PHP/ZendOptimizer in AMD64 jail, all the 32-bit s library (*.so) in /lib and /usr /lib must be stored in /usr/lib32 of the jail ins tead of /lib or /usr/lib. Note that the copy_dep.pl script can not use on 32-bit programs on 64-bit OS.
Table 12. Apache Files for Jail
| Name | Description/Contents |
|---|---|
| /usr/local/sbin/httpd | Apache daemon |
| /usr/local/libexec/apache/* |
#/home/admin/bin/copy_dep.pl /home/vhost/test /usr/local/libexec/apache/* |
| /usr/local/lib/php/* |
#rsync -a -v /usr/local/lib/php /home/vhost/test/usr/local/lib #/home/admin/bin/copy_dep.pl /home/vhost/test /usr/local/lib/php/20020429/*.so |
| /usr/local/etc/apache/* |
#cp -r /usr/local/etc/apache /home/vhost/test/usr/local/etcYou may need to modify httpd.conf for jail as indicate in Config Apache. Don't forget to change Listen,ServerName ,SSLCertificateFile and SSLCertificateKeyFile. Set User to www_test and Group to test or any user/group you want to run apache as. Don't forget to remove unused host SSL key and certificate. |
| /usr/local/etc/php |
#rsync -a -v --delete /usr/local/etc/php /home/vhost/test/usr/local/etc |
| /usr/local/etc/php.ini |
#cp /usr/local/etc/php.ini /home/vhost/test/usr/local/etc |
| /var/log/httpd |
#mkdir -p /home/vhost/test/var/log/httpd |
| Another files need by PHP extension | If you install a PHP extension, the extension may need a data file to work properly. For example a curl extension need /usr/local/share/curl/curl-ca-bundle.crt when fetch an SSL page. You can check the missing file by test apache in a chroot environment and check the error.log as show in the end of Prepare Base System section when try to access your script. |
| Another files need by mod_perl |
mod_perl needs modules in /usr/local/lib/perl to run. If you allow user to use mod_perl (I allow only PHP on user site) you must check and copy any required files for the user. Or just copy everything from host to jail #rsync -a -v /usr/local/lib/perl5 /home/vhost/test/usr/local/lib/ |
As pointed out in Prepare Host , you can not use 127.0.0.1 to access to the database ( both MySQL and PostgreSQL. There are 2 solutions to this problem:
Use the same method as named, Listen to real ip and use ipfw to block remote traffic.
Use unix socket instead of TCP/IP. This method need a change to databases configuration to allow an access accross jail. But it is faster, so I use this method
/home/mysql/my.cnf
[client] ... socket = /home/tmp/mysql.sock [mysqld] port = 3306 ... socket = /home/tmp/mysql.sock ...
/home/pgsql/data/postgresql.conf
unix_socket_directory = '/home/tmp'
There are problems about of legacy code and permission need to be solved with the following command:
#vi /etc/group home_tmp:*:900:mysql,pgsql #mkdir /home/tmp #chgrp home_tmp /home/tmp #chmod g+w /home/tmp #ln -s /home/tmp/mysql.sock /tmp/mysql.sock #ln -s /home/tmp/.s.PGSQL.5432 /tmp/.s.PGSQL.5432 #ln -s /home/tmp/.s.PGSQL.5432.lock /tmp/.s.PGSQL.5432.lock
The next problem is to create a hard link from a socket in /home/tmp to the jail.It is not easy as it seems. Using ln(1) to create a hard link may seem to work in the first time buf after the database is stopped, the socket in /home/tmp is deleted but the socket in jail is not deleted (its link count will only reduce) and it will point to the limbo. When the database is restarted, it create a new socket but the socket in jail still point to the old one. So, there is no database server to listen to the request from the client in jail. To fix this problem I write a simple script to check the i-node of both sockets and recreate a link if there are not match. You must
Run this script everytime you restart the database servers or
Use cron(8) to run it every minute or
Modify /usr/local/etc/rc.d/mysql-server.sh and /usr/local/etc/rc.d/010.pgsql.sh to run this script as start_postcmd command.
/home/admin/bin/check_jail_db.pl
#!/usr/local/bin/perl -w
use strict;
my @FILE_LIST = ('mysql.sock','.s.PGSQL.5432','.s.PGSQL.5432.lock');
my %CHECK_INODE;
sub trim($)
{
my $string = shift;
$string =~ s/^\s+//;$string =~ s/\s+$//;return $string;
}
if ( !open(RC_CONF,"/etc/rc.conf") ){ die "Can not open /etc/rc.conf"; }
my $jail_list = '';
READ_RC_CONF:
while ( <RC_CONF> )
{
my $line = $_;
if ( $line =~ /\s*jail_list\s*=\s*(.*)/ )
{
$jail_list = $1;chop($jail_list);last READ_RC_CONF;
}
}
if ( $jail_list =~ /([^#]+)/ ) { $jail_list = trim($1); }
if ( $jail_list =~ /"([^"]+)/ ) { $jail_list = trim($1); }
my @JAIL_USER = split(/\s+/,$jail_list);
my $check_name;
foreach $check_name (@FILE_LIST)
{
my @st = stat("/home/tmp/$check_name");
if ( scalar(@st) >= 13 ) { $CHECK_INODE{$check_name} = $st[1]; }
else { $CHECK_INODE{$check_name} = 0; }
}
foreach my $user (@JAIL_USER)
{
foreach $check_name (@FILE_LIST)
{
my $re_link = 1;
my $fname = "/home/vhost/$user/tmp/$check_name";
if ( -e $fname )
{
my @st = stat($fname);
if ( scalar(@st) > 1 && $st[1] == $CHECK_INODE{$check_name} ) { $re_link = 0; }
}
if ( $re_link && $CHECK_INODE{$check_name} > 0 )
{
unlink($fname);
if ( !link("/home/tmp/$check_name",$fname) )
{
print STDERR "Can not create a hard link [$fname]\n";
}
}
}
}
Send a mail from jail is not easy. You need the full version of mail/postfix or sendmail(8) which are both very large. In our jailed system, we only want to allow the customer PHP script to send an e-mail to another host. The simplest solution is use SMTP mail such as Mail package from pear.php.net to connect to the host IP (10.0.0.34) and use SASL2 to authenticate before submit the mail.
Unfortunely not all of our customer happy with this solution. Some customer has a lagacy code that use PHP's mail to send the mail and can not change the code. The solution for this problem is to create a minimum command to allow PHP's mail to work. The PHP command need two programs when called. The first program is /bin/sh which already in our jail and the second command is /usr/sbin/sendmail which will be executed with the parameter -t -i (see the output of phpinfo() for the value on your system).
sendmail.c
/*
##################################################################
# $Id: article.sgml,v 1.2 2006/05/27 05:44:30 cws Exp $
# Copyright (C) MiracleNet Group (http://miraclenet.co.th)
# Chatchawan Wongsiriprasert
##################################################################
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
main(int argc,char *argv[])
{
FILE * out;
char buf[8192];
time_t t;
time(&t);
srandom(t);
umask(0007);
sprintf(buf,"/var/spool/mqueue/mail-%010d-%010d",t,random());
if ( (out=fopen(buf,"wt")) == 0 )
{
perror("Can not open mail file");
exit(1);
}
while ( fgets(buf,sizeof(buf),stdin) != 0 )
{
fputs(buf,out);
}
fclose(out);
return 0;
}
Compile this program and put in in /usr/sbin. Don't forget to
change the permision of /var/spoll/mqueue/ in the jail to allow
the user that run apache to write to that folder.
#gcc -O2 -o /home/vhost/test/usr/sbin/sendmail sendmail.c #mkdir -p /home/vhost/test/var/spool/mqueue #chgrp test /home/vhost/test/var/spool/mqueue #chmod g+w /home/vhost/test/var/spool/mqueueNow, when you call PHP's mail you will get a new file in the jail var/spool/mqueue. The next step is to run a program in the host system that scan this directory and send out the mail. This can be done with the following shell script and cron(8)
vhost-relay-mail.sh
#!/bin/sh
user_id=`/usr/bin/id -nu`
VHOST_BASE=/home/vhost
SPOOL_FOLDER=var/spool/mqueue
MAIL_LIST=`ls ${VHOST_BASE}/${user_id}/${SPOOL_FOLDER}/mail-* 2>/dev/null`
if [ $? -eq 0 ];
then
for mail_file in ${MAIL_LIST}
do
/bin/cat ${mail_file} | /usr/sbin/sendmail -t -i
/bin/rm ${mail_file}
done
fi
Add the cron entry to relay mail from this spool every 5 minutes.
#crontab -u test -e 0-59/5 * * * * /home/admin/bin/vhost-relay-mail.sh
In FreeBSD 6, it is very easy to create a jail using /etc/rc.d/jail script. All you need is put the jail configuration in /etc/rc.conf and let FreeBSD startup script do the work.
/etc/rc.conf
vhost_enable="YES" # Our script to handle virtual host script jail_enable="YES" # Set to NO to disable starting of any jails jail_list="test" # Space separated list of names of jails jail_set_hostname_allow="NO" # Allow root user in a jail to change its hostname jail_socket_unixiproute_only="YES" # Route only TCP/IP within a jail jail_sysvipc_allow="YES" #For ZendOptimizer jail_test_rootdir="/home/vhost/test" jail_test_hostname="selene.net0.intranet" jail_test_ip="10.0.0.35" jail_test_exec_start="/bin/sh /etc/rc.jail" jail_test_devfs_enable="YES" jail_test_devfs_ruleset="devfsrules_jail"
/home/vhost/test/etc/rc.jail
#!/bin/sh /usr/sbin/sshd /usr/local/sbin/httpd -DSSL
/usr/local/etc/rc.d/vhost.sh
#!/bin/sh
# PROVIDE: vhost
# REQUIRE: NETWORKING SERVERS
# BEFORE: DAEMON
# AFTER: mysql-server postgresql
# KEYWORD: shutdown
. /etc/rc.subr
name="vhost"
rcvar=`set_rcvar`
load_rc_config ${name}
: ${postfix_enable="NO"}
start_cmd="vhost_start"
stop_cmd="vhost_stop"
vhost_start () {
echo "Starting ${name}."
/home/admin/bin/check_jail_db.pl
}
vhost_stop () {
echo "Stopping ${name}."
}
run_rc_command "$1"
You can use the command jls(8) , jexec(8) and killall(1) -j to manipulate the program in the jail.