iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
💇‍♂️

Supporting Legacy Ciphers for cloudflared HTTPS Connections

に公開

Introduction

The Cipher suites supported by the HTTPS protocol in cloudflared are as follows as of November 2025.

Go TLS library

which relies on the Go TLS library for its TLS implementation.
As stated here, it appears to be the same as Go.

Connections cannot be made to legacy HTTPS servers that do not support these ciphers.
One possible solution is to insert a TLS proxy, so I will try that.

Connection failure due to cipher mismatch

I will simulate a case where connecting directly from cloudflared to an HTTPS server fails.

  • HTTPS server
    Listens on port 44443 and supports only AES256-SHA256
    HTTPS server
    sudo openssl s_server -accept 44443 -cert cert.pem -key key.pem -tls1_2 -cipher 'AES256-SHA256' -www
    
  • cloudflared
    Connects to the public hostname on port 44443 (HTTPS)

  • Result
    The client receives a 502 error.
    Client 502
    curl https://tls.tun.oymk.work/ -sv
    :
    < HTTP/2 502
    :
    error code: 502%
    
    The HTTPS server observes a cipher mismatch, and a Handshake Failure Alert can be seen.
    HTTPS server (log) no shared cipher
    40B7DBB51B7C0000:error:0A0000C1:SSL routines:tls_post_process_client_hello:no shared cipher:../ssl/statem/statem_srvr.c:2220:
    
    HTTPS server (pcap) Handshake Failure
    Transport Layer Security
      TLSv1.2 Record Layer: Alert (Level: Fatal, Description: Handshake Failure)
          Content Type: Alert (21)
          Version: TLS 1.2 (0x0303)
          Length: 2
          Alert Message
              Level: Fatal (2)
              Description: Handshake Failure (40)
    

Connecting via a TLS proxy

By inserting a TLS proxy, we can bridge the gap in supported Cipher suites and enable the connection.

  • cloudflared
    Connects to the public hostname on port 4443 (HTTPS)

  • Result
    The client receives a 200 status.
    Client 200
    curl https://tls.tun.oymk.work/ -sv
    :
    < HTTP/2 200
    :
    <HTML><BODY BGCOLOR="#ffffff">
    :
    
    The HTTPS server responds with a Server hello.
    HTTPS server (pcap) server hello
    Transport Layer Security
      TLSv1.2 Record Layer: Handshake Protocol: Server Hello
          Content Type: Handshake (22)
          Version: TLS 1.2 (0x0303)
          Length: 89
          Handshake Protocol: Server Hello
              Handshake Type: Server Hello (2)
    :
    

TLS proxy configuration example

Without performing any in-depth comparisons, I chose to use stunnel as it seemed quick and easy.
Web servers (such as Nginx) or load balancers could also be used.

Environment
ubuntu 22.04 
stunnel 5.63
OpenSSL 3.0.2
stunnel status
sudo systemctl status stunnel

 stunnel.service - Stunnel TLS Bridge
     Loaded: loaded (/etc/systemd/system/stunnel.service; enabled; vendor preset: enabled)
     :
     CGroup: /system.slice/stunnel.service
          └─1142426 /usr/bin/stunnel4 /etc/stunnel/tls-bridge.conf

Change the Backend connection target according to your environment.
In this example, the HTTPS server is set up on the same host.

stunnel config
cat /etc/stunnel/tls-bridge.conf

##########################################
# Global stunnel settings
##########################################
pid = /var/run/stunnel4/stunnel.pid
setuid = stunnel4
setgid = stunnel4
foreground = yes
debug = notice
output = /var/log/stunnel4/stunnel.log

# Each parameter is for a test environment only
socket = l:TCP_NODELAY=1
socket = r:TCP_NODELAY=1
sessionCacheSize    = 1024
sessionCacheTimeout = 3600
sessionResume       = yes
TIMEOUTbusy    = 600
TIMEOUTclose   = 10
TIMEOUTconnect = 30
TIMEOUTidle    = 300

##########################################
# Frontend: accept modern TLS from clients
##########################################
[tls_front]
accept = 0.0.0.0:4443
connect = 127.0.0.1:8443
cert = /etc/stunnel/server.pem
key  = /etc/stunnel/server-key.pem
sslVersion = TLSv1.3
ciphersuites = TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
verifyChain = no

##########################################
# Backend: connect to legacy TLS server
##########################################
[tls_back]
accept = 127.0.0.1:8443
connect = 127.0.0.1:44443
client = yes
sslVersion = TLSv1.2
ciphers = AES256-SHA256
verifyChain = no
sni = tls.tun.oymk.work
stunnel.log debug = debug
# frontend
2025.11.04 11:39:33 LOG5[0]: Service [tls_front] accepted connection from 127.0.0.1:38486
:
2025.11.04 11:39:33 LOG6[0]: TLSv1.3 ciphersuite: TLS_AES_256_GCM_SHA384 (256-bit encryption)
:
# backend
2025.11.04 11:39:33 LOG5[1]: Service [tls_back] connected remote server from 127.0.0.1:59620
:
2025.11.04 11:39:33 LOG6[1]: TLSv1.2 ciphersuite: AES256-SHA256 (256-bit encryption)

In reality, this is the structure:

Disclaimer

  • I have not investigated the stunnel configuration deeply; there may be oversights.
  • This configuration is for testing purposes only and is not intended for production.
  • As testing was performed with only a single connection, issues might arise under multiple connections or high load.
  • If you place the TLS proxy on the same server as cloudflared and scale cloudflared using replicas, it should be possible to increase the number of TLS proxies accordingly.

Discussion