The Encrypted Client Hello (ECH) mechanism draft-spec is a way to plug a few privacy-holes that remain in the Transport Layer Security (TLS) protocol that’s used as the security layer for the web. OpenSSL is a widely used library that provides an implementation of the TLS protocol. The DEfO project has developed an implementation of ECH for OpenSSL, and proof-of-concept implementations of various clients and servers that use OpenSSL, and other TLS libraries, as a demonstration and for interoperability testing. DEfO is funded by the Open Technology Fund (OTF).
This guide is aimed at those who would like to try out ECH with our experimental Debian unstable packages for OpenSSL, curl and nginx. The DEfO project is making packages temporarily available so people can carry out such experiments before upstream maintainers include ECH in standard releases.
This guide describes two set ups: one assumes that you want to setup everything (i.e. a web server and associated DNS authoritative server) on one virtual machine and a second describes a way to set up an ECH-enabled web server if you have an existing DNS configuration elsewhere. Many other configurations are of course possible. Comments/questions are welcome via email or as GitLab issues/merge requests.
In summary this guide shows how to: - find and install our Debian unstable packages - configure a minimal DNS setup (using dnsmasq or bind) that enables use of ECH from browsers - configure a minimal nginx setup for an ECH enabled web sites - test your DNS set up and that ECH is working
We assume the reader is comfortable with simple DNS management and system administration tasks.
When commands shown below should be run as
root they are prepended with a ‘#’, if not they are prepended with a ‘$’.
You need to first have access to a virtual machine running Debian unstable. Many online resources describe many ways to do this.
We start with an empty (minimal) Debian unstable system and install OpenSSL from the DEfO apt repository.
# apt install ca-certificates # echo "deb [trusted=yes] https://defo.ie/debian/ ./" > /etc/apt/sources.list.d/defo.list # apt update # apt install openssl
Pick some DNS names to use
In the text below we assume that the web server for which we wish to benefit from ECH is called
ECH also involves a so-called
public_name - that’s present in the outer ClientHello when ECH is used and hence is visible to a network observer, unlike the server name we include inside the Encrypted Client Hello. The
public_name we use below is
You should obviously substitute your chosen DNS names.
Generate an ECH key pair
For ECH to work, we need a new asymmetric key pair. (Different from those used for TLS server authentication), so next we generate an ECH key pair:
# mkdir /etc/echkeydir/ # openssl ech -public_name example.com -pemout /etc/echkeydir/example.pem.ech
Note that we need this file later to set up DNS as well as nginx. Nginx will make use of the private key from the ECH key file, whilst the DNS will be used to publish the corresponding public key in an HTTPS resource record (that’s where ECH-enabled browsers will look for ECH public keys).
The file name chosen should end with
Set up DNS
There are many ways in which one can set up the DNS records required to enable experimenting with ECH. We document two ways here, the first is where the main DNS name with which you’re experimenting is newly registered DNS name and has no existing DNS set up. The second describes a case where a DNS name exists but we need to add some new resource records to enable ECH.
In both cases,
<IP> should be the IPv4 address for the host running the web server. (We leave IPv6 handling as an exercise for the reader, but there’s nothing ECH-specific required for IPv6.)
Standalone/New setup : dnsmasq as a authoritative DNS server to serve the ECH key (on the same VM)
In this case we also need to pick some DNS nameserver names, we assume the authoritative primary nameserver for both web server DNS names is
ns.example.com with a secondary of
The DNS set up to use starting from a clean dnsmasq install:
# apt install dnsmasq # cat <<EOF > /etc/dnsmasq.d/example.conf no-resolv no-hosts auth-server=ns.example.com,ns2.example.com auth-zone=example.com auth-sec-servers=ns2.example.com auth-soa=42,admin.example.com host-record=example.com,<IP> host-record=hidden.example.com,<IP> dns-rr=hidden.example.com,65,<ascii-hex encoded HTTPS rdata> EOF # systemctl restart dnsmasq
Note that you need to bump the SOA version (42 in the above) every time you change the config and make sure the zone distributes to ns2.
<ascii-hex encoded HTTPS rdata> will be published as the HTTPS resource record (type == 65) for
hidden.example.com and can be produced from the file we generated earlier via a shell script you can download and use as follows:
$ _curl_ https://raw.githubusercontent.com/sftcd/openssl/ECH-draft-13c/esnistuff/pem2rr.sh -o pem2rr.sh $ chmod u+x pem2rr.sh $ ./pem2rr.sh /etc/echkeydir/example.pem.ech 0001000005003b0039fe0d0035db0020002059907d619054c907a1f296ceb63dde1d57f72f15db172601a2f6b55e66e7cd0f00040001000100066261722e69650000 $
Existing DNS name setup: using bind to publish new ECH related resource records
Let’s assume you have an existing bind-based DNS setup for
, but are moving the IP address for that to a new VM that'll run the ECH-enabled web server for bothexample.com
. The change you might then make using the bindnsupdate` command on the authoritative DNS server would then look like:
- Change/add IP address records for our DNS names.
$ sudo nsupdate -l > update delete example.com a > update delete example.com aaaa > update add example.com 300 a <IP> > update add hidden.example.com 300 a <IP> > send > quit $
- Add new HTTPS resource record for
First we need to get the base64 encoded public from our
$ tail -2 /etc/echkeydir/example.pem.ech | head -1 ADr+DQA2mQAgACAF7cLT+KtK0oR2DrRCGXmzBWM1eHgJgDEGLqL644/OcAAEAAEAAQAHaG9iYS5pZQAA $
That base64 encoded value is what we need to publish in the DNS…
$ sudo nsupdate -l > update delete hidden.example.com HTTPS > update add hidden.example.com 300 HTTPS 1 . ech=<base64-encoded-value> > send > quit $
Checking your DNS set up
You can check that value is correctly published in the DNS e.g. using the
$ dig +short https hidden.example.com 1 . ech=ADr+DQA2mQAgACAF7cLT+KtK0oR2DrRCGXmzBWM1eHgJgDEGLqL644/OcAAEAAEAAQAHaG9iYS5pZQAA $
If your version of
dig is older and doesn’t know about HTTPS resource records, then you may need to check via:
$ dig +short +unknownformat -t TYPE65 hidden.example.com \# 67 0001000005003C003AFE0D0036990020002005EDC2D3F8AB4AD28476 0EB4421979B30563357878098031062EA2FAE38FCE70000400010001 0007686F62612E69650000 $
The 2nd-last invocation of
dig above shows the presentation format version of the HTTPS resource record. The last invocation above shows the equivalent in ascii-hex, which (minus the spaces) is what
dnsmasq needs in it’s configuration file.
dnsmasq will likely accept presentation format for HTTPS resource records in future as
bind tooling already.
Set up nginx to serve an outer and an inner (hidden) website
Install nginx-light from the DEfO repo, as above:
# apt install _nginx_-light certbot python3-certbot-_nginx_
If you don’t already have a web server config for
example.com then you can create a basic pair of
sites-enabled configuration files via the following commands:
# rm /etc/_nginx_/sites-enabled/default # cp /etc/_nginx_/sites-available/default /etc/_nginx_/sites-enabled/example.conf # sed -i 's/server_name _;/server_name example.com;/' /etc/_nginx_/sites-enabled/example.conf # echo "ssl_echkeydir /etc/echkeydir/;" >> /etc/_nginx_/sites-enabled/example.conf # cp /etc/_nginx_/sites-available/default /etc/_nginx_/sites-enabled/hidden.example.conf # sed -i -e 's/server_name _;/server_name hidden.example.com;/' -e sed 's/\(listen.*\)default_server;/\1;/' /etc/_nginx_/sites-enabled/hidden.example.conf # systemctl restart _nginx_
It’s possible the
sed commands above may not work for you, e.g. if default files change, or perhaps you won’t use those if you have an existing web server config for
. In that case you can simply edit the config files to ensure the following, before runningcertbot``:
- you have
hidden.example.comservers set up with correct
- optionally change the DocRoot (
root) in these configuration files to serve different content
- there’s a line within the
httpstanza as follows:
ssl_echkeydirline can in
httpstanza or within either of the files in
sites-enabledwhen it must be outside the
Run certbot to get a TLS server certificate
Next you need to rRun
certbot to get TLS server public keys certificates for both DNS names (
example.com` and `hidden.example.com). Those can be in the same certificate (or not) for the purposes of this experimental setup.
# certbot --nginx ...iteractions... #
certbot may prompt you for e.g. an email address or approval and if you have an existing certificate for
example.com you may be prompted as to whether you want to add
hidden.example.com to that certificate.
Use curl to test the set up
Install our ECH-enabled curl package from the DEfO repo and run:
$ curl -v --ech true --doh-url https://22.214.171.124/dns-query https://hidden.example.com/ |& grep Succeeded
You should see this in the output:
ECH: result: status is Succeeded, inner is hidden.example.com, outer is example.com
Check with browsers
If you’re running a recent browser version (chromium-based since version 105, firefox since before then:-) you can enable ECH in the browser by following our client configuration instructions. Unfortunately, browsers don’t provide user interface to show if ECH has succeeded or not, but you can verify that your browser is able to use ECH if you visit https://defo.ie/ech-check.php.
All going well, you should now have an ECH-enabled web site and be able to extend/play with that as you like. We’d appreciate feedback on this guide if you have a chance. As before, comments/questions are welcome via email or as GitLab issues/merge requests.