Skip to content

Instantly share code, notes, and snippets.

@freephile
Created April 3, 2015 19:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save freephile/2d73f0f6cacc3d31d2f0 to your computer and use it in GitHub Desktop.
Save freephile/2d73f0f6cacc3d31d2f0 to your computer and use it in GitHub Desktop.
LAMP+ stack script for Linode
#!/bin/bash
set -e
source <ssinclude StackScriptID="1">
##################
# IMPORTANT
# Some functions in the script rely on other functions being used first
# (i.e. munin expects mysql to be installed)
##################
##################
# Instructions
##################
# Make sure you copy the UDFs to any script that uses these functions
# Make sure you source stackscript id 1
# source <ssinclude StackScriptID="1">
# <udf name="INSTALL_MYSQL" label="Instal MySQL?" oneOf="Yes,No">
# <udf name="MYSQL_PASSWORD" label="MySQL root password">
# <udf name="NGINX_VERSION" label="Install nginx from PPA" oneOf="Yes,No" example="See https://launchpad.net/~nginx/+archive/stable">
# <udf name="SSH_PORT" label="SSH port" default="22">
# <udf name="USER" label="Unprivileged User Account" />
# <udf name="USER_PASSWORD" label="Unprivileged User Password" />
# <udf name="USER_SSHKEY" label="Public Key for User" default="" />
# <udf name="ROOT_EMAIL" label="Email alias for root" />
# <udf name="HOSTNAME" label="Hostname" default="" />
# <udf name="WHICHKERNEL" label="Use ubuntu ec2 kernel?" oneOf="No,Yes" />
# <udf name="WEBSERVER" label="Which webserver to use?" oneOf="Nginx,Apache Prefork,Apache Worker,None" />
##################
# Support
##################
# https://www.linode.com/docs/platform/stackscripts
# For support please email info@eQuality-Tech.com
##################
#TODO
##################
#add monit for mysql/postfix/ssh/munin
#enabled mod rewrite/expires/deflate/status by default
#set apache monit to use mod status
#add option to set up an initial domain in /srv/www/domain.com
function main {
#update system and set hostname
prep_system
#setup firewall
install_shorewall
#setup standard user
configure_user
#secure ssh
configure_ssh
#setup postfix
install_postfix
if [ "$INSTALL_MYSQL" == "Yes" ]
then
#setup mysql
install_mysql
fi
if [ "$WEBSERVER" == "Nginx" ]
then
#setup php
install_php_fcgi
fi
if [ "$WEBSERVER" == "Nginx" ]
then
#setup nginx
install_nginx
fi
if [ "$WEBSERVER" == "Apache Prefork" ]
then
#setup apache
apache_install
apache_tune
fi
if [ "$WEBSERVER" == "Apache Prefork" ]
then
#setup php
install_php_apache
fi
if [ "$WEBSERVER" == "Apache Worker" ]
then
#setup apache and php
install_php_apache_worker
fi
#install monit/munin/security tools/other tools
install_monit
install_munin
install_security
install_tools
#install stock kernel
#this requires user interaction
if [ "$WHICHKERNEL" == "Yes" ]
then
install_ubuntu_stock_kernel
fi
#set root .profile
set_root_profile
#delete users that aren't needed
deleteusers
#cleanup
cleanup
#send notification
notification_email
}
function prep_system {
#update system
#setup hostname
if [ -z "$HOSTNAME" ]
then
export HOSTNAME=`get_rdns_primary_ip`
fi
HOST=`echo $HOSTNAME | sed 's/\(\[a-z0-9\]\)*\..*/\1/'`
echo "$HOST" > /etc/hostname
echo "`system_primary_ip` $HOSTNAME $HOST" >> /etc/hosts
start hostname
echo "/usr/sbin/nologin" >> /etc/shells
# set timezone to UTC
# ln -s -f /usr/share/zoneinfo/UTC /etc/localtime
# set timezone to US/Eastern
ln -s -f /usr/share/zoneinfo/US/Eastern /etc/localtime
system_update
}
function install_nginx {
#add nginx ppa
if [ $NGINX_VERSION == "Yes" ]
then
aptitude -y install python-software-properties
add-apt-repository ppa:nginx/stable
aptitude update
fi
#Install nginx
aptitude -y install nginx
cat <<EOT > /etc/nginx/fastcgi_config
fastcgi_intercept_errors on;
fastcgi_ignore_client_abort on;
fastcgi_connect_timeout 60;
fastcgi_send_timeout 180;
fastcgi_read_timeout 180;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_max_temp_file_size 0;
fastcgi_index index.php;
EOT
service nginx start
mkdir -p /etc/monit/conf.d
cat <<EOT >/etc/monit/conf.d/nginx
check process nginx with pidfile /var/run/nginx.pid
group www-data
start program = "/etc/init.d/nginx start"
stop program = "/etc/init.d/nginx stop"
if failed port 80 protocol HTTP request / within 5 cycles then restart
if 5 restarts within 5 cycles then timeout
EOT
sed -i 's/# gzip_types/gzip_types/' /etc/nginx/nginx.conf
sed -i 's/# gzip_vary/gzip_vary/' /etc/nginx/nginx.conf
}
function notification_email {
#mail root to confirm installation
mail -s "Linode "`cat /etc/hostname`" setup complete" root <<EOT
Your linode setup is complete, if you encounter problems or would like commercial support email info@eQuality-Tech.com.
Your linode will reboot shortly after this email is sent.
You will need to shutdown your linode and change the kernel to pv-grub-x86_32 in the linode manager if you installed the ubuntu stock kernel
EOT
$(shutdown -r +1) &
}
function edit_php_ini {
ini_file=$1
sed -i 's/;[ \t]*allow_call_time_pass_reference[ \t]*.*$/allow_call_time_pass_reference = Off/' $ini_file
sed -i 's/post_max_size = 8M/post_max_size = 20M/' $ini_file
sed -i 's/upload_max_filesize = 2M/upload_max_filesize = 20M/' $ini_file
sed -i 's/default_charset = "iso-8859-1"/default_charset = "UTF-8"/' $ini_file
sed -i 's/short_open_tag = On/short_open_tag = Off/' $ini_file
# As of v 5.3.0 dl() is now disabled in all SAPIs except CLI and Embed
# sed -i 's/disable_functions =/disable_functions = dl/' $ini_file
sed -i 's/expose_php = On/expose_php = Off/' $ini_file
# I don't want to lower the memory limit
# sed -i 's/memory_limit = 128M/memory_limit = 32M/' $ini_file
sed -i 's/;[ \t]*arg_separator.output/arg_separator.output/' $ini_file
sed -i 's/;[ \t]*date.timezone =/date.timezone = UTC/' $ini_file
sed -i 's/session.name = PHPSESSID/session.name = SESSID/' $ini_file
sed -i 's@;[ \t]*error_log = syslog@error_log = /var/log/php/error.log@' $ini_file
}
function monit_apache {
mkdir -p /etc/monit/conf.d
cat <<EOT >/etc/monit/conf.d/apache2
check process apache with pidfile /var/run/apache2.pid
group www-data
start program = "/etc/init.d/apache2 start"
stop program = "/etc/init.d/apache2 stop"
if failed port 80 protocol HTTP request / within 5 cycles then restart
if 5 restarts within 5 cycles then timeout
EOT
}
function make_php_logdir {
mkdir -p /var/log/php/
chown www-data /var/log/php/
}
# This is not neccessary because
# all debian derivatives now use
# php5enmod
# enable_php_extension /etc/php5/conf.d/mcrypt.ini
# becomes php5enmod mcrypt
function enable_php_extension {
extension_file=$1
extension=$(basename $extension_file .ini)
# extension=mcrypt.so
sed -i "s/;[ \t]*extension=$extension.so/extension=$extension.so/" $extension_file
}
function install_php_apache {
aptitude -y install libapache2-mod-php5 php5-cli php5-curl php5-gd php5-mcrypt php5-mysql php5-sqlite php-apc
edit_php_ini /etc/php5/apache2/php.ini
make_php_logdir
php5enmod mcrypt
monit_apache
}
function install_php_apache_worker {
aptitude -y install apache2-mpm-worker libapache2-mod-fcgid php5-cgi php5-cli php5-curl php5-gd php5-mcrypt php5-mysql php5-sqlite php-apc
edit_php_ini /etc/php5/cgi/php.ini
make_php_logdir
php5enmod mcrypt
cat <<EOT >/etc/apache2/conf.d/fcgi.conf
FcgidMaxProcesses 4
FcgidMaxRequestsPerProcess 5000
AddHandler fcgid-script .php
FcgidWrapper /usr/local/bin/php5-fcgi-wrapper .php
EOT
cat <<EOT > /usr/local/bin/php5-fcgi-wrapper
#!/bin/sh
PHP_FCGI_MAX_REQUESTS=0
export PHP_FCGI_MAX_REQUESTS
PHP_FCGI_CHILDREN=0
export PHP_FCGI_CHILDREN
exec /usr/bin/php-cgi
EOT
chmod +x /usr/local/bin/php5-fcgi-wrapper
monit_apache
a2dissite default # disable the interfering default virtualhost
# clean up, or add the NameVirtualHost line to ports.conf
sed -i -e 's/^NameVirtualHost \*$/NameVirtualHost *:80/' /etc/apache2/ports.conf
if ! grep -q NameVirtualHost /etc/apache2/ports.conf; then
echo 'NameVirtualHost *:80' > /etc/apache2/ports.conf.tmp
cat /etc/apache2/ports.conf >> /etc/apache2/ports.conf.tmp
mv -f /etc/apache2/ports.conf.tmp /etc/apache2/ports.conf
fi
}
function install_php_fcgi {
#Install PHP and common extensions
aptitude -y install php5-cgi php5-cli php5-curl php5-gd php5-mcrypt php5-mysql php5-sqlite php-apc
#configure php to run as fcgi under user www-data on port 8000 edit init script to change this
edit_php_ini /etc/php5/cgi/php.ini
make_php_logdir
php5enmod mcrypt
cat <<EOT >/etc/init/php5-fcgi.conf
# php5-fcgi
#
# Spawns php5 in fastcgi mode
start on (local-filesystems and net-device-up IFACE=eth0)
stop on runlevel [!12345]
respawn
respawn limit 5 300
script
. /etc/php5/upstart/\$UPSTART_JOB
su -p -s /bin/bash -c "\$PHP_ENVS /usr/bin/php-cgi -b \$PHP_LISTEN -c \$PHP_INI" \$PHP_USER
end script
#create pidfile
post-start script
sleep 1
status \$UPSTART_JOB | egrep -o '([0-9]+)\$' | head -n1 > /var/run/\$UPSTART_JOB.pid
end script
#delete pid file
post-stop exec rm /var/run/\$UPSTART_JOB.pid
EOT
mkdir -p /etc/php5/upstart/
cat <<EOT >/etc/php5/upstart/php5-fcgi
export PHP_USER=www-data
export PHP_LISTEN=127.0.0.1:8000
export PHP_ENVS="PHP_FCGI_CHILDREN=4 PHP_FCGI_MAX_REQUESTS=5000"
export PHP_INI="/etc/php5/php.ini.sites/www-data.ini"
EOT
mkdir -p /etc/php5/php.ini.sites/
cp /etc/php5/cgi/php.ini /etc/php5/php.ini.sites/www-data.ini
start php5-fcgi
mkdir -p /etc/monit/conf.d
cat <<EOT >/etc/monit/conf.d/php5-fcgi
check process php5-fcgi with pidfile /var/run/php5-fcgi.pid
start program "/sbin/start php5-fcgi"
stop program "/sbin/stop php5-fcgi"
if failed port 8000 type tcp then restart
if 5 restarts within 5 cycles then timeout
EOT
}
function install_mysql {
#Install mysql
mysql_install "$MYSQL_PASSWORD"
mysql_tune 30
sed -i -e 's/^skip-innodb/#skip-innodb/' /etc/mysql/my.cnf # re-enable innodb, it may take more memory but it's becoming standard
#enable slow query logging to table compatible with mysql workbench
cat <<EOT > /etc/mysql/conf.d/logging.cnf
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 1
log-queries-not-using-indexes
log-output=TABLE
EOT
#make pid file static name across installations
cat <<EOT > /etc/mysql/conf.d/pid.cnf
[mysqld]
pid-file = /var/lib/mysql/mysqld.pid
EOT
cat <<EOT > /root/.my.cnf
[client]
user=root
password=$MYSQL_PASSWORD
EOT
chmod 0400 /root/.my.cnf
mkdir -p /etc/monit/conf.d/
cat <<EOT > /etc/monit/conf.d/mysql
check process mysqld with pidfile /var/lib/mysql/mysqld.pid
group mysql
start program = "/sbin/start mysql"
stop program = "/sbin/stop mysql"
if failed unixsocket /var/run/mysqld/mysqld.sock protocol mysql then restart
if 5 restarts within 5 cycles then timeout
EOT
}
function install_postfix {
#Install postfix
postfix_install_loopback_only
#install mail sending utilities
aptitude -y install mailutils
#configure root alias
echo "root: $ROOT_EMAIL" >> /etc/aliases
echo "$USER: root" >> /etc/aliases
cat /etc/hostname > /etc/mailname
/usr/bin/newaliases
sed -i "s/mydestination = localhost, localhost.localdomain, , localhost/mydestination = localhost, localhost.localdomain, $HOSTNAME/" /etc/postfix/main.cf
service postfix restart
}
function configure_ssh {
#setup ssh
#add ssh key
sudo -u $USER mkdir /home/$USER/.ssh
sudo -u $USER echo "${USER_SSHKEY}" >> /home/$USER/.ssh/authorized_keys
chmod 0600 /home/$USER/.ssh/authorized_keys
chown $USER:$USER /home/$USER/.ssh/authorized_keys
sed -i "s/Port 22/Port $SSH_PORT/" /etc/ssh/sshd_config #set ssh port
sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config #disable root ligin
if [ "$USER_SSHKEY" != "" ]
then
#disable ssh password auth if $USER_SSHKEY is not empty
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
fi
#disable xforwarding
sed -i 's/X11Forwarding yes/X11Forwarding no/' /etc/ssh/sshd_config
#disable dns lookups
echo "UseDNS no" >> /etc/ssh/sshd_config
#only allow access from $USER
echo "AllowUsers $USER" >> /etc/ssh/sshd_config
}
function configure_user {
#configure ssh/sudo
useradd -m -s /bin/bash $USER #add user account
echo "$USER:$USER_PASSWORD" | chpasswd #setpassword
#add user to sudoers
usermod -a -G admin $USER
#lock out root
passwd -l root
}
function install_shorewall {
#sets up shorewall firewall
aptitude -y install shorewall shorewall6
cp /usr/share/doc/shorewall/examples/one-interface/* /etc/shorewall/
sed -i 's/BLACKLISTNEWONLY=Yes/BLACKLISTNEWONLY=No/' /etc/shorewall/shorewall.conf
sed -i 's/REJECT/DROP/' /etc/shorewall/policy
if [ "$WEBSERVER" != "None" ]
then
echo "#accept http/s" >> /etc/shorewall/rules
echo "ACCEPT net \$FW:`system_primary_ip` tcp 80" >> /etc/shorewall/rules
echo "ACCEPT net \$FW:`system_primary_ip` tcp 443" >> /etc/shorewall/rules
fi
echo '#accept ssh and ratelimit to 5 connections per miniute per ip' >> /etc/shorewall/rules
echo "ACCEPT net \$FW:`system_primary_ip` tcp $SSH_PORT - - s:ssh:5/min:1" >> /etc/shorewall/rules
sed -i 's/STARTUP_ENABLED=No/STARTUP_ENABLED=Yes/' /etc/shorewall/shorewall.conf
sed -i 's/startup=0/startup=1/' /etc/default/shorewall
#disable ipv6 by default
cp /usr/share/doc/shorewall6/examples/one-interface/* /etc/shorewall6/
sed -i 's/BLACKLISTNEWONLY=Yes/BLACKLISTNEWONLY=No/' /etc/shorewall6/shorewall6.conf
sed -i 's/REJECT/DROP/' /etc/shorewall6/policy
sed -i 's/STARTUP_ENABLED=No/STARTUP_ENABLED=Yes/' /etc/shorewall6/shorewall6.conf
sed -i 's/startup=0/startup=1/' /etc/default/shorewall6
}
function install_monit {
#install and enable monit
aptitude -y install monit
sed -i 's/startup=0/startup=1/' /etc/default/monit
mkdir -p /etc/monit/conf.d/
echo "include /etc/monit/conf.d/*" >> /etc/monit/monitrc
sed -i "s/#[ \t]*set daemon 120/set daemon 120/" /etc/monit/monitrc
sed -i "s/#[ \t]*with start delay 240/with start delay 240/" /etc/monit/monitrc
sed -i "s/#[ \t]*set logfile syslog facility log_daemon/set logfile \/var\/log\/monit.log/" /etc/monit/monitrc
sed -i "s/#[ \t]*set mailserver mail.bar.baz,/set mailserver localhost/" /etc/monit/monitrc
sed -i "s/#[ \t]*set eventqueue/set eventqueue/" /etc/monit/monitrc
sed -i "s/#[ \t]*basedir \/var\/monit/basedir \/var\/monit/" /etc/monit/monitrc
sed -i "s/#[ \t]*slots 100 /slots 100/" /etc/monit/monitrc
sed -i "s/#[ \t]*set alert sysadm@foo.bar/set alert root@localhost reminder 180/" /etc/monit/monitrc
sed -i "s/#[ \t]*set httpd port 2812 and/ set httpd port 2812 and/" /etc/monit/monitrc
sed -i "s/#[ \t]*use address localhost/use address localhost/" /etc/monit/monitrc
sed -i "s/#[ \t]*allow localhost/allow localhost/" /etc/monit/monitrc
sed -i "s/#[ \t]*set mail-format { from: monit@foo.bar }/set mail-format { from: monit@`hostname -f` }/" /etc/monit/monitrc
cat << EOT > /etc/monit/conf.d/system
check system `hostname -f`
if loadavg (1min) > 4 then alert
if loadavg (5min) > 4 then alert
if memory usage > 90% then alert
if cpu usage (user) > 70% then alert
if cpu usage (system) > 30% then alert
if cpu usage (wait) > 20% then alert
check filesystem rootfs with path /
if space > 80% then alert
EOT
}
function install_munin {
#install munin
aptitude -y install munin munin-node libcache-cache-perl libdbd-mysql-perl
sed -i 's/host \*/host 127.0.0.1/' /etc/munin/munin-node.conf
sed -i "s/localhost.localdomain/`hostname -f`/" /etc/munin/munin.conf
echo "munin: root" >> /etc/aliases
sed -i "s#\[mysql\*\]#[mysql*]\nenv.mysqladmin /usr/bin/mysqladmin#" /etc/munin/plugin-conf.d/munin-node
if [ -x /usr/bin/newaliases ]
then
/usr/bin/newaliases
fi
}
function install_security {
#install chrootkit rkhunter logwatch
aptitude -y install chkrootkit rkhunter logwatch libsys-cpu-perl build-essential
echo "yes" | perl -MCPAN -e 'install Sys::MemInfo'
sed -i 's/#ALLOWHIDDENDIR=\/dev\/.initramfs/ALLOWHIDDENDIR=\/dev\/.initramfs/' /etc/rkhunter.conf
sed -i 's/#ALLOWHIDDENDIR=\/dev\/.udev/ALLOWHIDDENDIR=\/dev\/.udev/' /etc/rkhunter.conf
sed -i 's/DISABLE_TESTS="suspscan hidden_procs deleted_files packet_cap_apps apps"/DISABLE_TESTS="suspscan hidden_procs deleted_files packet_cap_apps apps os_specific"/' /etc/rkhunter.conf
rkhunter --propupd
sed -i 's/--output mail/--output mail --detail 10 --service All/' /etc/cron.daily/00logwatch
}
function install_tools {
#install full vim, nano, less, htop (nice version of top), iotop (top for disk io), logrotate (rotates logs..), lynx (text webbrowser), mytop (top for mysql), ntop (top for networks), screen (terminal emulator), sqlite3 (command line interface for sqlite databases)
aptitude -y install vim nano less htop iotop logrotate lynx mytop nmap ntop screen sqlite3 cron-apt ntp curl
sed -i 's/# EXITON="error"/EXITON=""/' /etc/cron-apt/config
sed -i 's/# SYSLOGON="upgrade"/SYSLOGON=""/' /etc/cron-apt/config
sed -i 's/# MAILON="error"/MAILON=""/' /etc/cron-apt/config
}
function install_ubuntu_stock_kernel {
#installs ubuntu ec2 kernel which works best on linode
#configures grub to load kernel after 3 seconds
#sets console to hvc0 so you can access via listh
#enables delayacct so iotop works
aptitude -y install grub
update-grub -y
sed -i 's#kopt=root=.* ro#kopt=root=/dev/xvda ro#' /boot/grub/menu.lst
sed -i 's#groot=.*#groot=(hd0)#' /boot/grub/menu.lst
sed -i 's/defoptions=quiet splash/defoptions=quiet delayacct console=hvc0/' /boot/grub/menu.lst
sed -i 's/# indomU=detect/# indomU=true/' /boot/grub/menu.lst
aptitude -y install linux-ec2
chmod 0600 /boot/grub/menu.lst
}
function set_root_profile {
#Black 0;30 Dark Gray 1;30
#Blue 0;34 Light Blue 1;34
#Green 0;32 Light Green 1;32
#Cyan 0;36 Light Cyan 1;36
#Red 0;31 Light Red 1;31
#Purple 0;35 Light Purple 1;35
#Brown 0;33 Yellow 1;33
#Light Gray 0;37 White 1;37
cat <<EOT >> /root/.profile
PS1='\[\033[0;33m\]root@'
#add hostname
PS1=\$PS1\$(hostname -f)'\n'
#add ipv4 addresses
PS1=\$PS1\$(ifconfig | grep -v '127.0.0.1' | awk -F: '/inet addr:/ {print \$2}' | awk '{ print \$1 }')
#add ipv6 addresses
PS1=\$PS1'\n'\$(ifconfig | grep 'Global' | awk -F / '/inet6 addr: / {print \$1}' | awk '{ print \$3 }')
#add current working dir and close colors
PS1=\$PS1'\n\$PWD:\$\033[00m\]\n'
export PS1
EOT
}
function cleanup {
#disable services not required
if [ -f /etc/init/atd.conf ]
then
stop atd
mv /etc/init/atd.conf /etc/init/atd.conf.noexec
fi
update-locale
}
#deletes users from /etc/passwd that you don't need
function deleteusers {
deluser irc #delete irc user
deluser games #delete games user
deluser news #delete nntp daemon user
deluser uucp #delete uucp user
deluser proxy #delete proxy user
deluser list #delete mailing list user
deluser gnats #delete gnats bug reporting user
}
# invoke our stack script
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment