<?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>Acme on Carlos Vaz</title>
    <link>https://carlosvaz.com/tags/acme/</link>
    <description>Recent content in Acme on Carlos Vaz</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-US</language>
    <managingEditor>carlos@carjorvaz.com (Carlos Vaz)</managingEditor>
    <webMaster>carlos@carjorvaz.com (Carlos Vaz)</webMaster>
    <lastBuildDate>Mon, 26 Dec 2022 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://carlosvaz.com/tags/acme/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Setting up wildcard Let&#39;s Encrypt certificates on NixOS</title>
      <link>https://carlosvaz.com/posts/setting-up-wildcard-lets-encrypt-certificates-on-nixos/</link>
      <pubDate>Mon, 26 Dec 2022 00:00:00 +0000</pubDate><author>carlos@carjorvaz.com (Carlos Vaz)</author>
      <guid>https://carlosvaz.com/posts/setting-up-wildcard-lets-encrypt-certificates-on-nixos/</guid>
      <description>&lt;p&gt;After setting up an Headscale server in order to have a mesh-VPN for my devices, I was now able to set up self-hosted services on my home server and access them anywhere in the world.&lt;/p&gt;&#xA;&lt;p&gt;However, because they&amp;rsquo;re inside this private network, I&amp;rsquo;m not able to get Let&amp;rsquo;s Encrypt certificates through the usual HTTP-01 challenge, exactly because the home server is not accessible from the public Internet.&lt;/p&gt;&#xA;&lt;p&gt;Because of this, and to prevent my browsers from screaming that I&amp;rsquo;m acessing my services through an &amp;ldquo;Insecure Connection&amp;rdquo; (even though the encrypted connection provided by Tailscale is plenty secure), I decided to set up wildcard certificates, provided by Let&amp;rsquo;s Encrypt.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>After setting up an Headscale server in order to have a mesh-VPN for my devices, I was now able to set up self-hosted services on my home server and access them anywhere in the world.</p>
