├── .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 |
--------------------------------------------------------------------------------