1. The problem I’m having:
Here’s my scenario. I have docker running frankenphp and mysql as two containers on a docker compose network on Mac OS X Sequoia. That runs an API. I have an Xcode iPhone app which I’m deploying to a local device via USB. I’m running dnsmasq
on the MacBook Pro in order to allow the iPhone to connect to the FQDN which Caddy is serving. This works but safari complains about the certificate and so does the iPhone app. I installed the Caddy root.crt to the iPhone and it’s listed under Certificate Trust Settings (see attached image)
However, the URL is still not trusted.
2. Error messages and/or full log output:
There’s nothing in the logs because the iPhone app can’t connect. But what Xcode logs provide are
nw_application_id_create_self NECP_CLIENT_ACTION_GET_SIGNED_CLIENT_ID [80: Authentication error]
Failed to resolve host network app I’d
Task <8F1D2FB2-2CE6-4CC6-B47A-1D52D4AD0F1F>.<1> finished with error [-1009] Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo={_kCFStreamErrorCodeKey=50, NSUnderlyingError=0x301498ea0 {Error Domain=kCFErrorDomainCFNetwork Code=-1009 "(null)" UserInfo={_NSURLErrorNWPathKey=satisfied (Path is satisfied), interface: en0[802.11], ipv4, ipv6, dns, uses wifi, _kCFStreamErrorCodeKey=50, _kCFStreamErrorDomainKey=1}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <8F1D2FB2-2CE6-4CC6-B47A-1D52D4AD0F1F>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <8F1D2FB2-2CE6-4CC6-B47A-1D52D4AD0F1F>.<1>"
), NSLocalizedDescription=The Internet connection appears to be offline., NSErrorFailingURLStringKey=https://app.sportch.localhost/api/v1/getInformationImportant, NSErrorFailingURLKey=https://app.sportch.localhost/api/v1/getInformationImportant, _kCFStreamErrorDomainKey=1}
Connection 2: default TLS Trust evaluation failed(-9807)
Connection 2: TLS Trust encountered error 3:-9807
Connection 2: encountered error(3:-9807)
Task <7A92A31F-E39A-46F9-A06D-6B5A7752777C>.<2> HTTP load failed, 0/0 bytes (error code: -1202 [3:-9807])
Task <7A92A31F-E39A-46F9-A06D-6B5A7752777C>.<2> finished with error [-1202] Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “app.sportch.localhost” which could put your confidential information at risk." UserInfo={NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, NSErrorPeerCertificateChainKey=(
"<cert(0x103868400) s: app.sportch.localhost i: Caddy Local Authority - ECC Intermediate>",
"<cert(0x103868a00) s: Caddy Local Authority - ECC Intermediate i: Caddy Local Authority - 2024 ECC Root>"
), NSErrorClientCertificateStateKey=0, NSErrorFailingURLKey=https://app.sportch.localhost/api/v1/login, NSErrorFailingURLStringKey=https://app.sportch.localhost/api/v1/login, NSUnderlyingError=0x301496e50 {Error Domain=kCFErrorDomainCFNetwork Code=-1202 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x302b01860>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9807, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9807, kCFStreamPropertySSLPeerCertificates=(
"<cert(0x103868400) s: app.sportch.localhost i: Caddy Local Authority - ECC Intermediate>",
"<cert(0x103868a00) s: Caddy Local Authority - ECC Intermediate i: Caddy Local Authority - 2024 ECC Root>"
)}}, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDataTask <7A92A31F-E39A-46F9-A06D-6B5A7752777C>.<2>"
), _kCFStreamErrorCodeKey=-9807, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <7A92A31F-E39A-46F9-A06D-6B5A7752777C>.<2>, NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x302b01860>, NSLocalizedDescription=The certificate for this server is invalid. You might be connecting to a server that is pretending to be “app.sportch.localhost” which could put your confidential information at risk.}
3. Caddy version:
I’m sorry but I don’t know how to do this on frankenphp
Is it this?
# /usr/local/bin/frankenphp -v
v2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=
4. How I installed and ran Caddy:
I ran docker-compose up
.
a. System environment:
Docker
b. Command:
I have this in my Dockerfile
# Configure supervisord to run both frankenphp and Postfix
RUN mkdir -p /etc/supervisor/conf.d && \
echo "[supervisord]" > /etc/supervisord.conf && \
echo "nodaemon=true" >> /etc/supervisord.conf && \
echo "[program:postfix]" >> /etc/supervisord.conf && \
echo "command=/usr/sbin/postfix start-fg" >> /etc/supervisord.conf && \
echo "[program:frankenphp]" >> /etc/supervisord.conf && \
echo "command=/usr/local/bin/frankenphp run --config /etc/frankenphp/Caddyfile" >> /etc/supervisord.conf
c. Service/unit/compose file:
networks:
web-network:
services:
mysql:
image: mysql/mysql-server:latest-aarch64
restart: on-failure
ports:
- "23306:3306"
environment:
MYSQL_ROOT_HOST: "%"
MYSQL_ROOT_USER: root
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: aasportch10700
MYSQL_USER: my_user
MYSQL_PASSWORD: my_password
MYSQL_SQL_MODE: "NO_ENGINE_SUBSTITUTION"
volumes:
- $PWD/db/data:/var/lib/mysql
- $PWD/db/config:/etc/mysql/conf.d
networks:
- web-network
extra_hosts:
host.docker.internal: host-gateway
frankenphp:
build:
context: .
dockerfile: php/caddy/frankenphp/Dockerfile
ports:
- "80:80"
- "443:443"
- "2019:2019"
volumes:
- ./caddy/frankenphp/data:/data
- ./caddy/frankenphp/config:/config
- ./caddy/frankenphp/php.ini:/etc/php.d/dev.ini
- ./caddy/frankenphp/Caddyfile:/etc/frankenphp/Caddyfile
- ./caddy/frankenphp/logs:/var/logs/caddy
- ./caddy/certificates:/root/.local/share/caddy/certificates
- ./caddy/pki:/root/.local/share/caddy/pki
- ./app:/var/www/html
networks:
- web-network
extra_hosts:
host.docker.internal: host-gateway
d. My complete Caddy config:
{
# Enable FrankenPHP
frankenphp
# Logging
log {
output stderr
level DEBUG
}
debug
}
app.sportch.localhost {
# Enable compression (optional)
encode zstd br gzip
root * /var/www/html/SportchLegacyApp/
# Security: Hide specific paths
@hide path /.env *.sql *.template /gtscripts/* *.txt *.md /random /support vapi/ var/ /support /system.* /lib
respond @hide 404
# Execute PHP files
php_server
# Rewrite subfolder requests for screenscrapers
rewrite /ladder/* /ladder/index.php
rewrite /manager/* /manager/index.php
# Serve media files
handle /media/* {
root * /var/www/html
file_server
}
}
5. Links to relevant resources:
Could it be related to this?
I notice that the certificate issued is valid for 10 years, which is probably too long for Apple to trust it if that article is correct.
I tried adding lifetime
setting:
app.sportch.localhost {
tls internal {
lifetime 69120000
}
But that caused frankenphp to fail with errors (whatever I put inside those curly braces causes errors):
frankenphp-1 | 2024-11-12 21:05:01,168 INFO spawned: 'frankenphp' with pid 7
frankenphp-1 | 2024-11-12 21:05:01,170 INFO spawned: 'postfix' with pid 8
frankenphp-1 | 2024-11-12 21:05:01,223 WARN exited: frankenphp (exit status 1; not expected)
frankenphp-1 | 2024-11-12 21:05:02,340 INFO spawned: 'frankenphp' with pid 93
frankenphp-1 | 2024-11-12 21:05:02,340 INFO success: postfix entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
frankenphp-1 | 2024-11-12 21:05:02,389 WARN exited: frankenphp (exit status 1; not expected)
frankenphp-1 | 2024-11-12 21:05:04,401 INFO spawned: 'frankenphp' with pid 107
frankenphp-1 | 2024-11-12 21:05:04,454 WARN exited: frankenphp (exit status 1; not expected)
frankenphp-1 | 2024-11-12 21:05:07,466 INFO spawned: 'frankenphp' with pid 121
frankenphp-1 | 2024-11-12 21:05:07,516 WARN exited: frankenphp (exit status 1; not expected)
frankenphp-1 | 2024-11-12 21:05:08,518 INFO gave up: frankenphp entered FATAL state, too many start retries too quickly
According to ChatGPT, caddy is not serving the root cert? Here’s some more logs:
% openssl s_client -connect app.sportch.localhost:443 -showcerts
Connecting to 192.168.68.102
CONNECTED(00000005)
depth=1 CN=Caddy Local Authority - ECC Intermediate
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0
verify return:1
---
Certificate chain
0 s:
i:CN=Caddy Local Authority - ECC Intermediate
a:PKEY: id-ecPublicKey, 256 (bit); sigalg: ecdsa-with-SHA256
v:NotBefore: Nov 12 18:07:41 2024 GMT; NotAfter: Nov 13 06:07:41 2024 GMT
-----BEGIN CERTIFICATE-----
REDACTED
-----END CERTIFICATE-----
1 s:CN=Caddy Local Authority - ECC Intermediate
i:CN=Caddy Local Authority - 2024 ECC Root
a:PKEY: id-ecPublicKey, 256 (bit); sigalg: ecdsa-with-SHA256
v:NotBefore: Nov 12 18:07:40 2024 GMT; NotAfter: Nov 19 18:07:40 2024 GMT
-----BEGIN CERTIFICATE-----
REDACTED
-----END CERTIFICATE-----
---
Server certificate
subject=
issuer=CN=Caddy Local Authority - ECC Intermediate
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: ECDSA
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 1281 bytes and written 396 bytes
Verification error: unable to get local issuer certificate
---
New, TLSv1.3, Cipher is TLS_AES_128_GCM_SHA256
Protocol: TLSv1.3
Server public key is 256 bit
This TLS version forbids renegotiation.
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 20 (unable to get local issuer certificate)
---
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
Protocol : TLSv1.3
Cipher : TLS_AES_128_GCM_SHA256
Session-ID: 50E3F14965E235B2CC1050BC2E5B084C7F23AF2FE8783148685F010A8D6A72C6
Session-ID-ctx:
Resumption PSK: 3D1A483D9AF7D820AE2B20DC2EFA9E7216933903ADB83A740DFDC2150BB7FEB0
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket lifetime hint: 604800 (seconds)
TLS session ticket:
0000 - 6f 97 a5 b0 42 fa c6 a0-f0 e6 f1 e7 fa 1b 73 b0 o...B.........s.
0010 - 07 5c e5 41 29 7d 17 1b-5e 32 2f fa c8 0d b2 22 .\.A)}..^2/...."
0020 - 8f f9 40 c3 22 01 2c ec-2d 2c df 70 97 eb 71 ff ..@.".,.-,.p..q.
0030 - 25 b8 85 fc f6 22 b7 ad-07 b5 92 59 62 19 09 0b %....".....Yb...
0040 - 2b 91 fd d9 01 46 11 5b-12 a5 e2 cc 4c 93 68 f1 +....F.[....L.h.
0050 - da af 55 a8 e1 92 cb 15-8c 35 33 7a 34 31 2a e4 ..U......53z41*.
0060 - 31 bd 81 7d 31 4b 61 e5-41 1..}1Ka.A
Start Time: 1731447816
Timeout : 7200 (sec)
Verify return code: 20 (unable to get local issuer certificate)
Extended master secret: no
Max Early Data: 0
---
read R BLOCK