-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstartup.sh
509 lines (413 loc) · 14.2 KB
/
startup.sh
1
2
3
4
5
6
7
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
#!/bin/sh
#===============================================================================
#
# Initialization script for the Q2A web server, to be deployed on AWS.
#
# Deployment Requirements:
# AWS RDS MySQL must be instantiated
# Ubuntu 20.04 or newer
# Custom domain name must be claimed
#
#===============================================================================
# Paths to app configuration files that will be modified during startup
CONFIG_PATH=''
COMPOSE_PATH=''
# Default values that will be replaced in the above files
RDS_MASTER_USERNAME='RDS_MASTER_USERNAME'
RDS_MASTER_PASSWORD='RDS_MASTER_PASSWORD'
RDS_DB_NAME='RDS_DB_NAME'
RDS_ENDPOINT='RDS_ENDPOINT'
# Values to be read from the user
QA_MYSQL_HOSTNAME=''
QA_MYSQL_USERNAME=''
QA_MYSQL_PASSWORD=''
QA_MYSQL_DATABASE=''
# SSL Certification information
ADMIN_EMAIL=$ADMIN_EMAIL_ADDRESS
DOMAIN=$DOMAIN_NAME
WEBROOT=$(realpath public)
#===============================================================================
#
# Installs any docker/git dependencies on this OS needed for the server to run.
#
# Currently only installs for debian-based distros, as VM is Ubuntu.
#
#===============================================================================
install_dependencies() {
echo
echo 'Installing runtime dependencies...'
# Only install if necessary
dpkg -l | grep docker > /dev/null 2>&1
if [ $? -eq 0 ];
then
echo 'Docker already detected on system. Skipping installation.'
else
# Follows the steps outlined on: https://docs.docker.com/engine/install/ubuntu/
# Pre-install setup
sudo apt-get update -y
sudo apt-get install -y ca-certificates curl gnupg lsb-release
# Adding GPG key
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor --yes -o /etc/apt/keyrings/docker.gpg
# Set up repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Refresh & install Docker packages
sudo apt-get update -y
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
fi
# Add user to docker group if needed
grep 'docker' /etc/group | grep $USER > /dev/null 2>&1
if [ $? -eq 0 ];
then
echo "User $USER already has docker permissions."
else
# Allow non-root users to use Docker (requires logout/login)
sudo groupadd docker
sudo usermod -aG docker $USER
fi
# Start the docker process on boot
sudo systemctl enable docker.service
sudo systemctl enable containerd.service
echo 'Docker installation complete'
}
#===============================================================================
#
# Perform any necessary post-install cleanup
#
#===============================================================================
cleanup() {
# Remove package lists
sudo rm -rf /var/lib/apt/lists/*
# This script is copied to /var/lib/cloud/instance/user-data.txt upon
# launching the instance, so it can safely be removed.
if [ -s ./ec2_user_data.sh ];
then
echo "Deleting EC2 User Data script"
rm ./ec2_user_data.sh
fi
}
#===============================================================================
#
# Locates the configuration files for setting up the Q2A site and database.
#
# These files will get modified to hold the user-inputted credentials for the
# MySQL database.
#
# Global variables used:
# COMPOSE_PATH
# CONFIG_PATH
#
#===============================================================================
locate_config_files() {
echo
echo 'Locating configuration files...'
CONFIG_PATH=$(realpath $(find . -type f -name "qa-config-secure.php"))
COMPOSE_PATH=$(realpath $(find . -type f -name "docker-compose.yml"))
echo "Configuration files found:"
echo " $CONFIG_PATH"
echo " $COMPOSE_PATH"
}
#===============================================================================
#
# Checks if the credentials have already been set, prompting the user if not.
#
# Global variables used:
# CONFIG_PATH
# RDS_MASTER_USERNAME
# RDS_MASTER_PASSWORD
# RDS_DB_NAME
# RDS_ENDPOINT
# QA_MYSQL_HOSTNAME
# QA_MYSQL_USERNAME
# QA_MYSQL_PASSWORD
# QA_MYSQL_DATABASE
#
#===============================================================================
check_credentials() {
echo
echo 'Checking if MySQL credentials have been set...'
# Set the username, if needed
if grep -q $RDS_MASTER_USERNAME $CONFIG_PATH;
then
read -p "Provide the username for the RDS MySQL master account > " QA_MYSQL_USERNAME
else
echo '- Username already defined'
fi
# Set the database password, if needed
if grep -q $RDS_MASTER_PASSWORD $CONFIG_PATH;
then
stty -echo
read -p "Provide the password for the RDS MySQL master account > " QA_MYSQL_PASSWORD
stty echo
echo
else
echo '- Password already defined'
fi
# Set the database hostname, if needed
if grep -q $RDS_ENDPOINT $CONFIG_PATH;
then
read -p "Provide the endpoint/URL of RDS MySQL database to connect to > " QA_MYSQL_HOSTNAME
else
echo '- Hostname already defined'
fi
# Set the database name, if needed
if grep -q $RDS_DB_NAME $CONFIG_PATH;
then
read -p "Provide the name of the RDS MySQL database (Probably already defined in AWS) > " QA_MYSQL_DATABASE
else
echo '- Database name already defined'
fi
# Reload the environment variables
. /etc/environment
# If the environment variables are not set, get values for them (but don't set yet)
# Otherwise, just fetch their values for local usage
if [ -z $DOMAIN_NAME ]; then
read -p "Enter the domain name of the site to be hosted > " DOMAIN
else
echo "- Domain name already set to $DOMAIN_NAME"
echo " To change this, modify /etc/environment"
DOMAIN=$DOMAIN_NAME
fi
if [ -z $ADMIN_EMAIL_ADDRESS ]; then
read -p "Enter an email address to utilize as the webmaster/administrator (for contact regarding SSL expiration) > " ADMIN_EMAIL
else
echo "- Administrator email already set to $ADMIN_EMAIL_ADDRESS"
echo " To change this, modify /etc/environment"
ADMIN_EMAIL=$ADMIN_EMAIL_ADDRESS
fi
}
#===============================================================================
#
# Sets the MySQL credentials provided by the user.
#
# Global variables used:
# CONFIG_PATH
# RDS_MASTER_USERNAME
# RDS_MASTER_PASSWORD
# RDS_DB_NAME
# RDS_ENDPOINT
# QA_MYSQL_HOSTNAME
# QA_MYSQL_USERNAME
# QA_MYSQL_PASSWORD
# QA_MYSQL_DATABASE
#
#===============================================================================
set_credentials() {
echo
echo 'Setting MySQL credentials...'
# Set the standard account's username
sed -i "s/$RDS_MASTER_USERNAME/$QA_MYSQL_USERNAME/" $CONFIG_PATH
# Set the standard account's password
sed -i "s/$RDS_MASTER_PASSWORD/$QA_MYSQL_PASSWORD/" $CONFIG_PATH
# Set the hostname of the database (name of the DB container)
sed -i "s/$RDS_ENDPOINT/$QA_MYSQL_HOSTNAME/" $CONFIG_PATH
# Set the database's name
sed -i "s/$RDS_DB_NAME/$QA_MYSQL_DATABASE/" $CONFIG_PATH
# Set the web server's domain name and admin email environment variables
if [ -z $DOMAIN_NAME ] || [ -z $ADMIN_EMAIL_ADDRESS ]; then
export ADMIN_EMAIL_ADDRESS=$ADMIN_EMAIL
export DOMAIN_NAME=$DOMAIN
echo "PATH=\"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin\"
export ADMIN_EMAIL_ADDRESS=$ADMIN_EMAIL
export DOMAIN_NAME=$DOMAIN" | sudo tee /etc/environment
fi
echo "MySQL credentials set"
}
#===============================================================================
#
# Sets up the auto-start script to automatically restart the docker containers
# whenever the EC2 instance is restarted.
#
# Note that this doesn't effect machines not hosted on AWS EC2.
#
# Global variables used:
# COMPOSE_PATH
#
#===============================================================================
enable_autolaunch() {
echo
echo 'Setting containers to launch automatically on system start...'
startserver='/var/lib/cloud/scripts/per-boot/startserver.sh'
# Re-run this startup script
#sudo echo "#!/bin/sh
#sh $(realpath $0)" | sudo tee $startserver
# In case we just want to re-start the containers, uncomment this
echo "#!/bin/sh
docker compose -f $COMPOSE_PATH up -d" | sudo tee $startserver
# Mark as executable
sudo chmod a+x $startserver
echo 'Autostart policy set'
}
#===============================================================================
#
# Launches the web server through docker compose.
#
# Global variables used:
# COMPOSE_PATH
#
#===============================================================================
launch_service() {
echo
echo 'Launching website service via docker...'
docker compose -f $COMPOSE_PATH up -d
echo 'Docker containers launched'
}
#===============================================================================
#
# Performs the SSL certification process on the web server.
#
# The certification process uses Certbot (certbot.eff.org) and stores the cert
# files in `/etc/letsencrypt/live/<DOMAIN_NAME>/``
#
# Global variables used:
# SSL_EMAIL
# DOMAIN_NAME
# WEBROOT
#
#===============================================================================
generate_ssl() {
echo
echo 'Establishing SSL certification...'
# Install certbot and packages, if necessary
sudo apt-get install -y certbot
#============================================
#
# ***IMPORTANT***
#
# This line is for DEVELOPMENT ONLY. Notice
# the `--dry-run` flag? Removing that will
# run in production mode. Production mode is
# RATE LIMITED! Don't use unless you need to!
#
#============================================
# sudo certbot certonly --dry-run \
sudo certbot certonly \
--non-interactive \
--agree-tos \
--expand \
-m $ADMIN_EMAIL \
--webroot -w $WEBROOT \
-d $DOMAIN_NAME \
-d www.$DOMAIN_NAME
echo 'SSL certification complete'
}
#===============================================================================
#
# Copies SSL certs into ./local_certs as a backup.
#
# Also makes copies labeled for Portainer.
#
# Global variables used:
# DOMAIN_NAME
#
#===============================================================================
init_ssl() {
echo
echo 'Configuring and backing up SSL certificates...'
# Make a backup directory for the SSL certs
sudo mkdir ./local_certs > /dev/null 2>&1
sudo cp -Lr /etc/letsencrypt/live/$DOMAIN_NAME/ ./local_certs/
# Copy the appropriate certs for Portainer
sudo mkdir /certs/
sudo cp -L ./local_certs/cert.pem /certs/portainer.crt
sudo cp -L ./local_certs/privkey.pem /certs/portainer.key
}
#===============================================================================
#
# Builds and launches a HTTP docker container of the web server.
#
# This is used for generating SSL certificates and nothing more. The container
# automatically deletes itself after stopping.
#
# Global variables used:
# WEBROOT
#
#===============================================================================
launch_http() {
echo
echo 'Launching basic HTTP web service...'
# The name of this container doesn't really matter
container_name='apache-no-ssl'
# First, build the image with no SSL support
docker build -t q2a-apache-no-ssl -f $WEBROOT/DockerfileNoSSL $WEBROOT
# If the container is already running, stop it
docker stop $container_name 2> /dev/null
# Now run the no-ssl container and delete it afterwards
docker run -d --rm --name $container_name \
-v "$WEBROOT:/var/www/html" \
-v "$(pwd)/config:/var/www/config" \
-p "80:80" -p "443:443" \
q2a-apache-no-ssl
docker ps
echo 'Launched HTTP web service'
}
#===============================================================================
#
# Stops and deletes ALL running containers
#
#===============================================================================
kill_containers() {
echo
echo 'Stopping and removing all containers...'
docker stop $(docker ps -aq) 2> /dev/null
docker rm $(docker ps -aq) 2> /dev/null
echo 'Containers removed'
}
#===============================================================================
#
# Builds and launches a HTTPS docker container of the web server.
#
# This web service automatically redirects all traffic to HTTPS.
#
# Global variables used:
# COMPOSE_PATH
#
#===============================================================================
launch_https() {
echo
echo 'Launching HTTPS web service...'
docker compose -f $COMPOSE_PATH up -d
echo 'Launched HTTPS web service'
}
#===============================================================================
#
# Displays status of docker containers after launch.
#
#===============================================================================
display_status() {
echo
echo 'All docker containers containers:'
docker ps -a 2> /dev/null
}
#===============================================================================
#
# Entrypoint of this startup script.
#
# Installs dependencies, fetches credentials, generates SSL certificates, and
# launches an autostarting web service via Docker.
#
# Global variables used:
# DOMAIN_NAME
#
#===============================================================================
main() {
# Program execution
install_dependencies
locate_config_files
check_credentials
set_credentials
enable_autolaunch
kill_containers
# If SSL certs have not yet been generated, do so
if [ ! -d /etc/letsencrypt/live/$DOMAIN_NAME ]; then
launch_http
generate_ssl
init_ssl
kill_containers
fi
# Now launch the web server with HTTPS
launch_https
#cleanup
display_status
}
main