<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[bytes fyi]]></title><description><![CDATA[fun with tech, photos and code]]></description><link>https://bytes.fyi/</link><image><url>https://bytes.fyi/favicon.png</url><title>bytes fyi</title><link>https://bytes.fyi/</link></image><generator>Ghost 3.42</generator><lastBuildDate>Sat, 18 Apr 2026 03:41:25 GMT</lastBuildDate><atom:link href="https://bytes.fyi/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[The Dark Side]]></title><description><![CDATA[I've given my blog an overhaul, upgrading to Ghost v3 on a more powerful server]]></description><link>https://bytes.fyi/the-dark-side/</link><guid isPermaLink="false">5ed8f46176803413812a7eeb</guid><category><![CDATA[ghost]]></category><dc:creator><![CDATA[Matt Holdsworth]]></dc:creator><pubDate>Fri, 05 Jun 2020 16:39:04 GMT</pubDate><media:content url="https://cdn.bytes.fyi/content/images/2020/06/smartmockups_kb2frv3e.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://cdn.bytes.fyi/content/images/2020/06/smartmockups_kb2frv3e.jpg" alt="The Dark Side"><p>I've been meaning to upgrade to <a href="https://ghost.org/3/">Ghost v3</a> for months, but procrastinated over updating my theme to make it compatible.</p><p>I wanted some of the nice touches of the v3 flavour of <a href="https://github.com/TryGhost/Casper">Casper</a>, the rather gorgeous theme that is enabled by default for new Ghost installs. </p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://cdn.bytes.fyi/content/images/2020/06/bytes.fyi_real-time-goaccess-reports-with-nginx_--1--min.png" width="2400" height="1800" srcset="https://cdn.bytes.fyi/content/images/size/w600/2020/06/bytes.fyi_real-time-goaccess-reports-with-nginx_--1--min.png 600w, https://cdn.bytes.fyi/content/images/size/w1000/2020/06/bytes.fyi_real-time-goaccess-reports-with-nginx_--1--min.png 1000w, https://cdn.bytes.fyi/content/images/size/w1600/2020/06/bytes.fyi_real-time-goaccess-reports-with-nginx_--1--min.png 1600w, https://cdn.bytes.fyi/content/images/size/w2400/2020/06/bytes.fyi_real-time-goaccess-reports-with-nginx_--1--min.png 2400w" alt="The Dark Side"></div><div class="kg-gallery-image"><img src="https://cdn.bytes.fyi/content/images/2020/06/bytes.fyi_real-time-goaccess-reports-with-nginx_-min-1.png" width="2400" height="1800" srcset="https://cdn.bytes.fyi/content/images/size/w600/2020/06/bytes.fyi_real-time-goaccess-reports-with-nginx_-min-1.png 600w, https://cdn.bytes.fyi/content/images/size/w1000/2020/06/bytes.fyi_real-time-goaccess-reports-with-nginx_-min-1.png 1000w, https://cdn.bytes.fyi/content/images/size/w1600/2020/06/bytes.fyi_real-time-goaccess-reports-with-nginx_-min-1.png 1600w, https://cdn.bytes.fyi/content/images/size/w2400/2020/06/bytes.fyi_real-time-goaccess-reports-with-nginx_-min-1.png 2400w" alt="The Dark Side"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://cdn.bytes.fyi/content/images/2020/06/smartmockups_kb2ft5lu-1.jpg" width="2500" height="1825" srcset="https://cdn.bytes.fyi/content/images/size/w600/2020/06/smartmockups_kb2ft5lu-1.jpg 600w, https://cdn.bytes.fyi/content/images/size/w1000/2020/06/smartmockups_kb2ft5lu-1.jpg 1000w, https://cdn.bytes.fyi/content/images/size/w1600/2020/06/smartmockups_kb2ft5lu-1.jpg 1600w, https://cdn.bytes.fyi/content/images/size/w2400/2020/06/smartmockups_kb2ft5lu-1.jpg 2400w" alt="The Dark Side"></div><div class="kg-gallery-image"><img src="https://cdn.bytes.fyi/content/images/2020/06/smartmockups_kb2frv3e-2.jpg" width="2500" height="1825" srcset="https://cdn.bytes.fyi/content/images/size/w600/2020/06/smartmockups_kb2frv3e-2.jpg 600w, https://cdn.bytes.fyi/content/images/size/w1000/2020/06/smartmockups_kb2frv3e-2.jpg 1000w, https://cdn.bytes.fyi/content/images/size/w1600/2020/06/smartmockups_kb2frv3e-2.jpg 1600w, https://cdn.bytes.fyi/content/images/size/w2400/2020/06/smartmockups_kb2frv3e-2.jpg 2400w" alt="The Dark Side"></div></div></div><figcaption>Dark Mode via CSS (honouring the OS settings on supported browsers) is a feature of the latest Casper theme)</figcaption></figure><p>Given that my theme was already a customised Casper v2.11.1, I opted to re-add my modifications to a fresh copy of Casper v3.0.12. I then used the <a href="https://gscan.ghost.org/">Gscan</a> tool to validate my new theme as best I could.</p><p>The upgrade was initially deemed successful, but I soon discovered the automatic resizing to support responsive images was not working properly, when my first article after the upgrade weighed a hefty 5MB!  When a request for a resized image was made, Ghost was responding with a HTTP 302 direct to the original image - in all its glory - with terrible consequences for bandwidth and performance. </p><p>By coincidence, I'd already been mulling-over moving my blog from the rather crusty Raspberry Pi 2B, where it had been originally built, to a fresh VM on my (amd64) ESXI box.  <br>As much as I love Pis, it's been a bit of a struggle to update Ghost recently... I actually had three Ghost instances running on that poor little Pi:</p><ul><li>bytes.fyi (this blog)</li><li>dev.bytes.fyi (my sandbox for trying out stuff; only accessible locally)</li><li>annholdsworth.co.uk (my wife's blog-to-be, which I've been diligently updating - and paying for the domain - for years and years now; she's never used it)</li></ul><p>Using a new VM, I could allocate 2GB RAM, which would really help with running - and updating - three instances on the same box. Also, it would be a novelty to be running the supported stack for Ghost for the first time, err, ever...</p><p>So, with a slightly heavy heart and due sense of trepidation, I moved my blog(s) over to a new home.</p><p>The steps are listed below as an <em>aide-mémoire</em> for myself.</p><hr><ol><li>Export articles as Json (Settings &gt; Labs) from Ghost</li><li>Backup the <code>/var/www/ghost/content/images</code> folder using <code>tar</code></li><li>Make a note of the existing settings (especially re: <a href="https://ghost.org/docs/concepts/config/#mail">Mailgun config</a>) from the <code>config.production.json</code> file</li><li>Backup my <a href="https://posativ.org/isso/">Isso comments</a> config and (sqlite) DBs using <code>tar</code></li><li>Backup my nginx config with <code>tar</code></li><li>Spin up a new Ubuntu 18.04 LTS VM and apply all updates</li><li>The usual hardening: firewall, key-based auth (only) for SSH etc</li><li><a href="https://bytes.fyi/build-nginx-openssl-from-source/">Install the latest and greatest Nginx, building from source</a></li><li>Install MySQL, then do <a href="https://ghost.org/docs/install/ubuntu/#mysql-on-ubuntu-1804">MySQL setup steps for Ghost</a></li><li>Create a new user with no privileges to (install and) run Isso</li><li>Install Isso comments</li><li>Create services for comments.bytes.fyi and comments-dev.bytes.fyi, start and enable them</li><li><a href="https://ghost.org/docs/install/ubuntu/#install-nodejs">Install Node.js 12 LTS via the recommended method</a></li><li>Configure <code>npm</code> to enable sudo-less global package installs</li><li>Update <code>npm</code> </li><li>Install latest <code>ghost-cli</code> globally</li><li>Create folders for each site and apply correct permissions</li><li>Install Ghost using <code>ghost install --no-setup-ssl</code> (because I manage my <a href="https://letsencrypt.org/">Let's Encrypt</a> certificate automation separately, on my TLS termination proxy), but don't start Ghost yet</li><li>Repeat the previous step for each of the three Ghost instances, making sure that the port numbers are the same as those used on the Pi</li><li>Use <code>scp</code> to copy over the backup config files</li><li>Restore the backup config, then amend where needed (e.g. host IP in nginx config)</li><li>Temporarily disable WAN access to my Ghost domains, for the initial setup of admin accounts</li><li>Configure my reverse proxy to point 'dev' and 'ann' domains (and comments subdomains) at the new VM's IP</li><li>Configure my <a href="https://www.ngxpagespeed.com/">ngx_pagespeed</a> proxy to point to the new VM's IP</li><li>Adjust domain mapping in my ngx_pagespeed nginx config, so origin images are grabbed from new VM's IP</li><li>Fire up Ghost using <code>ghost start</code>, for each of the three instances</li><li>Complete initial setup of admin users for each of the three blogs</li><li>Import content from backup Json file, for each blog</li><li>For bytes.fyi (and dev.bytes.fyi), I use <a href="https://github.com/jamalneufeld/ghostHunter">ghostHunter search</a>, so create a new Custom Integration called <code>ghostHunter</code></li><li>Update the JavaScript in my custom theme to use the Content Key from the new integration, then apply the updated theme</li><li>Check everything works as expected: creating/amending/publishing articles, importing images, comments, search, etc...</li><li>Restore public access to the blog domains</li></ol>]]></content:encoded></item><item><title><![CDATA[OpenSSL Cheat Sheet]]></title><description><![CDATA[Some of the most useful OpenSSL commands]]></description><link>https://bytes.fyi/openssl-cheat-sheet/</link><guid isPermaLink="false">5ed800f676803413812a7ea2</guid><category><![CDATA[openssl]]></category><dc:creator><![CDATA[Matt Holdsworth]]></dc:creator><pubDate>Tue, 02 Jun 2020 16:02:46 GMT</pubDate><media:content url="https://cdn.bytes.fyi/content/images/2020/06/kYlYwQze5vI-1.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://cdn.bytes.fyi/content/images/2020/06/kYlYwQze5vI-1.jpg" alt="OpenSSL Cheat Sheet"><p>The popular <a href="https://openssl.org">OpenSSL</a> toolkit is the Swiss Army Knife of cryptography tools.  Whenever you're dealing with certificates, hashes, keys and that sort of thing, OpenSSL is probably what you need.</p>
<p>This page is just a collection of some commands that I've found useful over time, so will probably grow.</p>
<h1 id="doing">Doing</h1>
<p>Create and transform keys, CSRs and certificates:</p>
<p><strong>New 2048 (or 4096) bit RSA private key</strong></p>
<pre><code class="language-bash">openssl genrsa 2048 &gt; mysite.rsa2048.key
openssl genrsa 4096 &gt; mysite.rsa4096.key
</code></pre>
<p><strong>New ECDSA private key</strong></p>
<pre><code class="language-bash">openssl ecparam -genkey -name secp256r1 &gt; mysite.ecdsa.key
</code></pre>
<p><strong>Remove a passphrase from a private key</strong></p>
<pre><code class="language-bash">openssl rsa -in private.key -out privateNew.key
</code></pre>
<p><strong>Generate a new RSA private key and CSR</strong></p>
<pre><code class="language-bash">openssl req -out mycsr.csr -new -newkey rsa:2048 -nodes -keyout private.key
</code></pre>
<p><strong>Generate a CSR using an existing private key</strong></p>
<pre><code class="language-bash">openssl req -out mycsr.csr -new -key private.key
</code></pre>
<p><strong>Create PKCS#12 (.pfx .p12) from PEM (.pem .cer .crt)</strong></p>
<pre><code class="language-bash">openssl pkcs12 -export -out mypfx.p12 -inkey private.key -in cert.crt
</code></pre>
<p><strong>Extract encrypted private key from PKCS#12 file</strong></p>
<pre><code class="language-bash">openssl pkcs12 -in mypfx.p12 -out private.key -nocerts
</code></pre>
<p><strong>Extract <em>plaintext</em> private key from PKCS#12 file</strong></p>
<pre><code class="language-bash">openssl pkcs12 -in mypfx.p12 -out private.key -nodes -nocerts
</code></pre>
<p><strong>Extract certificate file from PKCS#12 file</strong></p>
<pre><code class="language-bash">openssl pkcs12 -in mypfx.p12 -out mycert.crt -nokeys
</code></pre>
<h1 id="checking">Checking</h1>
<p>Use these commands to check the contents of CSRs, certificates and keys:</p>
<p><strong>Check Certificate Signing Request (CSR)</strong></p>
<pre><code class="language-bash">openssl req -text -noout -verify -in mycsr.csr
</code></pre>
<p><strong>Check a certificate</strong></p>
<pre><code class="language-bash">openssl x509 -text -noout -in mycert.crt
</code></pre>
<p><strong>Check a PKCS#12 file</strong></p>
<pre><code class="language-bash">openssl pkcs12 -info -in mypfx.p12
</code></pre>
<p><strong>Get expiry date (and start date) for a website's certificate</strong></p>
<pre><code class="language-bash">echo | openssl s_client -servername bytes.fyi -connect bytes.fyi:443 2&gt;/dev/null | openssl x509 -noout -dates
</code></pre>
<p><strong>Expiry (and start) date for a local certificate file</strong></p>
<pre><code class="language-bash">openssl x509 -noout -dates -in bytes_fyi.crt
</code></pre>
<h1 id="subjectaltnamesancertificates">subjectAltName (SAN) certificates</h1>
<p>Using SAN entries, a certificate can cover multiple separate domains, or multiple subdomains of the same domain, or a mixture of both.</p>
<p>To create a CSR for a SAN certificate, you need to pass a config file in to <code>openssl</code>.</p>
<p>Create a config file (e.g. mysanconfig.cnf) with the list of domains at the bottom, like this:</p>
<pre><code class="language-bash">[ req ]
default_bits             = 2048
distinguished_name       = req_distinguished_name
req_extensions           = req_ext
prompt                   = no

