Changes in / [96a99bf:77019fa] in Klonkt
- File:
-
- 1 edited
-
scripts/klonkt-add-instance.sh (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
scripts/klonkt-add-instance.sh
r96a99bf r77019fa 38 38 [[ "$SLUG" =~ ^[a-z0-9][a-z0-9._-]*$ ]] || die "slug must be lowercase letters, digits, dot, dash or underscore." 39 39 40 # An internationalised domain is written into the Caddy block, the filename and41 # .env as ASCII. Not because Caddy needs it — it copes — but because an emoji42 # name can carry a zero-width joiner and variation selectors, and those are43 # INVISIBLE. An editor, a paste or a well-meant tidy-up drops one and the vhost44 # stops matching with nothing on screen to explain why.45 #46 # Per-label punycode, and deliberately not an IDNA library. Python's built-in47 # `idna` codec STRIPS the joiner and yields a different name (one that has no48 # certificate); the strict IDNA2008 tools reject emoji outright. Encoding what49 # the operator actually typed is the only thing that matches the DNS record50 # they actually made.51 to_ascii() {52 if LC_ALL=C printf '%s' "$1" | grep -q '[^ -~]'; then53 python3 -c 'import codecs,sys54 print(".".join(l if l.isascii() else "xn--" + codecs.encode(l, "punycode").decode()55 for l in sys.argv[1].split(".")))' "$1"56 else57 printf '%s\n' "$1"58 fi59 }60 61 # The same name with joiner and variation selectors removed: what a client that62 # normalises them away will ask for instead. Empty when the name has none.63 stripped_ascii() {64 python3 -c 'import codecs,sys65 d = sys.argv[1]66 s = d.replace("", "").replace("️", "")67 if s == d: raise SystemExit(0)68 print(".".join(l if l.isascii() else "xn--" + codecs.encode(l, "punycode").decode()69 for l in s.split(".")))' "$1"70 }71 72 HOST_ASCII="$(to_ascii "$DOMAIN")"73 74 40 DATA_DIR="$DATA_ROOT/$SLUG" 75 41 ENV_FILE="$DATA_DIR/.env" … … 82 48 id -u "$KLONKT_USER" >/dev/null 2>&1 || die "user $KLONKT_USER does not exist" 83 49 [ -e "$DATA_DIR" ] && die "$DATA_DIR already exists. Pick another slug." 84 85 if [ "$HOST_ASCII" != "$DOMAIN" ]; then86 say "IDN: ${DOMAIN} -> ${HOST_ASCII}"87 ALIAS_ASCII="$(stripped_ascii "$DOMAIN" || true)"88 if [ -n "$ALIAS_ASCII" ]; then89 die "refusing ${DOMAIN}90 91 This name contains a zero-width joiner or a variation selector, which makes92 it invalid under IDNA2008. Browsers reject it — two were tested — so nobody93 could reach the site by the name you just typed. Worse, clients that quietly94 strip those characters ask for a DIFFERENT name than the one you registered:95 96 you typed : ${HOST_ASCII}97 they ask : ${ALIAS_ASCII}98 99 Nothing has been created. Use an emoji that is a single codepoint, or an100 ordinary name.101 102 If you want this anyway, pass the punycode form as the domain, and give the103 stripped name its own DNS record and a redirect block so one identity keeps104 one canonical address:105 106 $0 $SLUG ${HOST_ASCII}"107 fi108 fi109 50 [ -f /etc/systemd/system/klonkt@.service ] || { 110 51 [ -f "$KLONKT_DIR/deploy/klonkt@.service" ] || die "missing $KLONKT_DIR/deploy/klonkt@.service" … … 136 77 echo "HOST=127.0.0.1" 137 78 echo "SESSION_SECRET=${SECRET}" 138 echo "PUBLIC_BASE_URL=https://${ HOST_ASCII}"79 echo "PUBLIC_BASE_URL=https://${DOMAIN}" 139 80 echo "DATABASE_PATH=${DATA_DIR}/database.sqlite" 140 81 echo "MEDIA_PATH=${DATA_DIR}/media" … … 164 105 step "Caddy" 165 106 CADDY=/etc/caddy/Caddyfile 166 CONFD=/etc/caddy/conf.d 167 # One file per site. Older machines keep every block in the single Caddyfile, 168 # so add the import if it is missing: this then works on both without moving 169 # anything that is already there. 170 mkdir -p "$CONFD" 171 grep -q '^[[:space:]]*import[[:space:]]\+conf\.d/' "$CADDY" 2>/dev/null \ 172 || printf '\nimport conf.d/*.caddyfile\n' >> "$CADDY" 173 BLOCK="$CONFD/${HOST_ASCII}.caddyfile" 174 if [ -e "$BLOCK" ]; then 175 say "a block for ${HOST_ASCII} already exists, left untouched" 107 if grep -q "^${DOMAIN} {" "$CADDY" 2>/dev/null; then 108 say "a block for ${DOMAIN} already exists, left untouched" 176 109 else 177 printf '%s {\n reverse_proxy 127.0.0.1:%s\n encode gzip zstd\n}\n' "$HOST_ASCII" "$PORT" > "$BLOCK" 178 # Take the block away again rather than leave a config that will not load. 179 # The running Caddy is unaffected until someone reloads, and reloading is 180 # precisely what the next person to touch this machine will do. 110 cp "$CADDY" "${CADDY}.bak.$(date +%s)" 2>/dev/null || true 111 printf '\n%s {\n reverse_proxy 127.0.0.1:%s\n encode gzip zstd\n}\n' "$DOMAIN" "$PORT" >> "$CADDY" 181 112 caddy validate --config "$CADDY" --adapter caddyfile >/dev/null 2>&1 \ 182 || { rm -f "$BLOCK"; die "Caddy config invalid for ${HOST_ASCII} — the block was removed again, nothing changed"; } 183 systemctl reload caddy \ 184 || die "caddy reload failed. NOT restarting: that would drop every site on this machine. See: journalctl -u caddy -n 30" 185 say "serving ${HOST_ASCII}" 113 || die "Caddy config invalid after adding ${DOMAIN} — check $CADDY (a .bak was made)" 114 systemctl reload caddy 2>/dev/null || systemctl restart caddy 115 say "serving ${DOMAIN}" 186 116 fi 187 117 fi
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)