Feasibility of hosting Teddycloud

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…

Thanks,
Alex

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.

catchall.tld.conf (nginx)

map $ssl_client_fingerprint $reject {
	default 1;

	"b571ef0c5841a7c23bfce63f1a20286fcfcfcfcf"	0;
}

server {
	listen 8443 ssl;
	listen [::]:8443;
	server_name _;

	ssl_certificate /etc/ssl/certs/server/ttt-fullchain.pem;
	ssl_certificate_key /etc/ssl/certs/server/ttt.pem;

	ssl_client_certificate /etc/ssl/certs/client/ca.pem;
	ssl_verify_client optional_no_ca;

	root /usr/share/nginx/html/server;

	location / {
		if ($reject) {
			return 403;
		}
		set $upstream 127.0.0.1:443;
 		proxy_pass https://$upstream$request_uri;
		proxy_ssl_conf_command Options UnsafeLegacyRenegotiation;
	}
}

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.

Best
Christian

A post was split to a new topic: Reflash ESP32 to change hostname

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.

There will be more or less soon a update in the new /web gui where you can edit the settings for each known tonieBox. Have a look here: https://github.com/toniebox-reverse-engineering/teddycloud_web/issues/20

A first poc (readonly) is already in develop of teddycloud.

Feel free to add additional ideas or feature requests :slight_smile:

1 Like

With release 0.5.2 hosting TeddyCloud becomes a thing :slight_smile:

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.

this should also work with traefik and the TLS passthrough

Indeed Traefik does support TCP routing as well.
I don’t know if you can mix TLS termination though.

I’ll try to come up with a Traefik example and post it here.

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.

Any further news on this? I am quite interested to host teddycloud on my server and make it available there

You can use nginx or Haproxy as described above.
Just make sure to setup basic auth in the reverse proxy for now.

3 posts were split to a new topic: Limit access to box

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:

map $ssl_client_fingerprint $toniebox {
        default invalid;
        "client_cert_fingerprint" my_toniebox;
}
server {
        listen 0.0.0.0:443 http2 ssl;
        server_name _;

        ssl_certificate /teddycloud/server/fullchain.pem;
        ssl_certificate_key /teddycloud/server/ca-key.key;
        ssl_verify_client optional_no_ca;
        location / {
                if ($toniebox ~ my_toniebox)
                {
                        proxy_pass https://127.0.0.1:4444;
                        proxy_ssl_certificate /teddycloud/client/client.pem;
                        proxy_ssl_certificate_key /teddycloud/client/client.key;
                }
                return 444;
        }
}

I only have to support one Toniebox right now, but this can be easily extended to support multiple, however in a manual fashion.

A post was split to a new topic: Use certificates and nginx

I get an error at the line

                    proxy_ssl_certificate /teddycloud/client/client.pem;

nginx: [emerg] “proxy_ssl_certificate” directive is not allowed here in /data/nginx/default_host/site.conf:30

I know nginx is special with some settings but I wonder if this config is accepted by your nginx version.

I don’t need the support of multiple boxes at the moment, so I can go with the config from the other thread.

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

The certificate is not the problem.
The problem is that my nginx does not accept this directive inside an if inside the location directive.

nginx: [emerg] “proxy_ssl_certificate” directive is not allowed here in /data/nginx/default_host/site.conf:30

With the config from Feasibility of hosting Teddycloud - #7 by cfelder my nginx works.

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?

certs/server/ca-key.pem
certs/server/ca-root.pem
certs/server/ca.der
certs/server/teddy-key.pem
certs/server/teddy-cert.pem

My nginx complains about teddy-key.pem not being a valid key.

Thanks for reaching out. The following should shed more light on this.

server:

  • ttt-fullchain.pem
    • teddy-cert.pem
    • converted teddy-key.pem (rsa private key; openssl rsa -inform DER -in teddy-key.pem -out ttt-teddy-key.pem)
    • ca-root.pem
    • converted ca-key.pem (rsa private key; openssl rsa -inform DER -in ca-key.pem -out ttt-ca-key.pem)

cat teddy-cert.pem ttt-teddy-key.pem ca-root.pem ttt-ca-key.pem > ttt-fullchain.pem

	ssl_certificate /etc/ssl/certs/server/ttt-fullchain.pem;
	ssl_certificate_key /etc/ssl/certs/server/ttt-teddy-key.pem;

client:

  • ca.pem
    • Issuer: C=DE, ST=NW, L=Duesseldorf, O=Boxine GmbH, CN=Boxine CA
    • C=DE, ST=NW, L=Duesseldorf, O=Boxine GmbH, CN=Boxine CA
	ssl_client_certificate /etc/ssl/certs/client/ca.pem;

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)

That the reason why I am using

	ssl_verify_client optional_no_ca;
2 Likes

Thanks for getting back this fast. I tried this but nginx fails to start because of certificate validation errors:

I did:

  112  openssl rsa -inform DER -in teddy-key.pem -out ttt-teddy-key.pem
  113  openssl rsa -inform DER -in ca-key.pem -out ttt-ca-key.pem
  114  cat teddy-cert.pem ttt-teddy-key.pem ca-root.pem ttt-ca-key.pem > ttt-fullchain.pem
  115  cp ttt-fullchain.pem /data/custom_ssl/npm-10/fullchain.pem
  116  cp ttt-teddy-key.pem /data/custom_ssl/npm-10/privkey.pem
  117  /usr/sbin/nginx -t

(/data/custom_ssl/npm-10 is the path where Nginx-Proxy-Manager expects the certs in my configuration)

this throws the following certificate validation error:

nginx: [emerg] cannot load certificate "/data/custom_ssl/npm-10/fullchain.pem": PEM_read_bio_X509() failed (SSL: error:068000DD:asn1 encoding routines::illegal padding error:0688010A:asn1 encoding routines::nested asn1 error:Field=serialNumber, Type=X509_CINF error:0688010A:asn1 encoding routines::nested asn1 error:Field=cert_info, Type=X509 error:0488000D:PEM routines::ASN1 lib)

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?