<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title></title>
		<description>My personal website built in Jekyll, Liquid, and good old HTML5.</description>
		<link>https://zach.bloomqu.ist</link>
		<atom:link href="https://zach.bloomqu.ist/feed.xml" rel="self" type="application/rss+xml" />
		
			<item>
				<title>Reliable, Deliverable, Self-Hosted Email</title>
				<description>&lt;p&gt;I have been on an &lt;a href=&quot;/blog/2019/11/site-to-site-wireguard-vpn.html&quot;&gt;ongoing&lt;/a&gt; &lt;a href=&quot;/blog/2020/08/pilo-raspberry-pi-lights-out-management.html&quot;&gt;quest&lt;/a&gt; to free myself from cloud services for years now. During this time, I have hosted my personal email (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@bloomqu.ist&lt;/code&gt;) on a &lt;strike&gt;Google Apps&lt;/strike&gt; &lt;strike&gt;G Suite&lt;/strike&gt; &lt;em&gt;Google Workspace&lt;/em&gt; account, which, while convenient, also means that my personal emails are at the whims of &lt;a href=&quot;https://en.wikipedia.org/wiki/Privacy_concerns_regarding_Google&quot;&gt;one of the world’s most privacy-hostile companies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/email/do-be-evil.png&quot; alt=&quot;Man looking at the words &amp;quot;Do Be Evil&amp;quot; in Google font on the wall.&quot; /&gt;
&lt;small&gt;Google’s famous slogan.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Obviously, this cannot stand forever. I wanted to self-host my email, but I know that self-hosting email is fraught with issues. The problems I wanted to avoid are as follows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Open-source email software is complicated to set up if you were not a sysadmin in the 90’s.&lt;/li&gt;
  &lt;li&gt;Popular email providers like Google Mail frequently block emails from residential/public cloud IP addresses for anti-spam reasons.&lt;/li&gt;
  &lt;li&gt;If your self-hosted mail server goes down, you can potentially miss out on important email.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I cannot use an email service that does not have &lt;em&gt;reliable sending and receiving&lt;/em&gt;. Happily, each of these problems has a solution:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The &lt;a href=&quot;https://mailu.io/&quot;&gt;Mailu&lt;/a&gt; project bundles antispam, POP3, IMAP, SMTP, webmail, administrative interface, etc. into a set of Docker containers that can be managed with Docker Compose or your container tool of choice. This gives us a good, well-architected base setup to configure further.&lt;/li&gt;
  &lt;li&gt;Use a trusted SMTP outgoing relay to send email. Yes, this is not self-hosting, but you cannot “self-host” an outgoing email anyways, it is ultimately leaving your network one way or another.&lt;/li&gt;
  &lt;li&gt;Use a backup email server to receive email when your server is down. Set up MX records with a lower priority than your self-hosted mail server to have them act as a fallback. You can either self-host this backup or use a public service. Then, when the primary server is back up, use IMAP to mirror messages from the backup automatically.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In fact, Mailu is so easy to use, that we can configure (2) and (3) out of the box. Here is a step-by-step guide to configuring Mailu with an outbound SMTP gateway and backup MX server:&lt;/p&gt;

&lt;h3 id=&quot;part-1-install-mailu&quot;&gt;Part 1: Install Mailu&lt;/h3&gt;

&lt;p&gt;Follow the &lt;a href=&quot;https://mailu.io/1.7/setup.html&quot;&gt;Mailu setup guide&lt;/a&gt; published on their website. Make sure to enable the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetchmail&lt;/code&gt; service - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetchmail&lt;/code&gt; will pull email from our backup mail server.&lt;/p&gt;

&lt;p&gt;Set up your DNS records appropriately. For most use cases, this means pointing an A record from your mail hostname (in my case, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mail.chary.us&lt;/code&gt;) to your mail server’s public IP, and configuring MX records on the domains that will be receiving email:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~ dig A mail.chary.us
mail.chary.us.      300	IN	A	107.191.100.174
~ dig MX chary.us
chary.us.           300	IN	MX	1 mail.chary.us.
~ dig MX bloomqu.ist
bloomqu.ist.        300	IN	MX	1 mail.chary.us.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We do not need to set up SPF/DKIM DNS records at this time, since we will be using an outgoing SMTP relay to send email, not the mail server itself.&lt;/p&gt;

&lt;h3 id=&quot;part-2-set-up-an-outgoing-smtp-relay&quot;&gt;Part 2: Set up an outgoing SMTP relay&lt;/h3&gt;

&lt;p&gt;To prevent other email servers from discarding our emails because they’re from an IP with no reputation for mail sending, we can use an outgoing SMTP relay instead of sending email ourselves.&lt;/p&gt;

&lt;p&gt;There are many options available for this, with &lt;a href=&quot;https://mailgun.com/&quot;&gt;Mailgun&lt;/a&gt;, &lt;a href=&quot;https://mandrillapp.com/&quot;&gt;Mandrill by Mailchimp&lt;/a&gt; and &lt;a href=&quot;https://sendgrid.com/&quot;&gt;SendGrid by Twilio&lt;/a&gt; being the three heavy-weights of the industry. I chose to go with SendGrid, because their free plan offers sending 100 emails/day forever, which is far more emails than I can see myself sending.&lt;/p&gt;

&lt;p&gt;Once you have an account with your SMTP relay provider, you will want to add the domain(s) that you will be sending from to your account. In SendGrid, this is called “Sender Authentication”. As part of this, you will configure DNS CNAME records for &lt;a href=&quot;https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail&quot;&gt;DKIM&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Sender_Policy_Framework&quot;&gt;SPF&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/email/sendgrid-dns.png&quot; alt=&quot;Screenshot of SendGrid CNAME instructions&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Confused about how SPF can work like this, even though it’s being set on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;em0000.yourdomain.com&lt;/code&gt;, not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yourdomain.com&lt;/code&gt;? SPF is used to validate the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Return-Path&lt;/code&gt; header, not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;From&lt;/code&gt;, and SendGrid uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;em0000.yourdomain.com&lt;/code&gt; as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Return-Path&lt;/code&gt;. 💥. &lt;a href=&quot;https://stackoverflow.com/q/67156334/3474615&quot;&gt;Read more on StackOverflow.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that the domains are configured, obtain the credentials for your provider’s SMTP gateway. In SendGrid, this is under the “API Keys” settings panel. Your API key is your password.&lt;/p&gt;

&lt;p&gt;In your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mailu.env&lt;/code&gt; file, update the following environment variables:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Change to your SMTP relay host - port 587 will automatically use SSL&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;RELAYHOST&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;smtp.sendgrid.net:587
&lt;span class=&quot;c&quot;&gt;# Change to your username, on SendGrid this is the literal string &quot;apikey&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;RELAYUSER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;apikey
&lt;span class=&quot;c&quot;&gt;# Change to your password, on SendGrid this is the API key you obtained&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;RELAYPASSWORD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;your-api-key-goes-here
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you have already started Mailu, run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose down; docker-compose up -d&lt;/code&gt; to reload the env file.&lt;/p&gt;

&lt;p&gt;You should now be able to send an outgoing email (for example, via the Mailu webmail) and by inspecting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Received&lt;/code&gt; header at the recipient end, see that it was relayed through SendGrid:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// more headers
Received: from xtranbvx.outbound-mail.sendgrid.net (xtranbvx.outbound-mail.sendgrid.net. [167.12.12.138])
// more headers
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If this does not work, you can tail the logs using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose logs -f --tail=0&lt;/code&gt; and re-send the email to see what the error was.&lt;/p&gt;

&lt;h3 id=&quot;part-3-set-up-the-backup-email-server&quot;&gt;Part 3: Set up the backup email server&lt;/h3&gt;

&lt;p&gt;If, heaven forbid, your power goes out, your server gets knocked off the shelf, or the fiber line to your house is cut, you will not want to miss out on incoming e-mails. Although some mail transfer agents (MTAs) will retry delivery if the recipient SMTP server is unavailable, you really really don’t want to be relying on “maybe the sender will retry” when it comes to important email.&lt;/p&gt;

&lt;p&gt;Luckily, this is pretty easy to fix, by the nature of how MX (&lt;em&gt;M&lt;/em&gt;ail E&lt;em&gt;X&lt;/em&gt;changer) DNS records work. Each record has a priority, with the lowest priority records being considered first. Records with equal priority are “randomly” chosen by the sending MTA. For example, with the following MX records, mail would always be delivered to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mail.foo.net&lt;/code&gt;, unless &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mail.foo.net&lt;/code&gt; is down, in which case it will go to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backup-mail.foo.net&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10 &amp;lt; 20&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;foo.net.   300  IN  MX  10 mail.foo.net.
foo.net.   300  IN  MX  20 backup-mail.foo.net.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can either set up your own mail server for this purpose, or use a public Internet service as the backup.&lt;/p&gt;

&lt;p&gt;If you decide to self-host a backup mail server, make sure the infrastructure is separate from your primary mail server. There is &lt;a href=&quot;https://github.com/Mailu/Mailu/issues/591&quot;&gt;some discussion on the Mailu repo&lt;/a&gt; about setting up a backup MX server, and if you DuckDuckGo “backup MX server”, you can find plenty of information about setting up a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;postfix&lt;/code&gt; server in this manner.&lt;/p&gt;

&lt;p&gt;I went with the non-self-hosted option - since I already have GSuite set up to receive emails for all my domains, I can use it as the “backup” MX. Here is what my MX records look like:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;➜  ~ dig MX bloomqu.ist
bloomqu.ist.		300	IN	MX	1 mail.chary.us.
bloomqu.ist.		300	IN	MX	3 aspmx.l.google.com.
bloomqu.ist.		300	IN	MX	5 alt1.aspmx.l.google.com.
bloomqu.ist.		300	IN	MX	5 alt2.aspmx.l.google.com.
bloomqu.ist.		300	IN	MX	10 aspmx2.googlemail.com.
bloomqu.ist.		300	IN	MX	10 aspmx3.googlemail.com.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As a result, MTAs will first attempt to deliver to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mail.chary.us&lt;/code&gt;. If my server is down, delivery will fall back to Google Mail.&lt;/p&gt;

&lt;p&gt;There are also paid services that offer to do specifically this - a cursory search found &lt;a href=&quot;https://www.prolateral.com/email-services/backup-smtp/backup-mx.html&quot;&gt;this affordable offering&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once your backup MX records are in place, the last step is to configure Mailu to automatically retrieve email from your backup MX using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetchmail&lt;/code&gt;. This is available via the web administration interface. Navigate to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/admin&lt;/code&gt; and click on “Fetched accounts”. From here, you can configure automatic fetching via IMAP or POP3. My setup for Google Apps looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/email/fetched-account.png&quot; alt=&quot;Screenshot of Fetched Account page&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Note that Mailu is configured to only pull unread email via “Fetched accounts”.&lt;/p&gt;

&lt;p&gt;By default, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fetchmail&lt;/code&gt; will run every 60 seconds to pull in email.&lt;/p&gt;

&lt;h3 id=&quot;part-4-next-steps&quot;&gt;Part 4: Next Steps&lt;/h3&gt;

&lt;p&gt;Now you are sending and receiving email in a safe, reliable way, while still maintaining control of your data at rest. 🎉🎉🎉!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/email/life-is-happy.jpg&quot; alt=&quot;Life is happy!&quot; /&gt;
&lt;small&gt;Life is happy!&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;After setting everything up, you may wish to take some additional steps to enhance and secure your setup:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Set up a catch-all address for your domains using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Aliases&lt;/code&gt; in the Mailu admin panel.&lt;/li&gt;
  &lt;li&gt;Establish a backup strategy - as with anything self-hosted, the data is now your ultimate responsibility. Nobody will be around to help you recover lost emails when your server’s hard drive inevitably crashes, so establish a strategy now.&lt;/li&gt;
  &lt;li&gt;Increase your mailbox quota - by default, Mailu creates all accounts with a 1GB mail quota, which is pretty small.&lt;/li&gt;
  &lt;li&gt;Disable any services you don’t need running 24/7 - this could include the admin panel and webmail.&lt;/li&gt;
  &lt;li&gt;Test your email setup by sending and receiving emails from your friends. This will help catch any errors with your setup before they manifest into truly embarrassing email problems.&lt;/li&gt;
