<feed xmlns='http://www.w3.org/2005/Atom'>
<title>hetzner-infra/vpn, branch master</title>
<subtitle>Unnamed repository; edit this file 'description' to name the repository.
</subtitle>
<link rel='alternate' type='text/html' href='http://git.yyamashita.com/hetzner-infra/'/>
<entry>
<title>Re-enable IKE fragmentation (remove fragmentation=no)</title>
<updated>2026-07-02T12:48:03+00:00</updated>
<author>
<name>yyamashita</name>
<email>yyamashita@hetzner.yyamashita.com</email>
</author>
<published>2026-07-02T12:48:03+00:00</published>
<link rel='alternate' type='text/html' href='http://git.yyamashita.com/hetzner-infra/commit/?id=2854b063b4cea071be2e90eb059fef095c26d9a1'/>
<id>2854b063b4cea071be2e90eb059fef095c26d9a1</id>
<content type='text'>
Android 12 IKEv2 may handle IKE-level fragments better than
IP-level fragmentation. iOS was failing with IKE fragmentation
previously (ECDSA cert era) — test if RSA cert + IKE fragmentation
now works for both platforms.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Android 12 IKEv2 may handle IKE-level fragments better than
IP-level fragmentation. iOS was failing with IKE fragmentation
previously (ECDSA cert era) — test if RSA cert + IKE fragmentation
now works for both platforms.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>Force send ISRG Root X1 via local.cacerts for Android compat</title>
<updated>2026-07-02T12:43:55+00:00</updated>
<author>
<name>yyamashita</name>
<email>yyamashita@hetzner.yyamashita.com</email>
</author>
<published>2026-07-02T12:43:55+00:00</published>
<link rel='alternate' type='text/html' href='http://git.yyamashita.com/hetzner-infra/commit/?id=71e307963829e7a4a7f7277829a2a60791747cb2'/>
<id>71e307963829e7a4a7f7277829a2a60791747cb2</id>
<content type='text'>
strongSwan skips self-signed root CAs from CERT payloads by default.
Setting cacerts = chain-3.crt in the local section forces it to include
ISRG Root X1 explicitly so Android 12 can complete trust chain validation.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
strongSwan skips self-signed root CAs from CERT payloads by default.
Setting cacerts = chain-3.crt in the local section forces it to include
ISRG Root X1 explicitly so Android 12 can complete trust chain validation.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>Append issuer root CA to cert chain for Android IKEv2 compat</title>
<updated>2026-07-02T12:35:37+00:00</updated>
<author>
<name>yyamashita</name>
<email>yyamashita@hetzner.yyamashita.com</email>
</author>
<published>2026-07-02T12:35:37+00:00</published>
<link rel='alternate' type='text/html' href='http://git.yyamashita.com/hetzner-infra/commit/?id=d99eef8641fe819cb0cc90e7eea0514b769e6389'/>
<id>d99eef8641fe819cb0cc90e7eea0514b769e6389</id>
<content type='text'>
Android 12 native IKEv2 requires the presented cert chain to include
a cert that is directly in the device trust store. Fetch the issuer
root CA (ISRG Root X1) via AIA from the last intermediate cert and
append it to server-chain.crt so strongSwan sends 4 CERT payloads:
Leaf + YR1 + Root YR + ISRG Root X1 (trusted on all Android versions).

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Android 12 native IKEv2 requires the presented cert chain to include
a cert that is directly in the device trust store. Fetch the issuer
root CA (ISRG Root X1) via AIA from the last intermediate cert and
append it to server-chain.crt so strongSwan sends 4 CERT payloads:
Leaf + YR1 + Root YR + ISRG Root X1 (trusted on all Android versions).

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>Remove cert logic duplication from vpn/install.sh</title>
<updated>2026-07-02T04:17:18+00:00</updated>
<author>
<name>yyamashita</name>
<email>yyamashita@hetzner.yyamashita.com</email>
</author>
<published>2026-07-02T04:17:18+00:00</published>
<link rel='alternate' type='text/html' href='http://git.yyamashita.com/hetzner-infra/commit/?id=2a1b87ebcb167cededb22782241900a82d0e8d4d'/>
<id>2a1b87ebcb167cededb22782241900a82d0e8d4d</id>
<content type='text'>
install.sh and renew-cert.sh had identical cert fetch/split code.
install.sh now delegates to renew-cert.sh, which in turn calls deploy.sh.
Flow: install.sh → renew-cert.sh → deploy.sh

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
install.sh and renew-cert.sh had identical cert fetch/split code.
install.sh now delegates to renew-cert.sh, which in turn calls deploy.sh.
Flow: install.sh → renew-cert.sh → deploy.sh

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>Unify install.sh pattern: always delegate to deploy.sh</title>
<updated>2026-07-02T04:14:11+00:00</updated>
<author>
<name>yyamashita</name>
<email>yyamashita@hetzner.yyamashita.com</email>
</author>
<published>2026-07-02T04:14:11+00:00</published>
<link rel='alternate' type='text/html' href='http://git.yyamashita.com/hetzner-infra/commit/?id=7be614fa7aa9672e0ebe0424be2e3dae88c75c83'/>
<id>7be614fa7aa9672e0ebe0424be2e3dae88c75c83</id>
<content type='text'>
Both hetzner-admin and vpn install.sh were calling docker compose
directly instead of going through their own deploy.sh, unlike mail
which already followed the correct pattern.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Both hetzner-admin and vpn install.sh were calling docker compose
directly instead of going through their own deploy.sh, unlike mail
which already followed the correct pattern.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>Send full cert chain to fix iOS IKEv2 validation</title>
<updated>2026-07-02T01:15:30+00:00</updated>
<author>
<name>yyamashita</name>
<email>yyamashita@hetzner.yyamashita.com</email>
</author>
<published>2026-07-02T01:15:30+00:00</published>
<link rel='alternate' type='text/html' href='http://git.yyamashita.com/hetzner-infra/commit/?id=7d8d2951bdc1af183d2b362d649e370d107c87fc'/>
<id>7d8d2951bdc1af183d2b362d649e370d107c87fc</id>
<content type='text'>
iOS IKEv2 does not look up intermediates from the system trust store
(unlike TLS/HTTPS). Send all 3 certs: YE1 + Root YE + cross-signed
ISRG Root X2, so iOS can build the full chain through to ISRG Root X1.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
iOS IKEv2 does not look up intermediates from the system trust store
(unlike TLS/HTTPS). Send all 3 certs: YE1 + Root YE + cross-signed
ISRG Root X2, so iOS can build the full chain through to ISRG Root X1.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>Disable IKE fragmentation, use IP-level fragmentation for iOS</title>
<updated>2026-07-01T15:52:49+00:00</updated>
<author>
<name>yyamashita</name>
<email>yyamashita@hetzner.yyamashita.com</email>
</author>
<published>2026-07-01T15:52:41+00:00</published>
<link rel='alternate' type='text/html' href='http://git.yyamashita.com/hetzner-infra/commit/?id=8f0380a933f420320716de61a5838400117dbb8a'/>
<id>8f0380a933f420320716de61a5838400117dbb8a</id>
<content type='text'>
iOS may have a bug where it advertises N(FRAG_SUP) but fails to reassemble
IKE fragments in IKE_AUTH responses. Switching to fragmentation=no forces
the large IKE_AUTH response to be sent as a single UDP packet with IP-level
fragmentation, which iOS's standard IP stack handles correctly.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
iOS may have a bug where it advertises N(FRAG_SUP) but fails to reassemble
IKE fragments in IKE_AUTH responses. Switching to fragmentation=no forces
the large IKE_AUTH response to be sent as a single UDP packet with IP-level
fragmentation, which iOS's standard IP stack handles correctly.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>Send only YE1+RootYE intermediates, skip cross-signed ISRG Root X2</title>
<updated>2026-07-01T15:41:24+00:00</updated>
<author>
<name>yyamashita</name>
<email>yyamashita@hetzner.yyamashita.com</email>
</author>
<published>2026-07-01T15:41:24+00:00</published>
<link rel='alternate' type='text/html' href='http://git.yyamashita.com/hetzner-infra/commit/?id=f6f06e6aa292110bf774fa1ff4591c288b275305'/>
<id>f6f06e6aa292110bf774fa1ff4591c288b275305</id>
<content type='text'>
iOS 14+ and Android 11+ have ISRG Root X2 in their trust store.
Sending the cross-signed ISRG Root X2 alongside the trusted
self-signed version can confuse the cert path-building algorithm.

