<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Tls on Guillaume Delré</title><link>https://guillaumedelre.github.io/tags/tls/</link><description>Recent content in Tls on Guillaume Delré</description><generator>Hugo</generator><language>en</language><lastBuildDate>Thu, 17 Apr 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://guillaumedelre.github.io/tags/tls/index.xml" rel="self" type="application/rss+xml"/><item><title>Local HTTPS with Traefik: traefik.me is dead, long live sslip.io</title><link>https://guillaumedelre.github.io/2025/04/17/local-https-with-traefik-traefik.me-is-dead-long-live-sslip.io/</link><pubDate>Thu, 17 Apr 2025 00:00:00 +0000</pubDate><guid>https://guillaumedelre.github.io/2025/04/17/local-https-with-traefik-traefik.me-is-dead-long-live-sslip.io/</guid><description>traefik.me&amp;#39;s wildcard cert was revoked in 2025. Here&amp;#39;s how to replace it with sslip.io, mkcert, and a local Traefik setup.</description><content:encoded><![CDATA[<p>The setup seemed perfect. Point <code>*.traefik.me</code> at 127.0.0.1, download a wildcard certificate from the same domain, drop it into Traefik, and every local service gets a clean HTTPS URL with no IP in the address bar. No Let&rsquo;s Encrypt rate limits, no <code>mkcert</code> to explain to teammates, no self-signed warnings to click through. Just <code>https://myapp.traefik.me</code> and a green padlock.</p>
<p>Then in March 2025, Let&rsquo;s Encrypt revoked the certificate. The wildcard cert for traefik.me is gone and it&rsquo;s not coming back.</p>
<h2 id="what-traefikme-was-actually-selling">What traefik.me was actually selling</h2>
<p>traefik.me is a wildcard DNS resolver. Type <code>anything.traefik.me</code> and it resolves to 127.0.0.1. Type <code>anything.10.0.0.1.traefik.me</code> and it resolves to 10.0.0.1. No account, no configuration, no infrastructure to maintain. The DNS part still works fine, by the way.</p>
<p>The certificate was the bonus: a wildcard cert for <code>*.traefik.me</code> that pyrou, the maintainer, generated with Let&rsquo;s Encrypt and distributed at <code>https://traefik.me/cert.pem</code> and <code>https://traefik.me/privkey.pem</code>. It was convenient precisely because it was shared: download, drop into Traefik, done.</p>
<p>Sharing a private key is why it died.</p>
<p>The CA/Browser Forum Baseline Requirements, section 9.6.3, require subscribers to &ldquo;maintain sole control&rdquo; over their private key. Distributing it to anyone who visits a URL is the exact opposite of sole control. Let&rsquo;s Encrypt sent a notice, blocked future issuance for the domain, and revoked the existing certificate. Pyrou confirmed the situation and recommended mkcert as an alternative. The project will live on as a DNS resolver only.</p>
<p>The cert had already been revoked twice before 2025. Third time was the last.</p>
<h2 id="sslipio-does-the-same-thing-differently">sslip.io does the same thing, differently</h2>
<p>sslip.io is also a wildcard DNS resolver, with one difference: the IP is encoded in the hostname rather than resolved from a fallback. <code>10-0-0-1.sslip.io</code> resolves to <code>10.0.0.1</code>. <code>myapp.192-168-1-10.sslip.io</code> resolves to <code>192.168.1.10</code>. IPv6 works too.</p>
<p>The infrastructure behind sslip.io is also more visible: three nameservers in Singapore, the US, and Poland, handling over 10,000 requests per second, with public monitoring. About 1,000 GitHub stars and active maintenance under the Apache 2.0 licence.</p>
<p>Strip away the certificate story and the comparison is pretty straightforward:</p>
<table>
  <thead>
      <tr>
          <th></th>
          <th>traefik.me</th>
          <th>sslip.io</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>DNS wildcard</td>
          <td>yes</td>
          <td>yes</td>
      </tr>
      <tr>
          <td>Fallback to 127.0.0.1</td>
          <td>yes</td>
          <td>no</td>
      </tr>
      <tr>
          <td>IPv6</td>
          <td>no</td>
          <td>yes</td>
      </tr>
      <tr>
          <td>Wildcard certificate</td>
          <td><del>yes</del> revoked</td>
          <td>no</td>
      </tr>
      <tr>
          <td>Infrastructure</td>
          <td>opaque</td>
          <td>documented</td>
      </tr>
      <tr>
          <td>Project activity</td>
          <td>stalled</td>
          <td>active</td>
      </tr>
  </tbody>
