<?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>Caddy on rooiratel</title><link>https://rooiratel.red/tags/caddy/</link><description>Stephan Snyman's personal website</description><language>en-gb</language><generator>Hugo</generator><atom:link href="https://rooiratel.red/tags/caddy/index.xml" rel="self" type="application/rss+xml"/><item><title>When being root is not enough</title><link>https://rooiratel.red/posts/when-being-root-is-not-enough/</link><pubDate>Fri, 25 Nov 2022 15:19:31 +0200</pubDate><guid isPermaLink="true">https://rooiratel.red/posts/when-being-root-is-not-enough/</guid><description>The other day, when I logged into my server, I realised that I hadn’t upgraded it in a while. Fedora 37 had just been released, so it was a good time for an upgrade. The process went smoothly, but I noticed after rebooting that the Caddy service failed to start. At first I thought that maybe something in the config was no longer compatible with the new version of Caddy. Everything checked out fine. When I manually executed the command in the caddy.service file then the service would start up without any issues.</description><content:encoded><![CDATA[<p>The other day, when I logged into my server, I realised that I hadn&rsquo;t upgraded it in a while. Fedora 37 had just been released, so it was a good time for an upgrade. The process went smoothly, but I noticed after rebooting that the <a href="https://caddyserver.com/">Caddy</a> service failed to start. At first I thought that maybe something in the config was no longer compatible with the new version of Caddy. Everything checked out fine. When I manually executed the command in the caddy.service file then the service would start up without any issues.</p>
<p>That was strange. The Caddyfile is owned by root, the caddy.service file is owned by root, and the service is executed and run as root. So it wasn&rsquo;t a permission issue.</p>
<p>Let&rsquo;s see what systemd has to say for itself.</p>
<p>I ran the command <code>journalctl -u caddy --no-pager | less</code>, and that is when I saw this:</p>
<p><code>caddy[736]: run: reading config file: open /the/path/to/Caddyfile: permission denied</code></p>
<p>So it was a permission issue after all. But how? Is being root not enough?</p>
<p>I furiously started searching the web and eventually stumbled upon <a href="https://stackoverflow.com/questions/51209967/systemd-service-file-failed-to-execute-command-permission-denied">this StackOverflow post</a>, which suggested that SELinux could cause this type of behaviour.</p>
<p>I temporarily disabled SELinux with <code>sudo setenforce Permissive</code> to see if it would fix my issue ‒ and it did. Cool, but why would SELinux cause the issue in the first place? What didn&rsquo;t it like about my caddy.service file?</p>
<p>To find out, I started with the <code>semanage fcontext --list</code> command. This is when I realised that I was storing my Caddyfile in a directory that SELinux didn&rsquo;t approve of. So my final solution was just to move it to a directory that SELinux was happy with. The last thing I needed to do was update the caddy.service file to point to the new location of the Caddyfile and restart the service.</p>
]]></content:encoded></item><item><title>One Very Simple Way to Host a Website</title><link>https://rooiratel.red/posts/one-very-simple-way-to-host-a-website/</link><pubDate>Wed, 21 Apr 2021 18:33:46 +0200</pubDate><guid isPermaLink="true">https://rooiratel.red/posts/one-very-simple-way-to-host-a-website/</guid><description>I generally enjoy tinkering, and I like to optimise things as much as I can. However, when it came to hosting this website, I found that this habit was actually holding me back. I spent forever deciding which tech stack to use and tried various combinations of things, but I never actually got around to publishing anything. Finally, I decided to just pick something that was simple but that still met all my needs.</description><content:encoded><![CDATA[<p>I generally enjoy tinkering, and I like to optimise things as much as I can. However, when it came to hosting this website, I found that this habit was actually holding me back. I spent forever deciding which tech stack to use and tried various combinations of things, but I never actually got around to publishing anything. Finally, I decided to just pick something that was simple but that still met all my needs.</p>
<h2 id="step-1--static-site-generator">Step 1 : Static Site Generator</h2>
<p>In the beginning, I was debating whether I should build a dynamic or static site. Since this is just a basic website that is mainly comprised of my blog, I realised that there would be no reason for me to have a dynamic site.</p>
<p>Okay &mdash; so there are many static site generators out there. I quickly settled on <a href="https://gohugo.io">Hugo</a>. I didn&rsquo;t benchmark it or anything, but I liked it&rsquo;s simplicity, syntax and speed. Moreover, I especially liked that it has a built-in mini-webserver that you can use to view a draft version of your website on your local machine. I converted the few posts I&rsquo;d written in markdown to a basic site and liked what it looked like.</p>
<h2 id="step-2--hosting-provider">Step 2 : Hosting Provider</h2>
<p>I don&rsquo;t really mind where my website is hosted. The important thing is that the provider is reliable, reasonably priced, and has an easy-to-use interface. I ended up using <a href="https://www.digitalocean.com/">DigitalOcean</a> &mdash; I was familiar with them from small projects in the past, and they offered really good prices for what I needed. Finally, they also met my deal-breaker requirement: supporting IPv6. See my other <a href="/posts/non-tech-reasons-for-ipv6/">post</a> about that.</p>
<h2 id="step-3--domain-name-registrar">Step 3 : Domain Name Registrar</h2>
<p>Nothing special here. I don&rsquo;t think one is particularly better than the other. I ended up using <a href="https://porkbun.com">Porkbun</a>, since they had the best prices for the <code>.tld</code> I wanted to use.</p>
<h2 id="step-4--web-server">Step 4 : Web Server</h2>
<p>I know Apache really well, and I can get around with nginx. But I&rsquo;m so glad I stumbled upon <a href="https://caddyserver.com/">Caddy</a>. It may not scale as well as something like nginx, but it is perfect for a small website. And it has two killer features:</p>
<ol>
<li>It has a <em>very</em> simple config file</li>
<li>It automatically obtains/renews ssl certs for you</li>
</ol>
<p>The first one is great and already had me convinced. However, the second one is just crazy! I am quite capable of setting up a bot to get certs from Let&rsquo;s Encrypt and later renew them. But not having to bother with that and simply letting my webserver do it for me feels futuristic, to say the least.</p>
<h2 id="step-5--web-log-analyser">Step 5 : Web Log Analyser</h2>
<p>I was strongly considering <a href="https://matomo.org/">Matomo</a>, as it&rsquo;s a nice open-source alternative to Google Analytics. Before settling though, I decided to look around and see what other options there were. I quite liked <a href="https://www.goatcounter.com/">GoatCounter</a> as well; but in the end, I settled on <a href="https://goaccess.io/">GoAccess</a> for two simple reasons: I can install it from a regular Linux package manager and didn&rsquo;t need to set up a database.</p>
<h2 id="step-6--putting-it-all-together">Step 6 : Putting It All Together</h2>
<h3 id="nameservers">Nameservers</h3>
<p>After buying your domain name, point the authoritative nameservers on your domain name registrar&rsquo;s website to DigitalOcean&rsquo;s nameservers: <code>ns1.digitalocean.com</code>, <code>ns2.digitalocean.com</code>, and <code>ns3.digitalocean.com</code>.</p>
<h3 id="create-a-digitalocean-droplet">Create a DigitalOcean droplet</h3>
<p>I selected the following options:</p>
<ul>
<li>Latest Fedora Linux distro</li>
<li>Basic plan</li>
<li>Regular Intel CPU with SSD</li>
<li>$5/month option</li>
<li>Datacenter location doesn&rsquo;t matter. Probably choose the one that&rsquo;s geographically located closest to you or your intended audience.</li>
<li>I ticked the IPv6 and Monitoring options (Both are free)</li>
<li>I selected ssh login instead of password login.</li>
</ul>
<p>Finally, I added my ssh key and assigned the droplet to my default project.</p>
<h3 id="dns-records">DNS Records</h3>
<p>Once the droplet was created, I went to the &ldquo;Domains&rdquo; setting for it and added the A, AAAA and NS records. See below:
<picture>
  
  

  

  
    

    <source srcset="/images/dns_records.avif" type="image/avif" >

    
    

    

  

  <figure>
    <img
      src="/images/dns_records.png"
      alt="DNS Records"
      loading="lazy"
      decoding="async"
      width="1212"
      height="866"
    />
	<figcaption></figcaption>
  </figure>
