initial import

This commit is contained in:
tengel 2024-03-17 12:53:54 -05:00
parent 3ed58b0021
commit 4b70c0023c
48 changed files with 1540 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
assets/diras/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

26
assets/diras/readme.txt Normal file
View file

@ -0,0 +1,26 @@
Thank you for downloading a favicon from Free Favicon! This favicon was created from an image at http://openclipart.org/ and converted to a favicon and other sizes for you to use in your projects. For more details about this image visit this page: https://openclipart.org/detail/263820/Diras%20Rune
For more infomation about how you can use this image from OpenClipArt see this page: http://openclipart.org/may-clipart-be-used-comparison
Here are the contents of this compressed package:
* favicon.ico -- The favicon file (supports both 16*16 and 32*32 dimensions). You will need to rename this to favicon.ico and upload to your web site.
* You can add a favicon to your web page by uploading favicon.ico to Root of your website and inserting the following HTML tag between the <head> ... </head> tags of your web page.
<link rel="shortcut icon" href="favicon.ico" />
* There are many different sized png files for use a icons for smartphones, tablets and desktop use.
If you are having any problems installing your favicon we have tips on the Free Favicon blog. http://www.freefavicon.com/blog/
Thank you once again for download from Free Favicon! If you like your favicon we appreciate you taking the time to mention our service to others, blogging about us and of course by linking to us.
We also encourage you to check out Backblaze online backup. We use Backblaze to backup our computers and keep our data safe. They offer a free 15 day trial and are the easiest online backup service we have used.
Use this link http://www.freefavicon.com/blog/outgoing/backblaze.php to try Backblaze for Free!
By signing up for Backblaze you help keep Free Favicon free!
Thank you,
FreeFavicon.com

26
bin/jailstat.sh Executable file
View file

@ -0,0 +1,26 @@
#!/usr/bin/env bash
#
# Get list of fail2ban jails and print status of each
#
# SPDX-License-Identifier: MIT
# fail2ban-client requires root
if [[ $(id -u) -ne 0 ]]; then
echo "Must run as root user"
exit 1
fi
## there's a tab after "list:"
## fail2ban-client status ->
# Status
# |- Number of jail: 3
# `- Jail list: nginx-4xx, nginx-limit-req, sshd
JAILS=$(fail2ban-client status | \
awk -F: '/list/{gsub(/, /," ",$2);gsub(/^[ \t]+/,"",$2);print $2}'
)
for jail in ${JAILS}; do
fail2ban-client status "${jail}"
done

67
bin/teabak.sh Executable file
View file

@ -0,0 +1,67 @@
#!/usr/bin/env bash
#
# Create Gitea backups
#
# Prep
# groupadd --system bkp
# mkdir /var/xyzzy/backup
# chmod 0750 /var/xyzzy/backup
# chown git:bkp /var/xyzzy/backup
#
# SPDX-License-Identifier: MIT
_VERSION="0.0.2"
BDTS=$(date +"%Y%m%d%H%M")
BDIR="/var/xyzzy/backup"
BFILE="${BDIR}/gitea-${BDTS}"
# gitea dump adds ".tar.xz" to the name dynamically
BDMP="${BDIR}/gitea-${BDTS}.tar.xz"
BGRP="bkp"
GCNF="/var/xyzzy/etc/gitea/app.ini"
GBIN="/var/xyzzy/bin/gitea"
GDATA="/var/xyzzy/gitea"
# healthchecks.io ping URL upon success, uses curl - "none" to disable
HCPING="none"
# delete backups older than
# see 'man find'; "+3" = 3*24h ago
BDEL="-mtime +3"
# we need to chgrp/chmod to a foreign group
if [[ $(id -u) -ne 0 ]]; then
echo "Must run as root user"
exit 1
fi
# just to be sure
cd "${BDIR}" || (echo "Cannot cd to ${BDIR}"; exit 1)
# runuser exits with the error code of the command
_EC=1
runuser -u git -- "${GBIN}" dump --config "${GCNF}" --tempdir "${BDIR}" \
--work-path "${GDATA}" --skip-log --type tar.xz --file "${BFILE}"
_EC=$?
# post processing
if [[ $_EC -eq 0 ]]; then
# gitea dump writes git:git 0600
if [[ -f "${BDMP}" ]]; then
chgrp "${BGRP}" "${BDMP}"
chmod 0640 "${BDMP}"
fi
# delete older than BDEL backups
# shellcheck disable=SC2086
find "${BDIR}" -type f ${BDEL} \
-regextype egrep \
-regex '.*/gitea-[0-9]{12}\.tar\.xz' \
-delete
# ping healthchecks.io
if [[ "${HCPING}" != "none" ]]; then
curl -fsS -m 10 --retry 5 -o /dev/null "${HCPING}"
fi
fi

144
bin/teaup.sh Executable file
View file