[ req_distinguished_name ]
countryName              = GB
stateOrProvinceName      = Nottinghamshire
localityName             = Nottingham
organizationName         = Chicken McChicken Face
organizationalUnitName   = IT
commonName               = www.mysite.com

[ req_ext ]
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = www.mysite.com
DNS.2 = mysite.com
DNS.3 = email.mysite.com
DNS.4 = otherplace.net
DNS.5 = ftp.otherplace.net
</code></pre>
<p>Then create the CSR using the config:</p>
<p><strong>Generate a new RSA private key and SAN CSR</strong></p>
<pre><code class="language-bash">openssl req -out mycsr.csr -new -newkey rsa:2048 -nodes -keyout private.key -config mysanconfig.cnf
</code></pre>
<p><strong>Generate a SAN CSR using an existing private key</strong></p>
<pre><code class="language-bash">openssl req -out mycsr.csr -new -key private.key -config mysanconfig.cnf
</code></pre>
<p>Please let me know via the comments if you have any other suggestions for this list.</p>
<p><small>Photo by <a href="https://unsplash.com/@milkovi">MILKOVÍ</a> on <a href="https://unsplash.com/s/photos/security">Unsplash</a></small></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Real-time GoAccess reports with Nginx]]></title><description><![CDATA[Use Nginx to serve real-time web analytics reports with GoAccess]]></description><link>https://bytes.fyi/real-time-goaccess-reports-with-nginx/</link><guid isPermaLink="false">5ed800f676803413812a7ea9</guid><category><![CDATA[goaccess]]></category><category><![CDATA[nginx]]></category><dc:creator><![CDATA[Matt Holdsworth]]></dc:creator><pubDate>Mon, 14 Oct 2019 17:49:52 GMT</pubDate><media:content url="https://cdn.bytes.fyi/content/images/2020/05/photo-1494236581341-7d38b2e7d824.jpeg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://cdn.bytes.fyi/content/images/2020/05/photo-1494236581341-7d38b2e7d824.jpeg" alt="Real-time GoAccess reports with Nginx"><p>In this final post of my two-part series about the GoAccess log analysis tool, I'll explain how to serve real-time HTML reports securely using Nginx, at a subdomain of the monitored website.</p>
<p>If you haven't already read the first part yet (and you don't already use GoAccess), you might like to read that first, to learn <a href="https://bytes.fyi/goaccess/">how to install and use GoAccess for CLI and static HTML web analytics reports</a>.</p>
<h3 id="prerequisites">Pre-requisites</h3>
<p>As per the first part of this guide, it'll be most relevant to you if:</p>
<ul>
<li>You are using Debian, Ubuntu (or Raspbian) for your server.</li>
<li>It's your server and you have an account with <code>sudo</code> rights.</li>
</ul>
<p>Additionally, for the more-advanced usage explored in this post, I assume:</p>
<ul>
<li>You have GoAccess installed and working on your server.</li>
<li>You've already set up TLS for your site, perhaps using a free certificate from <a href="https://letsencrypt.org/">Let's Encrypt</a>.</li>
<li>You're happy to setup a new subdomain (or domain) for accessing your web statistics at - and to point DNS for it at your existing server. For example, if your <code>access.log</code> is for <strong>www.myblog.com</strong>, you could use <strong>goaccess.myblog.com</strong> to host the real-time statistics in the same Nginx instance.</li>
</ul>
<p>Sounds good? Then let's go!</p>
<h3 id="realtimehtmlreports">Real-time HTML reports</h3>
<p>We looked at how to use GoAccess to generate static one-off HTML reports on the command line, in the first post of this two-part series.</p>
<p>Although static reports can be very useful, wouldn't it be great to have a live report available, which updated itself automatically as visitors hit the monitored site (without needing to refresh the page)?  That's what we'll be setting up in this article.</p>
<p>Let's run through what we'll need:</p>
<ol>
<li>A new subdomain for accessing the report at - e.g. <strong>goaccess.myblog.com</strong> - with a TLS certificate (perhaps you could add a SAN entry for <strong>goaccess.myblog.com</strong> next time you renew your certificate for <strong>www.myblog.com</strong>?).</li>
<li>A new virtual host / server block in our Nginx config for <strong>goaccess.myblog.com</strong>, restricted to local access only. (You don't want to share your report with the whole world!)</li>
<li>A <code>systemd</code> unit file to run GoAccess as a service automatically from system startup.</li>
</ol>
<blockquote>
<p>Creating a certificate for your new 'goaccess' subdomain is left as an exercise for the reader, but there are plenty of <a href="https://certbot.eff.org/lets-encrypt/debianbuster-nginx">guides</a> out there on how to get your own auto-renewing certificates from Let's Encrypt, completely free of charge.</p>
</blockquote>
<p>Throughout this post, I'll use some fictional examples:</p>
<ul>
<li><strong>www.myblog.com</strong> represents the monitored domain - i.e. the existing Nginx site/vhost creating the access log that you want to report on.</li>
<li><strong>goaccess.myblog.com</strong> represents a new (sub)domain where your real-time report will live at.</li>
</ul>
<blockquote>
<p>Be sure to replace these examples with your <em><strong>actual</strong></em> (sub)domain names if you copy and paste the sample configs I've provided!</p>
</blockquote>
<p>As well as serving the report HTML, we will also configure Nginx to reverse-proxy the websocket server that's used to update the report in real-time.</p>
<h3 id="createalocationforthereportfile">Create a location for the report file</h3>
<p>First off, let's create a folder for the web root of <strong>goaccess.myblog.com</strong>. This folder is where GoAccess will output the <code>report.html</code> to, for Nginx to serve at <strong>goaccess.myblog.com</strong>.</p>
<pre><code class="language-bash">sudo mkdir -p /var/www/goaccess
</code></pre>
<p>This will be the folder where our GoAccess service will write the <code>report.html</code> file to.<br>
It will also be the folder where Nginx will serve the file from.</p>
<h3 id="systemdsetup">Systemd setup</h3>
<p>We want to run GoAccess as a service and start it automatically at bootup.</p>
<ol>
<li>Create a new custom unit file:</li>
</ol>
<pre><code class="language-bash">sudo nano /etc/systemd/system/goaccess.service
</code></pre>
<ol start="2">
<li>Paste in the config below, updating the following items to match your own setup:
<ul>
<li><code>/var/log/nginx/access.log</code> is the path to your access log</li>
<li><code>goaccess.myblog.com</code> is the subdomain for your real-time statistics (we'll configure Nginx for this in the next section). Make sure to update both <code>--ws-url</code> and <code>--origin</code> items to reflect your actual stats (sub)domain.</li>
<li><code>/var/www/goaccess</code> is the web root folder that you created at the end of the previous section</li>
</ul>
</li>
</ol>
<pre><code class="language-ini">[Unit]
Description=GoAccess real-time web log analysis
After=network.target

[Service]
Type=simple

ExecStart=/usr/local/bin/goaccess -f /var/log/nginx/access.log \
          --real-time-html --ws-url=wss://goaccess.myblog.com:443/ws \
          -o /var/www/goaccess/report.html --port=7890 \
          --config-file=/usr/local/etc/goaccess/goaccess.conf \
          --origin=https://goaccess.myblog.com

ExecStop=/bin/kill -9 ${MAINPID}
WorkingDirectory=/tmp

NoNewPrivileges=true
PrivateTmp=true
ProtectHome=read-only
ProtectSystem=strict
SystemCallFilter=~@clock @cpu-emulation @debug @keyring @memlock @module \
                  @mount @obsolete @privileged @reboot @resources @setuid \
                  @swap @raw-io

ReadOnlyPaths=/
ReadWritePaths=/proc/self
ReadWritePaths=/var/www/goaccess

PrivateDevices=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes

[Install]
WantedBy=multi-user.target
</code></pre>
<ol start="3">
<li>Refresh <code>systemd</code>, start the service and enable it to run automatically at bootup:</li>
</ol>
<pre><code class="language-bash"># update systemd
sudo systemctl daemon-reload

# start the service
sudo systemctl start goaccess

# enable it to start automatically
sudo systemctl enable goaccess

# wait a minute or so, then use this to check its status
sudo systemctl status goaccess
</code></pre>
<p>If all is well, you should see output similar to this:</p>
<pre><code class="language-adoc">goaccess.service - GoAccess real-time web log analysis
   Loaded: loaded (/etc/systemd/system/goaccess.service; enabled; vendor preset: enabled)
   Active: active (running) since ...
</code></pre>
<p>If there are errors showing, you can use <code>sudo journalctl -u goaccess</code> to see what's gone wrong.</p>
<h3 id="nginxconfiguration">Nginx configuration</h3>
<p>Because we have a TLS certificate for <strong>goaccess.myblog.com</strong>, we can also secure the websocket server using Nginx this way; it means we don't need to configure GoAccess itself for TLS, yet we still get a secure websocket connection (<code>wss:</code>) from the browser. Neat, eh?</p>
<blockquote>
<p>By using Nginx to serve the HTML report <em>and</em> reverse-proxy the websocket server, we can keep all the TLS config in Nginx where it belongs. It also means we don't need to open a separate port for the websocket server (e.g. 7890) - both HTTPS and secure websockets will be accessible at port 443.</p>
</blockquote>
<p>Most Nginx setups use folders for including extra config files in the main configuration. This separation helps to keep things easy to manage.</p>
<p>For example, your main Nginx configuration file, e.g. <code>/etc/nginx/nginx.conf</code>, might contain the following lines towards the bottom of its <code>http</code> block:</p>
<pre><code class="language-nginx">include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
</code></pre>
<p>Those lines mean that any files in <code>/etc/nginx/conf.d</code> folder with an extension of <code>.conf</code> will be included in the main config.<br>
Also, <em>any</em> files in the <code>/etc/nginx/sites-enabled</code> folder will be included.</p>
<p>The best way to manage virtual hosts is to save each host's config in the <code>/etc/nginx/sites-available</code> folder:</p>
<ul>
<li>The <code>sites-available</code> folder is for storing <em>all</em> of your vhost configurations, whether or not they're currently enabled.</li>
<li>The <code>sites-enabled</code> folder contains symlinks to files in the <code>sites-available</code> folder. This allows you to selectively disable vhosts by removing the symlink.</li>
</ul>
<p>Let's make the necessary changes to our Nginx config for <strong>goaccess.myblog.com</strong>.</p>
<ol>
<li>Edit the main <code>nginx.conf</code> file, adding the following somewhere in the <code>http</code> block:</li>
</ol>
<pre><code class="language-nginx">map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}
</code></pre>
<ol start="2">
<li>Create a config file for our new subdomain:</li>
</ol>
<pre><code class="language-bash"># replace with your actual stats domain name
sudo nano /etc/nginx/sites-available/goaccess.myblog.com
</code></pre>
<ol start="3">
<li>Paste in the config below and amend as per the comments:</li>
</ol>
<pre><code class="language-nginx">upstream gwsocket {
    server 127.0.0.1:7890;
}

server {
    listen 80;
    
    # replace with your actual domain
    server_name goaccess.myblog.com;
    
    # and again
    return 301 https://goaccess.myblog.com$request_uri;
}

server {
    listen 443 ssl http2;

    # replace with your actual domain
    server_name goaccess.myblog.com;
    
    # if you chose a different location for the webroot, use it here
    root /var/www/goaccess;

    # update these values to wherever your cert and key are
    ssl_certificate /path/to/your/certificate.pem;
    ssl_certificate_key /path/to/your/keyfile.key;

    # update to match your local network, to allow access from your LAN
    allow 192.168.1.0/24;
    deny all; # block rest of the world
	
    access_log /var/log/nginx/goaccess-access.log;
    error_log /var/log/nginx/goaccess-error.log warn;

    location / {
        try_files $uri/report.html =404;
    }

    location /ws {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_pass http://gwsocket;
        proxy_buffering off;
        proxy_read_timeout 7d;
    }
}
</code></pre>
<ol start="4">
<li>Apply the new config:</li>
</ol>
<pre><code class="language-bash"># create a symlink to activate the new virtual host
sudo ln -s /etc/nginx/sites-available/goaccess.myblog.com /etc/nginx/sites-enabled/goaccess.myblog.com

# test the new nginx config is valid
sudo nginx -t

# re-load nginx to apply the new config
sudo nginx -s reload
</code></pre>
<h3 id="lockingthingsdown">Locking things down</h3>
<p>The <code>allow</code> and <code>deny</code> directives in the Nginx config above are to make sure that only you can view your statistics, from your local network.</p>
<p>If you'd like to whitelist other friendly subnets - perhaps you run a VPN server to dial in - then just add the appropriate subnet as another <code>allow</code>.</p>
<p>You can whitelist individual public IPs too, if you like:</p>
<pre><code class="language-nginx">    # update to match your local subnet, to allow access from your LAN
    allow 192.168.1.0/24;
    
    # allow another local subnet, such as VPN-connected clients
    allow 10.20.30.0/24;
    
    # allow a specific public IP
    allow 172.217.169.36;
    
    # block rest of the world
    deny all; 
</code></pre>
<p>If you make changes to your config, just use <code>sudo nginx -t</code> to test it's valid, then <code>sudo nginx -s reload</code> to apply.</p>
<h3 id="thefinalresult">The final result</h3>
<p>If all has gone well, when you point your browser to your new statistics subdomain, you should get something like this:</p>
<p><img src="https://cdn.bytes.fyi/content/images/2019/10/goaccess-realtime-report.png" alt="Real-time GoAccess reports with Nginx"></p>
<p>That little green spot next to the settings cog at the top-left of the dashboard indicates that the websocket connection is working, so the report will be updated in real-time, as visitors hit your site (and Nginx updates the access log).</p>
<p>If you browse some pages on the monitored website from another window/device, you should see the statistics update straight away. Nice.</p>
<blockquote>
<p><strong>A minor hiccup</strong><br>
There is a bug with the report in GoAccess v1.3 (the latest stable version at the time of writing): the 'Last Updated' timestamp at the top-right never updates when a websocket update is received. The statistics are updated, but the last updated time never changes. The time shown indicates when the <code>goaccess</code> service was started and the log was parsed. I've raised an <a href="https://github.com/allinurl/goaccess/issues/1548">issue on Github</a> about this and I'll update the post when a solution is identified.</p>
</blockquote>
<p>You should confirm that you cannot reach your stats site via an Internet/external IP, e.g. test via 4G from your mobile; you should only be able to see the report from your LAN.</p>
<h3 id="whatdoesitallmean">What does it all mean?</h3>
<p>For more info on the various panels - and extra ones that you can enable via GoAccess configuration, please see the <a href="https://goaccess.io/man#description">GoAccess docs</a>.</p>
<p>With some tweaks to your Nginx and GoAccess configs, you can customise your statistics further: e.g. measure the time taken to serve different resources from your site, or track multiple virtual hosts in the same log.</p>
<h3 id="conclusion">Conclusion</h3>
<p>Rather than including Google Analytics or similar code in all of your pages, you can get some really useful insights from a bog-standard access log.</p>
<p>GoAccess is a great way to get some basic but valuable analytics to help you manage your website(s), without compromising your users' privacy.</p>
<p>If you've found this article useful or have a suggestion, I'd love to hear from you in the comments below.</p>
<hr>
<p><small>Photo by <a href="https://unsplash.com/@anniespratt?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Annie Spratt</a> on <a href="https://unsplash.com/s/photos/quotes?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></small></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Log analysis using GoAccess]]></title><description><![CDATA[How to install and use GoAccess for log analysis and basic web analytics]]></description><link>https://bytes.fyi/goaccess/</link><guid isPermaLink="false">5ed800f676803413812a7ea6</guid><category><![CDATA[goaccess]]></category><category><![CDATA[nginx]]></category><dc:creator><![CDATA[Matt Holdsworth]]></dc:creator><pubDate>Mon, 14 Oct 2019 17:48:55 GMT</pubDate><media:content url="https://cdn.bytes.fyi/content/images/2020/05/photo-1495865870076-53f4c607d692.jpeg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><blockquote>
<img src="https://cdn.bytes.fyi/content/images/2020/05/photo-1495865870076-53f4c607d692.jpeg" alt="Log analysis using GoAccess"><p>Updated January 2021 for latest GoAccess release (v1.4.5)</p>
</blockquote>
<p><a href="https://goaccess.io/">GoAccess</a> is a great tool for getting some useful insights from web server access logs, with hardly any effort.</p>
<p>In this two-part series, I'll explain how to install GoAccess and configure it with an Nginx web server, to build an attractive web-based analytics report:</p>
<ol>
<li>In this post, we'll install GoAccess and explore how to use it for quick reports from the command line, as well as generating some one-off HTML reports.</li>
<li>The next post focuses on more advanced usage: <a href="https://bytes.fyi/real-time-goaccess-reports-with-nginx/">how to serve a real-time HTML report with Nginx and run GoAccess as a service</a>. Also, we'll reverse-proxy the GoAccess websocket server to support secure real-time updates to the report.</li>
</ol>
<p>Let's start with the basics.</p>
<h3 id="whatisanaccessloganyway">What is an <code>access.log</code> anyway?</h3>
<p>If you have a website, it's likely that your web server will be diligently logging all the requests it receives as users (and non-human visitors like bots and search engine crawlers) access the various resources that make up the site.<br>
Even with the default access log settings, the logs can still give some useful insights into your site and its visitors:</p>
<ul>
<li>How many users are visiting your site?</li>
<li>What are the most popular browsers and operating systems?</li>
<li>Where are your visitors located (or where do they VPN to)?</li>
<li>Which sites have links to your site?</li>
<li>What are the busiest times of day for your site, on average?</li>
</ul>
<p>Checking the logs manually (e.g. using <code>grep</code> and <code>sed</code>) is possible, of course. For some very specific searches, those tools might still be the best way, but for visualising trends over time or aggregating the data... not so much.</p>
<p>GoAccess reads an access log and analyses the data to produce useful statistical summaries and pretty visualisations that are very easy to interpret.</p>
<p>Unlike Google Analytics, Piwik and similar Javascript-based analytics tools, GoAccess doesn't require any extra code on your web pages to work. Bonus.</p>
<h3 id="beforewebegin">Before we begin</h3>
<p>A few assumptions about your existing setup:</p>
<ul>
<li>You already have Nginx configured to serve (or reverse-proxy) one or more websites/services.</li>
<li>Your server is Debian or Ubuntu (or Raspbian) based.</li>
<li>It's your server and you have an account with <code>sudo</code> rights.</li>
</ul>
<p>If some of the above don't apply to you, some of this article might not be relevant.</p>
<p>Right then, let's go!</p>
<h3 id="installation">Installation</h3>
<ol>
<li>Update existing packages:</li>
</ol>
<pre><code class="language-bash">sudo apt-get update &amp;&amp; sudo apt-get upgrade
</code></pre>
<ol start="2">
<li>Install dependencies</li>
</ol>
<pre><code class="language-bash">sudo apt-get install libncursesw5-dev libgeoip-dev build-essential -y
</code></pre>
<ol start="3">
<li>Download, build and install:</li>
</ol>
<pre><code class="language-bash"># create a folder for building packages, if it's not there already
mkdir -p ~/build/
cd ~/build/

# download the latest stable version (1.4.5 as at January 2021)
wget https://tar.goaccess.io/goaccess-1.4.5.tar.gz

# expand the package files into a subfolder
tar -xzvf goaccess-1.4.5.tar.gz
cd goaccess-1.4.5/

# compile and install
./configure --enable-utf8 --enable-geoip=legacy
make
sudo make install
</code></pre>
<h3 id="commandlineusage">Command-line usage</h3>
<p>Let's check that GoAccess is working as expected:</p>
<pre><code class="language-bash"># amend the path if your access log lives somewhere else
goaccess /var/log/nginx/access.log -c
</code></pre>
<p>You should be prompted to choose a log format:<br>
<img src="https://cdn.bytes.fyi/content/images/2019/10/goaccess-choose-log-format.png" alt="Log analysis using GoAccess"></p>
<p>If you haven't customised your Nginx server's access log format, it will use the 'NCSA Combined Log Format'. Press <code>Space</code> to select the first option, then press <code>Enter</code> to proceed. (Or you can customise the log string, date and time format before proceeding, if your server has been configured differently from the defaults.)</p>
<p>After the log has been parsed, you'll see an initial screen of statistics, something like this:<br>
<img src="https://cdn.bytes.fyi/content/images/2020/06/Screenshot-2020-06-30-at-18.23.26.png" alt="Log analysis using GoAccess"></p>
<p>You can navigate using the cursor keys. After you've had a look around, press <code>q</code> to quit. Here's the full list of interactive controls, in case you want to explore further before quitting:</p>
<ul>
<li><code>F1</code> or <code>h</code> Main help.</li>
<li><code>F5</code> Redraw main window.</li>
<li><code>q</code> Quit the program, current window or collapse active module</li>
<li><code>o</code> or <code>ENTER</code> Expand selected module or open window</li>
<li><code>0-9</code> and <code>Shift + 0</code> Set selected module to active</li>
<li><code>j</code> Scroll down within expanded module</li>
<li><code>k</code> Scroll up within expanded module</li>
<li><code>c</code> Set or change scheme color</li>
<li><code>^ f</code> Scroll forward one screen within active module</li>
<li><code>^ b</code> Scroll backward one screen within active module</li>
<li><code>TAB</code> Iterate modules (forward)</li>
<li><code>SHIFT + TAB</code> Iterate modules (backward)</li>
<li><code>s</code> Sort options for active module</li>
<li><code>/</code> Search across all modules (regex allowed)</li>
<li><code>n</code> Find position of the next occurrence</li>
<li><code>g</code> Move to the first item or top of screen</li>
<li><code>G</code> Move to the last item or bottom of screen</li>
</ul>
<h3 id="configuration">Configuration</h3>
<p>We should set some global defaults for our GoAccess settings, so we won't need to specify them manually. This will be useful later on.</p>
<p>Firstly, check the location of the current default config file using the <code>goaccess --dcf</code> command.</p>
<p>In my case, the example config file installed earlier (by the <code>sudo make install</code> command) is listed as the default, located at <code>/usr/local/etc/goaccess/goaccess.conf</code>.</p>
<p>You might find there is no current default config file found, with a result like this:</p>
<pre><code class="language-adoc">No default config file found.
You may specify one with `-p /path/goaccess.conf`
</code></pre>
<p>In that case, then you can copy the file to its parent folder, where GoAccess can find it:</p>
<pre><code class="language-bash">sudo cp /usr/local/etc/goaccess/goaccess.conf /usr/local/etc/
</code></pre>
<p>But for me, I just made a copy of the default config so that I could revert easily in case I accidentally broke something:</p>
<pre><code class="language-bash">sudo cp /usr/local/etc/goaccess/goaccess.conf /usr/local/etc/goaccess/goaccess.conf.default
</code></pre>
<p>If you try the <code>goaccess --dcf</code> command again now, it should report the correct location.</p>
<p>Now let's edit the file so we have a few sensible default options for our logs:</p>
<pre><code class="language-bash">sudo nano /usr/local/etc/goaccess/goaccess.conf
</code></pre>
<p>We want to uncomment the appropriate lines for <code>time-format</code>, <code>date-format</code> and <code>log-format</code>, so find the lines below and remove the <code>#</code> from the start of each of these lines:</p>
<pre><code class="language-adoc">time-format %H:%M:%S
date-format %d/%b/%Y
log-format %h %^[%d:%t %^] &quot;%r&quot; %s %b &quot;%R&quot; &quot;%u&quot;
</code></pre>
<p>Press <code>Ctrl-x</code> to exit nano and follow the prompts to save your changes (keep the existing name).</p>
<h3 id="statichtmlreports">Static HTML reports</h3>
<p>For a self contained one-off report, you can use a command like this:</p>
<pre><code class="language-bash">goaccess /var/log/nginx/access.log -o report.html

# or use this if you've not saved a custom default config as per the previous section
goaccess /var/log/nginx/access.log --log-format=COMBINED -o report.html
</code></pre>
<p>After running that command, just open the output <code>report.html</code> in a browser and you'll get some great high-level insights into your website and its readers - and some pretty graphs too. :)</p>
<p>The static reports output by GoAccess are totally self-contained (the scripts and styles are all inlined in the file with the HTML), so you can easily copy or share/email them without any fuss.</p>
<h3 id="nextsteps">Next steps</h3>
<p>One-off reports are great, but wouldn't it be good if we could set up an always-up-to-date version, with the statistics updating in real-time as visitors browsed our site?</p>
<p>That's just what we'll be looking at in the <a href="https://bytes.fyi/real-time-goaccess-reports-with-nginx/">next post</a> of this two-part series.</p>
<p>See you there!</p>
<hr>
<p><small>Photo by <a href="https://unsplash.com/@vizaformemories?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">VizAforMemories</a> on <a href="https://unsplash.com/s/photos/sigapore?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></small></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Latest Nginx and OpenSSL on Raspberry Pi]]></title><description><![CDATA[How to roll-your-own latest and greatest Nginx from source on Raspbian (or Debian/Ubuntu)]]></description><link>https://bytes.fyi/build-nginx-openssl-from-source/</link><guid isPermaLink="false">5ed800f676803413812a7e9d</guid><category><![CDATA[nginx]]></category><category><![CDATA[openssl]]></category><category><![CDATA[pi]]></category><dc:creator><![CDATA[Matt Holdsworth]]></dc:creator><pubDate>Tue, 30 May 2017 17:39:52 GMT</pubDate><media:content url="https://cdn.bytes.fyi/content/images/2020/05/photo-1575282059984-0707e8932f89.jpeg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://cdn.bytes.fyi/content/images/2020/05/photo-1575282059984-0707e8932f89.jpeg" alt="Latest Nginx and OpenSSL on Raspberry Pi"><p><em>Note: to skip the reasoning and cut to the chase, see <a href="#installation">Installation</a> below.</em></p>
<h3 id="whynginxonpi">Why Nginx on Pi?</h3>
<p>Although <a href="https://httpd.apache.org/">Apache</a> still powers the majority of sites overall, its popularity is declining. Conversely, <a href="http://nginx.org/en/">Nginx</a> (pronounced engine-x) enjoys a fast-growing share of the market; almost two-thirds of the world's top 10,000 websites run on Nginx<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>.<br>
In large part, the key to Nginx's success is its event-driven design, meaning it scales very predictably, enabling sites to support massive amounts of traffic. It's extremely fast and flexible, with a reasonable configuration syntax.<br>
If you need a web server, reverse proxy or software load balancer, you would be daft not to at least consider using Nginx.</p>
<blockquote>
<p>On average, every minute one of the top 10 million websites starts to use Nginx</p>
</blockquote>
<p>Nginx is so efficient that its performance is pretty good even when running on modest hardware like a Raspberry Pi 2 or 3.<br>
I'm not suggesting you can run a major website from a Raspberry Pi, but the faster models are surprisingly capable as small web servers - especially when you consider their frugal power consumption of around 1.5W idle and 2-4W when busy<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>.</p>
<h3 id="whynotjustaptgetit">Why not just <code>apt-get</code> it?</h3>
<p>There are good reasons to use the latest and greatest version of Nginx.  Some advanced features like <a href="https://www.nginx.com/blog/websocket-nginx/">proxying websocket connections</a> (which I need for the web front ends of my Ubiquiti Edgerouter Lite and Unifi wifi controller) have only matured recently.  I serve both ECDSA and RSA certificates for my sites, to get the best of both worlds in terms of performance and compatibility with older clients. Such 'hybrid' certificates are only supported in Nginx &gt;= 1.11.</p>
<p>If you want up-to-date cryptography for your web server, such as the ChaCha20-Poly1305 cipher suites for better performance on mobile devices, then you also need an up-to-date version of the <a href="https://www.openssl.org/">OpenSSL</a> cryptography toolkit for Nginx to use. There are good reasons to do so:</p>
<blockquote>
<p>The new cipher suites are fast. As Adam Langley described, ChaCha20-Poly1305 is three times faster than AES-128-GCM on mobile devices. Spending less time on decryption means faster page rendering and better battery life. Although the cipher part of TLS may not be the biggest source of battery consumption, spending fewer CPU cycles on encryption saves battery life, especially on large files.<sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup></p>
</blockquote>
<p>New features like these take considerable time to filter down to the offical, packaged versions available for Raspbian Jessie. At the time of writing, using <code>sudo apt-get install nginx</code> would install version 1.6.2 - a couple of years behind the latest mainline version, 1.13.1.</p>
<p>Finally, just in case you're still not convinced, building from source allows you to control which optional modules are included or not for your specific Nginx build.<br>
In other words, if you'll be needing modules like <code>ngx_http_stub_status_module</code> (which I use for monitoring the load on my Nginx servers via my dashboard), then you'll need to compile from source anyway.</p>
<h3 id="whatsthecatch">What's the catch?</h3>
<p>Compiling software from source isn't (usually) as easy as installing a package and requires a bit of extra work to set up things like a service definition for <code>systemd</code>, so that your web server runs automatically on startup following a reboot.</p>
<p>You need patience, too: it takes quite a long time to compile OpenSSL and Nginx, especially on older / less powerful Pi devices. Making a cup of tea helps with this one.</p>
<p>There is an argument for using older, package-managed versions of software rather than self-built versions simply because it's easy to update to a new version using your package manager (e.g. <code>apt</code>) when updates are made available in your chosen repositories. If it's easier to maintain your software in the future, you'll be more likely to apply any important security fixes, goes the theory.</p>
<p>We can mitigate these problems by using a script to make installation and upgrades almost as easy as using a packaged version.</p>
<p>Luckily for us, we don't have to write anything ourselves, as there's a great <a href="https://github.com/MatthewVance/nginx-build">Nginx Build Script</a> by Matthew Vance (based on an original script by <a href="https://github.com/MattWilcox">Matt Wilcox</a>), which automates most of the process for us.</p>
<h3 id="howthescriptworks">How the script works</h3>
<p>Here's a summary of what it does:</p>
<ol>
<li>Installs/updates tools essential to the build</li>
<li>Downloads the required source code packages, comparing the SHA256 checksum values of each download against known good ones, to make sure the download wasn't corrupted somehow:</li>
</ol>
<ul>
<li><strong>PCRE</strong> (Perl Compatible Regular Expressions), to enable and optimise the use of regular expressions in Nginx configurations</li>
<li><strong>zlib</strong>, enabling <a href="http://nginx.org/en/docs/http/ngx_http_gzip_module.html">ngx_http_gzip_module</a>, for gzip encoding of responses</li>
<li><strong>OpenSSL</strong> for cryptographic functions i.e. SSL/TLS</li>
<li><strong>Nginx</strong> itself, of course</li>
</ul>
<ol start="3">
<li>Double-checks that the Nginx and OpenSSL sources are genuine downloads, by checking their code-signing signatures</li>
<li>Unpacks the compressed download files</li>
<li>Renames the current <code>/etc/nginx</code> folder (if present) to make a dated backup copy</li>
<li>Creates various subfolders for Nginx caches in <code>/var/cache</code></li>
<li>Sets up the <code>nginx</code> user and group (if they don't already exist) ready for Nginx to run as</li>
<li>Tests the local <code>gcc</code> compiler capabilities and optionally enables a special performance boost, if possible</li>
<li>Builds Nginx, which in turn builds OpenSSL, with various optional modules enabled/disabled</li>
<li>Restores the backed up config from the previous install (if available)</li>
<li>Creates a <code>systemd</code> service file, to enable Nginx to run as a service from startup</li>
</ol>
<p>By customising the script, we can fine-tune which optional modules are included/excluded from our own version of Nginx.<br>
The defaults are quite sensible for general web server and reverse proxy uses, but you can always run the script again with different options - see <a href="#upgrades">Upgrades</a> for how to do this.</p>
<h3 id="installation">Installation</h3>
<p>I've reproduced these installation instructions from the <a href="https://github.com/MatthewVance/nginx-build"><code>nginx-build</code> Github page</a>, with some explanatory comments:</p>
<pre><code class="language-bash"># create a working folder where the source will be unpacked and compiled
sudo mkdir /usr/local/src/nginx/

# switch to the working folder we just created
cd /usr/local/src/nginx/

# download the latest version of the script
sudo curl -L https://raw.githubusercontent.com/MatthewVance/nginx-build/master/build-nginx.sh -o build_nginx.sh
</code></pre>
<p>This next step is <em>really important</em>. Please take the time to check the contents of the script to make sure you're happy with it. You don't have to be an expert to compare the steps listed above explaining what the script does (in <a href="#howthescriptworks">how the script works</a>), against the copy you've just downloaded.</p>
<blockquote>
<p>You're going to make this script executable and then effectively give it root privileges by running it with sudo, so it would be madness not to check it before you run it.</p>
</blockquote>
<pre><code class="language-bash"># review downloaded code before executing
cat build_nginx.sh

# make the script executable
sudo chmod +x build_nginx.sh

# run it
sudo ./build_nginx.sh
</code></pre>
<p>It will take quite a while for the script to do its work. You can watch along, or make yourself a well-earned cuppa at this point.</p>
<p>When finished, you can start Nginx once using <code>sudo nginx</code>, but you probably really want to make Nginx run automatically on startup, so use these commands:</p>
<pre><code class="language-bash"># start nginx service
sudo systemctl start nginx

# make it start automatically when the system starts
sudo systemctl enable nginx
</code></pre>
<h3 id="upgrades">Upgrades</h3>
<p>When a new version of Nginx and/or OpenSSL are released, the script is updated shortly afterwards to use the new version(s). You might also decide to re-run the script after customising the list of optional modules in the compilation settings in the script.</p>
<p>Whatever the reason, the steps involved in upgrading are very similar to the initial installation:</p>
<pre><code class="language-bash"># move to the working folder
cd /usr/local/src/nginx/

# remove the old version of the script
# skip this step if you're just adding/removing Nginx modules
sudo rm /usr/local/src/nginx/build_nginx.sh

# download the latest version of the script
# (don't bother if you're just re-running to add/remove modules)
sudo curl -L https://raw.githubusercontent.com/MatthewVance/nginx-build/master/build-nginx.sh -o build_nginx.sh

# IMPORTANT! review downloaded code before executing
cat build_nginx.sh

# make the downloaded script executable
sudo chmod +x build_nginx.sh

# run the script
sudo ./build_nginx.sh

# restart the service
sudo systemctl restart nginx

# confirm the new version number is reported (if applicable)
nginx -v

# check the full version details, including OpenSSL version and compilation options
nginx -V
</code></pre>
<h3 id="conclusion">Conclusion</h3>
<p>Rolling your own Nginx - including the latest stable OpenSSL - is well worth doing. Any concerns over complex compilation syntax or difficulty keeping your custom copy up-to-date, can be addressed using a script specially written to make building and maintaining your own Nginx easy peasy, lemon squeezy!</p>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p><a href="https://w3techs.com/blog/entry/nginx_reaches_33_3_percent_web_server_market_share_while_apache_falls_below_50_percent">https://w3techs.com/blog/entry/nginx_reaches_33_3_percent_web_server_market_share_while_apache_falls_below_50_percent</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p>
</li>
<li id="fn2" class="footnote-item"><p><a href="https://www.pidramble.com/wiki/benchmarks/power-consumption">https://www.pidramble.com/wiki/benchmarks/power-consumption</a> <a href="#fnref2" class="footnote-backref">↩︎</a></p>
</li>
<li id="fn3" class="footnote-item"><p><a href="https://blog.cloudflare.com/do-the-chacha-better-mobile-performance-with-cryptography/">https://blog.cloudflare.com/do-the-chacha-better-mobile-performance-with-cryptography/</a> <a href="#fnref3" class="footnote-backref">↩︎</a></p>
</li>
</ol>
</section>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Headless Raspberry Pi setup]]></title><description><![CDATA[How to setup a new Pi for headless use, without attaching a keyboard or monitor]]></description><link>https://bytes.fyi/headless-raspberry-pi-setup/</link><guid isPermaLink="false">5ed800f676803413812a7e9c</guid><category><![CDATA[pi]]></category><dc:creator><![CDATA[Matt Holdsworth]]></dc:creator><pubDate>Sat, 22 Apr 2017 20:47:28 GMT</pubDate><media:content url="https://cdn.bytes.fyi/content/images/2017/05/raspberry-pi-3-drawing.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://cdn.bytes.fyi/content/images/2017/05/raspberry-pi-3-drawing.jpg" alt="Headless Raspberry Pi setup"><p>I follow these steps when I get a shiny new Raspberry Pi or want to re-purpose one completely and 'start from scratch':</p>
<ol>
<li>Download and unzip the <a href="https://www.raspberrypi.org/downloads/raspbian/">latest Raspbian Lite</a>.</li>
<li>Burn the unzipped image to your microSD card, using <a href="https://etcher.io/">Etcher</a> if on a Mac, or see <a href="https://www.raspberrypi.org/documentation/installation/installing-images/README.md">Raspbian installation instructions for other OSes</a>.</li>
<li>Enable ssh for headless setup of the Pi, by keeping the Raspbian SD card mounted, then creating a file (an empty one will do fine) called 'ssh' in the root of the 'boot' partition:</li>
</ol>
<ul>
<li>In Terminal, type <code>cd /Volumes/boot/</code></li>
<li>then <code>ls</code></li>
<li>check that the folder has a <code>start.elf</code> file listed (just to make sure you're in the right place).</li>
<li>type <code>touch ssh</code> (to create an empty file called <code>ssh</code>, which magically enables the Pi's ssh server at bootup time).</li>
</ul>
<ol start="4">
<li>Unmount/eject the microSD card and put it into your Pi. Connect a network cable from the Pi to your switch/router, then connect power. You should see various LEDs on the Pi flashing as it boots for the first time.</li>
<li>Use the Admin UI for your router to find out the local IP that's been assigned to the Pi (e.g. 192.168.1.10 or whatever).</li>
<li>[Optional] Create a MAC/IP reservation in your router's DHCP configuration for the Pi's MAC address, so the Pi always gets the local IP address and that you assign to it. (Alternatively, you could assign a static IP to the Pi later, but I prefer to have the Pi pick up an IP and control things from the router.)</li>
<li>Connect to the Pi via ssh:</li>
</ol>
<ul>
<li>In Terminal, type <code>ssh pi@192.168.1.10</code>, where you replace <code>192.168.1.10</code> with the actual IP address you looked up in the previous step.</li>
<li>The default password is <code>raspberry</code>.</li>
</ul>
<ol start="8">
<li>Install any updates available for Raspbian and existing packages:</li>
</ol>
<ul>
<li><code>sudo apt-get update</code></li>
<li><code>sudo apt-get dist-upgrade</code></li>
</ul>
<ol start="9">
<li><s>Update the Pi's firmware with <code>sudo rpi-update</code></s></li>
</ol>
<blockquote>
<p>Update Sep 2019 - I no longer run <code>rpi-update</code> after setting up a new Pi, as it's not needed for most purposes; official, stable firmware and kernel updates for Pi can be applied with a simple <code>sudo apt-get update &amp;&amp; sudo apt-get upgrade</code><sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>
</blockquote>
<ol start="10">
<li>Reboot the Pi:</li>
</ol>
<ul>
<li><code>sudo reboot</code></li>
</ul>
<ol start="11">
<li>Wait for the Pi to reboot, then connect to it again (see step 7).</li>
<li>Configure the Pi for typical headless server use:</li>
</ol>
<ul>
<li><code>sudo raspi-config</code></li>
<li>Change the default password for the <code>pi</code> user (use a strong password, of course!)</li>
<li>Set the hostname (something simple, short and easy to remember)</li>
<li>Boot Options &gt; Desktop / CLI &gt; Console</li>
<li>Boot Options &gt; Wait for Network at Boot &gt; Yes</li>
<li>Interfacing Options &gt; SSH &gt; Yes</li>
<li>Advanced Options &gt; Expand Filesystem</li>
<li>Advanced Options &gt; Memory Split &gt; 16 (we don't need lots of graphics memory as we aren't connecting a display)</li>
<li>(Press right arrow twice then) Finish</li>
</ul>
<ol start="13">
<li>Reboot the Pi to expand the filesystem and apply any other changes:</li>
</ol>
<ul>
<li><code>sudo reboot</code></li>
</ul>
<p>Next, I often setup a simple firewall (ufw), setup passwordless (key based) SSH from my trusted devices and enable two factor authentication (2FA) for SSH using Google Authenticator, but this post covers the basic setup of a fresh Raspberry Pi, for use as a headless server on a private network.</p>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p><a href="https://raspberrypi.stackexchange.com/a/73771">https://raspberrypi.stackexchange.com/a/73771</a> <a href="#fnref1" class="footnote-backref">↩︎</a></p>
</li>
</ol>
</section>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[I've made a blog]]></title><description><![CDATA[Starting out with Ghost: here's my new blog]]></description><link>https://bytes.fyi/ive-made-a-blog/</link><guid isPermaLink="false">5ed800f676803413812a7e9a</guid><category><![CDATA[pi]]></category><category><![CDATA[ghost]]></category><dc:creator><![CDATA[Matt Holdsworth]]></dc:creator><pubDate>Mon, 13 Mar 2017 07:37:50 GMT</pubDate><media:content url="https://cdn.bytes.fyi/content/images/2017/05/bytes-fyi-mockup-new.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://cdn.bytes.fyi/content/images/2017/05/bytes-fyi-mockup-new.png" alt="I've made a blog"><p>I discovered the beautiful <a href="https://ghost.org">Ghost</a> open source blogging software about a year ago. I love the clean UI, great default theme and unashamed focus on writing, paired with the simplicity of <a href="https://blog.ghost.org/markdown/">Markdown</a> for writing naturally and formatting posts using an easy-to-learn syntax.</p>
<p>It was another fun little technical project for me to build on one of my many Raspberry Pi devices (a Pi 2 in this case). I like playing with new toys, and I'd never played with Node.js or configured an Nginx cache before.</p>
<p>In common with all my previous (failed) attempts at setting up a blog, so far I've just been tinkering: adding a comments system, choosing and customising a theme, adding favicons, optimising my Nginx web server config, etc... all without actually <em><strong>writing</strong></em> anything.</p>
<p>Hopefully I'll get around to putting some content on here one day.  If not, it was a fun experiment anyway. :)</p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>