</picture>
</p>
<h3 id="server-setup">Server setup</h3>
<p>ssh into your server and do a quick <code>dnf update</code> to get the system up to date. Then install the packages below:</p>
<ul>
<li><code>goacess</code></li>
<li><code>caddy</code></li>
</ul>
<h4 id="caddy-setup">Caddy setup</h4>
<p>Create a Caddy file. You can store it anywhere, but typically it is located at <code>/etc/caddy/Caddyfile</code></p>
<p>Mine looks something like this:</p>
<pre tabindex="0"><code>www.example.com {
        redir https://example.com{uri}
}

example.com {
        root * /var/www/html
        encode zstd gzip
        file_server
        # 404 path below must be relative to the root dir difined below
        handle_errors {
            @404 {
                expression {http.error.status_code} == 404
            }
            handle @404 {
                rewrite * 404.html
                file_server
            }
        }

        header {
                Strict-Transport-Security &#34;max-age=31536000; includeSubDomains; preload&#34;
                X-Xss-Protection &#34;1; mode=block&#34;
                X-Content-Type-Options &#34;nosniff&#34;
                X-Frame-Options &#34;DENY&#34;
                Content-Security-Policy &#34;upgrade-insecure-requests&#34;
                Referrer-Policy &#34;strict-origin-when-cross-origin&#34;
                Cache-Control &#34;public, max-age=31557600, must-revalidate&#34;
        }

        log {
                output file /var/log/caddy_access.log {
                        # roll_disabled
                        roll_size     5000MiB
                        # roll_keep     &lt;num&gt;
                        # roll_keep_for &lt;days&gt;
                }
        }
}
</code></pre><p>The first section redirects the pointless www subdomain to the correct URL. The second section points to the root directory where your website content will be stored and enables compression. The handle_errors subsection redirects you to the 404.html page if there is an HTTP 404 response. In the header subsection, I enabled some security headers. Finally, in the log subsection, I told Caddy where to store its log file, and I set the log rotation settings.</p>
<p>Now, create a systemd service for running Caddy and enable it. Save it at <code>/etc/systemd/system/caddy.service</code>
Mine looks something like this:</p>
<pre tabindex="0"><code>[Unit]
Description=Caddy web server
Documentation=https://caddyserver.com/docs
After=network.target

