@stacksjs/buddy 0.70.285 → 0.70.286

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -147,26 +147,65 @@ fi
147
147
  # list already inside it and skips names that have no certificate file of
148
148
  # their own. Adding a name takes an acme:issue for the union of the names
149
149
  # already on the certificate plus this one.
150
+ #
151
+ # --cert-name pins where that union lands. Without it tlsx names the output
152
+ # after the FIRST --domains entry, and $CURRENT is read back from the
153
+ # certificate, where openssl prints the SANs sorted. So the union was written
154
+ # to whichever hostname sorted first (autodiscover.chrisbreuer.me, on the box
155
+ # this was found on) while the mail server went on reading the untouched
156
+ # mail.stacksjs.com.crt. The issuance succeeded, the deploy reported success,
157
+ # and the certificate was correct - just not in the file anyone was serving.
158
+ #
159
+ # Nothing is appended to the scheduled renewal script, deliberately. The
160
+ # renewal renews mail.stacksjs.com.crt using the SAN list inside that file, so
161
+ # a name added here is renewed by construction. The previous code tried to
162
+ # splice the hostname into that script's --domains list with
163
+ # sed -i "s|acme:renew -d "|..."
164
+ # whose quotes close before the | in the JS template literal, leaving sed the
165
+ # unterminated expression 's|acme:renew -d ' and a shell pipe. That aborted the
166
+ # whole mail step. Adding names there was never necessary and, until tlsx
167
+ # renewed in place, was actively harmful: each extra name made the renewal open
168
+ # another certificate file, and two files sharing a CN meant one overwrote the
169
+ # other.
150
170
  CERTFILE=/etc/bun-gateway/certs/mail.stacksjs.com.crt
151
- RENEW=/opt/mail/renew-mail-cert.sh
152
171
  if [ -n "$DOMAIN" ] && [ -f "$CERTFILE" ] && [ -d /opt/tlsx ]; then
153
172
  MAILHOSTNAME="mail.$DOMAIN"
173
+ CERTNAME=$(basename "$CERTFILE" .crt)
174
+ CERTKEY=/etc/bun-gateway/certs/mail.stacksjs.com.key
154
175
  CURRENT=$(openssl x509 -in "$CERTFILE" -noout -text 2>/dev/null | grep -A1 'Subject Alternative Name' | tail -1 | tr -d ' ' | sed 's/DNS://g')
155
176
  case ",$CURRENT," in
156
177
  *",$MAILHOSTNAME,"*) : ;;
157
178
  *)
158
179
  ALL="$CURRENT,$MAILHOSTNAME"
