diff --git a/1 - StackScript Bash Library.sh b/1 - StackScript Bash Library.sh index cec2773..9466e20 100644 --- a/1 - StackScript Bash Library.sh +++ b/1 - StackScript Bash Library.sh @@ -35,7 +35,8 @@ ########################################################### function system_update { - aptitude update + apt-get update + apt-get -y install aptitude aptitude -y full-upgrade } @@ -342,9 +343,9 @@ function wordpress_install { do sed -i "0,/put your unique phrase here/s/put your unique phrase here/$(randomString 50)/" wp-config.php done - sed -i 's/putyourdbnamehere/wordpress/' wp-config.php - sed -i 's/usernamehere/wordpress/' wp-config.php - sed -i "s/yourpasswordhere/$WPPASS/" wp-config.php + sed -i 's/database_name_here/wordpress/' wp-config.php + sed -i 's/username_here/wordpress/' wp-config.php + sed -i "s/password_here/$WPPASS/" wp-config.php # http://downloads.wordpress.org/plugin/wp-super-cache.0.9.8.zip } @@ -369,7 +370,7 @@ function goodstuff { function restartServices { # restarts services that have a file in /tmp/needs-restart/ - for service in $(ls /tmp/restart-* | cut -d- -f2); do + for service in $(ls /tmp/restart-* | cut -d- -f2-10); do /etc/init.d/$service restart rm -f /tmp/restart-$service done @@ -382,4 +383,4 @@ function randomString { fi echo $( /etc/apache2/mods-available/wsgi.load - - a2enmod wsgi -} - function apache_cleanup { a2dissite default # disable default vhost } diff --git a/123 - lib-system-ubuntu.sh b/123 - lib-system-ubuntu.sh index 8d228a3..775f3a2 100644 --- a/123 - lib-system-ubuntu.sh +++ b/123 - lib-system-ubuntu.sh @@ -11,46 +11,42 @@ function lower { echo $1 | tr '[:upper:]' '[:lower:]' } -function system_get_codename { - echo `lsb_release -sc` -} - -function system_get_release { - echo `lsb_release -sr` -} - function system_add_user { - # $1 - username - # $2 - password - # $3 - groups + # system_add_user(username, password, groups, shell=/bin/bash) USERNAME=`lower $1` PASSWORD=$2 SUDO_GROUP=$3 - SHELL="/bin/bash" + SHELL=$4 + if [ -z "$4" ]; then + SHELL="/bin/bash" + fi useradd --create-home --shell "$SHELL" --user-group --groups "$SUDO_GROUP" "$USERNAME" echo "$USERNAME:$PASSWORD" | chpasswd } function system_add_system_user { - # $1 - username - # $2 - home + # system_add_system_user(username, home, shell=/bin/bash) USERNAME=`lower $1` HOME_DIR=$2 - if [ -z "$HOME_DIR" ]; then - useradd --system --no-create-home --user-group $USERNAME - else - useradd --system --no-create-home --home-dir "$HOME_DIR" --user-group $USERNAME - fi; + SHELL=$3 + if [ -z "$3" ]; then + SHELL="/bin/bash" + fi + useradd --system --create-home --home-dir "$HOME_DIR" --shell "$SHELL" --user-group $USERNAME +} + +function system_lock_user { + # system_lock_user(username) + passwd -l "$1" } function system_get_user_home { - # $1 - username + # system_get_user_home(username) cat /etc/passwd | grep "^$1:" | cut --delimiter=":" -f6 } function system_user_add_ssh_key { - # $1 - username - # $2 - ssh key + # system_user_add_ssh_key(username, ssh_key) USERNAME=`lower $1` USER_HOME=`system_get_user_home "$USERNAME"` sudo -u "$USERNAME" mkdir "$USER_HOME/.ssh" @@ -60,8 +56,7 @@ function system_user_add_ssh_key { } function system_sshd_edit_bool { - # $1 - param name - # $2 - Yes/No + # system_sshd_edit_bool (param_name, "Yes"|"No") VALUE=`lower $2` if [ "$VALUE" == "yes" ] || [ "$VALUE" == "no" ]; then sed -i "s/^#*\($1\).*/\1 $VALUE/" /etc/ssh/sshd_config @@ -76,57 +71,96 @@ function system_sshd_passwordauthentication { system_sshd_edit_bool "PasswordAuthentication" "$1" } -function system_sshd_pubkeyauthentication { - system_sshd_edit_bool "PubkeyAuthentication" "$1" -} - -function system_sshd_passwordauthentication { - system_sshd_edit_bool "PasswordAuthentication" "$1" -} - -function system_enable_universe { - sed -i 's/^#\(.*deb.*\) universe/\1 universe/' /etc/apt/sources.list - aptitude update -} - -function system_update_locale_en_US_UTF_8 { - # locale-gen en_US.UTF-8 - dpkg-reconfigure locales - update-locale LANG=en_US.UTF-8 -} - function system_update_hostname { - # $1 - system hostname - if [ ! -n "$1" ]; then + # system_update_hostname(system hostname) + if [ -z "$1" ]; then echo "system_update_hostname() requires the system hostname as its first argument" return 1; fi echo $1 > /etc/hostname hostname -F /etc/hostname - echo -e "\n127.0.0.1 $1.local $1\n" >> /etc/hosts + echo -e "\n127.0.0.1 $1 $1.local\n" >> /etc/hosts } -function system_security_fail2ban { - aptitude -y install fail2ban +function system_security_logcheck { + aptitude -y install logcheck logcheck-database + # configure email + # start after setup } -function system_security_ufw_install { - aptitude -y install ufw +function system_security_fail2ban { + aptitude -y install fail2ban } function system_security_ufw_configure_basic { # see https://help.ubuntu.com/community/UFW - ufw logging on + ufw logging on ufw default deny - ufw allow ssh - ufw allow http - ufw allow https + ufw allow ssh/tcp + ufw limit ssh/tcp + + ufw allow http/tcp + ufw allow https/tcp ufw enable } -function system_security_logcheck { - aptitude -y install logcheck logcheck-database +function system_configure_private_network { + # system_configure_private_network(private_ip) + PRIVATE_IP=$1 + NETMASK="255.255.128.0" + cat >>/etc/network/interfaces < /dev/null # catch "nothing changed" return code } diff --git a/125 - lib-postgresql.sh b/125 - lib-postgresql.sh index bdd6c2d..6bcc17e 100644 --- a/125 - lib-postgresql.sh +++ b/125 - lib-postgresql.sh @@ -7,54 +7,33 @@ # My ref: http://www.linode.com/?r=aadfce9845055011e00f0c6c9a5c01158c452deb function postgresql_install { - aptitude -y install postgresql postgresql-contrib postgresql-dev postgresql-client libpq-dev -} - - -function postgresql_recreate_cluster { - # WARNING: this procedure removes all data in the main cluster - # drop default cluster and recreate it with UTF-8 encoding - # $1 - root data dicrecotry, i.e. /var/lib or /srv - BASE_DIRECTORY=$1 - pg_dropcluster -stop 8.4 main - if [ -z "$BASE_DIRECTORY" ]; then - pg_createcluster -start -e UTF-8 8.4 main - else - DATA_DIRECTORY="$BASE_DIRECTORY/postgresql" - mkdir -p "$DATA_DIRECTORY/8.4/main" - chown -R postgres:postgres "$DATA_DIRECTORY" - pg_createcluster -start -e UTF-8 -d "$DATA_DIRECTORY/8.4/main" 8.4 main - fi; + aptitude -y install postgresql postgresql-contrib postgresql-dev libpq-dev } function postgresql_create_user { - # $1 - the user to create - # $2 - their password - - if [ ! -n "$1" ]; then + # postgresql_create_user(username, password) + if [ -z "$1" ]; then echo "postgresql_create_user() requires username as the first argument" return 1; fi - if [ ! -n "$2" ]; then + if [ -z "$2" ]; then echo "postgresql_create_user() requires a password as the second argument" return 1; fi - echo "CREATE ROLE $1 WITH LOGIN ENCRYPTED PASSWORD '$2';" | sudo -u postgres psql + echo "CREATE ROLE $1 WITH LOGIN ENCRYPTED PASSWORD '$2';" | sudo -i -u postgres psql } function postgresql_create_database { - # $1 - the db name to create - # $2 - the db owner user - - if [ ! -n "$1" ]; then + # postgresql_create_database(dbname, owner) + if [ -z "$1" ]; then echo "postgresql_create_database() requires database name as the first argument" return 1; fi - if [ ! -n "$2" ]; then + if [ -z "$2" ]; then echo "postgresql_create_database() requires an owner username as the second argument" return 1; fi - sudo -u postgres createdb --owner=$2 $1 + sudo -i -u postgres createdb --owner=$2 $1 } diff --git a/126 - lib-python.sh b/126 - lib-python.sh index ac2d45a..b5c1141 100644 --- a/126 - lib-python.sh +++ b/126 - lib-python.sh @@ -8,5 +8,6 @@ function python_install { aptitude -y install python python-dev python-setuptools - easy_install virtualenv virtualenvwrapper pip + easy_install pip + pip install virtualenv virtualenvwrapper } diff --git a/127 - lib-django.sh b/127 - lib-django.sh index f2cd71e..fbaf5c8 100644 --- a/127 - lib-django.sh +++ b/127 - lib-django.sh @@ -6,80 +6,49 @@ # # My ref: http://www.linode.com/?r=aadfce9845055011e00f0c6c9a5c01158c452deb -function django_get_project_path { - PROJECT_NAME="$1" - echo "/srv/$PROJECT_NAME" -} +PROJECT_CODE_DIR=app +DJANGO_PROJECT=webapp function django_change_project_owner { + # django_change_project_owner(project_path, user) PROJECT_PATH="$1" USER="$2" - GROUP="$3" - if [ -z "$USER" ]; then - USER="www-data" - fi - if [ -z "$GROUP" ]; then - GROUP="$USER" - fi - chown -R "$USER:$GROUP" "$PROJECT_PATH" + chown -R "$USER:$USER" "$PROJECT_PATH" } - function django_create_project { - # $1 - name of the project to create create + # django_create_project(project_path) - PROJECT_NAME="$1" - if [ -z "$PROJECT_NAME" ]; then - echo "django_create_project() requires the project name as the first argument" + PROJECT_PATH="$1" + if [ -z "$PROJECT_PATH" ]; then + echo "django_create_project() requires the project root path as the first argument" return 1; fi - PROJECT_PATH=`django_get_project_path "$PROJECT_NAME"` - mkdir -p "$PROJECT_PATH/app" "$PROJECT_PATH/app/conf/apache" + mkdir -p "$PROJECT_PATH/$PROJECT_CODE_DIR/conf/apache" mkdir -p "$PROJECT_PATH/logs" "$PROJECT_PATH/run/eggs" - virtualenv --no-site-packages "$PROJECT_PATH/venv" - pip -E "$PROJECT_PATH/venv" install django + virtualenv "$PROJECT_PATH/venv" + $PROJECT_PATH/venv/bin/pip install Django - pushd "$PROJECT_PATH/app" - "$PROJECT_PATH/venv/bin/python" "$PROJECT_PATH/venv/bin/django-admin.py" startproject webapp + pushd "$PROJECT_PATH/$PROJECT_CODE_DIR" + "$PROJECT_PATH/venv/bin/python" "$PROJECT_PATH/venv/bin/django-admin.py" startproject "$DJANGO_PROJECT" . popd - mkdir -p "$PROJECT_PATH/app/webapp/site_media" - - cat > "$PROJECT_PATH/app/conf/apache/django.wsgi" << EOF -import os -import sys - -root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..')) -sys.path.insert(0, os.path.abspath(os.path.join(root_path, 'venv/lib/python2.6/site-packages/'))) -sys.path.insert(0, os.path.abspath(os.path.join(root_path, 'app'))) -sys.path.insert(0, os.path.abspath(os.path.join(root_path, 'app', 'webapp'))) - -os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' + mkdir -p "$PROJECT_PATH/$PROJECT_CODE_DIR/$DJANGO_PROJECT/static" -import django.core.handlers.wsgi -application = django.core.handlers.wsgi.WSGIHandler() -EOF + echo "Django" >> "$PROJECT_PATH/$PROJECT_CODE_DIR/requirements.txt" } - function django_install_db_driver { - # $1 - project root - # $2 - driver package - pip -E "$1/venv" install "$2" + # django_install_db_driver(project_path, driver_package) + $1/venv/bin/pip install "$2" + echo "$2" >> "$PROJECT_PATH/$PROJECT_CODE_DIR/requirements.txt" } - function django_configure_db_settings { - # $1 - project root - # $2 - engine - # $3 - name - # $4 - user - # $5 - password - # $6 - host (optional) - # $7 - port (optional) - - SETTINGS="$1/app/webapp/settings.py" + # django_configure_db_settings(project_path, engine, name, user, password, [host, [port]]) + PROJECT_PATH="$1" + SETTINGS="$PROJECT_PATH/$PROJECT_CODE_DIR/$DJANGO_PROJECT/settings.py" sed -i -e "s/'ENGINE': 'django.db.backends.'/'ENGINE': 'django.db.backends.$2'/" "$SETTINGS" sed -i -e "s/'NAME': ''/'NAME': '$3'/" "$SETTINGS" sed -i -e "s/'USER': ''/'USER': '$4'/" "$SETTINGS" @@ -92,18 +61,13 @@ function django_configure_db_settings { fi } - function django_configure_apache_virtualhost { - - # $1 - required - the hostname of the apache virtualhost to create - # $2 - required - path to the django project - # $3 - wsgi process user - # $4 - wsgi process group + # django_configure_apache_virtualhost(hostname, project_path, wsgi_user) VHOST_HOSTNAME="$1" PROJECT_PATH="$2" USER="$3" - GROUP="$4" + GROUP="$USER" if [ -z "$VHOST_HOSTNAME" ]; then echo "django_configure_apache_virtualhost() requires the hostname as the first argument" @@ -115,38 +79,37 @@ function django_configure_apache_virtualhost { return 1; fi - if [ -z "$USER" ]; then - USER="www-data" - fi - if [ -z "$GROUP" ]; then - GROUP="$USER" - fi + APACHE_CONF="200-$VHOST_HOSTNAME" + APACHE_CONF_PATH="$PROJECT_PATH/$PROJECT_CODE_DIR/conf/apache/$APACHE_CONF" - cat > "/etc/apache2/sites-available/$VHOST_HOSTNAME" << EOF + cat > "$APACHE_CONF_PATH" << EOF ServerAdmin root@$VHOST_HOSTNAME ServerName $VHOST_HOSTNAME + ServerSignature Off - Alias /site_media/ $PROJECT_PATH/app/webapp/site_media/ - Alias /media/ $PROJECT_PATH/venv/lib/python2.6/site-packages/django/contrib/admin/media/ - Alias /robots.txt $PROJECT_PATH/app/webapp/site_media/robots.txt - Alias /favicon.ico $PROJECT_PATH/app/webapp/site_media/favicon.ico + Alias /static/ $PROJECT_PATH/$PROJECT_CODE_DIR/$DJANGO_PROJECT/static/ + Alias /robots.txt $PROJECT_PATH/$PROJECT_CODE_DIR/$DJANGO_PROJECT/static/robots.txt + Alias /favicon.ico $PROJECT_PATH/$PROJECT_CODE_DIR/$DJANGO_PROJECT/static/favicon.ico - CustomLog "|/usr/sbin/rotatelogs $PROJECT_PATH/logs/access.log.%Y%m%d-%H%M%S 5M" combined - ErrorLog "|/usr/sbin/rotatelogs $PROJECT_PATH/logs/error.log.%Y%m%d-%H%M%S 5M" + SetEnvIf User_Agent "monit/*" dontlog + CustomLog "|/usr/sbin/rotatelogs $PROJECT_PATH/logs/access.log.%Y%m%d-%H%M 5M" combined env=!dontlog + ErrorLog "|/usr/sbin/rotatelogs $PROJECT_PATH/logs/error.log.%Y%m%d-%H%M 5M" LogLevel warn - WSGIDaemonProcess $VHOST_HOSTNAME user=$USER group=$GROUP processes=1 threads=15 maximum-requests=10000 python-path=$PROJECT_PATH/venv/lib/python2.6/site-packages python-eggs=$PROJECT_PATH/run/eggs + WSGIScriptAlias / $PROJECT_PATH/$PROJECT_CODE_DIR/$DJANGO_PROJECT/wsgi.py + + WSGIDaemonProcess $VHOST_HOSTNAME user=$USER group=$GROUP processes=2 threads=10 maximum-requests=10000 display-name=%{GROUP} python-path=$PROJECT_PATH/$PROJECT_CODE_DIR:$PROJECT_PATH/venv/lib/python2.7/site-packages python-eggs=$PROJECT_PATH/run/eggs WSGIProcessGroup $VHOST_HOSTNAME - WSGIScriptAlias / $PROJECT_PATH/app/conf/apache/django.wsgi + WSGIScriptAlias / $PROJECT_PATH/$PROJECT_CODE_DIR/$DJANGO_PROJECT/wsgi.py - + Order deny,allow Allow from all Options -Indexes FollowSymLinks - + Order deny,allow Allow from all @@ -154,5 +117,6 @@ function django_configure_apache_virtualhost { EOF - a2ensite "$VHOST_HOSTNAME" + ln -s -t /etc/apache2/sites-available/ "$APACHE_CONF_PATH" + a2ensite "$APACHE_CONF" } diff --git a/128 - lib-mongodb.sh b/128 - lib-mongodb.sh index a4b1ea0..ce01355 100644 --- a/128 - lib-mongodb.sh +++ b/128 - lib-mongodb.sh @@ -1,26 +1,12 @@ #!/bin/bash # -# Installs MongoDB from 10gen.com repository. +# Installs MongoDB. # # Copyright (c) 2010 Filip Wasilewski . # # My ref: http://www.linode.com/?r=aadfce9845055011e00f0c6c9a5c01158c452deb -function system_configure_mongodb_repository { - RELEASE=`lsb_release -sr | sed 's/\.0/\./'` - echo -e "\n##MongoDB repository by 10gen\ndeb http://downloads.mongodb.org/distros/ubuntu $RELEASE 10gen\n" >> /etc/apt/sources.list - apt-key adv --keyserver pgp.mit.edu --recv 7F0CEB10 || apt-key adv --keyserver keyserver.ubuntu.com --recv 7F0CEB10 - aptitude update -} function mongodb_install { - BASE_DIRECTORY=$1 # /var/lib or /srv - aptitude -y install mongodb-stable - if [ -n "$BASE_DIRECTORY" ]; then - DATA_DIRECTORY="$BASE_DIRECTORY/mongodb" - mkdir -p "$DATA_DIRECTORY" - chown mongodb:mongodb "$DATA_DIRECTORY" - sed -i "s:dbpath=.*:dbpath=$DATA_DIRECTORY:" /etc/mongodb.conf - fi; - touch /tmp/restart-mongodb + aptitude -y install mongodb } diff --git a/130 - monit.sh b/130 - monit.sh new file mode 100644 index 0000000..ac30968 --- /dev/null +++ b/130 - monit.sh @@ -0,0 +1,233 @@ +#!/bin/bash +# +# Monit system monitoring. See also http://mmonit.com/wiki/Monit/ConfigurationExamples +# +# Copyright (c) 2012 Filip Wasilewski . +# +# My ref: http://www.linode.com/?r=aadfce9845055011e00f0c6c9a5c01158c452deb + + +function monit_install { + aptitude -y install monit +} + +function monit_configure_email { + # system_monit_configure_email(email) +cat </etc/monit/conf.d/email-interface + set mailserver localhost + set alert $1 +EOT +} + +function monit_configure_web { + # system_monit_configure_web(domain) +cat </etc/monit/conf.d/web-interface + set httpd port 2812 and + use address $1 + allow $(randomString 10):$(randomString 30) + allow @sudo readonly + signature disable +EOT +ufw allow 2812/tcp +} + +function monit_def_system { + # monit_def_system(hostname) +cat </etc/monit/conf.d/system.cfg + check system $1 + if loadavg (1min) > 10 then alert + if loadavg (5min) > 7 then alert + if memory usage > 85% then alert + if swap usage > 25% then alert + if cpu usage (user) > 90% then alert + if cpu usage (system) > 60% then alert + if cpu usage (wait) > 50% then alert + group system +EOT +} + +function monit_def_rootfs { +cat </etc/monit/conf.d/rootfs.cfg + check filesystem rootfs with path / + if space usage > 80% for 5 times within 15 cycles then alert + if inode usage > 85% then alert + group system +EOT +} + +function monit_def_cron { +cat </etc/monit/conf.d/cron.cfg + check process cron with pidfile /var/run/crond.pid + start program = "/sbin/start cron" + stop program = "/sbin/stop cron" + if 5 restarts within 5 cycles then timeout + depends on cron_rc + group system + + check file cron_rc with path /etc/init.d/cron + if failed checksum then unmonitor + if failed permission 755 then unmonitor + if failed uid root then unmonitor + if failed gid root then unmonitor + group system +EOT +} + +function monit_def_sshd { +cat </etc/monit/conf.d/sshd.cfg + check process sshd with pidfile /var/run/sshd.pid + start program "/etc/init.d/ssh start" + stop program "/etc/init.d/ssh stop" + # if failed port 22 protocol ssh then restart + # if 3 restarts within 3 cycles then timeout +EOT +} + +function monit_def_ping_google { +cat </etc/monit/conf.d/ping_google.cfg + check host google-ping with address google.com + if failed port 80 proto http then alert + group server +EOT +} + +function monit_def_postfix { +cat </etc/monit/conf.d/postfix.cfg + check process postfix with pidfile /var/spool/postfix/pid/master.pid + start program = "/etc/init.d/postfix start" + stop program = "/etc/init.d/postfix stop" + if cpu > 60% for 2 cycles then alert + if cpu > 80% for 5 cycles then restart + if totalmem > 200.0 MB for 5 cycles then restart + if children > 250 then restart + if loadavg(5min) greater than 10 for 8 cycles then stop + if failed host localhost port 25 protocol smtp with timeout 15 seconds then alert + if failed host localhost port 25 protocol smtp for 3 cycles then restart + if 3 restarts within 5 cycles then timeout + group mail + + check file postfix_rc with path /etc/init.d/postfix + if failed checksum then unmonitor + if failed permission 755 then unmonitor + if failed uid root then unmonitor + if failed gid root then unmonitor + group mail +EOT +} + + +function monit_def_postgresql { +cat </etc/monit/conf.d/postgresql.cfg + check process postgres with pidfile /var/run/postgresql/9.1-main.pid + start program = "/etc/init.d/postgresql start" + stop program = "/etc/init.d/postgresql stop" + if failed unixsocket /var/run/postgresql/.s.PGSQL.5432 protocol pgsql then restart + if failed host localhost port 5432 protocol pgsql then restart + if 5 restarts within 5 cycles then timeout + depends on postgresql_bin + depends on postgresql_rc + group database + + check file postgresql_bin with path /usr/lib/postgresql/9.1/bin/postgres + if failed checksum then unmonitor + if failed permission 755 then unmonitor + if failed uid root then unmonitor + if failed gid root then unmonitor + group database + + check file postgresql_rc with path /etc/init.d/postgresql + if failed checksum then unmonitor + if failed permission 755 then unmonitor + if failed uid root then unmonitor + if failed gid root then unmonitor + group database + + check file postgresql_log with path /var/log/postgresql/postgresql-9.1-main.log + if size > 100 MB then alert + group database +EOT +} + +function monit_def_mysql { +cat < /etc/monit/conf.d/mysql.cfg + check process mysqld with pidfile /var/run/mysqld/mysqld.pid + start program = "/sbin/start mysql" with timeout 20 seconds + stop program = "/sbin/stop mysql" + if failed host localhost port 3306 protocol mysql then restart + if failed unixsocket /var/run/mysqld/mysqld.sock protocol mysql then restart + if 5 restarts within 5 cycles then timeout + depends on mysql_bin + depends on mysql_rc + group database + + check file mysql_bin with path /usr/sbin/mysqld + if failed checksum then unmonitor + if failed permission 755 then unmonitor + if failed uid root then unmonitor + if failed gid root then unmonitor + group database + + check file mysql_rc with path /etc/init.d/mysql + if failed checksum then unmonitor + if failed permission 755 then unmonitor + if failed uid root then unmonitor + if failed gid root then unmonitor + group database +EOT +} + +function monit_def_mongodb { +cat </etc/monit/conf.d/mongodb.cfg + check process mongodb with pidfile /var/lib/mongodb/mongod.lock + start program = "/sbin/start mongodb" + stop program = "/sbin/stop mongodb" + if failed host localhost port 28017 protocol http + and request "/" with timeout 10 seconds then restart + if 5 restarts within 5 cycles then timeout + group database +EOT +} + +function monit_def_memcached { +cat </etc/monit/conf.d/memcached.cfg + check process memcached with pidfile /var/run/memcached.pid + start program = "/etc/init.d/memcached start" + stop program = "/etc/init.d/memcached stop" + if 5 restarts within 5 cycles then timeout + group database +EOT +} + +function monit_def_apache { +cat </etc/monit/conf.d/apache2.cfg + check process apache with pidfile /var/run/apache2.pid + start program = "/etc/init.d/apache2 start" + stop program = "/etc/init.d/apache2 stop" + if cpu > 60% for 2 cycles then alert + if cpu > 80% for 5 cycles then alert + if totalmem > 200.0 MB for 5 cycles then alert + if children > 250 then alert + if loadavg(5min) greater than 10 for 8 cycles then stop + if failed host localhost port 80 protocol HTTP request / within 2 cycles then alert + if failed host localhost port 80 protocol apache-status + dnslimit > 25% or loglimit > 80% or waitlimit < 20% retry 2 within 2 cycles then alert + #if 5 restarts within 5 cycles then timeout + depends on apache_bin + depends on apache_rc + group www + + check file apache_bin with path /usr/sbin/apache2 + if failed checksum then unmonitor + if failed permission 755 then unmonitor + if failed uid root then unmonitor + if failed gid root then unmonitor + group www + + check file apache_rc with path /etc/init.d/apache2 + if failed checksum then unmonitor + if failed permission 755 then unmonitor + if failed uid root then unmonitor + if failed gid root then unmonitor + group www +EOT +} diff --git a/131 - Stack - Security, PostgreSQL, MySQL, MongoDB, Apache, Django.sh b/131 - Stack - Security, PostgreSQL, MySQL, MongoDB, Apache, Django.sh index 01738e6..bf84b94 100644 --- a/131 - Stack - Security, PostgreSQL, MySQL, MongoDB, Apache, Django.sh +++ b/131 - Stack - Security, PostgreSQL, MySQL, MongoDB, Apache, Django.sh @@ -6,36 +6,44 @@ # # My ref: http://www.linode.com/?r=aadfce9845055011e00f0c6c9a5c01158c452deb -# +# + +# # -# +# +# +# + +# -# -# +# -# +# +# +# +# -# +# +# +# +# +# -# -# -# -# +# -# -# -# -# -# +# -# +# +# +# +# -# -# -# -# +# +# -# +set -e +set -u +#set -x USER_GROUPS=sudo @@ -43,24 +51,18 @@ exec &> /root/stackscript.log source # StackScript Bash Library system_update -goodstuff - -source # lib-system-ubuntu -system_enable_universe source # lib-system system_install_mercurial -system_start_etc_dir_versioning +system_start_etc_dir_versioning #start recording changes of /etc config files -# source # lib-system-ubuntu # Configure system -system_update_locale_en_US_UTF_8 -system_record_etc_dir_changes "Updated locale" # SS124 +source # lib-system-ubuntu system_update_hostname "$SYS_HOSTNAME" system_record_etc_dir_changes "Updated hostname" # SS124 # Create user account -system_add_user "$USER_NAME" "$USER_PASSWORD" "$USER_GROUPS" +system_add_user "$USER_NAME" "$USER_PASSWORD" "$USER_GROUPS" "$USER_SHELL" if [ "$USER_SSHKEY" ]; then system_user_add_ssh_key "$USER_NAME" "$USER_SSHKEY" fi @@ -69,33 +71,36 @@ system_record_etc_dir_changes "Added unprivileged user account" # SS124 # Configure sshd system_sshd_permitrootlogin "$SSHD_PERMITROOTLOGIN" system_sshd_passwordauthentication "$SSHD_PASSWORDAUTH" -system_sshd_pubkeyauthentication "yes" touch /tmp/restart-ssh system_record_etc_dir_changes "Configured sshd" # SS124 -# Setup firewall -system_security_ufw_install -system_record_etc_dir_changes "Installed UFW" # SS124 -system_security_ufw_configure_basic -system_record_etc_dir_changes "Configured UFW" # SS124 +# Lock user account if not used for login +if [ "SSHD_PERMITROOTLOGIN" == "No" ]; then + system_lock_user "root" + system_record_etc_dir_changes "Locked root account" # SS124 +fi -# Setup fail2ban -system_security_fail2ban -system_record_etc_dir_changes "Installed fail2ban" # SS124 +# Install Postfix +postfix_install_loopback_only # SS1 +system_record_etc_dir_changes "Installed postfix loopback" # SS124 # Setup logcheck system_security_logcheck system_record_etc_dir_changes "Installed logcheck" # SS124 -# Install postfix -postfix_install_loopback_only # SS1 -system_record_etc_dir_changes "Installed postfix loopback" # SS124 +# Setup fail2ban +system_security_fail2ban +system_record_etc_dir_changes "Installed fail2ban" # SS124 + +# Setup firewall +system_security_ufw_configure_basic +system_record_etc_dir_changes "Configured UFW" # SS124 source # lib-python python_install system_record_etc_dir_changes "Installed python" # SS124 -# source # lib-system +# lib-system - SS124 system_install_utils system_install_build system_install_subversion @@ -103,20 +108,20 @@ system_install_git system_record_etc_dir_changes "Installed common utils" # Install and configure apache and mod_wsgi -source # lib-apache -apache_worker_install -system_record_etc_dir_changes "Installed apache" # SS124 -#apache_mod_wsgi_install -apache_mod_wsgi_install_from_source "3.3" # install a more recent mod-wsgi version -system_record_etc_dir_changes "Installed mod-wsgi" # SS124 -apache_cleanup -system_record_etc_dir_changes "Cleaned up apache config" # SS124 +if [ "$SETUP_APACHE" == "Yes" ]; then + source # lib-apache + apache_worker_install + system_record_etc_dir_changes "Installed apache" # SS124 + apache_mod_wsgi_install + system_record_etc_dir_changes "Installed mod-wsgi" # SS124 + apache_cleanup + system_record_etc_dir_changes "Cleaned up apache config" # SS124 +fi # Install PostgreSQL and setup database if [ "$SETUP_POSTGRESQL" == "Yes" ]; then source # lib-postgresql postgresql_install - postgresql_recreate_cluster "$BASE_DATA_DIRECTORY" system_record_etc_dir_changes "Installed PostgreSQL" postgresql_create_user "$POSTGRESQL_USER" "$POSTGRESQL_PASSWORD" postgresql_create_database "$POSTGRESQL_DATABASE" "$POSTGRESQL_USER" @@ -125,63 +130,136 @@ fi # Install MySQL and setup database if [ "$SETUP_MYSQL" == "Yes" ]; then + set +u # ignore undefined variables in Linode's SS1 mysql_install "$MYSQL_DATABASE_PASSWORD" && mysql_tune 30 mysql_create_database "$MYSQL_DATABASE_PASSWORD" "$MYSQL_DATABASE" mysql_create_user "$MYSQL_DATABASE_PASSWORD" "$MYSQL_USER" "$MYSQL_PASSWORD" mysql_grant_user "$MYSQL_DATABASE_PASSWORD" "$MYSQL_USER" "$MYSQL_DATABASE" + set -u system_record_etc_dir_changes "Configured MySQL" fi # Install MongoDB if [ "$SETUP_MONGODB" == "Yes" ]; then source # lib-mongodb - system_configure_mongodb_repository - system_record_etc_dir_changes "Added MongoDB repository to sources.list" - mongodb_install "$BASE_DATA_DIRECTORY" + mongodb_install system_record_etc_dir_changes "Installed MongoDB" fi # Setup and configure sample django project -if [ ! -n "$DJANGO_DOMAIN" ]; then - DJANGO_DOMAIN=$(get_rdns_primary_ip) -fi -if [ "$SETUP_DJANGO_PROJECT" == "Yes" ]; then - source # lib-django +RDNS=$(get_rdns_primary_ip) +DJANGO_PROJECT_PATH="" - DJANGO_PROJECT_PATH=`django_get_project_path "$DJANGO_PROJECT_NAME"` - django_create_project "$DJANGO_PROJECT_NAME" +if [ "$SETUP_DJANGO_PROJECT" != "No" ]; then + source # lib-django - system_add_system_user "$DJANGO_USER" "$DJANGO_PROJECT_PATH" - chsh -s /bin/bash "$DJANGO_USER" + if [ -z "$DJANGO_DOMAIN" ]; then DJANGO_DOMAIN=$RDNS; fi + + case "$SETUP_DJANGO_PROJECT" in + Standalone) + DJANGO_PROJECT_PATH="/srv/$DJANGO_PROJECT_NAME" + if [ -n "$DJANGO_USER" ]; then + if [ "$DJANGO_USER" != "$USER_NAME" ]; then + system_add_system_user "$DJANGO_USER" "$DJANGO_PROJECT_PATH" "$USER_SHELL" + else + mkdir -p "$DJANGO_PROJECT_PATH" + fi + else + DJANGO_USER="www-data" + fi + ;; + InUserHome) + DJANGO_USER=$USER_NAME + DJANGO_PROJECT_PATH=$(system_get_user_home "$USER_NAME")/$DJANGO_PROJECT_NAME + ;; + InUserHomeRoot) + DJANGO_USER=$USER_NAME + DJANGO_PROJECT_PATH=$(system_get_user_home "$USER_NAME") + ;; + esac + + django_create_project "$DJANGO_PROJECT_PATH" + django_change_project_owner "$DJANGO_PROJECT_PATH" "$DJANGO_USER" - django_configure_apache_virtualhost "$DJANGO_DOMAIN" "$DJANGO_PROJECT_PATH" "$DJANGO_USER" + if [ "$SETUP_APACHE" == "Yes" ]; then + django_configure_apache_virtualhost "$DJANGO_DOMAIN" "$DJANGO_PROJECT_PATH" "$DJANGO_USER" + touch /tmp/restart-apache2 + fi if [ "$SETUP_POSTGRESQL" == "Yes" ]; then django_install_db_driver "$DJANGO_PROJECT_PATH" "psycopg2" - django_configure_db_settings "$DJANGO_PROJECT_PATH" "postgresql_psycopg2" "$POSTGRESQL_DATABASE" "$POSTGRESQL_USER" "$POSTGRESQL_PASSWORD" "127.0.0.1" + django_configure_db_settings "$DJANGO_PROJECT_PATH" "postgresql_psycopg2" "$POSTGRESQL_DATABASE" "$POSTGRESQL_USER" "$POSTGRESQL_PASSWORD" "127.0.0.1" "" fi if [ "$SETUP_MYSQL" == "Yes" ]; then django_install_db_driver "$DJANGO_PROJECT_PATH" "MySQL-python" fi - django_change_project_owner "$DJANGO_PROJECT_PATH" "$DJANGO_USER" system_record_etc_dir_changes "Configured django project '$DJANGO_PROJECT_NAME'" - touch /tmp/restart-apache2 -fi; +fi -restartServices +if [ -n "$SYS_PRIVATE_IP" ]; then + system_configure_private_network "$SYS_PRIVATE_IP" + system_record_etc_dir_changes "Configured private network" +fi +restart_services +restart_initd_services + +if [ "$SETUP_MONIT" == "Yes" ]; then + source # lib-monit + monit_install + system_record_etc_dir_changes "Installed Monit" + + monit_configure_email "$NOTIFY_EMAIL" + monit_configure_web $(system_primary_ip) + system_record_etc_dir_changes "Configured Monit interfaces" + + monit_def_system "$SYS_HOSTNAME" + monit_def_rootfs + monit_def_cron + monit_def_postfix + monit_def_ping_google + if [ "$SETUP_POSTGRESQL" == "Yes" ]; then monit_def_postgresql; fi + if [ "$SETUP_MYSQL" == "Yes" ]; then monit_def_mysql; fi + if [ "$SETUP_MONGODB" == "Yes" ]; then monit_def_mongodb; fi + if [ "$SETUP_APACHE" == "Yes" ]; then monit_def_apache; fi + #if [ "$SETUP_MEMCACHE" == "Yes" ]; then monit_def_memcached; fi + system_record_etc_dir_changes "Created Monit rules for installed services" + monit reload +fi # Send info message -if [ -n "$NOTIFY_EMAIL" ]; then - mail -s "Your Linode VPS is configured" "$NOTIFY_EMAIL" < ~/setup_message <> ~/setup_message <> ~/setup_message <> ~/setup_message <)", re.IGNORECASE | re.MULTILINE) + return udf_re.findall(content) + + +def parse_param(udf): + element = fromstring(udf) + choices = element.get("oneof") + if choices: + choices = choices.split(",") + return { + 'name': element.get("name"), + 'label': element.get("label"), + 'choices': choices, + 'default': element.get("default"), + 'example': element.get("example") + } + + +def build_argparser(content): + parser = ArgParser( + description='Process StackScript file params.', + fromfile_prefix_chars='@', + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + epilog="Use @settings.txt as param to read configuration from settings file." + ) + parser.add_argument('script', metavar='SCRIPT', nargs=1, help='main script') + + for udf in get_udfs(content): + param = parse_param(udf) + help = param["example"] or "" + default = param["default"] + required = param["default"] is None + parser.add_argument( + '--%s' % param["name"], dest=param["name"].upper(), + action='store', default=default, choices=param["choices"] or None, + required=required, help=help + ) + return parser + + +def lookup_script(script_id): + try: + return glob.glob("%s - *.sh" % script_id)[0] + except IndexError: + raise ValueError("Script for given id does not exist: %s." % script_id) + + +def get_content(script_id): + with open(lookup_script(script_id)) as fp: + return fp.read() + + +def expand_script(content, mode="source"): + assert mode in ["source", "include"] + re_source = re.compile("""""") + re_include = re.compile("""^(\s*source\s+.*)$""", re.MULTILINE) + if mode == "source": + content = re_source.sub( + lambda x: '"%s"' % lookup_script(x.group(1)), + content + ) + else: + content = re_include.sub( + lambda x: '\n#==> Content of %s ==>>\n%s\n#<<== end of %s <=\n' % + (x.group(2), get_content(x.group(2)), x.group(2)), + content + ) + return content + + +def add_variables(content, args): + # Add script variables + variables = "%s\n" % "\n".join( + '%s="%s"' % (k,v) for k,v in sorted(args.__dict__.items()) if k.isupper() + ) + lines = iter(content.splitlines()) + content = [] + for line in lines: + if line.startswith("#") or not line.strip(): + content.append(line) + else: + content.append(variables) + content.append(line) + content.extend(lines) + break + return "\n".join(content) + + +def main(stack_script): + + if stack_script: + with open(stack_script) as fp: + content = fp.read() + + parser = build_argparser(content) + args = parser.parse_args() + + content = expand_script(content, mode="include") + content = add_variables(content, args) + + print content + + +if __name__ == "__main__": + if len(sys.argv) < 2 or not os.path.exists(sys.argv[1]): + print "First argument must be a valid script name" + main(sys.argv[1]) diff --git a/settings.txt b/settings.txt new file mode 100644 index 0000000..d7616ae --- /dev/null +++ b/settings.txt @@ -0,0 +1,21 @@ +--user_name ubuntu +--user_password pass +--user_sshkey +--sshd_permitrootlogin No +--sshd_passwordauth Yes +--sys_hostname myvps +--base_data_directory /srv +--setup_postgresql Yes +--postgresql_database django +--postgresql_user django +--postgresql_password django +--setup_mysql No +--mysql_database_password +--mysql_database +--mysql_user +--mysql_password +--setup_mongodb Yes +--setup_django_project Yes +--django_domain +--django_project_name my_project +--django_user django