<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Certificates on Deevnet IoTaaS</title><link>https://deevnet.github.io/deevnet-docs/docs/runbook/substrate/certificates/</link><description>Recent content in Certificates on Deevnet IoTaaS</description><generator>Hugo</generator><language>en-us</language><atom:link href="https://deevnet.github.io/deevnet-docs/docs/runbook/substrate/certificates/index.xml" rel="self" type="application/rss+xml"/><item><title>Renewing</title><link>https://deevnet.github.io/deevnet-docs/docs/runbook/substrate/certificates/renewing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deevnet.github.io/deevnet-docs/docs/runbook/substrate/certificates/renewing/</guid><description>&lt;h1 id="renewing">
 Renewing
 &lt;a class="anchor" href="#renewing">#&lt;/a>
&lt;/h1>
&lt;p>&lt;code>certs.yml&lt;/code> in &lt;code>ansible-collection-deevnet.mgmt&lt;/code> lays down every certificate and trust anchor on the
site, and is how certificates are renewed: re-run it. Nothing renews a certificate on a timer, so a
certificate on a host nobody re-applies will expire (risk register R-12).&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>cd ansible-collection-deevnet.mgmt
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ansible-playbook playbooks/certs.yml &lt;span style="color:#75715e"># everything&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ansible-playbook playbooks/certs.yml --tags omada &lt;span style="color:#75715e"># one play: trust, hypervisors, router, openbao, omada&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ansible-playbook playbooks/certs.yml --limit dv02obs001v01
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>It needs the inventory&amp;rsquo;s vaults decrypted, and &lt;code>deevnet.builder&lt;/code> and &lt;code>deevnet.net&lt;/code> published
(&lt;code>make publish&lt;/code> in each), because the trust, hypervisor and router plays are theirs.&lt;/p></description></item><item><title>Proxmox</title><link>https://deevnet.github.io/deevnet-docs/docs/runbook/substrate/certificates/proxmox/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deevnet.github.io/deevnet-docs/docs/runbook/substrate/certificates/proxmox/</guid><description>&lt;h1 id="proxmox">
 Proxmox
 &lt;a class="anchor" href="#proxmox">#&lt;/a>
&lt;/h1>
&lt;p>Each hypervisor&amp;rsquo;s web UI and API (&lt;code>:8006&lt;/code>) serve a certificate signed by the Substrate CA on the
control node, installed by &lt;code>deevnet.builder.proxmox_node_cert&lt;/code> with Proxmox&amp;rsquo;s own command:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>pvenode cert set &amp;lt;chain.pem&amp;gt; &amp;lt;key.pem&amp;gt; --force &lt;span style="color:#ae81ff">1&lt;/span> --restart &lt;span style="color:#ae81ff">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>pvenode(1)&lt;/code> writes &lt;code>/etc/pve/local/pveproxy-ssl.pem&lt;/code> and &lt;code>.key&lt;/code>. It refuses a key that does not
match, restoring the previous files, and never touches the node&amp;rsquo;s own &lt;code>pve-ssl.pem&lt;/code>, which pveproxy
falls back to when no custom certificate is present. &lt;code>--restart 1&lt;/code> restarts pveproxy, which serves
the new certificate only after a restart. The node fingerprint changes with the certificate; nothing
on the site pins it, and the nodes are not clustered.&lt;/p></description></item><item><title>Core Router</title><link>https://deevnet.github.io/deevnet-docs/docs/runbook/substrate/certificates/core-router/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deevnet.github.io/deevnet-docs/docs/runbook/substrate/certificates/core-router/</guid><description>&lt;h1 id="core-router">
 Core Router
 &lt;a class="anchor" href="#core-router">#&lt;/a>
&lt;/h1>
&lt;p>The core router&amp;rsquo;s web GUI and API (&lt;code>:443&lt;/code>, one server) serve a certificate signed by the Substrate
CA. Ansible signs it on the control node and imports it, with the Deevnet Root CA, the Site CA and
the Substrate CA, over the documented Trust API (&lt;code>deevnet.net.opnsense_cert&lt;/code>). &lt;strong>OPNsense has
no API for which certificate the web GUI serves&lt;/strong>, so choosing it is a manual step, made once.&lt;/p></description></item><item><title>Omada Controller</title><link>https://deevnet.github.io/deevnet-docs/docs/runbook/substrate/certificates/omada-controller/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deevnet.github.io/deevnet-docs/docs/runbook/substrate/certificates/omada-controller/</guid><description>&lt;h1 id="omada-controller">
 Omada Controller
 &lt;a class="anchor" href="#omada-controller">#&lt;/a>
