Changes in / [96a99bf:77019fa] in Klonkt


Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • scripts/klonkt-add-instance.sh

    r96a99bf r77019fa  
    3838[[ "$SLUG" =~ ^[a-z0-9][a-z0-9._-]*$ ]] || die "slug must be lowercase letters, digits, dot, dash or underscore."
    3939
    40 # An internationalised domain is written into the Caddy block, the filename and
    41 # .env as ASCII. Not because Caddy needs it — it copes — but because an emoji
    42 # name can carry a zero-width joiner and variation selectors, and those are
    43 # INVISIBLE. An editor, a paste or a well-meant tidy-up drops one and the vhost
    44 # stops matching with nothing on screen to explain why.
    45 #
    46 # Per-label punycode, and deliberately not an IDNA library. Python's built-in
    47 # `idna` codec STRIPS the joiner and yields a different name (one that has no
    48 # certificate); the strict IDNA2008 tools reject emoji outright. Encoding what
    49 # the operator actually typed is the only thing that matches the DNS record
    50 # they actually made.
    51 to_ascii() {
    52   if LC_ALL=C printf '%s' "$1" | grep -q '[^ -~]'; then
    53     python3 -c 'import codecs,sys
    54 print(".".join(l if l.isascii() else "xn--" + codecs.encode(l, "punycode").decode()
    55                for l in sys.argv[1].split(".")))' "$1"
    56   else
    57     printf '%s\n' "$1"
    58   fi
    59 }
    60 
    61 # The same name with joiner and variation selectors removed: what a client that
    62 # normalises them away will ask for instead. Empty when the name has none.
    63 stripped_ascii() {
    64   python3 -c 'import codecs,sys
    65 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 
    7440DATA_DIR="$DATA_ROOT/$SLUG"
    7541ENV_FILE="$DATA_DIR/.env"
     
    8248id -u "$KLONKT_USER" >/dev/null 2>&1 || die "user $KLONKT_USER does not exist"
    8349[ -e "$DATA_DIR" ] && die "$DATA_DIR already exists. Pick another slug."
    84 
    85 if [ "$HOST_ASCII" != "$DOMAIN" ]; then
    86   say "IDN: ${DOMAIN} -> ${HOST_ASCII}"
    87   ALIAS_ASCII="$(stripped_ascii "$DOMAIN" || true)"
    88   if [ -n "$ALIAS_ASCII" ]; then
    89     die "refusing ${DOMAIN}
    90 
    91   This name contains a zero-width joiner or a variation selector, which makes
    92   it invalid under IDNA2008. Browsers reject it — two were tested — so nobody
    93   could reach the site by the name you just typed. Worse, clients that quietly
    94   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 an
    100   ordinary name.
    101 
    102   If you want this anyway, pass the punycode form as the domain, and give the
    103   stripped name its own DNS record and a redirect block so one identity keeps
    104   one canonical address:
    105 
    106       $0 $SLUG ${HOST_ASCII}"
    107   fi
    108 fi
    10950[ -f /etc/systemd/system/klonkt@.service ] || {
    11051  [ -f "$KLONKT_DIR/deploy/klonkt@.service" ] || die "missing $KLONKT_DIR/deploy/klonkt@.service"
     
    13677  echo "HOST=127.0.0.1"
    13778  echo "SESSION_SECRET=${SECRET}"
    138   echo "PUBLIC_BASE_URL=https://${HOST_ASCII}"
     79  echo "PUBLIC_BASE_URL=https://${DOMAIN}"
    13980  echo "DATABASE_PATH=${DATA_DIR}/database.sqlite"
    14081  echo "MEDIA_PATH=${DATA_DIR}/media"
     
    164105  step "Caddy"
    165106  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"
    176109  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"
    181112    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}"
    186116  fi
    187117fi
Note: See TracChangeset for help on using the changeset viewer.