@ -0,0 +1,144 @@
#!/usr/bin/env bash
# shellcheck disable=SC2164,SC2181
#
# Upgrade gitea binary
# - run this under sudo as it replaces and restarts gitea
# - "gitea" is a symlink to the numbered github download binary
# - allows for quick rollback if needed
# - e.g.: 'ln -s gitea-1.16.3-linux-amd64 gitea'
#
# Exit codes
# 0 = Success (already newest or upgrade worked)
# 1 = curl failed to download new version
# 2 = sha256sum check failed on download
# 3 = "gitea" wasn't a symlink
# 4 = upgrade version check failed, gitea not restarted
# 5 = could not change directory to run sha256sum
# 6 = cannot determine Gitea version info
# 20 = not running under sudo (see TEA_SUDO)
# 21 = gitea upgraded but not restarted (see TEA_HUP)
# 22 = gitea upgrade downloaded only (see TEA_LINK)
#
# Requires: curl, awk, grep, sha256sum
# Debug: bash -x /path/to/script.sh
#
# SPDX-License-Identifier: MIT
_VERSION="0.0.1"
# symlink name used to run gitea (e.g. "gitea")
TEA_SYM="gitea"
# where the binary is located (e.g. "/usr/local/bin")
TEA_DIR="/var/xyzzy/bin"
# 0 = verbose status, 1 = silent and rely on exit codes
TEA_QUIET=0
# require running this script under sudo, 0 to disable
TEA_SUDO=1
# restart gitea using TEA_CMD, 0 to disable
TEA_HUP=0
# replace symlink, 0 to disable (download only, implies TEA_HUP=0)
TEA_LINK=1
# command to restart gitea (e.g. "systemctl restart gitea")
TEA_CMD="systemctl restart gitea"
# github API endpoint to get latest version
TEA_API="https://api.github.com/repos/go-gitea/gitea/releases/latest"
# github download base URL to prepend with version info
TEA_DLB="https://github.com/go-gitea/gitea/releases/download"
# architecture being used, matches download name
TEA_ARCH="linux-amd64"
function noise() {
if [[ ${TEA_QUIET} -eq 0 ]]; then
echo "$*"
fi
}
if [[ ${TEA_SUDO} -eq 1 ]]; then
if [[ $(id -u) -ne 0 ]]; then
noise "Run this script as root (sudo)"
exit 20
fi
fi
# disable curl download progress if in quiet mode
_COPT=""
if [[ ${TEA_QUIET} -eq 1 ]]; then
_COPT="-s"
fi
noise "Checking gitea..."
# get installed version
_LOCAL=$("${TEA_DIR}/${TEA_SYM}" -version | awk '{print $3}')
# get latest version, strip leading "v" (v1.55.1 -> 1.55.1)
_REMOTE=$(curl -s "${TEA_API}" | grep -Po '"tag_name": "\K.*?(?=")')
_REMOTE=${_REMOTE#v}
# API failed, can't run local binary, etc. - something went wrong
if [[ -z "${_LOCAL}" || -z "${_REMOTE}" ]]; then
noise "Cannot determine Gitea version information"
exit 6
fi
# bash doesn't see versions as numbers, but as strings
if [[ "${_LOCAL}" != "${_REMOTE}" ]]; then
_TEA_NAME="gitea-${_REMOTE}-${TEA_ARCH}"
noise "Upgrading gitea - installed ${_LOCAL}, latest ${_REMOTE}"
# curl will handle a failure being able to write to output dir, etc.
curl ${_COPT} -L --output-dir "${TEA_DIR}" --remote-name-all \
"${TEA_DLB}/v${_REMOTE}/${_TEA_NAME}" \
"${TEA_DLB}/v${_REMOTE}/${_TEA_NAME}.sha256"
if [[ $? -eq 0 ]]; then
# downloads were successful and written to disk
pushd "$(pwd)" >/dev/null
cd "${TEA_DIR}" || (noise "Cannot cd to ${TEA_DIR}"; exit 5)
noise "Checking sha256sum..."
sha256sum --status -c "${_TEA_NAME}.sha256"
if [[ $? -eq 0 ]]; then
# sha256sum check passed
chmod +x "${_TEA_NAME}"
if [[ ${TEA_LINK} -eq 1 ]]; then
# user requested replacing symlink
if [[ -h "${TEA_SYM}" ]]; then
noise "Replacing symlink..."
rm -f "${TEA_SYM}"
ln -s "${_TEA_NAME}" "${TEA_SYM}"
# trust, but verify
_TEA_NEW=$("${TEA_DIR}/${TEA_SYM}" -version | awk '{print $3}')
if [[ "${_TEA_NEW}" == "${_REMOTE}" ]]; then
noise "Gitea binary/symlink upgraded to ${_TEA_NEW}"
if [[ ${TEA_HUP} -eq 1 ]]; then
# user requested restart
noise "Restarting gitea..."
${TEA_CMD}
else
noise "Gitea needs restarted"
exit 21
fi
else
noise "Upgrade failed, not restarting gitea"
exit 4
fi
else
noise "${TEA_SYM} is not a symlink, not overwriting"
exit 3
fi
else
noise "Gitea ${_TEA_NAME} downloaded, ready to upgrade"
exit 22
fi
else
noise "Download of ${_TEA_NAME} failed sha256sum, not upgrading"
exit 2
fi
popd >/dev/null
else
noise "Download of ${_TEA_NAME} and sha256 failed, not upgrading"
exit 1
fi
else
noise "Installed gitea is the latest - ${_LOCAL}"
fi

336
doc/SETUP.md Normal file
View file

@ -0,0 +1,336 @@
# Server Setup
Debian 12 minimal installation, ensure the SSH server and standard system tools are included.
## Base Configuration
Internal hostname, replace "myhostname":
```
hostnamectl set-hostname myhostname
```
**/etc/hosts**
```
127.0.0.1 localhost
127.0.1.1 myhostname
```
Install packages:
```
apt-get update
echo "iptables-persistent iptables-persistent/autosave_v4 boolean true" | debconf-set-selections
echo "iptables-persistent iptables-persistent/autosave_v6 boolean true" | debconf-set-selections
echo "unattended-upgrades unattended-upgrades/enable_auto_updates boolean true" | debconf-set-selections
apt-get install curl sysstat unattended-upgrades iptables-persistent \
man less vim rsync bc net-tools git git-lfs sqlite3 strace sudo \
apparmor-utils rsyslog
```
Install packages without `recommends`:
```
apt-get install --no-install-recommends smem \
nginx nginx-core libnginx-mod-stream \
certbot python3-certbot-nginx
```
Set up services:
```
systemctl disable remote-fs.target rsync.service
systemctl enable sysstat unattended-upgrades netfilter-persistent
```
As needed:
```
apt-get update
apt-get full-upgrade
reboot
```
## User Setup
Add a login user:
```
export MYUSER="frankthetank"
useradd -m -d /home/${MYUSER} -s /bin/bash -g users -G sudo ${MYUSER}
passwd ${MYUSER}
mkdir -p /var/xyzzy/bin
chown -R ${MYUSER}: /var/xyzzy
```
If `ssh-copy-id` can't be used:
```
mkdir /home/${MYUSER}/.ssh
cp /root/.ssh/authorized_keys /home/${MYUSER}/.ssh/
chmod 0700 /home/${MYUSER}/.ssh
chmod 0600 /home/${MYUSER}/.ssh/authorized_keys
chown -R ${MYUSER}:users /home/${MYUSER}/.ssh
```
Disable root login:
```
_SCFG="/etc/ssh/sshd_config"
if $(grep -iEq '^PermitRootLogin[[:space:]]+yes' "${_SCFG}"); then
sed -i.bak -e 's/^PermitRootLogin.*/PermitRootLogin no/gi' "${_SCFG}"
else
sed -i.bak -e 's/^#PermitRootLogin.*/PermitRootLogin no/gi' "${_SCFG}"
fi
```
After confirming the change is correct:
```
systemctl restart sshd
```
Test logging in again as the user and sudo to root in another term.
## Server Configuration
Get rid of mouse in `vim`:
```
echo 'set mouse=' >> ~/.vimrc
```
Get rid of bracketed paste:
```
echo 'set enable-bracketed-paste Off' >> ~/.inputrc
```
Enable `sysstat`:
```
sed -i.bak -e 's|^ENABLED=".*"|ENABLED="true"|g' /etc/default/sysstat
```
Configure `unattended-upgrades`:
```
cp 02periodic /etc/apt/apt.conf.d/
```
Configure `iptables` and `ip6tables`:
```
cp rules.* /etc/iptables/
```
Configure tweaks:
```
mkdir /etc/systemd/journald.conf.d
mkdir /etc/systemd/system/nginx.service.d
cp sysctl.d/local.conf /etc/sysctl.d/
cp systemd/journald.conf.d/local.conf \
/etc/systemd/journald.conf.d/
cp systemd/system/nginx.service.d/override.conf \
/etc/systemd/system/nginx.service.d/
systemctl daemon-reload
```
## Fail2ban Setup
SSH jail:
```
apt-get install fail2ban
cp jail.local /etc/fail2ban/
systemctl enable --now fail2ban
```
Maintenance tasks:
```
cp dbpurge.sql /etc/fail2ban/
cp f2b-cleanup /etc/cron.weekly/
chown root:root /etc/cron.weekly/f2b-cleanup
chmod 0755 /etc/cron.weekly/f2b-cleanup
cp bin/jailstat.sh /var/xyzzy/bin/
```
## Nginx Setup
Gitea first-time setup is easier with nginx + SSL already working.
#### Basic Prep
```
cd /etc/nginx/modules-enabled
rm \
50-mod-http-geoip.conf \
50-mod-http-image-filter.conf \
50-mod-http-xslt-filter.conf \
50-mod-mail.conf \
70-mod-stream-geoip.conf
cp security.conf /etc/nginx/conf.d/
```
#### Site Prep
```
mkdir /var/xyzzy/html
cp html* /var/xyzzy/html/
cp xyzzy.ee.conf.bootstrap /etc/nginx/sites-available/xyzzy.ee.conf
cp xyzzy.fi.conf.bootstrap /etc/nginx/sites-available/xyzzy.fi.conf
cp git.xyzzy.ee.conf.bootstrap /etc/nginx/sites-available/git.xyzzy.ee.conf
cd /etc/nginx/sites-enabled
rm default
ln -s /etc/nginx/sites-available/xyzzy.ee.conf 00xyzzy.ee.conf
ln -s /etc/nginx/sites-available/xyzzy.fi.conf 01xyzzy.fi.conf
ln -s /etc/nginx/sites-available/git.xyzzy.ee.conf 02git.xyzzy.ee.conf
nginx -t
systemctl restart nginx
certbot --nginx -d xyzzy.ee,www.xyzzy.ee \
--agree-tos -m "hostmaster@xyzzy.ee" --no-eff-email \
--deploy-hook "systemctl reload nginx"
certbot --nginx -d xyzzy.fi,www.xyzzy.fi \
--agree-tos -m "hostmaster@xyzzy.fi" --no-eff-email \
--deploy-hook "systemctl reload nginx"
certbot --nginx -d git.xyzzy.ee \
--agree-tos -m "hostmaster@xyzzy.ee" --no-eff-email \
--deploy-hook "systemctl reload nginx"
cp xyzzy.ee.conf /etc/nginx/sites-available/xyzzy.ee.conf
cp xyzzy.fi.conf /etc/nginx/sites-available/xyzzy.fi.conf
cp git.xyzzy.ee.conf /etc/nginx/sites-available/git.xyzzy.ee.conf
nginx -t
systemctl restart nginx
```
## Gitea Setup
All content lives under `/var/xyzzy`:
```
cp bin/teaup.sh /var/xyzzy/bin/
cd /var/xyzzy/bin
export TVER="1.21.8"
curl -L --output-dir /var/xyzzy/bin --remote-name-all \
"https://github.com/go-gitea/gitea/releases/download/v${TVER}/gitea-${TVER}-linux-amd64" \
"https://github.com/go-gitea/gitea/releases/download/v${TVER}/gitea-${TVER}-linux-amd64.sha256"
sha256sum -c "gitea-${TVER}-linux-amd64.sha256"
chmod +x "gitea-${TVER}-linux-amd64"
ln -s "gitea-${TVER}-linux-amd64" gitea
```
Primary `git` user:
```
adduser \
--system \
--shell /bin/bash \
--gecos 'git' \
--group \
--disabled-password \
--home /var/xyzzy/git \
git
```
Gitea prep:
```
mkdir -p /var/xyzzy/gitea/{custom,data,log}
chown -R git:git /var/xyzzy/gitea/
chmod -R 750 /var/xyzzy/gitea/
mkdir -p /var/xyzzy/etc/gitea
chown root:git /var/xyzzy/etc/gitea
chmod 770 /var/xyzzy/etc/gitea
cp gitea.service /etc/systemd/system/
systemctl daemon-reload
```
Gitea init:
```
systemctl start gitea.service
( browser -> https://git.xyzzy.ee )
systemctl stop gitea.service
```
Gitea deploy: (app.ini needs secrets set)
```
cp custom/* /var/xyzzy/gitea/custom/
cp app.ini /var/xyzzy/etc/gitea/
chmod 750 /var/xyzzy/etc/gitea
chmod 640 /var/xyzzy/etc/gitea/app.ini
chown root:git /var/xyzzy/etc/gitea/app.ini
systemctl enable --now gitea.service
```
## AppArmor
```
cp apparmor.d/* /etc/apparmor.d/
systemctl restart apparmor.service
```
## Backup
See `teabak.sh`; prep: (HCPING needs set in script)
```
groupadd --system bkp
mkdir /var/xyzzy/backup
chmod 0750 /var/xyzzy/backup
chown git:bkp /var/xyzzy/backup
cp teaback.sh /var/xyzzy/bin/
chmod 0755 /var/xyzzy/bin/teabak.sh
cp teabak.service teabak.timer /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now teabak.timer
```
Allow restricted backup user to rsync the data:
```
adduser \
--shell /bin/bash \
--gecos 'reposync' \
--ingroup bkp \
--disabled-password \
--home /home/reposync \
reposync
```
The "reposync" user has a restricted `.ssh/authorized_keys` like so:
```
command="/usr/bin/rrsync -ro /var/xyzzy/backup/",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty,no-user-rc,restrict ssh-ed25519 ...
```
The `rrsync` script is part of the Debian `rsync` package

View file

@ -0,0 +1,35 @@
#include <tunables/global>
/usr/sbin/nginx flags=(complain) {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/openssl>
#include <abstractions/ssl_certs>
#include <abstractions/ssl_keys>
# privilege drop
capability dac_override,
capability dac_read_search,
capability net_bind_service,
capability setgid,
capability setuid,
# abstractions/apache2-common
@{PROC}/@{pid}/attr/current rw,
# nginx operational
/etc/letsencrypt/options-ssl-nginx.conf r,
/etc/letsencrypt/ssl-dhparams.pem r,
/etc/nginx/** r,
/run/nginx.pid rw,
/usr/lib/nginx/** r,
/usr/sbin/nginx mr,
/usr/share/nginx/** r,
/var/lib/nginx/** rw,
/var/log/nginx/error.log w,
/var/log/nginx/access.log w,
# data
/var/xyzzy/html/** r,
}

View file

@ -0,0 +1,41 @@
#include <tunables/global>
/var/xyzzy/bin/gitea* flags=(complain) {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/user-tmp>
/dev/tty rw,
/etc/gitconfig r,
/etc/machine-id r,
/etc/mime.types r,
/proc/sys/net/core/somaxconn r,
/proc/version r,
/sys/devices/system/cpu/online r,
/sys/kernel/mm/transparent_hugepage/hpage_pmd_size r,
/usr/bin/basename mrix,
/usr/bin/bash mrix,
/usr/bin/cat mrix,
/usr/bin/dash mrix,
/usr/bin/env rix,
/usr/bin/git mrix,
/usr/bin/gzip mrix,
/usr/lib/git-core/git mrix,
/usr/share/git-core/templates r,
/usr/share/mime/globs2 r,
/var/xyzzy/backup/* rw,
/var/xyzzy/bin/gitea* mrix,
/var/xyzzy/etc/gitea/app.ini r,
/var/xyzzy/gitea/** r,
/var/xyzzy/gitea/data/gitea-repositories/*/*.git/hooks/* mrix,
/var/xyzzy/gitea/data/gitea-repositories/*/*.git/hooks/*.d/* mrix,
owner /proc/*/cpuset r,
owner /var/xyzzy/git/.gitconfig rw,
owner /var/xyzzy/git/.gitconfig.lock rw,
owner /var/xyzzy/git/.ssh/* rw,
owner /var/xyzzy/gitea/data/** rwkl,
owner /var/xyzzy/gitea/log/* rw,
}

View file

@ -0,0 +1,5 @@
APT::Periodic::Enable "1";
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "5";
APT::Periodic::Unattended-Upgrade "1";

5
etc/cron.weekly/f2b-cleanup Executable file
View file

@ -0,0 +1,5 @@
#!/bin/sh
if [ -x /usr/bin/sqlite3 ]; then
sqlite3 /var/lib/fail2ban/fail2ban.sqlite3 < /etc/fail2ban/dbpurge.sql
fi
systemctl restart fail2ban.service

3
etc/fail2ban/dbpurge.sql Normal file
View file

@ -0,0 +1,3 @@
delete from bans where timeofban <= strftime('%s', date('now', '-7 days'));
vacuum;
.quit

View file

@ -0,0 +1,3 @@
[Definition]
failregex = ^<HOST>.*"(GET|POST).*" (404|444|403|400) .*$
ignoreregex =

26
etc/fail2ban/jail.local Normal file
View file

@ -0,0 +1,26 @@
[DEFAULT]
ignoreip = 127.0.0.1/8
bantime = 3600
maxretry = 3
backend = systemd
destemail = root@localhost
# filter.d/nginx-4xx.conf
# [Definition]
# failregex = ^<HOST>.*"(GET|POST).*" (404|444|403|400) .*$
# ignoreregex =
[nginx-4xx]
enabled = false
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 10
findtime = 3600
bantime = 3600
[nginx-limit-req]
enabled = false
port = http,https
logpath = /var/log/nginx/error.log
findtime = 3600
bantime = 3600

13
etc/iptables/rules.v4 Normal file
View file

@ -0,0 +1,13 @@
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT

13
etc/iptables/rules.v6 Normal file
View file

@ -0,0 +1,13 @@
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p ipv6-icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp6-adm-prohibited
-A FORWARD -j REJECT --reject-with icmp6-adm-prohibited
COMMIT

3
etc/letsencrypt/cli.ini Normal file
View file

@ -0,0 +1,3 @@
max-log-backups = 0
preconfigured-renewal = True
deploy-hook = systemctl reload nginx

View file

@ -0,0 +1,14 @@
# https://www.nginx.com/blog/rate-limiting-nginx/
# https://nginx.org/en/docs/http/ngx_http_limit_req_module.html
limit_req_zone $binary_remote_addr zone=normal:10m rate=20r/s;
limit_req zone=normal burst=30 nodelay;
limit_req_status 429;
# https://nginx.org/en/docs/http/ngx_http_core_module.html
server_tokens off;
keepalive_requests 100;
keepalive_timeout 15s;
client_body_timeout 20s;
client_header_timeout 20s;

View file

@ -0,0 +1,61 @@
# git.xyzzy.ee
server {
server_name git.xyzzy.ee;
root /var/xyzzy/html;
index index.html;
location ~ ^\/(robots\.txt|\.well-known\/security\.txt)$ {
try_files $uri $uri/ =404;
}
location / {
proxy_pass http://127.0.0.1:3000;
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;
}
listen [::]:443 ssl; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/git.xyzzy.ee/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/git.xyzzy.ee/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
# https://ssl-config.mozilla.org/
add_header Strict-Transport-Security "max-age=15724800" always;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/git.xyzzy.ee/chain.pem;
resolver 9.9.9.9 8.8.8.8 1.1.1.1;
resolver_timeout 5s;
# https://observatory.mozilla.org
# gitea already adds X-Frame-Options SAMEORIGIN
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "strict-origin-when-cross-origin";
# https://csp-evaluator.withgoogle.com/
# https://github.com/go-gitea/gitea/issues/305
# add_header Content-Security-Policy "upgrade-insecure-requests; default-src 'self'; connect-src 'self'; font-src 'self' data:; form-action 'self'; img-src 'self' data:; object-src 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; worker-src 'self'; frame-src 'self'; frame-ancestors 'self'; manifest-src 'self' data:;";
}
server {
if ($host = git.xyzzy.ee) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80;
server_name git.xyzzy.ee;
return 301 https://git.xyzzy.ee$request_uri;
return 404; # managed by Certbot
}

View file

@ -0,0 +1,15 @@
# git.xyzzy.ee
server {
listen 80;
listen [::]:80;
server_name git.xyzzy.ee;
root /var/xyzzy/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}

View file

@ -0,0 +1,108 @@
# xyzzy.ee
# www.xyzzy.ee
server {
server_name xyzzy.ee;
root /var/xyzzy/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
listen [::]:443 ssl default_server; # managed by Certbot
listen 443 ssl default_server; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/xyzzy.ee/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/xyzzy.ee/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
# https://ssl-config.mozilla.org/
add_header Strict-Transport-Security "max-age=15724800" always;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/xyzzy.ee/chain.pem;
resolver 9.9.9.9 8.8.8.8 1.1.1.1;
resolver_timeout 5s;
# https://observatory.mozilla.org
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Content-Security-Policy "default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; base-uri 'self'; form-action 'self'; frame-ancestors 'self';";
}
server {
server_name www.xyzzy.ee;
root /var/xyzzy/html;
index index.html;
location ~ /\.well-known {
allow all;
}
location ~ / {
return 301 $scheme://xyzzy.ee$request_uri;
}
listen [::]:443 ssl; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/xyzzy.ee/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/xyzzy.ee/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
# https://ssl-config.mozilla.org/
add_header Strict-Transport-Security "max-age=15724800" always;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/xyzzy.ee/chain.pem;
resolver 9.9.9.9 8.8.8.8 1.1.1.1;
resolver_timeout 5s;
# https://observatory.mozilla.org
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Content-Security-Policy "default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; base-uri 'self'; form-action 'self'; frame-ancestors 'self';";
}
server {
if ($host = xyzzy.ee) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80;
server_name xyzzy.ee;
root /var/xyzzy/html;
index index.html;
return 404; # managed by Certbot
}
server {
if ($host = www.xyzzy.ee) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80;
server_name www.xyzzy.ee;
root /var/xyzzy/html;
index index.html;
return 404; # managed by Certbot
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 301 https://xyzzy.ee$request_uri;
}

View file

@ -0,0 +1,32 @@
# xyzzy.ee
# www.xyzzy.ee
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name xyzzy.ee;
root /var/xyzzy/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
server {
listen 80;
listen [::]:80;
server_name www.xyzzy.ee;
root /var/xyzzy/html;
index index.html;
location ~ /\.well-known {
allow all;
}
location ~ / {
return 301 $scheme://xyzzy.ee$request_uri;
}
}

View file

@ -0,0 +1,101 @@
# xyzzy.fi
# www.xyzzy.fi
server {
server_name xyzzy.fi;
root /var/xyzzy/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
listen [::]:443 ssl; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/xyzzy.fi/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/xyzzy.fi/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
# https://ssl-config.mozilla.org/
add_header Strict-Transport-Security "max-age=15724800" always;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/xyzzy.fi/chain.pem;
resolver 9.9.9.9 8.8.8.8 1.1.1.1;
resolver_timeout 5s;
# https://observatory.mozilla.org
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Content-Security-Policy "default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; base-uri 'self'; form-action 'self'; frame-ancestors 'self';";
}
server {
server_name www.xyzzy.fi;
root /var/xyzzy/html;
index index.html;
location ~ /\.well-known {
allow all;
}
location ~ / {
return 301 $scheme://xyzzy.fi$request_uri;
}
listen [::]:443 ssl; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/xyzzy.fi/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/xyzzy.fi/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
# https://ssl-config.mozilla.org/
add_header Strict-Transport-Security "max-age=15724800" always;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/xyzzy.fi/chain.pem;
resolver 9.9.9.9 8.8.8.8 1.1.1.1;
resolver_timeout 5s;
# https://observatory.mozilla.org
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Content-Security-Policy "default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; base-uri 'self'; form-action 'self'; frame-ancestors 'self';";
}
server {
if ($host = xyzzy.fi) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80;
server_name xyzzy.fi;
root /var/xyzzy/html;
index index.html;
return 404; # managed by Certbot
}
server {
if ($host = www.xyzzy.fi) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80;
server_name www.xyzzy.fi;
root /var/xyzzy/html;
index index.html;
return 404; # managed by Certbot
}

View file

@ -0,0 +1,32 @@
# xyzzy.fi
# www.xyzzy.fi
server {
listen 80;
listen [::]:80;
server_name xyzzy.fi;
root /var/xyzzy/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
server {
listen 80;
listen [::]:80;
server_name www.xyzzy.fi;
root /var/xyzzy/html;
index index.html;
location ~ /\.well-known {
allow all;
}
location ~ / {
return 301 $scheme://xyzzy.fi$request_uri;
}
}

1
etc/sysctl.d/local.conf Normal file
View file

@ -0,0 +1 @@
vm.swappiness = 10

View file

@ -0,0 +1,2 @@
[Journal]
MaxRetentionSec=1week

View file

@ -0,0 +1,31 @@
[Unit]
Description=Gitea
After=syslog.target
After=network.target
[Service]
# Modify these two values and uncomment them if you have
# repos with lots of files and get an HTTP error 500 because
# of that
###
#LimitMEMLOCK=infinity
#LimitNOFILE=65535
###
RestartSec=2s
Type=simple
User=git
Group=git
WorkingDirectory=/var/xyzzy/gitea/
ExecStart=/var/xyzzy/bin/gitea web --config /var/xyzzy/etc/gitea/app.ini
Restart=always
Environment=USER=git HOME=/var/xyzzy/git GITEA_WORK_DIR=/var/xyzzy/gitea
Environment=PATH=/var/xyzzy/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# If you want to bind Gitea to a port below 1024, uncomment
# the two values below, or use socket activation to pass Gitea its ports as above
###
#CapabilityBoundingSet=CAP_NET_BIND_SERVICE
#AmbientCapabilities=CAP_NET_BIND_SERVICE
###
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,3 @@
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=876365
[Service]
ExecStartPost=/usr/bin/sleep 0.1

View file

@ -0,0 +1,10 @@
[Unit]
Description=Gitea Backup
Wants=teabak.timer
[Service]
Type=oneshot
ExecStart=/var/xyzzy/bin/teabak.sh
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,10 @@
[Unit]
Description=Gitea Backup Timer
Requires=teabak.service
[Timer]
Unit=teabak.service
OnCalendar=*-*-* 00,08,16:00:00
[Install]
WantedBy=timers.target

151
gitea/app.ini Normal file
View file

@ -0,0 +1,151 @@
# https://github.com/go-gitea/gitea/blob/main/custom/conf/app.example.ini
APP_NAME = xyzzy gitea
RUN_USER = git
RUN_MODE = prod
# same as $GITEA_WORK_DIR in systemd unit env
WORK_PATH = /var/xyzzy/gitea
[ui]
DEFAULT_THEME = arc-green
SHOW_USER_EMAIL = false
AMBIGUOUS_UNICODE_DETECTION = false
[attachment]
ALLOWED_TYPES = "*/*"
[ui.meta]
AUTHOR = xyzzy
DESCRIPTION = git repositories
[database]
DB_TYPE = sqlite3
HOST = 127.0.0.1:3306
NAME = gitea
USER = gitea
PASSWD =
SCHEMA =
SSL_MODE = disable
CHARSET = utf8
PATH = /var/xyzzy/gitea/data/gitea.db
LOG_SQL = false
AUTO_MIGRATION = true
[repository]
ROOT = /var/xyzzy/gitea/data/gitea-repositories
DEFAULT_BRANCH = main
DISABLE_STARS = true
[repository.signing]
DEFAULT_TRUST_MODE = committer
[server]
SSH_DOMAIN = git.xyzzy.ee
DOMAIN = git.xyzzy.ee
ROOT_URL = https://git.xyzzy.ee/
# https://github.com/go-gitea/gitea/issues/9427
HTTP_ADDR = 127.0.0.1
HTTP_PORT = 3000
DISABLE_SSH = false
SSH_PORT = 22
LFS_START_SERVER = true
LFS_JWT_SECRET = (REDACTED)
OFFLINE_MODE = true
LANDING_PAGE = home
# if needed, see app.example.ini
;STATIC_ROOT_PATH =
;APP_DATA_PATH = data
[lfs]
STORAGE_TYPE = local
PATH = /var/xyzzy/gitea/data/lfs
[mailer]
ENABLED = false
[service]
REGISTER_EMAIL_CONFIRM = false
REGISTER_MANUAL_CONFIRM = true
ENABLE_NOTIFY_MAIL = false
DISABLE_REGISTRATION = true
ALLOW_ONLY_INTERNAL_REGISTRATION = true
ALLOW_ONLY_EXTERNAL_REGISTRATION = false
ENABLE_CAPTCHA = false
REQUIRE_CAPTCHA_FOR_LOGIN = false
REQUIRE_SIGNIN_VIEW = false
DEFAULT_KEEP_EMAIL_PRIVATE = true
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
DEFAULT_ENABLE_TIMETRACKING = true
NO_REPLY_ADDRESS = noreply.xyzzy.ee
ENABLE_USER_HEATMAP = false
[service.explore]
DISABLE_USERS_PAGE = true
[picture]
DISABLE_GRAVATAR = true
ENABLE_FEDERATED_AVATAR = false
[openid]
ENABLE_OPENID_SIGNIN = false
ENABLE_OPENID_SIGNUP = false
[oauth2]
ENABLE = false
JWT_SECRET = (REDACTED)
[session]
PROVIDER = file
COOKIE_SECURE = true
COOKIE_NAME = __Host-gitea_session
[log]
MODE = console
LEVEL = critical
ROOT_PATH = /var/xyzzy/gitea/log
COLORIZE = false
logger.access.MODE =
logger.router.MODE =
logger.xorm.MODE =
[log.console]
COLORIZE = false
[cron.update_mirrors]
SCHEDULE = @midnight
[cron.delete_old_actions]
ENABLED = true
SCHEDULE = @midnight
OLDER_THAN = 8760h
[cron.delete_old_system_notices]
ENABLED = true
SCHEDULE = @midnight
OLDER_THAN = 720h
# https://pkg.go.dev/time#pkg-constants
[time]
FORMAT = RFC3339
[mirror]
DEFAULT_INTERVAL = 4h
[security]
INSTALL_LOCK = true
LOGIN_REMEMBER_DAYS = 365
INTERNAL_TOKEN = (REDACTED)
PASSWORD_HASH_ALGO = pbkdf2
DISABLE_QUERY_AUTH_TOKEN = true
[other]
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false
ENABLE_FEED = false
[federation]
ENABLED = false
SHARE_USER_STATISTICS = false
[actions]
ENABLED = false

View file

@ -0,0 +1,10 @@
{{template "base/head" .}}
<div role="main" aria-label="{{if .IsSigned}}{{ctx.Locale.Tr "dashboard"}}{{else}}{{ctx.Locale.Tr "home"}}{{end}}" class="page-content home">
<div class="gt-mb-5 gt-px-5">
<div class="center">
<div class="hero">
</div>
</div>
</div>
</div>
{{template "base/footer" .}}

View file

@ -0,0 +1 @@
Contact: https://xyzzy.ee

BIN
html/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

163
html/index.css Normal file
View file

@ -0,0 +1,163 @@
header,section{
display:block;
}
body{
font-size:16px;
margin:0 auto;
line-height:1.4;
background:#0b0c0d;
color:#d4d4d4;
font-family:"Noto Sans", Futura, Verdana, "Liberation Sans", sans-serif;
}
a{
color:#c79908;
text-decoration:none;
}
a:hover{
text-decoration:underline;
}
::selection {
color:#000;
background:#7a9e55;
}
h1{
font-size:2em;
margin:.67em 0;
}
kbd {
border-radius: 3px;
padding: 1px 2px 0;
border: 1px solid #666666;
}
#page{
position:relative;
}
#header{
padding-top:50px;
transform:translate3d(0,0,0);
}
.title-wrapper{
text-align:center;
}
.content{
margin-left:auto;
margin-right:auto;
padding:1px 20px;
position:relative;
z-index:2;
min-width:270px;
}
.title-group.animate{
animation:slide-fade-in .8s ease;
}
.title{
font-size:42px;
line-height:1.2;
margin:40px 0 0;
word-wrap:break-word;
}
.title a:active{
transform:translateY(1px);
}
.title a{
color:#e4e4e4;
display:block;
text-decoration:none;
}
.description{
color:#7a9e55;
margin:50px 0 0;
display:block;
}
.title+.description{
margin-top:10px!important;
}
.footdesc{
color:#666666;
margin:20px 0 0;
display:block;
font-size: 14px;
}
.footlink {
text-align: center;
}
.footlink.animate {
animation:slide-fade-in .8s ease;
}
.middle {
overflow: auto;
margin: auto;
display: inline;
text-align: center;
vertical-align: middle;
}
@media screen and (max-width:568px){
.content{
padding:1px 10px;
}
.title{
font-size:36px;
margin-top:30px;
}
.description{
margin-top:35px;
max-width:80%;
margin-left:auto;
margin-right:auto;
}
.title+.description{
margin-top:4px!important;
}
}
@keyframes slide-fade-in{
0%{
opacity:0;
transform:translate3d(0,20px,0);
}
to{
opacity:1;
transform:translate3d(0,0,0);
}
}
.divTable {
font-size: 16px;
line-height: 2.0;
display: table;
margin: auto;
border: 1px solid #444;
}
.divTable.animate {
animation:slide-fade-in .8s ease;
}
.divTableRow {
display: table-row;
}
.divTableHeading {
display: table-header-group;
background-color: #ddd;
}
.divTableCell, .divTableHead {
vertical-align: middle;
display: table-cell;
padding: 5px 10px;
border: 1px solid #444;
}
.divTableHeading {
display: table-header-group;
background-color: #ddd;
font-weight: bold;
}
.divTableFoot {
display: table-footer-group;
font-weight: bold;
background-color: #ddd;
}
.divTableBody {
display: table-row-group;
}
.valign {
vertical-align: middle;
}

41
html/index.html Normal file
View file

@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>xyzzy</title>
<meta name="description" content="a maze of twisty little passages">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" media="screen" href="index.css">
<link rel="shortcut icon" href="favicon.ico">
</head>
<body>
<header id="header">
<div class="title-wrapper content">
<div class="title-group">
<h1 class="title">xyzzy</h1>
<span class="description">
<em>a personal domain</em>
</span>
</div>
</div>
</header>
<main id="main">
<div class="content middle">
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Xyzzy_(computing)">The magic word XYZZY</a></p>
</div>
</main>
<footer id="footer">
<div class="content middle">
<span class="footdesc">[ .ee | .fi ]</span>
</div>
</footer>
</body>
</html>

8
html/robots.txt Normal file
View file

@ -0,0 +1,8 @@
User-Agent: *
# credit: jonty, asimov
Disallow: /harming/humans
Disallow: /ignoring/human/orders
Disallow: /harm/to/self
Allow: /