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