Would you happen to have an example for Haproxy on how to do this? Looks like SNI and mTLS is rather exclusive. One is an HTTP feature the other is part of the TLS handshake.
I wouldn’t want users with arbitrary client certs to go down the mTLS road when they actually want to connect to any of the websites I run on the machine…
I do not have a HAProxy configuration but porting the nginx configuration to HAProxy should be straightforward.
The following nginx configuration uses mTLS w/ a mapping to allow clients based on the fingerprint of their certificate.
In this example teddycloud runs on port 443 and nginx on 8443. In my setup both services are Containerized running in one pod and I ingress traffic on the host ip from port 443 to the nginx container on port 8443.
This allows specified toniebox clients to connect to teddycloud through nginx.
For access via web browsers you can expose the same teddycloud instance based on the SNI hostname using a separate nginx configuration file for the corresponding DNS name.
Just for reference, answering this from all the useful pieces of information I received…
In the flash file only the Boxine CA is replaced by TeddyCloud’s CA.
The existing box certs are dumped of course in order to be used by TeddyCloud when talking to Boxine.
The config.overlay.ini file allows for proper configuration of multiple boxes - each referenced by their dumped client certificates.
With release 0.5.2 hosting TeddyCloud becomes a thing
The only technical challenge is running the SSL backend without a second public IP address. If you use NGINX as a reverse proxy you’ll find a way to do it above.
In case of HAProxy I am using the following snippet:
global
...
crt-base /usr/local/etc/haproxy/certs
...
defaults
log global
mode http
option httplog
option dontlognull
option forwardfor
...
userlist TeddyCloudUsers
user teddy insecure-password SomeStrongPasswordHerePlease
...
frontend tcp-in
bind *:443,[::]:443 v6only
mode tcp
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
use_backend tcp2http if { req.ssl_sni -i tc.example.org }
use_backend tcp2http if { req.ssl_sni -i regular.site1.com }
use_backend tcp2http if { req.ssl_sni -i regular.site2.com }
...
use_backend tcp2http if { req.ssl_sni -i final.regular.site.com }
default_backend teddycloud
frontend http-in
bind *:80,[::]:80 v6only
bind 127.0.0.1:10443 alpn h2,http/1.1 ssl crt /usr/local/etc/haproxy/certs accept-proxy
...
acl teddycloud hdr(host) tc.example.org
acl teddycloud_auth http_auth(TeddyCloudUsers)
http-request auth if teddycloud !teddycloud_auth
use_backend teddycloud_web if teddycloud
...
default_backend internalserver
backend tcp2http
mode tcp
server haproxy-loopback 127.0.0.1:10443 check send-proxy-v2
backend teddycloud
mode tcp
server teddycloud teddycloud:443 no-check
backend teddycloud_web
mode http
server teddycloud_web teddycloud:80 no-check
backend internalserver
server internalserver apache:80
http-request set-header X-Forwarded-Port %[dst_port]
http-request add-header X-Forwarded-Proto https if { ssl_fc }
http-request add-header X-Forwarded-Host %[hdr(Host)] if { ssl_fc }
The cool thing about HAProxy is that it can act as a TCP proxy. The benefit of which is that it does not have to terminate the SSL connection (no unsafe legacy renegotiation with the actual backend, no need to share any certificates). Unfortunatly the only piece of information passed at layer 4 is the SSL connection setup (no certificates or anything here) - the only useful thing is the SNI though.
It would be easy to route to the Teddycloud backend based on the SNI, but here of all things the box does NOT pass an SNI.
Hence, the setup needs to tediously document all hosted domains and route them based on SNI to the actual internal server.
The default routing will be applied to all SSL traffic WITHOUT SNI (= our TonieBoxes) or without specified SNI.
As a bonus I included BasicAuth authentication for the TeddyCloud frontend.
Hi,
was anyone able to configure Caddy as reverse proxy?
I have configured a normal reverse proxy for my subdomain (by default it creates its own certificates), but no access from the Toniebox appears in the access log.
Switching off auto https and linking the Teddycloud certificates for this subdomain did not help either.
Thanks, this almost worked for me. I had to modify this slightly to pass on my client certificate, otherwise the HTTPS Client Auth of Teddycloud fails. My nginx is living outside of docker, serving multiple (sub) domains, so I just have this default/invalid server name at the top of my config:
You’ll either need to mount the teddycloud volume to your nginx, or extract the certificate from the volume and make it available within the nginx container.
extracting can be done with in example docker cp teddycloud:/teddycloud/certs/client ./client
Hi @cfelder - I’m struggling to get this working. Which certificates are you using here? Can you provide a map between you used files and the ones available on the teddycloud server?
The client ca actually should not matter. We cannot use that to verify the cert.
openssl verify -CAfile ca.pem client.pem
C = DE, ST = NRW, L = D\C3\BCsseldorf, O = Boxine GmbH, CN = 1234567890CF
error 20 at 0 depth lookup:unable to get local issuer certificate
client.pem: verification failed: 20 (unable to get local issuer certificate)
Looks like the fullchain.pem is somehow malformed but I don’t know how. I googled around but haven’t found anything that matches this error.
I narrowed it down to ca-root.pem. As soon as ca-root.pem is part of/used as fullchain.pem. the error is thrown. openssl x509 -in ttt-fullchain.pem -text -noout works/looks fine though:
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 1729265846 (0x671280b6)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = DE, ST = NW, L = Duesseldorf, O = Team RevvoX, CN = TeddyCloud CA Root Cert.
Validity
Not Before: Nov 3 15:23:19 2015 GMT
Not After : Jun 24 15:23:19 2040 GMT
Subject: C = DE, ST = NW, L = Duesseldorf, O = Team RevvoX, CN = TeddyCloud Server
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:a4:1b:48:2b:e2:7a:62:58:cc:c1:97:8e:40:98:
72:67:04:03:b8:80:21:b8:e2:9f:61:1f:26:e7:3b:
a9:12:2a:2a:c6:b9:37:5f:0f:90:0e:cc:33:9f:24:
ba:99:2f:4e:5e:a5:ca:62:28:b5:4a:75:37:ce:aa:
3a:e5:71:ce:03:bd:7f:4e:24:26:02:dd:28:9b:56:
a9:46:21:f7:1e:88:15:ac:ef:71:26:cb:8f:6f:cd:
72:68:03:77:b4:f7:da:1f:07:d1:ef:74:d6:21:1c:
55:88:2b:ec:f2:da:f1:f3:ba:bb:8c:8c:81:e7:a8:
02:1a:a3:86:d8:ab:d2:63:44:10:bd:d9:50:f4:36:
fa:f6:54:19:87:5e:07:e9:26:c4:db:d6:7f:1f:f0:
6c:51:ad:09:b7:4b:3b:29:2e:79:1a:f0:d7:57:a6:
d9:1d:65:7f:15:7b:c5:32:52:81:48:b0:b3:ec:18:
6e:5d:f2:ab:06:bf:7c:38:c5:b2:2a:85:f3:48:24:
65:b9:af:16:c2:0c:8f:32:32:d4:8b:42:18:25:20:
39:77:97:72:1a:fb:10:4c:af:78:d0:69:88:55:e9:
89:fa:85:60:73:1a:f7:24:b5:83:23:e0:34:bb:31:
c2:dc:31:55:69:6c:47:15:36:5a:58:54:f8:b4:82:
ca:db
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
3B:F9:12:BD:C6:72:E5:B7:A5:08:97:E6:C1:12:56:90:16:05:A9:FC
X509v3 Authority Key Identifier:
C1:8C:E8:30:AE:C7:E5:3C:61:B0:1A:65:6E:02:09:EB:69:DE:C5:7C
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
6a:43:11:7e:a5:3d:28:d3:29:29:58:96:23:68:9e:c9:f4:ef:
85:76:bc:ce:af:73:d0:e6:d1:5d:99:ca:7e:f3:b3:db:a7:f9:
4f:80:dd:eb:40:86:85:5c:a7:2e:8b:7b:8f:bc:1d:a6:3d:fd:
11:b5:1e:0d:2a:94:71:1b:ea:ec:b6:d7:11:16:1b:53:c4:7b:
8f:57:24:31:26:c9:7d:23:6d:b9:39:4d:d3:c3:3d:75:36:ba:
f8:a2:75:cc:c9:50:74:28:75:df:57:bc:ea:82:c3:9f:b4:39:
c6:99:04:27:4a:ed:44:f2:69:c2:72:70:e5:48:a2:91:ab:0d:
0f:78:b6:7f:90:06:8f:a3:97:3a:40:e2:80:a3:0b:35:78:4b:
7d:06:5e:e4:f5:b0:28:96:f7:c0:40:61:d9:15:4d:5e:53:c4:
7e:b1:5d:66:59:05:17:76:71:43:e0:b0:f6:ec:7d:39:f1:09:
9b:2a:07:a2:28:ed:80:d7:fb:fe:f9:93:0c:ed:47:48:40:19:
1e:2f:8b:a6:7f:0d:6b:d9:c6:87:17:61:a6:ae:24:d1:fc:e9:
1a:c6:29:6a:6c:33:6c:61:0e:01:7c:ac:e5:38:23:14:05:9d:
ff:ed:aa:73:20:07:e3:67:7b:a0:ad:a4:02:3d:01:15:f5:96:
02:ac:8e:2f:9d:b2:55:73:bb:f8:02:d6:17:ef:16:f5:89:8c:
6a:a9:49:56:b9:ce:d4:e7:ca:46:e5:bf:62:d3:54:8d:a1:3b:
b1:0e:bc:5d:f2:f6:11:27:da:23:6b:46:76:94:0a:30:eb:af:
bd:ff:6a:5c:73:44:3e:88:bf:c0:de:36:f6:f7:f4:15:8e:13:
ee:55:d9:0f:9c:37:fe:39:9c:26:aa:b6:e5:5b:d7:7f:a0:f0:
3b:21:a6:6d:9b:a7:06:d7:77:2c:64:88:09:26:a9:e9:5b:7c:
4c:4b:0b:da:c2:24:4a:42:f5:ec:95:ca:2f:68:a8:6b:5f:2b:
ba:b5:87:0f:fe:50:30:14:95:4f:17:52:6d:e7:33:d5:a7:d5:
53:2e:fa:48:1b:a9:1c:ad:36:a8:18:78:f3:13:07:a6:ca:3a:
40:91:dd:e4:5e:4e:4e:22:87:46:9f:18:4a:b8:6a:63:78:45:
30:cf:94:36:74:5b:70:1d:fa:39:b7:90:ad:9f:2c:8b:96:f2:
b0:f8:f3:6b:3c:57:56:36:d0:be:a0:4d:7e:75:92:0b:35:5e:
00:9d:a6:95:3b:78:d4:60:86:5f:e0:0b:57:21:51:55:59:72:
f9:27:04:16:f9:b3:d9:87:25:29:72:8f:1a:16:ef:c6:30:c0:
67:dc:73:06:ec:93:ff:75
Do you have any idea on what could be wrong/differing from you setup here?