[Service]
User=root
LimitNOFILE=8192
Environment=STNORESTART=yes
ExecStart=/usr/bin/caddy run -config /etc/caddy/Caddyfile
Restart=on-failure

[Install]
WantedBy=multi-user.target
</code></pre><h4 id="goaccess-setup">GoAccess setup</h4>
<p>Run the following command to generate a report from your Caddy logs : <code>goaccess /var/log/caddy_access.log -o /var/www/html/stats.html --log-format=CADDY --real-time-html --daemonize</code></p>
<p>If you browse to <code>https://example.com/stats.html</code>, you can read the nicely formatted report that GoAccess generated, detailing things like most visited pages, number of visitors, geolocation of visitors, TLS version etc.</p>
<p>You can also tweak these settings (log files, output file, log format etc. and much more) in the <code>goaccess.conf</code> file that is normally located at <code>/etc/goaccess/goacces.conf</code>, but I&rsquo;m happy with the default settings.</p>
<h3 id="actually-put-your-content-online">Actually put your content online</h3>
<p>Once you are happy with how your website is looking on your local machine (using Hugo to preview it), execute the <code>hugo</code> command, and it will generate the final HTML output in the <code>public</code> subdirectory.</p>
<p>Now, you can <code>rsync</code> the output onto your server. And just like that, your website is live!</p>
<p><code>rsync --progress -avzP -e &quot;ssh -i ~/.ssh/your_ssh_key&quot; /path/to/hugo/root/dir/public/ root@example.com:/var/www/html</code></p>
<h2 id="conclusion">Conclusion</h2>
<p>It was a long explanation, but I got this all up and running in, like, 20 minutes. It&rsquo;s really fast and simple. And I&rsquo;ll probably be using this setup for a long time thanks to the very low maintenance requirements.</p>
]]></content:encoded></item></channel></rss>