{
    "version": "https://jsonfeed.org/version/1",
    "title": "tobyx.com",
    "home_page_url": "https://tobyx.com/",
    "feed_url": "https://tobyx.com/feed.json",
    "description": "tobyx.com is the digital refuge of Tobias Horvath. He is a visual effects artist and writer based in Berlin, Germany. He likes typography and attempts to write more than one novel in his spare time.",
    "icon": "https://tobyx.com/apple-touch-icon.png",
    "favicon": "https://tobyx.com/favicon.ico",
    "author": { "name": "Tobias Horvath", "url": "https://tobyx.com" },
    "expired": false,
    "items": [
    
        {
            "id": "https://tobyx.com/2025/in-the-year-2025",
            "title": "In The Year 2025",
            "content_html": "<p>A year is Earth’s annual marathon around the Sun, clocking 940 million kilometers at 30 kilometers per second. Gravity keeps us tethered, inertia keeps us moving, and Earth spends the journey tilting and spinning like it’s auditioning for a dance competition. This relentless motion doesn’t just define time—it fuels life. Seasons shift, cycles renew, and we’re reminded that even the most “stable” systems are really just organized chaos in constant motion.</p><p>It’s a balancing act on a cosmic scale: axial tilts, elliptical orbits, and gravitational nudges syncing Earth’s rhythm with the Sun’s celestial calendar. Sure, the planet wobbles and the equinoxes drift, but somehow, it all works out. Meanwhile, humans—sitting on this spinning rock—like to think we’ve got it all figured out. Spoiler: we don’t.</p><p>For all our hubris, we imagine our actions rippling out to the cosmic scale. The truth is less flattering. Our grandest achievements amount to self-sabotage: destabilizing ecosystems, driving countless species to extinction, and reshaping our planet’s surface for worse. The Earth? She doesn’t care. Burn it, freeze it, flood it—she’ll keep spinning, unbothered, for millions of years. We’ll just be the fleeting footnote labeled “Experiment Gone Wrong.”</p><p><strong>But here’s the kicker: knowing this doesn’t depress me—it motivates me.</strong> It’s oddly comforting to know the universe doesn’t revolve around us—literally or figuratively. <strong>It frees us up to focus on what actually matters: making life less terrible for each other.</strong> That’s all we can do. Nothing more, nothing less. So for 2025, how about we try something radical? Let’s be decent. Kindness isn’t just text on a postcard you send—it’s gravity for humans. It pulls us together, grounds us, and keeps us moving forward.</p><p>And let’s not stop there. Kindness alone isn’t enough. Racism, transphobia, inequality—they’re not cosmic inevitabilities; they’re human-made disasters, like pineapple on pizza but significantly worse. These problems persist because we let them. No, we don’t just let them, we pretend like they don’t exist. The more privileges you have, the easier it is to just sit in your happy bubble and ignore the injustices those less privileged than you suffer. Speak up. Amplify marginalized voices. Call out bigotry, even when it’s uncomfortable. Especially when it’s uncomfortable. Justice isn’t some lofty ideal—it’s the bare minimum we owe each other. Silence is complicity.</p><p>I want 2025 to be a year of growth. Not the “New Year, New Me” kind of growth, but the messy, uncomfortable, do-the-work kind. It will be fucking hard for me, and that’s good. Last year, I was dumbfounded to realize just how bigoted I was myself and how much of it remains in me. It’s hard work to make things better. Growth that happens when we build better communities, challenge our biases, and stop pretending that “neutrality” is a valid stance when people’s lives are on the line.</p><p><strong>Kindness gets us started. Justice keeps us going. Let’s make 2025 the year we all step up, do more, and suck a whole lot less.</strong></p>",
            "url": "https://tobyx.com/2025/in-the-year-2025",
            
            "date_published": "2025-01-06T11:30:00+00:00",
            "date_modified": "2025-01-06T11:30:00+00:00",
            "author": {
                "name": ""
            }
        },
    
        {
            "id": "https://tobyx.com/2024/analytics",
            "title": "Privacy Preserving Analytics with Plausible",
            "content_html": "<p>Google Analytics is free because you’re giving your visitors’ data over to Google. The GDPR has outlawed using it without getting prior explicit consent. How do you get that consent? It’s complicated.</p><p>I’m not going to have cookie banners. They are ugly, intrusive, and just leave a lingering feeling of “Why do they need my data? I just want to read this stupid blog post.”</p><p>I feel very uncomfortable giving away personally identifiable information to Google, who could use this data to track users across the web. <a href=\"https://blog.google/around-the-globe/google-europe/google-analytics-facts/\">Google says they are not doing this</a>, but their incentives are just too high for me to be ok with it. I don’t want to feel filthy.</p><p>Companies already <a href=\"https://noyb.eu/en/noyb-win-first-major-fine-eu-1-million-using-google-analytics\">had to pay up for the use of Google Analytics and the subsequent transfer of user data across EU borders</a> and rightfully so. This was <strong>with</strong> the use of cookie banners if I understand correctly. It appears even consent doesn’t make this behavior ok.</p><p>So I had to find a way to do analytics right. With my limited use of social media (for reasons I should elaborate on at some point) and the avoidance of far-right platforms like X, writing things on here is like writing into the void. I like to know if an article is performing well, but do so without gaining any knowledge on the person visiting this site.</p><p>Before this, I had a simple measurement of page views extracted from nginx access logs. My script would run once a month and just write a number into a text file. I’m not a developer so the script was hacked together, but it worked and at least gave me some data. Data I needed because I licensed fonts which need me to calculate monthly page views. This number was very inaccurate though because so many hits to this site are bots and intrusion attempts (it’s insane how many folks out there are probing for WordPress installations).</p><p>I could have self-hosted a tool like <a href=\"https://matomo.org\">Matomo</a> but this would add overhead to my hosting setup that I simply don’t accept. This site is <a href=\"/2015/jekyll-vs-world\">static for a reason</a>, and hosting a PHP application with potential security issues increases my attack surface substantially.</p><p>I could have analyzed server log files with a tool like <a href=\"https://goaccess.io\">GoAccess</a> but this turned out to be clumsy. My nginx installation is writing log files with IP addresses anonymized so I don’t store personally identifiable information in the file system at any point in time. This makes the analytics very inaccurate, but most of all, GoAccess doesn’t filter out intrusion attempts and bots effectively. It was a nice idea, but yielded unusable results for me personally.</p><p><strong>I ended up with <a href=\"https://plausible.io\">Plausible.io</a></strong>. Plausible is an EU company with <a href=\"https://plausible.io/data-policy\">just the right stance on privacy</a>. In order to differentiate unique visitors, a daily rotating salt is used together with the domain name, IP address, and user agent in order to create a hash and only this hash is stored. After a day, each returning visitor would count as unique again, so there’s no way to identify returning users past 24 hours.</p><h2 id=\"a-battle-against-technology\">A battle against technology</h2><p>Look. When you use the Internet, you leak personally identifiable information left and right, starting with your IP address. This differs by access method, country, and device to some extent, and it can be mitigated by using tools like <a href=\"https://support.apple.com/en-us/102602\">iCloud Private Relay</a>. But even that doesn’t protect you fully. Are you logged into a Meta or Google account? Nice. Every site using social or login integrations with Facebook, Instagram, and Google now allows those companies to merge together your browsing habits, despite you using iCloud Private Relay or a VPN<sup id=\"fnref:1\"><a href=\"#fn:1\" class=\"footnote\" rel=\"footnote\" role=\"doc-noteref\">1</a></sup>.</p><p>That’s how the Internet was built. The GDPR doesn’t protect you from the reality of technology, nor does it demand that the technology changes. You have to protect yourself. GDPR isn’t going to do it. It’s a legal framework that over decades will fine big companies some pocket change here and there, but your privacy is still on you. Sorry.</p><p>Every single website and service out there can do their part in making sure privacy rights are protected. You have the choice of who you do business with. Start small. Look for those who do right. <a href=\"https://ar.al/2020/08/07/what-is-the-small-web/\">We need more of the Small Web</a> and less of the Big Web.</p><p>I’m just trying to do my part, and with intent. The next step for me is moving off of a bigger cloud provider like Linode (I am hosting in Frankfurt, Germany) to a smaller hosting company that wasn’t <a href=\"https://techhq.com/2023/02/linode-becomes-akamai-connected-cloud/\">slurped up by Akamai</a>. Small steps. I’ll probably write about it when that is done.</p><p>Oh, no one paid me to write this by the way.</p><div class=\"footnotes\" role=\"doc-endnotes\">  <ol>    <li id=\"fn:1\">      <p>A VPN doesn’t protect you at all. As long as you’re logged into a service by big tech, they still know who you are. All that changes is your IP address. Sure, your Internet Service Provider now knows less about you, but those are not your prime enemy. Google and Meta are. A VPN does not protect you from them. <a href=\"#fnref:1\" class=\"reversefootnote\" role=\"doc-backlink\">&#x21A9;&#xFE0E;</a></p>    </li>  </ol></div>",
            "url": "https://tobyx.com/2024/analytics",
            
            "date_published": "2024-11-11T11:15:00+00:00",
            "date_modified": "2024-11-11T11:15:00+00:00",
            "author": {
                "name": ""
            }
        },
    
        {
            "id": "https://tobyx.com/2024/defaults",
            "title": "Defaults",
            "content_html": "<p><em>Update: Apple Passwords just screwed up badly, so I went back to 1Password 😅 There will be a blog post on this soon.</em></p><p>Now that my blog is alive again, I decided to follow all those other bloggers with a <strong>list of defaults (and not so defaults)</strong> on <a href=\"https://defaults.rknight.me\">App Defaults</a>, inspired by the <a href=\"https://listen.hemisphericviews.com/097\">97th episode of the Hemispheric Views podcast</a>.</p><p>I’m a Mac user. I won’t be linking out to the Apple apps listed below that come preinstalled. Some entries have footnotes. 😄</p><p>📨 <strong>Mail Client</strong>: Apple Mail<sup id=\"fnref:1\"><a href=\"#fn:1\" class=\"footnote\" rel=\"footnote\" role=\"doc-noteref\">1</a></sup><br />📮 <strong>Mail Server</strong>: <a href=\"https://www.fastmail.com\">Fastmail</a><br />📝 <strong>Notes</strong>: Apple Notes<br />✅ <strong>To-Do</strong>: <a href=\"https://culturedcode.com/things/\">Things</a><br />📷 <strong>iPhone Photo Shooting</strong>: <a href=\"https://halide.cam\">Halide</a><br />🟦 <strong>Photo Management</strong>: Apple Photos<br />📸 <strong>Photo Editing</strong>: Apple Photos<sup id=\"fnref:2\"><a href=\"#fn:2\" class=\"footnote\" rel=\"footnote\" role=\"doc-noteref\">2</a></sup><br />📆 <strong>Calendar</strong>: Apple Calendar<br />📁 <strong>Cloud File Storage</strong>: iCloud Drive (with <a href=\"/2024/icloud-advanced-data-protection\">Advanced Data Protection</a>)<br />📖 <strong>RSS</strong>: <a href=\"https://reederapp.com\">Reeder</a><br />📇 <strong>Contacts</strong>: Apple Contacts<sup id=\"fnref:3\"><a href=\"#fn:3\" class=\"footnote\" rel=\"footnote\" role=\"doc-noteref\">3</a></sup><br />🌐 <strong>Browser</strong>: Apple Safari<br />💬 <strong>Chat</strong>: Apple iMessage and <a href=\"https://signal.org\">Signal</a><br />🔖 <strong>Bookmarks</strong>: Safari Bookmarks<br />📑 <strong>Read It Later</strong>: <a href=\"https://reederapp.com\">Reeder</a> and Safari Reading List<br />📜 <strong>Word Processing</strong>: <a href=\"https://ia.net/writer\">iA Writer</a> and Apple Pages<sup id=\"fnref:4\"><a href=\"#fn:4\" class=\"footnote\" rel=\"footnote\" role=\"doc-noteref\">4</a></sup><br />📈 <strong>Spreadsheets</strong>: Apple Numbers<br />📊 <strong>Presentations</strong>: Apple Keynote<sup id=\"fnref:5\"><a href=\"#fn:5\" class=\"footnote\" rel=\"footnote\" role=\"doc-noteref\">5</a></sup><br />✍🏻 <strong>Writing Books</strong>: <a href=\"https://ulysses.app\">Ulysses</a><br />🛒 <strong>Shopping Lists</strong>: <a href=\"https://culturedcode.com/things/\">Things</a><br />🍴 <strong>Meal Planning</strong>: <a href=\"https://mela.recipes\">Mela</a><br />💰 <strong>Budgeting and Personal Finance</strong>: <a href=\"https://www.bunq.com\">bunq</a><br />📰 <strong>News</strong>: -<br />🎵 <strong>Music</strong>: Apple Music<br />🎤 <strong>Podcasts</strong>: <a href=\"https://overcast.fm\">Overcast</a><br />📚 <strong>Books</strong>: Apple Books<sup id=\"fnref:6\"><a href=\"#fn:6\" class=\"footnote\" rel=\"footnote\" role=\"doc-noteref\">6</a></sup><br />🔐 <strong>Password Management</strong>: <a href=\"https://1password.com/\">1Password</a><sup id=\"fnref:7\"><a href=\"#fn:7\" class=\"footnote\" rel=\"footnote\" role=\"doc-noteref\">7</a></sup><br />🔍 <strong>Search Engine</strong>: Google<sup id=\"fnref:8\"><a href=\"#fn:8\" class=\"footnote\" rel=\"footnote\" role=\"doc-noteref\">8</a></sup><br />🍿 <strong>Movie Tracking</strong>: <a href=\"https://www.caseyliss.com/2023/8/7/callsheet\">Callsheet</a><br />📝 <strong>Blogging Platform</strong>: <a href=\"https://jekyllrb.com\">Jekyll</a><br />🚚 <strong>FTP</strong>: <a href=\"https://panic.com/transmit/\">Transmit</a><br />🧑🏻‍💻 <strong>Code</strong>: <a href=\"https://nova.app\">Nova</a><br />🎬 <strong>Visual Effects and Finishing</strong>: <a href=\"https://www.autodesk.com/products/flame/overview\">Autodesk Flame</a><sup id=\"fnref:9\"><a href=\"#fn:9\" class=\"footnote\" rel=\"footnote\" role=\"doc-noteref\">9</a></sup></p><div class=\"footnotes\" role=\"doc-endnotes\">  <ol>    <li id=\"fn:1\">      <p>Sometimes I use the Fastmail web app which I’ve installed as a <a href=\"https://support.apple.com/en-us/104996\">Safari Web App</a>. <a href=\"#fnref:1\" class=\"reversefootnote\" role=\"doc-backlink\">&#x21A9;&#xFE0E;</a></p>    </li>    <li id=\"fn:2\">      <p>I have <a href=\"https://www.adobe.com/products/photoshop.html\">Adobe Photoshop</a> licensed due to my profession and if Apple Photos is not up to the task, I will default to Photoshop. <a href=\"#fnref:2\" class=\"reversefootnote\" role=\"doc-backlink\">&#x21A9;&#xFE0E;</a></p>    </li>    <li id=\"fn:3\">      <p>iCloud + Fastmail on the server side with <a href=\"https://support.apple.com/guide/contacts/link-contacts-from-different-accounts-adrb33f38d93/mac\">Linked Contacts</a>) active. This quite fascinatingly works and keeps all changes to contacts up to date across servers. <a href=\"#fnref:3\" class=\"reversefootnote\" role=\"doc-backlink\">&#x21A9;&#xFE0E;</a></p>    </li>    <li id=\"fn:4\">      <p>On the publishing side, I need to revert to <a href=\"https://www.microsoft.com/en/microsoft-365/word\">Microsoft Word</a> unfortunately, because it’s the industry standard and people expect <em>Track Changes</em> to work. <a href=\"#fnref:4\" class=\"reversefootnote\" role=\"doc-backlink\">&#x21A9;&#xFE0E;</a></p>    </li>    <li id=\"fn:5\">      <p>This would be <a href=\"https://ia.net/presenter\">iA Presenter</a> if I held presentations more often. What a nice app. <a href=\"#fnref:5\" class=\"reversefootnote\" role=\"doc-backlink\">&#x21A9;&#xFE0E;</a></p>    </li>    <li id=\"fn:6\">      <p>I simply can’t stand Kindle anymore. The UI on their devices, the reading experience, and the horrendous companion apps on iOS and Android distract me from reading the actual books. At least the text looks nice in Apple Books, where I’m using the Paper Theme and Iowan as my font. <a href=\"#fnref:6\" class=\"reversefootnote\" role=\"doc-backlink\">&#x21A9;&#xFE0E;</a></p>    </li>    <li id=\"fn:7\">      <p><del>Starting with macOS Sequoia and iOS 18, I was able to fully move from 1Password to Apple Passwords.</del> Went back to 1Password within 2 weeks, because Apple Passwords screwed up. Blog post coming! <a href=\"#fnref:7\" class=\"reversefootnote\" role=\"doc-backlink\">&#x21A9;&#xFE0E;</a></p>    </li>    <li id=\"fn:8\">      <p>I’m sorry, everything else is just very bad in 🇩🇪. <a href=\"#fnref:8\" class=\"reversefootnote\" role=\"doc-backlink\">&#x21A9;&#xFE0E;</a></p>    </li>    <li id=\"fn:9\">      <p>Don’t look at the price. <a href=\"#fnref:9\" class=\"reversefootnote\" role=\"doc-backlink\">&#x21A9;&#xFE0E;</a></p>    </li>  </ol></div>",
            "url": "https://tobyx.com/2024/defaults",
            
            "date_published": "2024-11-10T00:00:00+00:00",
            "date_modified": "2024-11-14T08:20:00+00:00",
            "author": {
                "name": ""
            }
        },
    
        {
            "id": "https://tobyx.com/2024/icloud-advanced-data-protection",
            "title": "Advanced Data Protection for iCloud",
            "content_html": "<p>iCloud is the only major storage provider with the option for zero knowledge.</p><p>All data is encrypted in transit and on the server; the difference is who has access to it all. With Advanced Data Protection (ADP), Apple takes itself out of the equation for most services. With standard data protection, several services remain recoverable (and accessible) by Apple.</p><p>So why doesn’t Apple use Advanced Data Protection by default? It would be a customer service nightmare. Only activate it if you know what you’re doing. If you lose access to your account, you need to use the recovery contact or recovery key <em>together</em> with a trusted device passcode or iCloud account password to get your data back. If none of those are available, Apple will be able to restore access to your account, <strong>but most of the data will be lost. Irrevocably.</strong></p><p>But I still implore you to use Advanced Data Protection if you know what you’re doing. It is a huge boost to privacy, especially if you store a lot of data in iCloud.</p><p>Here’s a list of iCloud services and how Apple treats their encryption as of November 2024:</p><table>  <thead>    <tr>      <th>iCloud Service</th>      <th>Standard data protection</th>      <th>Advanced Data Protection</th>    </tr>  </thead>  <tbody>    <tr>      <td>iCloud Mail</td>      <td>Apple has keys</td>      <td>Apple has keys</td>    </tr>    <tr>      <td>Contacts</td>      <td>Apple has keys</td>      <td>Apple has keys</td>    </tr>    <tr>      <td>Calendars</td>      <td>Apple has keys</td>      <td>Apple has keys</td>    </tr>    <tr>      <td>iCloud Backup (incl. Messages)</td>      <td>Apple has keys</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>iCloud Drive</td>      <td>Apple has keys</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Photos</td>      <td>Apple has keys</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Notes</td>      <td>Apple has keys</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Reminders</td>      <td>Apple has keys</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Safari Bookmarks</td>      <td>Apple has keys</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Siri Shortcuts</td>      <td>Apple has keys</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Voice Memos</td>      <td>Apple has keys</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Wallet passes</td>      <td>Apple has keys</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Freeform</td>      <td>Apple has keys</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Passwords and Keychain</td>      <td>Only you have keys (E2E)</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Health data</td>      <td>Only you have keys (E2E)</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Journal data</td>      <td>Only you have keys (E2E)</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Home data</td>      <td>Only you have keys (E2E)</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Messages in iCloud</td>      <td>Only you have keys (E2E)</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Payment information</td>      <td>Only you have keys (E2E)</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Apple Card transactions</td>      <td>Only you have keys (E2E)</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Maps</td>      <td>Only you have keys (E2E)</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Keyboard vocabulary</td>      <td>Only you have keys (E2E)</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Safari</td>      <td>Only you have keys (E2E)</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Screen Time</td>      <td>Only you have keys (E2E)</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Siri information</td>      <td>Only you have keys (E2E)</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Wi-Fi passwords</td>      <td>Only you have keys (E2E)</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>W1 and H1 Bluetooth keys</td>      <td>Only you have keys (E2E)</td>      <td>Only you have keys (E2E)</td>    </tr>    <tr>      <td>Memoji</td>      <td>Only you have keys (E2E)</td>      <td>Only you have keys (E2E)</td>    </tr>  </tbody></table><p>The more control users have, the more responsibility they must bear.</p><h2 id=\"how-does-web-access-on-icloudcom-work-with-adp-on\">How does web access on icloud.com work with ADP on?</h2><p>Every time you attempt to access one of the E2E-encrypted services above in a web browser on icloud.com, Apple will send a request to your trusted devices, where you’ll have the option to approve the request. The device will then generate temporary keys for the browser session to decrypt and show your data.</p><h2 id=\"recovery-options\">Recovery Options</h2><p>An alternate recovery option is required in order to activate Advanced Data Protection. That’s either a recovery contact or a recovery key.</p><p>With a recovery contact, a trusted friend or family member gets the ability to receive a recovery code for you if you need one. Apple will not save any information on who your recovery contacts are, so remember who you picked. Your contact does not get access to your data, they will only get the code they can hand over to you if you request one.</p><p>With a recovery key, you’ll get a 28-character code to store in a safe place. This code can be used to regain access to your account and E2E encrypted data. If you turn on the recovery key option, the regular account recovery process from Apple will no longer be available. <strong>Store the key in a safe place!</strong></p><p>If you’re interested in reading more details about iCloud security, head on over to <a href=\"https://support.apple.com/en-us/102651\">iCloud data security overview (Apple Support)</a>.</p><p>And here’s Apple’s overview on <a href=\"https://support.apple.com/en-us/108756\">How to turn on Advanced Data Protection for iCloud (Apple Support)</a>.</p>",
            "url": "https://tobyx.com/2024/icloud-advanced-data-protection",
            
            "date_published": "2024-11-09T20:30:00+00:00",
            "date_modified": "2024-11-09T20:30:00+00:00",
            "author": {
                "name": ""
            }
        },
    
        {
            "id": "https://tobyx.com/2020/dark-mode",
            "title": "Dark Mode for All the Things",
            "content_html": "<figure>  <img src=\"https://tobyx.com/img/2020/darkmode-01.jpg\" alt=\"tobyx.com split of light and dark mode\" class=\"anchor-left\" />  </figure><p>Before the introduction of Dark Mode support in macOS Mojave in 2018, many websites had offered manual Dark Mode support with a JavaScript toggle. In principle, this method implemented a “Dark Mode Switch” somewhere in the UI. On click, the site would load a Dark Mode CSS alternative to switch colors using JavaScript. Persistence was offered through setting a cookie to remember the setting.</p><p>This method used to be the most elegant way to offer users a dark theme since you couldn’t store your preference anywhere globally. The preference needed to be saved on a per-site basis, and while setting a cookie for this has no real privacy implications, EU law disagrees with that<sup id=\"fnref:1\"><a href=\"#fn:1\" class=\"footnote\" rel=\"footnote\" role=\"doc-noteref\">1</a></sup> and makes the solution in the very least cumbersome to deal with.</p><p>macOS added support for <code class=\"language-plaintext highlighter-rouge\">prefers-color-scheme</code>, because now there was a reason to. You could set your preference for a dark theme across the operating system and when switching, all apps with support as well as Safari and all currently loaded websites would switch accordingly.</p><p>The code for this is deceptively simple:</p><figure class=\"highlight\"><pre><code class=\"language-css\" data-lang=\"css\"><span class=\"k\">@media</span> <span class=\"p\">(</span><span class=\"n\">prefers-color-scheme</span><span class=\"p\">:</span> <span class=\"n\">dark</span><span class=\"p\">)</span> <span class=\"p\">{</span>  <span class=\"c\">/* add your dark theme changes here */</span><span class=\"p\">}</span></code></pre></figure><h2 id=\"things-to-consider\">Things to Consider</h2><p>It’s important to check your light and dark themes thoroughly. I personally use a light theme when I’m in well-lit rooms during the day and a dark theme later in the day when my surroundings are darker.</p><p>If you find images on your site to be too bright in dark mode, you can swap them out in your dark theme. If your background color is neutral (all RGB channels with the same value), you can also try reducing the <code class=\"language-plaintext highlighter-rouge\">opacity</code> of the respective <code class=\"language-plaintext highlighter-rouge\">img</code> elements a bit. With a neutral background color, you’ll avoid changing the overall chroma of the images.</p><p>In the end, all issues with the JavaScript and cookie-preference method aside, you could still offer a manual switch for users. You may consider only showing the UI element for this on browsers not supporting <code class=\"language-plaintext highlighter-rouge\">prefers-color-scheme</code>.</p><h2 id=\"apple-platforms\">Apple Platforms</h2><p>macOS, iOS, and iPadOS all support Dark Mode system-wide and Safari supports CSS @media query <code class=\"language-plaintext highlighter-rouge\">prefers-color-scheme</code>.</p><p><a href=\"https://support.apple.com/en-us/HT208976\">macOS added Dark Mode support</a> with macOS Mojave (10.14) in 2018. <a href=\"https://support.apple.com/en-us/HT210332\">iOS added Dark Mode support</a> with iOS 13.0 in 2019. iPadOS supports it just the same since the name change with iPadOS 13.1.</p><h2 id=\"microsoft-windows-and-edge\">Microsoft Windows and Edge</h2><p>Microsoft Edge supports Dark Mode. If you <a href=\"https://blogs.windows.com/windowsexperience/2016/08/08/windows-10-tip-personalize-your-pc-by-enabling-the-dark-theme/\">set your Windows to Dark Mode</a>, Microsoft Edge will by default use that preference to also interpet <code class=\"language-plaintext highlighter-rouge\">prefers-color-scheme</code> appropriately.</p><p>In addition to that, users can <a href=\"https://blogs.windows.com/windowsexperience/2016/09/26/windows-10-tip-enable-the-dark-theme-in-microsoft-edge/\">enable Dark Mode only for Edge</a> directly in the browser.</p><h2 id=\"android-and-google-chrome\">Android and Google Chrome</h2><p>Android <a href=\"https://developer.android.com/guide/topics/ui/look-and-feel/darktheme\">supports a system-wide Dark Theme since Android 10</a> in 2019 and Google’s mobile Chrome browser will adhere to that preference.</p><p>Google Chrome on desktop platforms will adhere to system-wide settings of macOS and Windows to also offer Dark Mode @media query support according to user preference.</p><div class=\"footnotes\" role=\"doc-endnotes\">  <ol>    <li id=\"fn:1\">      <p>The GDPR mandates that cookies may only be stored on a user’s computer after specific consent is given, except for strictly mandatory cookies. While most law experts agree on e.g. cookies for load balancer operation or shopping carts being required, thus requiring no prior consent, the discussion is a bit more difficult on topics like setting a theme preference. Since any data stored to differentiate a user’s preference could potentially be labelled “personally identifiable information”, we’re in for a wild ride. The biggest drawback of these privacy laws is big corporations continuing with what they’re doing, gaining a competetive advantage over those who can’t simply weather lawsuits coming their way. <a href=\"#fnref:1\" class=\"reversefootnote\" role=\"doc-backlink\">&#x21A9;&#xFE0E;</a></p>    </li>  </ol></div>",
            "url": "https://tobyx.com/2020/dark-mode",
            
            "date_published": "2020-07-18T00:00:00+00:00",
            "date_modified": "2020-07-18T00:00:00+00:00",
            "author": {
                "name": ""
            }
        },
    
        {
            "id": "https://tobyx.com/2016/jekyll-rsync",
            "title": "Using rsync to Deploy Your Jekyll Site",
            "content_html": "<figure>  <img src=\"https://tobyx.com/img/2016/jekyll-rsync-01.jpg\" alt=\"Image of the macOS Dock\" />  <figcaption>No one uploads their stuff via FTP anymore. Or at least they shouldn't.</figcaption></figure><p><strong>When you locally build your site with Jekyll</strong>, you end up with a finished generated site in the <code class=\"language-plaintext highlighter-rouge\">_site</code> folder that you can simply upload to your web host.</p><p>But instead of using SFTP to do this, we’ll be making it a little fancier. Let’s assume you have your host ready and can SSH into it.</p><h2 id=\"deploying-to-a-vps\">Deploying to a VPS</h2><p>The following script will use <code class=\"language-plaintext highlighter-rouge\">rsync</code> to mirror your local folder to your remote server. <code class=\"language-plaintext highlighter-rouge\">rsync</code> is quite intelligent in doing so, only transferring the changed portions of your site on subsequent commits.</p><p>Make sure to edit the variables above. <strong>Note:</strong> <code class=\"language-plaintext highlighter-rouge\">rsync</code> will in fact wipe the destination folder clean and only leave the mirrored content there so double-check the path.</p><p>I have had issues with wrong permissions on files and folders on the destination host, so you might want to adjust permissions at the target location. This requires a newer version of <code class=\"language-plaintext highlighter-rouge\">rsync</code> that’s not available on a default installation of macOS.</p><p>Simply install a more current version of <code class=\"language-plaintext highlighter-rouge\">rsync</code> with:</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">brew <span class=\"nb\">install </span>rsync</code></pre></figure><p>The user-installed version should automatically get priority.</p><p>The script will attempt to change permissions on files and folders accordingly.</p><p>If your <code class=\"language-plaintext highlighter-rouge\">sshuser</code> is unprivileged—he definitely shouldn’t be root—, make sure that the content in your web root is owned by him, the group can stay at <code class=\"language-plaintext highlighter-rouge\">www-data</code> or whichever group your web server belongs to. The script will also leave hidden files and directories alone and not change or delete them, ensuring compatibility with the web root functionality of <code class=\"language-plaintext highlighter-rouge\">letsencrypt</code>.</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\"><span class=\"c\">#!/bin/sh</span><span class=\"c\"># -----------------------------------------------------------</span><span class=\"c\"># commit-vps.sh</span><span class=\"c\"># -----------------------------------------------------------</span><span class=\"c\"># Variables</span><span class=\"nv\">localjekyll</span><span class=\"o\">=</span><span class=\"s2\">\"/Users/tobyx/websites/tobyx.com/current\"</span><span class=\"nv\">remotewebroot</span><span class=\"o\">=</span><span class=\"s2\">\"/var/www/tobyx.com/public_html\"</span><span class=\"nv\">instancehost</span><span class=\"o\">=</span><span class=\"s2\">\"yourinstance.example.com\"</span><span class=\"nv\">sshuser</span><span class=\"o\">=</span><span class=\"s2\">\"admin\"</span><span class=\"nv\">sshport</span><span class=\"o\">=</span><span class=\"s2\">\"22\"</span><span class=\"nv\">sshidentity</span><span class=\"o\">=</span><span class=\"s2\">\"~/.ssh/your-private-key.key\"</span><span class=\"c\"># Execution</span><span class=\"nb\">cd</span> <span class=\"nv\">$localjekyll</span><span class=\"c\"># bundle install</span>bundle <span class=\"nb\">exec </span>jekyll build <span class=\"nt\">--config</span><span class=\"o\">=</span>_config.yml,_config_production.yml<span class=\"nb\">echo</span> <span class=\"s2\">\"rsync to SSH host </span><span class=\"nv\">$instancehost</span><span class=\"s2\"> ...\"</span>rsync <span class=\"nt\">-vrh</span> <span class=\"nt\">-e</span> <span class=\"s2\">\"ssh -p </span><span class=\"nv\">$sshport</span><span class=\"s2\"> -i </span><span class=\"nv\">$sshidentity</span><span class=\"s2\">\"</span> <span class=\"nt\">--exclude</span> <span class=\"s2\">\".*\"</span> <span class=\"nt\">--delete-after</span> <span class=\"se\">\\</span>  <span class=\"nt\">--chmod</span><span class=\"o\">=</span><span class=\"nv\">Du</span><span class=\"o\">=</span>rwx,Dg<span class=\"o\">=</span>rx,Do<span class=\"o\">=</span>rx,Fu<span class=\"o\">=</span>rw,Fg<span class=\"o\">=</span>r,Fo<span class=\"o\">=</span>r <span class=\"se\">\\</span>  <span class=\"nv\">$localjekyll</span>/_site/ <span class=\"nv\">$sshuser</span>@<span class=\"nv\">$instancehost</span>:<span class=\"nv\">$remotewebroot</span><span class=\"nb\">echo</span> <span class=\"s2\">\"SSH connection closed. Done. Committed.\"</span></code></pre></figure><h2 id=\"other-methods\">Other methods</h2><p>Check out <a href=\"http://jekyllrb.com/docs/deployment-methods/\">other methods of Jekyll deployment</a>.</p>",
            "url": "https://tobyx.com/2016/jekyll-rsync",
            
            "date_published": "2016-12-11T00:00:00+00:00",
            "date_modified": "2020-02-02T12:00:00+00:00",
            "author": {
                "name": ""
            }
        },
    
        {
            "id": "https://tobyx.com/2016/aws-ssh-ports",
            "title": "On-demand Opening of SSH Ports on AWS",
            "content_html": "<figure>  <img src=\"https://tobyx.com/img/2016/aws-ssh-ports-01.jpg\" alt=\"Image of BBEdit running on a MacBook Pro with the article's script loaded\" />  <figcaption>Scripting helps automate mundane things and make your life wonderful.</figcaption></figure><p><em>Update 19-03-01: The script has been amended to support IPv6 as well and will allow access coming from your public IPv4 and IPv6 address simultaneously. I will also use CIDR notation for letting both addresses in, which is optional for IPv4 but mandatory for IPv6.</em></p><p><em>Update 24-11-07: I am no longer running on <abbr title=\"Amazon Web Services\">AWS</abbr>, and this script will not be updated. It might still work.</em></p><hr /><p><strong>While other cloud providers usually force you</strong> to leave SSH open, restrict access with your own firewalls, or set up IP ranges from which access is possible, <a href=\"https://aws.amazon.com\"><abbr title=\"Amazon Web Services\">AWS</abbr></a> has the major advantage of security groups<sup id=\"fnref:1\"><a href=\"#fn:1\" class=\"footnote\" rel=\"footnote\" role=\"doc-noteref\">1</a></sup>. You can stop any traffic dead in its tracks before your server has to deal with it.</p><p>You can configure Security Groups in a few ways:</p><ul>  <li>Through the <abbr title=\"Amazon Web Services\">AWS</abbr> web interface.</li>  <li>With the <a href=\"https://aws.amazon.com/cli/\"><abbr title=\"Amazon Web Services\">AWS</abbr> CLI</a> command-line interface.</li>  <li>Not at all.</li></ul><p>Some people just leave their SSH port open. This is arguably ok with public key authentication in place, but being able to prevent automated login attempts from around the world gives you peace of mind. You can also restrict access to your static IP or IP ranges, but people on ISPs with dynamically changing IP addresses are out of luck.</p><h2 id=\"pre-requisite-install-the-aws-cli\">Pre-requisite: Install the <abbr title=\"Amazon Web Services\">AWS</abbr> CLI</h2><p>The <a href=\"https://aws.amazon.com/cli/\"><abbr title=\"Amazon Web Services\">AWS</abbr>-CLI website</a> has information on how to install the CLI, with most Linux distros having packages for it for an easy install. On macOS, you can go the <a href=\"https://brew.sh\">Homebrew</a> route.</p><p>If you haven’t already done so, install Homebrew. Homebrew is a package manager for all the developey things your Mac lacks—which is usually not much compared to Windows, but still a lot for a developer to be sad. Use Homebrew and its version of Python.</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">/usr/bin/ruby <span class=\"nt\">-e</span> <span class=\"s2\">\"</span><span class=\"si\">$(</span>curl <span class=\"nt\">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/master/install<span class=\"si\">)</span><span class=\"s2\">\"</span></code></pre></figure><p>Make sure you know what this command does, <a href=\"https://brew.sh\">refer to the Homebrew website</a>.</p><p>Now we install Python and the <abbr title=\"Amazon Web Services\">AWS</abbr> CLI.</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">brew <span class=\"nb\">install </span>pythonpip <span class=\"nb\">install</span> <span class=\"nt\">--upgrade</span> <span class=\"nt\">--user</span> awscli</code></pre></figure><h2 id=\"scripting\">Scripting</h2><p>I’m using a script to connect to my <abbr title=\"Amazon Web Services\">AWS</abbr> instances. The script will open an SSH port in the associated Security Group, connect me via SSH, and when the connection ends or disconnects, remove that rule again, closing access to the instance off.</p><p>Here’s the script <code class=\"language-plaintext highlighter-rouge\">connect-aws.sh</code>:</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\"><span class=\"c\">#!/bin/bash</span><span class=\"c\"># -----------------------------------------------------------</span><span class=\"c\"># connect-aws.sh</span><span class=\"c\"># -----------------------------------------------------------</span><span class=\"c\"># Variables</span><span class=\"nv\">instancehost</span><span class=\"o\">=</span><span class=\"s2\">\"yourhost.example.com\"</span><span class=\"nv\">sshuser</span><span class=\"o\">=</span><span class=\"s2\">\"admin\"</span><span class=\"nv\">sshport</span><span class=\"o\">=</span><span class=\"s2\">\"22\"</span><span class=\"nv\">sshidentity</span><span class=\"o\">=</span><span class=\"s2\">\"~/.ssh/yourprivatekey.key\"</span><span class=\"nv\">securitygroup</span><span class=\"o\">=</span><span class=\"s2\">\"sg-1234a12345b1cd12e\"</span><span class=\"nv\">region</span><span class=\"o\">=</span><span class=\"s2\">\"eu-central-1\"</span><span class=\"c\"># Execution</span><span class=\"nv\">ip</span><span class=\"o\">=</span><span class=\"sb\">`</span>curl <span class=\"nt\">-s</span> https://api.ipify.org<span class=\"sb\">`</span><span class=\"nv\">ip6</span><span class=\"o\">=</span><span class=\"sb\">`</span>curl <span class=\"nt\">-s</span> https://api6.ipify.org<span class=\"sb\">`</span><span class=\"k\">if</span> <span class=\"o\">[</span> <span class=\"s2\">\"</span><span class=\"nv\">$ip</span><span class=\"s2\">\"</span> <span class=\"o\">!=</span> <span class=\"s2\">\"</span><span class=\"nv\">$ip6</span><span class=\"s2\">\"</span> <span class=\"o\">]</span><span class=\"p\">;</span> <span class=\"k\">then  </span><span class=\"nv\">ip6avail</span><span class=\"o\">=</span>1<span class=\"k\">fi</span><span class=\"nb\">echo</span> <span class=\"s2\">\"Current IP address: </span><span class=\"nv\">$ip</span><span class=\"s2\">\"</span><span class=\"k\">if</span> <span class=\"o\">[</span> <span class=\"nv\">$ip6avail</span> <span class=\"o\">]</span><span class=\"p\">;</span> <span class=\"k\">then  </span><span class=\"nb\">echo</span> <span class=\"s2\">\"Current IPv6 address: </span><span class=\"nv\">$ip6</span><span class=\"s2\">\"</span><span class=\"k\">else  </span><span class=\"nb\">echo</span> <span class=\"s2\">\"IPv6 is not available.\"</span><span class=\"k\">fi</span><span class=\"nb\">echo</span> <span class=\"s2\">\"Instance Host: </span><span class=\"nv\">$instancehost</span><span class=\"s2\">\"</span><span class=\"nb\">echo</span> <span class=\"s2\">\"AWS Security Group ID: </span><span class=\"nv\">$securitygroup</span><span class=\"s2\">\"</span><span class=\"nb\">echo</span> <span class=\"s2\">\"Adding IP </span><span class=\"nv\">$ip</span><span class=\"s2\"> to AWS Security Group </span><span class=\"nv\">$securitygroup</span><span class=\"s2\"> in region </span><span class=\"se\">\\</span><span class=\"s2\">  </span><span class=\"nv\">$region</span><span class=\"s2\"> ...\"</span>aws ec2 authorize-security-group-ingress <span class=\"nt\">--group-id</span> <span class=\"nv\">$securitygroup</span> <span class=\"se\">\\</span>  <span class=\"nt\">--region</span> <span class=\"nv\">$region</span> <span class=\"nt\">--ip-permissions</span> <span class=\"se\">\\</span>  <span class=\"nv\">IpProtocol</span><span class=\"o\">=</span>tcp,FromPort<span class=\"o\">=</span>22,ToPort<span class=\"o\">=</span>22,IpRanges<span class=\"o\">=[{</span><span class=\"nv\">CidrIp</span><span class=\"o\">=</span><span class=\"nv\">$ip</span>/32<span class=\"o\">}]</span><span class=\"k\">if</span> <span class=\"o\">[</span> <span class=\"nv\">$ip6avail</span> <span class=\"o\">]</span><span class=\"p\">;</span> <span class=\"k\">then  </span><span class=\"nb\">echo</span> <span class=\"s2\">\"Adding IP </span><span class=\"nv\">$ip6</span><span class=\"s2\"> to AWS Security Group </span><span class=\"nv\">$securitygroup</span><span class=\"s2\"> in region </span><span class=\"se\">\\</span><span class=\"s2\">  </span><span class=\"nv\">$region</span><span class=\"s2\"> ...\"</span>  aws ec2 authorize-security-group-ingress <span class=\"nt\">--group-id</span> <span class=\"nv\">$securitygroup</span> <span class=\"se\">\\</span>  <span class=\"nt\">--region</span> <span class=\"nv\">$region</span> <span class=\"nt\">--ip-permissions</span> <span class=\"se\">\\</span>  <span class=\"nv\">IpProtocol</span><span class=\"o\">=</span>tcp,FromPort<span class=\"o\">=</span>22,ToPort<span class=\"o\">=</span>22,Ipv6Ranges<span class=\"o\">=[{</span><span class=\"nv\">CidrIpv6</span><span class=\"o\">=</span><span class=\"nv\">$ip6</span>/128<span class=\"o\">}]</span><span class=\"k\">fi</span><span class=\"nb\">echo</span> <span class=\"s2\">\"Connecting via SSH to </span><span class=\"nv\">$instancehost</span><span class=\"s2\"> ...\"</span>ssh <span class=\"nv\">$instancehost</span> <span class=\"nt\">-l</span> <span class=\"nv\">$sshuser</span> <span class=\"nt\">-p</span> <span class=\"nv\">$sshport</span> <span class=\"nt\">-i</span> <span class=\"nv\">$sshidentity</span><span class=\"k\">if</span> <span class=\"o\">[</span> <span class=\"nv\">$ip6avail</span> <span class=\"o\">]</span><span class=\"p\">;</span> <span class=\"k\">then  </span><span class=\"nb\">echo</span> <span class=\"s2\">\"SSH connection closed. Removing IP </span><span class=\"nv\">$ip</span><span class=\"s2\"> and </span><span class=\"nv\">$ip6</span><span class=\"s2\"> from AWS </span><span class=\"se\">\\</span><span class=\"s2\">    Security Group </span><span class=\"nv\">$securitygroup</span><span class=\"s2\"> ...\"</span>  aws ec2 revoke-security-group-ingress <span class=\"nt\">--group-id</span> <span class=\"nv\">$securitygroup</span> <span class=\"se\">\\</span>    <span class=\"nt\">--region</span> <span class=\"nv\">$region</span> <span class=\"nt\">--ip-permissions</span> <span class=\"se\">\\</span>    <span class=\"nv\">IpProtocol</span><span class=\"o\">=</span>tcp,FromPort<span class=\"o\">=</span>22,ToPort<span class=\"o\">=</span>22,IpRanges<span class=\"o\">=[{</span><span class=\"nv\">CidrIp</span><span class=\"o\">=</span><span class=\"nv\">$ip</span>/32<span class=\"o\">}]</span>  aws ec2 revoke-security-group-ingress <span class=\"nt\">--group-id</span> <span class=\"nv\">$securitygroup</span> <span class=\"se\">\\</span>    <span class=\"nt\">--region</span> <span class=\"nv\">$region</span> <span class=\"nt\">--ip-permissions</span> <span class=\"se\">\\</span>    <span class=\"nv\">IpProtocol</span><span class=\"o\">=</span>tcp,FromPort<span class=\"o\">=</span>22,ToPort<span class=\"o\">=</span>22,Ipv6Ranges<span class=\"o\">=[{</span><span class=\"nv\">CidrIpv6</span><span class=\"o\">=</span><span class=\"nv\">$ip6</span>/128<span class=\"o\">}]</span><span class=\"k\">else  </span><span class=\"nb\">echo</span> <span class=\"s2\">\"SSH connection closed. Removing IP </span><span class=\"nv\">$ip</span><span class=\"s2\"> from AWS Security </span><span class=\"se\">\\</span><span class=\"s2\">    Group </span><span class=\"nv\">$securitygroup</span><span class=\"s2\"> ...\"</span>  aws ec2 revoke-security-group-ingress <span class=\"nt\">--group-id</span> <span class=\"nv\">$securitygroup</span> <span class=\"se\">\\</span>    <span class=\"nt\">--region</span> <span class=\"nv\">$region</span> <span class=\"nt\">--ip-permissions</span> <span class=\"se\">\\</span>    <span class=\"nv\">IpProtocol</span><span class=\"o\">=</span>tcp,FromPort<span class=\"o\">=</span>22,ToPort<span class=\"o\">=</span>22,IpRanges<span class=\"o\">=[{</span><span class=\"nv\">CidrIp</span><span class=\"o\">=</span><span class=\"nv\">$ip</span>/32<span class=\"o\">}]</span><span class=\"k\">fi</span><span class=\"nb\">echo</span> <span class=\"s2\">\"Done.\"</span></code></pre></figure><p>You only need to modify the <code class=\"language-plaintext highlighter-rouge\"># Variables</code>:</p><p><code class=\"language-plaintext highlighter-rouge\">instancehost</code>: Hostname or IP address of your <abbr title=\"Amazon Web Services\">AWS</abbr> instance.<br /><code class=\"language-plaintext highlighter-rouge\">sshuser</code>: The local user name you’re using to login via SSH.<br /><code class=\"language-plaintext highlighter-rouge\">sshport</code>: The SSH port, default is 22.<br /><code class=\"language-plaintext highlighter-rouge\">sshidentity</code>: The path to the private key used for connecting to your <abbr title=\"Amazon Web Services\">AWS</abbr> instance.<br /><code class=\"language-plaintext highlighter-rouge\">securitygroup</code>: The Security Group identifier.<br /><code class=\"language-plaintext highlighter-rouge\">region</code>: Your <abbr title=\"Amazon Web Services\">AWS</abbr> region (since you can have instances in multiple regions).</p><p><strong>Note:</strong> To get the current IP address programmatically, I’m using <a href=\"https://www.ipify.org\">https://www.ipify.org</a>. By default, they will simply output your current IP address as plain text. There are more options, so please have a look at their site. Their traffic is encrypted.</p><p>Make the script executable:</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\"><span class=\"nv\">$ </span><span class=\"nb\">chmod </span>u+x connect-aws.sh</code></pre></figure><p>Now, simply execute the script to open a SSH shell to your instance:</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\"><span class=\"nv\">$ </span>./connect-aws.sh</code></pre></figure><div class=\"footnotes\" role=\"doc-endnotes\">  <ol>    <li id=\"fn:1\">      <p><a href=\"http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-network-security.html\"><abbr title=\"Amazon Web Services\">AWS</abbr> Security Groups</a> are virtual firewalls, not only giving you fine-grained control over external access to your instances, but also isolating your instances from each other if needed. Great great great. <a href=\"#fnref:1\" class=\"reversefootnote\" role=\"doc-backlink\">&#x21A9;&#xFE0E;</a></p>    </li>  </ol></div>",
            "url": "https://tobyx.com/2016/aws-ssh-ports",
            
            "date_published": "2016-12-10T00:00:00+00:00",
            "date_modified": "2024-11-07T12:00:00+00:00",
            "author": {
                "name": ""
            }
        },
    
        {
            "id": "https://tobyx.com/2015/search-client-based",
            "title": "Search, a Client-based Approach",
            "content_html": "<figure>  <img src=\"https://tobyx.com/img/2015/search-01.jpg\" alt=\"Image of a MacBook Pro with backlit keyboard in the dark\" />  <figcaption>You can do search locally, it's much faster than server-side search if your dataset is reasonably sized.</figcaption></figure><p>I’m serving this website statically, no exceptions. I’m doing this for security and performance reasons. But since I did want to have a search feature on here, I had to come up with something inventive.</p><p>Google offers custom search for websites to search only that site’s domain, but my disdain for Google’s pratices ruled that out. I’m not serving any external trackers, so I most certainly wasn’t going to start now by giving visitor data freely to Google. It’s an extremely clumsy and ugly implementation as well.</p><p>While I might eventually end up building a search feature in my backend, the amount of content on here is still small enough to do the search client-side.</p><p>When building a new version of the site, I’m also creating a JSON file with all articles and content listed, as well as a few keywords that people might be searching for. This JSON file is parsed in your browser. The benefits are clear:</p><ul>  <li>Privacy-centric approach as no data about searches is exchanged between client and server</li>  <li>Blazingly fast approach as all results appear instantly</li></ul><p>However, once the site grows large enough, downloading and handling the JSON file client-side might become too cumbersome. Then again, I post so infrequently that this solution might serve me well for the next few decades.</p><p>I wrote the JavaScript search myself and built cool features into it like full keyboard navigation. Just hit <code class=\"language-plaintext highlighter-rouge\">s</code> or <code class=\"language-plaintext highlighter-rouge\">f</code> to immediately jump to the search bar and set focus on it. You can navigate the results with <code class=\"language-plaintext highlighter-rouge\">⬆</code> and <code class=\"language-plaintext highlighter-rouge\">⬇</code>. Confirm with <code class=\"language-plaintext highlighter-rouge\">ENTER</code>.</p><p>Since I am tagging my articles with invisible made-up keywords, you can also search for related words. For example, my <a href=\"/2015/arch-linux-wde\">Arch Linux with Whole Disk Encryption</a> article also responds to <em>wde</em> or <em>lvm</em>.</p><p>Things to consider:</p><ul>  <li>Defer loading of the <code class=\"language-plaintext highlighter-rouge\">search.json</code> until after someone enters thesearch field deliberately. This might delay the search results by a tinybit, but users won’t have to download the search dataset if they never use the search in the first place.</li>  <li>Develop a backend search. I might end up doing it just to get some exercise.</li></ul>",
            "url": "https://tobyx.com/2015/search-client-based",
            
            "date_published": "2015-11-15T00:00:00+00:00",
            "date_modified": "2017-05-10T12:00:00+00:00",
            "author": {
                "name": ""
            }
        },
    
        {
            "id": "https://tobyx.com/2015/jekyll-vs-world",
            "title": "Jekyll Against the Rest of the World",
            "content_html": "<figure>  <img src=\"https://tobyx.com/img/2015/jekyll-vs-world-03.jpg\" alt=\"Image of birb\" />  <figcaption>I took a picture of a birb.</figcaption></figure><p><strong>Heavy with confidence, I would deposit my lumbering frame in the centerof the room.</strong> My choice to go all static had been a good one. It wasquite a serene scenery—nothing could touch me.</p><p>There were website owners running around wildly, chasing WordPresssecurity vulnerabilities<sup id=\"fnref:1\"><a href=\"#fn:1\" class=\"footnote\" rel=\"footnote\" role=\"doc-noteref\">1</a></sup>, while at the same time trying to plugholes, which the dubious free plug-ins they downloaded had ripped intotheir very essence.</p><p>I saw a man standing at the window and bashing his head against it in aslow rhythmic pace. This wasn’t a medical condition. He was simplyrepeating the rate at which his PHP-based and database-driven publishingsolution would accept new requests.</p><p>From the corner of my eye I could make out a woman who was franticallyflailing her arms about. She was trying to get into the room.Unfortunately, no one would let her in—her website had been infectedwith malware and whenever someone tried to visit it, there was awarning, urging users to run away as fast as they could.</p><p>Static publishing, oh blissful serenity. Why do I like you so much?</p><h2 id=\"dynamic-is-evil\">Dynamic is evil</h2><p>It’s not, really. But bear with me for a moment.</p><p>Imagine you’re publishing a new post on your site. How often does thatcontent change? Is it really necessary to recreate the final HTML outputyou deliver to your users every single time someone accesses your site?Query the database, run it through the templating engine, run it throughplugins, render HTML, deliver. That’s a lot of work.</p><p>Most likely, you won’t notice this immediately. But if your site<em>suddenly becomes popular</em>, chances are, it won’t scale very well. Itcould go down. Of course there’s options for you if you wish to staydynamic and have that flexibility. You could use caching plugins or evenfull-fledged web accelerators like<a href=\"https://www.varnish-cache.org\">Varnish</a>.To me, this is just throwing huge piles of code and applications at theproblem.</p><p>The problem is: Your static content should be generated statically,delivered to your visitors as is. That’s dealing with the problem in theright way.</p><p>Easy for me to say, right?</p><p>There are downsides.</p><ul>  <li>Statically generated pages usually offer no way of showing <strong>anysort</strong> of dynamic content.</li>  <li>If you need comments on your site—a common request, though why youwould want that is beyond me—you would have to resort to externalproviders like Disqus or other commenting platforms. There’s pros andcons to this, I let you decide.</li>  <li>If you want your visitors to be able to search, you need to implementit yourself on the client-side.<sup id=\"fnref:2\"><a href=\"#fn:2\" class=\"footnote\" rel=\"footnote\" role=\"doc-noteref\">2</a></sup> Externally, you could use<a href=\"https://www.google.com/work/search/products/gss.html\">Google Site Search</a>.</li></ul><h2 id=\"what-is-it-doing\">What is it doing?</h2><figure>  <img src=\"https://tobyx.com/img/2015/jekyll-vs-world-01.jpg\" alt=\"Jekyll running in serve mode\" />  <figcaption>Jekyll launched in serve mode, generating pages on the fly while working on the site.</figcaption></figure><p>Look, that’s my Terminal running Jekyll in <code class=\"language-plaintext highlighter-rouge\">serve</code> mode. It launches atiny web server and let’s me live preview all the changes in my site inmy browsers of choice. Locally.</p><p>This is actually quite amazing. The setup for a database-driven <abbr title=\"Content Management System\">CMS</abbr>solution is usually so cumbersome that most people will work with thelive site on a server somewhere else in the world. Bad for you if youdon’t have an Internet connection. And do you really want to write yourcontent in a browser? Really?</p><p>This setup is so easy that I can always work on a local master and pushmy changes to my server. I let my server do the build (just like I couldlocally, but why not) and refresh what’s currently in my web root.Automatically.</p><p>Whenever an article is done, I just commit my changes via git. It’sactually really simple to set up.</p><p>And it is oh so flexible. And safe! There’s hundreds of plugins tochoose from to do fun stuff with your content during the build process.The end result will always be static pages. Nothing to break into.</p><p>Find out more at <a href=\"http://jekyllrb.com\">jekyllrb.com</a>.</p><h2 id=\"what-it-doesnt-do\">What it doesn’t do</h2><p>With Jekyll, you start out with nothing. Nothing at all.</p><p>It actually allows you to generate a very simple blog-ready site with<code class=\"language-plaintext highlighter-rouge\">jekyll new</code>, but it’s just something to start from and learn thestructure of how a Jekyll site can be built.</p><p>You should really be interested in starting from scratch.</p><p>If all you want to do is download a theme and start writing, you shouldprobably go elsewhere. Try<a href=\"https://medium.com\">Medium</a>,it’s a well-designed new service with a great community, made for peoplewho just want to write. Now you can even<a href=\"https://medium.com/building-ulysses/hey-there-medium-nice-to-meet-you-16bcb50a63d7\">publish directly to Medium from the best editor in the world</a>.</p><p>If you think this sounds like loads of fun, go for it. You will notregret it.</p><h2 id=\"serene\">Serene</h2><p>So now I’m sitting here with<a href=\"http://www.barebones.com/products/bbedit/\">BBEdit</a>and<a href=\"http://ulyssesapp.com\">Ulysses</a>,coding and writing. Which is really all I ever wanted. I do it in theenvironment I desire to be in. It frees me.</p><figure>  <img src=\"https://tobyx.com/img/2015/jekyll-vs-world-02.jpg\" alt=\"A deer\" />  <figcaption>This is a baby deer standing around. It's happy. I'm happy.</figcaption></figure><p>I’m back to where it all began. I have a text editor and I fill it withwords. When I’m done, I save the file and that’s it. I can preview to myheart’s content. And once happy, I will simply <code class=\"language-plaintext highlighter-rouge\">commit</code>.</p><p>You know you want it.</p><p>Simplify.</p><p>You don’t want a site that’s not really you. A site filled with socialbuttons and widgets and gadgets and analytics and tracking cookies andbanner ads and pop-up ads and everything else that makes your visitorsscream at you from afar. Generated anew every time someone visits yoursite. Every. Single. Time.</p><p>Be that person in the middle of the room. Perfectly rooted, smilingcontentedly.</p><p>They cannot touch you.</p><div class=\"footnotes\" role=\"doc-endnotes\">  <ol>    <li id=\"fn:1\">      <p>WordPress is a wonderful but complex system that’s majorly responsible for the blogging revolution of the past decade. Its security track record <a href=\"http://www.cvedetails.com/vulnerability-list/vendor_id-2337/product_id-4096/\">isn’t the best</a> and you can like it’s code <a href=\"http://jshak.es/the-dire-state-of-wordpress/\">or not</a>. The fact remains that it is software running on a public-facing server, installed and run by people who shouldn’t touch a server if their life depended on it. WordPress perpetuates this with their “famous 5-minute installation”. <a href=\"#fnref:1\" class=\"reversefootnote\" role=\"doc-backlink\">&#x21A9;&#xFE0E;</a></p>    </li>    <li id=\"fn:2\">      <p>I really wanted search to work on tobyx.com, so I wrote it myself. Can’t hurt to freshen up that rusty JavaScript. If you have JavaScript activated, a small JSON file containing the sitemap will be loaded and can be queried from the search bar up above. It’s super fast. Try it. <a href=\"#fnref:2\" class=\"reversefootnote\" role=\"doc-backlink\">&#x21A9;&#xFE0E;</a></p>    </li>  </ol></div>",
            "url": "https://tobyx.com/2015/jekyll-vs-world",
            
            "date_published": "2015-11-10T00:00:00+00:00",
            "date_modified": "2015-11-10T00:00:00+00:00",
            "author": {
                "name": ""
            }
        },
    
        {
            "id": "https://tobyx.com/2015/arch-linux-wde",
            "title": "Arch Linux with Whole Disk Encryption",
            "content_html": "<p><em>Note: This guide is from 2015 and now most likely broken. Use from it what you will, but consider yourself warned.</em></p><hr /><p>For some reason, it brings me great joy to reinstall fresh Linuxes on mynon-Mac systems. While my servers usually run Debian,my personal computers almost exclusively run Arch. Arch Linux isbleeding edge, rolling release and just awesome in general. The <a href=\"https://wiki.archlinux.org/index.php/Arch_Linux#Principles\">Arch Way</a>requires you to do it all on your own, with the help of online resourcesto guide you along the way.</p><p>Having said that, this guide will lead you through the installation ofArch Linux. You will also encrypt your whole root volume so it’s saferin every day use. This kind of goes against the <em>Arch Way</em> because astep-by-step instruction makes it way too easy for you. Therefore, Isuggest you follow all the links I provide as background information andreally read up on the subject matter.</p><p>Needless to say, all of this can be done with the help of the wonderful<a href=\"https://wiki.archlinux.org\">Arch Linux wiki</a>. It is a marvelousresource of Linux computing splendor. If you have a question, you willmost likely find the answer there. I’m not kidding. It’s incrediblythorough.</p><p>General installation hints can be found at the <a href=\"https://wiki.archlinux.org/index.php/Installation_guide\">Arch Wiki - Installation Guide</a> and there’s also a wonderful <a href=\"https://wiki.archlinux.org/index.php/Beginners%27_guide\">Arch Wiki - Beginners’ guide</a>.</p><h2 id=\"installation-media\">Installation Media</h2><p>Prepare an EFI USB drive with Arch Linux. If you need help creating such a contraption, go help yourself at <a href=\"https://wiki.archlinux.org/index.php/USB_flash_installation_media\">Arch Wiki - USB flash installation media</a>.</p><h2 id=\"booting-into-our-installation-environment\">Booting into our installation environment</h2><p>Boot it! This will be different on every system so it’s kind ofpointless to make a generic guide for that. Make sure that your systemis able to boot from USB. Bring up the boot menu. There should beshortcuts displayed during boot-up, if not, try hitting <code class=\"language-plaintext highlighter-rouge\">F12</code> or <code class=\"language-plaintext highlighter-rouge\">F8</code>. Nowlaunch the UEFI boot loader it finds on the installation media youcreated.</p><p>If this is successful, you should end up with a command prompt similarto this:</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">Arch Linux 4.2.2-1-ARCH <span class=\"o\">(</span>tty1<span class=\"o\">)</span>archiso login: root <span class=\"o\">(</span>automatic login<span class=\"o\">)</span>root@archiso ~ <span class=\"c\">#</span></code></pre></figure><h2 id=\"locale\">Locale</h2><p>Before we continue, we’re assuming a US keyboard layout for theinstallation. If you however want to setup a different locale for the next 15 minutes, you can do so!</p><p>Let’s edit <code class=\"language-plaintext highlighter-rouge\">/etc/locale.gen</code>. Go through the file and uncomment the linerepresenting your chosen locale.</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">nano /etc/locale.gen</code></pre></figure><p>We shall generate the locale!</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">locale-gen</code></pre></figure><p>Now that we’re done with that, we just need to set the locale.</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">localectl set-locale <span class=\"nv\">LANG</span><span class=\"o\">=</span>en_US.UTF-8</code></pre></figure><p>You can read more on this here: <a href=\"https://wiki.archlinux.org/index.php/Locale\">Arch Wiki - Locale</a>.</p><h2 id=\"partitioning\">Partitioning</h2><p>It is time to partition your drive. Like, the one your old stuff is on.I can’t stress this enough. This guide assumes you only want Arch Linuxon your system and <strong>will most likely erase everything else you have</strong>. Youwill have to carefully adapt the workflows in here in order to installArch Linux elsewhere and/or alongside other operating systems. If you have old datayou need, <strong>back it up!</strong></p><p>We want to be modern and use <abbr title=\"GUID Partition Table\">GPT</abbr>, so we’re using <code class=\"language-plaintext highlighter-rouge\">gdisk</code> for partitioning:</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">gdisk /dev/sda</code></pre></figure><p>If you want more information on what <code class=\"language-plaintext highlighter-rouge\">gdisk</code> does, type <code class=\"language-plaintext highlighter-rouge\">?</code> and <code class=\"language-plaintext highlighter-rouge\">[ENTER]</code>.</p><p>We’re going for the most simple setup here. We want an EFI bootpartition and put all the rest in an LVM partition that will beencrypted. I will assume an EFI partition of 512 MiB in size with thesecond partition filling the rest of it. I will also assume you want todo all this on the first internal drive there is, this should be<code class=\"language-plaintext highlighter-rouge\">/dev/sda</code>.</p><p>Here’s a list of steps:</p><ul>  <li>Hit <code class=\"language-plaintext highlighter-rouge\">p</code> and <code class=\"language-plaintext highlighter-rouge\">[ENTER]</code> to print all your current partitions. Does itlook familiar, does it make sense? If so, good. We’re going to erasethem all. <strong>THIS WILL DESTROY YOUR DATA ON THESE PARTITIONS!</strong></li>  <li>Hit <code class=\"language-plaintext highlighter-rouge\">d</code> and <code class=\"language-plaintext highlighter-rouge\">[ENTER]</code> to delete partitions, and type in a number todelete it. Do this until all are gone.</li>  <li>Hit <code class=\"language-plaintext highlighter-rouge\">n</code> and <code class=\"language-plaintext highlighter-rouge\">[ENTER]</code> to create the EFI system partition now.</li>  <li>Hit <code class=\"language-plaintext highlighter-rouge\">[ENTER]</code> again to confirm the default next partition, which is 1.We have no other.</li>  <li>Hit <code class=\"language-plaintext highlighter-rouge\">[ENTER]</code> once more to confirm the default first sector.</li>  <li>Enter <code class=\"language-plaintext highlighter-rouge\">512M</code> and hit <code class=\"language-plaintext highlighter-rouge\">[ENTER]</code> to set the size for our partition.</li>  <li>To make this an EFI system partition, enter <code class=\"language-plaintext highlighter-rouge\">EF00</code> and hit <code class=\"language-plaintext highlighter-rouge\">[ENTER]</code>.</li></ul><p>Same procedure for our LVM partition.</p><ul>  <li><code class=\"language-plaintext highlighter-rouge\">n</code> then <code class=\"language-plaintext highlighter-rouge\">[ENTER]</code>.</li>  <li><code class=\"language-plaintext highlighter-rouge\">[ENTER]</code>, confirming the default.</li>  <li><code class=\"language-plaintext highlighter-rouge\">[ENTER]</code>, confirming the default.</li>  <li><code class=\"language-plaintext highlighter-rouge\">[ENTER]</code>, confirming the default of maximum size.</li>  <li>For type we’re using <code class=\"language-plaintext highlighter-rouge\">8E00</code> and hit <code class=\"language-plaintext highlighter-rouge\">[ENTER]</code>.</li>  <li>To finalize our changes, hit <code class=\"language-plaintext highlighter-rouge\">w</code> and <code class=\"language-plaintext highlighter-rouge\">[ENTER]</code>.</li>  <li>Confirm with <code class=\"language-plaintext highlighter-rouge\">y</code>.</li></ul><p>You can check what you just did with <code class=\"language-plaintext highlighter-rouge\">gdisk -l /dev/sda</code>. It shouldstill make sense. If it doesn’t make sense anymore, stop now.</p><h2 id=\"creating-the-encrypted-container\">Creating the encrypted container</h2><p>Now we’ll create and mount our encrypted container on the partition wecreated. The wiki has more information on <a href=\"https://wiki.archlinux.org/index.php/Disk_encryption\">Arch Wiki - Disk Encryption</a>.</p><p>Read what’s on your screen in the following steps carefully! You will also have to pick a passphrase for your encrypted volume.</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">cryptsetup <span class=\"nt\">-y</span> luksFormat /dev/sda2cryptsetup luksOpen /dev/sda2 lvm</code></pre></figure><h2 id=\"creating-logical-volumes-and-filesystems\">Creating logical volumes and filesystems</h2><p>Let’s create some filesystems.The EFI boot partition will be a FAT partition.</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">mkfs.vfat /dev/sda1</code></pre></figure><p>Now we’ll do some logical volumes inside the encrypted container we justmade. I picked <code class=\"language-plaintext highlighter-rouge\">16GB</code> for the swap partition, you can decidedifferently. Make sure not to mix up upper and lower case here.</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">pvcreate /dev/mapper/lvmvgcreate vg /dev/mapper/lvmlvcreate <span class=\"nt\">--name</span> cryptswap <span class=\"nt\">-L</span> 16GB vglvcreate <span class=\"nt\">--name</span> cryptroot <span class=\"nt\">-l</span> 100%FREE vg</code></pre></figure><p>Let’s make a swap partition and an ext4 partition for the rest.</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">mkswap /dev/mapper/vg-cryptswapswapon /dev/mapper/vg-cryptswapmkfs.ext4 /dev/mapper/vg-cryptroot</code></pre></figure><h2 id=\"mount-what-we-have\">Mount what we have</h2><p>And mount it all.</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">mount /dev/mapper/vg-cryptroot /mnt<span class=\"nb\">mkdir</span> /mnt/bootmount /dev/sda1 /mnt/boot</code></pre></figure><h2 id=\"finally-the-installation\">Finally, the installation</h2><p>We’ll do a default Arch Linux installation on our new system now. If youneed a wireless network connection supported by the current Linuxkernel, you can simply connect to one with:</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">wifi-menu</code></pre></figure><p>Remember that if you’re connected via Ethernet, you most likely alreadyhave networking running. Try pinging something on the Internet with<code class=\"language-plaintext highlighter-rouge\">ping</code>.</p><p>If you rely on wireless networking, make sure to include necessarypackages. The <code class=\"language-plaintext highlighter-rouge\">wifi-menu</code> that just worked so flawlessly willotherwise not be there when you reboot. There is help for you at<a href=\"https://wiki.archlinux.org/index.php/Wireless_network_configuration\">Arch Wiki - Wireless network configuration</a>.</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">pacstrap <span class=\"nt\">-i</span> /mnt base base-develgenfstab <span class=\"nt\">-U</span> <span class=\"nt\">-p</span> /mnt <span class=\"o\">&gt;&gt;</span> /mnt/etc/fstab</code></pre></figure><p>Hooray! Let’s chroot into our new system.</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">arch-chroot /mnt /bin/bash</code></pre></figure><p>We’re at that point again where we want to setup our system-widelocale(s). As before, we’re going to edit <code class=\"language-plaintext highlighter-rouge\">/etc/locale.gen</code>. Go throughthe file and uncomment the locales you want.</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">nano /etc/locale.gen</code></pre></figure><h2 id=\"locales-but-now-persistently\">Locales, but now persistently</h2><p>We’re generating the locales again:</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">locale-gen</code></pre></figure><p>We could use the wonderful new <code class=\"language-plaintext highlighter-rouge\">localectl</code> to set our locale but sincewe’re chrooted, we have no DBus and thus it doesn’t work. Bummer.</p><p>Let’s do it the old way (put your locale after LANG= accordingly):</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\"><span class=\"nb\">echo </span><span class=\"nv\">LANG</span><span class=\"o\">=</span>en_US.UTF-8 <span class=\"o\">&gt;</span> /etc/locale.conf<span class=\"nb\">export </span><span class=\"nv\">LANG</span><span class=\"o\">=</span>en_US.UTF-8</code></pre></figure><p>Once again, you can read more on this here on the <a href=\"https://wiki.archlinux.org/index.php/Locale\">Arch Wiki - Locale</a>.</p><h2 id=\"time-zones\">Time zones</h2><p>I expect systems to be running UTC, because time zones are badinventions of humankind. You can pick a default timezone that suits youbetter of course. There’s lots to choose from in<code class=\"language-plaintext highlighter-rouge\">/usr/share/zoneinfo/</code>, just have a look around.</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\"><span class=\"nb\">ln</span> <span class=\"nt\">-s</span> /usr/share/zoneinfo/UTC /etc/localtimehwclock <span class=\"nt\">--systohc</span> <span class=\"nt\">--utc</span></code></pre></figure><h2 id=\"root-password\">Root password</h2><p>Set your root password:</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">passwd</code></pre></figure><p>Do I need to say it? Make at least one non-privileged user to work in.More on this at <a href=\"https://wiki.archlinux.org/index.php/Users_and_groups\">Arch Wiki - Users and Groups</a> and <a href=\"https://wiki.archlinux.org/index.php/Sudo\">Arch Wiki - Sudo</a>.</p><h2 id=\"host-name\">Host name</h2><p>And a fun hostname:</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\"><span class=\"nb\">echo</span> <span class=\"s2\">\"funhostname\"</span> <span class=\"o\">&gt;</span> /etc/hostname</code></pre></figure><h2 id=\"boot-manager\">Boot manager</h2><p><code class=\"language-plaintext highlighter-rouge\">systemd</code> comes with its own boot manager by default, which is basedon gummiboot. We don’t need to install it ourselves anymore, it’s justthere. For our configuration, it works perfectly.</p><p>All you do is:</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">bootctl <span class=\"nb\">install</span></code></pre></figure><p>For more information on <strong>systemd-boot</strong>, refer to the <a href=\"https://wiki.archlinux.org/index.php/Systemd-boot\">Arch Wiki - systemd-boot</a>.</p><p>We need to find the UUID of <code class=\"language-plaintext highlighter-rouge\">/dev/sda2</code>, which is the partition hostingour encrypted container. That is, if you followed the guide. If you mademodifications, partitioned things differently, you need to adapt this toyour specifications.</p><p>Just like gummiboot did before, <code class=\"language-plaintext highlighter-rouge\">systemd-boot</code> has its boot loaderentries at <code class=\"language-plaintext highlighter-rouge\">/boot/loader/entries</code> and that’s where we’re going to putour <code class=\"language-plaintext highlighter-rouge\">arch.conf</code>.</p><p>First, we’ll use <code class=\"language-plaintext highlighter-rouge\">blkid</code> to filter out our UUID and put it in the<code class=\"language-plaintext highlighter-rouge\">arch.conf</code> we want:</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">blkid <span class=\"nt\">-s</span> UUID <span class=\"nt\">-o</span> value /dev/sda2 <span class=\"o\">&gt;</span> /boot/loader/entries/arch.conf</code></pre></figure><p>We should edit the boot entry now. Make sure the UUID wanders in placeof <code class=\"language-plaintext highlighter-rouge\">putUUIDhere</code>.</p><figure class=\"highlight\"><pre><code class=\"language-conf\" data-lang=\"conf\"><span class=\"n\">title</span>       <span class=\"n\">Arch</span> <span class=\"n\">Linux</span><span class=\"n\">linux</span>       /<span class=\"n\">vmlinuz</span>-<span class=\"n\">linux</span><span class=\"n\">initrd</span>      /<span class=\"n\">initramfs</span>-<span class=\"n\">linux</span>.<span class=\"n\">img</span><span class=\"n\">options</span>     <span class=\"n\">cryptdevice</span>=<span class=\"n\">UUID</span>=<span class=\"n\">putUUIDhere</span>:<span class=\"n\">lvm</span> <span class=\"n\">resume</span>=/<span class=\"n\">dev</span>/<span class=\"n\">mapper</span>/<span class=\"n\">vg</span>-<span class=\"n\">cryptswap</span> <span class=\"n\">root</span>=/<span class=\"n\">dev</span>/<span class=\"n\">mapper</span>/<span class=\"n\">vg</span>-<span class=\"n\">cryptroot</span> <span class=\"n\">quiet</span> <span class=\"n\">rw</span></code></pre></figure><p>Now we should update our <code class=\"language-plaintext highlighter-rouge\">systemd-boot</code>:</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">bootctl update</code></pre></figure><p>In order to boot with the necessary kernel modules loaded for decrypting our fancy container, edit the <code class=\"language-plaintext highlighter-rouge\">mkinitcpio.conf</code>:</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">nano /etc/mkinitcpio.conf</code></pre></figure><p>Find the line starting with <code class=\"language-plaintext highlighter-rouge\">HOOKS</code> and edit it as follows:</p><figure class=\"highlight\"><pre><code class=\"language-conf\" data-lang=\"conf\"><span class=\"n\">HOOKS</span>=<span class=\"s2\">\"base udev autodetect modconf block keymap encrypt lvm2 resume filesystems keyboard fsck\"</span></code></pre></figure><p>Have <code class=\"language-plaintext highlighter-rouge\">mkinitcpio</code> create its ramdisk environment again:</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\">mkinitcpio <span class=\"nt\">-p</span> linux</code></pre></figure><h2 id=\"done\">Done</h2><p>And that’s it!Reboot.</p><figure class=\"highlight\"><pre><code class=\"language-bash\" data-lang=\"bash\"><span class=\"nb\">exit</span>umount /mnt/bootumount /mntshutdown <span class=\"nt\">-r</span> now</code></pre></figure><p>This should be all. Enjoy your new encrypted system.</p><p><strong>Note:</strong> Once you’re done, the <code class=\"language-plaintext highlighter-rouge\">/boot</code> partition will still not beencrypted. There are ways around this, please consult the<a href=\"https://wiki.archlinux.org/index.php/Dm-crypt/Specialties#Securing_the_unencrypted_boot_partition\">Arch Wiki - dm-crypt/Specialties Securing the unencrypted boot partition</a>.If you go this far, also consider how you could make your systemtamper-evident—this means once you notice a tamper event, you can consider your computer compromised and happily throw it out the window.</p>",
            "url": "https://tobyx.com/2015/arch-linux-wde",
            
            "date_published": "2015-10-29T00:00:00+00:00",
            "date_modified": "2015-10-30T00:00:00+00:00",
            "author": {
                "name": ""
            }
        }
    
    ]
}