159
- if (cd /opt/tlsx && /usr/local/bin/bun run packages/tlsx/bin/cli.ts acme:issue -d "$ALL" --method http-01 --webroot /var/www/acme-challenge --dir /etc/bun-gateway/certs --prod) >/tmp/.mailtenant-cert 2>&1; then
160
- install -m 644 /etc/bun-gateway/certs/mail.stacksjs.com.crt /etc/letsencrypt/live/mail.stacksjs.com/fullchain.pem
161
- install -m 640 -g mail-server /etc/bun-gateway/certs/mail.stacksjs.com.key /etc/letsencrypt/live/mail.stacksjs.com/privkey.pem
162
- systemctl restart mail || true
163
- # Keep the scheduled renewal issuing the same set, or the name drops
164
- # off the certificate at the next renewal.
165
- [ -f "$RENEW" ] && ! grep -q "$MAILHOSTNAME" "$RENEW" && sed -i "s|acme:renew -d "|acme:renew -d "$MAILHOSTNAME,|" "$RENEW" 2>/dev/null
166
- echo "CERTHOST:$MAILHOSTNAME"
180
+ # acme:issue overwrites the certificate in place, so keep a copy: this is
181
+ # a certificate other tenants are served from, and a partial result must
182
+ # not be what they get.
183
+ SAFE=$(mktemp -d)
184
+ cp -a "$CERTFILE" "$SAFE/cert" 2>/dev/null || true
185
+ cp -a "$CERTKEY" "$SAFE/key" 2>/dev/null || true
186
+ if (cd /opt/tlsx && /usr/local/bin/bun run packages/tlsx/bin/cli.ts acme:issue -d "$ALL" --cert-name "$CERTNAME" --method http-01 --webroot /var/www/acme-challenge --dir /etc/bun-gateway/certs --prod) >/tmp/.mailtenant-cert 2>&1; then
187
+ # Verify rather than assume. The old code reported CERTHOST on a zero
188
+ # exit alone, which is how an issuance that wrote somewhere else was
189
+ # reported as "added to the mail certificate" for weeks.
190
+ UPDATED=$(openssl x509 -in "$CERTFILE" -noout -text 2>/dev/null | grep -A1 'Subject Alternative Name' | tail -1 | tr -d ' ' | sed 's/DNS://g')
191
+ MISSING=
192
+ for n in $(echo "$ALL" | tr ',' ' '); do
193
+ case ",$UPDATED," in *",$n,"*) : ;; *) MISSING="$MISSING $n" ;; esac
194
+ done
195
+ if [ -n "$MISSING" ]; then
196
+ [ -f "$SAFE/cert" ] && cp -a "$SAFE/cert" "$CERTFILE"
197
+ [ -f "$SAFE/key" ] && cp -a "$SAFE/key" "$CERTKEY"
198
+ echo "CERTFAIL:issued certificate does not carry$MISSING - previous certificate kept"
199
+ else
200
+ install -m 644 "$CERTFILE" /etc/letsencrypt/live/mail.stacksjs.com/fullchain.pem
201
+ install -m 640 -g mail-server "$CERTKEY" /etc/letsencrypt/live/mail.stacksjs.com/privkey.pem
202
+ systemctl restart mail || true
203
+ echo "CERTHOST:$MAILHOSTNAME"
204
+ fi
167
205
  else
168
206
  echo "CERTFAIL:$(tail -c 200 /tmp/.mailtenant-cert | tr '\\n' ' ')"
169
207
  fi
208
+ rm -rf "$SAFE"
170
209
  rm -f /tmp/.mailtenant-cert
171
210
  ;;
172
211
  esac
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/buddy",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.70.285",
5
+ "version": "0.70.286",
6
6
  "description": "Meet Buddy. The Stacks runtime.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -95,53 +95,53 @@
95
95
  "prepublishOnly": "bun run build"
96
96
  },