&lt;/h1>
&lt;p>The controller&amp;rsquo;s web UI and Open API (&lt;code>:8043&lt;/code>) and its portal (&lt;code>:8843&lt;/code>) serve a certificate from
the Substrate CA, through the container image&amp;rsquo;s own mechanism. Its entrypoint builds the
keystore from two files in &lt;code>/cert&lt;/code> on every start (&lt;code>mbentley/omada-controller&lt;/code> README):&lt;/p>
&lt;ul>
&lt;li>&lt;code>/cert/tls.crt&lt;/code>: &lt;strong>the full chain, root included&lt;/strong>;&lt;/li>
&lt;li>&lt;code>/cert/tls.key&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>The &lt;code>omada_controller&lt;/code> role writes both into &lt;code>/opt/omada-controller/cert&lt;/code> with &lt;code>site_cert&lt;/code>, mounts
the directory at &lt;code>/cert&lt;/code>, and restarts the controller when they change. It is on where
&lt;code>omada_tls_enabled: true&lt;/code> (&lt;code>group_vars/network_controllers&lt;/code>).&lt;/p></description></item><item><title>Trusting the Root</title><link>https://deevnet.github.io/deevnet-docs/docs/runbook/substrate/certificates/trusting-the-root/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deevnet.github.io/deevnet-docs/docs/runbook/substrate/certificates/trusting-the-root/</guid><description>&lt;h1 id="trusting-the-root">
 Trusting the Root
 &lt;a class="anchor" href="#trusting-the-root">#&lt;/a>
&lt;/h1>
&lt;p>Everything the site serves over TLS by name chains to one root, the Deevnet Root CA,
&lt;code>deevnet-root-ca.pem&lt;/code>. Trust it once on your computer and browsers and command-line tools verify the
Deevnet API, the platform services, Proxmox, the core router and the Omada controller without flags
or warnings.&lt;/p>
&lt;p>Check its SHA-256 fingerprint before you trust it:
&lt;code>F6:8A:BD:B3:1E:A5:6D:0A:88:1F:31:28:56:8A:4C:14:B0:3A:3F:5C:3F:38:CC:F1:7C:C4:F0:09:8B:EB:94:52&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>curl -fsSLk -O https://downloads.mobile.deevnet.net/deevnet-root-ca.pem
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>openssl x509 -in deevnet-root-ca.pem -noout -fingerprint -sha256
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The first fetch skips verification (&lt;code>-k&lt;/code>) because you do not trust the root yet. The fingerprint
check is what makes it safe.&lt;/p></description></item><item><title>Rotating</title><link>https://deevnet.github.io/deevnet-docs/docs/runbook/substrate/certificates/rotating/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deevnet.github.io/deevnet-docs/docs/runbook/substrate/certificates/rotating/</guid><description>&lt;h1 id="rotating">
 Rotating
 &lt;a class="anchor" href="#rotating">#&lt;/a>
&lt;/h1>
&lt;p>Certificates are renewed by 
 &lt;a href="https://deevnet.github.io/deevnet-docs/deevnet-docs/docs/runbook/substrate/certificates/renewing/">Renewing&lt;/a>. The CAs
above them change only deliberately, in an offline ceremony, about a year before each expires
(
 &lt;a href="https://deevnet.github.io/deevnet-docs/deevnet-docs/docs/runbook/root-of-trust/custody/">Custody&lt;/a> has the calendar and what each rotation reaches).&lt;/p>
&lt;h2 id="rotate-the-substrate-ca-with-a-new-key-and-an-issuing-ca-ceremony">
 Rotate the Substrate CA with a new key and an Issuing CA ceremony
 &lt;a class="anchor" href="#rotate-the-substrate-ca-with-a-new-key-and-an-issuing-ca-ceremony">#&lt;/a>
&lt;/h2>
&lt;p>Valid to 2031-10-04. Remove the &lt;code>ADR-0031 Substrate CA key&lt;/code> block from the vault and the old request,
then:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>cd ansible-collection-deevnet.mgmt
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ansible-playbook playbooks/substrate-ca.yml &lt;span style="color:#75715e"># a new key into the vault, its request to pki/mobile/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cd ../ansible-inventory-deevnet &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> make vault &lt;span style="color:#75715e"># then commit and push: the key exists nowhere else&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Take the request through the 
 &lt;a href="https://deevnet.github.io/deevnet-docs/deevnet-docs/docs/runbook/root-of-trust/issuing-ca/">Issuing CA&lt;/a> ceremony, accept