&lt;/ul&gt;
</description>
				<pubDate>Thu, 08 Jul 2021 11:56:19 +0000</pubDate>
				<link>https://zach.bloomqu.ist/blog/2021/07/reliable-self-hosted-email.html</link>
				<guid isPermaLink="true">https://zach.bloomqu.ist/blog/2021/07/reliable-self-hosted-email.html</guid>
			</item>
		
			<item>
				<title>Replacing my phone's battery with a cheap AliExpress knock-off</title>
				<description>&lt;p&gt;This is a story of one man’s quest for power.&lt;/p&gt;

&lt;p&gt;I purchased my current phone, a OnePlus 5T, in 2017. This summer, after about two and a half years of ownership, I noticed that it was no longer holding a charge all day. Frequently, the phone would reach 0% and shut off, right in the middle of tracking an evening bike ride or watching Netflix while cooking dinner. Although cell phone battery wear is a well-known issue, I got tired of it pretty quickly.&lt;/p&gt;

&lt;p&gt;I used the &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.digibites.accubattery&amp;amp;hl=en_US&amp;amp;gl=US&quot;&gt;AccuBattery&lt;/a&gt; app for about two months to try and get a handle on my battery health. It measured my phone’s amperage draw during the day and used that to estimate that of the 3300 milliamp-hour (mAh) capacity that my battery originally offered, only about 2400 mAh of capacity remained - only about 75% of the battery’s original health:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/battery/oem-capacity.png&quot; alt=&quot;Screenshot of AccuBattery app for OEM battery capacity&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This answered the question of “why does it feel like my phone is shutting off so quickly?” pretty clearly. Now, it was up to me to get a replacement battery.&lt;/p&gt;

&lt;h3 id=&quot;attempting-to-get-a-genuine-battery&quot;&gt;Attempting to get a genuine battery&lt;/h3&gt;

&lt;p&gt;My first thought was that I could simply order the OEM OnePlus 5T battery somewhere online. Why not? I found a page on the OnePlus website where prices are listed for replacement parts. The USA page was down at the time of this writing, but the &lt;a href=&quot;https://www.oneplus.in/support/pricing/detail?code=7&quot;&gt;India support page&lt;/a&gt; lists a OnePlus 5T OEM battery replacement as being about $15.&lt;/p&gt;

&lt;p&gt;This seemed acceptable to me. My first thought was to email OnePlus support asking how to purchase the battery. Unfortunately, according to the service rep, they do not ship or sell OEM batteries without service:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We would like to inform you that we do not ship or sell the accessories in any parts of the world, and all the repairs are carried out by our Authorized service centers only. So if you wish to get the device repaired, you can send it to the OnePlus authorized service center and get the same repaired.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is in line with what other OnePlus customers have reported - nobody, as far as I can tell, has ever been able to source OEM batteries from OnePlus, leaving DIY customers like myself to try and find knock-offs elsewhere.&lt;/p&gt;

&lt;p&gt;I would’ve sent my phone in for repairs, but after I received the above email, I had such a long and terrible customer support experience trying to arrange the repair that by the end of it, I no longer trusted OnePlus to reliably service and return my phone. This lack of trust was reinforced by horror stories from other OnePlus customers - one customer’s phone was &lt;a href=&quot;https://www.reddit.com/r/oneplus/comments/eleckw/sent_my_oneplus_5_to_fort_worth_tx_for_repair_no/&quot;&gt;lost by the Fort Worth, TX service center&lt;/a&gt;, another’s was &lt;a href=&quot;https://www.reddit.com/r/oneplus/comments/depgkg/oneplus_lost_my_coworkers_phone_during_repair_at/&quot;&gt;lost and took 4 weeks before being returned&lt;/a&gt;, and yet another customer had &lt;a href=&quot;https://www.reddit.com/r/oneplus/comments/jke2kd/sent_my_op3t_for_a_battery_replacement_oneplus/&quot;&gt;their phone held hostage unless they agreed to repairing EVERYTHING instead of just getting the battery replaced&lt;/a&gt;. These stories, combined with my awful customer support experience, convinced me that sending my phone in would be a truly bad idea.&lt;/p&gt;

&lt;h3 id=&quot;buying-an-aftermarket-battery&quot;&gt;Buying an aftermarket battery&lt;/h3&gt;

&lt;p&gt;Many people on the /r/oneplus5t subreddit have recommended purchasing a &lt;a href=&quot;https://www.ifixit.com/Store/Android/OnePlus-5-5T-Replacement-Battery/IF330-018?o=2&quot;&gt;replacement battery from iFixit&lt;/a&gt;, but I felt like iFixit was simply selling cheap Chinese batteries with a nice label on them. I mean, if OnePlus can fix it for $15, why does the iFixit battery cost $30, if not for marketing?&lt;/p&gt;

&lt;p&gt;So, I hit up eBay and AliExpress, and eventually found the [sic] “Specail Mobilephone Parts Store”, where they offer a &lt;a href=&quot;https://web.archive.org/web/20201109223630/https://www.aliexpress.com/item/4000438352423.html&quot;&gt;“4650 mAh” “Perfect business battery”&lt;/a&gt; for the OnePlus 5T. With slogans like &lt;a href=&quot;/assets/battery/giant-energy-huge-capacity.webp&quot;&gt;“Giant energy; huge capacity”&lt;/a&gt;, &lt;a href=&quot;/assets/battery/safety-does-not-explode.webp&quot;&gt;“Safety does not explode”&lt;/a&gt;, and &lt;a href=&quot;/assets/battery/ensure-qualified-and-safe-to-use.webp&quot;&gt;“Ensure qualified and safe to use”&lt;/a&gt;, I felt confident that my $11.87 was going to a good place. I placed the order and, about 3 weeks later, I received the battery in my mailbox.&lt;/p&gt;

&lt;h3 id=&quot;battery-physics-101&quot;&gt;Battery Physics 101&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/battery/oem-and-aftermarket.jpg&quot; alt=&quot;Photo of the OEM battery and the aftermarket battery side-by-side&quot; /&gt;
&lt;small&gt;The OEM battery (left) and the aftermarket battery installed (right).&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;The first thing I noticed about the replacement battery was that the capacity was even HIGHER than what I ordered. The OnePlus 5T OEM battery is rated at 3300 mAh capacity, the AliExpress product page advertised a battery with 4650 mAh capacity, and the label on the battery I received claimed an astounding &lt;em&gt;5350 mAh&lt;/em&gt; capacity - 162% of the OEM capacity. Clearly, I had gotten a great deal!&lt;/p&gt;

&lt;p&gt;The second thing I noticed was that the aftermarket battery was significantly lighter than the OEM battery. So much lighter that I weighed the batteries out of curiosity. The OEM OnePlus 5T battery weighed 47.0g. The aftermarket OnePlus 5T battery weighed 38.7g, or about 17% less.&lt;/p&gt;

&lt;p&gt;It’s amazing that Da Da Xiong was able to achieve 162% capacity with 17% less weight. Too amazing to be true, in fact.&lt;/p&gt;

&lt;p&gt;Via Wikipedia, I learned that the &lt;a href=&quot;https://en.wikipedia.org/wiki/Specific_energy&quot;&gt;specific energy&lt;/a&gt; of a lithium-ion polymer battery can be up to &lt;a href=&quot;https://en.wikipedia.org/wiki/Lithium-ion_battery&quot;&gt;265 watt-hours per kilogram (Wh/kg)&lt;/a&gt;. The nominal voltage of the lithium-ion polymer batteries here is about 3.8V. We can use &lt;a href=&quot;https://en.wikipedia.org/wiki/Ohm%27s_law&quot;&gt;Ohm’s law&lt;/a&gt; to calculate the maximum possible capacity of each battery based on weight, assuming that each battery is always supplying the nominal 3.8V.&lt;/p&gt;

&lt;p&gt;Let’s start by calculating the maximum possible Amp-hours (Ah) per kilogram (kg) for a Li-ion poly battery at 3.8V, using Ohm’s law:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;265 Wh/kg / 3.8 V = 69 Ah/kg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, we can calculate the maximum physically possible capacity for each battery by multiplying this number by the weights of each battery:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;OEM battery:          .047 kg * 69 Ah/kg = 3.2 Ah = 3200 mAh
Aftermarket battery: .0387 kg * 69 Ah/kg = 2.6 Ah = 2600 mAh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The astute reader might be wondering why this estimate for the maximum capacity of the OEM battery (3200 mAh) is less than the capacity OnePlus advertises (3300 mAh). Why is this? Well, it’s because the assumption we made - that each battery is always supplying the nominal 3.8V - is false. The voltage output of a Li-ion poly battery &lt;a href=&quot;https://learn.adafruit.com/li-ion-and-lipoly-batteries/voltages&quot;&gt;drops over time&lt;/a&gt;, so the calculation shown is only a lower bound approximation of each battery’s maximum capacity.&lt;/p&gt;

&lt;p&gt;I don’t have information about the exact chemical composition of these batteries, nor the voltage charts, nor do I know what the upper and lower voltage limits are on the OnePlus 5T charging circuit. However, if we estimate that the voltage drops from 3.8V to 3.0V in a linear fashion (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;V = 3.8 - .8t, 0 &amp;lt;= t &amp;lt;= 1&lt;/code&gt;), we can use integration to arrive at approximately 3600 mAh maximum capacity for the OEM battery and 2900 mAh maximum capacity for the aftermarket battery.&lt;/p&gt;

&lt;p&gt;Even without exact numbers, these calculations demonstrate that &lt;em&gt;something&lt;/em&gt; is fishy about the Da Da Xiong battery’s mAh claims.&lt;/p&gt;

&lt;h3 id=&quot;real-world-usage&quot;&gt;Real-world usage&lt;/h3&gt;

&lt;p&gt;Anyways, I didn’t buy this shady AliExpress battery just so that I could do a bunch of math. I purchased it to restore my phone’s ability to last all day, and it has definitely succeeded at that. From a qualitative perspective, I now have enough juice to keep my phone’s battery fueled all day until I can recharge it at night.&lt;/p&gt;

&lt;p&gt;From a quantitative perspective, AccuBattery reports that the aftermarket battery has an estimated 3360 mAh capacity, which about matches the capacity of the OEM battery:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/battery/aftermarket-capacity.png&quot; alt=&quot;Screenshot of AccuBattery app for aftermarket battery capacity&quot; /&gt;&lt;/p&gt;

&lt;p&gt;However, what AccuBattery fails to account for is the fact that once the aftermarket battery reaches 15%, the battery percentage begins to free-fall until it reaches 0% and shuts off. It seems like 15% on the aftermarket battery is equivalent to 1% on the OEM battery. I think this is because the Android OS cannot correctly estimate the battery’s remaining charge because it has different voltage characteristics than the OEM battery, but it doesn’t really bother me, I just have to make sure that to charge the phone at 15% instead of 1%. This seems to be an extremely common experience with DIY battery replacements - even folks using the iFixit battery run in to this issue.&lt;/p&gt;

&lt;p&gt;If we take 15% off of AccuBattery’s estimated capacity, we get 2856 mAh, which is really really close to what a brand new OnePlus 5T reports - AccuBattery estimates the OEM battery as having ~3000 mAh capacity when it is brand new. That about matches my experience - with the Da Da Xiong battery, the phone is staying alive longer, almost like when it was new.&lt;/p&gt;