97
97
  "dependencies": {
98
- "@stacksjs/actions": "^0.70.285",
99
- "@stacksjs/ai": "^0.70.285",
100
- "@stacksjs/alias": "^0.70.285",
101
- "@stacksjs/arrays": "^0.70.285",
102
- "@stacksjs/auth": "^0.70.285",
103
- "@stacksjs/build": "^0.70.285",
104
- "@stacksjs/cache": "^0.70.285",
105
- "@stacksjs/cli": "^0.70.285",
98
+ "@stacksjs/actions": "^0.70.286",
99
+ "@stacksjs/ai": "^0.70.286",
100
+ "@stacksjs/alias": "^0.70.286",
101
+ "@stacksjs/arrays": "^0.70.286",
102
+ "@stacksjs/auth": "^0.70.286",
103
+ "@stacksjs/build": "^0.70.286",
104
+ "@stacksjs/cache": "^0.70.286",
105
+ "@stacksjs/cli": "^0.70.286",
106
106
  "@stacksjs/clapp": "^0.2.12",
107
- "@stacksjs/cloud": "^0.70.285",
108
- "@stacksjs/collections": "^0.70.285",
109
- "@stacksjs/config": "^0.70.285",
110
- "@stacksjs/database": "^0.70.285",
111
- "@stacksjs/desktop-build": "^0.70.285",
112
- "@stacksjs/dns": "^0.70.285",
113
- "@stacksjs/email": "^0.70.285",
114
- "@stacksjs/enums": "^0.70.285",
115
- "@stacksjs/error-handling": "^0.70.285",
116
- "@stacksjs/events": "^0.70.285",
117
- "@stacksjs/git": "^0.70.285",
107
+ "@stacksjs/cloud": "^0.70.286",
108
+ "@stacksjs/collections": "^0.70.286",
109
+ "@stacksjs/config": "^0.70.286",
110
+ "@stacksjs/database": "^0.70.286",
111
+ "@stacksjs/desktop-build": "^0.70.286",
112
+ "@stacksjs/dns": "^0.70.286",
113
+ "@stacksjs/email": "^0.70.286",
114
+ "@stacksjs/enums": "^0.70.286",
115
+ "@stacksjs/error-handling": "^0.70.286",
116
+ "@stacksjs/events": "^0.70.286",
117
+ "@stacksjs/git": "^0.70.286",
118
118
  "@stacksjs/gitit": "^0.2.5",
119
- "@stacksjs/health": "^0.70.285",
119
+ "@stacksjs/health": "^0.70.286",
120
120
  "@stacksjs/dnsx": "^0.2.3",
121
121
  "@stacksjs/httx": "^0.1.10",
122
- "@stacksjs/image": "^0.70.285",
123
- "@stacksjs/lint": "^0.70.285",
124
- "@stacksjs/logging": "^0.70.285",
125
- "@stacksjs/notifications": "^0.70.285",
126
- "@stacksjs/objects": "^0.70.285",
127
- "@stacksjs/orm": "^0.70.285",
128
- "@stacksjs/path": "^0.70.285",
129
- "@stacksjs/skills": "^0.70.285",
130
- "@stacksjs/payments": "^0.70.285",
131
- "@stacksjs/realtime": "^0.70.285",
132
- "@stacksjs/router": "^0.70.285",
122
+ "@stacksjs/image": "^0.70.286",
123
+ "@stacksjs/lint": "^0.70.286",
124
+ "@stacksjs/logging": "^0.70.286",
125
+ "@stacksjs/notifications": "^0.70.286",
126
+ "@stacksjs/objects": "^0.70.286",
127
+ "@stacksjs/orm": "^0.70.286",
128
+ "@stacksjs/path": "^0.70.286",
129
+ "@stacksjs/skills": "^0.70.286",
130
+ "@stacksjs/payments": "^0.70.286",
131
+ "@stacksjs/realtime": "^0.70.286",
132
+ "@stacksjs/router": "^0.70.286",
133
133
  "@stacksjs/rpx": "^0.11.42",
134
- "@stacksjs/search-engine": "^0.70.285",
135
- "@stacksjs/security": "^0.70.285",
136
- "@stacksjs/server": "^0.70.285",
137
- "@stacksjs/storage": "^0.70.285",
138
- "@stacksjs/strings": "^0.70.285",
139
- "@stacksjs/testing": "^0.70.285",
140
- "@stacksjs/tunnel": "^0.70.285",
141
- "@stacksjs/types": "^0.70.285",
142
- "@stacksjs/ui": "^0.70.285",
143
- "@stacksjs/utils": "^0.70.285",
144
- "@stacksjs/validation": "^0.70.285",
134
+ "@stacksjs/search-engine": "^0.70.286",
135
+ "@stacksjs/security": "^0.70.286",
136
+ "@stacksjs/server": "^0.70.286",
137
+ "@stacksjs/storage": "^0.70.286",
138
+ "@stacksjs/strings": "^0.70.286",
139
+ "@stacksjs/testing": "^0.70.286",
140
+ "@stacksjs/tunnel": "^0.70.286",
141
+ "@stacksjs/types": "^0.70.286",
142
+ "@stacksjs/ui": "^0.70.286",
143
+ "@stacksjs/utils": "^0.70.286",
144
+ "@stacksjs/validation": "^0.70.286",
145
145
  "@stacksjs/ts-cloud": "^0.7.103",
146
146
  "ajv": "^8.20.0",
147
147
  "ajv-formats": "^3.0.1",