vps.org (6237B)
1 * Security 2 ** APT 3 *** Switch to HTTPS 4 ~sudo sed -i 's/http:/https:/' /etc/apt/sources.list~ 5 6 Granted, the repository signature provides enough protection; still, 7 no sense in wasting bandwidth and CPU if someone is meddling. 8 ** fail2ban 9 ~lastb~ says there's about 4000 login attempts per day; that makes 10 =/var/log/btmp= much bigger than it needs to be. 11 12 Debian's fail2ban comes with a jail for ~sshd~, so it's just a matter 13 of ~apt install fail2ban~. 14 ** Accounts 15 *** =root= 16 On OVH's Debian image: 17 - The =root= account has no password. 18 - =PermitRootLogin= defaults to =prohibit-password=: set it to =no=. 19 *** =debian= 20 Seems popular among bots looking for valid usernames. 21 22 Ideally I'd just rename the =debian= account, but renaming is not a 23 well-defined operation: ~usermod --login $name --move-home --home 24 /home/$name debian~ gets partway there, but leaves a bunch of 25 miscellany to take care of (e.g. sudoers). 26 27 So instead, 28 - create my own user account: ~sudo adduser 𝓊~ 29 - add it to all groups =debian= belongs to: 30 #+begin_src sh 31 groups=$(groups | tr ' ' '\n' | grep -v debian | paste -sd,) 32 sudo usermod --append --groups ${groups} 𝓊 33 #+end_src 34 - only allow authentication over SSH for this user: 35 #+begin_src conf 36 AllowUsers 𝓊 37 #+end_src 38 - in case I ever change my mind about =AllowUsers=, at least limit 39 password authentication to this user: 40 #+begin_src conf 41 PasswordAuthentication no 42 Match User 𝓊 43 PasswordAuthentication yes 44 #+end_src 45 46 * System 47 #+begin_src sh 48 sudo hostnamectl set-hostname $DOMAIN 49 sudo timedatectl set-timezone $tz 50 #+end_src 51 52 ** Console keyboard 53 To avoid stammering at the OVH KVM console: 54 #+begin_src sh 55 sudo apt install console-setup keyboard-configuration 56 # Pick "French - French (legacy, alt.)" for fr latin9. 57 # To bring up this selection again: 58 sudo dpkg-reconfigure keyboard-configuration 59 #+end_src 60 Then add =ctrl:nocaps= to =XKBOPTIONS= in =/etc/default/keyboard=, 61 =setupcon= in the KVM console, reboot. 62 * Services 63 ** Web server 64 Run ~sudo apt install nginx~; then, in 65 =/etc/nginx/sites-available/$DOMAIN=: 66 #+begin_src conf 67 server { 68 listen 80; 69 listen [::]:80; 70 71 server_name $DOMAIN www.$DOMAIN; 72 access_log /var/log/nginx/$DOMAIN.access.log; 73 74 root /var/www/$DOMAIN/html; 75 index index.html; 76 77 location / { 78 try_files $uri $uri/ =404; 79 } 80 } 81 #+end_src 82 Use one =access_log= file per site, to simplify analytics. 83 84 Run ~sudo systemctl restart nginx~. 85 *** fail2ban 86 With the following files in =$HOME=: 87 #+begin_src conf 88 # nginx-botsearch.local 89 [Init] 90 91 block = \S*(php|wp-|wordpress|jenkins|hudson|sql|boaform)[^,]* 92 93 [Definition] 94 95 # Change from distro: just remove the leading slash before <block>. 96 failregex = ^<HOST> \- \S+ \[\] \"(GET|POST|HEAD) <block> \S+\" 404 .+$ 97 ^ \[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+\"\, .*?$ 98 99 # jail.local 100 [nginx-http-auth] 101 enabled = true 102 103 [nginx-botsearch] 104 enabled = true 105 # Assume that each requests to $DOMAIN will be logged to "$DOMAIN.access.log". 106 logpath = /var/log/nginx/*access.log 107 #+end_src 108 109 Then: 110 #+begin_src sh 111 sudo cp ~/nginx-botsearch.local /etc/fail2ban/filter.d/ 112 sudo cp ~/jail.local /etc/fail2ban/ 113 sudo systemctl restart fail2ban 114 #+end_src 115 116 Check how these rules fare against real bot searches with: 117 #+begin_src sh 118 fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-botsearch.local 119 #+end_src 120 121 *** HTTPS 122 #+begin_src sh 123 sudo apt install certbot python3-certbot-nginx 124 sudo certbot --nginx -d $DOMAIN www.$DOMAIN 125 sudo systemctl reload nginx 126 #+end_src 127 128 ** Git server 129 *** SSH access 130 #+begin_src sh 131 sudo apt install git 132 sudo tee -a /etc/shells <<< $(which git-shell) 133 sudo adduser git --disabled-password --shell $(which git-shell) 134 sudo mkdir /srv/git 135 sudo chown git:git /srv/git 136 # For every new repo: 137 sudo -u git git init --bare --shared=group /srv/git/${repo} 138 #+end_src 139 140 *** Web mirror 141 With =/etc/nginx/sites-available/git.$DOMAIN=: 142 #+begin_src conf 143 server { 144 listen 80; 145 listen [::]:80; 146 147 server_name git.$DOMAIN; 148 access_log /var/log/nginx/git.$DOMAIN.access.log; 149 150 root /usr/share/cgit; 151 try_files $uri @cgit; 152 153 location @cgit { 154 include fastcgi_params; 155 fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi; 156 fastcgi_param PATH_INFO $uri; 157 fastcgi_param QUERY_STRING $args; 158 fastcgi_param HTTP_HOST $server_name; 159 fastcgi_pass unix:/run/fcgiwrap.socket; 160 } 161 } 162 #+end_src 163 164 And =/etc/cgitrc/=: 165 #+begin_src conf 166 css=/cgit.css 167 logo=/cgit.png 168 169 virtual-root=/ 170 # Change to https:// after setting up certbot: 171 clone-url=http://git.$DOMAIN/$CGIT_REPO_URL 172 snapshots=tar.xz 173 174 enable-git-config=1 175 enable-http-clone=1 176 enable-index-owner=0 177 178 scan-path=/srv/git 179 #+end_src 180 181 In each repository: 182 - fill in =description=, 183 - fill =[cgit]= section in =config= (=hide=, =owner=, =section=). 184 185 Do: 186 #+begin_src sh 187 sudo apt install cgit fcgiwrap 188 ( cd /etc/sites-enabled/ && ln -s ../sites-avaiable/git.$DOMAIN . ) 189 sudo systemctl restart nginx 190 # Make fail2ban notice the new log file. 191 sudo systemctl restart fail2ban 192 #+end_src 193 194 **** "Idle" vs default branch 195 cgit struggles to guess what to print for the "Idle" column on the 196 index page when the default branch is not "master". [[https://lists.zx2c4.com/pipermail/cgit/2020-August/004515.html][Workarounds]]: 197 198 - set =repo.defbranch=, 199 - update =agefile= with [[https://git.zx2c4.com/cgit/tree/contrib/hooks/post-receive.agefile][a post-receive hook]]. 200 ** CGI 201 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 202 program to handle requests: 203 204 - URI bits are passed through environment variables and stdin; 205 - the program spits the page on stdout. 206 207 Had some fun toying with Python's ~cgi~ module; sadly though the 208 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 209 paths]], and there's a [[https://pypi.org/project/legacy-cgi/][legacy-cgi package on PyPI]] if I really want to 210 keep using it I guess. 211 212 Also nginx has no support for CGI either, though their documentation 213 explains how to combine their FastCGI support with ~fcgiwrap~ to 214 enable CGI scripts.