diff options
Diffstat (limited to 'selfpublish.sh')
-rwxr-xr-x | selfpublish.sh | 980 |
1 files changed, 980 insertions, 0 deletions
diff --git a/selfpublish.sh b/selfpublish.sh new file mode 100755 index 0000000..1d97356 --- /dev/null +++ b/selfpublish.sh | |||
@@ -0,0 +1,980 @@ | |||
1 | #!/bin/sh | ||
2 | set -e | ||
3 | |||
4 | DYNDNSHOST=cryptonomic.net | ||
5 | DEFAULT_UPSTREAM=d@cryptonomic.net:public_git/selfpublish.sh | ||
6 | |||
7 | dependencies() | ||
8 | { | ||
9 | cat <<EOF | ||
10 | apache2 (>= 2.4.46) | ||
11 | basez | ||
12 | bind9-dnsutils | ||
13 | cgit | ||
14 | curl | ||
15 | fortune-mod | ||
16 | fortunes-min | ||
17 | gnupg (>= 2.2.14) | ||
18 | libssl1.1 (>= 1.1.1d) | ||
19 | netmask | ||
20 | openssl | ||
21 | ssh | ||
22 | EOF | ||
23 | } | ||
24 | |||
25 | force() | ||
26 | { | ||
27 | [ "$FORCE" ] | ||
28 | } | ||
29 | |||
30 | in_group() | ||
31 | { | ||
32 | local g | ||
33 | for g in $(groups) | ||
34 | do | ||
35 | [ "$g" = "$1" ] && return | ||
36 | done | ||
37 | false | ||
38 | } | ||
39 | |||
40 | as_root() | ||
41 | { | ||
42 | if [ "$(id -u)" = 0 ] | ||
43 | then | ||
44 | "$@" | ||
45 | elif sudo -n true 2>/dev/null || in_group sudo | ||
46 | then | ||
47 | sudo "$@" | ||
48 | else | ||
49 | su -c "$*" | ||
50 | fi | ||
51 | } | ||
52 | |||
53 | apt_install() | ||
54 | { | ||
55 | as_root apt-get install "$@" | ||
56 | } | ||
57 | |||
58 | write_line_once() | ||
59 | { | ||
60 | local line="$2" target_file="$1" | ||
61 | if [ -e "$target_file" ] && grep -qF "$line" "$target_file" | ||
62 | then | ||
63 | return | ||
64 | else | ||
65 | printf %s "$line" | as_root tee -a "$target_file" >/dev/null | ||
66 | fi | ||
67 | } | ||
68 | |||
69 | self_codename() | ||
70 | { | ||
71 | if which lsb_release >/dev/null | ||
72 | then | ||
73 | lsb_release -cs | ||
74 | else | ||
75 | sed -ne 's/^VERSION_CODENAME=//p' /etc/os-release | ||
76 | fi | ||
77 | } | ||
78 | |||
79 | dpkg_install() | ||
80 | { | ||
81 | case "$(self_codename)" in | ||
82 | buster) | ||
83 | apt-cache policy | grep -q 'http://httpredir.debian.org/debian buster-backports/main' || | ||
84 | write_line_once /etc/apt/sources.list.d/buster-backports.list \ | ||
85 | 'deb http://httpredir.debian.org/debian buster-backports main' | ||
86 | ;; | ||
87 | esac | ||
88 | |||
89 | as_root $SHELL -c "set -$- +e; dpkg -i $*; apt-get -t buster-backports -f install" | ||
90 | } | ||
91 | |||
92 | fmt_dependencies() | ||
93 | { | ||
94 | local outline= | ||
95 | while read inline | ||
96 | do | ||
97 | if [ ! "$outline" ] | ||
98 | then | ||
99 | outline="Depends: $inline" | ||
100 | elif [ $((${#outline} + ${#inline})) -ge 60 ] | ||
101 | then | ||
102 | printf '%s,\n' "$outline" | ||
103 | outline=" $inline" | ||
104 | else | ||
105 | outline="$outline, $inline" | ||
106 | fi | ||
107 | done | ||
108 | if [ "$outline" ] | ||
109 | then | ||
110 | printf '%s\n' "$outline" | ||
111 | fi | ||
112 | } | ||
113 | |||
114 | control_file_file() | ||
115 | { | ||
116 | printf 'File: %s %s\n' "$1" "$2" | ||
117 | sed -e ' s/^[.]/../; s/^$/./; s/^/ /; ' | ||
118 | } | ||
119 | |||
120 | control_file_file_check() | ||
121 | { | ||
122 | local prefix RESULT=0 header= data o | ||
123 | tempout=$(mktemp) | ||
124 | while [ "$header" ] || read header | ||
125 | do | ||
126 | set -- $header | ||
127 | case "$1" in | ||
128 | File:) | ||
129 | |||
130 | case "$2" in | ||
131 | /*) ;; | ||
132 | *) return 1 ;; | ||
133 | esac | ||
134 | |||
135 | :> "$tempout" | ||
136 | while IFS= read -r data | ||
137 | do | ||
138 | case "$data" in | ||
139 | ' .'*) | ||
140 | printf '%s\n' "${data# .}" >> "$tempout" | ||
141 | continue | ||
142 | ;; | ||
143 | ' '*) | ||
144 | printf '%s\n' "${data# }" >> "$tempout" | ||
145 | continue | ||
146 | ;; | ||
147 | esac | ||
148 | |||
149 | if ! diff -q "$2" "$tempout" | ||
150 | then | ||
151 | RESULT=1 | ||
152 | as_root install -v -m "$3" "$tempout" -T "$2" | ||
153 | fi | ||
154 | header=$data | ||
155 | continue 2 | ||
156 | |||
157 | done | ||
158 | ;; | ||
159 | esac | ||
160 | header= | ||
161 | done | ||
162 | return $RESULT | ||
163 | } | ||
164 | |||
165 | SELF_VERSION='0.1' | ||
166 | SELF_EXECUTABLE=$(realpath "$0") | ||
167 | SELF_PACKAGE=cryptonomic | ||
168 | control_file() | ||
169 | { | ||
170 | cat <<EOF | ||
171 | Version: ${SELF_VERSION} | ||
172 | Package: ${SELF_PACKAGE} | ||
173 | Description: selfpublish.sh dependency package | ||
174 | This package depends on the dependencies of the | ||
175 | selfpublish.sh script, and is installed by that | ||
176 | script to self-satisfy those dependencies. | ||
177 | EOF | ||
178 | dependencies | fmt_dependencies | ||
179 | # cat <<EOF | ||
180 | # File: /usr/share/doc/${SELF_PACKAGE}/README.Debian 644 | ||
181 | # This gets overwritten :( | ||
182 | # EOF | ||
183 | control_file_file /usr/lib/cryptonomic/cryptonomic-ipv4 755 <<'EOF' | ||
184 | #!/bin/bash | ||
185 | |||
186 | ipv4_check_masks_() | ||
187 | { | ||
188 | ip=$(netmask -x "$1"/32) || return | ||
189 | ip=${ip%/*} | ||
190 | shift | ||
191 | for cidr in "$@" | ||
192 | do | ||
193 | prefix=${cidr%/*} | ||
194 | mask=${cidr#*/} | ||
195 | if (( (ip & mask) == prefix )) | ||
196 | then | ||
197 | return 1 | ||
198 | fi | ||
199 | done | ||
200 | true | ||
201 | } | ||
202 | |||
203 | ipv4_address_is_public() | ||
204 | { | ||
205 | ipv4_check_masks_ "$1" $CIDR_NONPUBLIC | ||
206 | } | ||
207 | |||
208 | CIDR_NONPUBLIC=$(netmask -x 127.0/8 10.0/8 192.168.0/16 172.16.0/20 169.254.0/16) | ||
209 | |||
210 | ip -4 -oneline addr | | ||
211 | while read n dev _ inet _ | ||
212 | do | ||
213 | a=${inet%%/*} | ||
214 | if ipv4_address_is_public "$a"; then | ||
215 | echo "$a" | ||
216 | fi | ||
217 | done | ||
218 | |||
219 | |||
220 | EOF | ||
221 | control_file_file /usr/lib/cryptonomic/cryptonomic-gpg 755 <<'EOF' | ||
222 | #!/bin/sh | ||
223 | |||
224 | die() | ||
225 | { | ||
226 | printf 'Error: %s\n' "$*" | ||
227 | exit 1 | ||
228 | } | ||
229 | |||
230 | process_colons() | ||
231 | { | ||
232 | local callback="$*" IFS process_colons_break= | ||
233 | set -- | ||
234 | while read LINE | ||
235 | do | ||
236 | IFS=: | ||
237 | set -- $LINE | ||
238 | unset IFS | ||
239 | |||
240 | case "$1" in | ||
241 | sec|pub) CURRENT_KEY=$LINE ;; | ||
242 | fpr) CURRENT_FPR=${10} ;; | ||
243 | esac | ||
244 | |||
245 | if [ "$CURRENT_KEY" -a "$CURRENT_FPR" ] | ||
246 | then | ||
247 | $callback "$@" | ||
248 | fi | ||
249 | |||
250 | if [ "$process_colons_break" ] | ||
251 | then | ||
252 | break | ||
253 | fi | ||
254 | done | ||
255 | } | ||
256 | |||
257 | is_secret_key() | ||
258 | { | ||
259 | case "$CURRENT_KEY" in | ||
260 | sec:*) true ;; | ||
261 | *) false ;; | ||
262 | esac | ||
263 | } | ||
264 | |||
265 | match_domain() | ||
266 | { | ||
267 | domain=$1 | ||
268 | action=$2 | ||
269 | shift 2 | ||
270 | case "$1:$2:${10%>}" in | ||
271 | uid:u:*.${domain}) $action "$@";; | ||
272 | esac | ||
273 | } | ||
274 | |||
275 | match_first_secret_key() | ||
276 | { | ||
277 | action=$1 | ||
278 | shift | ||
279 | is_secret_key || return | ||
280 | case "$1:$2" in | ||
281 | fpr:*) $action "$@" | ||
282 | process_colons_break=y | ||
283 | ;; | ||
284 | esac | ||
285 | } | ||
286 | |||
287 | show_fpr() | ||
288 | { | ||
289 | echo ${CURRENT_FPR} | ||
290 | } | ||
291 | |||
292 | find_secret_key_with_domain() | ||
293 | { | ||
294 | process_colons match_domain "$1" show_fpr | ||
295 | } | ||
296 | |||
297 | force() { [ "$FORCE" ]; } | ||
298 | verbose() | ||
299 | { | ||
300 | if [ "$VERBOSE" ] | ||
301 | then | ||
302 | "$@" | ||
303 | fi | ||
304 | } | ||
305 | get_home() | ||
306 | { | ||
307 | [ "$1" ] && getent passwd "$1" | (IFS=: read _ _ _ _ _ h _; echo $h) | ||
308 | } | ||
309 | |||
310 | GPG() | ||
311 | { | ||
312 | set -- gpg "$@" | ||
313 | if [ "$SUDO_USER" ] | ||
314 | then | ||
315 | su "$SUDO_USER" -c "$(bash -c 'printf "%q " "$@"' bash "$@")" | ||
316 | else | ||
317 | "$@" | ||
318 | fi | ||
319 | } | ||
320 | |||
321 | ensure_key_exists() | ||
322 | { | ||
323 | # 2. CHECK IF EXISTING SECRET KEY | ||
324 | t=$(GPG -K --with-colons) | ||
325 | if [ "$t" ] | ||
326 | then | ||
327 | # 3. CHECK IF EXISTING KEY HAS UID | ||
328 | if GPG -K --with-colons | find_secret_key_with_domain "${cryptonomic_hostname#*.}" | grep -q . && ! force | ||
329 | then | ||
330 | return | ||
331 | fi | ||
332 | |||
333 | # 4. ADD UID TO EXISTING KEY | ||
334 | gpg_default_key=$(GPG -K --with-colons | process_colons match_first_secret_key show_fpr) | ||
335 | [ "$gpg_default_key" ] | ||
336 | GPG --quick-add-uid "$gpg_default_key" "$uid" || force | ||
337 | verbose GPG -K "$gpg_default_key" | ||
338 | else | ||
339 | # 2.5 GENERATE NEW KEY | ||
340 | GPG --batch --passphrase '' --quick-generate-key "$uid" | ||
341 | verbose GPG -K "$uid" | ||
342 | fi | ||
343 | } | ||
344 | |||
345 | make_directories_maybe_become_root() | ||
346 | { | ||
347 | if [ -d "$html_dir" ] | ||
348 | then | ||
349 | if [ -d "$hu_dir" ] | ||
350 | then | ||
351 | [ -w "$hu_dir" ] || exec sudo -- "$0" "$@" || die "failed to exec sudo" | ||
352 | else | ||
353 | [ -w "$html_dir" ] || exec sudo -- "$0" "$@" || die "failed to exec sudo" | ||
354 | mkdir -m0755 -p "$hu_dir" | ||
355 | fi | ||
356 | else | ||
357 | die "Directory not found: '$html_dir' -- you probably need to run selfpublish.sh" | ||
358 | # TODO: just run selfpublish here | ||
359 | fi | ||
360 | find "$html_dir"/.well-known/openpgpkey/ -xdev -type d -exec chmod 755 '{}' ';' | ||
361 | } | ||
362 | |||
363 | process_key() | ||
364 | { | ||
365 | local uid="$1" domain="${1#*@}" destdir="$2" tdir | ||
366 | while read keyid some_uid | ||
367 | do | ||
368 | [ "$some_uid" = "$uid" ] || continue | ||
369 | tempdir=$(mktemp -d) | ||
370 | /usr/lib/gnupg/gpg-wks-client --install-key -C "$tempdir" "$keyid" "$uid" 2>/dev/null | ||
371 | mkdir -p "$destdir" | ||
372 | mv "$tempdir"/"$domain"/hu/* -t "$destdir" | ||
373 | rm -rf "$tempdir" | ||
374 | done | ||
375 | } | ||
376 | |||
377 | set -e | ||
378 | |||
379 | # 1. GET CRYPTONOMIC UID | ||
380 | cryptonomic_hostname=$(cryptonomic hostname) | ||
381 | [ "$cryptonomic_hostname" ] | ||
382 | |||
383 | if [ "$SUDO_USER" ] | ||
384 | then | ||
385 | username=$SUDO_USER | ||
386 | export GNUPGHOME=~$username/.gnupg | ||
387 | else | ||
388 | username=$(id -un) | ||
389 | fi | ||
390 | [ "$username" ] | ||
391 | |||
392 | uid=${username}@${cryptonomic_hostname} | ||
393 | html_dir=/srv/${cryptonomic_hostname#*.}/public_html | ||
394 | hu_dir=$html_dir/.well-known/openpgpkey/hu | ||
395 | |||
396 | ensure_key_exists | ||
397 | |||
398 | make_directories_maybe_become_root | ||
399 | |||
400 | GPG --list-options show-only-fpr-mbox -k "$uid" 2>&- | process_key "$uid" "$hu_dir" | ||
401 | |||
402 | printf 'gpg --locate-keys %s\n' "$uid" | ||
403 | EOF | ||
404 | control_file_file /usr/bin/cryptonomic 755 <<'EOF' | ||
405 | #!/bin/dash | ||
406 | set -e | ||
407 | |||
408 | DOMAIN=cryptonomic.net | ||
409 | HASH_TYPE=2 | ||
410 | HOSTNAME=$(hostname --short) | ||
411 | KEY_TYPE=ed25519 | ||
412 | KEY_FILE=/etc/ssh/ssh_host_${KEY_TYPE}_key | ||
413 | HostKeyAlgorithm=ssh-ed25519 | ||
414 | |||
415 | die() { echo "$0: Error: $*" >&2; exit 1; } | ||
416 | |||
417 | b16_to_b32() | ||
418 | { | ||
419 | printf %s "$1" | basez -x -d | basez -j -l | tr -d = | ||
420 | } | ||
421 | |||
422 | get_domain() | ||
423 | { | ||
424 | get_sshfp "$1" | ||
425 | |||
426 | domain=$(printf %s.%s.%s "$sshfp_b32" "$KEY_TYPE" "$DOMAIN" | tail -c64) | ||
427 | domain=$(printf %s.%s "$HOSTNAME" "$domain") | ||
428 | } | ||
429 | |||
430 | get_sshfp() | ||
431 | { | ||
432 | [ -f "$1" ] || return | ||
433 | sshfp_b16=$(ssh-keygen -r . -f "$1" | sed -ne 's/^. IN SSHFP [0-9]* '"$HASH_TYPE"' //p') && | ||
434 | [ "$sshfp_b16" ] || die "could not determine ssh client fingerprint" | ||
435 | sshfp_b32=$(b16_to_b32 "$sshfp_b16") | ||
436 | } | ||
437 | |||
438 | indent() | ||
439 | { | ||
440 | sed 's/^/\t/' | ||
441 | } | ||
442 | |||
443 | withsetx() | ||
444 | { | ||
445 | printf "\n\n\n+ %s\n\n" "$*" | ||
446 | "$@" | indent | ||
447 | } | ||
448 | |||
449 | delegate_command=/usr/lib/cryptonomic/cryptonomic-$1 | ||
450 | |||
451 | if [ $# = 0 ] | ||
452 | then | ||
453 | hostname=$(cryptonomic hostname) | ||
454 | uid=$(id -un)@${hostname} | ||
455 | 2>/dev/null withsetx ssh-keyscan -t "${HostKeyAlgorithm}" "$hostname" | ||
456 | 2>/dev/null withsetx dig +nocmd -taaaa "$hostname" +noall +answer | ||
457 | 2>/dev/null withsetx dig +nocmd "$hostname" +noall +answer | ||
458 | 2>&1 withsetx gpg --locate-keys "$uid" | ||
459 | 2>/dev/null withsetx delv @1.1.1.1 -t sshfp "$hostname" | ||
460 | export hostname HostKeyAlgorithm | ||
461 | 2>&1 withsetx sh -c 'ssh -v -i /dev/null -o BatchMode=yes -o HostKeyAlgorithms=${HostKeyAlgorithm} -o VerifyHostKeyDNS=yes -o GlobalKnownHostsFile=/dev/null -o UserKnownHostsFile=/dev/null "$hostname" -- true 2>&1 | egrep "DNS|Server host key|match:|Connecting to|Connection|Authenticating to"' | ||
462 | |||
463 | elif [ "$1" = hostname ] | ||
464 | then | ||
465 | get_domain "${KEY_FILE}".pub || exit | ||
466 | printf '%s\n' "$domain" | ||
467 | |||
468 | elif [ "$1" = dyndns ] | ||
469 | then | ||
470 | shift | ||
471 | # further command line options are ssh options | ||
472 | if [ -z "$(cryptonomic ipv4)" ] | ||
473 | then set -- "$@" -6 | ||
474 | fi | ||
475 | set -- ssh -i "$KEY_FILE" dyndns@"$DOMAIN" "$@" -- "$(hostname)" | ||
476 | |||
477 | if [ -r "$KEY_FILE" ] | ||
478 | then | ||
479 | "$@" | ||
480 | elif sudo -n true 2>/dev/null || groups | grep '\bsudo\b' | ||
481 | then | ||
482 | sudo -- "$@" | ||
483 | else | ||
484 | su -c "$(bash -c 'printf "%q " "$@"' bash "$@")" | ||
485 | fi | ||
486 | elif [ -x "$delegate_command" ] | ||
487 | then | ||
488 | shift | ||
489 | exec "$delegate_command" "$@" | ||
490 | |||
491 | else | ||
492 | echo "Usage: $0 [dyndns|hostname|gpg]" >&2 | ||
493 | exit 1 | ||
494 | fi | ||
495 | EOF | ||
496 | control_file_file /usr/bin/selfpublish.sh 755 < "$SELF_EXECUTABLE" | ||
497 | echo | ||
498 | } | ||
499 | |||
500 | in_temp_dir() | ||
501 | { | ||
502 | ( | ||
503 | __tempdir=$(mktemp -d) || return | ||
504 | __r=1 | ||
505 | trap 'rm -rf "$__tempdir"; (exit $__r)' EXIT | ||
506 | cd "$__tempdir" && "$@" | ||
507 | __r=$? | ||
508 | ) | ||
509 | } | ||
510 | |||
511 | _control_file_unchanged() | ||
512 | { | ||
513 | [ "$(dpkg-query -f '${Version}' -W ${SELF_PACKAGE})" = "${SELF_VERSION}" ] || return | ||
514 | |||
515 | dependencies > want-dependencies | ||
516 | dpkg-query -f '${Depends}\n' -W "${SELF_PACKAGE}" | sed 's/, */\n/g' | sort -u > have-dependencies | ||
517 | diff -q want-dependencies have-dependencies | ||
518 | } | ||
519 | |||
520 | control_file_unchanged() | ||
521 | { | ||
522 | in_temp_dir _control_file_unchanged "$@" | ||
523 | } | ||
524 | |||
525 | equivocate() | ||
526 | { | ||
527 | if dpkg-query -s "${SELF_PACKAGE}" | grep -q '^Status: install ok installed' 2>/dev/null | ||
528 | then | ||
529 | if control_file_unchanged && control_file | control_file_file_check | ||
530 | then | ||
531 | if ! force | ||
532 | then return | ||
533 | fi | ||
534 | fi | ||
535 | fi | ||
536 | which equivs-build >/dev/null 2>&1 || apt_install equivs | ||
537 | ( | ||
538 | destdir=$(mktemp -d) | ||
539 | cd "$destdir" | ||
540 | control_file > ./control | ||
541 | equivs-build ./control >&2 | ||
542 | DEB=${SELF_PACKAGE}_${SELF_VERSION}_all.deb | ||
543 | |||
544 | if ! [ "$NO_APT" ] | ||
545 | then | ||
546 | as_root dpkg -r "${SELF_PACKAGE}" | ||
547 | dpkg_install "$DEB" | ||
548 | as_root install -m0644 "$DEB" "$DEBDEST" | ||
549 | fi | ||
550 | ) || exit | ||
551 | } | ||
552 | |||
553 | ssh_keytag_to_path_fragment() | ||
554 | { | ||
555 | case "$1" in | ||
556 | ssh-dss) echo dsa ;; | ||
557 | ecdsa-sha2-nistp256) echo ecdsa ;; | ||
558 | ssh-rsa|ssh-ed25519) echo ${1#ssh-} ;; | ||
559 | *) return 1 ;; | ||
560 | esac | ||
561 | } | ||
562 | |||
563 | path_fragment_to_ssh_keytag() | ||
564 | { | ||
565 | case "$1" in | ||
566 | ssh-dss|ecdsa-sha2-nistp256|ssh-rsa|ssh-ed25519) echo $1;; | ||
567 | dss|rsa|ed25519) echo ssh-$1 ;; | ||
568 | dsa) echo ssh-dss ;; | ||
569 | ecdsa) echo ecdsa-sha2-nistp256 ;; | ||
570 | *) return 1 ;; | ||
571 | esac | ||
572 | } | ||
573 | |||
574 | get_dyndns_domain() | ||
575 | { | ||
576 | fragment=$(ssh_keytag_to_path_fragment "$1") || return | ||
577 | |||
578 | host_keyfile=/etc/ssh/ssh_host_${fragment}_key | ||
579 | user_keyfile=$HOME/.ssh/id_${fragment} | ||
580 | |||
581 | set -- -q dyndns@"$DYNDNSHOST" "$HOSTNAME" | ||
582 | |||
583 | if [ -r "$host_keyfile" ] | ||
584 | then | ||
585 | set -- ssh -i "$host_keyfile" "$@" | ||
586 | |||
587 | elif in_group sudo | ||
588 | then | ||
589 | set -- sudo ssh -i "$host_keyfile" "$@" | ||
590 | |||
591 | elif [ -r "$user_keyfile" ] | ||
592 | then | ||
593 | set -- ssh -i "$user_keyfile" "$@" | ||
594 | |||
595 | else | ||
596 | set -- ssh "$@" | ||
597 | fi | ||
598 | |||
599 | "$@" | ||
600 | } | ||
601 | |||
602 | enable_apache_modules() | ||
603 | { | ||
604 | local restart= | ||
605 | for MODULE in $APACHE_MODULES | ||
606 | do | ||
607 | if ! [ -e /etc/apache2/mods-enabled/$MODULE.load ] && | ||
608 | ! [ "$MODULE" = cgi -a -e /etc/apache2/mods-enabled/cgid.load ] | ||
609 | then | ||
610 | a2enmod $MODULE >/dev/null | ||
611 | restart=y | ||
612 | fi | ||
613 | done | ||
614 | if [ "$restart" ] | ||
615 | then | ||
616 | as_root systemctl restart apache2 | ||
617 | fi | ||
618 | } | ||
619 | |||
620 | site_conf_template() | ||
621 | { | ||
622 | cat <<END | ||
623 | <MDomain $DOMAIN> | ||
624 | MDContactEmail webmaster@$DOMAIN | ||
625 | MDCertificateAgreement accepted | ||
626 | MDRequireHttps temporary | ||
627 | MDMember $HOSTNAME.$DOMAIN | ||
628 | </MDomain> | ||
629 | <VirtualHost *:80> | ||
630 | ServerName $DOMAIN | ||
631 | ServerAlias $DOMAIN. | ||
632 | Redirect / https://$DOMAIN | ||
633 | </VirtualHost> | ||
634 | <VirtualHost *:80> | ||
635 | ServerAlias $HOSTNAME.$DOMAIN | ||
636 | ServerAlias $HOSTNAME.$DOMAIN. | ||
637 | Redirect / https://$HOSTNAME.$DOMAIN | ||
638 | </VirtualHost> | ||
639 | |||
640 | <VirtualHost *:443> | ||
641 | ServerName $HOSTNAME.$DOMAIN | ||
642 | ServerAlias $DOMAIN | ||
643 | |||
644 | ServerAdmin webmaster@$HOSTNAME.$DOMAIN | ||
645 | SSLEngine on | ||
646 | |||
647 | ErrorLog /srv/$DOMAIN/logs/error.log | ||
648 | CustomLog /srv/$DOMAIN/logs/access.log combined | ||
649 | |||
650 | <Location "/server-status"> | ||
651 | SetHandler server-status | ||
652 | </Location> | ||
653 | |||
654 | DocumentRoot /srv/$DOMAIN/public_html/ | ||
655 | <Directory /srv/$DOMAIN/public_html> | ||
656 | Options Indexes FollowSymLinks MultiViews Includes | ||
657 | |||
658 | IndexOrderDefault Descending Date | ||
659 | IndexOptions +IgnoreCase FancyIndexing | ||
660 | IndexOptions +HTMLTable SuppressDescription SuppressHTMLPreamble | ||
661 | IndexStyleSheet /css/autoindex.css | ||
662 | IndexIgnore /unindexed /HEADER.html /css /images | ||
663 | |||
664 | # Using an absolute url for header | ||
665 | HeaderName /HEADER.html | ||
666 | |||
667 | XBitHack on | ||
668 | AllowOverride None | ||
669 | Require all granted | ||
670 | </Directory> | ||
671 | |||
672 | ScriptAlias /git "/usr/lib/cgit/cgit.cgi/" | ||
673 | Alias /cgit-css "/usr/share/cgit/" | ||
674 | |||
675 | </VirtualHost> | ||
676 | END | ||
677 | } | ||
678 | |||
679 | wait_for_certificate_issuance() | ||
680 | { | ||
681 | local f state reloaded_apache= | ||
682 | f=/etc/apache2/md/domains/"$1"/md.json | ||
683 | local set=$- | ||
684 | set +x | ||
685 | echo -n Waiting for certificate... >&2 | ||
686 | while true | ||
687 | do | ||
688 | if [ -e "$f" ] && state=$(sed -ne 's/^ *"state": *\([0-9]\+\),/\1/p' "$f") | ||
689 | then | ||
690 | case "$state" in | ||
691 | 1) | ||
692 | if ! [ "$reloaded_apache" ] | ||
693 | then | ||
694 | as_root systemctl reload apache2 | ||
695 | reloaded_apache=y | ||
696 | fi | ||
697 | ;; | ||
698 | 2) | ||
699 | set -$set | ||
700 | echo ' Done waiting for certificate.' >&2 | ||
701 | return | ||
702 | ;; | ||
703 | esac | ||
704 | fi | ||
705 | |||
706 | sleep 1 | ||
707 | echo -n . >&2 | ||
708 | done | ||
709 | } | ||
710 | |||
711 | install_apache_vhost() | ||
712 | { | ||
713 | if [ -e "$SITE_CONF" ] && ! force | ||
714 | then | ||
715 | return | ||
716 | fi | ||
717 | |||
718 | for DIR in $APACHE_SITE_DIRS | ||
719 | do | ||
720 | mkdir -p "$SITE_DIR"/"$DIR" | ||
721 | done | ||
722 | |||
723 | local tmp | ||
724 | tmp=$(mktemp "$SITE_CONF".XXXXXX) | ||
725 | |||
726 | site_conf_template > "$tmp" | ||
727 | mv -T "$tmp" "$SITE_CONF" || { rm -f "$tmp"; false; } | ||
728 | a2ensite "$DOMAIN" >/dev/null | ||
729 | } | ||
730 | |||
731 | install_self_to_site() | ||
732 | { | ||
733 | SOURCE_BASENAME=${0##*/} | ||
734 | [ -d "$SITE_DIR"/public_html ] || return | ||
735 | dst=$SITE_DIR/public_html/$SOURCE_BASENAME | ||
736 | src=$0 | ||
737 | [ -e "$src" ] || return 0 | ||
738 | if [ ! "$src" -ef "$dst" ] | ||
739 | then | ||
740 | cp -Tuv "$src" "$dst" >&2 | ||
741 | cp -Tuv "$src" "$dst".txt >&2 | ||
742 | fi | ||
743 | DEB="${SELF_PACKAGE}_${SELF_VERSION}_all.deb" | ||
744 | if [ -e "$DEB" ] | ||
745 | then | ||
746 | cp -Tuv "$DEB" "$SITE_DIR/public_html/$DEB" | ||
747 | fi | ||
748 | } | ||
749 | |||
750 | get_upstream() | ||
751 | { | ||
752 | if [ -e selfpublish.sh -a -e .git ] | ||
753 | then | ||
754 | UPSTREAM=$(git config --get branch.master.remote 2>/dev/null) | ||
755 | case "$UPSTREAM" in | ||
756 | *:*) ;; | ||
757 | *) UPSTREAM=$(git remote get-url "$UPSTREAM") ;; | ||
758 | esac | ||
759 | fi | ||
760 | UPSTREAM=${UPSTREAM:-$DEFAULT_UPSTREAM} | ||
761 | [ "$UPSTREAM" ] | ||
762 | } | ||
763 | |||
764 | write_cgit_config() | ||
765 | { | ||
766 | get_upstream | ||
767 | cgit_scan_dir=/srv/public_git | ||
768 | if ! [ -e "$cgit_scan_dir"/selfpublish.sh ] | ||
769 | then | ||
770 | mkdir -p "$cgit_scan_dir" | ||
771 | (cd "$cgit_scan_dir" | ||
772 | GIT_SSH_COMMAND='ssh -i /etc/ssh/ssh_host_ed25519_key' \ | ||
773 | git clone --bare "$UPSTREAM" selfpublish.sh) | ||
774 | else | ||
775 | git push --all "$cgit_scan_dir"/selfpublish.sh | ||
776 | fi | ||
777 | line="scan-path=$cgit_scan_dir" | ||
778 | grep -qxF "$line" /etc/cgitrc || printf '%s\n' "$line" >> /etc/cgitrc | ||
779 | } | ||
780 | |||
781 | install_tls_public_certificate() | ||
782 | { | ||
783 | local src=/etc/apache2/md/domains/"$DOMAIN"/pubcert.pem dst="$SITE_DIR"/public_html/pubcert.pem | ||
784 | if [ "$src" -nt "$dst" ] | ||
785 | then | ||
786 | cp -Tuv /etc/apache2/md/domains/"$DOMAIN"/pubcert.pem "$SITE_DIR"/public_html/pubcert.pem | ||
787 | openssl x509 -in "$dst" -noout -text > "$dst".txt | ||
788 | fi | ||
789 | } | ||
790 | |||
791 | install_gpg_rings() | ||
792 | { | ||
793 | cryptonomic gpg | ||
794 | } | ||
795 | |||
796 | configure_apache_vhost() | ||
797 | { | ||
798 | enable_apache_modules | ||
799 | install_self_to_site | ||
800 | install_header_to_site | ||
801 | install_tls_public_certificate | ||
802 | install_gpg_rings | ||
803 | write_cgit_config | ||
804 | } | ||
805 | |||
806 | install_header_to_site() | ||
807 | { | ||
808 | cat > "$SITE_DIR"/public_html/HEADER.html <<'EOF' | ||
809 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> | ||
810 | <html> | ||
811 | <head> | ||
812 | <title> | ||
813 | selfpublish.sh at <!--#echo var="SERVER_NAME" --> | ||
814 | </title> | ||
815 | <link rel="stylesheet" href="/css/autoindex.css" type="text/css"> | ||
816 | |||
817 | <style type=text/css> | ||
818 | td { padding: 10px; } | ||
819 | td.cmds {border: 1px solid;} | ||
820 | |||
821 | #toggle_expand[type="checkbox"]:checked ~ * .expand {display: "";} | ||
822 | #toggle_expand[type="checkbox"]:not(:checked) ~ * .expand {display: none;} | ||
823 | |||
824 | #main { | ||
825 | text-align:left; | ||
826 | margin-top: 10%; | ||
827 | margin-bottom: 10%; | ||
828 | font-size: 15px; | ||
829 | line-height: 2em; | ||
830 | border-spacing: 10px; | ||
831 | } | ||
832 | </style> | ||
833 | </head> | ||
834 | <body> | ||
835 | |||
836 | <input type=checkbox id=toggle_expand name=toggle_expand style='visibility: hidden' /> | ||
837 | <table id=main> | ||
838 | <tr> | ||
839 | <td colspan=2 style='border: 2px solid; padding: 18px;'> | ||
840 | <strong style='font-size: 20px'>selfpublish.sh</strong> | ||
841 | <tr> | ||
842 | <td> | ||
843 | <td style='border: 1px solid; padding: 12px; text-align: center;'> | ||
844 | <label for=toggle_expand> | ||
845 | <strong>Install</strong> | ||
846 | </label> | ||
847 | <tr> | ||
848 | <td> | ||
849 | <td> | ||
850 | <td> | ||
851 | <td class=cmds> curl -OR https://<!--#echo var="SERVER_NAME" -->/selfpublish.sh && sudo bash selfpublish.sh | ||
852 | |||
853 | <tr> | ||
854 | <td> | ||
855 | <td style='border: 1px solid; padding: 12px; text-align: center;'> | ||
856 | <label for=toggle_expand> | ||
857 | <strong>Requirements</strong> | ||
858 | </label> | ||
859 | <tr class=expand> | ||
860 | <td> | ||
861 | <td> | ||
862 | <td>Distribution | ||
863 | <td style='border: 1px solid; padding: 12px; text-align: center;'> <a href="https://debian.org">Debian</a> GNU/Linux | ||
864 | <tr class=expand> | ||
865 | <td> | ||
866 | <td> | ||
867 | <td>Version | ||
868 | <td style='border: 1px solid; padding: 12px; text-align: center;'> <code>buster</code> or later | ||
869 | |||
870 | <tr> | ||
871 | <td> | ||
872 | <td style='border: 1px solid; padding: 12px; text-align: center;'> | ||
873 | <label for=toggle_expand> | ||
874 | <strong>Browse source</strong> | ||
875 | </label> | ||
876 | <tr class=expand> | ||
877 | <td> | ||
878 | <td> | ||
879 | <td style='text-align: right;'>Local repository: | ||
880 | <td class=cmds> | ||
881 | <a href="git/selfpublish.sh"> | ||
882 | https://<!--#echo var="SERVER_NAME" -->/git/selfpublish.sh | ||
883 | </a> | ||
884 | <tr class=expand> | ||
885 | <td> | ||
886 | <td> | ||
887 | <td style='text-align: right;'>Upstream repository: | ||
888 | <td class=cmds> | ||
889 | <a href="https://git.cryptonomic.net/selfpublish.sh"> | ||
890 | https://git.cryptonomic.net/selfpublish.sh/ | ||
891 | </a> | ||
892 | <tr class=expand> | ||
893 | <td> | ||
894 | <td> | ||
895 | <td style='text-align: right;'>Download single file: | ||
896 | <td class=cmds> | ||
897 | <a href="selfpublish.sh"> https://<!--#echo var="SERVER_NAME" -->/selfpublish.sh</a> | ||
898 | |||
899 | <tr> | ||
900 | <td> | ||
901 | <td style='border: 1px solid; padding: 12px; text-align: center;'> | ||
902 | <label for=toggle_expand><strong>Develop (<a href="https://git-scm.com/">git</a>)</strong></label> | ||
903 | <tr class=expand> | ||
904 | <td> | ||
905 | <td> | ||
906 | <td style='text-align: right'>Clone local repository: | ||
907 | <td class=cmds> | ||
908 | git clone /srv/<!--#echo var="SERVER_NAME"-->/public_git/selfpublish.sh/ && cd selfpublish.sh && make | ||
909 | <tr class=expand> | ||
910 | <td> | ||
911 | <td> | ||
912 | <td style='text-align: right'>Share local repository (read-only): | ||
913 | <td class=cmds> | ||
914 | git clone https://<!--#echo var="SERVER_NAME"-->/git/selfpublish.sh && cd selfpublish.sh && make | ||
915 | <tr class=expand> | ||
916 | <td> | ||
917 | <td> | ||
918 | <td style='text-align: right;'>To push changes upstream: | ||
919 | <td class=cmds> | ||
920 | git clone d@cryptonomic.net:public_git/selfpublish.sh<br> | ||
921 | </tr> | ||
922 | <tr class=expand> | ||
923 | <td> | ||
924 | <td> | ||
925 | <td style='text-align: right;'>Local git repository: | ||
926 | <td class=cmds> | ||
927 | git clone https://<!--#echo var="SERVER_NAME"-->/git/selfpublish.sh<br> | ||
928 | </tr> | ||
929 | |||
930 | </table> | ||
931 | |||
932 | <pre style=font-size:1.3em> | ||
933 | Fortunately, | ||
934 | |||
935 | <!--#exec cmd="/usr/games/fortune |sed 's/^/ /'" --> | ||
936 | </pre> | ||
937 | EOF | ||
938 | chmod +x "$SITE_DIR"/public_html/HEADER.html | ||
939 | } | ||
940 | |||
941 | check_tls() | ||
942 | { | ||
943 | local flags=$- | ||
944 | set +e | ||
945 | curl -s -S -I https://"$1" >/dev/null | ||
946 | check_tls_result=$? | ||
947 | set $flags | ||
948 | } | ||
949 | |||
950 | DEBDEST=$(realpath .) | ||
951 | [ "$NO_EQUIVS" ] || equivocate >&2 | ||
952 | |||
953 | APACHE_MODULES='status md rewrite ssl include cgi' | ||
954 | APACHE_SITE_DIRS='logs public_html' | ||
955 | |||
956 | HOSTNAME=$(hostname --short) | ||
957 | DOMAIN=$(cryptonomic hostname) | ||
958 | cryptonomic dyndns | ||
959 | |||
960 | SITE_DIR=/srv/${DOMAIN#$HOSTNAME.} | ||
961 | SITE_CONF=/etc/apache2/sites-available/${DOMAIN#$HOSTNAME.}.conf | ||
962 | |||
963 | check_tls "$DOMAIN" | ||
964 | tls_result=$? | ||
965 | |||
966 | install_apache_vhost | ||
967 | configure_apache_vhost | ||
968 | |||
969 | if [ $tls_result != 0 ] || force | ||
970 | then | ||
971 | as_root systemctl restart apache2 | ||
972 | wait_for_certificate_issuance "$DOMAIN" | ||
973 | as_root systemctl reload apache2 || as_root systemctl restart apache2 | ||
974 | else | ||
975 | as_root systemctl reload apache2 || as_root systemctl restart apache2 | ||
976 | fi | ||
977 | |||
978 | check_tls "$DOMAIN" | ||
979 | printf '%s\n' "https://$DOMAIN/selfpublish.sh" | ||
980 | |||