<p>However, because they&rsquo;re inside this private network, I&rsquo;m not able to get Let&rsquo;s Encrypt certificates through the usual HTTP-01 challenge, exactly because the home server is not accessible from the public Internet.</p>
<p>Because of this, and to prevent my browsers from screaming that I&rsquo;m acessing my services through an &ldquo;Insecure Connection&rdquo; (even though the encrypted connection provided by Tailscale is plenty secure), I decided to set up wildcard certificates, provided by Let&rsquo;s Encrypt.</p>
<h2 id="nixos-configuration">NixOS configuration</h2>
<p>As usual, the NixOS configuration part of this setup is not too difficult:</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-nix" data-lang="nix"><span style="display:flex;"><span>{ self<span style="color:#f92672">,</span> config<span style="color:#f92672">,</span> lib<span style="color:#f92672">,</span> pkgs<span style="color:#f92672">,</span> <span style="color:#f92672">...</span> }:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  security<span style="color:#f92672">.</span>acme <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    acceptTerms <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>    defaults<span style="color:#f92672">.</span>email <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;john.doe@example.com&#34;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    certs<span style="color:#f92672">.</span><span style="color:#e6db74">&#34;example.com&#34;</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      domain <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;example.com&#34;</span>;
</span></span><span style="display:flex;"><span>      extraDomainNames <span style="color:#f92672">=</span> [ <span style="color:#e6db74">&#34;*.example.com&#34;</span> ];
</span></span><span style="display:flex;"><span>      dnsProvider <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;ovh&#34;</span>;
</span></span><span style="display:flex;"><span>      dnsPropagationCheck <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>      credentialsFile <span style="color:#f92672">=</span> <span style="color:#e6db74">/path/to/secrets/file</span>;
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  users<span style="color:#f92672">.</span>users<span style="color:#f92672">.</span>nginx<span style="color:#f92672">.</span>extraGroups <span style="color:#f92672">=</span> [ <span style="color:#e6db74">&#34;acme&#34;</span> ];
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><p>In this example, I set my provider to OVH. You may want to change this to <a href="https://go-acme.github.io/lego/dns/">your provider of choice</a>.</p>
<p>Also worthy of note is that we need to add the <code>nginx</code> user to the <code>acme</code> group, so that nginx can read the certificate files created by the acme service.
Adjust this according to your preference of reverse proxy software.</p>
<h2 id="ovh-api-credentials">OVH API credentials</h2>
<p>The DNS-01 challenge, as the name implies, requires setting up DNS records to prove ownership of resources.
Because of that, we&rsquo;ll need to set up an API key for the acme NixOS module, so that it can set up these records automatically.</p>
<p>To create these credentials:</p>
<ol>
<li>Go to <a href="https://eu.api.ovh.com/createToken/">https://eu.api.ovh.com/createToken/</a></li>
<li>Fill the form with the required information as shown below:
<ul>
<li>Account ID or email address: usual OVH login</li>
<li>Password: usual OVH password</li>
<li>Script Name: for example, &ldquo;NixOS wildcard certificate&rdquo;</li>
<li>Script description: for example, &ldquo;NixOS wildcard certificate&rdquo;</li>
<li>Validity: Unlimited</li>
<li>Rights: use the + button to add the following lines
<ul>
<li><code>POST</code> : <code>/domain/zone/*</code></li>
<li><code>DELETE</code> : <code>/domain/zone/*</code></li>
</ul>
</li>
</ul>
</li>
</ol>
<p>After creating them, we&rsquo;ll need to place them in the <code>/path/to/secrets/file</code>.</p>
<p>Be careful not to share these publicly.
I personally use <a href="https://github.com/ryantm/agenix">agenix</a> for managing secrets in my NixOS configuration.</p>
<p>As per the <a href="https://go-acme.github.io/lego/dns/ovh/">OVH lego provider documentation</a>, this file will include something like this:</p>





<pre tabindex="0"><code class="language-nil" data-lang="nil">OVH_ENDPOINT=ovh-eu
OVH_APPLICATION_KEY=&lt;application-key&gt;
OVH_APPLICATION_SECRET=&lt;application-secret&gt;
OVH_CONSUMER_KEY=&lt;consumer-key&gt;</code></pre><h2 id="dns-zone-configuration">DNS zone configuration</h2>
<p>Initially, I tried to set up DNS like this:</p>
<ul>
<li>Create a wildcard CNAME record so that <code>*.example.com</code> points to <code>home-server.tailnet.example.com</code>.</li>
</ul>
<p>However, with this setup, the acme service failed to fetch the certificate.</p>
<p>I ended up setting DNS this way instead:</p>
<ul>
<li>Create a wildcard A record so that <code>*.example.com</code> points to <code>100.64.0.1</code>, my home-server&rsquo;s Tailscale IP.</li>
</ul>
<p>Now everything worked perfectly, although I feel that the CNAME method was more elegant, as it didn&rsquo;t depend on the IP Tailscale assigned to my machine but only on its hostname and headscale&rsquo;s <a href="https://tailscale.com/kb/1081/magicdns/">MagicDNS</a>.</p>
<h2 id="using-the-certificate">Using the certificate</h2>
<p>Now, after running <code>nixos-rebuild switch</code> and making sure that no errors showed up while getting the certificate, all that&rsquo;s left is to start using it.</p>
<p>A simple nginx configuration that uses this certificate would like the following:</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-nix" data-lang="nix"><span style="display:flex;"><span>{ config<span style="color:#f92672">,</span> lib<span style="color:#f92672">,</span> pkgs<span style="color:#f92672">,</span> <span style="color:#f92672">...</span> }:
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  services<span style="color:#f92672">.</span>nginx <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    enable <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    virtualHosts <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#34;some-service.example.com&#34;</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>        forceSSL <span style="color:#f92672">=</span> <span style="color:#66d9ef">true</span>;
</span></span><span style="display:flex;"><span>        useACMEHost <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;example.com&#34;</span>;
</span></span><span style="display:flex;"><span>        locations<span style="color:#f92672">.</span><span style="color:#e6db74">&#34;/&#34;</span><span style="color:#f92672">.</span>proxyPass <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;http://127.0.0.1:12345&#34;</span>;
</span></span><span style="display:flex;"><span>      };
</span></span><span style="display:flex;"><span>    };
</span></span><span style="display:flex;"><span>  };
</span></span><span style="display:flex;"><span>}</span></span></code></pre></div><h2 id="closing-remarks">Closing remarks</h2>
<p>Once again, NixOS made this process really straightforward.</p>
<p>I have read that Caddy makes this workflow really easy as well, if I weren&rsquo;t using NixOS I&rsquo;d probably be using that instead.</p>
<p>This setup is now ready to provide self-hosted services only accessible inside the headscale mesh-VPN and without the headache of using self-signed certificates or skipping the &ldquo;Insecure Connection&rdquo; warnings all the time.
It also didn&rsquo;t require port-forwarding on my router and having my services available to the hostile and chaotic public Internet.</p>
<p>Another subtle consequence of using wildcard certificates is that there&rsquo;s improved privacy in what concerns <a href="https://en.wikipedia.org/wiki/Certificate_Transparency">certificate transparency</a>.
When someone searches for your domain at a service such as <a href="https://crt.sh/">crt.sh</a>, all they&rsquo;ll see is that a wildcard certificate was emitted for that domain.
This way, you&rsquo;ll avoid having a public list of all the private services you&rsquo;ve set up at home.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://nixos.org/manual/nixos/stable/index.html#module-security-acme-config-dns">https://nixos.org/manual/nixos/stable/index.html#module-security-acme-config-dns</a></li>
<li><a href="https://discourse.nixos.org/t/setup-a-wildcard-certificate-with-acme-on-a-custom-domain-name-hosted-by-powerdns/15055">https://discourse.nixos.org/t/setup-a-wildcard-certificate-with-acme-on-a-custom-domain-name-hosted-by-powerdns/15055</a></li>
<li><a href="https://go-acme.github.io/lego/dns/ovh/">https://go-acme.github.io/lego/dns/ovh/</a></li>
<li><a href="https://yunohost.org/en/providers/registrar/ovh/autodns">https://yunohost.org/en/providers/registrar/ovh/autodns</a></li>
</ul>
]]></content:encoded>
    </item>
  </channel>
</rss>
