Fixed minor formatting error in mail/web/git server notes.
[website_subgeniuskitty.com] / data / notes / mail_web_git_server.md
CommitLineData
4ccc706c
AT
1# Overview #
2
3These notes cover the creation of a server, hosted with Linode, running Debian
4Linux, offering the following services:
5
6 - Mail server
7
8 - SMTP via postfix
9
10 - IMAP via dovecot
11
12 - Realtime blacklists for SPAM rejection
13
14 - MySQL for virtual domain and user management.
15
16 - Web server
17
18 - Apache as central HTTP server with multiple vhosts.
19
562bd187 20 - [CMless](http://git.subgeniuskitty.com/cmless/.git) as CGI for content management
4ccc706c
AT
21
22 - ACME for automated SSL certificate management
23
24 - Git server
25
26 - SSH-based, authenticated read-write access to all git repositories
27
28 - Anonymous read-only access to a subset of git repositories via:
29
562bd187 30 - Customized [gitweb](http://git.subgeniuskitty.com/gitweb-sgk/.git) for
4ccc706c
AT
31 GUI git browsing with syntax highlighting, diffs, etc
32
33 - Git-daemon for cloning repositories via the `git://` protocol
34
35These notes are a high-level checklist for my reference rather than a
36step-by-step installation guide for the public. That means they make no attempt
37to explain all options at each step, rather that they mention only the options
38I use on my servers. It also means they use my domains, my file system paths,
39etc in the examples.
40
41
42# TODO List #
43
4ccc706c
AT
44 - Setup SSL with automatic certificate renewal.
45
46 - Find a reliable way to alert me when renewal fails.
47
48 - Websites should auto-redirect to the SSL version of the site for newer
49 browsers only. All sites should still be accessible on pre-SSL vintage
50 computers.
51
52 - Take a snapshot on Linode's backup service once the basic services are
53 operational.
54
7c21ac6b 55 - Migrate mail server. Delete old linode vserver after downloading a disk image.
4ccc706c
AT
56
57 - Finish this documentation.
58
59 - Improve CSS on gitweb, especially for displaying READMEs.
60
61
62# Basic Configuration #
63
64
65## General Information ##
66
67**Name:** SGK-Main-2020
68
69**OS:** Debian 10
70
71**Creation Date:** 2020-11-01
72
73**Filesystem Points of Interest:**
74
75 - `/srv/apache_vhosts`: Contains websites hosted by Apache2. See
76 `/etc/apache2/sites_available` for vhost configurations.
77
78 - `/srv/git`: Master location for bare git repositories. All are private.
79
80 - `/srv/gitweb_cache`: Contains checked out copies of git repositories from
81 `/srv/git`, publicly visible via `gitweb` and cloneable via `git-daemon`.
82
83
84## Preparation ##
85
86Setup DNS entries for `subgeniuskitty.com` and `logicavalanche.com` through
87Linode. Remember to do IPv4 and IPv6 entries for the bare domain, `www`,
88`mail`, and `git`.
89
90Create a new Debian 10 on Linode and update the system with `apt-get update &&
91apt-get upgrade`.
92
93Add a user via `adduser ataylor` and following the prompts. Edit
94`/etc/ssh/sshd_config` to set `PermitRootLogin: no` and restart SSH. For both
95the `ataylor` user and `root`, add the line `set mouse=` to `~/.vimrc` in order
96to disable mouse support in `vim`, allowing normal mark-and-paste in the
97terminal.
98
99Install useful packages:
100
101 apt-get install net-tools screen bzip2 zip
102
103
104# Web Server #
105
106
107## HTTP: Apache2 ##
108
109Install Apache2.
110
111 apt-get install apache2
112
b989fcd3
AT
113If not already defined elsewhere, add a `ServerName 127.0.0.1` entry to the
114bottom of `/etc/apache2/apache2.conf`, or whatever is appropriate.
2fbf9e1e 115
4ccc706c
AT
116Since we use `/srv` instead of `/var/www`, edit `/etc/apache2/apache2.conf` to
117comment out the `<Directory ...>` entry for `/var/www` and replace it with
118this:
119
120 <Directory /srv/>
121 Options Indexes FollowSymLinks
122 AllowOverride None
123 Require all granted
124 </Directory>
125
126Make and edit the file `/srv/apache_vhosts/default/index.html` with (rewritten)
127contents:
128
129 <p>Invalid VHost</p>
130 <p>Contact user @at@ domain .dot. com</p>
131
132Ensure everything under `/srv/apache_vhosts`, including that directory itself,
133is owned recursively by `www-data:www-data`.
134
135Edit `/etc/apache2/sites-available/000-default.conf` and
136`/etc/apache2/sites-available/000-default.conf`, changing references for the
137default sites from `/var/www/...` to `/srv/apache_vhosts/...` as necessary.
138
139Reload Apache2 with `systemctl reload apache2` and check status with `systemctl
140status apache2`.
141
142
143### SSL ###
144
145TODO
146
147TODO
148
149TODO
150
151TODO
152
153TODO
154
155TODO
156
157TODO
158
159TODO
160
161
162## Basic Website ##
163
164Using <http://archive.subgeniuskitty.com> as an example of a basic website,
165create an Apache2 vhost configuration file at
166`/etc/apache2/sites-available/archive.subgeniuskitty.com.conf`.
167
168 <VirtualHost *:80>
169 DocumentRoot "/srv/apache_vhosts/archive.subgeniuskitty.com"
170 ServerName archive.subgeniuskitty.com
171 ServerAdmin webmaster@subgeniuskitty.com
172 ErrorLog /var/log/apache2/error_log.archive.subgeniuskitty.com
173 CustomLog /var/log/apache2/access_log.archive.subgeniuskitty.com combined
174 <Directory "/srv/apache_vhosts/archive.subgeniuskitty.com">
175 Options +FollowSymLinks
176 AllowOverride None
177 Require all granted
178 </Directory>
179 <Directory "/srv/apache_vhosts/archive.subgeniuskitty.com/sites">
180 Options +FollowSymLinks +Indexes
181 AllowOverride None
182 Require all granted
183 </Directory>
184 </VirtualHost>
185
186Make the directory `/srv/apache_vhosts/archive.subgeniuskitty.com`, move your
187data into it, and ensure everything is owned by `www-data:www-data`.
188
189Enable the vhost with `a2ensite archive.subgeniuskitty.com` and `systemctl
190reload apache2`.
191
192
193## CMless Website ##
194
195Enable `mod_rewrite` and either `mod_cgi` or `mod_cgid` as appropriate with
196these commands.
197
198 a2enmod rewrite
199 a2enmod cgid
200
201Install `discount` to convert Markdown to HTML.
202
203 apt-get install discount
204
205Create `/etc/apache2/sites-available/subgeniuskitty.com.conf`.
206
207 <VirtualHost *:80>
208 DocumentRoot "/srv/apache_vhosts/subgeniuskitty.com"
209 ServerName subgeniuskitty.com
210 ServerAlias www.subgeniuskitty.com
211 ServerAdmin webmaster@subgeniuskitty.com
212 ErrorLog /var/log/apache2/error_log.subgeniuskitty.com
213 CustomLog /var/log/apache2/access_log.subgeniuskitty.com combined
214 AddHandler cgi-script .py
215 <Directory "/srv/apache_vhosts/subgeniuskitty.com">
216 Options -ExecCGI -Indexes
217 AllowOverride None
218 Require all granted
219 </Directory>
220 <Directory "/srv/apache_vhosts/subgeniuskitty.com/bin">
221 Options ExecCGI
222 AllowOverride None
223 Require all granted
224 </Directory>
225 RewriteEngine On
226 RewriteRule (.*) /srv/apache_vhosts/subgeniuskitty.com/site/data/$1
227 RewriteCond %{REQUEST_FILENAME} !-f
228 RewriteRule .* /srv/apache_vhosts/subgeniuskitty.com/bin/cmless.py
229 </VirtualHost>
230
231Enable the site with `a2ensite subgeniuskitty.com`.
232
562bd187 233Clone a copy of [CMless](http://git.subgeniuskitty.com/cmless/.git) into
4ccc706c
AT
234`/srv/apache_vhosts/subgeniuskitty.com` and ensure everything is owned by
235`www-data:www-data`.
236
237Clone a copy of [the website
562bd187 238data](http://git.subgeniuskitty.com/website_subgeniuskitty.com/.git) into
4ccc706c
AT
239`/srv/apache_vhosts/subgeniuskitty.com/site`. Verify it is owned by
240`ataylor:ataylor` (but still readable by all) so we can update the site
241remotely with a simple script like this:
242
243 #!/usr/local/bin/bash
244 #
245 # Usage: No cmdline arguments
246 # Update content of www.subgeniuskitty.com to the latest version.
247
248 ssh ataylor@git.subgeniuskitty.com "cd /srv/apache_vhosts/subgeniuskitty.com/site && git pull"
249
250Reload Apache's configuration with `systemctl reload apache2` and test access
251to the website.
252
253Repeat this process for <http://logicavalanche.com>.
254
255
256# Git Server #
257
258The git server provides read-write access to a private collection of bare
259repositories located at `/srv/git` via SSH. It also provides read-only access
260to a public collection of normal repositories located at `/srv/gitweb_cache`
261via <http://git.subgeniuskitty.com/repo_name> through gitweb and via
262[git://git.subgeniuskitty.com/repo_name](git://git.subgeniuskitty.com/repo_name)
263through `git-daemon`.
264
265
266## Read/Write: SSH ##
267
268Install git with `apt-get install git`.
269
270On my workstation, generate an SSH key with `ssh-keygen -t rsa`.
271
272On the server, as user `ataylor`:
273
274 mkdir ~/.ssh
275 chmod 700 ~/.ssh
276 touch ~/.ssh/authorized_keys
277 chmod 600 ~/.ssh/authorized_keys
278
279Then `cat` the public SSH key from the workstation to the server, appending it
280onto `~/.ssh/authorized_keys`.
281
282Verify ability to login using new certificate.
283
284Create a directory `/srv/git` owned by `ataylor:ataylor`. This will hold bare
285git repositories and act as the central private store for SGK git repos.
286
287From the workstation, we can create a new bare repository on the server. For
288example, packed up in a simple script:
289
290 #!/usr/local/bin/bash
291 #
292 # Usage: sgkgit-new-repo project_name
293 # Setup a repository on the SGK git server.
294
295 if [ "$#" -ne 1 ]; then
296 echo "Must specify repo name as only parameter."
297 exit 2
298 fi
299
300 ssh ataylor@git.subgeniuskitty.com "git init --bare /srv/git/$@"
301
302We then set the remote of an existing repository to the new bare repository.
303Again as a script:
304
305 #!/usr/local/bin/bash
306 #
307 # Usage: sgkgit-set-origin project_name
308 # Sets remote to the correct path for 'project_name' on the SGK git server.
309
310 if [ "$#" -ne 1 ]; then
311 echo "Must specify repo name as only parameter."
312 exit 2
313 fi
314
315 git remote remove origin
316 git remote add origin ataylor@git.subgeniuskitty.com:/srv/git/$@
317 echo "Remember to make the first push with \"git push --set-upstream origin master\"."
318
319After the first push to the bare repository on the server, simply `git push`
320and `git pull` as normal.
321
322You can also list the repositories currently on the server with simple SSH commands.
323
324 #!/usr/local/bin/bash
325 #
326 # Usage: No cmdline arguments.
327 # List all remote repos available on SGK git server.
328
329 ssh ataylor@git.subgeniuskitty.com "ls -lt /srv/git/"
330
331Clone one of these repositories, with remote correspondingly pre-set.
332
333 #!/usr/local/bin/bash
334 #
335 # Usage: sgkgit-checkout project_name
336 # Clone a local copy of repo "project_name" from SGK git server.
337
338 if [ "$#" -ne 1 ]; then
339 echo "Must specify repo name as only parameter."
340 exit 2
341 fi
342
343 git clone ataylor@git.subgeniuskitty.com:/srv/git/$@
344
345
346## Read-Only: Gitweb ##
347
348Enable `mod_rewrite` and either `mod_cgi` or `mod_cgid` as appropriate with
349these commands.
350
351 a2enmod rewrite
352 a2enmod cgid
353
354Install `discount` to convert Markdown to HTML, `highlight` for syntax
355highlighting, and `gitweb` to pull in any dependencies.
356
357 apt-get install discount highlight gitweb
358
359Create `/etc/apache2/sites-available/git.subgeniuskitty.com.conf`.
360
361 <VirtualHost *:80>
362 ServerName git.subgeniuskitty.com
363 ServerAdmin webmaster@subgeniuskitty.com
364
365 DocumentRoot "/srv/apache_vhosts/git.subgeniuskitty.com"
366
367 ErrorLog /var/log/apache2/error_log.git.subgeniuskitty.com
368 CustomLog /var/log/apache2/access_log.git.subgeniuskitty.com combined
369
370 <Directory "/srv/apache_vhosts/git.subgeniuskitty.com">
371 Options +FollowSymLinks +ExecCGI
372 AllowOverride None
373 Require all granted
374 AddHandler cgi-script .cgi
375 DirectoryIndex gitweb.cgi
376 RewriteEngine On
377 RewriteCond %{REQUEST_FILENAME} !-f
378 RewriteCond %{REQUEST_FILENAME} !-d
379 RewriteRule ^.* /gitweb.cgi/$0 [L,PT]
380 </Directory>
381 </VirtualHost>
382
383Enable the site with `a2ensite git.subgeniuskitty.com`.
384
562bd187 385Clone a copy of [the SGK gitweb fork](http://git.subgeniuskitty.com/gitweb-sgk/.git)
4ccc706c
AT
386into `/srv/apache_vhosts/git.subgeniuskitty.com` and ensure everything is owned
387by `ataylor:ataylor` (but world-readable!) so we can update via SSH with a script like this.
388
389 #!/usr/local/bin/bash
390 #
391 # Usage: No cmdline arguments
392 # Update git.subgeniuskitty.com to the latest version of forked gitweb repo.
393
394 ssh ataylor@git.subgeniuskitty.com "cd /srv/apache_vhosts/git.subgeniuskitty.com && git pull"
395
396This fork makes a few changes to gitweb, displaying READMEs by default, etc.
397Read the gitweb project's `README.md` for more details.
398
399Create a gitweb config file at `/etc/gitweb.conf`. Note that we are adding a
400`clone url` to the toolbar with the address of our `git-daemon` server.
401Remember to set that up.
402
403 $site_name = "git.subgeniuskitty.com";
404 @git_base_url_list = ("git://git.subgeniuskitty.com");
405 $projectroot = "/srv/gitweb_cache";
406 $git_temp = "/tmp";
407
408 @stylesheets = ("static/gitweb.css");
409 $javascript = "static/gitweb.js";
410 $logo = "static/sgk-logo.png";
411 $favicon = "static/git-favicon.png";
412
413 # git-diff-tree(1) options to use for generated patches
414 @diff_opts = ();
415
416 # Enable PATH_INFO so the server can produce URLs of the
417 # form: http://git.hokietux.net/project.git/xxx/xxx
418 # This allows for pretty URLs *within* the Git repository,
419 # also needs the Apache rewrite rules for full effect.
420 $feature{'pathinfo'}{'default'} = [1];
421
422 # HTML text to include as home page header.
423 $home_text = "indextext.html";
424
425 # Add a toolbar option with the 'git clone url'.
426 $feature{'actions'}{'default'} = [('clone url', 'git://git.subgeniuskitty.com/%n', 'summary')];
427
428 # Category name is read from .git/category, in the same manner as .git/description.
429 $projects_list_group_categories = 1;
430 $project_list_default_category = "misc";
431
432 # Needed for displaying README files.
433 $prevent_xss = 0;
434
435 # Enable syntax highlighting.
436 $feature{'highlight'}{'default'} = [1];
437
438 ################################################################################
439
440 # Enable blame, pickaxe search, snapshop, search, and grep
441 # support, but still allow individual projects to turn them off.
442 # These are features that users can use to interact with your Git trees. They
443 # consume some CPU whenever a user uses them, so you can turn them off if you
444 # need to. Note that the 'override' option means that you can override the
445 # setting on a per-repository basis.
446 $feature{'blame'}{'default'} = [1];
447 $feature{'blame'}{'override'} = [1];
448
449 $feature{'pickaxe'}{'default'} = [1];
450 $feature{'pickaxe'}{'override'} = [1];
451
452 $feature{'snapshot'}{'default'} = [1];
453 $feature{'snapshot'}{'override'} = [1];
454
455 $feature{'search'}{'default'} = [1];
456
457 $feature{'grep'}{'default'} = [1];
458 $feature{'grep'}{'override'} = [1];
459
460Create the directory `/srv/gitweb_cache`. It should be readable by the web
461server (`www-data`), but owned by `ataylor:ataylor` so that simple scripts like
462the following can make repositories public/private.
463
464 #!/usr/local/bin/bash
465 #
466 # Usage: sgkgit-make-public project_name
467 # Make a repo accessible through gitweb and git-daemon.
468
469 if [ "$#" -ne 1 ]; then
470 echo "Must specify repo name as only parameter."
471 exit 2
472 fi
473
474 read -p "Enter a category name: " category
475 read -p "Enter a description: " description
476
477 echo "You entered:"
478 printf "\tCategory: $category \n"
479 printf "\tDescription: $description \n"
480 read -p "Confirm (y/n)?: " confirm
481
482 if [ "$confirm" == "y" ]; then
483 ssh ataylor@git.subgeniuskitty.com "cd /srv/gitweb_cache && rm -rf $@ && git clone /srv/git/$@"
484 ssh ataylor@git.subgeniuskitty.com "cd /srv/git && \
485 printf '#!/usr/bin/bash\ncd /srv/gitweb_cache/$@\ngit --git-dir=.git pull\n' > \
486 /srv/git/$@/hooks/post-update && chmod +x /srv/git/$@/hooks/post-update"
487 ssh ataylor@git.subgeniuskitty.com "echo \"$category\" > /srv/gitweb_cache/$@/.git/category"
488 ssh ataylor@git.subgeniuskitty.com "echo \"$description\" > /srv/gitweb_cache/$@/.git/description"
489 fi
490
491In addition to cloning the git repo into `/srv/gitweb_cache` from the private,
492SSH-only collection of repositories in `/srv/git`, this also creates a
493`post-update` hook in the bare repo in `/srv/git` to ensure that gitweb's cache
494is updated every time a push is made via SSH to the private repo.
495
496This script also sets the `.git/description` and `.git/category` files needed
497by gitweb for displays like the project summary page. These are not under
498version control and may be directly edited to change what is displayed in
499gitweb.
500
501We can remove the public cache, making the repo private-only as shown in this
502script.
503
504 #!/usr/local/bin/bash
505 #
506 # Usage: sgkgit-make-private project_name
507 # Make a repo inaccessible through gitweb and git-daemon.
508
509 if [ "$#" -ne 1 ]; then
510 echo "Must specify repo name as only parameter."
511 exit 2
512 fi
513
514 ssh ataylor@git.subgeniuskitty.com "rm -rf /srv/gitweb_cache/$@ && rm /srv/git/$@/hooks/post-update"
515
516Any repos made private/public via this method are immediately reflected in
517`gitweb` and `git-daemon`.
518
519
520## Read-Only: Git Daemon ##
521
522Since `git` was already installed, simply create a new service profile in
523`/etc/systemd/system/git-daemon.service`.
524
525 [Unit]
526 Description=Start Git Daemon
527
528 [Service]
529 ExecStart=/usr/bin/git daemon --export-all --reuseaddr --base-path=/srv/gitweb_cache/ /srv/gitweb_cache/
530
531 Restart=always
532 RestartSec=500ms
533
534 StandardOutput=syslog
535 StandardError=syslog
536 SyslogIdentifier=git-daemon
537
538 User=ataylor
539 Group=ataylor
540
541 [Install]
542 WantedBy=multi-user.target
543
544Then start the service with `systemctl daemon-reload` and `systemctl start
545git-daemon`. Verify functionality before enabling daemon-autostart with
546`systemctl enable git-daemon`.
547
548The `--export-all` flag tells `git-daemon` to export all repositories located
549at the specified path, regardless of the presence (or lack) of the file
550`.git/git-daemon-export-ok` in the individual repository. We do this because
551only public repos are checked out to this folder. All private repos are kept
552entirely elsewhere.
553
554The directory `/srv/gitweb_cache` should have already been created when
555installing gitweb. If not, go read those instructions.
556
557A repository located at `/srv/gitweb_cache/repo_name` may be cloned through
558`git-daemon` at
559[git://git.subgeniuskitty.com/repo_name](git://git.subgeniuskitty.com/repo_name)
560with `git clone`.
561
562
563# Mail Server #
564
565TODO
566
567TODO
568
569TODO
570
571TODO
572
573TODO
574
575TODO
576
577TODO
578
579TODO
580