├── 1 - StackScript Bash Library.sh ├── 122 - lib-apache.sh ├── 123 - lib-system-ubuntu.sh ├── 124 - lib-system.sh ├── 125 - lib-postgresql.sh ├── 126 - lib-python.sh ├── 127 - lib-django.sh ├── 128 - lib-mongodb.sh ├── 130 - monit.sh ├── 131 - Stack - Security, PostgreSQL, MySQL, MongoDB, Apache, Django.sh ├── README.md ├── prepare.py └── settings.txt /1 - StackScript Bash Library.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # StackScript Bash Library 4 | # 5 | # Copyright (c) 2010 Linode LLC / Christopher S. Aker 6 | # All rights reserved. 7 | # 8 | # Redistribution and use in source and binary forms, with or without modification, 9 | # are permitted provided that the following conditions are met: 10 | # 11 | # * Redistributions of source code must retain the above copyright notice, this 12 | # list of conditions and the following disclaimer. 13 | # 14 | # * Redistributions in binary form must reproduce the above copyright notice, this 15 | # list of conditions and the following disclaimer in the documentation and/or 16 | # other materials provided with the distribution. 17 | # 18 | # * Neither the name of Linode LLC nor the names of its contributors may be 19 | # used to endorse or promote products derived from this software without specific prior 20 | # written permission. 21 | # 22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 23 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 | # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 25 | # SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 27 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 28 | # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 31 | # DAMAGE. 32 | 33 | ########################################################### 34 | # System 35 | ########################################################### 36 | 37 | function system_update { 38 | apt-get update 39 | apt-get -y install aptitude 40 | aptitude -y full-upgrade 41 | } 42 | 43 | function system_primary_ip { 44 | # returns the primary IP assigned to eth0 45 | echo $(ifconfig eth0 | awk -F: '/inet addr:/ {print $2}' | awk '{ print $1 }') 46 | } 47 | 48 | function get_rdns { 49 | # calls host on an IP address and returns its reverse dns 50 | 51 | if [ ! -e /usr/bin/host ]; then 52 | aptitude -y install dnsutils > /dev/null 53 | fi 54 | echo $(host $1 | awk '/pointer/ {print $5}' | sed 's/\.$//') 55 | } 56 | 57 | function get_rdns_primary_ip { 58 | # returns the reverse dns of the primary IP assigned to this system 59 | echo $(get_rdns $(system_primary_ip)) 60 | } 61 | 62 | ########################################################### 63 | # Postfix 64 | ########################################################### 65 | 66 | function postfix_install_loopback_only { 67 | # Installs postfix and configure to listen only on the local interface. Also 68 | # allows for local mail delivery 69 | 70 | echo "postfix postfix/main_mailer_type select Internet Site" | debconf-set-selections 71 | echo "postfix postfix/mailname string localhost" | debconf-set-selections 72 | echo "postfix postfix/destinations string localhost.localdomain, localhost" | debconf-set-selections 73 | aptitude -y install postfix 74 | /usr/sbin/postconf -e "inet_interfaces = loopback-only" 75 | #/usr/sbin/postconf -e "local_transport = error:local delivery is disabled" 76 | 77 | touch /tmp/restart-postfix 78 | } 79 | 80 | 81 | ########################################################### 82 | # Apache 83 | ########################################################### 84 | 85 | function apache_install { 86 | # installs the system default apache2 MPM 87 | aptitude -y install apache2 88 | 89 | a2dissite default # disable the interfering default virtualhost 90 | 91 | # clean up, or add the NameVirtualHost line to ports.conf 92 | sed -i -e 's/^NameVirtualHost \*$/NameVirtualHost *:80/' /etc/apache2/ports.conf 93 | if ! grep -q NameVirtualHost /etc/apache2/ports.conf; then 94 | echo 'NameVirtualHost *:80' > /etc/apache2/ports.conf.tmp 95 | cat /etc/apache2/ports.conf >> /etc/apache2/ports.conf.tmp 96 | mv -f /etc/apache2/ports.conf.tmp /etc/apache2/ports.conf 97 | fi 98 | } 99 | 100 | function apache_tune { 101 | # Tunes Apache's memory to use the percentage of RAM you specify, defaulting to 40% 102 | 103 | # $1 - the percent of system memory to allocate towards Apache 104 | 105 | if [ ! -n "$1" ]; 106 | then PERCENT=40 107 | else PERCENT="$1" 108 | fi 109 | 110 | aptitude -y install apache2-mpm-prefork 111 | PERPROCMEM=10 # the amount of memory in MB each apache process is likely to utilize 112 | MEM=$(grep MemTotal /proc/meminfo | awk '{ print int($2/1024) }') # how much memory in MB this system has 113 | MAXCLIENTS=$((MEM*PERCENT/100/PERPROCMEM)) # calculate MaxClients 114 | MAXCLIENTS=${MAXCLIENTS/.*} # cast to an integer 115 | sed -i -e "s/\(^[ \t]*MaxClients[ \t]*\)[0-9]*/\1$MAXCLIENTS/" /etc/apache2/apache2.conf 116 | 117 | touch /tmp/restart-apache2 118 | } 119 | 120 | function apache_virtualhost { 121 | # Configures a VirtualHost 122 | 123 | # $1 - required - the hostname of the virtualhost to create 124 | 125 | if [ ! -n "$1" ]; then 126 | echo "apache_virtualhost() requires the hostname as the first argument" 127 | return 1; 128 | fi 129 | 130 | if [ -e "/etc/apache2/sites-available/$1" ]; then 131 | echo /etc/apache2/sites-available/$1 already exists 132 | return; 133 | fi 134 | 135 | mkdir -p /srv/www/$1/public_html /srv/www/$1/logs 136 | 137 | echo "" > /etc/apache2/sites-available/$1 138 | echo " ServerName $1" >> /etc/apache2/sites-available/$1 139 | echo " DocumentRoot /srv/www/$1/public_html/" >> /etc/apache2/sites-available/$1 140 | echo " ErrorLog /srv/www/$1/logs/error.log" >> /etc/apache2/sites-available/$1 141 | echo " CustomLog /srv/www/$1/logs/access.log combined" >> /etc/apache2/sites-available/$1 142 | echo "" >> /etc/apache2/sites-available/$1 143 | 144 | a2ensite $1 145 | 146 | touch /tmp/restart-apache2 147 | } 148 | 149 | function apache_virtualhost_from_rdns { 150 | # Configures a VirtualHost using the rdns of the first IP as the ServerName 151 | 152 | apache_virtualhost $(get_rdns_primary_ip) 153 | } 154 | 155 | 156 | function apache_virtualhost_get_docroot { 157 | if [ ! -n "$1" ]; then 158 | echo "apache_virtualhost_get_docroot() requires the hostname as the first argument" 159 | return 1; 160 | fi 161 | 162 | if [ -e /etc/apache2/sites-available/$1 ]; 163 | then echo $(awk '/DocumentRoot/ {print $2}' /etc/apache2/sites-available/$1 ) 164 | fi 165 | } 166 | 167 | ########################################################### 168 | # mysql-server 169 | ########################################################### 170 | 171 | function mysql_install { 172 | # $1 - the mysql root password 173 | 174 | if [ ! -n "$1" ]; then 175 | echo "mysql_install() requires the root pass as its first argument" 176 | return 1; 177 | fi 178 | 179 | echo "mysql-server-5.1 mysql-server/root_password password $1" | debconf-set-selections 180 | echo "mysql-server-5.1 mysql-server/root_password_again password $1" | debconf-set-selections 181 | apt-get -y install mysql-server mysql-client 182 | 183 | echo "Sleeping while MySQL starts up for the first time..." 184 | sleep 5 185 | } 186 | 187 | function mysql_tune { 188 | # Tunes MySQL's memory usage to utilize the percentage of memory you specify, defaulting to 40% 189 | 190 | # $1 - the percent of system memory to allocate towards MySQL 191 | 192 | if [ ! -n "$1" ]; 193 | then PERCENT=40 194 | else PERCENT="$1" 195 | fi 196 | 197 | sed -i -e 's/^#skip-innodb/skip-innodb/' /etc/mysql/my.cnf # disable innodb - saves about 100M 198 | 199 | MEM=$(awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo) # how much memory in MB this system has 200 | MYMEM=$((MEM*PERCENT/100)) # how much memory we'd like to tune mysql with 201 | MYMEMCHUNKS=$((MYMEM/4)) # how many 4MB chunks we have to play with 202 | 203 | # mysql config options we want to set to the percentages in the second list, respectively 204 | OPTLIST=(key_buffer sort_buffer_size read_buffer_size read_rnd_buffer_size myisam_sort_buffer_size query_cache_size) 205 | DISTLIST=(75 1 1 1 5 15) 206 | 207 | for opt in ${OPTLIST[@]}; do 208 | sed -i -e "/\[mysqld\]/,/\[.*\]/s/^$opt/#$opt/" /etc/mysql/my.cnf 209 | done 210 | 211 | for i in ${!OPTLIST[*]}; do 212 | val=$(echo | awk "{print int((${DISTLIST[$i]} * $MYMEMCHUNKS/100))*4}") 213 | if [ $val -lt 4 ] 214 | then val=4 215 | fi 216 | config="${config}\n${OPTLIST[$i]} = ${val}M" 217 | done 218 | 219 | sed -i -e "s/\(\[mysqld\]\)/\1\n$config\n/" /etc/mysql/my.cnf 220 | 221 | touch /tmp/restart-mysql 222 | } 223 | 224 | function mysql_create_database { 225 | # $1 - the mysql root password 226 | # $2 - the db name to create 227 | 228 | if [ ! -n "$1" ]; then 229 | echo "mysql_create_database() requires the root pass as its first argument" 230 | return 1; 231 | fi 232 | if [ ! -n "$2" ]; then 233 | echo "mysql_create_database() requires the name of the database as the second argument" 234 | return 1; 235 | fi 236 | 237 | echo "CREATE DATABASE $2;" | mysql -u root -p$1 238 | } 239 | 240 | function mysql_create_user { 241 | # $1 - the mysql root password 242 | # $2 - the user to create 243 | # $3 - their password 244 | 245 | if [ ! -n "$1" ]; then 246 | echo "mysql_create_user() requires the root pass as its first argument" 247 | return 1; 248 | fi 249 | if [ ! -n "$2" ]; then 250 | echo "mysql_create_user() requires username as the second argument" 251 | return 1; 252 | fi 253 | if [ ! -n "$3" ]; then 254 | echo "mysql_create_user() requires a password as the third argument" 255 | return 1; 256 | fi 257 | 258 | echo "CREATE USER '$2'@'localhost' IDENTIFIED BY '$3';" | mysql -u root -p$1 259 | } 260 | 261 | function mysql_grant_user { 262 | # $1 - the mysql root password 263 | # $2 - the user to bestow privileges 264 | # $3 - the database 265 | 266 | if [ ! -n "$1" ]; then 267 | echo "mysql_create_user() requires the root pass as its first argument" 268 | return 1; 269 | fi 270 | if [ ! -n "$2" ]; then 271 | echo "mysql_create_user() requires username as the second argument" 272 | return 1; 273 | fi 274 | if [ ! -n "$3" ]; then 275 | echo "mysql_create_user() requires a database as the third argument" 276 | return 1; 277 | fi 278 | 279 | echo "GRANT ALL PRIVILEGES ON $3.* TO '$2'@'localhost';" | mysql -u root -p$1 280 | echo "FLUSH PRIVILEGES;" | mysql -u root -p$1 281 | 282 | } 283 | 284 | ########################################################### 285 | # PHP functions 286 | ########################################################### 287 | 288 | function php_install_with_apache { 289 | aptitude -y install php5 php5-mysql libapache2-mod-php5 290 | touch /tmp/restart-apache2 291 | } 292 | 293 | function php_tune { 294 | # Tunes PHP to utilize up to 32M per process 295 | 296 | sed -i'-orig' 's/memory_limit = [0-9]\+M/memory_limit = 32M/' /etc/php5/apache2/php.ini 297 | touch /tmp/restart-apache2 298 | } 299 | 300 | ########################################################### 301 | # Wordpress functions 302 | ########################################################### 303 | 304 | function wordpress_install { 305 | # installs the latest wordpress tarball from wordpress.org 306 | 307 | # $1 - required - The existing virtualhost to install into 308 | 309 | if [ ! -n "$1" ]; then 310 | echo "wordpress_install() requires the vitualhost as its first argument" 311 | return 1; 312 | fi 313 | 314 | if [ ! -e /usr/bin/wget ]; then 315 | aptitude -y install wget 316 | fi 317 | 318 | VPATH=$(apache_virtualhost_get_docroot $1) 319 | 320 | if [ ! -n "$VPATH" ]; then 321 | echo "Could not determine DocumentRoot for $1" 322 | return 1; 323 | fi 324 | 325 | # download, extract, chown, and get our config file started 326 | cd $VPATH 327 | wget http://wordpress.org/latest.tar.gz 328 | tar xfz latest.tar.gz 329 | chown -R www-data: wordpress/ 330 | cd $VPATH/wordpress 331 | cp wp-config-sample.php wp-config.php 332 | chown www-data wp-config.php 333 | chmod 640 wp-config.php 334 | 335 | # database configuration 336 | WPPASS=$(randomString 20) 337 | mysql_create_database "$DB_PASSWORD" wordpress 338 | mysql_create_user "$DB_PASSWORD" wordpress "$WPPASS" 339 | mysql_grant_user "$DB_PASSWORD" wordpress wordpress 340 | 341 | # configuration file updates 342 | for i in {1..4} 343 | do sed -i "0,/put your unique phrase here/s/put your unique phrase here/$(randomString 50)/" wp-config.php 344 | done 345 | 346 | sed -i 's/database_name_here/wordpress/' wp-config.php 347 | sed -i 's/username_here/wordpress/' wp-config.php 348 | sed -i "s/password_here/$WPPASS/" wp-config.php 349 | 350 | # http://downloads.wordpress.org/plugin/wp-super-cache.0.9.8.zip 351 | } 352 | 353 | ########################################################### 354 | # Other niceties! 355 | ########################################################### 356 | 357 | function goodstuff { 358 | # Installs the REAL vim, wget, less, and enables color root prompt and the "ll" list long alias 359 | 360 | aptitude -y install wget vim less 361 | sed -i -e 's/^#PS1=/PS1=/' /root/.bashrc # enable the colorful root bash prompt 362 | sed -i -e "s/^#alias ll='ls -l'/alias ll='ls -al'/" /root/.bashrc # enable ll list long alias <3 363 | } 364 | 365 | 366 | ########################################################### 367 | # utility functions 368 | ########################################################### 369 | 370 | function restartServices { 371 | # restarts services that have a file in /tmp/needs-restart/ 372 | 373 | for service in $(ls /tmp/restart-* | cut -d- -f2-10); do 374 | /etc/init.d/$service restart 375 | rm -f /tmp/restart-$service 376 | done 377 | } 378 | 379 | function randomString { 380 | if [ ! -n "$1" ]; 381 | then LEN=20 382 | else LEN="$1" 383 | fi 384 | 385 | echo $(. 6 | # 7 | # My ref: http://www.linode.com/?r=aadfce9845055011e00f0c6c9a5c01158c452deb 8 | 9 | function apache_worker_install { 10 | aptitude -y install apache2-mpm-worker apache2-dev 11 | } 12 | 13 | function apache_mod_wsgi_install { 14 | aptitude -y install libapache2-mod-wsgi 15 | } 16 | 17 | function apache_cleanup { 18 | a2dissite default # disable default vhost 19 | } 20 | -------------------------------------------------------------------------------- /123 - lib-system-ubuntu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # System related utilities 4 | # 5 | # Copyright (c) 2010 Filip Wasilewski . 6 | # 7 | # My ref: http://www.linode.com/?r=aadfce9845055011e00f0c6c9a5c01158c452deb 8 | 9 | function lower { 10 | # helper function 11 | echo $1 | tr '[:upper:]' '[:lower:]' 12 | } 13 | 14 | function system_add_user { 15 | # system_add_user(username, password, groups, shell=/bin/bash) 16 | USERNAME=`lower $1` 17 | PASSWORD=$2 18 | SUDO_GROUP=$3 19 | SHELL=$4 20 | if [ -z "$4" ]; then 21 | SHELL="/bin/bash" 22 | fi 23 | useradd --create-home --shell "$SHELL" --user-group --groups "$SUDO_GROUP" "$USERNAME" 24 | echo "$USERNAME:$PASSWORD" | chpasswd 25 | } 26 | 27 | function system_add_system_user { 28 | # system_add_system_user(username, home, shell=/bin/bash) 29 | USERNAME=`lower $1` 30 | HOME_DIR=$2 31 | SHELL=$3 32 | if [ -z "$3" ]; then 33 | SHELL="/bin/bash" 34 | fi 35 | useradd --system --create-home --home-dir "$HOME_DIR" --shell "$SHELL" --user-group $USERNAME 36 | } 37 | 38 | function system_lock_user { 39 | # system_lock_user(username) 40 | passwd -l "$1" 41 | } 42 | 43 | function system_get_user_home { 44 | # system_get_user_home(username) 45 | cat /etc/passwd | grep "^$1:" | cut --delimiter=":" -f6 46 | } 47 | 48 | function system_user_add_ssh_key { 49 | # system_user_add_ssh_key(username, ssh_key) 50 | USERNAME=`lower $1` 51 | USER_HOME=`system_get_user_home "$USERNAME"` 52 | sudo -u "$USERNAME" mkdir "$USER_HOME/.ssh" 53 | sudo -u "$USERNAME" touch "$USER_HOME/.ssh/authorized_keys" 54 | sudo -u "$USERNAME" echo "$2" >> "$USER_HOME/.ssh/authorized_keys" 55 | chmod 0600 "$USER_HOME/.ssh/authorized_keys" 56 | } 57 | 58 | function system_sshd_edit_bool { 59 | # system_sshd_edit_bool (param_name, "Yes"|"No") 60 | VALUE=`lower $2` 61 | if [ "$VALUE" == "yes" ] || [ "$VALUE" == "no" ]; then 62 | sed -i "s/^#*\($1\).*/\1 $VALUE/" /etc/ssh/sshd_config 63 | fi 64 | } 65 | 66 | function system_sshd_permitrootlogin { 67 | system_sshd_edit_bool "PermitRootLogin" "$1" 68 | } 69 | 70 | function system_sshd_passwordauthentication { 71 | system_sshd_edit_bool "PasswordAuthentication" "$1" 72 | } 73 | 74 | function system_update_hostname { 75 | # system_update_hostname(system hostname) 76 | if [ -z "$1" ]; then 77 | echo "system_update_hostname() requires the system hostname as its first argument" 78 | return 1; 79 | fi 80 | echo $1 > /etc/hostname 81 | hostname -F /etc/hostname 82 | echo -e "\n127.0.0.1 $1 $1.local\n" >> /etc/hosts 83 | } 84 | 85 | function system_security_logcheck { 86 | aptitude -y install logcheck logcheck-database 87 | # configure email 88 | # start after setup 89 | } 90 | 91 | function system_security_fail2ban { 92 | aptitude -y install fail2ban 93 | } 94 | 95 | function system_security_ufw_configure_basic { 96 | # see https://help.ubuntu.com/community/UFW 97 | ufw logging on 98 | 99 | ufw default deny 100 | 101 | ufw allow ssh/tcp 102 | ufw limit ssh/tcp 103 | 104 | ufw allow http/tcp 105 | ufw allow https/tcp 106 | 107 | ufw enable 108 | } 109 | 110 | function system_configure_private_network { 111 | # system_configure_private_network(private_ip) 112 | PRIVATE_IP=$1 113 | NETMASK="255.255.128.0" 114 | cat >>/etc/network/interfaces <. 6 | # 7 | # My ref: http://www.linode.com/?r=aadfce9845055011e00f0c6c9a5c01158c452deb 8 | 9 | function system_install_utils { 10 | aptitude -y install htop iotop bsd-mailx python-software-properties zsh 11 | } 12 | 13 | function system_install_build { 14 | aptitude -y install build-essential gcc 15 | } 16 | 17 | function system_install_subversion { 18 | aptitude -y install subversion 19 | } 20 | 21 | function system_install_git { 22 | aptitude -y install git-core 23 | } 24 | 25 | function system_install_mercurial { 26 | aptitude -y install mercurial 27 | } 28 | 29 | function system_start_etc_dir_versioning { 30 | hg init /etc 31 | hg add /etc 32 | hg commit -u root -m "Started versioning of /etc directory" /etc 33 | chmod -R go-rwx /etc/.hg 34 | } 35 | 36 | function system_record_etc_dir_changes { 37 | if [ ! -n "$1" ]; 38 | then MESSAGE="Committed /etc changes" 39 | else MESSAGE="$1" 40 | fi 41 | hg addremove /etc 42 | hg commit -u root -m "$MESSAGE" /etc || echo > /dev/null # catch "nothing changed" return code 43 | } 44 | -------------------------------------------------------------------------------- /125 - lib-postgresql.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Install PostgreSQL 4 | # 5 | # Copyright (c) 2010 Filip Wasilewski . 6 | # 7 | # My ref: http://www.linode.com/?r=aadfce9845055011e00f0c6c9a5c01158c452deb 8 | 9 | function postgresql_install { 10 | aptitude -y install postgresql postgresql-contrib postgresql-dev libpq-dev 11 | } 12 | 13 | function postgresql_create_user { 14 | # postgresql_create_user(username, password) 15 | if [ -z "$1" ]; then 16 | echo "postgresql_create_user() requires username as the first argument" 17 | return 1; 18 | fi 19 | if [ -z "$2" ]; then 20 | echo "postgresql_create_user() requires a password as the second argument" 21 | return 1; 22 | fi 23 | 24 | echo "CREATE ROLE $1 WITH LOGIN ENCRYPTED PASSWORD '$2';" | sudo -i -u postgres psql 25 | } 26 | 27 | function postgresql_create_database { 28 | # postgresql_create_database(dbname, owner) 29 | if [ -z "$1" ]; then 30 | echo "postgresql_create_database() requires database name as the first argument" 31 | return 1; 32 | fi 33 | if [ -z "$2" ]; then 34 | echo "postgresql_create_database() requires an owner username as the second argument" 35 | return 1; 36 | fi 37 | 38 | sudo -i -u postgres createdb --owner=$2 $1 39 | } 40 | -------------------------------------------------------------------------------- /126 - lib-python.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Install python and base packages 4 | # 5 | # Copyright (c) 2010 Filip Wasilewski . 6 | # 7 | # My ref: http://www.linode.com/?r=aadfce9845055011e00f0c6c9a5c01158c452deb 8 | 9 | function python_install { 10 | aptitude -y install python python-dev python-setuptools 11 | easy_install pip 12 | pip install virtualenv virtualenvwrapper 13 | } 14 | -------------------------------------------------------------------------------- /127 - lib-django.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Setup django project and add apache vhost configuration 4 | # 5 | # Copyright (c) 2010 Filip Wasilewski . 6 | # 7 | # My ref: http://www.linode.com/?r=aadfce9845055011e00f0c6c9a5c01158c452deb 8 | 9 | PROJECT_CODE_DIR=app 10 | DJANGO_PROJECT=webapp 11 | 12 | function django_change_project_owner { 13 | # django_change_project_owner(project_path, user) 14 | PROJECT_PATH="$1" 15 | USER="$2" 16 | chown -R "$USER:$USER" "$PROJECT_PATH" 17 | } 18 | 19 | function django_create_project { 20 | # django_create_project(project_path) 21 | 22 | PROJECT_PATH="$1" 23 | if [ -z "$PROJECT_PATH" ]; then 24 | echo "django_create_project() requires the project root path as the first argument" 25 | return 1; 26 | fi 27 | 28 | mkdir -p "$PROJECT_PATH/$PROJECT_CODE_DIR/conf/apache" 29 | mkdir -p "$PROJECT_PATH/logs" "$PROJECT_PATH/run/eggs" 30 | 31 | virtualenv "$PROJECT_PATH/venv" 32 | $PROJECT_PATH/venv/bin/pip install Django 33 | 34 | pushd "$PROJECT_PATH/$PROJECT_CODE_DIR" 35 | "$PROJECT_PATH/venv/bin/python" "$PROJECT_PATH/venv/bin/django-admin.py" startproject "$DJANGO_PROJECT" . 36 | popd 37 | mkdir -p "$PROJECT_PATH/$PROJECT_CODE_DIR/$DJANGO_PROJECT/static" 38 | 39 | echo "Django" >> "$PROJECT_PATH/$PROJECT_CODE_DIR/requirements.txt" 40 | } 41 | 42 | function django_install_db_driver { 43 | # django_install_db_driver(project_path, driver_package) 44 | $1/venv/bin/pip install "$2" 45 | echo "$2" >> "$PROJECT_PATH/$PROJECT_CODE_DIR/requirements.txt" 46 | } 47 | 48 | function django_configure_db_settings { 49 | # django_configure_db_settings(project_path, engine, name, user, password, [host, [port]]) 50 | PROJECT_PATH="$1" 51 | SETTINGS="$PROJECT_PATH/$PROJECT_CODE_DIR/$DJANGO_PROJECT/settings.py" 52 | sed -i -e "s/'ENGINE': 'django.db.backends.'/'ENGINE': 'django.db.backends.$2'/" "$SETTINGS" 53 | sed -i -e "s/'NAME': ''/'NAME': '$3'/" "$SETTINGS" 54 | sed -i -e "s/'USER': ''/'USER': '$4'/" "$SETTINGS" 55 | sed -i -e "s/'PASSWORD': ''/'PASSWORD': '$5'/" "$SETTINGS" 56 | if [ -n "$6" ]; then 57 | sed -i -e "s/'HOST': ''/'HOST': '$6'/" "$SETTINGS" 58 | fi 59 | if [ -n "$7" ]; then 60 | sed -i -e "s/'PORT': ''/'PORT': '$7'/" "$SETTINGS" 61 | fi 62 | } 63 | 64 | function django_configure_apache_virtualhost { 65 | # django_configure_apache_virtualhost(hostname, project_path, wsgi_user) 66 | 67 | VHOST_HOSTNAME="$1" 68 | PROJECT_PATH="$2" 69 | USER="$3" 70 | GROUP="$USER" 71 | 72 | if [ -z "$VHOST_HOSTNAME" ]; then 73 | echo "django_configure_apache_virtualhost() requires the hostname as the first argument" 74 | return 1; 75 | fi 76 | 77 | if [ -z "$PROJECT_PATH" ]; then 78 | echo "django_configure_apache_virtualhost() requires path to the django project as the second argument" 79 | return 1; 80 | fi 81 | 82 | APACHE_CONF="200-$VHOST_HOSTNAME" 83 | APACHE_CONF_PATH="$PROJECT_PATH/$PROJECT_CODE_DIR/conf/apache/$APACHE_CONF" 84 | 85 | cat > "$APACHE_CONF_PATH" << EOF 86 | 87 | ServerAdmin root@$VHOST_HOSTNAME 88 | ServerName $VHOST_HOSTNAME 89 | ServerSignature Off 90 | 91 | Alias /static/ $PROJECT_PATH/$PROJECT_CODE_DIR/$DJANGO_PROJECT/static/ 92 | Alias /robots.txt $PROJECT_PATH/$PROJECT_CODE_DIR/$DJANGO_PROJECT/static/robots.txt 93 | Alias /favicon.ico $PROJECT_PATH/$PROJECT_CODE_DIR/$DJANGO_PROJECT/static/favicon.ico 94 | 95 | SetEnvIf User_Agent "monit/*" dontlog 96 | CustomLog "|/usr/sbin/rotatelogs $PROJECT_PATH/logs/access.log.%Y%m%d-%H%M 5M" combined env=!dontlog 97 | ErrorLog "|/usr/sbin/rotatelogs $PROJECT_PATH/logs/error.log.%Y%m%d-%H%M 5M" 98 | LogLevel warn 99 | 100 | WSGIScriptAlias / $PROJECT_PATH/$PROJECT_CODE_DIR/$DJANGO_PROJECT/wsgi.py 101 | 102 | 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 103 | WSGIProcessGroup $VHOST_HOSTNAME 104 | WSGIScriptAlias / $PROJECT_PATH/$PROJECT_CODE_DIR/$DJANGO_PROJECT/wsgi.py 105 | 106 | 107 | Order deny,allow 108 | Allow from all 109 | Options -Indexes FollowSymLinks 110 | 111 | 112 | 113 | Order deny,allow 114 | Allow from all 115 | 116 | 117 | 118 | EOF 119 | 120 | ln -s -t /etc/apache2/sites-available/ "$APACHE_CONF_PATH" 121 | a2ensite "$APACHE_CONF" 122 | } 123 | -------------------------------------------------------------------------------- /128 - lib-mongodb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Installs MongoDB. 4 | # 5 | # Copyright (c) 2010 Filip Wasilewski . 6 | # 7 | # My ref: http://www.linode.com/?r=aadfce9845055011e00f0c6c9a5c01158c452deb 8 | 9 | 10 | function mongodb_install { 11 | aptitude -y install mongodb 12 | } 13 | -------------------------------------------------------------------------------- /130 - monit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Monit system monitoring. See also http://mmonit.com/wiki/Monit/ConfigurationExamples 4 | # 5 | # Copyright (c) 2012 Filip Wasilewski . 6 | # 7 | # My ref: http://www.linode.com/?r=aadfce9845055011e00f0c6c9a5c01158c452deb 8 | 9 | 10 | function monit_install { 11 | aptitude -y install monit 12 | } 13 | 14 | function monit_configure_email { 15 | # system_monit_configure_email(email) 16 | cat </etc/monit/conf.d/email-interface 17 | set mailserver localhost 18 | set alert $1 19 | EOT 20 | } 21 | 22 | function monit_configure_web { 23 | # system_monit_configure_web(domain) 24 | cat </etc/monit/conf.d/web-interface 25 | set httpd port 2812 and 26 | use address $1 27 | allow $(randomString 10):$(randomString 30) 28 | allow @sudo readonly 29 | signature disable 30 | EOT 31 | ufw allow 2812/tcp 32 | } 33 | 34 | function monit_def_system { 35 | # monit_def_system(hostname) 36 | cat </etc/monit/conf.d/system.cfg 37 | check system $1 38 | if loadavg (1min) > 10 then alert 39 | if loadavg (5min) > 7 then alert 40 | if memory usage > 85% then alert 41 | if swap usage > 25% then alert 42 | if cpu usage (user) > 90% then alert 43 | if cpu usage (system) > 60% then alert 44 | if cpu usage (wait) > 50% then alert 45 | group system 46 | EOT 47 | } 48 | 49 | function monit_def_rootfs { 50 | cat </etc/monit/conf.d/rootfs.cfg 51 | check filesystem rootfs with path / 52 | if space usage > 80% for 5 times within 15 cycles then alert 53 | if inode usage > 85% then alert 54 | group system 55 | EOT 56 | } 57 | 58 | function monit_def_cron { 59 | cat </etc/monit/conf.d/cron.cfg 60 | check process cron with pidfile /var/run/crond.pid 61 | start program = "/sbin/start cron" 62 | stop program = "/sbin/stop cron" 63 | if 5 restarts within 5 cycles then timeout 64 | depends on cron_rc 65 | group system 66 | 67 | check file cron_rc with path /etc/init.d/cron 68 | if failed checksum then unmonitor 69 | if failed permission 755 then unmonitor 70 | if failed uid root then unmonitor 71 | if failed gid root then unmonitor 72 | group system 73 | EOT 74 | } 75 | 76 | function monit_def_sshd { 77 | cat </etc/monit/conf.d/sshd.cfg 78 | check process sshd with pidfile /var/run/sshd.pid 79 | start program "/etc/init.d/ssh start" 80 | stop program "/etc/init.d/ssh stop" 81 | # if failed port 22 protocol ssh then restart 82 | # if 3 restarts within 3 cycles then timeout 83 | EOT 84 | } 85 | 86 | function monit_def_ping_google { 87 | cat </etc/monit/conf.d/ping_google.cfg 88 | check host google-ping with address google.com 89 | if failed port 80 proto http then alert 90 | group server 91 | EOT 92 | } 93 | 94 | function monit_def_postfix { 95 | cat </etc/monit/conf.d/postfix.cfg 96 | check process postfix with pidfile /var/spool/postfix/pid/master.pid 97 | start program = "/etc/init.d/postfix start" 98 | stop program = "/etc/init.d/postfix stop" 99 | if cpu > 60% for 2 cycles then alert 100 | if cpu > 80% for 5 cycles then restart 101 | if totalmem > 200.0 MB for 5 cycles then restart 102 | if children > 250 then restart 103 | if loadavg(5min) greater than 10 for 8 cycles then stop 104 | if failed host localhost port 25 protocol smtp with timeout 15 seconds then alert 105 | if failed host localhost port 25 protocol smtp for 3 cycles then restart 106 | if 3 restarts within 5 cycles then timeout 107 | group mail 108 | 109 | check file postfix_rc with path /etc/init.d/postfix 110 | if failed checksum then unmonitor 111 | if failed permission 755 then unmonitor 112 | if failed uid root then unmonitor 113 | if failed gid root then unmonitor 114 | group mail 115 | EOT 116 | } 117 | 118 | 119 | function monit_def_postgresql { 120 | cat </etc/monit/conf.d/postgresql.cfg 121 | check process postgres with pidfile /var/run/postgresql/9.1-main.pid 122 | start program = "/etc/init.d/postgresql start" 123 | stop program = "/etc/init.d/postgresql stop" 124 | if failed unixsocket /var/run/postgresql/.s.PGSQL.5432 protocol pgsql then restart 125 | if failed host localhost port 5432 protocol pgsql then restart 126 | if 5 restarts within 5 cycles then timeout 127 | depends on postgresql_bin 128 | depends on postgresql_rc 129 | group database 130 | 131 | check file postgresql_bin with path /usr/lib/postgresql/9.1/bin/postgres 132 | if failed checksum then unmonitor 133 | if failed permission 755 then unmonitor 134 | if failed uid root then unmonitor 135 | if failed gid root then unmonitor 136 | group database 137 | 138 | check file postgresql_rc with path /etc/init.d/postgresql 139 | if failed checksum then unmonitor 140 | if failed permission 755 then unmonitor 141 | if failed uid root then unmonitor 142 | if failed gid root then unmonitor 143 | group database 144 | 145 | check file postgresql_log with path /var/log/postgresql/postgresql-9.1-main.log 146 | if size > 100 MB then alert 147 | group database 148 | EOT 149 | } 150 | 151 | function monit_def_mysql { 152 | cat < /etc/monit/conf.d/mysql.cfg 153 | check process mysqld with pidfile /var/run/mysqld/mysqld.pid 154 | start program = "/sbin/start mysql" with timeout 20 seconds 155 | stop program = "/sbin/stop mysql" 156 | if failed host localhost port 3306 protocol mysql then restart 157 | if failed unixsocket /var/run/mysqld/mysqld.sock protocol mysql then restart 158 | if 5 restarts within 5 cycles then timeout 159 | depends on mysql_bin 160 | depends on mysql_rc 161 | group database 162 | 163 | check file mysql_bin with path /usr/sbin/mysqld 164 | if failed checksum then unmonitor 165 | if failed permission 755 then unmonitor 166 | if failed uid root then unmonitor 167 | if failed gid root then unmonitor 168 | group database 169 | 170 | check file mysql_rc with path /etc/init.d/mysql 171 | if failed checksum then unmonitor 172 | if failed permission 755 then unmonitor 173 | if failed uid root then unmonitor 174 | if failed gid root then unmonitor 175 | group database 176 | EOT 177 | } 178 | 179 | function monit_def_mongodb { 180 | cat </etc/monit/conf.d/mongodb.cfg 181 | check process mongodb with pidfile /var/lib/mongodb/mongod.lock 182 | start program = "/sbin/start mongodb" 183 | stop program = "/sbin/stop mongodb" 184 | if failed host localhost port 28017 protocol http 185 | and request "/" with timeout 10 seconds then restart 186 | if 5 restarts within 5 cycles then timeout 187 | group database 188 | EOT 189 | } 190 | 191 | function monit_def_memcached { 192 | cat </etc/monit/conf.d/memcached.cfg 193 | check process memcached with pidfile /var/run/memcached.pid 194 | start program = "/etc/init.d/memcached start" 195 | stop program = "/etc/init.d/memcached stop" 196 | if 5 restarts within 5 cycles then timeout 197 | group database 198 | EOT 199 | } 200 | 201 | function monit_def_apache { 202 | cat </etc/monit/conf.d/apache2.cfg 203 | check process apache with pidfile /var/run/apache2.pid 204 | start program = "/etc/init.d/apache2 start" 205 | stop program = "/etc/init.d/apache2 stop" 206 | if cpu > 60% for 2 cycles then alert 207 | if cpu > 80% for 5 cycles then alert 208 | if totalmem > 200.0 MB for 5 cycles then alert 209 | if children > 250 then alert 210 | if loadavg(5min) greater than 10 for 8 cycles then stop 211 | if failed host localhost port 80 protocol HTTP request / within 2 cycles then alert 212 | if failed host localhost port 80 protocol apache-status 213 | dnslimit > 25% or loglimit > 80% or waitlimit < 20% retry 2 within 2 cycles then alert 214 | #if 5 restarts within 5 cycles then timeout 215 | depends on apache_bin 216 | depends on apache_rc 217 | group www 218 | 219 | check file apache_bin with path /usr/sbin/apache2 220 | if failed checksum then unmonitor 221 | if failed permission 755 then unmonitor 222 | if failed uid root then unmonitor 223 | if failed gid root then unmonitor 224 | group www 225 | 226 | check file apache_rc with path /etc/init.d/apache2 227 | if failed checksum then unmonitor 228 | if failed permission 755 then unmonitor 229 | if failed uid root then unmonitor 230 | if failed gid root then unmonitor 231 | group www 232 | EOT 233 | } 234 | -------------------------------------------------------------------------------- /131 - Stack - Security, PostgreSQL, MySQL, MongoDB, Apache, Django.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Installs a complete web environment with Apache, Python, Django and PostgreSQL. 4 | # 5 | # Copyright (c) 2010 Filip Wasilewski . 6 | # 7 | # My ref: http://www.linode.com/?r=aadfce9845055011e00f0c6c9a5c01158c452deb 8 | 9 | # 10 | 11 | # 12 | # 13 | # 14 | # 15 | # 16 | 17 | # 18 | 19 | # 20 | 21 | # 22 | # 23 | # 24 | # 25 | 26 | # 27 | # 28 | # 29 | # 30 | # 31 | 32 | # 33 | 34 | # 35 | 36 | # 37 | # 38 | # 39 | # 40 | 41 | # 42 | # 43 | 44 | set -e 45 | set -u 46 | #set -x 47 | 48 | USER_GROUPS=sudo 49 | 50 | exec &> /root/stackscript.log 51 | 52 | source # StackScript Bash Library 53 | system_update 54 | 55 | source # lib-system 56 | system_install_mercurial 57 | system_start_etc_dir_versioning #start recording changes of /etc config files 58 | 59 | # Configure system 60 | source # lib-system-ubuntu 61 | system_update_hostname "$SYS_HOSTNAME" 62 | system_record_etc_dir_changes "Updated hostname" # SS124 63 | 64 | # Create user account 65 | system_add_user "$USER_NAME" "$USER_PASSWORD" "$USER_GROUPS" "$USER_SHELL" 66 | if [ "$USER_SSHKEY" ]; then 67 | system_user_add_ssh_key "$USER_NAME" "$USER_SSHKEY" 68 | fi 69 | system_record_etc_dir_changes "Added unprivileged user account" # SS124 70 | 71 | # Configure sshd 72 | system_sshd_permitrootlogin "$SSHD_PERMITROOTLOGIN" 73 | system_sshd_passwordauthentication "$SSHD_PASSWORDAUTH" 74 | touch /tmp/restart-ssh 75 | system_record_etc_dir_changes "Configured sshd" # SS124 76 | 77 | # Lock user account if not used for login 78 | if [ "SSHD_PERMITROOTLOGIN" == "No" ]; then 79 | system_lock_user "root" 80 | system_record_etc_dir_changes "Locked root account" # SS124 81 | fi 82 | 83 | # Install Postfix 84 | postfix_install_loopback_only # SS1 85 | system_record_etc_dir_changes "Installed postfix loopback" # SS124 86 | 87 | # Setup logcheck 88 | system_security_logcheck 89 | system_record_etc_dir_changes "Installed logcheck" # SS124 90 | 91 | # Setup fail2ban 92 | system_security_fail2ban 93 | system_record_etc_dir_changes "Installed fail2ban" # SS124 94 | 95 | # Setup firewall 96 | system_security_ufw_configure_basic 97 | system_record_etc_dir_changes "Configured UFW" # SS124 98 | 99 | source # lib-python 100 | python_install 101 | system_record_etc_dir_changes "Installed python" # SS124 102 | 103 | # lib-system - SS124 104 | system_install_utils 105 | system_install_build 106 | system_install_subversion 107 | system_install_git 108 | system_record_etc_dir_changes "Installed common utils" 109 | 110 | # Install and configure apache and mod_wsgi 111 | if [ "$SETUP_APACHE" == "Yes" ]; then 112 | source # lib-apache 113 | apache_worker_install 114 | system_record_etc_dir_changes "Installed apache" # SS124 115 | apache_mod_wsgi_install 116 | system_record_etc_dir_changes "Installed mod-wsgi" # SS124 117 | apache_cleanup 118 | system_record_etc_dir_changes "Cleaned up apache config" # SS124 119 | fi 120 | 121 | # Install PostgreSQL and setup database 122 | if [ "$SETUP_POSTGRESQL" == "Yes" ]; then 123 | source # lib-postgresql 124 | postgresql_install 125 | system_record_etc_dir_changes "Installed PostgreSQL" 126 | postgresql_create_user "$POSTGRESQL_USER" "$POSTGRESQL_PASSWORD" 127 | postgresql_create_database "$POSTGRESQL_DATABASE" "$POSTGRESQL_USER" 128 | system_record_etc_dir_changes "Configured PostgreSQL" 129 | fi 130 | 131 | # Install MySQL and setup database 132 | if [ "$SETUP_MYSQL" == "Yes" ]; then 133 | set +u # ignore undefined variables in Linode's SS1 134 | mysql_install "$MYSQL_DATABASE_PASSWORD" && mysql_tune 30 135 | mysql_create_database "$MYSQL_DATABASE_PASSWORD" "$MYSQL_DATABASE" 136 | mysql_create_user "$MYSQL_DATABASE_PASSWORD" "$MYSQL_USER" "$MYSQL_PASSWORD" 137 | mysql_grant_user "$MYSQL_DATABASE_PASSWORD" "$MYSQL_USER" "$MYSQL_DATABASE" 138 | set -u 139 | system_record_etc_dir_changes "Configured MySQL" 140 | fi 141 | 142 | # Install MongoDB 143 | if [ "$SETUP_MONGODB" == "Yes" ]; then 144 | source # lib-mongodb 145 | mongodb_install 146 | system_record_etc_dir_changes "Installed MongoDB" 147 | fi 148 | 149 | # Setup and configure sample django project 150 | RDNS=$(get_rdns_primary_ip) 151 | DJANGO_PROJECT_PATH="" 152 | 153 | if [ "$SETUP_DJANGO_PROJECT" != "No" ]; then 154 | source # lib-django 155 | 156 | if [ -z "$DJANGO_DOMAIN" ]; then DJANGO_DOMAIN=$RDNS; fi 157 | 158 | case "$SETUP_DJANGO_PROJECT" in 159 | Standalone) 160 | DJANGO_PROJECT_PATH="/srv/$DJANGO_PROJECT_NAME" 161 | if [ -n "$DJANGO_USER" ]; then 162 | if [ "$DJANGO_USER" != "$USER_NAME" ]; then 163 | system_add_system_user "$DJANGO_USER" "$DJANGO_PROJECT_PATH" "$USER_SHELL" 164 | else 165 | mkdir -p "$DJANGO_PROJECT_PATH" 166 | fi 167 | else 168 | DJANGO_USER="www-data" 169 | fi 170 | ;; 171 | InUserHome) 172 | DJANGO_USER=$USER_NAME 173 | DJANGO_PROJECT_PATH=$(system_get_user_home "$USER_NAME")/$DJANGO_PROJECT_NAME 174 | ;; 175 | InUserHomeRoot) 176 | DJANGO_USER=$USER_NAME 177 | DJANGO_PROJECT_PATH=$(system_get_user_home "$USER_NAME") 178 | ;; 179 | esac 180 | 181 | django_create_project "$DJANGO_PROJECT_PATH" 182 | django_change_project_owner "$DJANGO_PROJECT_PATH" "$DJANGO_USER" 183 | 184 | if [ "$SETUP_APACHE" == "Yes" ]; then 185 | django_configure_apache_virtualhost "$DJANGO_DOMAIN" "$DJANGO_PROJECT_PATH" "$DJANGO_USER" 186 | touch /tmp/restart-apache2 187 | fi 188 | if [ "$SETUP_POSTGRESQL" == "Yes" ]; then 189 | django_install_db_driver "$DJANGO_PROJECT_PATH" "psycopg2" 190 | django_configure_db_settings "$DJANGO_PROJECT_PATH" "postgresql_psycopg2" "$POSTGRESQL_DATABASE" "$POSTGRESQL_USER" "$POSTGRESQL_PASSWORD" "127.0.0.1" "" 191 | fi 192 | if [ "$SETUP_MYSQL" == "Yes" ]; then 193 | django_install_db_driver "$DJANGO_PROJECT_PATH" "MySQL-python" 194 | fi 195 | system_record_etc_dir_changes "Configured django project '$DJANGO_PROJECT_NAME'" 196 | fi 197 | 198 | if [ -n "$SYS_PRIVATE_IP" ]; then 199 | system_configure_private_network "$SYS_PRIVATE_IP" 200 | system_record_etc_dir_changes "Configured private network" 201 | fi 202 | 203 | restart_services 204 | restart_initd_services 205 | 206 | if [ "$SETUP_MONIT" == "Yes" ]; then 207 | source # lib-monit 208 | monit_install 209 | system_record_etc_dir_changes "Installed Monit" 210 | 211 | monit_configure_email "$NOTIFY_EMAIL" 212 | monit_configure_web $(system_primary_ip) 213 | system_record_etc_dir_changes "Configured Monit interfaces" 214 | 215 | monit_def_system "$SYS_HOSTNAME" 216 | monit_def_rootfs 217 | monit_def_cron 218 | monit_def_postfix 219 | monit_def_ping_google 220 | if [ "$SETUP_POSTGRESQL" == "Yes" ]; then monit_def_postgresql; fi 221 | if [ "$SETUP_MYSQL" == "Yes" ]; then monit_def_mysql; fi 222 | if [ "$SETUP_MONGODB" == "Yes" ]; then monit_def_mongodb; fi 223 | if [ "$SETUP_APACHE" == "Yes" ]; then monit_def_apache; fi 224 | #if [ "$SETUP_MEMCACHE" == "Yes" ]; then monit_def_memcached; fi 225 | system_record_etc_dir_changes "Created Monit rules for installed services" 226 | monit reload 227 | fi 228 | 229 | # Send info message 230 | cat > ~/setup_message <> ~/setup_message <> ~/setup_message <> ~/setup_message <)", re.IGNORECASE | re.MULTILINE) 31 | return udf_re.findall(content) 32 | 33 | 34 | def parse_param(udf): 35 | element = fromstring(udf) 36 | choices = element.get("oneof") 37 | if choices: 38 | choices = choices.split(",") 39 | return { 40 | 'name': element.get("name"), 41 | 'label': element.get("label"), 42 | 'choices': choices, 43 | 'default': element.get("default"), 44 | 'example': element.get("example") 45 | } 46 | 47 | 48 | def build_argparser(content): 49 | parser = ArgParser( 50 | description='Process StackScript file params.', 51 | fromfile_prefix_chars='@', 52 | formatter_class=argparse.ArgumentDefaultsHelpFormatter, 53 | epilog="Use @settings.txt as param to read configuration from settings file." 54 | ) 55 | parser.add_argument('script', metavar='SCRIPT', nargs=1, help='main script') 56 | 57 | for udf in get_udfs(content): 58 | param = parse_param(udf) 59 | help = param["example"] or "" 60 | default = param["default"] 61 | required = param["default"] is None 62 | parser.add_argument( 63 | '--%s' % param["name"], dest=param["name"].upper(), 64 | action='store', default=default, choices=param["choices"] or None, 65 | required=required, help=help 66 | ) 67 | return parser 68 | 69 | 70 | def lookup_script(script_id): 71 | try: 72 | return glob.glob("%s - *.sh" % script_id)[0] 73 | except IndexError: 74 | raise ValueError("Script for given id does not exist: %s." % script_id) 75 | 76 | 77 | def get_content(script_id): 78 | with open(lookup_script(script_id)) as fp: 79 | return fp.read() 80 | 81 | 82 | def expand_script(content, mode="source"): 83 | assert mode in ["source", "include"] 84 | re_source = re.compile("""""") 85 | re_include = re.compile("""^(\s*source\s+.*)$""", re.MULTILINE) 86 | if mode == "source": 87 | content = re_source.sub( 88 | lambda x: '"%s"' % lookup_script(x.group(1)), 89 | content 90 | ) 91 | else: 92 | content = re_include.sub( 93 | lambda x: '\n#==> Content of %s ==>>\n%s\n#<<== end of %s <=\n' % 94 | (x.group(2), get_content(x.group(2)), x.group(2)), 95 | content 96 | ) 97 | return content 98 | 99 | 100 | def add_variables(content, args): 101 | # Add script variables 102 | variables = "%s\n" % "\n".join( 103 | '%s="%s"' % (k,v) for k,v in sorted(args.__dict__.items()) if k.isupper() 104 | ) 105 | lines = iter(content.splitlines()) 106 | content = [] 107 | for line in lines: 108 | if line.startswith("#") or not line.strip(): 109 | content.append(line) 110 | else: 111 | content.append(variables) 112 | content.append(line) 113 | content.extend(lines) 114 | break 115 | return "\n".join(content) 116 | 117 | 118 | def main(stack_script): 119 | 120 | if stack_script: 121 | with open(stack_script) as fp: 122 | content = fp.read() 123 | 124 | parser = build_argparser(content) 125 | args = parser.parse_args() 126 | 127 | content = expand_script(content, mode="include") 128 | content = add_variables(content, args) 129 | 130 | print content 131 | 132 | 133 | if __name__ == "__main__": 134 | if len(sys.argv) < 2 or not os.path.exists(sys.argv[1]): 135 | print "First argument must be a valid script name" 136 | main(sys.argv[1]) 137 | -------------------------------------------------------------------------------- /settings.txt: -------------------------------------------------------------------------------- 1 | --user_name ubuntu 2 | --user_password pass 3 | --user_sshkey 4 | --sshd_permitrootlogin No 5 | --sshd_passwordauth Yes 6 | --sys_hostname myvps 7 | --base_data_directory /srv 8 | --setup_postgresql Yes 9 | --postgresql_database django 10 | --postgresql_user django 11 | --postgresql_password django 12 | --setup_mysql No 13 | --mysql_database_password 14 | --mysql_database 15 | --mysql_user 16 | --mysql_password 17 | --setup_mongodb Yes 18 | --setup_django_project Yes 19 | --django_domain 20 | --django_project_name my_project 21 | --django_user django 22 | --------------------------------------------------------------------------------