the certificate into &lt;code>pki/mobile/&lt;/code>, then &lt;code>certs.yml&lt;/code>. Certificates under the old Substrate CA keep
chaining to the root, so clients trust both until each is reissued; ordinary renewal moves them all
within the year.&lt;/p></description></item><item><title>Troubleshooting</title><link>https://deevnet.github.io/deevnet-docs/docs/runbook/substrate/certificates/troubleshooting/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deevnet.github.io/deevnet-docs/docs/runbook/substrate/certificates/troubleshooting/</guid><description>&lt;h1 id="troubleshooting">
 Troubleshooting
 &lt;a class="anchor" href="#troubleshooting">#&lt;/a>
&lt;/h1>
&lt;p>Set &lt;code>R=/srv/dvnt/ansible-inventory-deevnet/pki/deevnet-root-ca.pem&lt;/code> first.&lt;/p>
&lt;h2 id="check-the-certificate-is-the-sites-and-chains-to-the-root">
 Check the certificate is the site&amp;rsquo;s and chains to the root
 &lt;a class="anchor" href="#check-the-certificate-is-the-sites-and-chains-to-the-root">#&lt;/a>
&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>openssl s_client -connect &amp;lt;host&amp;gt;:&amp;lt;port&amp;gt; -servername &amp;lt;name&amp;gt; -showcerts -CAfile $R -no-CApath -no-CAstore &amp;lt;/dev/null 2&amp;gt;&amp;amp;&lt;span style="color:#ae81ff">1&lt;/span> &lt;span style="color:#ae81ff">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ae81ff">&lt;/span> | grep -E &lt;span style="color:#e6db74">&amp;#39;^ *[0-9] s:|Verify return code&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;strong>The leaf alone, or the leaf and one CA:&lt;/strong> the service is not sending the whole chain (leaf,
Substrate CA, Site CA). Clients that hold only the root cannot build it.&lt;/li>
&lt;li>&lt;strong>&lt;code>unable to get local issuer certificate&lt;/code>:&lt;/strong> a certificate from something other than the Deevnet
Root CA. It is either the device&amp;rsquo;s own, or one issued before a re-root. Re-run its role or
&lt;code>certs.yml&lt;/code>; &lt;code>site_cert&lt;/code> and &lt;code>proxmox_node_cert&lt;/code> reissue anything that does not chain.&lt;/li>
&lt;li>&lt;strong>&lt;code>Verify return code: 0&lt;/code> but the client still refuses:&lt;/strong> check the name. Add
&lt;code>-verify_hostname &amp;lt;name&amp;gt;&lt;/code>, or &lt;code>-verify_ip &amp;lt;address&amp;gt;&lt;/code>, for exactly what the client dials. Every name
and address comes from the inventory (&lt;code>site_cert_dns_names&lt;/code>, &lt;code>site_cert_ip_sans&lt;/code>); a CNAME missing
from &lt;code>env.interfaces.&amp;lt;if&amp;gt;.dns.cnames&lt;/code> is a name the certificate does not carry.&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>openssl x509 -noout -ext subjectAltName &lt;span style="color:#f92672">&amp;lt;&amp;lt;&amp;lt;&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#66d9ef">$(&lt;/span>openssl s_client -connect &amp;lt;host&amp;gt;:&amp;lt;port&amp;gt; &amp;lt;/dev/null 2&amp;gt;/dev/null&lt;span style="color:#66d9ef">)&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="a-check-passes-that-should-fail-openssl-also-read-the-system-store">
 A check passes that should fail: OpenSSL also read the system store
 &lt;a class="anchor" href="#a-check-passes-that-should-fail-openssl-also-read-the-system-store">#&lt;/a>
&lt;/h2>
&lt;p>&lt;code>openssl verify -CAfile &amp;lt;root&amp;gt;&lt;/code> and &lt;code>openssl s_client -CAfile &amp;lt;root&amp;gt;&lt;/code> also load the host&amp;rsquo;s own trust
store unless told not to. A certificate under any root that store trusts, a public CA or an outgoing
Deevnet root, then verifies. Add &lt;code>-no-CApath -no-CAstore&lt;/code> whenever the question is &amp;ldquo;does this chain
to &lt;em>this&lt;/em> root&amp;rdquo;; every check in the roles and the ceremony tools does since CHG-0033. &lt;code>curl --cacert&lt;/code>
and Python&amp;rsquo;s &lt;code>cafile&lt;/code> use only the file given.&lt;/p></description></item></channel></rss>