diff options
author | Andrew Cady <d@jerkface.net> | 2020-09-04 16:21:58 -0400 |
---|---|---|
committer | Andrew Cady <d@jerkface.net> | 2020-09-04 16:21:58 -0400 |
commit | 03be69d4985731e10c2c168a8f50431eff18a0f7 (patch) | |
tree | 71db926c8c2f72f75547a6db7c48a625a74e68a8 |
initial commit
-rw-r--r-- | selfpublish.sh | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/selfpublish.sh b/selfpublish.sh new file mode 100644 index 0000000..8a17c67 --- /dev/null +++ b/selfpublish.sh | |||
@@ -0,0 +1,267 @@ | |||
1 | #!/bin/sh | ||
2 | set -e | ||
3 | |||
4 | DEFAULT_AUTH_TYPE=ed25519 | ||
5 | |||
6 | force() | ||
7 | { | ||
8 | [ "$FORCE" ] | ||
9 | } | ||
10 | |||
11 | in_group() | ||
12 | { | ||
13 | local g | ||
14 | for g in $(groups) | ||
15 | do | ||
16 | [ "$g" = "$1" ] && return | ||
17 | done | ||
18 | false | ||
19 | } | ||
20 | |||
21 | as_root() | ||
22 | { | ||
23 | if [ "$(id -u)" = 0 ] | ||
24 | then | ||
25 | "$@" | ||
26 | elif in_group sudo | ||
27 | then | ||
28 | sudo "$@" | ||
29 | else | ||
30 | su -c "$*" | ||
31 | fi | ||
32 | } | ||
33 | |||
34 | apt_install() | ||
35 | { | ||
36 | as_root apt-get install "$@" | ||
37 | } | ||
38 | |||
39 | dpkg_install() | ||
40 | { | ||
41 | as_root dpkg -i "$@" | ||
42 | } | ||
43 | |||
44 | control_file() | ||
45 | { | ||
46 | cat <<EOF | ||
47 | Package: selfpublish-dot-sh-deps | ||
48 | Depends: apache2 (>= 2.4.46), libssl1.1 (>= 1.1.1d) | ||
49 | Description: selfpublish.sh dependency package | ||
50 | This package depends on the dependencies of the | ||
51 | selfpublish.sh script, and is installed by that | ||
52 | script to self-satisfy those dependencies. | ||
53 | |||
54 | EOF | ||
55 | } | ||
56 | |||
57 | equivocate() | ||
58 | { | ||
59 | dpkg-query -s selfpublish-dot-sh-deps >/dev/null 2>&1 && return || true | ||
60 | which equivs-build >/dev/null 2>&1 || apt_install equivs | ||
61 | ( | ||
62 | destdir=$(mktemp -d) | ||
63 | cd "$destdir" | ||
64 | control_file > ./control | ||
65 | equivs-build ./control >/dev/null 2>&1 | ||
66 | dpkg_install selfpublish-dot-sh-deps_1.0_all.deb | ||
67 | ) | ||
68 | } | ||
69 | |||
70 | ssh_keytag_to_path_fragment() | ||
71 | { | ||
72 | case "$1" in | ||
73 | ssh-dss) echo dsa ;; | ||
74 | ecdsa-sha2-nistp256) echo ecdsa ;; | ||
75 | ssh-rsa|ssh-ed25519) echo ${1#ssh-} ;; | ||
76 | *) return 1 ;; | ||
77 | esac | ||
78 | } | ||
79 | |||
80 | path_fragment_to_ssh_keytag() | ||
81 | { | ||
82 | case "$1" in | ||
83 | ssh-dss|ecdsa-sha2-nistp256|ssh-rsa|ssh-ed25519) echo $1;; | ||
84 | dss|rsa|ed25519) echo ssh-$1 ;; | ||
85 | dsa) echo ssh-dss ;; | ||
86 | ecdsa) echo ecdsa-sha2-nistp256 ;; | ||
87 | *) return 1 ;; | ||
88 | esac | ||
89 | } | ||
90 | |||
91 | get_dyndns_domain() | ||
92 | { | ||
93 | fragment=$(ssh_keytag_to_path_fragment "$1") || return | ||
94 | |||
95 | host_keyfile=/etc/ssh/ssh_host_${fragment}_key | ||
96 | user_keyfile=$HOME/.ssh/id_${fragment} | ||
97 | |||
98 | set -- -q dyndns@cryptonomic.net | ||
99 | |||
100 | if [ -r "$host_keyfile" ] | ||
101 | then | ||
102 | set -- ssh -i "$host_keyfile" "$@" | ||
103 | |||
104 | elif in_group sudo | ||
105 | then | ||
106 | set -- sudo ssh -i "$host_keyfile" "$@" | ||
107 | |||
108 | elif [ -r "$user_keyfile" ] | ||
109 | then | ||
110 | set -- ssh -i "$user_keyfile" "$@" | ||
111 | |||
112 | else | ||
113 | set -- ssh "$@" | ||
114 | fi | ||
115 | |||
116 | "$@" | ||
117 | } | ||
118 | |||
119 | enable_apache_modules() | ||
120 | { | ||
121 | for MODULE in $APACHE_MODULES | ||
122 | do | ||
123 | a2enmod $MODULE >/dev/null | ||
124 | done | ||
125 | } | ||
126 | |||
127 | site_conf_template() | ||
128 | { | ||
129 | cat <<END | ||
130 | <MDomain $DOMAIN> | ||
131 | MDContactEmail webmaster@$DOMAIN | ||
132 | MDCertificateAgreement accepted | ||
133 | MDRequireHttps temporary | ||
134 | </MDomain> | ||
135 | <VirtualHost *:80> | ||
136 | ServerName $DOMAIN | ||
137 | ServerAlias ${DOMAIN}. | ||
138 | Redirect / https://$DOMAIN | ||
139 | </VirtualHost> | ||
140 | |||
141 | <VirtualHost *:443> | ||
142 | ServerName $DOMAIN | ||
143 | |||
144 | ServerAdmin webmaster@$DOMAIN | ||
145 | SSLEngine on | ||
146 | |||
147 | ErrorLog /srv/$DOMAIN/logs/error.log | ||
148 | CustomLog /srv/$DOMAIN/logs/access.log combined | ||
149 | |||
150 | <Location "/server-status"> | ||
151 | SetHandler server-status | ||
152 | </Location> | ||
153 | |||
154 | DocumentRoot /srv/$DOMAIN/public_html/ | ||
155 | <Directory /srv/$DOMAIN/public_html> | ||
156 | Options Indexes FollowSymLinks MultiViews Includes | ||
157 | |||
158 | IndexOrderDefault Descending Date | ||
159 | IndexOptions +IgnoreCase FancyIndexing | ||
160 | IndexOptions +HTMLTable SuppressDescription | ||
161 | IndexStyleSheet /css/autoindex.css | ||
162 | IndexIgnore /unindexed /HEADER.html /css /images | ||
163 | |||
164 | # Using an absolute url for header | ||
165 | HeaderName /HEADER.html | ||
166 | |||
167 | XBitHack on | ||
168 | AllowOverride None | ||
169 | Require all granted | ||
170 | </Directory> | ||
171 | |||
172 | </VirtualHost> | ||
173 | END | ||
174 | } | ||
175 | |||
176 | wait_for_certificate_issuance() | ||
177 | { | ||
178 | local f state | ||
179 | f=/etc/apache2/md/domains/"$1"/md.json | ||
180 | while true | ||
181 | do | ||
182 | if [ -e "$f" ] | ||
183 | then | ||
184 | state=$(sed -ne 's/^ *"state": *\([0-9]\+\),/\1/p' "$f") | ||
185 | |||
186 | if [ "$state" = 2 ] | ||
187 | then | ||
188 | return | ||
189 | fi | ||
190 | fi | ||
191 | sleep 1 | ||
192 | done | ||
193 | } | ||
194 | |||
195 | install_apache_site() | ||
196 | { | ||
197 | if [ -e "$SITE_CONF" ] && ! force | ||
198 | then | ||
199 | return | ||
200 | fi | ||
201 | |||
202 | for DIR in $APACHE_SITE_DIRS | ||
203 | do | ||
204 | mkdir -p "$SITE_DIR"/"$DIR" | ||
205 | done | ||
206 | |||
207 | local tmp | ||
208 | tmp=$(mktemp "$SITE_CONF".XXXXXX) | ||
209 | |||
210 | site_conf_template > "$tmp" | ||
211 | mv -T "$tmp" "$SITE_CONF" || { rm -f "$tmp"; false; } | ||
212 | a2ensite "$DOMAIN" >/dev/null | ||
213 | } | ||
214 | |||
215 | install_self_to_site() | ||
216 | { | ||
217 | SOURCE_BASENAME=${0##*/} | ||
218 | [ -d "$SITE_DIR"/public_html ] || return | ||
219 | dst=$SITE_DIR/public_html/$SOURCE_BASENAME | ||
220 | src=$0 | ||
221 | [ -e "$src" ] || return 0 | ||
222 | if [ ! "$src" -ef "$dst" ] | ||
223 | then | ||
224 | cp -Tuv "$src" "$dst" >&2 | ||
225 | cp -Tuv "$src" "$dst".txt >&2 | ||
226 | fi | ||
227 | } | ||
228 | |||
229 | check_tls() | ||
230 | { | ||
231 | curl -s -S -I https://"$1" >/dev/null | ||
232 | } | ||
233 | |||
234 | equivocate | ||
235 | |||
236 | APACHE_MODULES='status md rewrite ssl include' | ||
237 | APACHE_SITE_DIRS='logs public_html' | ||
238 | |||
239 | AUTH_TYPE=$(path_fragment_to_ssh_keytag "${1:-$DEFAULT_AUTH_TYPE}") | ||
240 | DYNDNS=$(get_dyndns_domain "$AUTH_TYPE") | ||
241 | DOMAIN=${DYNDNS%% *} | ||
242 | SITE_DIR=/srv/$DOMAIN | ||
243 | SITE_CONF=/etc/apache2/sites-available/$DOMAIN.conf | ||
244 | |||
245 | case "$DOMAIN" in | ||
246 | *."$AUTH_TYPE".cryptonomic.net) ;; | ||
247 | *) | ||
248 | printf 'Error: %s\n' "Unexpected domain returned by server: $DOMAIN" | ||
249 | exit 1 ;; | ||
250 | esac | ||
251 | |||
252 | if ! check_tls "$DOMAIN" || force | ||
253 | then | ||
254 | equivocate | ||
255 | enable_apache_modules | ||
256 | install_apache_site | ||
257 | install_self_to_site | ||
258 | systemctl restart apache2 | ||
259 | wait_for_certificate_issuance "$DOMAIN" | ||
260 | systemctl reload apache2 | ||
261 | else | ||
262 | install_self_to_site | ||
263 | fi | ||
264 | |||
265 | check_tls "$DOMAIN" | ||
266 | printf '%s\n' "https://$DOMAIN/selfpublish.sh" | ||
267 | |||