diff options
Diffstat (limited to 'guides/sysadmin/cloud')
| -rw-r--r-- | guides/sysadmin/cloud/vps.org | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/guides/sysadmin/cloud/vps.org b/guides/sysadmin/cloud/vps.org new file mode 100644 index 0000000..986e373 --- /dev/null +++ b/guides/sysadmin/cloud/vps.org @@ -0,0 +1,198 @@ +* Security +** Switch APT to HTTPS +~sudo sed -i 's/http:/https:/' /etc/apt/sources.list~ + +Granted, the repository signature provides enough protection; still, +no sense in wasting bandwidth and CPU if someone is meddling. +** Tweak root access +On OVH's Debian image: +- The =root= account has no password. +- =PermitRootLogin= defaults to =prohibit-password=: set it to =no=. +** Enable fail2ban +~lastb~ says there's about 4000 login attempts per day; that makes +=/var/log/btmp= much bigger than it needs to be. + +Debian's fail2ban comes with a jail for ~sshd~, so it's just a matter +of ~apt install fail2ban~. +** Tweak user accounts +=debian= seems mildly popular among bots looking for valid usernames. + +Ideally I'd just rename the =debian= account, but renaming does not +seem to be a very well-defined operation: ~usermod --login $name +--move-home --home /home/$name debian~ gets partway there, but leaves +a bunch of miscellany to take care of (e.g. sudoers). + +So instead, I'll +- create my own user account: ~sudo adduser $name~ +- add it to all groups =debian= belongs to: + #+begin_src sh + groups=$(groups | tr ' ' '\n' | grep -v debian | paste -sd,) + sudo usermod --append --groups ${groups} ${name} + #+end_src +- only allow password authentication over SSH for this new user + account: + #+begin_src conf + PasswordAuthentication no + Match User … + PasswordAuthentication yes + #+end_src + +* System +#+begin_src sh +sudo hostnamectl set-hostname $DOMAIN +sudo timedatectl set-timezone $tz +#+end_src + +* Services +** Web server +Run ~sudo apt install nginx~; then, in +=/etc/nginx/sites-available/$DOMAIN=: +#+begin_src conf +server { + listen 80; + listen [::]:80; + + server_name $DOMAIN www.$DOMAIN; + access_log /var/log/nginx/$DOMAIN.access.log; + + root /var/www/$DOMAIN/html; + index index.html; + + location / { + try_files $uri $uri/ =404; + } +} +#+end_src +Use one =access_log= file per site, to simplify analytics. + +Run ~sudo systemctl restart nginx~. +*** fail2ban +With the following files in =$HOME=: +#+begin_src conf +# nginx-botsearch.local +[Init] + +block = \S*(php|wp-|wordpress|jenkins|hudson|sql|boaform)[^,]* + +[Definition] + +# Change from distro: just remove the leading slash before <block>. +failregex = ^<HOST> \- \S+ \[\] \"(GET|POST|HEAD) <block> \S+\" 404 .+$ + ^ \[error\] \d+#\d+: \*\d+ (\S+ )?\"\S+\" (failed|is not found) \(2\: No such file or directory\), client\: <HOST>\, server\: \S*\, request: \"(GET|POST|HEAD) \/<block> \S+\"\, .*?$ + +# jail.local +[nginx-http-auth] +enabled = true + +[nginx-botsearch] +enabled = true +# Assume that each requests to $DOMAIN will be logged to "$DOMAIN.access.log". +logpath = /var/log/nginx/*access.log +#+end_src + +Then: +#+begin_src sh +sudo cp ~/nginx-botsearch.local /etc/fail2ban/filter.d/ +sudo cp ~/jail.local /etc/fail2ban/ +sudo systemctl restart fail2ban +#+end_src + +Check how these rules fare against real bot searches with: +#+begin_src sh +fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-botsearch.local +#+end_src + +*** HTTPS +#+begin_src sh +sudo apt install certbot python3-certbot-nginx +sudo certbot --nginx -d $DOMAIN www.$DOMAIN +sudo systemctl reload nginx +#+end_src + +** Git server +*** SSH access +#+begin_src sh +sudo apt install git +sudo tee -a /etc/shells <<< $(which git-shell) +sudo adduser git --disabled-password --shell $(which git-shell) +sudo mkdir /srv/git +sudo chown git:git /srv/git +# For every new repo: +sudo -u git git init --bare --shared=group /srv/git/${repo} +#+end_src + +*** Web mirror +With =/etc/nginx/sites-available/git.$DOMAIN=: +#+begin_src conf +server { + listen 80; + listen [::]:80; + + server_name git.$DOMAIN; + access_log /var/log/nginx/git.$DOMAIN.access.log; + + root /usr/share/cgit; + try_files $uri @cgit; + + location @cgit { + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi; + fastcgi_param PATH_INFO $uri; + fastcgi_param QUERY_STRING $args; + fastcgi_param HTTP_HOST $server_name; + fastcgi_pass unix:/run/fcgiwrap.socket; + } +} +#+end_src + +And =/etc/cgitrc/=: +#+begin_src conf +css=/cgit.css +logo=/cgit.png + +virtual-root=/ +# Change to https:// after setting up certbot: +clone-url=http://git.$DOMAIN/$CGIT_REPO_URL +snapshots=tar.xz + +enable-git-config=1 +enable-http-clone=1 +enable-index-owner=0 + +scan-path=/srv/git +#+end_src + +In each repository: +- fill in =description=, +- fill =[cgit]= section in =config= (=hide=, =owner=, =section=). + +Do: +#+begin_src sh +sudo apt install cgit fcgiwrap +( cd /etc/sites-enabled/ && ln -s ../sites-avaiable/git.$DOMAIN . ) +sudo systemctl restart nginx +# Make fail2ban notice the new log file. +sudo systemctl restart fail2ban +#+end_src + +**** "Idle" vs default branch +cgit struggles to guess what to print for the "Idle" column on the +index page when the default branch is not "master". [[https://lists.zx2c4.com/pipermail/cgit/2020-August/004515.html][Workarounds]]: + +- set =repo.defbranch=, +- update =agefile= with [[https://git.zx2c4.com/cgit/tree/contrib/hooks/post-receive.agefile][a post-receive hook]]. +** CGI +I like the idea of [[https://en.wikipedia.org/wiki/Common_Gateway_Interface#Using_CGI_scripts][CGI "scripts"]], i.e. having the web server fire a +program to handle requests: + +- URI bits are passed through environment variables and stdin; +- the program spits the page on stdout. + +Had some fun toying with Python's ~cgi~ module; sadly though the +project has [[https://peps.python.org/pep-0594/#cgi][decided to deprecate it]]. The docs [[https://docs.python.org/3.11/library/cgi.html][suggest some migration +paths]], and there's a [[https://pypi.org/project/legacy-cgi/][legacy-cgi package on PyPI]] if I really want to +keep using it I guess. + +Also nginx has no support for CGI either, though their documentation +explains how to combine their FastCGI support with ~fcgiwrap~ to +enable CGI scripts. |