&lt;h3 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Random Chinese batteries do not work as advertised - they will not magically double your phone’s battery capacity.&lt;/li&gt;
  &lt;li&gt;However, random Chinese batteries work &lt;em&gt;almost as well&lt;/em&gt; as brand new OEM batteries, but your battery percentage will forever be miscalibrated.&lt;/li&gt;
  &lt;li&gt;Never trust OnePlus customer service.&lt;/li&gt;
&lt;/ul&gt;
</description>
				<pubDate>Mon, 09 Nov 2020 17:06:48 +0000</pubDate>
				<link>https://zach.bloomqu.ist/blog/2020/11/aftermarket-cell-phone-battery.html</link>
				<guid isPermaLink="true">https://zach.bloomqu.ist/blog/2020/11/aftermarket-cell-phone-battery.html</guid>
			</item>
		
			<item>
				<title>Pilo: Raspberry Pi-Powered Lights-Out Remote Server Management for $60 or less</title>
				<description>&lt;p&gt;&lt;img src=&quot;/assets/pilo-naked.jpg&quot; alt=&quot;Pilo board before final tape-up&quot; /&gt;
&lt;small&gt;The completed Pilo controller, before final installation. The USB capture card and Arduino Nano USB serial are soldered to the underside of the 3B+.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Like many geeks, I have a “home server” made from off-the-shelf, consumer-grade PC parts, from which I run my weekend programming projects, game servers for friends, this website, and so on. Recently, I had a power event at the house that caused the server to reboot. When the power came back on, the server booted, but it was stuck at the boot screen waiting for me to enter the disk decryption passphrase!&lt;/p&gt;

&lt;p&gt;Luckily, I was at home and asleep at the time. Once I woke up and realized something was amiss, I was able to plug in a keyboard and enter the passphrase. But this event got me thinking - what if I wasn’t home at the time? What if I was in another country? What if someday, I move this server outside of my house and need to regularly access the physical screen and keyboard?&lt;/p&gt;

&lt;p&gt;In the “real server” world, the solution to this is known as &lt;a href=&quot;https://en.wikipedia.org/wiki/Out-of-band_management&quot;&gt;“lights-out management” (LOM)&lt;/a&gt;. Every major server manufacturer has their own flavor of this, such as HP’s iLO (Integrated Lights-Out). There are even industry standards like &lt;a href=&quot;https://en.wikipedia.org/wiki/Intelligent_Platform_Management_Interface&quot;&gt;IPMI&lt;/a&gt; that define common interfaces for LOM implementations. Commonly supported functions for LOM systems include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Controlling keyboard and mouse input&lt;/li&gt;
  &lt;li&gt;Controlling power button status (so you can restart, shutdown, force off…)&lt;/li&gt;
  &lt;li&gt;Seeing the raw video output from the motherboard (even pre-boot - even for BIOS)&lt;/li&gt;
  &lt;li&gt;Mounting ISOs as disks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I decided to make my own Raspberry Pi-based LOM that can do some of these things, to help decrease my stress next time I leave my house for an extended period of time. I’d like to introduce Pilo - “Pi Lights-Out”.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note: This post describes how I arrived at the final design of this system. If you just want the instructions for setting this up on your own, start reading at “Tutorial”.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;building-the-keyboard-controller&quot;&gt;Building the Keyboard Controller&lt;/h2&gt;

&lt;p&gt;When I started looking for ways to use my Pi to send keyboard commands to a computer, the problem I discovered was that all of the existing methods rely on &lt;a href=&quot;http://www.isticktoit.net/?p=1383&quot;&gt;using the Raspberry Pi Zero as a USB host&lt;/a&gt;, which disables using the onboard USB port for other purposes. Additionally, this method does not work on other boards, like the Raspberry Pi 3B+. This was problematic, because I wanted to use the 3B+ due to the on-board Ethernet - that, and the fact that I had one kicking around from a previous &lt;a href=&quot;https://pi-hole.net/&quot;&gt;Pi-hole&lt;/a&gt; deployment.&lt;/p&gt;

&lt;p&gt;A more suitable solution would be to emulate a keyboard via the GPIO pins of the Pi. This would theoretically not affect existing USB devices, and could be used on any model of Pi, not just the Pi Zero. So I started looked into trying to &lt;a href=&quot;http://www.jargon.net/jargonfile/b/bitbang.html&quot;&gt;“bit-bang”&lt;/a&gt; the USB Human Interface Device protocol via the Pi’s GPIO pins.&lt;/p&gt;

&lt;p&gt;I pretty quickly hit a dead end with that. There are &lt;a href=&quot;https://raspberrypi.stackexchange.com/q/82850/100317&quot;&gt;many&lt;/a&gt;, &lt;a href=&quot;https://www.element14.com/community/thread/38228/l/raspberry-pi-usb-output-from-gpio&quot;&gt;many&lt;/a&gt; existing discussions on the web about bit-banging the USB protocol on the Pi. The consensus seems to be that the Raspberry Pi’s GPIO is too slow to emulate USB, and even if it &lt;em&gt;could&lt;/em&gt; output bits fast enough to act as a USB device, the implementation would be extremely buggy because of the non-real-time nature of the Linux OS (&lt;a href=&quot;https://raspberrypi.stackexchange.com/a/87865/100317&quot;&gt;read more&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;But USB isn’t the only way to send keypresses to a computer - almost a decade before the USB standard was a twinkle in Compaq’s eye, IBM was using the &lt;a href=&quot;https://en.wikipedia.org/wiki/PS/2_port&quot;&gt;PS/2&lt;/a&gt; standard (not to be confused with the &lt;a href=&quot;https://en.wikipedia.org/wiki/PlayStation_2&quot;&gt;PS2&lt;/a&gt;) to connect mice and keyboards to PCs. It’s still not feasible to use the Pi to bit-bang the PS/2 protocol, but we can use an Arduino as a daughterboard, and the &lt;a href=&quot;https://github.com/Harvie/ps2dev&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ps2dev&lt;/code&gt;&lt;/a&gt; library can handle the nitty-gritty of the serial protocol for PS/2.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/pilo-pin-jam.jpg&quot; alt=&quot;Left: Arduino soldered up. Right: Breadboard jumpers jammed into the PS/2 port&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So that’s what I did. The Arduino Nano pictured on the left is plugged directly into the PS/2 combo port on the back of the motherboard (ignore the unused 5V wire, red). Originally, I planned on cutting the end off of a PS/2 cable and making it all nice, but the Goodwill near me didn’t have any PS/2 junk and it turns out that breadboard wires just fit oh-so-snugly into the DIN holes. So this is how it’s gonna be.&lt;/p&gt;

&lt;p&gt;The Arduino Nano is flashed with a &lt;a href=&quot;https://create.arduino.cc/editor/flotwig__/093ababe-c724-476f-aeb8-a76b239bf192/preview&quot;&gt;short program&lt;/a&gt; that makes it act as a dumb pipe which blindly shuttles bytes from the Arduino’s USB serial port to the PS/2 connection on the motherboard. This means that all of the logic for which keyboard commands should be sent has to be written on the Pi-side, which is nice, because it means that we should never have to re-flash the Arduino to update some keyboard logic.&lt;/p&gt;

&lt;p&gt;Note that currently, Pilo is only built to control keyboard input, since it is oriented towards server use. It would be possible to add PS/2 mouse output with no additional hardware, just 2 or 3 wires for PS/2 mouse CLK, DATA, and GND (unless using combo port). The &lt;a href=&quot;https://github.com/Harvie/ps2dev&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ps2dev&lt;/code&gt;&lt;/a&gt; library contains functions for mouse control as well.&lt;/p&gt;

&lt;h3 id=&quot;power-control-via-ps2&quot;&gt;Power Control via PS/2&lt;/h3&gt;

&lt;p&gt;Originally, I thought I was going to have to wire a relay to the motherboard’s RESET pin to allow Pilo to control the computer’s power. This is the approach that &lt;a href=&quot;https://github.com/Fmstrat/diy-ipmi&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;diy-ipmi&lt;/code&gt;&lt;/a&gt;, another similar project, uses. However, while researching the keyboard controller, I rediscovered a long-lost secret of the PS/2 standard: the “ACPI keys”. ACPI, or the Advanced Configuration and Power Interface, is a set of power management standards for PCs. The PS/2 standards define &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Power&lt;/code&gt;, &lt;a href=&quot;https://ux.stackexchange.com/q/83200/117790&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WakeUp&lt;/code&gt;&lt;/a&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sleep&lt;/code&gt; key scancodes that can be used to control the power status of the PC - just like the power button on the front of the box, a short-press of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Power&lt;/code&gt; key requests the OS to shutdown. However, on my motherboard, a long-press of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Power&lt;/code&gt; does &lt;em&gt;NOT&lt;/em&gt; seem to force the power off.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/pilo-bios-s5.png&quot; alt=&quot;A screenshot of the BIOS with the ACPI S5 Wake-On-Keyboard option selected&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Almost all BIOS support using the ACPI keys to power the system on from a powered down state. Above is what the option looks like in my BIOS (in ACPI, the powered-off state is known as “S5”). With this option enabled, sending the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Power&lt;/code&gt; scancode will boot the computer up from an off state.&lt;/p&gt;

&lt;p&gt;So, now the Arduino has two responsibilities in Pilo: to send regular keypresses to the computer, and to send power commands to the computer. This saves us from having to install a relay for the motherboard’s RESET pin.&lt;/p&gt;

&lt;h2 id=&quot;capturing-video-output&quot;&gt;Capturing Video Output&lt;/h2&gt;

&lt;p&gt;My server has a GPU with HDMI output, so I decided to use a USB HDMI capture card to get the video feed for Pilo. I found one on &lt;a href=&quot;https://smile.amazon.com/gp/product/B08BZ52Q65/&quot;&gt;Amazon for about $15&lt;/a&gt;. When connected to the Pi, it acts as a regular USB webcam, available under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/videoX&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before this project, I had no experience with streaming video over the web. I decided to use &lt;a href=&quot;https://www.linux-projects.org/uv4l/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uv4l&lt;/code&gt;&lt;/a&gt; (“Userspace Video4Linux”)’s &lt;a href=&quot;https://www.linux-projects.org/home/documentation/uv4l-server/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uv4l-server&lt;/code&gt;&lt;/a&gt; component to set up an HTTP video server. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uv4l&lt;/code&gt; makes it easy to set up a simple MJPEG stream, which is the goofiest possible video stream - each frame of the stream is a full JPEG image, sent to you in real time. As you can imagine, it’s not the lightest on bandwidth, but it is easy to embed - all web browsers support embedding it in an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;img src=&quot;/stream.mjpeg&quot;/&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I configured the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uv4l-server&lt;/code&gt; to only listen on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost&lt;/code&gt;, with the idea that I could reverse-proxy connections to the video stream to provide security.&lt;/p&gt;

&lt;h2 id=&quot;creating-the-application&quot;&gt;Creating the application&lt;/h2&gt;

&lt;p&gt;For the Pilo interface, I decided to go with a web app, instead of something like VNC, or the actual IPMI protocol. This was mostly due to my background in web development, and the fact that the app can be accessed with a web browser, something every computer has installed. Here is a short video showing the completed Pilo app in action:&lt;/p&gt;

&lt;video controls=&quot;&quot; autoplay=&quot;&quot; muted=&quot;&quot; loop=&quot;&quot; src=&quot;/assets/pilo-demo.webm&quot; type=&quot;video/webm&quot;&gt;
    &lt;p&gt;Your browser doesn&apos;t support WEBMs. &lt;a href=&quot;/assets/pilo-demo.webm&quot;&gt;Download the video instead.&lt;/a&gt;&lt;/p&gt;
&lt;/video&gt;

