ควรสงสัยไว้ก่อนว่า ข้อเขียนนี้ต้องมีที่ผิดพลาดแน่นอน หากจะทำตาม ควรมีความรู้เรื่องลินุกซ์พอควรที่จะแก้ปัญหาที่เกิดจากการผิดพลาดในข้อเขียนได้
จะติดตั้งเซิร์ฟเวอร์แบบ all-in-one สำหรับใช้ในหน่วยงานเล็ก ๆ โดย
(ดังนั้น เวลาติดตั้ง ควรให้มีเนื้อที่มากที่สุด)
ตั้งชื่อใน /sys1 ให้เลียนแบบกับไดเรกทอรี่จริง เช่น /var/www ต้องสำรองข้อมูลทุกวัน ก็เป็น /sys1/sysb/var/www เป็นต้น
รายการที่ต้องทำคือ
สมมุติว่า
หลังจากผ่านการติดตั้งแบบ Net Install หรือแบบ debootstrap มาแล้ว
เราจะเตรียมไดเรกทอรี่เก็บค่าต่าง ๆ ไว้ที่ /sys1 เพื่อให้สะดวกในการสำรองข้อมูล
สำหรับการนี้ ควรแยก /sys1 ออกมาเป็นอีกพาร์ติชั่นนึง โดยให้มีเนื้อที่มากที่สุด
# mkdir -p /sys1/{sysb,syst}
# mkdir -p /sys1/sysb/{etc,var}
ย้าย /home
# mv /home /sys1/sysb
# ln -sf /sys1/sysb/home /
ย้าย /usr/local/{bin,sbin}
# mkdir -p /sys1/sysb/usr/local
# mv /usr/local/{bin,sbin} /sys1/sysb/usr/local
# ln -sf /sys1/sysb/usr/local/{bin,sbin} /usr/local
ย้าย crontab
# mkdir -p /sys1/sysb/var/spool/cron
# mv /var/spool/cron/crontabs /sys1/sysb/var/spool/cron
# ln -sf /sys1/sysb/var/spool/cron/crontabs /var/spool/cron
มี ssh screen และ vim
# aptitude install ssh screen vim less pciutils rsync lynx
แต่ง vim เล็กน้อย
# vi /etc/vim/vimrc.local
syntax on set tabstop=4
ปรับให้ vim เป็นค่าปริยายของ editor และ vi
# update-alternatives --config editor
<--- เลือก vim.basic
# update-alternatives --config vi
<--- เลือก vim.basic
ปรับตั้ง interfaces
# vi /etc/network/interfaces
auto eth0 iface eth0 inet static address 192.168.1.3 netmask 255.255.255.0 network 192.168.1.0 broadcast 192.168.1.255 auto eth1 iface eth1 inet static address 192.168.5.3 netmask 255.255.255.0 network 192.168.5.0 broadcast 192.168.5.255 gateway 192.168.5.1
เพื่อให้เครือข่ายภายในสามารถออกสู่ภายนอกได้ โดยใช้เซิร์ฟเวอร์ตัวนี้เป็นเกตเวย์ จะตั้งให้ฟอร์เวิร์ดไอพีได้
# vi /etc/sysctl.conf
... net.ipv4.ip_forward=1
ทำให้มีผลทันที
# echo 1 > /proc/sys/net/ipv4/ip_forward
ติดตั้ง
# aptitude install portsentry iptables
สำหรับ portsentry ใช้ค่าปริยายทั้งหมด
สำหรับ iptables จะทำเป็นแบบสคริปต์ ไว้รันเวลาเปิดเครื่อง (จริง ๆ คือ รันตอนอินเทอร์เฟส eth1 เปิดขึ้นมาใช้งาน)
มีการเพิ่มกฎในการบล๊อกไอพีนิดหน่อย ทำให้ลดภาระ web server ในการกรองไอพี
เตรียมไฟล์ข้อมูล
# mkdir -p /sys1/sysb/etc/iptables
# ln -sf /sys1/sysb/etc/iptables /etc
# echo "#BLOCKED IP LIST" >> /etc/iptables/block_ip
สร้างสคริปต์ไว้เรียกตอนเปิด eth1 คือ /usr/local/sbin/d.eth1-iptables
# vi /usr/local/sbin/d.eth1-iptables
#!/bin/bash # SIMPLE IPTABLES INTERFACE=eth1 INT_NET=192.168.0.0/16 BLOCK_FILE=/etc/iptables/block_ip #DELETE OLD RULES iptables -F > /dev/null iptables -F -t nat > /dev/null #NAT iptables -t nat -A POSTROUTING -o $INTERFACE -j MASQUERADE #BLOCK SPECIFIC IP #SORT IP LIST IF DATA CHANGED touch $BLOCK_FILE "$BLOCK_FILE.bak" cmp -s "$BLOCK_FILE" "$BLOCK_FILE.bak" if [ $? -ne 0 ]; then TEMP_FILE=/tmp/block_ip touch $TEMP_FILE mv $BLOCK_FILE "$BLOCK_FILE.bak" cat "$BLOCK_FILE.bak" | while read LINE; do if [ ${LINE:0:1} == "#" ]; then echo $LINE >> $BLOCK_FILE else echo $LINE >> $TEMP_FILE fi done cat $TEMP_FILE | sort >> $BLOCK_FILE rm $TEMP_FILE fi #DO BLOCK cat $BLOCK_FILE | grep -v "#" | cut -d \ -f 1 | while read IP; do iptables -A INPUT -s $IP -j DROP done #BLOCK ssh INTRUDER BY RATE-LIMIT 4 FOR 600 SECONDS #FROM http://www.debian-administration.org/articles/187 iptables -I INPUT -p tcp --dport 22 -i $INTERFACE -m state --state NEW -m recent --set iptables -I INPUT -p tcp --dport 22 -i $INTERFACE -m state --state NEW -m recent --update --seconds 600 --hitcount 4 -j DROP #FORWARD INTERNAL iptables -A FORWARD -s $INT_NET -j ACCEPT iptables -A FORWARD -d $INT_NET -j ACCEPT #DROP REST iptables -A FORWARD -s ! $INT_NET -j DROP
# chmod 755 /usr/local/sbin/d.eth1-iptables
สร้างไพล์ข้อมูลไอพีที่ต้องการบล๊อก ชื่อ block_ip เอาไว้ที่ /etc/iptables
# vi /etc/iptables/block_ip
#BLOCKED IP LIST WWW.XXX.YYY.ZZZ #COMMENT ...
เปลี่ยนตัวเลขเอาตามจริงนะครับ
เวลาเราจะเพิ่มไอพี ก็มาแก้ไขที่ไฟล์นี้
นำกฎมาใช้ตอนเปิดเครื่องใหม่ ผ่าน /etc/network/interfaces
# vi /etc/network/interfaces
... auto eth1 iface eth1 inet static address 192.168.5.3 netmask 255.255.255.0 network 192.168.5.0 broadcast 192.168.5.255 gateway 192.168.5.1 post-up /usr/local/sbin/d.eth1-iptables pre-down /sbin/iptables-save > /etc/iptables/rules-backup
(เวลาจะปรับปรุงสคริปต์ เอาเนื้อความจาก /etc/iptables/rules-backup มาใช้อ้างอิงดูในการปรับปรุงได้)
สำหรับครั้งแรก ต้องรัน 1 ครั้ง ครั้งต่อไปไม่ต้องแล้ว จะรันผ่านอินเทอร์เฟส eth1 เอง
# /usr/local/sbin/d.eth1-iptables
เอาไว้เก็บแพกเกจเดเบียน สำหรับเครือข่ายภายใน อันนี้เนื้อหาไม่ค่อยจำเป็น จะเอาเก็บไว้ที่ /sys1/syst
เตรียมไดเรกทอรี่
# mkdir -p /sys1/syst/var/cache/apt-proxy
# ln -sf /sys1/syst/var/cache/apt-proxy /var/cache
ติดตั้งและปรับแต่ง
# aptitude install apt-proxy
# mv /etc/apt-proxy /sys1/sysb/etc
# ln -sf /sys1/sysb/etc/apt-proxy /etc
# vi /etc/apt-proxy/apt-proxy-v2.conf
... [debian] backends = http://ftp.debianclub.org/debian http://ftp.debian.org/debian ... [security] backends = ftp://security.debian.org/debian-security http://security.debian.org/ ...
ลูกข่ายสามารถใช้งานผ่าน apt-proxy โดยเปลี่ยน sources.list ดังนี้
... #deb http://ftp.debian.org/debian stable main contrib non-free deb http://server1.example.com:9999/debian stable main contrib non-free ... #deb http://security.debian.org/ stable/updates main contrib non-free deb http://server1.example.com:9999/security stable/updates main contrib non-free ...
แก้ปัญหา apt-proxy ชอบตายบ่อย ๆ ด้วยการให้เริ่ม apt-proxy ใหม่ ตอนเที่ยงคืนทุกวัน
# crontab -e
... 5 0 * * * /etc/init.d/apt-proxy restart >&2 ...
เพื่อให้ประหยัดแบนด์วิธสูงสุด ควรติดตั้งพร๊อกซี่สำหรับภายในด้วย
ติดตั้ง
# aptitude install squid3
ปรับเล็กน้อย
# vi /etc/squid3/squid.conf
... #http_port 3128 #MOVE 3128 TO 8080 AND MAKE TRANSPARENT PROXY http_port 8080 transparent ... acl to_localhost dst 127.0.0.0/8 acl ournetwork src 192.168.0.0/16 acl SSL_ports port 443 ... http_access allow localhost http_access allow ournetwork ...
สมมุติว่าได้สมัครเป็นสมาชิก ddns client ไว้ที่ www.zoneedit.com และ www.everydns.net ไว้เรียบร้อยแล้ว
การนี้เราจะใช้ทั้งคู่ในการเป็น name server ให้เรา กันเหนียวไว้ เวลาอันไหนตาย อีกอันจะได้ทำหน้าที่แทน
จากประวัติพบว่า zoneedit เสถียรและอัปเดตเร็วกว่า เราจึงให้ขึ้นเป็น primary
งานที่ต้องทำคือ
# mkdir -p /sys1/sysb/etc/ddns
# ln -sf /sys1/sysb/etc/ddns /etc
ตั้งชื่อว่า d.router-getip เอาไว้ใน /usr/local/sbin
โดยถ้ามีอาร์กิวเมนต์ต่อท้ายว่า "RESET" จะสั่งให้เราเตอร์เริ่มการทำงานใหม่ สำหรับใช้ในกรณีที่เราเตอร์ฝั่งเราหรือฝั่งไอเอสพีเกิดอาการแฮงก์
(ต้องปรับสคริปต์ตามเราเตอร์ที่ใช้นะครับ)
# vi /usr/local/sbin/d.router-getip
#!/bin/bash USER="ROUTER_ADMIN" PASSWORD="ROUTER_PASSWORD" IPAB="58.9" #OUR FIRST 2 DIGIT OF IP NUMBER ROUTERNAME="192.168.5.1" if [ "$1" == "RESET" ]; then ADDRESS="http://$ROUTERNAME/rebootinfo.cgi" wget -o /dev/null -O - \ --http-user="$USER" \ --http-passwd="$PASSWORD" \ "$ADDRESS" echo "RESET ROUTER" else ADDRESS="http://$ROUTERNAME/wancfg.cmd?action=view" IP_ADDR=`wget -o /dev/null -O - \ --http-user="$USER" \ --http-passwd="$PASSWORD" \ "$ADDRESS" \ | grep $IPAB \ | cut -d ">" -f 2 \ | cut -d "<" -f 1` echo $IP_ADDR fi
เปลี่ยนสถานะให้ root ดูได้เท่านั้น เพราะมีรหัสผ่านอยู่ในสคริปต์
# chmod 700 /usr/local/sbin/d.router-getip
ตั้งชื่อว่า d.router-cron-checkip เอาไว้ใน /usr/local/sbin
การทำงานคือ ถ้าอ่านค่าไอพีจากเราเตอร์ไม่ได้ (อาจเกิดจากเราเตอร์ของเราหรือของไอเอสพีแฮงก์) จะสั่งรีเซ็ตเราเตอร์ใหม่
# vi /usr/local/bin/d.router-cron-checkip
#!/bin/bash DATA="/etc/ddns/router-ip" OLD_IP=`cat $DATA` CUR_IP=`/usr/local/sbin/d.router-getip` #SOLVE ROUTER (OR ISP?) IS HUNG if [ ! $CUR_IP ]; then /usr/local/sbin/d.router-checkip RESET fi #TEST BETWEEN SAVED-IP AND NEW-READ-IP if [ "$OLD_IP" != "$CUR_IP" ]; then echo $CUR_IP > $DATA #SAVE NEW IP /usr/local/sbin/d.router-reconnect fi
เปลี่ยนสถานะให้รันได้
# chmod 700 /usr/local/bin/d.router-cron-checkip
ตั้ง cron ทุก 5 นาที (ถ้าคุณภาพสายไม่ดี อาจตั้งค่าให้บ่อยขึ้นก็ได้ครับ)
# crontab -e
... #CHECK ROUTER IP ADDRESS EVERY 5 MIN */5 * * * * /usr/local/bin/d.router-cron-checkip ...
ในครั้งแรก ที่ยังไม่มีไฟล์ router-ip ต้องสร้างก่อน เพียงครั้งเดียว
# d.router-getip > /etc/ddns/router-ip
สร้างไฟล์ข้อมูลโดเมนเอาไว้ที่ /etc/ddns (เลียนแบบการวางไฟล์จาก apache2)
โดยเราจะเขียนไฟล์คอนฟิกเอาไว้ใน /etc/ddns/ddns-available และเมื่อเสร็จแล้วก็โยงลิงก์เข้าไปที่ /etc/ddns/ddns-enabled
(วิธีนี้ทำให้ปรับแต่งไฟล์ง่าย อันไหนที่ทดสอบแล้วว่ายังไม่เรียบร้อย ก็แค่ลบลิงก์ไป)
สคริปต์จะเข้าไปอ่านไฟล์คอนฟิกจากในไดเรกทอรี่ /etc/ddns/ddns-enabled
# mkdir -p /etc/ddns/{ddns-available,ddns-enabled}
สมมุติว่าเราจดทะเบียนไว้ที่ zoneedit.com และ everydns.net ไว้ 2 โดเมน
คือ example.com และ example.org
ตัวอย่างไฟล์คอนฟิก จะเป็นดังนี้
# vi /etc/ddns/ddns-available/zoneedit
#ZONEEDIT DATA USER="ZONEEDIT-USER" PASSWORD="ZONEEDIT-PASSWORD" #UPDATE_DOMAIN="DOMAIN:NAME-SERVER, ... " #NO SPACE IN FIELD UPDATE_DOMAIN=" example.com:ns13.zoneedit.com example.org:ns2.zoneedit.com"
# vi /etc/ddns/ddns-available/everydns
#EVERYDNS DATA USER="EVERYDNS-USER" PASSWORD="EVERYDNS-PASSWORD" #UPDATE_DOMAIN="DOMAIN:NAME-SERVER, ... " #NO SPACE IN FIELD UPDATE_DOMAIN=" example.com:ns2.everydns.net example.org:ns2.everydns.net"
ต้องระวัง พยายามอย่าพิมพ์ผิด เพราะข้อมูลในนี้คือตัวแปรในสคริปต์โดยตรง ไม่มีการตรวจสอบความถูกต้องจากสคริปต์อีกแล้ว
โยงลิงก์ เพื่อเปิดใช้งาน
# ln -sf /etc/ddns/ddns-available/* /etc/ddns/ddns-enabled
ต่อไปสร้างสคริปต์ชื่อ d.router-cron-checkddns เอาไว้ที่ /usr/local/sbin เพื่อตรวจสอบไอพีของเราจากผู้ให้บริการ
# vi /usr/local/sbin/d.router-cron-checkddns
#!/bin/bash # CRON SCRIPT FOR TEST IP FROM PROVIDER. # KNOWN PROVIDER: # zoneedit.com # everydns.net # # PUT PROVIDERS DATA FILE IN /etc/ddns/ddns-available . # MAKE LINK TO /etc/ddns/ddns-enabled TO ENABLE. #DEFAULT DIRECTORY CONTAIN DDNS PROVIDERS DATA DIR="/etc/ddns/ddns-enabled" # GLOBAL VAR CUR_IP=`/usr/local/sbin/d.router-getip` UPDATE_SCRIPT="/usr/local/sbin/d.router-updatezone" for PROVIDER in `ls -1 $DIR`; do #GET VARIABLE: $USER,$PASSWORD,$UPDATE_DOMAIN . $DIR/$PROVIDER for i in $UPDATE_DOMAIN; do DOMAIN=`echo $i | cut -d: -f1` NAME_SERVER=`echo $i | cut -d: -f2` TEST_IP=`nslookup $DOMAIN $NAME_SERVER | grep -v "#" | grep Address | cut -d\ -f2` if [ "$TEST_IP" != "$CUR_IP" ]; then $UPDATE_SCRIPT $PROVIDER fi done done
ทำให้รันได้
# chmod 700 /usr/local/sbin/d.router-cron-checkddns
ตั้ง cron ทุก 30 นาที
# crontab -e
... #CHECK DOMAIN IP ADDRESS EVERY 30 MIN */30 * * * * /usr/local/sbin/d.router-cron-checkddns ...
เป็นการสั่งอัปเดต โดยนำค่าจากข้อมูลในไฟล์คอนฟิกข้างบน คือใน /etc/ddns/ddns-enabled มาอัปเดต ชื่อไฟล์คือค่าอาร์กิวเมนต์ของสคริปต์ ยกเว้นถ้าใส่ -a จะหมายถึง all คืออัปเดตทุก ๆ ผู้ให้บริการ
การทำงานของสคริปต์จะแบ่งเป็น 2 จังหวะ คือในครั้งแรกจะอัปเดตรวดเดียวกับทุกโดเมน เมื่อเสร็จเรียบร้อยแล้วจะย้อนกลับมาตรวจสอบอีกครั้งนึงว่า ในแต่ละโดเมนรายงานค่าไอพีถูกต้องหรือไม่ ถ้าไม่ถูกก็จะเว้นไป 5 วินาที แล้วเชื่อมต่อใหม่เป็นจำนวน 10 รอบการทำงาน
สร้างสคริปต์ชื่อ d.router-updatezone เอาไว้ที่ /usr/local/sbin ในการอัปเดตเมื่อไอพีเปลี่ยน
# vi /usr/local/sbin/d.router-updatezone
#!/bin/bash # SCRIPT FOR UPDATE DNS RECORD. # KNOWN PROVIDER: # zoneedit.com # everydns.net # # PUT PROVIDERS DATA FILE IN /etc/ddns/ddns-available . # MAKE LINK TO /etc/ddns/ddns-enabled TO ENABLE. # DATA FILE FORMAT: # USER="user" # PASSWORD="password" # UPDATE_DOMAIN=" # example.com:ns1.zoneedit.com # example.com:ns2.zoneedit.com" if [ ! $1 ]; then PROG=`basename $0` echo "Usage: $PROG [-a=all | ddns_provider_file]" exit fi #DEFAULT DIRECTORY CONTAIN DDNS PROVIDERS DATA DIR="/etc/ddns/ddns-enabled" if [ "$1" == "-a" ]; then DDNS_ENABLED=`ls -1 $DIR` else DDNS_ENABLED=$1 fi # GLOBAL VAR IP_ADDR=`/usr/local/sbin/d.router-getip` # FIRST FAST UPDATE fastupdate() { ADDRESS=$1 DOMAIN=$2 USER=$3 PASSWORD=$4 echo "$DOMAIN fast update ..." wget -O - --http-user=$USER --http-passwd=$PASSWORD "$ADDRESS" } # SECOND RECHECK & UPDATE recheck() { ADDRESS=$1 DOMAIN=$2 NAME_SERVER=$3 USER=$4 PASSWORD=$5 echo "$DOMAIN recheck with $NAME_SERVER ..." TEST_IP=`nslookup $DOMAIN $NAME_SERVER | grep -v "#" | grep Address | cut -d \ -f 2` echo "REAL_IP=$IP_ADDR , TEST_IP=$TEST_IP" I=0 LOOP=10 while [ $I -lt $LOOP ] && [ "$IP_ADDR" != "$TEST_IP" ] ; do wget -O - --http-user=$USER --http-passwd=$PASSWORD $ADDRESS sleep 5 echo "ROUND=$I , NAME_SERVER=$NAME_SERVER , REAL_IP=$IP_ADDR , TEST_IP=$TEST_IP" TEST_IP=`nslookup $DOMAIN $NAME_SERVER | grep -v "#" | grep Address | cut -d \ -f 2` I=$[$I+1] done } # BEGIN MAIN #1.FAST UPDATE for PROVIDER in $DDNS_ENABLED; do #GET VARIRABLE $USER,$PASSWORD,$UPDATE_DOMAIN . $DIR/$PROVIDER for i in $UPDATE_DOMAIN; do DOMAIN=`echo $i | cut -d: -f1` case $PROVIDER in zoneedit) ADDRESS="http://dynamic.zoneedit.com/auth/dynamic.html?host=$DOMAIN" ;; everydns) ADDRESS="http://dyn.everydns.net/index.php?ver=0.1&domain=$DOMAIN" ;; esac fastupdate $ADDRESS $DOMAIN $USER $PASSWORD done done #WAIT 30 SECONDS BEFORE RECHECK sleep 30 #2.RECHECK for PROVIDER in $DDNS_ENABLED; do #GET VARIABLE $USER,$PASSWORD,$UPDATE_DOMAIN . $DIR/$PROVIDER for i in $UPDATE_DOMAIN; do DOMAIN=`echo $i | cut -d: -f1` NAME_SERVER=`echo $i | cut -d: -f2` case $PROVIDER in zoneedit) # NEW wget , USED WHEN dynamic.zoneedit.com IS DOWN OR THIS MACHINE STAY BEHIND FIREWALL ADDRESS="http://www.zoneedit.com/auth/dynamic.html?host=$DOMAIN&type=A&dnsto=$IP_ADDR" ;; everydns) # WITH IP, SUITABLE FOR MACHINE BEHIND FIREWALL ADDRESS="http://dyn.everydns.net/index.php?ver=0.1&ip=$IP_ADDR&domain=$DOMAIN" ;; esac recheck $ADDRESS $DOMAIN $NAME_SERVER $USER $PASSWORD done done
เปลี่ยนสถานะให้รันได้
# chmod 700 /usr/local/sbin/d.router-updatezone
สคริปต์นี้แทบไม่มีอะไร เพียงแต่ไปเรียกใช้สคริปต์อัปเดตข้างบน เพียงแต่ใส่พารามิเตอร์เป็น -a (อัปเดตทุกผู้ให้บริการ) เท่านั้น
แต่ว่าต้องสร้างสคริปต์นี้ไว้ เพราะเราจะต้องเชื่อมโยงกับการอัปเดต bind9 ที่จะติดตั้งต่อไป
ตั้งชื่อว่า d.router-reconnect เอาไว้ใน /usr/local/sbin
# vi /usr/local/sbin/d.router-reconnect
#!/bin/bash # UPDATE ALL ENABLED ZONE #KILL OLD PROCESS killall d.router-updatezone #UPDATE ALL ZONE /usr/local/sbin/d.router-updatezone -a #-------ADDITIONAL BIND9 SCRIPT:------------------
เสร็จแล้ว
จะทำให้สามารถใช้งานได้ทั้งภายใน และภายนอก (ภายนอกไม่ค่อยจำเป็น แต่ติดตั้งไว้เผื่อในอนาคตอาจเพิ่มการ lookup จากเซิร์ฟเวอร์ของเราที่อยู่ภายนอก)
โดยจะแยกไอพีภายในและภายนอกเป็น 2 กลุ่ม
และสร้างกรงขังด้วย (chroot jail)
สมมุติว่า
ติดตั้งแพกเกจ แล้วสั่งหยุดการทำงาน
# aptitude install bind9 dnsutils
# /etc/init.d/bind9 stop
เรื่องกรงขัง (chroot jail) จะเก็บไว้ที่ /sys1/sysb/chroot/bind
สร้างไดเรกทอรี่ก่อน แล้วโยงลิงก์ chroot ไปที่ / ให้พิมพ์ง่าย
# mkdir -p /sys1/sysb/chroot/bind
# ln -sf /sys1/sysb/chroot /
# chown root:bind /chroot/bind
แก้ให้อยู่ในกรง
# vi /etc/default/bind9
... # OPTIONS="-u bind" OPTIONS="-u bind -t /chroot/bind" ...
แก้ไขผู้ใช้ชื่อ bind ให้ย้ายบ้านไปที่ /chroot/bind แทน
# usermod --home /chroot/bind bind
เตรียมไดเรคทอรี่ย่อยในกรง
# mkdir -p /chroot/bind/{etc,dev,var/{cache,log,run}}
# mkdir -p /chroot/bind/var/cache/bind
# mv /etc/bind /chroot/bind/etc
# ln -sf /chroot/bind/etc/bind /etc
# mknod /chroot/bind/dev/null c 1 3
# mknod /chroot/bind/dev/random c 1 8
# chmod 666 /chroot/bind/dev/{null,random}
# chown -R bind:bind /chroot/bind/var/*
ทำให้ระบบเก็บปูมของ bind (system logging - syslogd)
# vi /etc/default/syslogd
... # SYSLOGD="" SYSLOGD="-a /chroot/bind/dev/log" ...
ปรับตั้งระบบปูมของ bind
# vi /etc/bind/named.conf.local
... logging { channel "querylog" { file "/var/log/bind9-query.log"; print-time yes; }; category queries { querylog; }; }; ...
ปรับตั้งประวัติปูมของ bind
# vi /etc/logrotate.d/bind9-query
/chroot/bind/var/log/bind9-query.log { weekly missingok rotate 10 postrotate /etc/init.d/bind9 reload > /dev/null endscript compress notifempty }
# ln -sf /chroot/bind/var/log/bind9-query.log /var/log/bind9-query.log
แก้ไข options ให้มาเก็บ pid ในกรงขัง
# vi /etc/bind/named.conf.options
... options { directory "/var/cache/bind"; pid-file "/var/run/named.pid"; ...
เสร็จขั้นต้น ทดสอบระบบครั้งแรก
# /etc/init.d/sysklogd restart
# /etc/init.d/bind9 restart
ต้องไม่มีรายงานข้อผิดพลาด
ถ้าเรียบร้อยก็สั่งหยุดบริการ เพื่อปรับตั้งต่อ
# /etc/init.d/bind9 stop
ต่อไปเป็นเรื่องการทำให้รองรับเครือข่ายภายในและภายนอก
สร้างไดเรกทอรี่ขึ้นมารองรับ
# mkdir -p /etc/bind/{internal,external}
# cd /etc/bind
สร้างกฎ acl สำหรับเครือข่ายภายใน ตั้งชื่อว่า acl_internal (นอกจากนั้นจะถือว่าเป็น external ทั้งหมด)
และให้มาอ่านไฟล์คอนฟิกที่เราจะสร้างขึ้นภายหลัง ของโซน example.com และ example.org
# vi named.conf.local
... acl acl_internal { # 127.0.0.0/8; 192.168.0.0/16; }; include "/etc/bind/allzone.conf"; ...
หมายเหตุ - ปกติวง 127.0.0.0/8 ควรจะถึอเป็นวงในด้วย แต่ในที่นี้เราจะตัดออกจาก acl_internal เนื่องจากเราไม่สามารถสั่งอัปเดตไอพีจากวงในได้ เพราะตั้งเป็นแบบเปลี่ยนค่าไม่ได้ ดังนั้นเราจะใช้ localhost ในการอัปเดตไอพีที่ถูกเรียกจากวงนอก จึงต้องเว้นไว้ แต่ใส่คอมเมนต์ไว้เพื่อจะได้ไม่หลงลืมข้อนี้
สร้างไฟล์คอนฟิกของ example.com และ example.org รวมกัน ตั้งชื่อว่า allzone.conf โดยตัดเอาเนื้อไฟล์ใน named.conf ในส่วนของการกำหนดโซนมาใส่ไว้
(ถ้ามีการใช้ view เราต้องใส่การกำหนดโซนไว้ภายใต้ view เท่านั้น ไม่สามารถกำหนดแบบซ้อนได้)
ตัดเนื้อไฟล์จาก named.conf ออก
# vi named.conf
... //-----8<------------------------------------------- // prime the server with knowledge of the root servers zone "." { type hint; file "/etc/bind/db.root"; }; // be authoritative for the localhost forward and reverse zones, and for // broadcast zones as per RFC 1912 zone "localhost" { type master; file "/etc/bind/db.local"; }; zone "127.in-addr.arpa" { type master; file "/etc/bind/db.127"; }; zone "0.in-addr.arpa" { type master; file "/etc/bind/db.0"; }; zone "255.in-addr.arpa" { type master; file "/etc/bind/db.255"; }; //------------------------------------------->8----- ...
สร้างไฟล์ allzone.conf ในการกำหนดค่าไฟล์ในการคุมทุกโซน โดยเอาเนื้อจาก named.conf ข้างบนมาใส่ในส่วนของ view internal
# vi allzone.conf
include "/etc/bind/key.conf"; view "internal" { match-clients { acl_internal; }; // ----- PASTE FROM named.conf ---------------------------------------- // prime the server with knowledge of the root servers zone "." { type hint; file "/etc/bind/db.root"; }; // be authoritative for the localhost forward and reverse zones, and for // broadcast zones as per RFC 1912 zone "localhost" { type master; file "/etc/bind/db.local"; }; zone "127.in-addr.arpa" { type master; file "/etc/bind/db.127"; }; zone "0.in-addr.arpa" { type master; file "/etc/bind/db.0"; }; zone "255.in-addr.arpa" { type master; file "/etc/bind/db.255"; }; // ----- END PASTE FROM named.conf ------------------------------------ zone "example.com" IN { type master; file "/etc/bind/internal/example.com.zone"; allow-update { none; }; }; zone "1.168.192.in-addr.arpa" IN { type master; file "/etc/bind/internal/example.com.reverse"; allow-update { none; }; }; zone "example.org" IN { type master; file "/etc/bind/internal/example.org.zone"; allow-update { none; }; }; }; view "external" { match-clients { any; }; zone "example.com" IN { type master; file "/etc/bind/external/example.com.zone"; allow-update { key allhost.; }; }; zone "example.org" IN { type master; file "/etc/bind/external/example.org.zone"; allow-update { key allhost.; }; }; };
สร้างกุญแจสำหรับเปลี่ยนค่าโซน ใช้กุญแจเดียวกันทุกโซน
# dnssec-keygen -r /dev/urandom -a HMAC-MD5 -b 512 -n HOST allhost
Kallhost.+157+38278
และได้ไฟล์ Kallhost.+157+38278.key และ Kallhost.+157+38278.private
ตัวอย่างเนื้อไฟล์ของ Kallhost.+157+38278.key มีดังนี้
allhost. IN KEY 512 3 157 hvql4JlIIajNJzxFLuQQQg4HUNSQExpTuO2kzLudphxHPo3+N976ztqI dPXn2rdvVM6mHSG+0wwhlky+MRwQ+Q==
นำเนื้อไฟล์ของ Kallhost.+157+38278.key ท่อนที่เป็นรหัสมาสร้างไฟล์กุญแจ ตั้งชื่อว่า key.conf
# vi key.conf
key allhost. { algorithm HMAC-MD5; secret "hvql4JlIIajNJzxFLuQQQg4HUNSQExpTuO2kzLudphxHPo3+N976ztqI dPXn2rdvVM6mHSG+0wwhlky+MRwQ+Q=="; };
ส่วนต่อไปนี้ เขียนไว้เพื่อให้เห็นโครงสร้าง แต่ตอนหน้าจะเขียนเป็นสคริปต์สำหรับผลิตไฟล์โซน และไฟล์รีเวิร์ส หากคร้านที่จะเขียนโซนไฟล์เอง อาจข้ามไปอ่านส่วนขยายตอนหน้าเลยก็ได้ครับ
ต่อไปเป็นการสร้างไฟล์โซนและไฟล์รีเวิร์สโซน
ไฟล์โซน internal ของ example.com
# vi internal/example.com.zone
$TTL 86400 @ IN SOA server1.example.com. root.server1.example.com. ( 41 ; serial (d. adams) 3H ; refresh 15M ; retry 1W ; expiry 1D ) ; minimum @ NS ns1 server1 IN A 192.168.1.1 www IN CNAME server1 ftp IN CNAME server1 mail IN CNAME server1 ns1 IN CNAME server1 work1 IN A 192.168.1.101 work2 IN A 192.168.1.102 work3 IN A 192.168.1.103
ไฟล์รีเวิร์ส internal ของ example.com
# vi internal/example.com.reverse
$TTL 86400 @ IN SOA server1.example.com. root.server1.example.com. ( 41 ; serial (d. adams) 3H ; refresh 15M ; retry 1W ; expiry 1D ) ; minimum @ NS ns1 1 IN PTR server1.example.com. 101 IN PTR work1.example.com. 102 IN PTR work2.example.com. 103 IN PTR work3.example.com.
ไฟล์โซน internal ของ example.org
# vi internal/example.org.zone
$TTL 86400 @ IN SOA server1.example.org. root.server1.example.org. ( 42 ; serial (d. adams) 3H ; refresh 15M ; retry 1W ; expiry 1D ) ; minimum @ NS ns1 server1 IN A 192.168.1.1 www IN CNAME server1 ns1 IN CNAME server1 mail IN CNAME server1 ftp IN CNAME server1
สร้างโซนไฟล์ external ของ example.com
# vi external/example.com.zone
$TTL 86400 @ IN SOA server1.example.com. root.server1.example.com. ( 51 ; serial (d. adams) 3H ; refresh 15M ; retry 1W ; expiry 1D ) ; minimum @ NS ns1 server1 IN A 101.102.103.104 www IN CNAME server1 ftp IN CNAME server1 mail IN CNAME server1 ns1 IN CNAME server1
สร้างโซนไฟล์ external ของ example.org
# vi external/example.org.zone
$TTL 86400 @ IN SOA server1.example.org. root.server1.example.org. ( 52 ; serial (d. adams) 3H ; refresh 15M ; retry 1W ; expiry 1D ) ; minimum @ NS ns1 server1 IN A 101.102.103.104 www IN CNAME server1 ns1 IN CNAME server1 mail IN CNAME server1 ftp IN CNAME server1
เปลี่ยนเจ้าของและกลุ่ม
# chown -R bind:bind *
ทดสอบขั้นต้น
# /etc/init.d/bind9 restart
ต้องไม่มีรายงานข้อผิดพลาด
เสร็จหมดแล้ว
เนื่องจากเราแบ่งเป็นวงในและวงนอก โดยที่สามารถอัปเดตได้เฉพาะวงนอกเท่านั้น ดังนั้นในการสั่งอัปเดต ต้องระบุเซิร์ฟเวอร์เป็น localhost เท่านั้น (ถ้าระบุเซิร์ฟเวอร์เป็น 192.168.1.1 เขาจะไม่ยอมอัปเดตให้)
ทดลองอัปเดต
# nsupdate -d -v -k /etc/bind/Kallhost.+157+38278.private
Creating key... > server localhost > update add testing.example.com. 86400 IN A 101.102.103.155 > send Reply from SOA query: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22523 ;; flags: qr aa rd ra ; QUESTION: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1 ;; QUESTION SECTION: ;testing.example.com. IN SOA ;; AUTHORITY SECTION: example.com. 86400 IN SOA server1.example.com. root.server1.example.com. 57 10800 900 604800 86400 ;; TSIG PSEUDOSECTION: allhost. 0 ANY TSIG hmac-md5.sig-alg.reg.int. 1203992365 300 16 sMkuPmAy7VF1RCoGpHjrXA== 22523 NOERROR 0 Found zone name: example.com The master is: server1.example.com Sending update to 127.0.0.1#53 Outgoing update query: ;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id: 50100 ;; flags: ; ZONE: 1, PREREQ: 0, UPDATE: 1, ADDITIONAL: 1 ;; ZONE SECTION: ;example.com. IN SOA ;; UPDATE SECTION: testing.example.com. 86400 IN A 101.102.103.155 ;; TSIG PSEUDOSECTION: allhost. 0 ANY TSIG hmac-md5.sig-alg.reg.int. 1203992365 300 16 pmaYIKqjlgGU+FJvT3uZxA== 50100 NOERROR 0 Reply from update query: ;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id: 50100 ;; flags: qr ra ; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 1 ;; TSIG PSEUDOSECTION: allhost. 0 ANY TSIG hmac-md5.sig-alg.reg.int. 1203992365 300 16 9ca6E1RY9RfHacqi7uSuaQ== 50100 NOERROR 0 > quit
ทั้งหมดต้องไม่มีข้อผิดพลาด
ลองค้นค่าดู
# nslookup testing.example.com localhost
nslookup testing.example.com Server: localhost Address: 127.0.0.1#53 Name: testing.example.com Address: 101.102.103.155
เริ่ม bind ใหม่
# /etc/init.d/bind9 restart
จบส่วนของ name server ปกติ
(ส่วนนี้เป็นตอนต่อจากตอนที่แล้ว คือการทำเรื่อง dynamic dns client)
กลับไปแก้ไขไฟล์ d.router-reconnect โดยเพิ่มส่วนของการอัปเดตไอพี
# vi /usr/local/sbin/d.router-reconnect
... #-------ADDITIONAL BIND9 SCRIPT:------------------ #UPDATE LOCAL BIND9 IP=`/usr/local/sbin/d.router-getip` KEY="/etc/bind/Kallhost.+157+38278.private" HOSTNAME=`hostname` #GET DOMAIN FROM FIRST PROVIDER ONLY DIR="/etc/ddns/ddns-enabled" PROVIDER=`ls $DIR | cut -d\ -f1` . $DIR/$PROVIDER for i in $UPDATE_DOMAIN; do DOMAIN=`echo $i | cut -d: -f1` nsupdate -k $KEY << EOF server localhost update delete $HOSTNAME.$DOMAIN update add $HOSTNAME.$DOMAIN. 86400 IN A $IP send quit EOF done
จบจริง ๆ แล้ว
อ้างอิง
ท่อนนี้ไม่มีอะไรมาก เพียงแต่ต้องการรวมศูนย์ข้อมูลไว้ในไฟล์เดียว เพื่อต้องการให้ดูง่ายตรวจสอบง่ายและลดโอกาสผิดพลาดในการแก้ไขไฟล์โซนและไฟล์รีเวิร์สโซน
จึงสร้างเป็นสคริปต์เล็ก ๆ ขึ้นมาเพื่อใช้อ่านค่าข้อมูลที่เราสร้างเอาไว้ แล้วผลิตไฟล์โซนต่าง ๆ ออกมาให้ครบตามที่เราตั้งไว้
สคริปต์ตั้งชื่อว่า d.bind-genzone เอาไว้ที่ /usr/local/sbin มีดังนี้
# vi /usr/local/sbin/d.bind-genzone
#!/bin/bash # GENERATE ZONE FILE [AND REVERSE ZONE FILE IF ADD OPTION -b] # READ DATA FROM DATAFILE IN FORMAT # zone:ZONENAME:HOSTNAME:SERIAL:IP_ABC # ns:NAMESERVER1 NAMESERVER2 # mx:MAILSERVER1,RR1 MAILSERVER2,RR2 # IP_D:NAME CNAME1 CNAME2 ... # ... # EXAMPLE: # zone:example.com:server1:43:192.168.1 # ns:ns1 ns2 # mx:mail,10 mail2,20 # 1:server1 ns1 mail www ftp # 2:ns2 # 101:work1 # 102:work2 # ... #GLOBAL VAR TTL=86400 BINDUSER="bind" BINDGROUP="bind" #FUNTION function usage { cat << EOF Script to generate zone file and reverse zone file USAGE: $0 [-b] DATAFILE OPTIONS: -b = both, generate both forward and reverse zone file, only generate forward zone file, if omitted ARGUMENT: DATAFILE : datafile in format:- zone:ZONENAME:HOSTNAME:SERIAL:IP_ABC [ns:NAMESERVER1 NAMESERVER2] [mx:MAILSERVER1,RR1 MAILSERVER2,RR2] IP_D:NAME CNAME1 CNAME2 ... ... EXAMPLE OF DATAFILE: example1: zone:example.com:server1:43:192.168.1 ns:ns1 ns2 mx:mail,10 mail2,20 1:server1 ns1 mail www ftp 2:ns2 101:work1 102:work2 192.168.5.1:router ... example2: zone:example.org:ns2:44:192.168.1 2:ns2 ns:ns1 ns2 EOF } function gen_header { Z=$1 H=$2 S=$3 cat << EOF \$TTL $TTL @ IN SOA $H.$Z. root.$H.$Z. ( $S ; serial (d. adams) 3H ; refresh 15M ; retry 1W ; expiry 1D ) ; minimum EOF } function gen_ns { H=$1 cat << EOF @ NS $H EOF } function gen_mx { H=$1 R=$2 cat << EOF @ MX $R $H EOF } function gen_name { N=$1 I=$2 cat << EOF $N IN A $I EOF } function gen_ptr { I=$1 N=$2 cat << EOF $I IN PTR $N.$ZONENAME. EOF } function gen_cname { C=$1 N=$2 cat << EOF $C IN CNAME $N EOF } function get_ip { I1="$1." #input ip I2="$2." #$IP_ABC A=$(echo "$I1" | cut -d. -f1) B=$(echo "$I1" | cut -d. -f2) C=$(echo "$I1" | cut -d. -f3) D=$(echo "$I1" | cut -d. -f4) W=$(echo "$I2" | cut -d. -f1) X=$(echo "$I2" | cut -d. -f2) Y=$(echo "$I2" | cut -d. -f3) Z=$(echo "$I2" | cut -d. -f4) if [ ! "$B" ]; then echo "$W.$X.$Y.$A"; return elif [ ! "$C" ]; then echo "$W.$X.$A.$B"; return elif [ ! "$D" ]; then echo "$W.$A.$B.$C"; return else echo "$I1" fi } #BEGIN MAIN DATAFILE="" WITHREVERSE="" while getopts "b" ARGS; do case "$ARGS" in b) WITHREVERSE="true"; shift $(($OPTIND - 1)) ;; esac done DATAFILE=$1 shift while getopts "b" ARGS; do case "$ARGS" in b) WITHREVERSE="true"; shift $(($OPTIND - 1)) ;; esac done if [ ! "$DATAFILE" ] || [ ! -f "$DATAFILE" ]; then usage exit 1 fi ZONENAME="" HOSTNAME="" SERIAL="" IP_ABC="" ZONEFILE="" REVERSEFILE="" cat $DATAFILE | grep -v "#" | while read DATA; do #ZONE HEADER if [ "${DATA:0:5}" == "zone:" ]; then ZONENAME=$(echo $DATA | cut -d: -f2) HOSTNAME=$(echo $DATA | cut -d: -f3) SERIAL=$(echo $DATA | cut -d: -f4) IP_ABC=$(echo $DATA | cut -d: -f5) #FORWARD ZONE FILE ZONEFILE="$ZONENAME.zone" if [ -f "$ZONEFILE" ]; then mv "$ZONEFILE" "$ZONEFILE.bak" fi gen_header $ZONENAME $HOSTNAME $SERIAL > $ZONEFILE chown $BINDUSER:$BINDGROUP $ZONEFILE if [ "$WITHREVERSE" ]; then #REVERSE ZONE FILE REVERSEFILE="$ZONENAME.reverse" if [ -f "$REVERSEFILE" ]; then mv "$REVERSEFILE" "$REVERSEFILE.bak" fi gen_header $ZONENAME $HOSTNAME $SERIAL > $REVERSEFILE chown $BINDUSER:$BINDGROUP $REVERSEFILE echo "Generate $ZONEFILE and $REVERSEFILE ..." else echo "Generate $ZONEFILE ..." fi #NS elif [ "${DATA:0:3}" == "ns:" ] && [ "$ZONENAME" ]; then for i in $(echo $DATA | cut -d: -f2); do gen_ns $i >> $ZONEFILE if [ "$WITHREVERSE" ]; then gen_ns $i >> $REVERSEFILE fi done #MX elif [ "${DATA:0:3}" == "mx:" ] && [ "$ZONENAME" ]; then for i in $(echo $DATA | cut -d: -f2); do $MAILNAME=$(echo $i | cut -d, -f1) $RRPRIORITY=$(echo $i | cut -d, -f2) gen_mx $MAILNAME $RRPRIORITY >> $ $ZONEFILE done #DATA elif [ "$DATA" ]; then RAW_IP=$(echo $DATA | cut -d: -f1) FULL_IP=`get_ip $RAW_IP $IP_ABC` NAMES=$(echo $DATA | cut -d: -f2) FIRSTNAME=$(echo $NAMES | cut -d\ -f1) gen_name $FIRSTNAME $FULL_IP >> $ZONEFILE if [ "$WITHREVERSE" ]; then gen_ptr $RAW_IP $FIRSTNAME >> $REVERSEFILE fi for i in $NAMES; do if [ "$i" != "$FIRSTNAME" ]; then gen_cname $i $FIRSTNAME >> $ZONEFILE fi done fi done
แก้ให้รันได้เฉพาะ root
# chmod 700 /usr/local/sbin/d.bind-genzone
หลังจากนั้นสร้างไฟล์ข้อมูลของ internal บรรจุข้อมูลของ example.com เอาไว้ใน /etc/bind/internal ตั้งชื่อว่า both-data เพราะจะทำค้นย้อนด้วย ดังนี้
# cd /etc/bind/internal
# vi both-data
# DATAFILE FORMAT # zone:ZONENAME:HOSTNAME:SERIAL:IP_ABC # ns:NAMESERVER1 NAMESERVER2 # mx:MAILSERVER1,RR1 MAILSERVER2,RR2 # IP_D:NAME CNAME1 CNAME2 ... # ... # EXAMPLE: # zone:example.com:server1:43:192.168.1 # ns:ns1 ns2 # mx:mail,10 mail2,20 # 1:server1 ns1 mail www ftp # 2:ns2 # 101:work1 # 102:work2 # ... #EXAMPLE.COM zone:example.com:server1:41:192.168.1 ns:ns1 1:server1 www ftp mail ns1 101:work1 102:work2 103:work3
สั่งผลิตไฟล์
# d.bind-genzone -b both-data
จะได้ไฟล์โซนและไฟล์รีเวิร์สโซน ของ example.com ที่เป็นของเครือข่ายภายในออกมา
(ถ้ามีไฟล์เก่า จะสำเนาไว้ในชื่อเดิม ต่อท้ายนามสกุลด้วย .bak)
สร้างไฟล์ข้อมูลของ internal บรรจุข้อมูลของ example.org ตั้งชื่อว่า forward-data เพราะจะทำค้นชื่ออย่างเดียว ไม่ทำค้นย้อน ดังนี้
# vi forward-data
# DATAFILE FORMAT # zone:ZONENAME:HOSTNAME:SERIAL:IP_ABC # ns:NAMESERVER1 NAMESERVER2 # mx:MAILSERVER1,RR1 MAILSERVER2,RR2 # IP_D:NAME CNAME1 CNAME2 ... # ... # EXAMPLE: # zone:example.com:server1:43:192.168.1 # ns:ns1 ns2 # mx:mail,10 mail2,20 # 1:server1 ns1 mail www ftp # 2:ns2 # 101:work1 # 102:work2 # ... #EXAMPLE.ORG zone:example.org:server1:42:192.168.1 ns:ns1 1:server1 www ns1 mail ftp
สั่งผลิตไฟล์ โดยไม่ต้องใส่ออปชั่น -b
# d.bind-genzone forward-data
จะได้ไฟล์โซน ของ example.org ที่เป็นของเครือข่ายภายใน
และสร้างไฟล์ข้อมูลของ external บรรจุข้อมูลของทุกโซน เอาไว้ใน /etc/bind/external ตั้งชื่อว่า forward-data (จะทำแค่ forward อย่างเดียว) ดังนี้
# cd /etc/bind/external
# vi forward-data
# DATAFILE FORMAT # zone:ZONENAME:HOSTNAME:SERIAL:IP_ABC # ns:NAMESERVER1 NAMESERVER2 # mx:MAILSERVER1,RR1 MAILSERVER2,RR2 # IP_D:NAME CNAME1 CNAME2 ... # ... # EXAMPLE: # zone:example.com:server1:43:192.168.1 # ns:ns1 ns2 # mx:mail,10 mail2,20 # 1:server1 ns1 mail www ftp # 2:ns2 # 101:work1 # 102:work2 # ... #EXAMPLE.COM zone:example.com:server1:51:101.102.103 ns:ns1 104:server1 www ftp mail ns1 #EXAMPLE.ORG zone:example.org:server1:52:101.102.103 ns:ns1 104:server1 www ns1 mail ftp
สั่งผลิตไฟล์ โดยไม่ต้องใส่ออปชั่น -b
# d.bind-genzone forward-data
จะได้เฉพาะไฟล์โซนของ example.com และ example.org ที่เป็นของเครือข่ายภายนอก
เสร็จแล้ว สั่งเริ่ม bind9 ใหม่ได้เลย
# /etc/init.d/bind9 restart
ปรับสคริปต์นิดหน่อย
# vi /usr/local/sbin/d.bind-genzone
#!/bin/bash # GENERATE ZONE FILE [AND REVERSE ZONE FILE IF ADD OPTION -b] # READ DATA FROM DATAFILE IN FORMAT # zone:ZONENAME:IP_D:CNAME1 CNAME2 ...:SERIAL:IP_ABC # ns:NAMESERVER1 NAMESERVER2 # mx:MAILSERVER1,RR1 MAILSERVER2,RR2 # IP_D:NAME CNAME1 CNAME2 ... # ... # EXAMPLE: # zone:example.com:1:server1 ns1 mail www ftp:43:192.168.1 # ns:ns1 ns2 # mx:mail,10 mail2,20 # 2:ns2 # 101:work1 # 102:work2 # ... #GLOBAL VAR TTL=86400 BINDUSER="bind" BINDGROUP="bind" #FUNTION function usage { cat << EOF Script to generate zone file and reverse zone file USAGE: $0 [-b] DATAFILE OPTIONS: -b = both, generate both forward and reverse zone file, only generate forward zone file, if omitted ARGUMENT: DATAFILE : datafile in format:- zone:ZONENAME:IP_D:CNAME1 CNAME2 ...:SERIAL:IP_ABC [ns:NAMESERVER1 NAMESERVER2] [mx:MAILSERVER1,RR1 MAILSERVER2,RR2] IP_D:NAME CNAME1 CNAME2 ... ... EXAMPLE OF DATAFILE: example1: zone:example.com:1:server1 ns1 mail www ftp:43:192.168.1 ns:ns1 ns2 mx:mail,10 mail2,20 2:ns2 101:work1 102:work2 192.168.5.1:router ... example2: zone:ns2.example.org:2::44:192.168.1 ns:ns1 ns2 EOF } function gen_header { Z=$1 S=$2 cat << EOF \$TTL $TTL @ IN SOA $Z. root.$Z. ( $S ; serial (d. adams) 3H ; refresh 15M ; retry 1W ; expiry 1D ) ; minimum EOF } function gen_ns { H=$1 cat << EOF @ NS $H EOF } function gen_mx { H=$1 R=$2 cat << EOF @ MX $R $H EOF } function gen_name { N=$1 I=$2 cat << EOF $N IN A $I EOF } function gen_ptr { I=$1 N=$2 cat << EOF $I IN PTR $N.$ZONENAME. EOF } function gen_cname { C=$1 N=$2 cat << EOF $C IN CNAME $N. EOF } function get_ip { I1="$1." #input ip I2="$2." #$IP_ABC A=$(echo "$I1" | cut -d. -f1) B=$(echo "$I1" | cut -d. -f2) C=$(echo "$I1" | cut -d. -f3) D=$(echo "$I1" | cut -d. -f4) W=$(echo "$I2" | cut -d. -f1) X=$(echo "$I2" | cut -d. -f2) Y=$(echo "$I2" | cut -d. -f3) Z=$(echo "$I2" | cut -d. -f4) if [ ! "$B" ]; then echo "$W.$X.$Y.$A"; return elif [ ! "$C" ]; then echo "$W.$X.$A.$B"; return elif [ ! "$D" ]; then echo "$W.$A.$B.$C"; return else echo "$I1" fi } #BEGIN MAIN DATAFILE="" WITHREVERSE="" while getopts "b" ARGS; do case "$ARGS" in b) WITHREVERSE="true"; shift $(($OPTIND - 1)) ;; esac done DATAFILE=$1 shift while getopts "b" ARGS; do case "$ARGS" in b) WITHREVERSE="true"; shift $(($OPTIND - 1)) ;; esac done if [ ! "$DATAFILE" ] || [ ! -f "$DATAFILE" ]; then usage exit 1 fi ZONENAME="" SERIAL="" IP_ABC="" ZONEFILE="" REVERSEFILE="" cat $DATAFILE | grep -v "#" | while read DATA; do #ZONE HEADER if [ "${DATA:0:5}" == "zone:" ]; then ZONENAME=$(echo $DATA | cut -d: -f2) IP_D=$(echo $DATA | cut -d: -f3) CNAMES=$(echo $DATA | cut -d: -f4) SERIAL=$(echo $DATA | cut -d: -f5) IP_ABC=$(echo $DATA | cut -d: -f6) #FORWARD ZONE FILE ZONEFILE="$ZONENAME.zone" if [ -f "$ZONEFILE" ]; then mv "$ZONEFILE" "$ZONEFILE.bak" fi FULL_IP=`get_ip $IP_D $IP_ABC` gen_header $ZONENAME $SERIAL > $ZONEFILE chown $BINDUSER:$BINDGROUP $ZONEFILE gen_name "@" $FULL_IP >> $ZONEFILE for i in $CNAMES; do gen_cname $i $ZONENAME >> $ZONEFILE done if [ "$WITHREVERSE" ]; then #REVERSE ZONE FILE REVERSEFILE="$ZONENAME.reverse" if [ -f "$REVERSEFILE" ]; then mv "$REVERSEFILE" "$REVERSEFILE.bak" fi gen_header $ZONENAME $SERIAL > $REVERSEFILE chown $BINDUSER:$BINDGROUP $REVERSEFILE gen_ptr $IP_D "" >> $REVERSEFILE echo "Generate $ZONEFILE and $REVERSEFILE ..." else echo "Generate $ZONEFILE ..." fi #NS elif [ "${DATA:0:3}" == "ns:" ] && [ "$ZONENAME" ]; then for i in $(echo $DATA | cut -d: -f2); do gen_ns $i >> $ZONEFILE if [ "$WITHREVERSE" ]; then gen_ns $i >> $REVERSEFILE fi done #MX elif [ "${DATA:0:3}" == "mx:" ] && [ "$ZONENAME" ]; then for i in $(echo $DATA | cut -d: -f2); do $MAILNAME=$(echo $i | cut -d, -f1) $RRPRIORITY=$(echo $i | cut -d, -f2) gen_mx $MAILNAME $RRPRIORITY >> $ $ZONEFILE done #DATA elif [ "$DATA" ]; then RAW_IP=$(echo $DATA | cut -d: -f1) FULL_IP=`get_ip $RAW_IP $IP_ABC` NAMES=$(echo $DATA | cut -d: -f2) FIRSTNAME=$(echo $NAMES | cut -d\ -f1) echo "gen_name $FIRSTNAME $FULL_IP $ZONEFILE" gen_name $FIRSTNAME $FULL_IP >> $ZONEFILE if [ "$WITHREVERSE" ]; then gen_ptr $RAW_IP $FIRSTNAME >> $REVERSEFILE fi for i in $NAMES; do if [ "$i" != "$FIRSTNAME" ]; then gen_cname $i $FIRSTNAME >> $ZONEFILE fi done fi done
หัวข้อนี้สบายมาก เพราะไม่รู้เรื่องเลย ลอกมาอย่างเดียว จาก The Perfect Setup - Debian Etch (Debian 4.0) - Page 5
ของเราแปลงนิดเดียว ตรงที่จะให้กูเกิลเป็นตัวส่งเมลแทน (เรารับเหมือนเดิม แต่ให้กูเกิลส่งให้)
ทีแรกกะว่าจะใช้ exim4 แต่หาเอกสารติดตั้งที่สมบูรณ์ยาก และความนิยมของ postfix มีมากกว่า เลยตัดสินใจใช้อันนี้ครับ
ติดตั้ง
# aptitude install postfix libsasl2 sasl2-bin libsasl2-modules libdb3-util procmail
# dpkg-reconfigure postfix
General type of configuration? <<<--- Internet Site Where should mail for root go <<<--- [ENTER] Mail name? <<<--- mail.example.com Other destinations to accept mail for? (blank for none) <<<--- mail.example.com, server1.example.com, localhost.example.com, localhost, mail.example.org Force synchronous updates on mail queue? <<<--- No Local networks? <<<--- 127.0.0.0/8, 192.168.0.0/16 Use procmail for local delivery? <<<--- Yes Mailbox size limit <<<--- 0 Local address extension character? <<<--- + Internet protocols to use? <<<--- all
ปรับ
# postconf -e 'relayhost = smtp.gmail.com'
# postconf -e 'smtp_sasl_auth_enable = yes'
# postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
# postconf -e 'smtp_sasl_security_options ='
# postconf -e 'smtpd_sasl_security_options = noanonymous'
# postconf -e 'smtpd_sasl_local_domain ='
# postconf -e 'smtpd_sasl_auth_enable = yes'
# postconf -e 'broken_sasl_auth_clients = yes'
# postconf -e 'smtpd_recipient_restrictions = permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination'
# postconf -e 'inet_interfaces = all'
# echo 'pwcheck_method: saslauthd' >> /etc/postfix/sasl/smtpd.conf
# echo 'mech_list: plain login' >> /etc/postfix/sasl/smtpd.conf
ทำเรื่อง ssl
# mkdir /etc/postfix/ssl
# cd /etc/postfix/ssl/
# openssl genrsa -des3 -rand /etc/hosts -out smtpd.key 1024
2597 semi-random bytes loaded Generating RSA private key, 1024 bit long modulus ...................++++++ ..........................................++++++ e is 65537 (0x10001) Enter pass phrase for smtpd.key: <<<--- PASSWORD Verifying - Enter pass phrase for smtpd.key: <<<--- SAME-PASSWORD
สร้างกุญแจ
# chmod 600 smtpd.key
# openssl req -new -key smtpd.key -out smtpd.csr
Enter pass phrase for smtpd.key: <<<--- SAME-PASSWORD 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) []: <<<--- Bangrak Organization Name (eg, company) [Internet Widgits Pty Ltd]: <<<--- Example Company Organizational Unit Name (eg, section) []: <<<--- Computer Department Common Name (eg, YOUR name) []: <<<--- MYNAME-MYSURNAME Email Address []: <<<--- ***GMAIL-USER@gmail.com*** Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: <<<--- [ENTER] An optional company name []: <<<--- [ENTER]
ลงทะเบียน
# openssl x509 -req -days 3650 -in smtpd.csr -signkey smtpd.key -out smtpd.crt
Signature ok subject=/C=TH/ST=Bangkok/L=Bangrak/O=Example Company/OU=Computer Department/CN=myname-mysurname/emailAddress=gmail.user@gmail.com Getting Private key Enter pass phrase for smtpd.key: <<<--- SAME-PASSWORD
# openssl rsa -in smtpd.key -out smtpd.key.unencrypted
Enter pass phrase for smtpd.key: <<<--- SAME-PASSWORD writing RSA key
# mv -f smtpd.key.unencrypted smtpd.key
# openssl req -new -x509 -extensions v3_ca -keyout cakey.pem -out cacert.pem -days 3650
650 Generating a 1024 bit RSA private key ..................++++++ ....................++++++ writing new private key to 'cakey.pem' Enter PEM pass phrase: <<<--- SAME-PASSWORD Verifying - Enter PEM pass phrase: <<<--- SAME-PASSWORD ----- 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) []: <<<--- Bangrak Organization Name (eg, company) [Internet Widgits Pty Ltd]: <<<--- Example Company Organizational Unit Name (eg, section) []: <<<--- Computer Department Common Name (eg, YOUR name) []: <<<--- MYNAME-MYSURNAME Email Address []: <<<--- ***GMAIL-USER@gmail.com***
ปรับ postfix เรื่องกุญแจ
# postconf -e 'smtpd_tls_auth_only = no'
# postconf -e 'smtp_use_tls = yes'
# postconf -e 'smtpd_use_tls = yes'
# postconf -e 'smtp_tls_note_starttls_offer = yes'
# postconf -e 'smtpd_tls_key_file = /etc/postfix/ssl/smtpd.key'
# postconf -e 'smtpd_tls_cert_file = /etc/postfix/ssl/smtpd.crt'
# postconf -e 'smtpd_tls_CAfile = /etc/postfix/ssl/cacert.pem'
# postconf -e 'smtpd_tls_loglevel = 1'
# postconf -e 'smtpd_tls_received_header = yes'
# postconf -e 'smtpd_tls_session_cache_timeout = 3600s'
# postconf -e 'tls_random_source = dev:/dev/urandom'
# postconf -e 'myhostname = mail.example.com'
ไฟล์รหัสผ่าน gmail
# echo "smtp.gmail.com GMAIL-USER@gmail.com:GMAIL-PASSWORD" >> /etc/postfix/sasl_passwd
# chown root:root /etc/postfix/sasl_passwd
# chmod 600 /etc/postfix/sasl_passwd
# postmap /etc/postfix/sasl_passwd
เรื่อง sasl
# mkdir -p /var/spool/postfix/var/run/saslauthd
ปรับให้มาใช้ใน chroot ของ postfix
# vi /etc/default/saslauthd
... # Should saslauthd run automatically on startup? (default: no) #START=no START=yes ... # Other options (default: -c) # See the saslauthd man page for information about these options. # # Example for postfix users: "-c -m /var/spool/postfix/var/run/saslauthd" # Note: See /usr/share/doc/sasl2-bin/README.Debian #OPTIONS="-c" OPTIONS="-c -m /var/spool/postfix/var/run/saslauthd -r" ...
เริ่ม sasl ใหม่
# /etc/init.d/saslauthd start
ทดสอบ sasl
# telnet localhost 25
Trying 127.0.0.1... Connected to localhost.localdomain. Escape character is '^]'. 220 server1.example.com ESMTP Postfix (Debian/GNU) ehlo localhost 250-server1.example.com 250-PIPELINING 250-SIZE 10240000 250-VRFY 250-ETRN 250-STARTTLS 250-AUTH PLAIN LOGIN 250-AUTH=PLAIN LOGIN 250-ENHANCEDSTATUSCODES 250-8BITMIME 250 DSN quit 221 2.0.0 Bye Connection closed by foreign host.
ติดตั้ง courier เพื่อใช้งาน pop3 และ imap
# aptitude install courier-authdaemon courier-base courier-imap courier-imap-ssl courier-pop courier-pop-ssl courier-ssl gamin libgamin0 libglib2.0-0
Create directories for web-based administration ? <<<--- No SSL certificate required <<<--- Ok
ทำเรื่อง Maildir เป็นเรื่องสุดท้ายเสมอ
# postconf -e 'home_mailbox = Maildir/'
# postconf -e 'mailbox_command ='
# /etc/init.d/postfix restart
เสร็จเรื่องติดตั้ง
สมัครเข้าใช้บริการ gmail ที่ mail.google.com
ปรับให้สามารถส่งอีเมลจากเครื่องเราได้
การตั้งค่า -> บัญชี -> ส่งอีเมลเป็นแบบ เพิ่มที่อยู่อีเมลอื่นอีก
ทำตามขั้นตอน แล้วนำตัวเลขยืนยันมากรอกในช่อง เป็นอันเสร็จ
อ้างอิง
เตรียมสร้างไดเรกทอรี่ข้อมูลให้ apache2 และ mysql
อันนี้ลักไก่ ใช้คำสั่งเดียว แล้วสั่งหยุดบริการเลย
# aptitude install phpmyadmin mysql-server-5.0 php-apc
# /etc/init.d/apache2 stop
# /etc/init.d/mysql stop
ตัวคอนฟิกของ apache2 mysql และ php5 จะเอามาเก็บไว้ที่ /sys1/sysb
# mv /etc/apache2/ /sys1/sysb/etc/
# ln -sf /sys1/sysb/etc/apache2/ /etc
# mv /etc/mysql/ /sys1/sysb/etc/
# ln -sf /sys1/sysb/etc/mysql/ /etc
# mv /etc/php5 /sys1/sysb/etc/
# ln -sf /sys1/sysb/etc/php5 /etc
ตัวข้อมูล apache2 เอามาเก็บที่ /sys1/sysb
# mv /var/www /sys1/sysb/var/
# ln -sf /sys1/sysb/var/www /var
ตัวข้อมูลของ mysql ไม่สำรองแบบคัดลอกปกติ จึงเอามาเก็บไว้ที่ /sys1/syst แทน
# mkdir -p /sys1/syst/var/lib
# mv /var/lib/mysql /sys1/syst/var/lib
# mv /var/lib/mysql-cluster /sys1/syst/var/lib
# ln -sf /sys1/syst/var/lib/mysql /var/lib
# ln -sf /sys1/syst/var/lib/mysql-cluster /var/lib
สั่งให้บริการ
# /etc/init.d/mysql start
# /etc/init.d/apache2 start
เพิ่มผู้ใช้ admin ในระบบ
# adduser admin
ตั้งค่ารหัสผ่านของ root และผู้ใช้งาน admin
# mysql
mysql> SET PASSWORD FOR 'root'@'localhost' = PASSWORD('********');
mysql> GRANT ALL PRIVILEGES ON *.* TO 'admin'@'localhost' IDENTIFIED BY '*********';
mysql> \q
ต้องติดตั้งไปทีละตัว เพราะแพกเกจ phppgadmin ผูกกับ postgresql-7.4 ซึ่งเราไม่ต้องการ
เอา postgresql-8.1 มาก่อน แล้วหยุดการทำงาน
# aptitude install postgresql-8.1 php5-pgsql
# /etc/init.d/postgresql-8.1 stop
ย้ายคอนฟิกไป /sys1/sysb และย้ายข้อมูลไป /sys1/syst
# mv /etc/postgresql* /sys1/sysb/etc
# ln -sf /sys1/sysb/etc/postgresql* /etc
# mv /var/lib/postgresql/ /sys1/syst/var/lib
# ln -sf /sys1/syst/var/lib/postgresql/ /var/lib
ปรับให้เรียงภาษาไทยโดยการ initdb ใหม่ และปรับการอนุญาตใช้งาน
# dpkg-reconfigure locales
<--- เลือกให้มี th_TH.UTF-8
# su postgres
$ /usr/lib/postgresql/8.1/bin/initdb /var/lib/postgresql/8.1/temp --locale=th_TH.UTF-8
$ cd /var/lib/postgresql/8.1
$ cp -xa temp/* main
$ rm -rf temp
ปรับตั้งสิทธิ์
$ vi /etc/postgresql/8.1/main/pg_hba.conf
... #local all all ident sameuser local all all md5 host all all 192.168.0.0/16 md5 ...
สั่งเริ่ม postgresql ใหม่ และสร้างผู้ใช้งาน phppgadmin ชื่อ admin ให้เป็น superuser
$ exit
# /etc/init.d/postgresql-8.1 start
# su -l postgres -c "createuser -s -P admin"
ส่วนของ phppgadmin
ก่อนอื่นติดตั้งแพกเกจที่ขึ้นกับ phppgadmin ก่อน เหลือแค่ตัวเดียวคือ wwwconfig-common
# aptitude install wwwconfig-common
และไปดาวน์โหลด phppgadmin รุ่นของ testing มาติดตั้งเอง
# wget http://ftp.debianclub.org/debian/pool/main/p/phppgadmin/phppgadmin_4.1.3-0.1_all.deb
# dpkg -i phppgadmin_4.1.3-0.1_all.deb
ปรับตั้ง
# dpkg-reconfigure phppgadmin
Web server to reconfigure automatically: <<<--- Apache2
ปรับให้เรียกดูได้จากเฉพาะเครือข่ายภายใน
# vi /etc/phppgadmin/apache.conf
... order deny,allow deny from all allow from 127.0.0.0/255.0.0.0 allow from 192.168.0.0/16 # allow from all ...
เสร็จแล้ว
ตั้งให้รากของเว็บ www.example.com อยู่ที /var/www/example.com
และของ www.example.org อยู่ที่ /var/www/example.org ตามลำดับ
# mkdir -p /var/www/{example.com,example.org}
กำหนดให้ www.example.com เป็นเว็บปริยาย
ต้องไปแก้ไขไฟล์ default ของ sites-enable
# vi /etc/apache2/sites-enable/000-default
NameVirtualHost * <VirtualHost *> #ServerAdmin webmaster@localhost ServerAdmin webmaster@example.com ServerName www.example.com ServerAlias example.com DocumentRoot /var/www/example.com/ <Directory /> Options FollowSymLinks #AllowOverride None AllowOverride All </Directory> <Directory /var/www/example.com/> Options Indexes FollowSymLinks MultiViews #AllowOverride None AllowOverride All Order allow,deny allow from all # This directive allows us to have apache2's default start page # in /apache2-default/, but still have / go to the right place #RedirectMatch ^/$ /apache2-default/ </Directory> ...
และ www.example.org ต้องปรับตั้งใหม่ โดยเอาตัวอย่างจาก default
# cd /etc/apache2/sites-available
# cp default example.org
# vi example.org
NameVirtualHost * <VirtualHost *> #ServerAdmin webmaster@localhost ServerAdmin webmaster@example.org ServerName www.example.org ServerAlias example.org DocumentRoot /var/www/example.org/ <Directory /> Options FollowSymLinks #AllowOverride None AllowOverride All </Directory> <Directory /var/www/example.org/> Options Indexes FollowSymLinks MultiViews ...
สั่งให้เปิดใช้งาน example.org
# a2ensite example.org
มอดูลที่น่าจะเปิดใช้งาน
# a2enmod rewrite
# a2enmod proxy
# a2enmod userdir
ให้รองรับการเข้ารหัสภาษาไทยได้หลายแบบ
# vi /etc/apache2/conf.d/charset
#AddDefaultCharset UTF-8 AddCharset TIS-620 .tis-620 .th AddCharset CP874 .cp874
เพิ่มเติมพิเศษ
สำหรับไพธอน ถ้าจะใช้งาน django ต้องติดตั้งมอดูลเพิ่ม
# aptitude install libapache2-mod-python python-psycopg python-mysqldb
# a2enmod mod_python
อีกนิดนึง ถ้าจะทำให้การใช้งานแบบ (เขียนโค๊ดเอง) ไม่ขึ้นต่อฐานข้อมูล ควรติดตั้ง adodb เพิ่ม
# aptitude install python-adodb python-psyco libphp-adodb
เริ่ม apache2 ใหม่เพื่อให้มอดูลใหม่ทำงาน
# /etc/init.d/apache2 restart
จบแล้ว
ทำสคริปต์เล็ก ๆ สำหรับสำรองข้อมูลทุกวัน (ฮาร์ดดิสก์ควรมีเนื้อที่พอสำหรับการนี้ด้วยนะครับ) ใส่ไว้ใน cron
เมื่อสำรองเสร็จแล้ว จะเก็บในชื่อ all.sql อยู่ใน /sys1/sysb/backupdb โดยแยกไปตามชื่อระบบฐานข้อมูล และให้เจ้าของเป็น admin ดูได้คนเดียว
ไฟล์เก่าของเมื่อวานจะถูก gzip เก็บไว้ในชื่อ all.sql.tar.gz (หากต้องการเก็บเกิน 2 วัน ฝากเขียนต่อเอาเองครับ)
ตั้งชื่อว่า d.cron-daily ใส่ไว้ใน /usr/local/sbin
# vi /usr/local/sbin/d.cron-daily
#!/bin/bash #BACKUP DB ROOT="/sys1/sysb/backupdb" BACKUPFILE="all.sql" function chkfile() { D=$1 U=$2 if [ ! -d "$ROOT/$D" ]; then mkdir -p "$ROOT/$D" chmod 770 "$ROOT/$D" chown $DB:$DB "$ROOT/$D" fi #GZIP OLD FILE (ONLY ONE LAST) if [ -f "$ROOT/$D/$BACKUPFILE" ]; then rm $ROOT/$D/$BACKUPFILE.tar.gz su -l $U -c "tar cfz $ROOT/$D/$BACKUPFILE.tar.gz $ROOT/$D/$BACKUPFILE" rm $ROOT/$D/$BACKUPFILE chmod 700 "$ROOT/$D/$BACKUPFILE.tar.gz" fi } # MYSQL if [ `which mysqldump` ]; then USER="admin" PASSWORD="MYSQL-ADMIN-PASSWORD" DB="mysql" chkfile $DB $USER su -l $USER -c "mysqldump -A -u$USER -p$PASSWORD > $ROOT/$DB/$BACKUPFILE" chmod 700 "$ROOT/$DB/$BACKUPFILE" fi # POSTGRESQL if [ `which pg_dumpall` ]; then USER="admin" DB="postgres" chkfile $DB $USER su -l postgres -c "pg_dumpall > $ROOT/$DB/$BACKUPFILE" chown $USER "$ROOT/$DB/$BACKUPFILE" chmod 700 "$ROOT/$DB/$BACKUPFILE" fi
ตั้งให้รันได้
# chmod 700 /usr/local/sbin/d.cron-daily
ทำให้รันตอนเที่ยงคืนทุกวัน
# crontab -e
... #DAILY CRON TASK: BACKUP DB 20 0 * * * /usr/local/sbin/d.cron-daily ...
เสร็จ
ที่ทำเป็น คือใช้ proftpd (ตัวอื่นทำ VirtualHost ไม่เป็น หรืออาจทำยาก)
เนื่องจากเรามี 2 โดเมน คือ example.com และ example.org เราต้องการทำ ftp ทั้งสองโดเมน จึงต้องทำเรื่อง VirtualHost
แต่เนื่องจากระบบ VirtualHost ใน proftpd ไม่เหมือนใน apache2 เสียทีเดียว เนื่องจากเขาไม่ถือชื่อโฮสต์เป็นสำคัญ แต่จะถือไอพีและพอร์ตเป็นตัวจำแนกแทน ดังนั้นถ้าเราต้องการแยกไดเรกทอรี่ระหว่าง example.com และ example.org เราจะต้องใช้พอร์ตเป็นตัวแยกแทน
สมมุติฐานมีดังนี้คือ
เตรียมผู้ใช้และกลุ่ม
เพิ่มกลุ่ม ftpusers สำหรับพอร์ตกลาง และ comgroup กับ orggroup สำหรับพอร์ตพิเศษ
# groupadd ftpusers
# groupadd comgroup
# groupadd orggroup
แก้ไขผู้ใช้ ftpuser1 ให้เข้ามาเป็นสมาชิกของ comgroup และ ftpuser2 ให้เข้ามาเป็นสมาชิกของ orggroup
# useradd -g ftpusers -G comgroup -m -d /home/ftpuser1 ftpuser1
# useradd -g ftpusers -G orggroup -m -d /home/ftpuser2 ftpuser2
# passwd ftpuser1
# passwd ftpuser2
ป้อนรหัสผ่านตามปกติ
เตรียมไดเรกทอรี่
# mkdir -p /sys1/sysb/var/ftp/{pub,example.com,example.org}
# ln -sf /sys1/sysb/var/ftp /var
# chown ftpuser1:comgroup /var/ftp/example.com
# chown ftpuser2:orggroup /var/ftp/example.org
# chown ftp:ftpusers /var/ftp/pub
# chmod 775 /var/ftp/*
ติดตั้ง
# aptitude install proftpd
Run proftpd from inetd or standalone? <<<--- standalone
แก้ไขเรื่อง IPv6 ไม่ให้แสดงรายงานความผิดพลาด
และแก้ไขงานของ Anonymous โดยเอาจากตัวอย่างในไฟล์
และเพิ่มให้อ่านไฟล์คอนฟิกของ example.com และ example.org
# vi /etc/proftpd/proftpd.conf
... #UseIPv6 on UseIPv6 off ... # A basic anonymous configuration, no upload directories. #<Anonymous ~ftp> <Anonymous /var/ftp/pub> User ftp Group nogroup # We want clients to be able to login with "anonymous" as well as "ftp" UserAlias anonymous ftp # Cosmetic changes, all files belongs to ftp user DirFakeUser on ftp DirFakeGroup on ftp RequireValidShell off # Limit the maximum number of anonymous logins MaxClients 10 # We want 'welcome.msg' displayed at login, and '.message' displayed # in each newly chdired directory. DisplayLogin welcome.msg DisplayFirstChdir .message # Limit WRITE everywhere in the anonymous chroot <Directory *> <Limit WRITE> DenyAll </Limit> </Directory> # Uncomment this if you're brave. # <Directory incoming> # # Umask 022 is a good standard umask to prevent new files and dirs # # (second parm) from being group and world writable. # Umask 022 022 # <Limit READ WRITE> # DenyAll # </Limit> # <Limit STOR> # AllowAll # </Limit> # </Directory> </Anonymous> Include /etc/proftpd/example.com.conf Include /etc/proftpd/example.org.conf
สร้างไฟล์ example.com.conf ให้มาใช้พอร์ต 10021
# vi /etc/proftpd/example.com.conf
<VirtualHost ftp.example.com> ServerName "Example.com's FTP site" DefaultRoot ~ AllowOverwrite on Umask 002 User ftpuser1 Group comgroup MaxClients 10 Port 10021 <Anonymous /var/ftp/example.com> User ftp Group nogroup # We want clients to be able to login with "anonymous" as well as "ftp" UserAlias anonymous ftp # Cosmetic changes, all files belongs to ftp user DirFakeUser on ftp DirFakeGroup on ftp RequireValidShell off # Limit the maximum number of anonymous logins MaxClients 10 # We want 'welcome.msg' displayed at login, and '.message' displayed # in each newly chdired directory. DisplayLogin welcome.msg DisplayFirstChdir .message # Limit WRITE everywhere in the anonymous chroot <Directory /var/ftp/example.com> </Directory> <Directory *> <Limit WRITE> DenyAll </Limit> </Directory> </Anonymous> </VirtualHost>
สร้าง example.org.conf ให้มาใช้พอร์ต 10022
# vi /etc/proftpd/example.org.conf
<VirtualHost ftp.example.org> ServerName "Example.org's FTP site" DefaultRoot ~ AllowOverwrite on Umask 002 User ftpuser2 Group orggroup MaxClients 10 Port 10022 <Anonymous /var/ftp/example.org> User ftp Group nogroup # We want clients to be able to login with "anonymous" as well as "ftp" UserAlias anonymous ftp # Cosmetic changes, all files belongs to ftp user DirFakeUser on ftp DirFakeGroup on ftp RequireValidShell off # Limit the maximum number of anonymous logins MaxClients 10 # We want 'welcome.msg' displayed at login, and '.message' displayed # in each newly chdired directory. DisplayLogin welcome.msg DisplayFirstChdir .message # Limit WRITE everywhere in the anonymous chroot <Directory /var/ftp/example.org> </Directory> <Directory *> <Limit WRITE> DenyAll </Limit> </Directory> </Anonymous> </VirtualHost>
เริ่ม protftpd ใหม่
# /etc/init.d/proftpd restart
เสร็จแล้ว
ผู้ใช้งานสามารถใช้งานผ่าน
จะสร้าง 2 แชร์ บรรจุไว้ใน /sys1/sysb/samba แล้วโยงลิงก์ไปที่รูต เป็น /samba เฉย ๆ
สำหรับอ่านอย่างเดียวตั้งชื่อว่า app และ สำหรับอ่านเขียนตั้งชื่อว่า data
Workgroup ตั้งชื่อว่า smbdomain
ตั้งชื่อกลุ่มผู้มีสิทธิ์ใช้งานว่า smbgroup
ผู้คุมระบบชื่อ admin
ผู้ใช้มีชื่อ user1, user2, ... ตามลำดับ
มีพิเศษเพิ่มเติม คือแชร์ที่ทำ ftp ไว้แล้ว จะให้มาแชร์ใน samba ด้วย เพื่อให้ผู้ใช้งานเครือข่ายภายใน สามารถใช้งานได้ง่าย
สร้างโฟลเดอร์ขึ้นมารองรับก่อน
# mkdir -p /sys1/sysb/samba/{app,data}
# ln -sf /sys1/sysb/samba /
# mkdir -p /sys1/sysb/etc/samba
# ln -sf /sys1/sysb/etc/samba /etc
ติดตั้ง samba
# aptitude install samba
Workgroup/Domain Name: <<<--- smbdomain Modify smb.conf to use WINS settings from DHCP? <<<--- No
สร้างกลุ่มของ samba
# groupadd -g 1100 smbgroup
เขียนสคริปต์สำหรับสร้างผู้ใช้และรหัสผ่าน ตั้งชื่อว่า d.samba-adduser เอาไว้ใน /usr/local/sbin
# vi /usr/local/sbin/d.samba-adduser
#!/bin/bash # SAMBA - ADD USER FROM DATAFILE IN FORMAT: # USER:UID:PASSWORD # ... # EXAMPLE: # #USER:UID:PASSWORD # user1:1101:user1-password # user2:1102:user2-password # ... #GLOBAL VAR GROUPNAME="smbgroup" SYSTEMGROUP="lp,dialout,cdrom" #FUNTION function usage { PROG=`basename $0` cat << EOF Script to add user in linux system and samba suit USAGE: $PROG DATAFILE ARGUMENT: DATAFILE : datafile in format:- USER:UID:PASSWORD ... EXAMPLE OF DATAFILE: example1: #USER:UID:PASSWORD user1:1101:user1-password user2:1102:user2-password ... Please run as root. EOF } #BEGIN MAIN DATAFILE=$1 shift if [ ! $DATAFILE ] || [ ! -f $DATAFILE ]; then usage exit 1 fi cat $DATAFILE | grep -v "#" | while read DATA; do if [ $DATA ]; then USERNAME=$(echo $DATA | cut -d: -f1) USERID=$(echo $DATA | cut -d: -f2) PASSWORD=$(echo $DATA | cut -d: -f3) /usr/sbin/useradd -g $GROUPNAME -G $SYSTEMGROUP -u $USERID -m $USERNAME echo "$USERNAME:$PASSWORD" | /usr/sbin/chpasswd (echo "$PASSWORD"; echo "$PASSWORD") | /usr/bin/smbpasswd -a -s $USERNAME echo "User: $USERNAME , uid: $USERID added." fi done
# chmod 700 /usr/local/sbin/d.samba-adduser
สร้างไฟล์ข้อมูลผู้ใช้ตั้งชื่อว่า userdata เอาไว้ใน /etc/samba/secure และตั้งให้คนอื่นอ่านไม่ได้
# mkdir -p /etc/samba/secure
# vi /etc/samba/secure/userdata
# SAMBA USER LIST #USER:UID:PASSWORD admin:1100:admin_password user1:1101:user1_password user2:1102:user2_password user3:1103:user3_password
# chmod -R 700 /etc/samba/secure
สั่งผลิตรายชื่อผู้ใช้
# d.samba-adduser /etc/samba/secure/userdata
ปรับตั้ง samba
# vi /etc/samba/smb.conf
... [global] workgroup = smbdomain security = user unix charset = utf8 display charset = utf8 unix extensions = yes ... [app] comment = Application Dir path = /samba/app valid users = @smbgroup write list = admin public = no create mask = 0750 directory mask = 0750 fake oplocks = yes ;; increase speed writable = no [data] comment = Data Dir path = /samba/data valid users = @smbgroup write list = @smbgroup public = no create mask = 0770 directory mask = 0770 writable = yes [ftp-pub] comment = Public ftp files path = /var/ftp/pub valid users = @smbgroup write list = @smbgroup public = no create mask = 0775 directory mask = 0775 writable = yes [ftp-com] comment = Example.com ftp files path = /var/ftp/example.com valid users = @smbgroup, @comuser write list = @comuser public = no create mask = 0775 directory mask = 0775 writable = yes [ftp-org] comment = Example.org ftp files path = /var/ftp/example.org valid users = @smbgroup, @orguser write list = @smbgroup public = no create mask = 0775 directory mask = 0775 writable = yes ...
เปลี่ยนสิทธ์ของไดเรกทอรี่
# chown -R admin:smbgroup /samba
# chmod -R 0750 /samba/app
# chmod -R 0770 /samba/data
เริ่ม samba ใหม่
# /etc/init.d/samba restart
เสร้จ
สุดท้ายเป็นการเก็บกวาดเล็กน้อย
งานที่ทำคือ...
จุดประสงค์คือการรวมศูนย์การใช้งาน cron แบบรายวัน ไว้ที่ไฟล์นี้ไฟล์เดียว
ยกเลิก cron ของ apt-proxy
# crontab -e
... #5 0 * * * /etc/init.d/apt-proxy restart >&2 ...
เปลี่ยนมาบรรจุคำสั่งใน d.cron-daily แทน
# vi /usr/local/sbin/d.cron-daily
#!/bin/bash #-----RESTART APT-PROXY------------------------------------ /etc/init.d/apt-proxy restart #-----BACKUP DB-------------------------------------------- ROOT="/sys1/sysb/backupdb" ...
แม้จะนอกรายการไปนิดนึง แต่ก็ขอบันทึกไว้เพื่อให้สมบูรณ์อย่างที่ตั้งใจครับ
จะสมมุติว่ามีเครื่องเซิร์ฟเวอร์สำรองอยู่ข้างหลังเซิร์ฟเวอร์เครื่องนี้อีกเครื่องนึง เพื่อที่จะทำ rsync มาคัดลอกข้อมูลในไดเรกทอรี่ /sys1/sysb ทั้งหมดไปเก็บไว้ ถ้าหากเกิดความเสียหายรุนแรงที่เครื่อง server1 นี้ จะได้มีข้อมูลมาสำรองกลับได้
สมมุติว่าชื่อ server2.example.com ไอพี 192.168.1.2
งานนี้จะทำที่เครื่อง server2 ทั้งหมด
ติดตั้ง rsync และ ssh
# aptitude install rsync ssh
สร้างกุญแจให้กับ ssh
# ssh-keygen -t dsa
Enter file in which to save the key (/root/.ssh/id_dsa): <<<--- [ENTER] Enter passphrase (empty for no passphrase): <<<--- [ENTER] Enter same passphrase again: <<<--- [ENTER] Your identification has been saved in /root/.ssh/id_dsa. Your public key has been saved in /home/user1/.ssh/id_dsa.pub. The key fingerprint is: cd:43:9b:3a:b1:60:01:ae:a2:0e:f8:00:21:8c:d8:f0 root@server2
คัดลอกกุญแจไปยัง server1
# ssh-copy-id -i /root/.ssh/id_dsa.pub root@server1
ต่อเข้า server1 เพื่อบันทึกรหัสผ่าน 1 ครั้ง
# ssh server1 -l root
The authenticity of host 'server1 (192.168.1.1)' can't be established. RSA key fingerprint is 1a:d1:12:f4:bd:d4:4c:11:93:55:9c:75:a7:eb:7d:ae. Are you sure you want to continue connecting (yes/no)? <<<--- yes Warning: Permanently added 'server1,192.168.1.1' (RSA) to the list of known hosts. root@server1's password: <<<--- ROOT@SERVER1-PASSWORD Now try logging into the machine, with "ssh 'root@server1'", and check in: .ssh/authorized_keys to make sure we haven't added extra keys that you weren't expecting. #
ต่อเข้าจริงอีก 1 ครั้ง
# ssh root@server1
Last login: Mon Mar 3 21:02:28 2008 from work1.example.com Linux server1 2.6.18 #1 Mon Mar 3 13:02:29 ICT 2008 i686 The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. No mail. # exit
(***อย่าลืม exit ออกจาก server1 ด้วยนะครับ***)
เสร็จเรื่อง ssh
ต่อเรื่องทำ rsync
สร้างไดเรคทอรี่มารองรับไฟล์จาก server1 สมมุติว่าให้ชื่อ /sys2/server1-sysb
# mkdir -p /sys2/server1-sysb
สร้างไฟล์ d.cron-rsync-server1 ใน /usr/local/sbin
# vi /usr/local/sbin/d.cron-rsync-server1
#!/bin/bash # IF sys2 IS SEPARATE, MOUNT /sys2 FIRST #mount /sys2 # RSYNC FROM server1:/sys1/sysb TO /sys2/server1-sysb rsync -aq --delete -e "ssh -i /root/.ssh/id_dsa" root@server1.example.com:/sys1/sysb/ /sys2/server1-sysb/ # IF sys2 IS SEPARATE, MOUNT /sys2 FIRST #umount /sys2
ทำให้รันได้
# chmod 700 /usr/local/sbin/d.cron-rsync-server1
ตั้ง cron ให้รันตอนตี 1
# crontab -e
... #BACKUP server1:/sys1/sysb TO /sys2/server1-sysb DAILY 20 1 * * * /usr/local/sbin/d.cron-rsync-server1 ...
เสร็จหมดแล้ว
จากเรื่อง all-in-one ผมแบ่งพาร์ติชั่น /sys1 โดยผูกติดกับ / (root) ไว้ ซึ่งจะทำให้ไม่สามารถยกเลิกการเมานต์ในระหว่างการใช้งานได้
สำหรับเครื่องที่เปิดไว้ตลอด 24 ช.ม.คงไม่มีปัญหาอะไร แต่ถ้าเป็นเครื่องที่ต้องมีการปิดเปิดตามเวลา จะพบปัญหาที่ระบบไฟล์จะทำการตรวจสอบตัวเองเมื่อทำการเมานต์ครบ 30 ครั้ง ซึ่งถ้าพาร์ติชั่นไม่ใหญ่ก็คงไม่เป็นไรนัก เพราะโปรแกรม fsck ทำงานแป๊ปเดียวก็เสร็จ แต่ถ้าเป็นพาร์ติชั่นที่ใหญ่เกิน 200G ขึ้นไปจะเกิดปัญหาการบูตที่นานเกินควร
วิธีแก้คือการบังคับให้บูตในตอนที่ไม่มีใครใช้งานเครื่อง โดยให้บังคับให้ตรวจสอบระบบไฟล์หลังการบูต ด้วยคำสั่ง shutdown
ตามด้วยพารามิเตอร์ -F
(force check)
เริ่มเลย
แก้ /etc/fstab ตั้งค่า fs_passno ให้พาร์ติชั่นของ /sys1 เป็น 1 เหมือน root file system เพื่อให้ระบบทำการตรวจสอบตอนเปิดเครื่อง
# vi /etc/fstab
... # <file system> <mount point> <type> <options> <dump> <pass> ... #/dev/hda8 /sys1 ext3 defaults 0 2 /dev/hda8 /sys1 ext3 defaults 0 1 ...
สมมุติว่าปิดเครื่องสัปดาห์ละ 1 ครั้งคือเย็นวันศุกร์ เราจะตั้งค่า crontab ให้บูตเครื่องพร้อมตรวจสอบไฟล์ในวันพุธ ซึ่งเป็นช่วงกลางสัปดาห์ (ถ้ามีปัญหาอะไรก็จะไม่ยุ่งมากนัก) ตอนตี 1
# crontab -e
# m h dom mon dow command ... #REBOOT FOR FSCK IN WEDNESDAY 1:00am 0 1 * * 3 /sbin/shutdown -F -r now ...
เสร็จแล้วครับ เราจะได้เครื่องที่พร้อมใช้งานเมื่อเปิดทุกวันจันทร์
อ้างอิง
update
เราสามารถตั้งให้การตรวจสอบดิสก์ตอนบูต ให้เป็นแบบซ่อมอัตโนมัติได้ (ยังไม่ทราบผลเสีย) โดยการเปลี่ยนค่าตัวแปรใน /etc/default/rcS
# vi /etc/default/rcS
... #FSCKFIX=no FSCKFIX=yes ...