├── .gitignore ├── LICENSE ├── README.md ├── demo.svg └── install.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse 2 | .settings/ 3 | .classpath 4 | .project 5 | 6 | # IntelliJ IDEA 7 | .idea/ 8 | *.iml 9 | 10 | #VSCode 11 | .vscode/* 12 | 13 | # Notepad++ backups # 14 | *.bak 15 | 16 | # Dropbox settings and caches 17 | .dropbox 18 | .dropbox.attr 19 | .dropbox.cache 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # server-for-web 2 | One-time fully automated shell script to install all needed software to run any php framework on Ubuntu 18.04 LTS. Creates user, installs ufw, nginx (or apache), php, nodejs/yarn, MariaDB/MySQL, PostgreSQL, Certbot (Let's Encrypt), Redis, Memcached, Beanstalkd, fail2ban, mosh. Optional parameters available. 3 | 4 |

5 | 6 |

7 | 8 | Beyond the description, here some things that this script does (by default): 9 | - Enables ubuntu auto-upgrade security releases 10 | - Uses apt-fast to speed-up instalation 11 | - CLI tools: [`ncdu`](https://en.wikipedia.org/wiki/Ncdu), [`awscli`](https://aws.amazon.com/cli/), `whois`, [`httpie`](https://httpie.org/), [`mc`](http://linuxcommand.org/lc3_adv_mc.php), [`speedtest`](https://github.com/sivel/speedtest-cli), [`micro`](https://micro-editor.github.io/), [`mosh`](https://mosh.org/) 12 | - Installs and enable zsh with [oh-my-zsh](https://ohmyz.sh/), [pure](https://github.com/sindresorhus/pure), [neofetch](https://github.com/dylanaraps/neofetch) 13 | - Creates swap file to avoid lack of memory 14 | - Auto-generates secure and easy-to-copy passwords 15 | - Installs and enable ufw, and fail2ban 16 | - nginx with better gzip on, or Apache if you prefer. 17 | - Installs php7.4 (with FPM) (and others versions), many popular extensions, composer, [prestissimo](https://github.com/hirak/prestissimo) 18 | - Secure install MariaDB (mysql) and PostgreSQL 19 | - Installs supervisor daemon 20 | - [Certbot](https://certbot.eff.org/) with [DNS plugins](https://certbot.eff.org/docs/using.html#dns-plugins):cloudflare,digitalocean,dnsimple,google,rfc2136,route53 21 | - Generates server ssh key 22 | - Import keys from popular git services (github, bitbucket, gitlab) 23 | 24 | 25 | 26 | >To better choose what to install, check [Parameters](#parameters-all-optional) section 27 | 28 | ### Requisites 29 | - **Ubuntu 18.04 LTS** _(DEPRECATED HERE)_ or **Ubuntu 20.04 LTS _(BETA HERE)_** 30 | - **root**/sudo as current user 31 | - `curl` or `wget` should be installed (unless you clone the repo) 32 | - a **_new server_**. We are not responsible for any loss you may suffer. 33 | - My referral links: [Vultr](https://www.vultr.com/?ref=7205888) - [DigitalOcean](https://m.do.co/c/4361152a43e1) 34 | 35 | > Without a new server, the script possible will ask things to replace files. Never recommended. 36 | 37 | ### Full Installation 38 | 39 | This script is installed by running one of the following commands in your terminal. You can install this via the command-line with either `curl` or `wget`. 40 | 41 | >**_At the end you'll receive a report with all passwords. Keep it safe._** 42 | #### via curl 43 | 44 | ```shell 45 | bash -c "$(curl -fsSL https://git.io/Jv9a6)" 46 | ``` 47 | 48 | #### via wget 49 | 50 | ```shell 51 | bash -c "$(wget -qO- https://git.io/Jv9a6)" 52 | ``` 53 | #### Manual inspection 54 | 55 | It's a good idea to inspect the install script from projects you don't yet know. You can do 56 | that by downloading the install script first, looking through it so everything looks normal, 57 | then running it: 58 | 59 | ```shell 60 | curl -Lo install.sh https://raw.githubusercontent.com/insign/server-for-laravel/master/install.sh 61 | bash install.sh 62 | ``` 63 | 64 | ## Parameters (all optional) 65 | * `-u|--user=` - set new user name. Default: laravel 66 | * `-p|--pass=` - set new user password. Default is _random_ (shown at the end) 67 | * `--name=` - set your name. Default is _DevOps_ 68 | * `--email=` - set your e-mail. Default is _none@none_ 69 | * `--dont-create-new-user` - don't creates a new user (not recommended) 70 | * `--keep-existing-user` - keep existent user if it exists 71 | * `--skip-swap` - skip creation swapfile (not recommended unless already exists) 72 | * `--swap-size` - set swap file size in MB. Default is 2048 (2GB) 73 | * `--skip-updates` - Skip updates and upgrade the system (not recommended) 74 | * `--no-omz` - don't install [oh-my-zsh](https://ohmyz.sh/) framework (not recommended) 75 | * `--no-mosh` - don't install [mosh](https://mosh.org) (ssh alternative) 76 | * `--no-ufw` - don't install or configure UFW firewall (not recommended) 77 | * `--prefer-apache` - Install Apache Server (and don't install or configure nginx) 78 | * `--no-nginx` - don't install or configure nginx 79 | * `--no-php` - don't install or configure php 80 | * `--no-node` - don't install or configure yarn/node/npm 81 | * `--no-mysql` - don't install or configure mysql (MariaDB actually) 82 | * `--my-pass-root=` - set the mysql root password. Default is _random_ (shown at the end) 83 | * `--my-pass-user=` - set the mysql user password. Default is _random_ (shown at the end) 84 | * `--no-postgres` - don't install or configure postgresql 85 | * `--pg-pass=` - set the system user 'postgres' password. Default is _random_ (shown at the end) 86 | * `--pg-pass-root=` - set the pg postgres user password. Default is _random_ (shown at the end) 87 | * `--pg-pass-user=` - set the pg user password. Default is _random_ (shown at the end) 88 | * `--no-supervisor` - don't install or configure supervisor daemon 89 | * `--no-certbot` - don't install or configure certbot (let's encrypt) 90 | * `--no-redis` - don't install or configure redis-server 91 | * `--redis-pass` - set the redis master password. Default is _random_ (shown at the end) 92 | * `--no-memcached` - don't install or configure memcached 93 | * `--no-beanstalkd` - don't install or configure beanstalkd 94 | * `--key-only=` - put here (with quotes) your personal ssh pubkey if you want to disable login using password. _**WARNING**: Be sure to know what you are doing._ 95 | * `--reboot` - reboot the system at the end of the script executation. Normally should **_not_** be used. 96 | * `--human` - If there is a human waiting for the end. Then enters new terminal. 97 | 98 | ## Examples 99 | #### Directly from you computer 100 | ##### Importing your SSH pubkey 101 | ```shell script 102 | ssh root@YOUR.SERVER.IP.HERE "bash -c \"\$(curl -fsSL https://git.io/Jv9a6)\" \"\" --reboot --key-only=\"$(cat ~/.ssh/id_rsa.pub)\"" 103 | ``` 104 | > In the above case, it is safe to use `--reboot` parameter. 105 | ### Web Server 106 | #### with nginx & php 107 | ```shell script 108 | bash -c "$(curl -fsSL https://git.io/Jv9a6)" "" --no-mysql --no-postgres --no-redis --no-memcached --no-beanstalkd 109 | ``` 110 | ### Database Server 111 | > UFW are not configured to allow remote ports to db or cache. You should prefer private networking. 112 | #### with mysql 113 | ```shell script 114 | bash -c "$(curl -fsSL https://git.io/Jv9a6)" "" --no-nginx --no-php --no-postgres --no-node --no-certbot --no-redis --no-memcached --no-beanstalkd 115 | ``` 116 | #### with postgresql 117 | ```shell script 118 | bash -c "$(curl -fsSL https://git.io/Jv9a6)" "" --no-mysql --no-nginx --no-php --no-node --no-certbot --no-redis --no-memcached --no-beanstalkd 119 | ``` 120 | ### Cache Server / Queue Server 121 | ```shell script 122 | bash -c "$(curl -fsSL https://git.io/Jv9a6)" "" --no-mysql --no-nginx --no-php --no-node --no-postgres --no-certbot 123 | ``` 124 | 125 | 126 | ## Roadmap 127 | 128 | - [ ] Add pm2 and support for node sites 129 | - [ ] Adjust composer install to checksum https://getcomposer.org/doc/faqs/how-to-install-composer-programmatically.md 130 | - [ ] Add [qrcp](https://github.com/claudiodangelis/qrcp) 131 | - [X] Add Apache Server as alternative to nginx 132 | - [ ] Configure private network 133 | - [ ] Allow only some IPs via as parameter 134 | - [ ] Disable MySQL log and log bin 135 | - [ ] Fine tune our apps 136 | - [ ] Make the maintenance time random 137 | - [ ] Add mysql as alternative to MariaDB 138 | - [ ] Add colorls 139 | - [ ] Install [`fdfind`](https://github.com/sharkdp/fd), [`fzf`](https://github.com/junegunn/fzf) on 19.04+ 140 | - [ ] Add zsh some plugins by default (sudo, fd, fzf, zsh-interactive-cd, artisan) 141 | - [X] Add insign/server-scripts 142 | - [X] Finish postgresql installation 143 | - [X] Finish Certbot installation 144 | - [X] Finish supervisord installation 145 | - [X] Finish Redis server installation 146 | - [X] Finish Memcached installation 147 | - [X] Finish Beanstalkd installation 148 | - [X] Finish fail2ban installation 149 | - [X] Enable better gzip config for nginx by default 150 | - [X] Import popular git services ssh keys 151 | - [X] Generate ssh key 152 | - [X] Import private key 153 | - [X] Remove password login (ssh key only) 154 | - [X] Support for multiple php versions 155 | - [X] Install mosh as alternative of ssh 156 | - [ ] Send report via e-mail 157 | - [ ] Hide report at the end 158 | - [ ] Run quiet installation with minimum verbosity 159 | - [X] Reboot after done 160 | - [X] Count time passed during installation 161 | - [ ] Add CI for this script. 162 | - [ ] Add docker and docker-compose 163 | - [ ] Add better support for ufw 164 | - [ ] A SPA with command gen 165 | - [ ] Add tests 166 | - [ ] Add notifications via services like telegram, discord, etc 167 | 168 | ## Contributing 169 | You are welcome, just do a PR with some explanation. 170 | 171 | ## License 172 | > Licensed under lgpl-3.0. Check the [GNU GPL3 License](./LICENSE) file for more details. 173 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This script should be run via curl: 4 | # bash -c "$(curl -fsSL https://git.io/Jv9a6)" 5 | # or wget: 6 | # bash -c "$(wget -qO- https://git.io/Jv9a6)" 7 | # 8 | # As an alternative, you can first download the install script and run it afterwards: 9 | # wget https://raw.githubusercontent.com/insign/server-for-web/master/install.sh 10 | # bash install.sh 11 | 12 | set -e 13 | 14 | # Other options 15 | call_vars() { 16 | swapsize=${swapsize:-2048} 17 | KEY_ONLY=${KEY_ONLY:-false} 18 | 19 | name=${name:-DevOps} 20 | email=${user:-"no-one@got"} 21 | 22 | user=${user:-web} 23 | pass=${pass:=$(random_string)} 24 | pg_pass=${pg_pass:=$(random_string)} 25 | 26 | my_pass_root=${my_pass_root:=$(random_string)} 27 | my_pass_user=${my_pass_user:=$(random_string)} 28 | 29 | pg_pass_root=${pg_pass_root:=$(random_string)} 30 | pg_pass_user=${pg_pass_user:=$(random_string)} 31 | 32 | redis_pass=${redis_pass:=$(random_string)} 33 | 34 | start_time=$(date +"%s") 35 | 36 | REPORT='' 37 | } 38 | 39 | add_to_report() { 40 | REPORT="$REPORT""\n""$1" 41 | } 42 | 43 | show_report() { 44 | printTable ',' "$REPORT" 'true' 45 | 46 | warning "Lose this data then go cry to you mom." 47 | } 48 | 49 | random_string() { 50 | # shellcheck disable=SC2001 51 | # shellcheck disable=SC2046 52 | # shellcheck disable=SC2002 53 | sed "s/[^a-zA-Z0-9]//g" <<<$(cat /dev/urandom | tr -dc 'a-zA-Z0-9!@#$%*()+-' | fold -w 32 | head -n 1) 54 | } 55 | 56 | command_exists() { 57 | command -v "$@" >/dev/null 2>&1 58 | } 59 | 60 | install() { 61 | LC_ALL=C.UTF-8 apt-fast install -y "$@" 62 | } 63 | 64 | error() { 65 | echo -e "$RED""Error: $*""$RESET" >&2 66 | exit 1 67 | } 68 | 69 | info() { 70 | echo -e "$GREEN""$BOLD"SERVER FOR WEB:"$RESET $BLUE""$*""$RESET" >&2 71 | } 72 | warning() { 73 | echo -e "$YELLOW""Warning: $*""$RESET" >&2 74 | } 75 | success() { 76 | echo -e "$GREEN""$*""$RESET" >&2 77 | } 78 | 79 | removeEmptyLines() { 80 | echo -e "${1}" | sed '/^\s*$/d' 81 | } 82 | 83 | isEmptyString() { 84 | if [[ "$(trimString "${1}")" == '' ]]; then 85 | echo 'true' && return 0 86 | fi 87 | 88 | echo 'false' && return 1 89 | } 90 | 91 | trimString() { 92 | # shellcheck disable=SC2001 93 | sed 's,^[[:blank:]]*,,' <<<"${1}" | sed 's,[[:blank:]]*$,,' 94 | } 95 | 96 | isPositiveInteger() { 97 | if [[ "${1}" =~ ^[1-9][0-9]*$ ]]; then 98 | echo 'true' && return 0 99 | fi 100 | 101 | echo 'false' && return 1 102 | } 103 | 104 | repeatString() { 105 | local -r string="${1}" 106 | local -r numberToRepeat="${2}" 107 | 108 | if [[ "${string}" != '' && "$(isPositiveInteger "${numberToRepeat}")" == 'true' ]]; then 109 | local -r result="$(printf "%${numberToRepeat}s")" 110 | echo -e "${result// /${string}}" 111 | fi 112 | } 113 | 114 | printTable() { 115 | local -r delimiter="${1}" 116 | local -r tableData="$(removeEmptyLines "${2}")" 117 | local -r colorHeader="${3}" 118 | local -r displayTotalCount="${4}" 119 | 120 | if [[ "${delimiter}" != '' && "$(isEmptyString "${tableData}")" == 'false' ]]; then 121 | local -r numberOfLines="$(trimString "$(wc -l <<<"${tableData}")")" 122 | 123 | if [[ "${numberOfLines}" -gt '0' ]]; then 124 | local table='' 125 | local i=1 126 | 127 | for ((i = 1; i <= "${numberOfLines}"; i = i + 1)); do 128 | local line='' 129 | line="$(sed "${i}q;d" <<<"${tableData}")" 130 | 131 | local numberOfColumns=0 132 | numberOfColumns="$(awk -F "${delimiter}" '{print NF}' <<<"${line}")" 133 | 134 | # Add Line Delimiter 135 | 136 | if [[ "${i}" -eq '1' ]]; then 137 | table="${table}$(printf '%s#+' "$(repeatString '#+' "${numberOfColumns}")")" 138 | fi 139 | 140 | # Add Header Or Body 141 | 142 | table="${table}\n" 143 | 144 | local j=1 145 | 146 | for ((j = 1; j <= "${numberOfColumns}"; j = j + 1)); do 147 | table="${table}$(printf '#| %s' "$(cut -d "${delimiter}" -f "${j}" <<<"${line}")")" 148 | done 149 | 150 | table="${table}#|\n" 151 | 152 | # Add Line Delimiter 153 | 154 | if [[ "${i}" -eq '1' ]] || [[ "${numberOfLines}" -gt '1' && "${i}" -eq "${numberOfLines}" ]]; then 155 | table="${table}$(printf '%s#+' "$(repeatString '#+' "${numberOfColumns}")")" 156 | fi 157 | done 158 | 159 | if [[ "$(isEmptyString "${table}")" == 'false' ]]; then 160 | local output='' 161 | output="$(echo -e "${table}" | column -s '#' -t | awk '/^\+/{gsub(" ", "-", $0)}1')" 162 | 163 | if [[ "${colorHeader}" == 'true' ]]; then 164 | echo -e "\033[1;32m$(head -n 3 <<<"${output}")\033[0m" 165 | tail -n +4 <<<"${output}" 166 | else 167 | echo "${output}" 168 | fi 169 | fi 170 | fi 171 | 172 | if [[ "${displayTotalCount}" == 'true' && "${numberOfLines}" -ge '0' ]]; then 173 | echo -e "\n\033[1;36mTOTAL ROWS : $((numberOfLines - 1))\033[0m" 174 | fi 175 | fi 176 | } 177 | 178 | root_required() { 179 | if [[ $EUID -ne 0 ]]; then 180 | error "This script must be run as root" 181 | fi 182 | } 183 | others_checks() { 184 | root_required 185 | 186 | if [[ ($(lsb_release -rs) != "18.04") && ($(lsb_release -rs) != "20.04") ]]; then 187 | error "This script was tested only on Ubuntu 18.04 LTS or 20.04 LTS" 188 | fi 189 | } 190 | 191 | getDuration() { 192 | end_time=$(date +"%s") 193 | # shellcheck disable=SC2004 194 | local duration=$(($end_time - $start_time)) 195 | local shiff=$duration 196 | local secs=$((shiff % 60)) 197 | shiff=$((shiff / 60)) 198 | local mins=$((shiff % 60)) 199 | shiff=$((shiff / 60)) 200 | local hours=$shiff 201 | local splur 202 | if [ $secs -eq 1 ]; then splur=''; else splur='s'; fi 203 | local mplur 204 | if [ $mins -eq 1 ]; then mplur=''; else mplur='s'; fi 205 | local hplur 206 | if [ $hours -eq 1 ]; then hplur=''; else hplur='s'; fi 207 | if [[ $hours -gt 0 ]]; then 208 | txt="$hours hour$hplur, $mins minute$mplur, $secs second$splur" 209 | elif [[ $mins -gt 0 ]]; then 210 | txt="$mins minute$mplur, $secs second$splur" 211 | else 212 | txt="$secs second$splur" 213 | fi 214 | echo "$txt" 215 | } 216 | 217 | step_initial() { 218 | export DEBIAN_FRONTEND=noninteractive 219 | ln -sf /usr/share/zoneinfo/UTC /etc/localtime 220 | 221 | if [ "$SKIP_SWAP" != "true" ]; then 222 | info "Creating swapfile of $swapsize mb..." 223 | if [[ $(swapon --show) ]]; then 224 | swapoff /swapfile 225 | rm /swapfile 226 | fi 227 | dd if=/dev/zero of=/swapfile bs=1M count="$swapsize" 228 | chmod 600 /swapfile 229 | mkswap /swapfile 230 | swapon /swapfile 231 | swapon -s # status 232 | echo '/swapfile none swap sw 0 0' | tee -a /etc/fstab 233 | fi 234 | 235 | info Essential Updates and Upgrades... 236 | 237 | export LC_ALL=en_US.UTF-8 238 | export LANG=en_US.UTF-8 239 | 240 | add-apt-repository -yn ppa:apt-fast/stable 241 | 242 | add-apt-repository -yn universe 243 | 244 | # yarn 245 | curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | sudo tee /usr/share/keyrings/yarnkey.gpg >/dev/null 246 | echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian stable main" | sudo tee /etc/apt/sources.list.d/yarn.list 247 | 248 | # node / npm (which do apt update too) 249 | curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash - 250 | 251 | echo debconf apt-fast/maxdownloads string 16 | debconf-set-selections 252 | echo debconf apt-fast/dlflag boolean true | debconf-set-selections 253 | echo debconf apt-fast/aptmanager string apt | debconf-set-selections 254 | apt -y install apt-fast 255 | 256 | info Installing zsh and other basics... 257 | 258 | if [ "$SKIP_UPDATES" != "true" ]; then 259 | info Upgrading system... 260 | 261 | apt-fast upgrade -y 262 | fi 263 | 264 | install locales language-pack-en-base software-properties-common build-essential micro zsh net-tools git curl wget zip unzip expect fail2ban xclip whois awscli httpie mc p7zip-full htop neofetch python3-pip ruby ruby-dev ruby-colorize speedtest-cli 265 | gem install colorls 266 | 267 | git config --global user.name "$name" 268 | git config --global user.email "$email" 269 | 270 | add_to_report 'TYPE,USER,PASSWORD' 271 | } 272 | 273 | step_user_creation() { 274 | add_to_report "System,root,(untouched)" 275 | if [ "$CREATE_NEW_USER" != "false" ]; then 276 | if [ "$(getent passwd "$user")" ]; then 277 | if [ "$KEEP_EXISTING_USER" != "true" ]; then 278 | info Deleting current user: "$GREEN$BOLD$user$RESET" 279 | userdel -r "$user" 280 | success Deleted. 281 | else 282 | error user already exists, remove --keep-existing-user or choose another: "$GREEN""$BOLD""$user""$RESET" 283 | fi 284 | fi 285 | 286 | useradd "$user" --create-home --password "$(openssl passwd -1 "$pass")" --shell "$(which zsh)" 287 | usermod -aG sudo "$user" # append to sudo and user group 288 | success User created: "$BLUE""$BOLD""$user" 289 | add_to_report "System,$RED$BOLD$user$RESET,$RED$BOLD$pass$RESET" 290 | 291 | eval local -r user_home="~$user" 292 | # shellcheck disable=SC2154 293 | # shellcheck disable=SC2174 294 | mkdir -p "$user_home/.ssh/" -m 755 295 | 296 | chown -R "$user:$user" "$user_home" 297 | 298 | runuser -l "$user" -c "ssh-keygen -f ~$user/.ssh/id_rsa -t rsa -N ''" 299 | 300 | if [ "$KEY_ONLY" != "false" ]; then 301 | sed -i "/PasswordAuthentication.+/d" /etc/ssh/sshd_config 302 | sed -i "/PubkeyAuthentication.+/d" /etc/ssh/sshd_config 303 | echo "" | sudo tee -a /etc/ssh/sshd_config 304 | echo "" | sudo tee -a /etc/ssh/sshd_config 305 | echo "PasswordAuthentication no" | sudo tee -a /etc/ssh/sshd_config 306 | echo "PubkeyAuthentication yes" | sudo tee -a /etc/ssh/sshd_config 307 | 308 | echo -e "\n# User provided key\n${KEY_ONLY}\n\n" | tee -a ~root/.ssh/authorized_keys "$user_home/.ssh/authorized_keys" >/dev/null 309 | fi 310 | 311 | ( 312 | ssh-keyscan -H github.com 313 | ssh-keyscan -H bitbucket.org 314 | ssh-keyscan -H gitlab.com 315 | ) >>"$user_home/.ssh/known_hosts" 316 | 317 | chown -R "$user:$user" "$user_home" 318 | chmod -R 755 "$user_home" 319 | chmod 700 "$user_home/.ssh/id_rsa" 320 | 321 | fi 322 | } 323 | 324 | step_ufw() { 325 | if [ "$NO_UFW" != "true" ]; then 326 | # TODO allow add ips via command 327 | install ufw 328 | ufw --force reset 329 | ufw disable 330 | ufw default deny incoming 331 | ufw default allow outgoing 332 | ufw allow ssh 333 | ufw allow 22/tcp 334 | ufw allow 80/tcp 335 | ufw allow 443/tcp 336 | 337 | ufw --force enable 338 | 339 | ufw logging on 340 | 341 | ufw status 342 | fi 343 | } 344 | 345 | step_webserver() { 346 | if [ "$PREFER_APACHE" == "true" ]; then 347 | install apache2 348 | 349 | if [ "$NO_PHP" != "true" ]; then 350 | install libapache2-mod-php 351 | fi 352 | 353 | if command_exists ufw; then 354 | ufw allow 'Apache Full' 355 | fi 356 | else 357 | if [ "$NO_NGINX" != "true" ]; then 358 | install nginx 359 | 360 | if command_exists ufw; then 361 | ufw allow 'Nginx Full' 362 | fi 363 | 364 | cat >/etc/nginx/conf.d/gzip.conf </etc/nginx/sites-available/catch-all </etc/sudoers.d/php-fpm 415 | ( 416 | echo "$user ALL=NOPASSWD: /usr/sbin/service php7.4-fpm reload" 417 | echo "$user ALL=NOPASSWD: /usr/sbin/service php7.3-fpm reload" 418 | echo "$user ALL=NOPASSWD: /usr/sbin/service php7.2-fpm reload" 419 | echo "$user ALL=NOPASSWD: /usr/sbin/service php7.2-fpm reload" 420 | echo "$user ALL=NOPASSWD: /usr/sbin/service php7.1-fpm reload" 421 | echo "$user ALL=NOPASSWD: /usr/sbin/service php7.0-fpm reload" 422 | echo "$user ALL=NOPASSWD: /usr/sbin/service php5.6-fpm reload" 423 | echo "$user ALL=NOPASSWD: /usr/sbin/service php5-fpm reload" 424 | ) >>/etc/sudoers.d/php-fpm 425 | 426 | install php-{common,cli,fpm,bcmath,pear,curl,dev,gd,mbstring,zip,mysql,xml,soap,imagick,sqlite3,intl,readline,imap,pgsql,tokenizer,redis,memcached} 427 | install php 428 | 429 | sed -i "s/error_reporting = .*/error_reporting = E_ALL/" /etc/php/8.0/cli/php.ini 430 | sed -i "s/display_errors = .*/display_errors = On/" /etc/php/8.0/cli/php.ini 431 | sed -i "s/memory_limit = .*/memory_limit = 512M/" /etc/php/8.0/cli/php.ini 432 | sed -i "s/;date.timezone.*/date.timezone = UTC/" /etc/php/8.0/cli/php.ini 433 | 434 | sed -i "s/error_reporting = .*/error_reporting = E_ALL/" /etc/php/8.0/fpm/php.ini 435 | sed -i "s/display_errors = .*/display_errors = On/" /etc/php/8.0/fpm/php.ini 436 | sed -i "s/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/" /etc/php/8.0/fpm/php.ini 437 | sed -i "s/memory_limit = .*/memory_limit = 512M/" /etc/php/8.0/fpm/php.ini 438 | sed -i "s/;date.timezone.*/date.timezone = UTC/" /etc/php/8.0/fpm/php.ini 439 | 440 | sed -i "s/^user = www-data/user = $user/" /etc/php/8.0/fpm/pool.d/www.conf 441 | sed -i "s/^group = www-data/group = $user/" /etc/php/8.0/fpm/pool.d/www.conf 442 | sed -i "s/;listen\.owner.*/listen.owner = $user/" /etc/php/8.0/fpm/pool.d/www.conf 443 | sed -i "s/;listen\.group.*/listen.group = $user/" /etc/php/8.0/fpm/pool.d/www.conf 444 | sed -i "s/;listen\.mode.*/listen.mode = 0666/" /etc/php/8.0/fpm/pool.d/www.conf 445 | sed -i "s/;request_terminate_timeout.*/request_terminate_timeout = 60/" /etc/php/8.0/fpm/pool.d/www.conf 446 | 447 | chmod 733 /var/lib/php/sessions 448 | chmod +t /var/lib/php/sessions 449 | 450 | curl -sS https://getcomposer.org/installer | php 451 | mv composer.phar /usr/local/bin/composer 452 | 453 | service php8.0-fpm restart 454 | fi 455 | } 456 | step_node() { 457 | # yarn with node and npm 458 | if [ "$NO_NODE" != "true" ]; then 459 | install yarn nodejs 460 | yarn global add gulp pm2 pure-prompt 461 | fi 462 | } 463 | step_mysql() { 464 | if [ "$NO_MYSQL" != "true" ]; then 465 | debconf-set-selections <<<"mariadb-server-5.5 mysql-server/root_password password $my_pass_root" 466 | debconf-set-selections <<<"mariadb-server-5.5 mysql-server/root_password_again password $my_pass_root" 467 | install mariadb-server 468 | echo -e "[mariadb]\ndefault_password_lifetime = 0" >>/etc/mysql/mariadb.conf.d/mariadb.cnf 469 | ( 470 | echo '' 471 | echo "[mysqld]" 472 | echo "default_authentication_plugin=mysql_native_password" 473 | ) >>/etc/mysql/my.cnf 474 | sed -i '/^bind-address/s/bind-address.*=.*/bind-address = */' /etc/mysql/my.cnf 475 | 476 | local -r RAM=$(awk '/^MemTotal:/{printf "%3.0f", $2 / (1024 * 1024)}' /proc/meminfo) 477 | # shellcheck disable=SC2004 478 | local -r MAX_CONNECTIONS=$((70 * $RAM)) 479 | local -r REAL_MAX_CONNECTIONS=$((MAX_CONNECTIONS > 70 ? MAX_CONNECTIONS : 100)) 480 | sed -i "s/^max_connections.*=.*/max_connections=${REAL_MAX_CONNECTIONS}/" /etc/mysql/my.cnf 481 | 482 | expect -c " 483 | set timeout 3 484 | spawn mysql_secure_installation 485 | 486 | expect \"Enter current password for root (enter for none):\" 487 | send -- \"${my_pass_root}\r\" 488 | expect \"Switch to unix_socket authentication\" 489 | send -- \"n\r\" 490 | expect \"Set root password?\" 491 | send -- \"n\r\" 492 | expect \"Remove anonymous users?\" 493 | send -- \"Y\r\" 494 | expect \"Disallow root login remotely?\" 495 | send -- \"Y\r\" 496 | expect \"Remove test database and access to it?\" 497 | send -- \"Y\r\" 498 | expect \"Reload privilege tables now?\" 499 | send -- \"Y\r\" 500 | expect eof 501 | " 502 | 503 | add_to_report "MariaDB,$RED${BOLD}root$RESET,$RED$BOLD$my_pass_root$RESET" 504 | 505 | mysql -uroot -p"$my_pass_root" <<<"CREATE USER 'root'@'%' IDENTIFIED BY '$my_pass_root';" >/dev/null 2>&1 506 | 507 | local -r MY_USER_EXISTS="$(mysql -uroot -p"$my_pass_root" -sse "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$user')")" 508 | if [ "$MY_USER_EXISTS" = 1 ]; then 509 | mysql -uroot -p"$my_pass_root" <<<"ALTER USER '${user}'@'%' IDENTIFIED BY '${my_pass_user}';" 510 | else 511 | mysql -uroot -p"$my_pass_root" <<<"CREATE USER '${user}'@'%' IDENTIFIED BY '${my_pass_user}';" 512 | fi 513 | mysql -uroot -p"$my_pass_root" <<<"GRANT ALL PRIVILEGES ON *.* TO root@'%' WITH GRANT OPTION;" 514 | mysql -uroot -p"$my_pass_root" <<<"GRANT ALL PRIVILEGES ON *.* TO ${user}@'%' WITH GRANT OPTION;" 515 | mysql -uroot -p"$my_pass_root" <<<"FLUSH PRIVILEGES;" 516 | 517 | mysql -uroot -p"$my_pass_root" <<<"CREATE DATABASE IF NOT EXISTS ${user} CHARACTER SET utf8 COLLATE utf8_unicode_ci;" 518 | 519 | add_to_report "MariaDB,$RED${BOLD}${user}$RESET,$RED$BOLD${my_pass_user}$RESET" 520 | fi 521 | } 522 | step_postgres() { 523 | if [ "$NO_POSTGRES" != "true" ]; then 524 | install postgresql postgresql-contrib 525 | local -r pg_version=$(psql --version 2>&1 | tail -1 | awk '{print $3}' | sed 's/\./ /g' | awk '{print $1}') 526 | pg_ctlcluster "$pg_version" main start 527 | 528 | runuser -l postgres -c "psql -c \"CREATE ROLE ${user} CREATEDB CREATEROLE\"" 529 | runuser -l postgres -c "psql -c \"ALTER USER ${user} PASSWORD '${pg_pass_user}';\"" 530 | runuser -l postgres -c "psql -c \"ALTER USER postgres PASSWORD '${pg_pass_root}';\"" 531 | 532 | usermod -p "$(openssl passwd -1 "$pg_pass")" postgres 533 | 534 | add_to_report "System,$RED${BOLD}postgres$RESET,$RED$BOLD${pg_pass}$RESET" 535 | add_to_report "PostgreSQL,$RED${BOLD}postgres$RESET,$RED$BOLD${pg_pass_root}$RESET" 536 | add_to_report "PostgreSQL,$RED${BOLD}${user}$RESET,$RED$BOLD${pg_pass_user}$RESET" 537 | 538 | fi 539 | } 540 | step_supervisor() { 541 | if [ "$NO_SUPERVISOR" != "true" ]; then 542 | install supervisor 543 | service supervisor restart 544 | fi 545 | } 546 | 547 | step_certbot() { 548 | if [ "$NO_CERTBOT" != "true" ]; then 549 | install certbot python3-certbot-* 550 | fi 551 | } 552 | 553 | step_redis() { 554 | if [ "$NO_REDIS" != "true" ]; then 555 | install redis-server 556 | 557 | sed -i 's/bind 127.0.0.1/bind 0.0.0.0/' /etc/redis/redis.conf 558 | echo "requirepass $redis_pass" >>/etc/redis/redis.conf 559 | add_to_report "Redis,(none),$RED$BOLD$redis_pass$RESET" 560 | service redis-server restart 561 | systemctl enable redis-server 562 | fi 563 | } 564 | 565 | step_memcached() { 566 | if [ "$NO_MEMCACHED" != "true" ]; then 567 | install memcached libmemcached-tools 568 | sed -i 's/-l 127.0.0.1/-l 0.0.0.0/' /etc/memcached.conf 569 | service memcached restart 570 | fi 571 | } 572 | 573 | step_beanstalkd() { 574 | if [ "$NO_BEANSTALKD" != "true" ]; then 575 | install beanstalkd 576 | sed -i "s/BEANSTALKD_LISTEN_ADDR.*/BEANSTALKD_LISTEN_ADDR=0.0.0.0/" /etc/default/beanstalkd 577 | service beanstalkd restart 578 | fi 579 | } 580 | 581 | step_final() { 582 | if [ "$NO_OMZ" != "true" ]; then 583 | # shellcheck disable=SC2016 584 | runuser -l "$user" -c 'sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended' 585 | runuser -l "$user" -c "chsh --shell $(which zsh)" 586 | fi 587 | 588 | if command_exists yarn; then 589 | yarn global add pure-prompt 590 | runuser -l "$user" -c "echo 'autoload -U promptinit; promptinit' >> ~/.zshrc" 591 | runuser -l "$user" -c "echo 'prompt pure' >> ~/.zshrc" 592 | fi 593 | 594 | runuser -l "$user" -c $'echo \'export PATH="$PATH:$HOME/.composer/vendor/bin"\' >> ~/.zshrc' 595 | runuser -l "$user" -c $'echo \'export PATH="$PATH:$HOME/.config/composer/vendor/bin"\' >> ~/.zshrc' 596 | runuser -l "$user" -c $'echo \'export PATH="$PATH:$HOME/.yarn/bin"\' >> ~/.zshrc' 597 | runuser -l "$user" -c "echo 'neofetch' >> ~/.zshrc" 598 | 599 | if [ "$NO_MOSH" != "true" ]; then 600 | install mosh 601 | if command_exists ufw; then 602 | ufw allow 60000:61000/udp 603 | fi 604 | fi 605 | 606 | apt purge -y expect 607 | apt autoremove -y 608 | 609 | # Auto upgrade security 610 | cat >>/etc/apt/apt.conf.d/50unattended-upgrades </etc/apt/apt.conf.d/10periodic <&2 809 | exit 1 810 | ;; 811 | *) 812 | handle_argument "$1" 813 | shift 1 814 | ;; 815 | esac 816 | done 817 | } 818 | 819 | main() { 820 | 821 | setup_color 822 | call_vars 823 | others_checks 824 | 825 | parse_arguments "$@" 826 | 827 | info "Initial actions...." 828 | step_initial 829 | 830 | info "Creating user" 831 | step_user_creation 832 | 833 | info "Installing UFW" 834 | step_ufw 835 | 836 | info "Installing nginx (or Apache if you prefered)" 837 | step_webserver 838 | 839 | info "Installing php 8.0" 840 | step_php 841 | 842 | info "Installing node 14 LTS" 843 | step_node 844 | 845 | info "Installing MariaDB 10.4" 846 | step_mysql # Actually, it's MariaDB 847 | 848 | info "Installing PostgreSQL" 849 | step_postgres 850 | 851 | info "Installing supervisor daemon" 852 | step_supervisor 853 | 854 | info "Installing certbot" 855 | step_certbot 856 | 857 | info "Installing Redis" 858 | step_redis 859 | 860 | info "Installing Memcached" 861 | step_memcached 862 | 863 | info "Installing beanstalkd" 864 | step_beanstalkd 865 | 866 | info "Finishing up" 867 | step_final 868 | } 869 | 870 | main "$@" 871 | --------------------------------------------------------------------------------