&lt;p&gt;You can find the &lt;a href=&quot;https://github.com/flotwig/pilo&quot;&gt;GitHub repo for Pilo here&lt;/a&gt;. It consists of two major components:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/flotwig/pilo/tree/master/frontend&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;frontend&lt;/code&gt;&lt;/a&gt; - uses vanilla HTML/CSS/JS to display the interface, translate keypresses, and communicate with the server via websockets&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/flotwig/pilo/tree/master/server&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;server&lt;/code&gt;&lt;/a&gt; - the HTTP server, written in Node.js. Authenticates requests using HTTP basic auth, communicates with the keyboard controller via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;serialport&lt;/code&gt; library, and manages reverse-proxying of the &lt;a href=&quot;https://www.linux-projects.org/home/documentation/uv4l-server/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uv4l-server&lt;/code&gt;&lt;/a&gt; video stream&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are also end-to-end tests in the &lt;a href=&quot;https://github.com/flotwig/pilo/tree/master/e2e&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;e2e&lt;/code&gt;&lt;/a&gt; folder which use &lt;a href=&quot;https://cypress.io&quot;&gt;Cypress&lt;/a&gt; to test the application in real web browsers. This runs against Firefox and Chrome on every commit to CI via a &lt;a href=&quot;https://github.com/flotwig/pilo/blob/master/.github/workflows/test.yml&quot;&gt;GitHub Actions workflow&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/flotwig/pilo#pilo&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;README&lt;/code&gt;&lt;/a&gt; contains information on building and testing the project if you are interested in contributing. Built packages are also published to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm&lt;/code&gt; for production use.&lt;/p&gt;

&lt;h2 id=&quot;packaging-the-pilo&quot;&gt;Packaging the Pilo&lt;/h2&gt;

&lt;p&gt;One of my goals when building Pilo was to make it small enough to fit inside of my server case. Check out these photos to see how it fit in:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/pilo-in-server.jpg&quot; alt=&quot;Left: Before Pilo. Right: After Pilo.&quot; /&gt;
&lt;small&gt;Left: Server case before embedding the Pilo. Right: Server case after embedding the Pilo. HDMI, Ethernet, and micro-USB power are routed through the left-most PCI-E slot, while PS/2 is routed through the I/O shield in the top-right. The Pilo itself sits atop a ledge in the bottom-right of the image.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Here are some pictures of how the final assembly was made:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/pilo-assembly.jpg&quot; alt=&quot;Outer photo: Pi with USBs soldered on. Inner: Taped-up package. &quot; /&gt;
&lt;small&gt;Outer photo: Pi with the USB devices &lt;a href=&quot;https://raspberrypi.stackexchange.com/a/62678/100317&quot;&gt;soldered on to the underside&lt;/a&gt; to save space. Inset photo: Final package taped up and ready for install, with PS/2 cable coming out. Hot glue and tape make me the solderer I ain’t.&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;The completed package fits within a bounding box only slightly larger than the Pi itself, about 88mm x 60mm x 20mm, which means the Pilo can fit conveniently into a standard 3.5” hard drive bay.&lt;/p&gt;

&lt;h2 id=&quot;tutorial&quot;&gt;Tutorial&lt;/h2&gt;