</table>
<p>traefik.me&rsquo;s only remaining advantage is the 127.0.0.1 fallback: URLs without an IP segment. That matters if you really want <code>myapp.traefik.me</code> instead of <code>myapp.127-0-0-1.sslip.io</code>. Whether that difference is worth the infrastructure uncertainty is a short conversation.</p>
<h2 id="mkcert-fills-the-gap">mkcert fills the gap</h2>
<p>mkcert creates a local certificate authority, installs it in the system trust store and whatever browsers it finds, then issues certificates signed by that CA. Browsers see a trusted chain. No warning, no click-through, no &ldquo;proceed anyway&rdquo;.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkcert -install
</span></span></code></pre></div><p>That&rsquo;s the one-time setup. After that, generating a certificate is one command:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkcert <span style="color:#e6db74">&#34;*.127-0-0-1.sslip.io&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># produces _wildcard.127-0-0-1.sslip.io.pem</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">#          _wildcard.127-0-0-1.sslip.io-key.pem</span>
</span></span></code></pre></div><p>The limitation is that mkcert&rsquo;s CA is local. Other machines on the network won&rsquo;t trust it by default. For a solo dev setup that&rsquo;s fine. For a shared team environment, you&rsquo;d need to distribute the CA root, which is essentially the same operational problem traefik.me was trying to avoid, just smaller in scope.</p>
<h2 id="the-traefik-configuration">The Traefik configuration</h2>
<p>The setup is the same regardless of which DNS service you pick. Traefik needs the certificate mounted as a volume and a static file provider pointing at a TLS configuration file.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#75715e"># traefik/config/tls.yml</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">tls</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">certificates</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#f92672">certFile</span>: <span style="color:#ae81ff">/certs/cert.pem</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">keyFile</span>: <span style="color:#ae81ff">/certs/key.pem</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">stores</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">default</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">defaultCertificate</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">certFile</span>: <span style="color:#ae81ff">/certs/cert.pem</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">keyFile</span>: <span style="color:#ae81ff">/certs/key.pem</span>
</span></span></code></pre></div><p>The key practice: run Traefik in its own Compose project, separate from the services it routes to. Each service project connects to Traefik through a shared external network. Start and stop services independently without touching the reverse proxy.</p>
<p>Start by creating the external network once:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker network create traefik-public
</span></span></code></pre></div><p><strong><code>traefik/compose.yml</code></strong> - Traefik alone, owning the network:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">traefik</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">traefik:v3</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">ports</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;80:80&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;443:443&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">volumes</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">/var/run/docker.sock:/var/run/docker.sock</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">./config:/etc/traefik/config</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">./certs:/certs</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">command</span>:
</span></span><span style="display:flex;"><span>      - --<span style="color:#ae81ff">entrypoints.web.address=:80</span>
</span></span><span style="display:flex;"><span>      - --<span style="color:#ae81ff">entrypoints.websecure.address=:443</span>
</span></span><span style="display:flex;"><span>      - --<span style="color:#ae81ff">providers.docker=true</span>
</span></span><span style="display:flex;"><span>      - --<span style="color:#ae81ff">providers.docker.network=traefik-public</span>
</span></span><span style="display:flex;"><span>      - --<span style="color:#ae81ff">providers.file.directory=/etc/traefik/config</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">networks</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">traefik-public</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">networks</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">traefik-public</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">external</span>: <span style="color:#66d9ef">true</span>
</span></span></code></pre></div><p>Copy the mkcert output into <code>./certs/</code>, rename to <code>cert.pem</code> and <code>key.pem</code>, then:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose -f traefik/compose.yml up -d
</span></span></code></pre></div><p>Traefik is up, listening on 80 and 443, watching Docker for new containers. Nothing is routed yet.</p>
<p><strong><code>whoami/compose.yml</code></strong> - a service that joins the same network:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">whoami</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">traefik/whoami</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">labels</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;traefik.enable=true&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;traefik.http.routers.whoami.rule=Host(`whoami.127-0-0-1.sslip.io`)&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;traefik.http.routers.whoami.tls=true&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#e6db74">&#34;traefik.http.routers.whoami.entrypoints=websecure&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">networks</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">traefik-public</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">networks</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">traefik-public</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">external</span>: <span style="color:#66d9ef">true</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>docker compose -f whoami/compose.yml up -d
</span></span></code></pre></div><p>Traefik detects the new container via the Docker provider, reads its labels, and adds the route. <code>https://whoami.127-0-0-1.sslip.io</code> responds immediately. Bring <code>whoami</code> down and the route disappears. Traefik keeps running without noticing.</p>
<p>The <code>external: true</code> declaration is the load-bearing line. Without it, Compose creates a project-scoped network: Traefik and <code>whoami</code> end up on different networks and can&rsquo;t reach each other, even though both are running. The external network is the shared bus every service project must explicitly opt into.</p>
<p>If you prefer traefik.me URLs, replace the mkcert command and the host label:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>mkcert <span style="color:#e6db74">&#34;*.traefik.me&#34;</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#e6db74">&#34;traefik.http.routers.whoami.rule=Host(`whoami.traefik.me`)&#34;</span>
</span></span></code></pre></div><p>The DNS fallback to 127.0.0.1 handles the rest.</p>
<h2 id="what-the-traefikme-story-actually-teaches">What the traefik.me story actually teaches</h2>
<p>The certificate distribution model was always fragile. A &ldquo;public-private key pair&rdquo; is a contradiction in terms. Every revocation was a warning that the next one could be permanent. Eventually it was.</p>
<p>The lesson isn&rsquo;t specific to traefik.me. Any service that provides convenience by quietly removing a security boundary will eventually hit that boundary. mkcert is the right tool for this problem because it operates entirely within your own trust domain: you generate the CA, you install it, you issue the certificates. Nothing depends on a third party&rsquo;s continued willingness to bend certificate issuance rules.</p>
<p>sslip.io solves the DNS part cleanly. mkcert solves the TLS part cleanly. They compose well. The traefik.me setup was simpler, for a while. Until it wasn&rsquo;t.</p>
]]></content:encoded></item></channel></rss>