Chain: leaf -&gt; YE1 -&gt; Root YE -&gt; [ISRG Root X2 from trust store]

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
iOS 14+ and Android 11+ have ISRG Root X2 in their trust store.
Sending the cross-signed ISRG Root X2 alongside the trusted
self-signed version can confuse the cert path-building algorithm.

Chain: leaf -&gt; YE1 -&gt; Root YE -&gt; [ISRG Root X2 from trust store]

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>Fix VPN cert chain: split multi-cert PEM for strongSwan</title>
<updated>2026-07-01T15:28:31+00:00</updated>
<author>
<name>yyamashita</name>
<email>yyamashita@hetzner.yyamashita.com</email>
</author>
<published>2026-07-01T15:28:31+00:00</published>
<link rel='alternate' type='text/html' href='http://git.yyamashita.com/hetzner-infra/commit/?id=c463eddaab816b6ffe032f37f4228e4b22a1425d'/>
<id>c463eddaab816b6ffe032f37f4228e4b22a1425d</id>
<content type='text'>
Caddy's cert chain contains 4 certs:
  1. leaf (signed by YE1)
  2. YE1 (signed by Root YE)
  3. Root YE (cross-signed by ISRG Root X2)
  4. ISRG Root X2 (cross-signed by ISRG Root X1)

