501 lines
13 KiB
Bash
501 lines
13 KiB
Bash
|
#!/bin/bash
|
||
|
|
||
|
#############################################################
|
||
|
# Author: Taryel Hlontsi, 2023
|
||
|
# This program is free software: you can redistribute it and/or modify
|
||
|
# it under the terms of the GNU General Public License as published by
|
||
|
# the Free Software Foundation, either version 3 of the License, or
|
||
|
# (at your option) any later version.
|
||
|
#
|
||
|
# This program is distributed in the hope that it will be useful,
|
||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
# GNU General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU General Public License
|
||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>
|
||
|
#############################################################
|
||
|
|
||
|
|
||
|
#############################################################
|
||
|
# SETUP SECTION, use "sudo" to run the script
|
||
|
#############################################################
|
||
|
# if set to true, ssh login only with ssh key which must(!) be configured upfront
|
||
|
NO_PASSWORD_SSH=true
|
||
|
|
||
|
# user for gitea, and DB password that should be entered on the initialization web page
|
||
|
HOST_USER='git' # better don't change it
|
||
|
DB_PASSWORD='PasswordGoesHere'
|
||
|
|
||
|
GITEA_URL='https://dl.gitea.com/gitea/1.19.3/gitea-1.19.3-linux-arm64'
|
||
|
|
||
|
# change the IP. If domain is used then set it here as the server name
|
||
|
OS_SERVER_NAME="gitea.local"
|
||
|
OS_IP="192.168.0.10"
|
||
|
OS_COUNTRY="PL"
|
||
|
|
||
|
# may be left as is
|
||
|
LOG_DIR="/home/${SUDO_USER}"
|
||
|
LOG="${LOG_DIR}/log.txt"
|
||
|
|
||
|
#############################################################
|
||
|
# HELPER FUNCTIONS
|
||
|
#############################################################
|
||
|
warn() {
|
||
|
PURPLE='\033[1;35m'
|
||
|
NC='\033[0m'
|
||
|
echo -e "${PURPLE}$1${NC}"
|
||
|
echo -e "WARN:\t$1" >> $LOG
|
||
|
}
|
||
|
|
||
|
info() {
|
||
|
GREEN='\033[1;32m'
|
||
|
NC='\033[0m'
|
||
|
echo -e "${GREEN}$1${NC}"
|
||
|
echo -e "INFO:\t$1" >> $LOG
|
||
|
}
|
||
|
|
||
|
trace() {
|
||
|
YELLOW='\033[1;33m'
|
||
|
NC='\033[0m'
|
||
|
echo -e "${YELLOW}$1${NC}"
|
||
|
echo -e "TRACE:\t$1" >> $LOG
|
||
|
}
|
||
|
|
||
|
error() {
|
||
|
RED='\033[1;31m'
|
||
|
NC='\033[0m'
|
||
|
echo -e "${RED}$1${NC}"
|
||
|
echo -e "ERROR:\t$1" >> $LOG
|
||
|
}
|
||
|
|
||
|
not_set() {
|
||
|
count=$(grep -Pcx "$1" "$2")
|
||
|
code=$?
|
||
|
|
||
|
if [[ $code == 2 ]] && [[ -z "$count" ]]; then
|
||
|
echo "not_set() func or its caller contains a bug that has to be fixed! param1=${1} param2=${2} code=${code} count=${count}. Exiting!"
|
||
|
exit -1
|
||
|
fi
|
||
|
|
||
|
[[ $count == 0 ]] && [[ $code == 1 ]] && return 0
|
||
|
|
||
|
return 1
|
||
|
}
|
||
|
|
||
|
not_exist() {
|
||
|
if [[ ! -e "$2" ]]; then
|
||
|
warn "file does not exist: $2"
|
||
|
return 0
|
||
|
else
|
||
|
count=$(grep -Pc "$1" "$2")
|
||
|
code=$?
|
||
|
[[ $count == 0 ]] && [[ $code == 1 ]] && return 0
|
||
|
|
||
|
return 1
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
configure() {
|
||
|
if not_exist "$1" "$3"; then
|
||
|
warn "setting \"$1\" is not in the file, thus won't be changed"
|
||
|
return 1
|
||
|
else
|
||
|
if not_set "$2" "$3"; then
|
||
|
sed -i "s~.*${1}.*~${2}~gi" "$3" &&
|
||
|
trace "\"$1\" set to \"$2\"" &&
|
||
|
return 0
|
||
|
else
|
||
|
warn "\"$2\" already set in \"$3\""
|
||
|
return 2
|
||
|
fi
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
#############################################################
|
||
|
# 1 - ADD USER
|
||
|
#############################################################
|
||
|
add_user() {
|
||
|
info "STEP 1 Adding a user for container"
|
||
|
|
||
|
sudo adduser \
|
||
|
--system \
|
||
|
--shell /bin/bash \
|
||
|
--gecos 'Git Version Control' \
|
||
|
--group \
|
||
|
--disabled-password \
|
||
|
--home /home/git \
|
||
|
git
|
||
|
|
||
|
[[ $? -gt 0 ]] && { error 'Check git user setup, stopping now'; exit 1; }
|
||
|
|
||
|
info "STEP 1 done"
|
||
|
warn "Press enter to proceed with the next step"; read
|
||
|
}
|
||
|
|
||
|
#############################################################
|
||
|
# 2 - CHANGE SERVER NAME
|
||
|
#############################################################
|
||
|
change_servername() {
|
||
|
info "STEP 2 Changing server name to ${OS_SERVER_NAME}"
|
||
|
|
||
|
echo $OS_SERVER_NAME > /etc/hostname
|
||
|
hostentry="127.0.1.1\t${OS_SERVER_NAME}"
|
||
|
if not_set "$hostentry" "/etc/hosts"; then
|
||
|
echo -e "$hostentry" >> /etc/hosts
|
||
|
else
|
||
|
warn "\"$hostentry\" already set in \"/etc/hosts\""
|
||
|
fi
|
||
|
|
||
|
info "STEP 2 done"
|
||
|
warn "Press enter to proceed with the next step"; read
|
||
|
}
|
||
|
|
||
|
#############################################################
|
||
|
# 3 - INSTALL MARIADB
|
||
|
#############################################################
|
||
|
install_mariadb() {
|
||
|
info "STEP 3 Installing Maria DB"
|
||
|
|
||
|
apt-get -y install mariadb-server > /dev/null
|
||
|
systemctl --quiet is-active mariadb || systemctl start mariadb
|
||
|
systemctl --quiet is-enabled mariadb || systemctl enable mariadb
|
||
|
|
||
|
mysql -u root <<EOF
|
||
|
CREATE DATABASE IF NOT EXISTS gitea;
|
||
|
GRANT ALL PRIVILEGES ON gitea.* TO 'gitea'@'localhost' IDENTIFIED BY '${DB_PASSWORD}';
|
||
|
DROP DATABASE IF EXISTS test;
|
||
|
DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';
|
||
|
DELETE FROM mysql.user WHERE User='';
|
||
|
DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');
|
||
|
ALTER USER 'root'@'localhost' IDENTIFIED BY '${DB_PASSWORD}';
|
||
|
FLUSH PRIVILEGES;
|
||
|
EOF
|
||
|
[[ $? -eq 0 ]] || { error 'Something went bananas while setting up a Maria DB, proceed?'; }
|
||
|
|
||
|
mysql -u root -p${DB_PASSWORD} -h localhost <<EOF
|
||
|
SHOW DATABASES;
|
||
|
SELECT User FROM mysql.user;
|
||
|
EOF
|
||
|
|
||
|
info "STEP 3 done"
|
||
|
warn "Press enter to proceed with the next step"; read
|
||
|
}
|
||
|
|
||
|
#############################################################
|
||
|
# 4 - INSTALL NGINX
|
||
|
#############################################################
|
||
|
install_nginx() {
|
||
|
info "STEP 4 Installing Nginx"
|
||
|
|
||
|
which nginx && { warn 'Nginx already installed. Since there might be other sites configured on the server, advised to proceed with manual configuration of Nginx:
|
||
|
- main config: /etc/nginx/nginx.conf
|
||
|
- sites: /etc/nginx/sites-available
|
||
|
- linked to: /etc/nginx/sites-enabled
|
||
|
- logs: /var/log/nginx'; return 1; }
|
||
|
|
||
|
KEY="/etc/ssl/private/gitea-snakeoil.key"
|
||
|
CRT="/etc/ssl/certs/gitea-snakeoil.crt"
|
||
|
|
||
|
trace "Generating a self signed sertificate (${KEY} and ${CRT})"
|
||
|
|
||
|
openssl req -x509 \
|
||
|
-nodes \
|
||
|
-days 2048 \
|
||
|
-newkey rsa:2048 \
|
||
|
-keyout $KEY \
|
||
|
-out $CRT \
|
||
|
-subj "/C=${OS_COUNTRY}/ST=${OS_COUNTRY}/L=${OS_COUNTRY}/O=E Corp/OU=IT/CN=${OS_SERVER_NAME}/emailAddress=info@example.com"
|
||
|
|
||
|
apt-get install -y nginx > /dev/null
|
||
|
systemctl --quiet is-active nginx || systemctl start nginx
|
||
|
ufw allow 'Nginx Full'
|
||
|
|
||
|
trace "Rewriting default /etc/nginx/nginx.conf"
|
||
|
cat <<EOF | tee /etc/nginx/nginx.conf
|
||
|
user www-data;
|
||
|
worker_processes auto;
|
||
|
pid /run/nginx.pid;
|
||
|
include /etc/nginx/modules-enabled/*.conf;
|
||
|
|
||
|
events {
|
||
|
worker_connections 768;
|
||
|
}
|
||
|
|
||
|
http {
|
||
|
ssl_session_cache shared:SSL:10m;
|
||
|
ssl_session_timeout 10m;
|
||
|
sendfile on;
|
||
|
tcp_nopush on;
|
||
|
tcp_nodelay on;
|
||
|
keepalive_timeout 70;
|
||
|
types_hash_max_size 2048;
|
||
|
|
||
|
include /etc/nginx/mime.types;
|
||
|
default_type application/octet-stream;
|
||
|
|
||
|
ssl_protocols TLSv1.3;
|
||
|
ssl_prefer_server_ciphers on;
|
||
|
|
||
|
access_log /var/log/nginx/access.log;
|
||
|
error_log /var/log/nginx/error.log;
|
||
|
|
||
|
gzip on;
|
||
|
|
||
|
include /etc/nginx/conf.d/*.conf;
|
||
|
include /etc/nginx/sites-enabled/*;
|
||
|
}
|
||
|
EOF
|
||
|
|
||
|
trace "Generating a new config /etc/nginx/sites-available/${OS_SERVER_NAME}.conf"
|
||
|
|
||
|
cat <<EOF | tee /etc/nginx/sites-available/${OS_SERVER_NAME}.conf
|
||
|
server {
|
||
|
listen 80;
|
||
|
server_name ${OS_SERVER_NAME};
|
||
|
return 301 https://${OS_SERVER_NAME}$request_uri;
|
||
|
access_log /var/log/nginx/${OS_SERVER_NAME}-access.log;
|
||
|
error_log /var/log/nginx/${OS_SERVER_NAME}-error.log;
|
||
|
}
|
||
|
server {
|
||
|
listen 443 ssl http2;
|
||
|
|
||
|
server_name ${OS_SERVER_NAME};
|
||
|
client_max_body_size 256m;
|
||
|
|
||
|
proxy_set_header Host \$host;
|
||
|
proxy_set_header X-Real-IP \$remote_addr;
|
||
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
||
|
|
||
|
ssl_certificate ${CRT};
|
||
|
ssl_certificate_key ${KEY};
|
||
|
|
||
|
location / {
|
||
|
proxy_redirect off;
|
||
|
proxy_pass http://localhost:3000/;
|
||
|
}
|
||
|
|
||
|
access_log /var/log/nginx/${OS_SERVER_NAME}-access.log;
|
||
|
error_log /var/log/nginx/${OS_SERVER_NAME}-error.log;
|
||
|
}
|
||
|
EOF
|
||
|
|
||
|
trace 'Making a link from sites-available to sites-enabled...'
|
||
|
|
||
|
ln -s /etc/nginx/sites-available/${OS_SERVER_NAME}.conf \
|
||
|
/etc/nginx/sites-enabled
|
||
|
nginx -t
|
||
|
|
||
|
info "STEP 4 done"
|
||
|
warn "Press enter to proceed with the next step"; read
|
||
|
}
|
||
|
|
||
|
#############################################################
|
||
|
# 5 - INSTALL GITEA
|
||
|
#############################################################
|
||
|
install_gitea() {
|
||
|
info "STEP 5 Installing Gitea as a binary"
|
||
|
|
||
|
trace "Downloading ${GITEA_URL}"
|
||
|
|
||
|
apt-get install -y git wget > /dev/null
|
||
|
wget --continue $GITEA_URL --output-document gitea
|
||
|
chmod +x gitea
|
||
|
chown git:git gitea
|
||
|
|
||
|
trace 'Creating nessesary directories with git owner'
|
||
|
|
||
|
mkdir -p /var/lib/gitea/{custom,data,log}
|
||
|
chown -Rv git:git /var/lib/gitea/
|
||
|
chmod -Rv 750 /var/lib/gitea/
|
||
|
mkdir /etc/gitea
|
||
|
chown root:git /etc/gitea
|
||
|
chmod 770 /etc/gitea
|
||
|
cp --force gitea /usr/local/bin/gitea
|
||
|
|
||
|
cat <<EOF | tee /etc/systemd/system/gitea.service
|
||
|
[Unit]
|
||
|
Description=Gitea (Git with a cup of tea)
|
||
|
DefaultDependencies=no
|
||
|
After=syslog.target
|
||
|
After=network.target
|
||
|
Wants=mariadb.service
|
||
|
After=mariadb.service
|
||
|
|
||
|
[Service]
|
||
|
Type=simple
|
||
|
User=git
|
||
|
Group=git
|
||
|
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
|
||
|
Environment=GITEA_WORK_DIR=/var/lib/gitea/
|
||
|
Restart=always
|
||
|
RestartSec=5s
|
||
|
|
||
|
[Install]
|
||
|
WantedBy=multi-user.target
|
||
|
|
||
|
EOF
|
||
|
trace 'Installing a systemd service'
|
||
|
|
||
|
systemctl daemon-reload
|
||
|
systemctl --quiet is-enabled gitea || systemctl enable gitea
|
||
|
systemctl restart gitea
|
||
|
gitea --version
|
||
|
|
||
|
info "STEP 5 done"
|
||
|
warn "Press enter to proceed with the next step"; read
|
||
|
}
|
||
|
|
||
|
#############################################################
|
||
|
# 6 - ADDITIONAL SECURITY FOR THE SERVER
|
||
|
#############################################################
|
||
|
secure_server() {
|
||
|
info "STEP 6 Securing the server"
|
||
|
|
||
|
trace "Installing openssh server..."
|
||
|
apt-get install -y openssh-server > /dev/null
|
||
|
systemctl --quiet is-active sshd || systemctl start sshd
|
||
|
systemctl --quiet is-enabled sshd || systemctl enable sshd
|
||
|
|
||
|
SSH_CFG="/etc/ssh/sshd_config"
|
||
|
SSH_CFG_BACK="${LOG_DIR}/sshd_config.backup"
|
||
|
|
||
|
trace "Going to rewrite ${SSH_CFG}"
|
||
|
trace "Backup will be here: ${SSH_CFG_BACK}"
|
||
|
|
||
|
if [[ -e "$SSH_CFG_BACK" ]]; then
|
||
|
warn "SSH config backup is already there!"
|
||
|
else
|
||
|
cp $SSH_CFG $SSH_CFG_BACK
|
||
|
cat <<EOF | tee ${SSH_CFG}
|
||
|
Include /etc/ssh/sshd_config.d/*.conf
|
||
|
PermitRootLogin no
|
||
|
ChallengeResponseAuthentication no
|
||
|
UsePAM yes
|
||
|
X11Forwarding yes
|
||
|
PrintMotd no
|
||
|
AcceptEnv LANG LC_*
|
||
|
Subsystem sftp /usr/lib/openssh/sftp-server
|
||
|
EOF
|
||
|
if $NO_PASSWORD_SSH; then
|
||
|
info "Disabling SSH with password completely!"
|
||
|
echo 'PasswordAuthentication no' >> $SSH_CFG
|
||
|
fi
|
||
|
fi
|
||
|
|
||
|
trace "Installing fail2ban..."
|
||
|
apt-get install -y fail2ban > /dev/null
|
||
|
systemctl --quiet is-active fail2ban || systemctl start fail2ban
|
||
|
systemctl --quiet is-enabled fail2ban || systemctl enable fail2ban
|
||
|
fail2ban-client status
|
||
|
F2B_CFG_ORIG="/etc/fail2ban/jail.conf"
|
||
|
F2B_CFG_COPY="/etc/fail2ban/jail.local"
|
||
|
if [[ -e "$F2B_CFG_COPY" ]]; then
|
||
|
warn "Fail2ban was already set up"
|
||
|
else
|
||
|
cp $F2B_CFG_ORIG $F2B_CFG_COPY
|
||
|
trace "Setting up ${F2B_CFG_COPY}"
|
||
|
configure 'bantime = 10m' 'bantime = 60m' $F2B_CFG_COPY
|
||
|
configure 'maxretry = 5' 'maxretry = 20' $F2B_CFG_COPY
|
||
|
fi
|
||
|
|
||
|
trace "Configuring ufw..."
|
||
|
ufw --force enable
|
||
|
ufw allow 'OpenSSH'
|
||
|
ufw status
|
||
|
|
||
|
info "STEP 6 done"
|
||
|
warn "Press enter to proceed with the next step"; read
|
||
|
}
|
||
|
|
||
|
#############################################################
|
||
|
# 7 - MAKE AN UPDATE HELPER (AS A SCRIPT)
|
||
|
#############################################################
|
||
|
create_updater() {
|
||
|
info 'Step 7 Create an updater for gitea (small helper utility)'
|
||
|
|
||
|
MY_HOME="/home/${SUDO_USER}"
|
||
|
SCRIPT='/usr/local/bin/gitea-update'
|
||
|
|
||
|
cat <<EOF | tee $SCRIPT
|
||
|
#!/bin/bash
|
||
|
|
||
|
user=\$(whoami)
|
||
|
if [[ \$user != root ]]; then
|
||
|
echo 'You are using a non-privileged account'
|
||
|
exit 1
|
||
|
fi
|
||
|
|
||
|
if [[ \$# -eq 0 ]]; then
|
||
|
echo 'Provide a web link to the bin file as an argument!'
|
||
|
echo "Example: gitea-update $GITEA_URL"
|
||
|
exit 1
|
||
|
else
|
||
|
echo "Link: \$1"
|
||
|
fi
|
||
|
|
||
|
mkdir -p "${MY_HOME}" > /dev/null
|
||
|
cd "${MY_HOME}"
|
||
|
|
||
|
gitea --version
|
||
|
wget --continue \$1 --output-document gitea
|
||
|
systemctl stop gitea
|
||
|
chmod +x gitea
|
||
|
chown git:git gitea
|
||
|
cp --force gitea /usr/local/bin/gitea
|
||
|
systemctl restart gitea
|
||
|
gitea --version
|
||
|
|
||
|
EOF
|
||
|
|
||
|
chmod +x $SCRIPT
|
||
|
|
||
|
trace "Updater has been created. call it 'sudo gitea-update url_to_new_version'"
|
||
|
|
||
|
info 'Step 7 done'
|
||
|
}
|
||
|
|
||
|
#############################################################
|
||
|
# RUN SECTION
|
||
|
#############################################################
|
||
|
user=$(whoami)
|
||
|
if [ $user != root ]; then
|
||
|
error "You are using a non-privileged account"
|
||
|
exit 1
|
||
|
fi
|
||
|
|
||
|
echo $(date +'%Y-%m-%d %H:%M') > $LOG
|
||
|
|
||
|
info "Hiya!"
|
||
|
|
||
|
sudo apt update
|
||
|
sudo apt upgrade -y
|
||
|
|
||
|
add_user
|
||
|
change_servername
|
||
|
install_mariadb
|
||
|
install_nginx
|
||
|
install_gitea
|
||
|
secure_server
|
||
|
create_updater
|
||
|
|
||
|
info "Don't forget to reboot"
|
||
|
info "Bye!"
|
||
|
info "PS. Log is here: ${LOG}"
|
||
|
warn "REBOOT NOW!!!"
|
||
|
|
||
|
exit 0
|
||
|
|
||
|
# restart is mandatory!!!
|
||
|
|
||
|
# if a real domain is available - a certificate can be obtained, for examle:
|
||
|
# sudo apt install certbot python3-certbot-nginx
|
||
|
# sudo certbot --nginx -d gittar.crabdance.com
|
||
|
# sudo certbot renew --dry-run
|
||
|
|
||
|
# gitea can be upgraded or downgraded by executing
|
||
|
# sudo gitea-update 'https://dl.gitea.com/gitea/1.19.3/gitea-1.19.3-linux-arm64'
|
||
|
# however before running it ensure there is no old gitea file in the home dir
|
||
|
# otherwise it will install the same version again (such 'safety' measure;))
|