&lt;h3 id=&quot;parts-list&quot;&gt;Parts List&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Raspberry Pi 3B+ ($25 at &lt;a href=&quot;https://www.microcenter.com/product/505661/Raspberry_Pi_3_B_Plus?src=raspberrypi&quot;&gt;MicroCenter&lt;/a&gt;, $35 everywhere else)
    &lt;ul&gt;
      &lt;li&gt;Or other micro linux computer - even a Pi Zero could work, but you’d be using WiFi, and you’d need a USB hub for the Arduino serial, or set up the Arduino serial via GPIO&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Arduino Nano (&lt;a href=&quot;https://smile.amazon.com/gp/product/B015MGHH6Q/&quot;&gt;Amazon&lt;/a&gt; has them at $16.99 for 5 - $3.40 each)
    &lt;ul&gt;
      &lt;li&gt;Any other 5V-logic-level Arduino would work as well. Needs to be 5V or have the CLK + DATA outputs converted from 3V3 to 5V, since the PS/2 serial connection expects 5V.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;USB HDMI Capture Card (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;video4linux&lt;/code&gt; compatible - most cards are) (&lt;a href=&quot;https://smile.amazon.com/gp/product/B08BZ52Q65/&quot;&gt;Amazon&lt;/a&gt;, $13.99)&lt;/li&gt;
  &lt;li&gt;(optional) PS/2 plug, to make a tidy connection&lt;/li&gt;
  &lt;li&gt;Supplies: Wires, soldering iron if you need to solder, microSD card and micro-USB power supply for the Pi…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Comes out to roughly $60 if you buy everything at it’s cheapest, $50 if you’re lucky enough to live near a MicroCenter.&lt;/p&gt;

&lt;h3 id=&quot;setup&quot;&gt;Setup&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;Before getting started, make sure you have your devices ready:
    &lt;ul&gt;
      &lt;li&gt;Have a Debian-based OS like Raspbian or DietPi installed on the Pi’s microSD card.&lt;/li&gt;
      &lt;li&gt;Flash the &lt;a href=&quot;https://create.arduino.cc/editor/flotwig__/093ababe-c724-476f-aeb8-a76b239bf192/preview&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;serial_to_ps2&lt;/code&gt;&lt;/a&gt; program to your Arduino Nano.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Connect everything according to this beautiful wiring diagram:
    &lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; ┌───────────────────────────┐
 │                   Ethernet├───────────────────────────┤Ext. Ethernet
 │ RasPi 3B+     USB Power In├───────────────────────────┤Ext. 5V Power
 │ (or other)        USB Port├───────────┐
 │                   USB Port├──────┐    │
 └───────────────────────────┘      │    │
                                    │    │ ┌────────────────────────────┐
 ┌───────────────────────────┐      │    └─┤USB Plug  HDMI Capture Card │
 │ Server            HDMI Out├──────┼──────┤HDMI In                     │
 │         PS/2 Keyboard Port├───┐  │      └────────────────────────────┘
 └───────────────────────────┘   │  │
                                 │  │
                                 │  │      ┌────────────────────────────┐
                                 │  └──────┤USB Port                    │
                                 │      ┌──┤D2 (DATA)      Arduino Nano │
                                 └──────┤──┤D3 (CLK)        (or other)  │
                                        └──┤GND (GND)                   │
                                           └────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;ul&gt;
      &lt;li&gt;Connect D2 (DATA), D3 (CLK), and GND from the Arduino Nano directly to the respective pins of the PS/2 keyboard port on the motherboard. Consult the Internet for a reference pinout.
        &lt;ul&gt;
          &lt;li&gt;You can dress this up by scavenging a spare PS/2 pigtail and stripping the end, but breadboard wires also fit &lt;a href=&quot;/assets/pilo-pin-jam.jpg&quot;&gt;snugly&lt;/a&gt; into the DIN port.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;&lt;em&gt;Turn off your server&lt;/em&gt; before connecting or disconnecting anything to/from the PS/2 port. The PS/2 port was not designed with hotplugging in mind. You risk damaging your components and your OS most likely won’t detect a hot-plugged PS/2 device, so don’t bother.&lt;/li&gt;
      &lt;li&gt;&lt;em&gt;Do not&lt;/em&gt; connect the +5V pin from the PS/2 port to VIN on the Arduino - this is a bad idea for a &lt;a href=&quot;https://forum.allaboutcircuits.com/threads/can-i-use-2-dc-power-supplies-together.27346/&quot;&gt;myriad of reasons&lt;/a&gt;.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Set up the MJPEG streaming server for the USB HDMI capture card.
    &lt;ol&gt;
      &lt;li&gt;Follow the instructions on the &lt;a href=&quot;http://www.linux-projects.org/uv4l/installation/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uv4l&lt;/code&gt;&lt;/a&gt; website to install the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt&lt;/code&gt; sources for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uv4l&lt;/code&gt;. &lt;em&gt;Note for Debian Buster users&lt;/em&gt;: There is not a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uv4l&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt&lt;/code&gt; repo for Buster yet, but the Stretch repo seem to work fine on Buster.&lt;/li&gt;
      &lt;li&gt;Update the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt&lt;/code&gt; index, and install the required packages:
        &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; apt update
 apt install uv4l uv4l-server uv4l-uvc uv4l-mjpegstream
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
      &lt;li&gt;Retrieve your USB HDMI capture card’s ID by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lsusb&lt;/code&gt;. It should be a hexadecimal string like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1a2b:3c4d&lt;/code&gt;.&lt;/li&gt;
      &lt;li&gt;Start the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uv4l&lt;/code&gt; server listening on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;127.0.0.1:9000&lt;/code&gt;, substituting your device ID for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1a2b:3c4d&lt;/code&gt; below:
        &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; uv4l --driver uvc --device-id &apos;1a2b:3c4d&apos; --auto-video_nr --server-option &apos;--port=9000&apos; --server-option &apos;--bind-host-address=127.0.0.1&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
        &lt;p&gt;You should be able to access a MJPEG stream of the USB HDMI capture card locally on the Pi at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://127.0.0.1:9000/stream/video.mjpeg&lt;/code&gt;. You will not be able to access this over the network, since it is bound to localhost by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--bind-host-address&lt;/code&gt;.&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;Set the command from (4) to run on every boot, for example, by adding it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/rc.local&lt;/code&gt;.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;Now, it is time to generate your auth credentials. Follow this algorithm:
    &lt;ol&gt;
      &lt;li&gt;Think of a secure username and a password. For example: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bar&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;Join them with a colon. For example: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo:bar&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;SHA256 the result. For example: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a765a8beaa9d561d4c5cbed29d8f4e30870297fdfa9cb7d6e9848a95fec9f937&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;This is your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AUTH_SHA&lt;/code&gt;.&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;Install Node.js and npm:
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; apt install nodejs npm
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Install the latest distribution of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pilo&lt;/code&gt; from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm&lt;/code&gt;:
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; npm install --global pilo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;You may need to pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--unsafe-perms&lt;/code&gt; if installing as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;You can now start &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pilo&lt;/code&gt; on port 3000 by running the command with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AUTH_SHA&lt;/code&gt; set in an environment variable:
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; AUTH_SHA=a765a8beaa9d561d4c5cbed29d8f4e30870297fdfa9cb7d6e9848a95fec9f937 pilo &amp;amp;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;You should now be able to access Pilo at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://&amp;lt;your-pi-IP&amp;gt;:3000/&lt;/code&gt;, using the username and password you created earlier.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;Add the above command to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/rc.local&lt;/code&gt; to run it on boot.&lt;/li&gt;
&lt;/ol&gt;

</description>
				<pubDate>Thu, 20 Aug 2020 13:46:50 +0000</pubDate>
				<link>https://zach.bloomqu.ist/blog/2020/08/pilo-raspberry-pi-lights-out-management.html</link>
				<guid isPermaLink="true">https://zach.bloomqu.ist/blog/2020/08/pilo-raspberry-pi-lights-out-management.html</guid>
			</item>
		
			<item>
				<title>Creating a Site-to-Site WireGuard VPN for a home server</title>
				<description>&lt;p&gt;&lt;img src=&quot;/assets/bezos-gazing.jpg&quot; alt=&quot;Image of Bezos gazing at you.&quot; /&gt;
&lt;small&gt;This guy is looking at pictures of my wife, probably. &lt;a href=&quot;https://news.sky.com/story/amazon-boss-jeff-bezos-claims-he-was-blackmailed-by-national-enquirer-over-below-the-belt-selfie-11631136&quot;&gt;photo source&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;For the last decade or so, I’ve been steadily increasing the amount of data I send to the cloud. I sync photos of my friends and family to Amazon Photos, blast my private data off to Microsoft OneDrive, give my passwords to 1password, and trust my web hosting provider not to run away with my data.&lt;/p&gt;

&lt;p&gt;I’ve been growing less satisfied with the privacy options afforded by major cloud providers. Amazon Photos, for example, has started using machine learning to identify the people in my photos. I’m not really keen on the mental image of Jeff Bezos, sitting on a yacht, looking at pictures of my wife.&lt;/p&gt;

&lt;p&gt;For that reason, I’ve decided to start moving my personal data into my personal control. I want the bits and bytes that describe the intimate details of my life to live around under my own roof and only escape to the Internet with my permission.&lt;/p&gt;

&lt;p&gt;I’d like to self-host &lt;a href=&quot;https://nextcloud.com/&quot;&gt;NextCloud&lt;/a&gt; (to replace Amazon Photos and OneDrive), static site hosting (to replace my existing VPS and GitHub Pages), and continuous integration using &lt;a href=&quot;https://about.gitlab.com/install/?version=ce&quot;&gt;GitLab CE&lt;/a&gt; (to replace Travis CI). It’s also a pipe dream to one day host my own email server, so I can move off of Google Apps.&lt;/p&gt;

&lt;p&gt;To make all that possible, I’d need a way for Internet traffic to reach my home LAN, behind a router with a dynamic IP. I don’t want to use &lt;a href=&quot;https://en.wikipedia.org/wiki/Dynamic_DNS&quot;&gt;dynamic DNS&lt;/a&gt;, since I think it provides a poor user experience due to DNS caching. Also, my Internet service provider does not explicitly allow server hosting, so excess incoming Internet traffic might get me in trouble. 😅&lt;/p&gt;

&lt;p&gt;The solution comes in the form of an Internet-facing server with a static IP. That server will receive requests and forward them to the LAN server through an encrypted, performant &lt;a href=&quot;https://www.wireguard.com/&quot;&gt;WireGuard&lt;/a&gt; tunnel:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;┌-------------------┐     ┌------------------------┐
| Internet Traffic  | &amp;lt;-&amp;gt; | Internet-Facing Server |
└-------------------┘     └------------------------┘
                                    /\
                                    || WireGuard Site-to-Site VPN
                                    \/
                             ┌-----------------┐
                             | Home LAN Server |
                             └-----------------┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I chose &lt;a href=&quot;https://www.wireguard.com/&quot;&gt;WireGuard&lt;/a&gt; over other VPN candidates because of the simplicity of configuration and low server overhead. Without further ado, let’s get into how to set this up.&lt;/p&gt;

&lt;h3 id=&quot;step-1-internet-facing-server-setup&quot;&gt;Step 1: Internet-Facing Server Setup&lt;/h3&gt;

&lt;p&gt;When choosing a server provider for your Internet-facing server, make sure to choose one with low latency to your home network, since that latency will be added to every request you make.&lt;/p&gt;

&lt;p&gt;If the provider has test servers listed on their website, you can &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ping&lt;/code&gt; them from your home network to make an estimate of the round-trip-time that will be added to each request.&lt;/p&gt;

&lt;p&gt;I chose &lt;a href=&quot;https://ramnode.com/&quot;&gt;RamNode&lt;/a&gt; for my hosting, since I get about 3ms of ping to their &lt;a href=&quot;https://clientarea.ramnode.com/knowledgebase/17/Do-you-have-a-test-IP-I-can-ping.html&quot;&gt;test IP&lt;/a&gt; in Atlanta:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~ ping &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 4 107.191.101.180
PING 107.191.101.180 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;107.191.101.180&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 56&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;84&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; bytes of data.
64 bytes from 107.191.101.180: &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;1 &lt;span class=&quot;nv&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;55 &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3.08 ms
64 bytes from 107.191.101.180: &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2 &lt;span class=&quot;nv&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;55 &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3.34 ms
64 bytes from 107.191.101.180: &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3 &lt;span class=&quot;nv&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;55 &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3.08 ms
64 bytes from 107.191.101.180: &lt;span class=&quot;nv&quot;&gt;icmp_seq&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;4 &lt;span class=&quot;nv&quot;&gt;ttl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;55 &lt;span class=&quot;nb&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;3.41 ms

&lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt; 107.191.101.180 ping statistics &lt;span class=&quot;nt&quot;&gt;---&lt;/span&gt;
4 packets transmitted, 4 received, 0% packet loss, &lt;span class=&quot;nb&quot;&gt;time &lt;/span&gt;7ms
rtt min/avg/max/mdev &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 3.076/3.226/3.410/0.165 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Since WireGuard is really efficient, you don’t need a beefy, expensive server to run it on. I chose a server with 512MB of RAM, 1 CPU core, and 2 TB of outgoing bandwidth per month for $3/mo. This will be the only real expense of this project.&lt;/p&gt;

&lt;p&gt;I installed CentOS on my Internet-facing server, but WireGuard is compatible with &lt;a href=&quot;https://www.wireguard.com/install/&quot;&gt;a wide variety of operating systems&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you have your server, SSH in and follow this guide to configuring WireGuard:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Install WireGuard by following &lt;a href=&quot;https://www.wireguard.com/install/&quot;&gt;the instructions for your server OS&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;After installing WireGuard, you will have access to the &lt;a href=&quot;https://git.zx2c4.com/WireGuard/about/src/tools/man/wg.8&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wg&lt;/code&gt;&lt;/a&gt; command, which we will use to generate public/private keypairs for the server and client.
    &lt;ul&gt;
      &lt;li&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wg genkey&lt;/code&gt; to generate a private key. This will be the server’s private key. This should be kept a secret, as it can be used to decrypt data sent to the server.&lt;/li&gt;
      &lt;li&gt;Now, pipe that result into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wg pubkey&lt;/code&gt; to generate the server’s public key. This will used later to configure the client to send encrypted messages to the server. For example: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo &quot;server-private-key&quot; | wg pubkey&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;Repeat the above steps to generate a private &amp;amp; public key for the LAN client.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Create a file using your favorite text editor in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/wireguard/wg0.conf&lt;/code&gt;, and fill it out using the below template. If you’re curious about the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wg0.conf&lt;/code&gt; file format, check out the &lt;a href=&quot;https://git.zx2c4.com/WireGuard/about/src/tools/man/wg-quick.8&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wg-quick&lt;/code&gt; man page&lt;/a&gt; for more information.
&lt;script src=&quot;https://gist.github.com/5d50cdcb8d1a3548ab3fc607e14f128d.js?file=internet-wg0.conf&quot;&gt; &lt;/script&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;
    &lt;p&gt;Now that you’ve configured the server, you can bring up the WireGuard interface by doing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wg-quick up wg0&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;li&gt;
    &lt;p&gt;Do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wg show&lt;/code&gt; to see the status of your WireGuard network:&lt;/p&gt;

    &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~ wg show
interface: wg0
public key: your-server-public-key
private key: &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;hidden&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
listening port: 51820

peer: your-client-public-key
allowed ips: 10.222.0.2/32
persistent keepalive: every 25 seconds
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

  &lt;/li&gt;
&lt;li&gt;
    &lt;p&gt;Now use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;systemctl enable wg-quick@wg0&lt;/code&gt; to ensure that this interface is brought up on every boot.&lt;/p&gt;
  &lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;Congrats! Your Internet-facing server is now set up to act as a WireGuard host. Now let’s proceed to the client configuration on the LAN server.&lt;/p&gt;

&lt;h3 id=&quot;step-2-lan-server-setup&quot;&gt;Step 2: LAN Server Setup&lt;/h3&gt;

&lt;p&gt;Follow these instructions on your home LAN server to set it up as a WireGuard client:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Install WireGuard using the &lt;a href=&quot;https://www.wireguard.com/install/&quot;&gt;installation instructions for your OS&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Create a file using your favorite text editor in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/wireguard/wg0.conf&lt;/code&gt;, and fill it out using the below template. Again, for more info on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wg0.conf&lt;/code&gt; file format, check out the &lt;a href=&quot;https://git.zx2c4.com/WireGuard/about/src/tools/man/wg-quick.8&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wg-quick&lt;/code&gt; man page&lt;/a&gt;.
&lt;script src=&quot;https://gist.github.com/5d50cdcb8d1a3548ab3fc607e14f128d.js?file=lan-wg0.conf&quot;&gt; &lt;/script&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;
    &lt;p&gt;Now that you’ve configured the client, you can bring up the WireGuard interface by doing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wg-quick up wg0&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;li&gt;
    &lt;p&gt;Do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wg show&lt;/code&gt; to see the status of your WireGuard network:&lt;/p&gt;

    &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~ wg show
interface: wg0
  public key: your-client-private-key
  private key: &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;hidden&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
  listening port: 55018

peer: your-server-public-key
  endpoint: your-server-domain-name-or-IP-address:51820
  allowed ips: 10.222.0.0/16
  latest handshake: 1 minute, 41 seconds ago
  transfer: 959.23 MiB received, 1.57 GiB sent
  persistent keepalive: every 25 seconds
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

  &lt;/li&gt;
&lt;li&gt;
    &lt;p&gt;At this point, you should be able to do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ping 10.222.0.1&lt;/code&gt; to reach your WireGuard server through your new VPN.&lt;/p&gt;
  &lt;/li&gt;
&lt;li&gt;
    &lt;p&gt;Now use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;systemctl enable wg-quick@wg0&lt;/code&gt; to ensure that this interface is brought up on every boot.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now your VPN is set up and you are ready to start exposing services on your home server through your VPN.&lt;/p&gt;

&lt;h3 id=&quot;step-3-start-exposing-services&quot;&gt;Step 3: Start Exposing Services&lt;/h3&gt;

&lt;p&gt;You’ll need a way to proxy traffic that hits your Internet-facing server through the VPN to your home server.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;For &lt;strong&gt;HTTP traffic&lt;/strong&gt;, set up a reverse proxy on the Internet-facing server. My tool of choice for this is &lt;a href=&quot;https://nginx.org/&quot;&gt;nginx&lt;/a&gt;, which has a fantastic &lt;a href=&quot;https://nginx.org/en/docs/http/ngx_http_proxy_module.html&quot;&gt;reverse proxy module&lt;/a&gt;. Here’s a very basic nginx config to proxy traffic for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;example.com&lt;/code&gt; to port 8080 on your LAN server:
    &lt;div class=&quot;language-conf highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; {
  &lt;span class=&quot;n&quot;&gt;server_name&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;example&lt;/span&gt;.&lt;span class=&quot;n&quot;&gt;com&lt;/span&gt;;
  &lt;span class=&quot;n&quot;&gt;location&lt;/span&gt; / {
    &lt;span class=&quot;n&quot;&gt;proxy_pass&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;://&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;222&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;:&lt;span class=&quot;m&quot;&gt;8080&lt;/span&gt;/;
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;For &lt;strong&gt;other TCP/IP traffic&lt;/strong&gt;, set up &lt;a href=&quot;https://github.com/boutell/rinetd&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rinetd&lt;/code&gt;&lt;/a&gt; on the Internet-facing server. It will tunnel TCP traffic on one port/interface to another port/interface. For example, if you have an IRC server running on port 6667 of your home server, you could put this in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/rinetd.conf&lt;/code&gt; to forward traffic from port 6667 of the Internet-facing server:
    &lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# bind to all interfaces on 6667 and pass to LAN server
0.0.0.0 6667 10.222.0.2 6667
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With both of these methods, keep in mind that the IP of the original client will be obscured by the reverse proxy. You’ll need to use other methods (such as an &lt;a href=&quot;https://en.wikipedia.org/wiki/X-Forwarded-For&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;X-Proxied-For&lt;/code&gt; header&lt;/a&gt; containing the real client’s IP address) if you want to receive the client’s real IP at your home server.&lt;/p&gt;

&lt;p&gt;Now you can start moving all of the services you want to self-host under your own roof! In future articles, I will discuss setting up your own self-hosted photo storage, continuous integration pipelines, web hosting, and others.&lt;/p&gt;

&lt;h3 id=&quot;extra-securing-your-internet-facing-server&quot;&gt;Extra: Securing Your Internet-Facing Server&lt;/h3&gt;

&lt;p&gt;One of the benefits to this setup is that you no longer need to expose your Internet-facing server’s SSH port publicly. You can use the VPN to access it instead.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Set up your computer as a WireGuard client using the same method that you used to set up your home LAN server as a client. Or, just use your home LAN server as a &lt;a href=&quot;https://en.wikipedia.org/wiki/Bastion_host&quot;&gt;bastion host&lt;/a&gt;, so you must be SSH’d into it to SSH into your Internet-facing server.&lt;/li&gt;
  &lt;li&gt;Set up &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ufw&lt;/code&gt; on your Internet-facing server using these commands:
    &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;c&quot;&gt;# turn on ufw&lt;/span&gt;
  ufw &lt;span class=&quot;nb&quot;&gt;enable&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;# allow inbound access to WireGuard&apos;s port&lt;/span&gt;
  ufw allow 51820/udp
  &lt;span class=&quot;c&quot;&gt;# allow VPN IPs to access SSH on port 22&lt;/span&gt;
  ufw allow from 10.222.0.0/24 to any port 22 proto tcp
  &lt;span class=&quot;c&quot;&gt;# remove default SSH allow rules&lt;/span&gt;
  ufw delete allow SSH
  ufw delete allow 22/tcp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now you should only be able to access SSH on your Internet-facing server via the VPN IP address, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.222.0.1&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;extra-alternative-wireguard-distributions&quot;&gt;Extra: Alternative WireGuard Distributions&lt;/h3&gt;

&lt;p&gt;The official &lt;a href=&quot;https://www.wireguard.com/&quot;&gt;WireGuard&lt;/a&gt; distribution comes as a kernel mod. While the official implementation is best, there are also some alternatives that run in userspace, if you’re unwilling/unable to install a kernel mod:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://git.zx2c4.com/wireguard-go/about/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wireguard-go&lt;/code&gt;&lt;/a&gt; - This is WireGuard’s official userspace implementation, written in Go. Recommended.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://git.zx2c4.com/wireguard-rs&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wireguard-rs&lt;/code&gt;&lt;/a&gt; - Another userspace implementation, also by the WireGuard authors, written in Rust. WIP, not recommended for production.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/cloudflare/boringtun&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;boringtun&lt;/code&gt;&lt;/a&gt; - Cloudflare’s unofficial userspace WireGuard client, also written in Rust. Used in their proprietary &lt;a href=&quot;https://developers.cloudflare.com/argo-tunnel/&quot;&gt;Argo Tunnel&lt;/a&gt; Site-to-Site VPN. &lt;em&gt;Note: the original author of WireGuard, Jason A. Donenfield, &lt;a href=&quot;https://lists.zx2c4.com/pipermail/wireguard/2019-March/004048.html&quot;&gt;has expressed some opinions about Cloudflare’s involvement in WireGuard&lt;/a&gt;.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
				<pubDate>Sat, 02 Nov 2019 13:17:40 +0000</pubDate>
				<link>https://zach.bloomqu.ist/blog/2019/11/site-to-site-wireguard-vpn.html</link>
				<guid isPermaLink="true">https://zach.bloomqu.ist/blog/2019/11/site-to-site-wireguard-vpn.html</guid>
			</item>
		
			<item>
				<title>Dockerizing a Python 3 Flask App Line-by-Line</title>
				<description>&lt;p&gt;If you’re like me, you end up writing a lot of Flask apps for random web applications and APIs. When you’re done building these Flask apps, typically, you need to deploy them to production somehow. Unless you’re using a container service like Heroku, deployment is synonymous with SSHing to a live server, installing your dependencies, and starting your application from source.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docker.io&quot;&gt;Docker&lt;/a&gt; makes the process of deployment easier by containerizing your app and automatically installing dependencies from a simple declarative config called the Dockerfile. In your Dockerfile, you can specify the operating system to run on, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yum&lt;/code&gt; commands to run before your application starts, or any other logic you can think of. Docker will generate an image from this and handle running your application using that image inside a container.&lt;/p&gt;

&lt;p&gt;In this guide, we’ll walk through the process of writing a Dockerfile for a modern Flask app. By the time we’re done, we’ll have a nice Ubuntu + nginx + uwsgi + Flask stack all working.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;requirements&quot;&gt;Requirements&lt;/h3&gt;

&lt;p&gt;Before we begin, ensure that you have &lt;a href=&quot;https://docker.io&quot;&gt;Docker Community Edition&lt;/a&gt; installed. Instructions vary by operating system but it is available for Windows, OS X, and Linux.&lt;/p&gt;

&lt;p&gt;Also, you’ll need a Python 3 Flask application to deploy. Make sure that in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app.py&lt;/code&gt; , any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app.run()&lt;/code&gt; calls are wrapped in an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if __name__ == &apos;__main__&apos;&lt;/code&gt; check so that uwsgi does not accidentally spawn 2 copies of your server.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;the-dockerfile&quot;&gt;The Dockerfile&lt;/h3&gt;

&lt;p&gt;First, create a file named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; (no extension) in the root of your Flask application and open it in your favorite text editor.&lt;/p&gt;

&lt;p&gt;Docker uses a very simple, declarative language to define the build process. Let’s start off by identifying the operating system we’d like to use:&lt;/p&gt;

&lt;pre name=&quot;1b05&quot; id=&quot;1b05&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;FROM ubuntu:18.10&lt;/pre&gt;

&lt;p&gt;This tells Docker to fetch the Ubuntu 18.10 Cosmic Cuttlefish disk image from the &lt;a href=&quot;https://hub.docker.com/_/ubuntu/&quot;&gt;Docker official repository&lt;/a&gt; and use it as the base OS for this container. Next, let’s add some information about the maintainer of this package:&lt;/p&gt;

&lt;pre name=&quot;2da6&quot; id=&quot;2da6&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;LABEL maintainer=&quot;Zach Bloomquist &amp;lt;zach@bloomqu.ist&amp;gt;&quot;&lt;/pre&gt;

&lt;p&gt;Docker doesn’t use this information for anything except for setting the author field of the created image. It’s also nice to leave a breadcrumb for developers who may come after you. Next:&lt;/p&gt;

&lt;pre name=&quot;5904&quot; id=&quot;5904&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;RUN apt-get update
RUN apt-get install -y python3 python3-dev python3-pip nginx
RUN pip3 install uwsgi&lt;/pre&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RUN&lt;/code&gt; commands are executed while building the Docker image. These will update apt’s package index and then fetch our dependencies. The package &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python3-dev&lt;/code&gt; may stand out to you — this package is required for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uwsgi&lt;/code&gt; to build when we install it with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt; .&lt;/p&gt;

&lt;pre name=&quot;ea92&quot; id=&quot;ea92&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;COPY ./ ./app
WORKDIR ./app&lt;/pre&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY&lt;/code&gt; command copies files from the source’s filesystem to the container’s filesystem. These commands copy over the application’s source code to a new folder and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd&lt;/code&gt; into it for the rest of the build. Next:&lt;/p&gt;

&lt;pre name=&quot;3ba6&quot; id=&quot;3ba6&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;RUN pip3 install -r requirements.txt&lt;/pre&gt;

&lt;p&gt;This installs the requirements for your Python 3 app to execute, assuming you list your dependencies in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requirements.txt&lt;/code&gt;.&lt;/p&gt;

&lt;pre name=&quot;7481&quot; id=&quot;7481&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;COPY ./nginx.conf /etc/nginx/sites-enabled/default&lt;/pre&gt;

&lt;p&gt;This command sets up the configuration for nginx inside the container by overwriting Ubuntu’s default. It assumes you have a proper nginx configuration at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./nginx.conf&lt;/code&gt; containing something like this:&lt;/p&gt;

&lt;pre name=&quot;47cc&quot; id=&quot;47cc&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;server {
  location @flask {
    include uwsgi_params;
    uwsgi_pass unix://tmp/uwsgi.sock;
  }
  location / {
    try_files @flask;
  }
}&lt;/pre&gt;

&lt;p&gt;This example config will pass all requests to the container’s port 80 to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uwsgi&lt;/code&gt; application listening on that socket.&lt;/p&gt;

&lt;p&gt;Now, back to the Dockerfile:&lt;/p&gt;

&lt;pre name=&quot;ed09&quot; id=&quot;ed09&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;CMD service nginx start &amp;amp;&amp;amp; uwsgi -s /tmp/uwsgi.sock --chmod-socket=666 --manage-script-name --mount /=app:app&lt;/pre&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CMD&lt;/code&gt; command tells Docker what command to execute when someone runs the image our Dockerfile creates. In this case, we want to start nginx, then start up uwsgi to back it.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;building-running&quot;&gt;Building &amp;amp; Running&lt;/h3&gt;

&lt;p&gt;Now that our Dockerfile is created, let’s build the image from the current directory:&lt;/p&gt;

&lt;pre name=&quot;4886&quot; id=&quot;4886&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;docker build -t my-image-name .&lt;/pre&gt;

&lt;p&gt;You can watch Docker chew through installing Ubuntu, installing the system packages, and installing our pip requirements. Once that’s done, you’re ready to boot up a new container based off your image:&lt;/p&gt;

&lt;pre name=&quot;3e24&quot; id=&quot;3e24&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;docker run -d -p 1337:80 my-image-name&lt;/pre&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-d&lt;/code&gt; option tells Docker to run the container in the background and print out the container ID. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-p 1337:80&lt;/code&gt; maps port 1337 on the host machine to port 80 in the container.&lt;/p&gt;

&lt;p&gt;Now that your container is running and port 1337 is mapped, you should be able to visit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[http://localhost:1337/](http://localhost:1337/)&lt;/code&gt; and see your Flask application running. Woohoo!&lt;/p&gt;

&lt;p&gt;You can use the container ID printed out by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run -d&lt;/code&gt; to manage the life cycle of your app. Here are some good CLI commands to know:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image ls&lt;/code&gt; — list available images&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container ls&lt;/code&gt; — list all containers&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker logs &amp;lt;partial container ID&amp;gt;&lt;/code&gt; — tail logs from a container&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker kill &amp;lt;partial container ID&amp;gt;&lt;/code&gt; — kill execution of a container&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker restart &amp;lt;partial container ID&amp;gt;&lt;/code&gt; — restart container&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker start &amp;lt;partial container ID&amp;gt;&lt;/code&gt; — start stopped container&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker stop &amp;lt;partial container ID&amp;gt;&lt;/code&gt; — gracefully end container&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container prune&lt;/code&gt; — delete all non-running containers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I hope this guide was helpful to you in Dockerizing your Flask application. If you have any comments or questions, please leave them below.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;appendix-nextsteps&quot;&gt;Appendix: Next Steps&lt;/h3&gt;

&lt;p&gt;If you really want to turbo-charge your Dockerization, try these tips:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Make use of named containers so you don’t have to keep track of container IDs. Pass the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--name my-awesome-name&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run&lt;/code&gt; to name your new containers, then you can do things like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker restart my-awesome-name&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Create shell script helpers to help your users interact with the Dockerized app. For example, you might create a shell script called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;install.sh&lt;/code&gt; that ensures Docker is installed, then builds the Docker image and ensures it runs on startup.&lt;/li&gt;
  &lt;li&gt;Use Docker Compose to further abstract your deployment. Docker Compose allows you to orchestrate multiple containers for one deployment. In this example, we might’ve used a container just running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx&lt;/code&gt; to proxy requests to a separate container running our Flask app.&lt;/li&gt;
&lt;/ul&gt;
</description>
				<pubDate>Fri, 20 Jul 2018 00:00:00 +0000</pubDate>
				<link>https://zach.bloomqu.ist/blog/2018/07/dockerizing-flask.html</link>
				<guid isPermaLink="true">https://zach.bloomqu.ist/blog/2018/07/dockerizing-flask.html</guid>
			</item>
		
			<item>
				<title>Moving From React to Preact: A Developer’s Story</title>
				<description>&lt;p&gt;&lt;img src=&quot;https://cdn-images-1.medium.com/max/1600/1*mThPH_B2Ms7qG_nVySy3qA.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For the past few months, I’ve been working on a single-page application built using Facebook’s &lt;a href=&quot;https://reactjs.org&quot;&gt;React&lt;/a&gt; framework.&lt;/p&gt;

&lt;p&gt;Those who know me know that I’m obsessed with performance. Getting me on the React bandwagon took years simply because I didn’t like the bloat it introduces. Even though I’ve grown to love React for the deftness it affords when designing front-ends, I still have an issue with all the bloat it introduces.&lt;/p&gt;

&lt;p&gt;Recently, I heard about the &lt;a href=&quot;https://preactjs.com/&quot;&gt;Preact&lt;/a&gt; project — a drop-in, API-compatible replacement for the React framework that’s only &lt;strong&gt;3kb&lt;/strong&gt; in size compared to React’s &lt;strong&gt;135kb&lt;/strong&gt;. It’s faster and easier to understand than React. When I heard about this framework, I knew I had to try it. I’ve documented my efforts here (including some troubleshooting tips) to guide others who would follow this path.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;the-reactapp&quot;&gt;The React App&lt;/h3&gt;

&lt;p&gt;Before transitioning to Preact, I wanted to examine my existing app so I could be aware of any potential pain points I’d encounter while transitioning.&lt;/p&gt;

&lt;p&gt;My application was created with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create-react-app&lt;/code&gt;. This means a lot of the complexity of the build environment is hidden away in packages like react-scripts. This is good for a new user, but potentially bad when trying to switch away from React.&lt;/p&gt;

&lt;p&gt;I make heavy use of React’s &lt;a href=&quot;https://reactjs.org/docs/context.html#reactcreatecontext&quot;&gt;createContext&lt;/a&gt; API to enable authentication and back-end use throughout the app. Preact only supports the &lt;a href=&quot;https://reactjs.org/docs/legacy-context.html&quot;&gt;legacy context API&lt;/a&gt;, so I needed to use Georgios Valotasios’s &lt;a href=&quot;https://github.com/valotas/preact-context&quot;&gt;preact-context&lt;/a&gt; library to provide those APIs.&lt;/p&gt;

&lt;p&gt;My app uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;react-router&lt;/code&gt; for routing and passing state between linked components. I had some concerns about this working.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;sreactpreactg&quot;&gt;s/React/Preact/g&lt;/h3&gt;

&lt;p&gt;Preact has a &lt;a href=&quot;https://preactjs.com/guide/switching-to-preact&quot;&gt;helpful guide for switching&lt;/a&gt;. They provide a shim, preact-compat, that allows you to swap your codebase over with minimal code changes. There’s also a longer guide to migrating your codebase to use preact without the compat libraries.&lt;/p&gt;

&lt;p&gt;I chose to do the full migration in order to take advantage of the full performance benefits — the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;preact-compat&lt;/code&gt; library adds 2kb to Preact’s 3kb size. I wouldn’t have blinked at 2kb before, but now the bloat of React is no longer in the way, 2kb seems like a whole lot more.&lt;/p&gt;

&lt;p&gt;First, install the preact package and get rid of react:&lt;/p&gt;

&lt;pre name=&quot;5af4&quot; id=&quot;5af4&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;yarn add preact
yarn remove react react-dom&lt;/pre&gt;

&lt;p&gt;This will make all of your React plugins begin to throw warnings about unmet peer dependencies, this is fine. Currently there is no way to tell these plugins about Preact.&lt;/p&gt;

&lt;p&gt;Then, use the following command to replace all the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;import X from &apos;react&apos;&lt;/code&gt; statements in my package with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;import X from &apos;preact&apos;&lt;/code&gt; (assumes all your code is in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src&lt;/code&gt;):&lt;/p&gt;

&lt;pre name=&quot;195f&quot; id=&quot;195f&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;find ./src/ -type f -print0 | xargs -0 sed -Ei &quot;s/([&apos;\&quot;])react(-dom)?([&apos;\&quot;])/&apos;preact&apos;/g&quot;&lt;/pre&gt;

&lt;p&gt;The import in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.js&lt;/code&gt; must be corrected manually. Replace the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;React&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReactDOM&lt;/code&gt; imports with&lt;/p&gt;

&lt;pre name=&quot;8685&quot; id=&quot;8685&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;import React, { render } from &apos;preact&apos;;&lt;/pre&gt;

&lt;p&gt;Preact moves the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;render&lt;/code&gt; method out of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReactDOM&lt;/code&gt; and into its own export, so change the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ReactDOM.render&lt;/code&gt; call to just a call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;render&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;To fix React Contexts, install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;preact-context&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yarn add preact-context&lt;/code&gt;. Then, add the import statement to all your Contexts:&lt;/p&gt;

&lt;pre name=&quot;b94a&quot; id=&quot;b94a&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;import { createContext } from &apos;preact-context&apos;;&lt;/pre&gt;

&lt;p&gt;After changing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;React.createContext&lt;/code&gt; calls to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;createContext&lt;/code&gt; calls, all that’s left to do is run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yarn start&lt;/code&gt; and pray.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;youre-done&quot;&gt;You’re done!&lt;/h3&gt;

&lt;p&gt;(In a perfect world,) your app should now be working with Preact instead of React. Kudos to the developers of Preact for creating a drop-in replacement for the temperamental React framework. Enjoy your faster load times, easier-to-debug code base, and lower bandwidth usage!&lt;/p&gt;

&lt;p&gt;If you did encounter issues, continue on to Troubleshooting, where I’ve outlined a few of the issues I ran into when doing this on my app.&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;troubleshooting&quot;&gt;Troubleshooting&lt;/h3&gt;

&lt;p&gt;Of course, my migration did not go flawlessly — things never seem to, do they? I hope yours does, but if it does not, here are the issues I encountered and how I overcame them.&lt;/p&gt;

&lt;h4 id=&quot;context-changes&quot;&gt;Context changes&lt;/h4&gt;

&lt;p&gt;My first issue was that changes being broadcast from a Context were no longer being sent to child components. I had the Context set up like this:&lt;/p&gt;

&lt;pre name=&quot;b64f&quot; id=&quot;b64f&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;&amp;lt;Provider value={this.state}&amp;gt; ... &amp;lt;/Provider&amp;gt;&lt;/pre&gt;

&lt;p&gt;Context Consumers are supposed to re-render whenever the Provider’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;value&lt;/code&gt; changes. In React, the above line would cause all dependent components to re-render when the Provider’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;state&lt;/code&gt; changed.&lt;/p&gt;

&lt;p&gt;In Preact, this stopped working. I’m assuming this is because Preact does not copy &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;state&lt;/code&gt; on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setState&lt;/code&gt;; instead, it modifies &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;state&lt;/code&gt; in place. Because JS object comparisons are done by reference, no change appears to happen and so the Consumer components are never re-rendered.&lt;/p&gt;

&lt;p&gt;To get around this, I’m copying &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;state&lt;/code&gt; to a new object whenever it changes:&lt;/p&gt;

&lt;pre name=&quot;1eea&quot; id=&quot;1eea&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;&amp;lt;Provider value={Object.assign({}, this.state)}&amp;gt; ... &amp;lt;/Provider&amp;gt;&lt;/pre&gt;

&lt;p&gt;If anyone knows a cleaner way, please let me know.&lt;/p&gt;

&lt;h4 id=&quot;react-developer-tools&quot;&gt;React Developer Tools&lt;/h4&gt;

&lt;p&gt;I discovered that my React developer tools were no longer working. This is because Preact does not include the developer tools in the main bundle to cut down on unnecessary load time.&lt;/p&gt;

&lt;p&gt;To fix this, I just added the following line to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.js&lt;/code&gt; to load the debug module if this module is being hot-reloaded by Webpack:&lt;/p&gt;

&lt;pre name=&quot;111d&quot; id=&quot;111d&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;if(module.hot) require(&apos;preact/debug&apos;)&lt;/pre&gt;

&lt;h4 id=&quot;react-routererrors&quot;&gt;React Router errors&lt;/h4&gt;

&lt;p&gt;The last issue I encountered was a problem with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;react-router-dom&lt;/code&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BrowserRouter&lt;/code&gt; component was throwing the following error during render:&lt;/p&gt;

&lt;pre name=&quot;7e89&quot; id=&quot;7e89&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;Warning: Failed prop type: Invalid prop `children` supplied to `Router`, expected a ReactNode.&lt;/pre&gt;

&lt;p&gt;For some reason, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;div&amp;gt;&lt;/code&gt; element that was the immediate child of my BrowserRouter was not being recognized as a valid component. I tracked down this error to the definition of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isValidElement&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prop-types&lt;/code&gt;:&lt;/p&gt;

&lt;pre name=&quot;f951&quot; id=&quot;f951&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;var REACT_ELEMENT_TYPE = (typeof Symbol === &apos;function&apos; &amp;amp;&amp;amp;
  Symbol.for &amp;amp;&amp;amp;
  Symbol.for(&apos;react.element&apos;)) ||
  0xeac7;&lt;/pre&gt;

&lt;pre name=&quot;4d08&quot; id=&quot;4d08&quot; class=&quot;graf graf--pre graf-after--pre&quot;&gt;var isValidElement = function(object) {
  return typeof object === &apos;object&apos; &amp;amp;&amp;amp;
  object !== null &amp;amp;&amp;amp;
  object.$typeof === REACT_ELEMENT_TYPE;
};&lt;/pre&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;div&amp;gt;&lt;/code&gt; fails the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isValidElement&lt;/code&gt; test because it has no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$$typeof&lt;/code&gt; property. The only thing I can seem to figure out about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$$typeof&lt;/code&gt; is that it’s a special property React uses in order to distinguish React-created DOM objects from native DOM objects.&lt;/p&gt;

&lt;p&gt;I tried adding a hook to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;preact&lt;/code&gt; to automatically populate the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$$typeof&lt;/code&gt; on new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VNode&lt;/code&gt; (virtual nodes) in the DOM, but that just uncovered another host of issues related to differences between the React virtual DOM and Preact’s VDOM.&lt;/p&gt;

&lt;p&gt;So, with a heavy heart (it’s 2 kilobytes of code!!), I decided to install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;preact-compat&lt;/code&gt; (2kb!!!) and patch it into my app.&lt;/p&gt;

&lt;p&gt;Doing this was fairly simple, just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yarn add preact-compat&lt;/code&gt; and add 2 dependencies to your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resolve.alias&lt;/code&gt; configuration in your webpack config:&lt;/p&gt;

&lt;pre name=&quot;8d73&quot; id=&quot;8d73&quot; class=&quot;graf graf--pre graf-after--p&quot;&gt;alias: {
  &apos;react&apos;: &apos;react-compat&apos;,
  &apos;react-dom&apos;: &apos;react-compat&apos;
},&lt;/pre&gt;

&lt;p&gt;Note: Because I created my app with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;create-react-app&lt;/code&gt;, I had to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yarn eject&lt;/code&gt; so that I could edit my webpack config instead of using one from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node_modules&lt;/code&gt;. You may or may not have to do this first.&lt;/p&gt;

&lt;p&gt;After doing this, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;react-router-dom&lt;/code&gt; worked like a charm.&lt;/p&gt;

&lt;p&gt;I also went through my project and replaced all my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;preact&lt;/code&gt; imports with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;preact-compat&lt;/code&gt; just to match the documented way of doing it:&lt;/p&gt;

&lt;pre name=&quot;ce78&quot; id=&quot;ce78&quot; class=&quot;graf graf--pre graf-after--p graf--trailing&quot;&gt;find ./src/ -type f -print0 | xargs -0 sed -Ei &quot;s/&apos;preact&apos;/&apos;preact-compat&apos;/g&quot;&lt;/pre&gt;
</description>
				<pubDate>Tue, 17 Jul 2018 00:00:00 +0000</pubDate>
				<link>https://zach.bloomqu.ist/blog/2018/07/react-to-preact.html</link>
				<guid isPermaLink="true">https://zach.bloomqu.ist/blog/2018/07/react-to-preact.html</guid>
			</item>
		
			<item>
				<title>A survey of crossdomain.xml vulnerabilities</title>
				<description>&lt;p&gt;Vulnerable crossdomain.xml files can be used by malicious people to run CSRF attacks if the victim has Flash installed on their computer. In response to a post by chs on &lt;a href=&quot;http://www.chs.us/liberal-crossdomain-xml-exploit-example/&quot;&gt;crossdomain.xml proofs of concept&lt;/a&gt; and Seth Art’s &lt;a href=&quot;http://sethsec.blogspot.com/2014/07/crossdomain-bing.html&quot;&gt;real-world exploit of Bing using crossdomain.xml&lt;/a&gt;, I created an application in Ruby which parses the &lt;a href=&quot;http://s3.amazonaws.com/alexa-static/top-1m.csv.zip&quot;&gt;Alexa top million site list (CSV, 10MB)&lt;/a&gt; and scans for vulnerable crossdomain.xml files. Vulnerable here is defined as a crossdomain.xml file which permits connections from any domain name (*). It sorts the domains into four categories:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Unable to connect: Ruby was unable to establish a connection to the website. Interestingly enough, a significant portion of Alexa’s top million sites were inaccessible during this survey.&lt;/li&gt;
  &lt;li&gt;Invalid or 404: Returned 404 or the returned XML was not valid.&lt;/li&gt;
  &lt;li&gt;Secure: The XML returned does not contain a reference to allow-access-from domain=”*”. This does not necessarily mean that the whole crossdomain.xml file is secure, just that it is not vulnerable to the most basic of CSRF exploits.&lt;/li&gt;
  &lt;li&gt;Permissive: The XML returned from a GET to /crossdomain.xml does allow access from any domain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without further ado, let’s get into it.&lt;/p&gt;

&lt;h4 id=&quot;the-code&quot;&gt;The Code&lt;/h4&gt;

&lt;p&gt;I chose Ruby for this project because it has good XML processing libraries, is reasonably fast, and because I needed an excuse to practice Ruby.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;net/http&apos;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;rexml/document&apos;&lt;/span&gt;
&lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;REXML&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;csv&apos;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;counters&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&apos;unconnect&apos;&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&apos;invalid-404&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&apos;permissive&apos;&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&apos;secure&apos;&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;s1&quot;&gt;&apos;total-count&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;trap&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;SIGINT&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;counters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;130&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;permissive&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CSV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;permissive.csv&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;wb&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;CSV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;top-1m.csv&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;counters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;total-count&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Getting &apos;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;... &apos;&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;xd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Net&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;/crossdomain.xml&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;counters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;unconnect&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;unable to connect&apos;&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;next&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;xd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;REXML&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;counters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;invalid-404&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;invalid xml&apos;&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;next&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;wildcard_access&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;
	&lt;span class=&quot;no&quot;&gt;XPath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;//allow-access-from&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;access&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;access&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;domain&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;*&apos;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# &amp;lt;allow-access-from domain=&quot;*&quot;&amp;gt;
&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;wildcard_access&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;counters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;permissive&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;permissive&apos;&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;permissive&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wildcard_access&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;counters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;secure&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
		&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;secure&apos;&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;counters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h4 id=&quot;the-results&quot;&gt;The Results&lt;/h4&gt;

&lt;script type=&quot;text/javascript&quot; src=&quot;https://www.google.com/jsapi&quot;&gt;&lt;/script&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
google.load(&quot;visualization&quot;, &quot;1&quot;, {packages:[&quot;corechart&quot;]});
google.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable([
[&apos;Task&apos;, &apos;Sites&apos;],
[&apos;Unable to connect&apos;, 3535],
[&apos;Permissive&apos;,        4653],
[&apos;Invalid or 404&apos;,    84883],
[&apos;Secure&apos;,            67097]
]);

var options = {
title: &apos;crossdomain.xml Breakdown&apos;,
slices: { 1: {offset: 0.2} },
pieStartAngle: 90
};

var chart = new google.visualization.PieChart(document.getElementById(&apos;piechart&apos;));
chart.draw(data, options);
}
&lt;/script&gt;

&lt;div id=&quot;piechart&quot; style=&quot;width: 100%;&quot;&gt;&lt;/div&gt;

&lt;p&gt;After 160,169 websites were inspected over the course of a few days, the script hung.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;3,535 (2.2%) of the websites were down at the time of the scan.&lt;/li&gt;
  &lt;li&gt;84,883 (53%) of the websites had invalid or non-existent XML files at /crossdomain.xml.&lt;/li&gt;
  &lt;li&gt;67,097 (41.9%) of the websites surveyed had a “secure” crossdomain.xml file.&lt;/li&gt;
  &lt;li&gt;4,653 (2.9%) of the websites surveyed had insecure crossdomain.xml files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A wildcard crossdomain.xml file is fine for certain websites, but a quick scan of the results reveals a number of banks, bitcoin websites, and popular entertainment sites (9gag and Vimeo included) with poor crossdomain.xml files. &lt;a href=&quot;/assets/permissive.csv&quot;&gt;The results as a CSV with columns corresponding to the Alexa rank and the domain name.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although a full scan of the Alexa top million was not completed, an alarmingly large number of sites have overly permissive and insecure crossdomain.xml files.&lt;/p&gt;
</description>
				<pubDate>Fri, 15 Aug 2014 07:15:00 +0000</pubDate>
				<link>https://zach.bloomqu.ist/blog/2014/08/crossdomain-vulnerabilities.html</link>
				<guid isPermaLink="true">https://zach.bloomqu.ist/blog/2014/08/crossdomain-vulnerabilities.html</guid>
			</item>
		
			<item>
				<title>spoofident: A fake identd written in Python</title>
				<description>&lt;p&gt;&lt;img src=&quot;/images/spoofident.png&quot; alt=&quot;The workhorse function of spoofident&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Many protocols such as IRC require or strongly suggest the use of an &lt;a href=&quot;http://tools.ietf.org/html/rfc1413&quot;&gt;ident&lt;/a&gt; daemon to prove that you are who you say you are, or to hold you accountable for your actions. An identd is supposed to respond to queries as to which user is using which port; however, this information can be potentially dangerous. A real identd allows attackers to gain information about your system - usernames, active ports, even a fingerprint of your active operating system. The RFC linked above even cites these vulnerabilities.&lt;/p&gt;

&lt;p&gt;I had a need to run an ident server; however, I am wary of creating unnecessary security holes in my server. That’s why I wrote &lt;a href=&quot;https://github.com/flotwig/spoofident&quot;&gt;spoofident&lt;/a&gt;. spoofident is a daemon written in Python which provides a custom username/OS response to all incoming ident queries. It is dual-stack (meaning that it runs on both IPv4 and IPv6) and written to consume little resources, less than &lt;a href=&quot;http://en.wikipedia.org/wiki/Oidentd&quot;&gt;oidentd&lt;/a&gt;. I suggest using it if you are in a situation where you need to provide ident but refuse to compromise the security of your systems for that functionality.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/flotwig/spoofident&quot;&gt;GitHub repo for spoofident&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/flotwig/spoofident/blob/master/README.md&quot;&gt;README for spoofident&lt;/a&gt;&lt;/p&gt;
</description>
				<pubDate>Sat, 21 Jun 2014 20:27:25 +0000</pubDate>
				<link>https://zach.bloomqu.ist/blog/2014/06/spoofident.html</link>
				<guid isPermaLink="true">https://zach.bloomqu.ist/blog/2014/06/spoofident.html</guid>
			</item>
		
			<item>
				<title>Defeating Comcast BitTorrent Throttling: The Easy Way</title>
				<description>&lt;p&gt;&lt;img src=&quot;/images/transmission.png&quot; alt=&quot;Example settings in Transmission&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you torrent a lot, eventually Comcast/xfinity will &lt;a href=&quot;/images/throttling.png&quot;&gt;throttle your torrent speeds to 20kbps or below&lt;/a&gt;. Luckily, there is a simple fix which works without installing any external applications. Simply configure the listening port in your BitTorrent client to be 443, and ensure that the port is open on your router. This works because Comcast’s deep-packet inspection ignores packets on common Internet ports to save processing power, and 443 is the HTTPS port. This fix will not disrupt your HTTPS traffic. This also works on port 80 (HTTP) and port 53 (DNS).&lt;/p&gt;
</description>
				<pubDate>Sat, 21 Jun 2014 00:07:10 +0000</pubDate>
				<link>https://zach.bloomqu.ist/blog/2014/06/defeating-comcast-bittorrent-throttling.html</link>
				<guid isPermaLink="true">https://zach.bloomqu.ist/blog/2014/06/defeating-comcast-bittorrent-throttling.html</guid>
			</item>
		
			<item>
				<title>Fixing a corrupted TrueCrypt volume for Windows rescue</title>
				<description>&lt;p&gt;&lt;img src=&quot;/images/truecrypt.png&quot; alt=&quot;TrueCrypt rescue disk screenshot.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Like an idiot, I held down the power button to reboot my machine. It booted back up, and once I entered my TrueCrypt full-disk encryption password, I was greeted with a Windows rescue and repair boot screen. I managed to create a system volume which was both encrypted and corrupted, meaning that Windows rescue could not read the volume. Instead, it asked me to insert a driver disk for my hard drive so it could attempt rescue. If I entered the command line and used DISKPART to try to list volumes, it displayed C: as filesystem type RAW.&lt;/p&gt;

&lt;p&gt;Not a good thing.&lt;/p&gt;

&lt;p&gt;Happily enough, this is an issue with a simple solution. Insert your TrueCrypt rescue disk which you created when you originally encrypted your system and reboot. Press F8 for “Repair Options” when you get to the TrueCrypt authentication screen, and then press 1 to permanently decrypt your system disk. At this point, you’ll need to enter your decryption key, and decryption will begin. On a 500GB laptop drive, this process took over 8 hours to complete (!), so here are some other options you could try &lt;em&gt;before attempting to decrypt the drive&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Create a bootable live Linux thumb drive, install TrueCrypt onto it, and mount your system volume. From there, you can use any of the Windows repair tools available on Linux or do a chkdsk or what have you. TrueCrypt on Linux cannot mount partially decrypted volumes, so if you want to go this route, be sure not to begin decryption through the TrueCrypt rescue disk&lt;/li&gt;
  &lt;li&gt;Create a bootable WinPE drive and use it to repair Windows. I did not try this one so I do not know if TrueCrypt can handle partially decrypted volumes on WinPE.&lt;/li&gt;
  &lt;li&gt;Manage to get TrueCrypt running within the Windows rescue environment so the volume is mountable and rescuable. Windows rescue kills it, saying that “This image cannot run because the appropriate subsystem is not loaded.” This happens because Windows rescue is not running a full-fledged copy of Windows, so the subsystem is not available. If you have an afternoon to spare for fun Windows finagling, this is the option for you.&lt;/li&gt;
&lt;/ul&gt;
</description>
				<pubDate>Sun, 15 Jun 2014 12:19:56 +0000</pubDate>
				<link>https://zach.bloomqu.ist/blog/2014/06/fixing-corrupted-truecrypt.html</link>
				<guid isPermaLink="true">https://zach.bloomqu.ist/blog/2014/06/fixing-corrupted-truecrypt.html</guid>
			</item>
		
	</channel>
</rss>