iOS/Android trust ISRG Root X1 but not Root YE directly.
The full cross-signed chain must be sent in IKE_AUTH.

strongSwan only reads the first cert from a multi-cert PEM file,
so entrypoint.sh now splits server-chain.crt into individual files
in x509ca/ before starting charon.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Caddy's cert chain contains 4 certs:
  1. leaf (signed by YE1)
  2. YE1 (signed by Root YE)
  3. Root YE (cross-signed by ISRG Root X2)
  4. ISRG Root X2 (cross-signed by ISRG Root X1)

iOS/Android trust ISRG Root X1 but not Root YE directly.
The full cross-signed chain must be sent in IKE_AUTH.

strongSwan only reads the first cert from a multi-cert PEM file,
so entrypoint.sh now splits server-chain.crt into individual files
in x509ca/ before starting charon.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>Restore intermediate cert + reduce IKE fragment size to 512 bytes</title>
<updated>2026-07-01T15:21:40+00:00</updated>
<author>
<name>yyamashita</name>
<email>yyamashita@hetzner.yyamashita.com</email>
</author>
<published>2026-07-01T15:21:40+00:00</published>
<link rel='alternate' type='text/html' href='http://git.yyamashita.com/hetzner-infra/commit/?id=86bc044356ae37aa24ff352858d71641c00740fc'/>
<id>86bc044356ae37aa24ff352858d71641c00740fc</id>
<content type='text'>
iOS/Android VPN stacks don't follow AIA to download intermediate certs,
so YE1 must be sent by the server for cert chain validation.

The original 1236-byte IKE fragment was being dropped mid-path.
Setting fragment_size=512 splits the 1776-byte IKE_AUTH into ~4
fragments of ≤560 bytes each, which should traverse any NAT/router.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
iOS/Android VPN stacks don't follow AIA to download intermediate certs,
so YE1 must be sent by the server for cert chain validation.

The original 1236-byte IKE fragment was being dropped mid-path.
Setting fragment_size=512 splits the 1776-byte IKE_AUTH into ~4
fragments of ≤560 bytes each, which should traverse any NAT/router.

Co-Authored-By: Claude Sonnet 4.6 &lt;noreply@anthropic.com&gt;
</pre>
</div>
</content>
</entry>
</feed>
