<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[__biancatcatcat]]></title><description><![CDATA[I'm a Software Engineer in San Francisco. Previously at Tesla, now at Cruise 🚗. I like writing about what I'm learning or new ideas I get. This blog is a work-in-progress. :)]]></description><link>https://biancatamayo.me/blog/</link><generator>Ghost 0.11</generator><lastBuildDate>Wed, 07 Jan 2026 08:43:09 GMT</lastBuildDate><atom:link href="https://biancatamayo.me/blog/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[dotfile snippets: easily list `date` formatting and format strings on macOS (and linux)]]></title><description><![CDATA[<p>I don't like fiddling around typing out date formats when I'm working – who does? I found this snippet a while ago. Unfortunately, I don't remember where anymore. I would love to credit the person who I copied this from, so let me know if you find it!</p>

<h2 id="macos">macOS</h2>

<p>This is</p>]]></description><link>https://biancatamayo.me/blog/dotfile-snippets-date-formats-on-macos/</link><guid isPermaLink="false">1d3aaf44-30f3-4c3d-98ca-a63db32d50c9</guid><dc:creator><![CDATA[Bianca Tamayo]]></dc:creator><pubDate>Sat, 01 Feb 2025 23:39:30 GMT</pubDate><content:encoded><![CDATA[<p>I don't like fiddling around typing out date formats when I'm working – who does? I found this snippet a while ago. Unfortunately, I don't remember where anymore. I would love to credit the person who I copied this from, so let me know if you find it!</p>

<h2 id="macos">macOS</h2>

<p>This is a bit cheaty because it doesn't use macOS's built-in <code>date</code>. For uniformity, since I work on both macOS and Linux a lot, I just use GNU's tools. For example, <code>date</code> is provided through <code>coreutils</code>, which can be installed through <a href="https://formulae.brew.sh/formula/coreutils">Brew</a>:</p>

<pre><code>brew install coreutils  
</code></pre>

<p>This should, by default, add <code>gdate</code> to your <code>$PATH</code> (all GNU commands are prefixed with <code>g</code> to not conflict with the built-in macOS commands).</p>

<p>In <code>~/.zshrc</code>:</p>

<pre><code>dates() {

cat &lt;&lt; EOD  
        Format/result               |       Command               |          Output
------------------------------------+-----------------------------+------------------------------
YYYY-MM-DD_hh:mm:ss                 | gdate +%F_%T                | $(gdate +%F_%T)  
YYYYMMDD_hhmmss                     | gdate +%Y%m%d_%H%M%S        | $(gdate +%Y%m%d_%H%M%S)  
YYYYMMDD_hhmmss (UTC version)       | gdate --utc +%Y%m%d_%H%M%SZ | $(gdate --utc +%Y%m%d_%H%M%SZ)  
YYYYMMDD_hhmmss (with local TZ)     | gdate +%Y%m%d_%H%M%S%Z      | $(gdate +%Y%m%d_%H%M%S%Z)  
YYYYMMDD_Thhmmss (with num TZ) *    | gdate +%Y%m%d_T%H%M%S%z     | $(gdate +%Y%m%d_T%H%M%S%z)  
YYYYMMSShhmmss                      | gdate +%Y%m%d%H%M%S         | $(gdate +%Y%m%d%H%M%S)  
YYYYMMSShhmmssnnnnnnnnn             | gdate +%Y%m%d%H%M%S%N       | $(gdate +%Y%m%d%H%M%S%N)  
YYMMDD_hhmmss                       | gdate +%y%m%d_%H%M%S        | $(gdate +%y%m%d_%H%M%S)  
Seconds since UNIX epoch:           | gdate +%s                   | $(gdate +%s)  
Nanoseconds only:                   | gdate +%N                   | $(gdate +%N)  
Nanoseconds since UNIX epoch:       | gdate +%s%N                 | $(gdate +%s%N)  
ISO8601 UTC timestamp               | gdate --utc +%FT%TZ         | $(gdate --utc +%FT%TZ)  
ISO8601 UTC timestamp + ms          | gdate --utc +%FT%T.%3NZ     | $(gdate --utc +%FT%T.%3NZ)  
ISO8601 Local TZ timestamp          | gdate +%FT%T%Z              | $(gdate +%FT%T%Z)  
YYYY-MM-DD (Short day)              | gdate +%F\(%a\)             | $(gdate +%F\(%a\))  
YYYY-MM-DD (Long day)               | gdate +%F\(%A\)             | $(gdate +%F\(%A\))  
"ISO8601"-ish timestamp + w/        | gdate +%F_T%H-%M-%S%z       | $(gdate +%F_T%H-%M-%S%z)
  numerical timezone                |                             |
YYYYMMDD_hhmmss (with local TZ)     | gdate +%F_T%H%M%S%z         | $(gdate +%F_T%H-%M-%S%z)  
  numerical timezone (no dash)      |                             |

Notes:  
* use this for filenames 
EOD  
}
</code></pre>

<p>On Linux you just use <code>date</code> instead of <code>gdate</code>:</p>

<pre><code>dates() {

cat &lt;&lt; EOD  
        Format/result               |       Command               |          Output
------------------------------------+-----------------------------+------------------------------
YYYY-MM-DD_hh:mm:ss                 | date +%F_%T                | $(date +%F_%T)  
YYYYMMDD_hhmmss                     | date +%Y%m%d_%H%M%S        | $(date +%Y%m%d_%H%M%S)  
YYYYMMDD_hhmmss (UTC version)       | date --utc +%Y%m%d_%H%M%SZ | $(date --utc +%Y%m%d_%H%M%SZ)  
YYYYMMDD_hhmmss (with local TZ)     | date +%Y%m%d_%H%M%S%Z      | $(date +%Y%m%d_%H%M%S%Z)  
YYYYMMDD_Thhmmss (with num TZ) *    | date +%Y%m%d_T%H%M%S%z     | $(date +%Y%m%d_T%H%M%S%z)  
YYYYMMSShhmmss                      | date +%Y%m%d%H%M%S         | $(date +%Y%m%d%H%M%S)  
YYYYMMSShhmmssnnnnnnnnn             | date +%Y%m%d%H%M%S%N       | $(date +%Y%m%d%H%M%S%N)  
YYMMDD_hhmmss                       | date +%y%m%d_%H%M%S        | $(date +%y%m%d_%H%M%S)  
Seconds since UNIX epoch:           | date +%s                   | $(date +%s)  
Nanoseconds only:                   | date +%N                   | $(date +%N)  
Nanoseconds since UNIX epoch:       | date +%s%N                 | $(date +%s%N)  
ISO8601 UTC timestamp               | date --utc +%FT%TZ         | $(date --utc +%FT%TZ)  
ISO8601 UTC timestamp + ms          | date --utc +%FT%T.%3NZ     | $(date --utc +%FT%T.%3NZ)  
ISO8601 Local TZ timestamp          | date +%FT%T%Z              | $(date +%FT%T%Z)  
YYYY-MM-DD (Short day)              | date +%F\(%a\)             | $(date +%F\(%a\))  
YYYY-MM-DD (Long day)               | date +%F\(%A\)             | $(date +%F\(%A\))  
"ISO8601"-ish timestamp + w/        | date +%F_T%H-%M-%S%z       | $(date +%F_T%H-%M-%S%z)
  numerical timezone                |                            |
YYYYMMDD_hhmmss (with local TZ)     | date +%F_T%H%M%S%z         | $(date +%F_T%H-%M-%S%z)  
  numerical timezone (no dash)     

EOD  
}
</code></pre>

<p>Example output:</p>

<pre><code>        Format/result               |       Command               |          Output
------------------------------------+-----------------------------+------------------------------
YYYY-MM-DD_hh:mm:ss                 | gdate +%F_%T                | 2025-02-01_15:30:00  
YYYYMMDD_hhmmss                     | gdate +%Y%m%d_%H%M%S        | 20250201_153000  
YYYYMMDD_hhmmss (UTC version)       | gdate --utc +%Y%m%d_%H%M%SZ | 20250201_233000Z  
YYYYMMDD_hhmmss (with local TZ)     | gdate +%Y%m%d_%H%M%S%Z      | 20250201_153000PST  
YYYYMMDD_Thhmmss (with num TZ) *    | gdate +%Y%m%d_T%H%M%S%z     | 20250201_T153000-0800  
YYYYMMSShhmmss                      | gdate +%Y%m%d%H%M%S         | 20250201153000  
YYYYMMSShhmmssnnnnnnnnn             | gdate +%Y%m%d%H%M%S%N       | 20250201153000726647000  
YYMMDD_hhmmss                       | gdate +%y%m%d_%H%M%S        | 250201_153000  
Seconds since UNIX epoch:           | gdate +%s                   | 1738452600  
Nanoseconds only:                   | gdate +%N                   | 734241000  
Nanoseconds since UNIX epoch:       | gdate +%s%N                 | 1738452600737131000  
ISO8601 UTC timestamp               | gdate --utc +%FT%TZ         | 2025-02-01T23:30:00Z  
ISO8601 UTC timestamp + ms          | gdate --utc +%FT%T.%3NZ     | 2025-02-01T23:30:00.741Z  
ISO8601 Local TZ timestamp          | gdate +%FT%T%Z              | 2025-02-01T15:30:00PST  
YYYY-MM-DD (Short day)              | gdate +%F\(%a\)             | 2025-02-01(Sat)  
YYYY-MM-DD (Long day)               | gdate +%F\(%A\)             | 2025-02-01(Saturday)  
"ISO8601"-ish timestamp + w/        | gdate +%F_T%H-%M-%S%z       | 2025-02-01_T15-30-00-0800
  numerical timezone                |                             |
YYYYMMDD_hhmmss (with local TZ)     | gdate +%F_T%H%M%S%z         | 2025-02-01_T15-30-00-0800  
  numerical timezone (no dash)      |                             |

Notes:  
* use this for filenames 
</code></pre>]]></content:encoded></item><item><title><![CDATA[Quick note: Troubleshooting accessing a Docker container from macOS host vs Linux host]]></title><description><![CDATA[If you're using Docker Desktop for macOS, you won't be able to ping your containers by IP.
Docker Desktop for macOS can’t route traffic to containers.]]></description><link>https://biancatamayo.me/blog/docker-troubleshooting-macos-network/</link><guid isPermaLink="false">849971e7-7b28-4c6e-9ee0-a1eeeb2933e9</guid><category><![CDATA[docker]]></category><category><![CDATA[linux]]></category><category><![CDATA[macOS]]></category><dc:creator><![CDATA[Bianca Tamayo]]></dc:creator><pubDate>Mon, 09 Dec 2019 04:42:40 GMT</pubDate><content:encoded><![CDATA[<p><strong>A fair number of commenters asked a related question in a previous post. This post serves as quick information on troubleshooting connecting <em>to</em> containers <em>from</em> Docker Desktop for macOS, from: <a href="https://biancatamayo.me/blog/2017/11/03/docker-add-host-ip/#comment-4412585659">https://biancatamayo.me/blog/2017/11/03/docker-add-host-ip/#comment-4412585659</a></strong></p>

<hr>

<p><strong>For people having issues accessing containers from their macOS here's some info:</strong></p>

<p>Docker networking works differently for macOS than it does on Linux (it uses HyperKit).</p>

<p>If you're using Docker Desktop for Mac, you won't be able to ping your containers by IP. <strong>See: <a href="https://docs.docker.com/docker-for-mac/networking/">https://docs.docker.com/docker-for-mac/networking/</a></strong></p>

<blockquote>
  <p>Issue: I cannot ping my containers</p>
  
  <p>Docker Desktop for Mac can’t route traffic to containers.</p>
</blockquote>

<p><strong>BUT</strong> you should be able to connect using another port if you publish the ports correctly, <strong>using <code>localhost</code>, not the IP address</strong>.</p>

<p><strong>Example:</strong>
If I run the following command to run <code>nginx</code> in a container and expose port <code>80</code> inside the container, and map to port 80 on the host:</p>

<pre><code class="language-shell">docker run -d -p 80:80 --name webserver nginx  
</code></pre>

<p>It will have the following effect in macOS. It works as expected with curl using <strong>localhost</strong>.</p>

<pre><code class="language-shell">$ curl -I localhost:80
HTTP/1.1 200 OK  
Server: nginx/1.15.10  
</code></pre>

<p>But if I try to use the IP address in macOS:</p>

<pre><code class="language-shell"># Getting the IP address of the webserver container
$ docker inspect webserver | jq '.[] | .NetworkSettings.Networks.bridge.IPAddress'
</code></pre>

<p>which returned in this case "<code>172.17.0.3</code>".</p>

<p>Now using that returned IP in macOS (i.e. <strong>fails</strong> ping, and fails curl):</p>

<pre><code class="language-shell">$ ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes  
Request timeout for icmp_seq 0  
Request timeout for icmp_seq 1  
^C
--- 172.17.0.3 ping statistics ---
3 packets transmitted, 0 packets received, 100.0% packet loss  
</code></pre>

<p>The following <strong>times out</strong>:</p>

<pre><code class="language-shell">$ curl -I 172.17.0.3:80

...
</code></pre>

<p><strong>This link contains a lot more troubleshooting on macOS + Docker networking: <a href="https://docs.docker.com/docker-for-mac/networking/">https://docs.docker.com/docker-for-mac/networking/</a></strong></p>]]></content:encoded></item><item><title><![CDATA[dnsmasq + logrotate config, Debian 9 (stretch)]]></title><description><![CDATA[directives to `include` files in `/etc/logrotate.d/`, here's the config that I'm using now:

```
# /etc/logrotate.d/dnsmasq:

/var/log/dnsmasq.log {
  monthly
  missingok
  notifempty
]]></description><link>https://biancatamayo.me/blog/dnsmasq-logrotate/</link><guid isPermaLink="false">196329f7-1d7d-43ec-8251-b9691c0a4e8d</guid><category><![CDATA[dnsmasq]]></category><category><![CDATA[logrotate]]></category><dc:creator><![CDATA[Bianca Tamayo]]></dc:creator><pubDate>Sun, 08 Dec 2019 22:34:37 GMT</pubDate><media:content url="https://biancatamayo.me/blog/content/images/2019/12/Screen-Shot-2019-12-08-at-10.31.51-AM.png" medium="image"/><content:encoded><![CDATA[<img src="https://biancatamayo.me/blog/content/images/2019/12/Screen-Shot-2019-12-08-at-10.31.51-AM.png" alt="dnsmasq + logrotate config, Debian 9 (stretch)"><p>It's been a while since I got really into the weeds with my network setup. After not thinking about it for a while, I got an alert for disk usage. </p>

<p>So I come back to this:</p>

<p><img src="https://i.imgur.com/HHujTf5.png" alt="dnsmasq + logrotate config, Debian 9 (stretch)"></p>

<p>Whoops. Oh yeah – this was when I was tinkering with dnsmasq to do some overly complex stuff to see if I can do it. I turned on super verbose logging for this gateway and then apparently didn't turn it off. Then forgot to rotate those logs.</p>

<p>This was not what I planned to deal with today, but since this is surprisingly the only service I have to manually configure logrotate for, I'm putting the config here for reference – mostly my own.</p>

<p>Running Debian right now and using a default installation of logrotate, I have the following in <code>dnsmasq.conf</code>:</p>

<pre><code># /etc/dnsmasq.conf
...
log-facility=/var/log/dnsmasq.log  
...
</code></pre>

<p>Assuming global configs for logrotate are in <code>/etc/logrotate.conf</code>, and has directives to <code>include</code> files in <code>/etc/logrotate.d/</code>, here's the config that I'm using now:</p>

<pre><code># /etc/logrotate.d/dnsmasq:

/var/log/dnsmasq.log {
  monthly
  missingok
  notifempty
  maxsize 5M
  rotate 14
  delaycompress
  create 0640 dnsmasq root
  sharedscripts
  postrotate
    [ ! -f /var/run/dnsmasq.pid ] || kill -USR2 `cat /var/run/dnsmasq.pid`
  endscript
}
</code></pre>

<h3 id="todebugaspecificconfigurationegdnsmasqindryrunmode">💡 To debug a specific configuration (e.g. dnsmasq) in <strong>dry-run</strong> mode:</h3>

<p><strong><em>Note</em></strong>: Doing this will bypass any global configs in <code>logrotate.conf</code></p>

<pre><code class="language-shell">logrotate --debug /etc/logrotate.d/dnsmasq  
</code></pre>

<h3 id="todebugyourgeneraldefaultlogrotateconfigurationindryrunmode">💡 To debug your <em>general/default</em> logrotate configuration in <strong>dry-run</strong> mode:</h3>

<pre><code class="language-shell">logrotate --debug /etc/logrotate.conf  
</code></pre>

<h3 id="toforcelogrotatetorunignoringallconfigurationscriteriaforrotation">💡 To <strong>force</strong> logrotate to run, <strong>ignoring</strong> all configurations' criteria for rotation:</h3>

<pre><code class="language-shell">logrotate --verbose --force /etc/logrotate.conf  
</code></pre>

<p>S'all for now!</p>

<h3 id="bonusupdateafter6hours">Bonus update after 6 hours:</h3>

<p>So I didn't actually delete that giant logfile because I love data and have a hard time letting go. Instead, I compressed the hell out of it and will probabaly never actually look at it. <strong>It took six hours, but tada... 🎉 a 95% reduction!</strong> Compression (xz) + one-off logs = ❤</p>

<pre><code class="language-shell">root@px01:/var/log# xz --verbose dnsmasq.log.1  
dnsmasq.log.1 (1/1)  
  100 %      2,636.8 MiB / 53.0 GiB = 0.049   2.5 MiB/s    5:56:53
xz: dnsmasq.log.1: File seems to have been moved, not removing  
root@px01:/var/log# ls -lAth | grep dns  
-rw-r-x--- 1 dnsmasq  root     2.6G Sep 28 17:33 dnsmasq.log.1.xz
-rw-r-x--- 1 dnsmasq  root      53G Sep 28 17:33 dnsmasq.log.2
</code></pre>

<p>💡<strong>Tip:</strong> The <code>-h</code> and <code>-t</code> in <code>ls -lAth</code> means <em>"<code>h</code>uman readable, sort by modified <code>t</code>ime"</em>. </p>]]></content:encoded></item><item><title><![CDATA[Linux bits: how to change the incorrect sudo/login password timeout]]></title><description><![CDATA[The `nodelay` removes the default 2-second delay, allowing other configs to possibly override this delay with something less than 2 seconds. ]]></description><link>https://biancatamayo.me/blog/why-do-incorrect-sudo-passwords-take-so-long/</link><guid isPermaLink="false">f2e35c10-c2dc-4ec1-90f8-7d58b042b598</guid><category><![CDATA[linux]]></category><category><![CDATA[miniblog]]></category><category><![CDATA[pam]]></category><category><![CDATA[sudo]]></category><dc:creator><![CDATA[Bianca Tamayo]]></dc:creator><pubDate>Thu, 21 Mar 2019 17:44:00 GMT</pubDate><content:encoded><![CDATA[<p><b>If you wanna skip to the <a href="https://biancatamayo.me/blog/why-do-incorrect-sudo-passwords-take-so-long/#tldr">TLDR</a>, feel free to do that as long as you know what you're doing.</b> </p>

<p>So I woke up today and ssh'd into a VM that I own on a network that isn't connected to the internet and with no access to it. I typed in my annoyingly long password to <code>sudo !!</code> as my non-root (but sudoer) user, and the thing happens where I know I typed in my password wrong and now I have to wait what feels like an eternity to type in a command after forgetting to use <code>sudo</code> in the first place.</p>

<p>So anyway this has been plaguing me for years and stealing precious minutes of my attention span so I decided to figure out and how to change it.</p>

<p>I only know this for this VM's specs, which is currently <strong>Ubuntu 16.04.6 LTS</strong>. It might be different from yours. If I do it for other distros I'll maybe update this post, but it should be similar.</p>

<div class="ui warning message">  
  <div class="header">
    Security disclaimer
  </div>
  Don't do these things on other people's computers unless you know what you're doing and what it entails and possible unintended side effects.
</div>

<h2 id="whyhavethisinthefirstplace">Why have this in the first place?</h2>

<p>If you haven't thought about this before, you should. We have timed lock-outs for failed authentication for security reasons for <strong>general things</strong>, mainly because it limits the number of times that a bot/hacker/script can brute-force your password.</p>

<p>Now, the implementation/practical effectiveness of the lock-out for <code>sudo</code> and general login (i.e via PAM) can be argued for and against, but I won't go into that here. I assume you know the following: </p>

<ol>
<li>security should be an aspect of all layers  </li>
<li>every approach to building things has pros and cons with different weights</li>
</ol>

<p>Also, lastly, be aware that anything I write about PAM will be littered with these gifs and pictures.</p>

<p><img src="https://i.imgur.com/ptpk0nL.gif" alt="What up 2-1-2?">
<center><small>"What up 2-1-2?"</small></center></p>

<h2 id="okaysohow">Okay so how?</h2>

<p>Pretty ready to not have this workflow.</p>

<pre><code class="language-shell">btamayo@px01:~$ su deploy  
Password:  
&lt;THREE SECONDS OF UGH&gt;  
su: Authentication failure  
</code></pre>

<pre><code>btamayo@px01:~$ sudo su -  
[sudo] password for btamayo:
&lt;THREE SECONDS OF UGH&gt;  
Sorry, try again.  
</code></pre>

<p>So the first google result I looked at told me to go to <code>/etc/logins.defs</code> and look for something called <code>FAIL_DELAY</code> which was commented out and didn't affect anything. I checked on just Debian and it was the same way as well.</p>

<p><img src="https://i.imgur.com/cc9Qfwe.png" alt="commented out"></p>

<p>So I skipped that part and went to the second part where it told me to go to <code>/etc/pam.d/</code> and look in the configs there. I looked in the files <code>/etc/pam.d/login</code>, <code>/etc/pam.d/su</code>, and <code>/etc/pam.d/sudo</code>. </p>

<p>In <code>login</code>, there was a line that said:</p>

<pre><code>...
# Enforce a minimal delay in case of failure (in microseconds).
# (Replaces the `FAIL_DELAY' setting from login.defs)
# Note that other modules may require another minimal delay. (for example,
# to disable any delay, you should add the nodelay option to pam_unix)
auth       optional   pam_faildelay.so  delay=3000000  
...
</code></pre>

<p><strong>NOTE</strong> that said <strong>microseconds</strong> which I <strong>missed</strong> and wrongly assumed was in milliseconds the first time and was confused for five whole minutes as I didn't understand why it would be 50+ minutes long.</p>

<ol>
<li><code>login</code>, <code>sudo</code>, and <code>su</code> (and other files I assume) all included a file called <code>common-auth</code>, which I looked in and found the line that it told me to look for:</li>
</ol>

<pre><code>...
auth    [success=1 default=ignore]  pam_unix.so nullok_secure  
...
</code></pre>

<p>(The above is from <code>/etc/pam.d/common-auth</code>.)</p>

<p>Ok, so I just want to change <code>sudo</code> at this point because I really need to not get sidetracked in to learning PAM today, even though I'm curious and tempted because I love digging around stuff like this.</p>

<h3 id="nullok_secure">nullok_secure</h3>

<p>I don't remember seeing this in the manpage I read, so looked it up, and it seems to be a <strong><a href="https://www.redhat.com/archives/pam-list/2005-September/msg00001.html">Debian thing</a></strong>. I'm not sure whether it's supported in other distros, and if so, if it's just that it's the default in Debian. Let me know!</p>

<h3 id="aidtldrtldrachangingthedelay"><a id="TLDR">🔗 TLDR</a>: Changing the delay</h3>

<p>Change this line in <code>/etc/pam.d/common-auth</code> (Debian systems):</p>

<pre><code>...
auth    [success=1 default=ignore]  pam_unix.so nullok_secure  
...
</code></pre>

<p>to</p>

<pre><code>...
auth    [success=1 default=ignore]  pam_unix.so nullok_secure nodelay  
...
</code></pre>

<p>The <code>nodelay</code> removes the default 2-second delay, allowing other configs to possibly override this delay with something less than 2 seconds. <strong>All configs inheriting this file without adding their own delay, including sshd will have no delay</strong>. </p>

<p>To add in a delay now, (I assume that that minimum 2-second delay is built-in to <code>pam_unix.so</code> but lmk if that's not true! curious.) You can add a config to specify the delay by adding the following line to any file:</p>

<pre><code>auth       optional   pam_faildelay.so  delay=1500000  
</code></pre>

<p>(You can read the man page of this module via <code>man pam_faildelay</code>.)</p>

<p>And change the value of the <code>delay=</code> (which is in microseconds) in any of the config files. </p>

<p>For example, my <code>/etc/pam.d/sudo</code> file is now:</p>

<pre><code>auth       optional   pam_faildelay.so  delay=1000000

@include common-auth
@include common-account
@include common-session-noninteractive
</code></pre>

<p>Which gives me 1000000 microseconds of a delay when I password-authenticate with <code>sudo</code> and fail.</p>

<p>(If you mess around with PAM configuration on systems over the internet, you should also look into how PAM interacts with sshd. Read <code>/etc/ssh/sshd_config</code>'s text info block on the <code>UsePAM</code> option, <code>/etc/pam.d/sshd</code> file, and do some Googling as well.)</p>

<p>You can also mess around with the control flags (<code>optional</code> in the case of <code>pam_faildelay.so</code> to something that better suits you. I won't go into that now, but you can find their meanings here: <a href="https://linux.die.net/man/5/pam.d"><strong><a href="https://linux.die.net/man/5/pam.d">https://linux.die.net/man/5/pam.d</a></strong></a></p>

<hr>

<p>I hope this helps somehow. It's really interesting and I wish I had time today to fall into the PAM rabbit-hole but that's next time and we can write a blog post then too :)</p>

<p><img src="https://i.imgur.com/lVGrv5B.gif" alt=""></p>]]></content:encoded></item><item><title><![CDATA[Installing Go 1.10 + Datadog Agent v6 on Raspbian Jessie (32-bit ARM)]]></title><description><![CDATA[<h1 id="background">Background:</h1>

<p>I had trouble running the Datadog Agent (v6) on my Raspberry Pi. Here's what I did to get it running. I guess. Also available <a href="https://gist.github.com/btamayo/ce6668c886b41947191bbc47b3e9e04f">in this gist, yay</a>.</p>

<h2 id="sections">Sections:</h2>

<ol>
<li>Installing Go (skip if you already have Go 1.9+)  </li>
<li>Install/build the Datadog Agent</li>
</ol>

<h2 id="systeminfo">System Info:</h2>

<p><strong>pxpi</strong> is the</p>]]></description><link>https://biancatamayo.me/blog/install-go-1-10-datadog-agent-v6-on-raspbian-jessie-32-bit-arm/</link><guid isPermaLink="false">298e4262-9b39-4255-9537-235491cda5cb</guid><category><![CDATA[linux]]></category><category><![CDATA[datadog]]></category><category><![CDATA[arm]]></category><category><![CDATA[raspbian]]></category><category><![CDATA[rpi]]></category><category><![CDATA[infodump]]></category><category><![CDATA[golang]]></category><dc:creator><![CDATA[Bianca Tamayo]]></dc:creator><pubDate>Wed, 27 Jun 2018 21:27:56 GMT</pubDate><content:encoded><![CDATA[<h1 id="background">Background:</h1>

<p>I had trouble running the Datadog Agent (v6) on my Raspberry Pi. Here's what I did to get it running. I guess. Also available <a href="https://gist.github.com/btamayo/ce6668c886b41947191bbc47b3e9e04f">in this gist, yay</a>.</p>

<h2 id="sections">Sections:</h2>

<ol>
<li>Installing Go (skip if you already have Go 1.9+)  </li>
<li>Install/build the Datadog Agent</li>
</ol>

<h2 id="systeminfo">System Info:</h2>

<p><strong>pxpi</strong> is the Raspberry Pi's host name.</p>

<pre><code class="language-shell">root@pxpi:/etc/datadog-agent# uname -a

Linux pxpi 4.14.50-v7+ #1122 SMP Tue Jun 19 12:26:26 BST 2018 armv7l GNU/Linux  
</code></pre>

<pre><code class="language-shell">root@pxpi:/etc/datadog-agent# cat /etc/*rel*

/usr/lib/arm-linux-gnueabihf/libarmmem.so
PRETTY_NAME="Raspbian GNU/Linux 8 (jessie)"  
NAME="Raspbian GNU/Linux"  
VERSION_ID="8"  
VERSION="8 (jessie)"  
ID=raspbian  
ID_LIKE=debian  
HOME_URL="http://www.raspbian.org/"  
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"  
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"  
</code></pre>

<hr>

<h1 id="1installgogolangwithoutgvm">1. Install Go (Golang): without GVM</h1>

<p><strong>Note: I haven't tried gvm, but the corresponding guide is here: <a href="https://github.com/tgogos/rpi_golang#2-with-go-version-manager-gvm">https://github.com/tgogos/rpi_golang#2-with-go-version-manager-gvm</a>. The rest of this guide will show how to install Go without GVM.</strong></p>

<p>Note: This is from <a href="https://github.com/btamayo/rpi_golang">this fork</a> of <a href="https://github.com/tgogos/rpi_golang">tgogos/rpi_golang</a>, which has instructions on how to install Go version 1.10.3.</p>

<h3 id="quicksummary">Quick Summary:</h3>

<ol>
<li>Use the pre-built binary in the repo to install Go 1.4.3  </li>
<li>Optionally use Go 1.4.3 to upgrade (build) Go 1.10.3</li>
</ol>

<p><strong>This presumes you're the root user</strong>. If you are not, you may need to append <code>sudo</code> to some of the following commands.</p>

<p>Optionally, ensure that the following directories do not exist as they <em>may</em> conflict with the process.</p>

<ul>
<li><code>/usr/local/go</code></li>
<li><code>$HOME/go1.4</code></li>
</ul>

<h2 id="1clonetherepo">1. Clone the repo:</h2>

<pre><code class="language-shell">git clone https://github.com/tgogos/rpi_golang.git  
cd rpi_golang/  
</code></pre>

<h2 id="2installeitheronly143orupgradeto1103">2. Install either only 1.4.3 or upgrade to 1.10.3:</h2>

<h3 id="for143only">For 1.4.3 only:</h3>

<p>If you have no intention of upgrading to 1.10.3, you can untar the binary in the repo into <code>/usr/local</code>.  </p>

<pre><code class="language-shell"># untar into /usr/local
tar -xzf go1.4.3.linux-armv7.tar.gz -C /usr/local

# Add to shell and .bashrc
export PATH=/usr/local/go/bin:$PATH  
echo "export PATH=/usr/local/go/bin:$PATH" &gt;&gt; $HOME/.bashrc

# go version should now output something
go version

# If you're happy with 1.4, you can stop at this point.
</code></pre>

<p>You're done!</p>

<h3 id="for1103">For 1.10.3:</h3>

<p>If you would like to upgrade to 1.10.3, follow this to untar the binary from the repo into a different location, and use it to bootstrap the build:</p>

<p>Run <code>rm -rf /usr/local/go</code> if you accidentally made this directory already.</p>

<pre><code class="language-shell"># We use the 1.4 binary in the repo to bootstrap building 1.10:
# While still in rpi_golang directory, make the alternate directory:
mkdir -p $HOME/go1.4

# Untar into that directory
tar -xzf go1.4.3.linux-armv7.tar.gz -C $HOME/go1.4 --strip-components=1

# Download and untar the 1.10.3 src into /usr/local
wget https://dl.google.com/go/go1.10.3.src.tar.gz  
tar -xz -C /usr/local -f go1.10.3.src.tar.gz  
</code></pre>

<h4 id="build1103">Build 1.10.3:</h4>

<pre><code class="language-shell">cd /usr/local/go/src

time GOROOT_BOOTSTRAP=$HOME/go1.4 ./make.bash  
</code></pre>

<h4 id="output">Output:</h4>

<p>A successful output will look something like:</p>

<pre><code class="language-shell">root@pxpi:/usr/local/go/src# time GOROOT_BOOTSTRAP=$HOME/go1.4 ./make.bash

Building Go cmd/dist using /root/go1.4.  
Building Go toolchain1 using /root/go1.4.  
Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1.  
Building Go toolchain2 using go_bootstrap and Go toolchain1.  
Building Go toolchain3 using go_bootstrap and Go toolchain2.


Building packages and commands for linux/arm.  
---
Installed Go for linux/arm in /usr/local/go  
Installed commands in /usr/local/go/bin

real    8m42.421s  
user    18m36.579s  
sys     0m44.624s


# go version:
root@pxpi:/usr/local/go/src# go version  
go version go1.10.3 linux/arm  
</code></pre>

<h3 id="setupyourpathsandenvironmentvariables">Set up your paths and environment variables:</h3>

<p>Add the following to your <code>.bashrc</code>, <code>.bash_profile</code>, <code>.zshrc</code>, or what have you.</p>

<pre><code class="language-shell">export PATH=$PATH:/usr/local/go/bin  
export GOPATH=$HOME/go  
export PATH=$PATH:$GOPATH/bin  
</code></pre>

<p>Don't forget to <code>source</code> it or reload your shell.</p>

<hr>

<h1 id="installdatadogagent">Install datadog-agent</h1>

<p>Building for 32-bit ARM requires the Puppy variant of the datadog agent. See: <a href="https://github.com/DataDog/datadog-agent/issues/1069">https://github.com/DataDog/datadog-agent/issues/1069</a></p>

<h2 id="1installdependencies">1. Install dependencies:</h2>

<ul>
<li>Python2.7</li>
<li>invoke</li>
<li>Go 1.9+</li>
<li>dep</li>
</ul>

<h3 id="1installsystemdependencies">1. Install system dependencies:</h3>

<p>This presumes you are root or have <code>sudo</code> access. Optionally what you already have:</p>

<pre><code class="language-shell"># For all:
sudo apt-get update

# Python 2.7
sudo apt-get install python2.7-dev

# Install snmpd -- not sure if this is needed for the Puppy agent
sudo apt-get install libsnmp-base libsnmp-dev snmp-mibs-downloader

# Install systemd dev -- again, not sure if this is built against on the Puppy agent
sudo apt-get install libsystemd-dev
</code></pre>

<h3 id="2installinginvoke">2. Installing <code>invoke</code>:</h3>

<pre><code class="language-shell">pip install invoke  
</code></pre>

<h3 id="3installingdep">3. Installing <code>dep</code>:</h3>

<p>Dep does not support ARM binaries. You must <code>go get</code> it.</p>

<pre><code class="language-shell">go get -u github.com/golang/dep/cmd/dep  
</code></pre>

<p>The following should now work if <code>$GOPATH</code> is set up correctly. <br>
If it is not set up correctly, do so now. <strong>Remember that <code>go env</code> will print out Go env vars which is not necessarily the same as your shell's.</strong></p>

<pre><code class="language-shell">root@pxpi:/etc/datadog-agent# dep  
Dep is a tool for managing dependencies for Go projects

Usage: "dep [command]"  
[...]
</code></pre>

<h2 id="2downloadclonedatadogagentsource">2. Download/clone datadog agent source</h2>

<p>The current README's example states to clone the directory (especially if you would like the <code>master</code> branch) but I'm guessing <code>go get -u github.com/DataDog/datadog-agent</code> should work as essentially the same:</p>

<pre><code class="language-shell">git clone https://github.com/DataDog/datadog-agent.git $GOPATH/src/github.com/DataDog/datadog-agent  
</code></pre>

<h2 id="3buildingtheagent">3. Building the agent</h2>

<p>Building for 32-bit ARM requires the Puppy variant of the datadog agent. See: <a href="https://github.com/DataDog/datadog-agent/issues/1069">https://github.com/DataDog/datadog-agent/issues/1069</a></p>

<pre><code class="language-shell">cd $GOPATH/src/github.com/DataDog/datadog-agent

invoke deps  
invoke agent.build --puppy  
</code></pre>

<p>Check if it runs:</p>

<p>From <a href="https://github.com/DataDog/datadog-agent/blob/master/README.md#run">https://github.com/DataDog/datadog-agent/blob/master/README.md#run</a>:</p>

<pre><code class="language-shell">cd $GOPATH/src/github.com/DataDog/datadog-agent

# Might error out unless you set an API key in 
# ./bin/agent/dist/datadog.yaml
# You can also export it as `DD_API_KEY`
# e.g. export DD_API_KEY=1234

./bin/agent/agent -c bin/agent/dist/datadog.yaml

# OR
# DD_API_KEY=&lt;KEY HERE&gt; ./bin/agent/agent -c bin/agent/dist/datadog.yaml
</code></pre>

<h2 id="5postinstalloptional">5. Postinstall (optional):</h2>

<h3 id="moveandorcopyfilesintoexpecteddirectories">Move and/or copy files into expected directories</h3>

<p>This depends on your preferences, but this is how I decided to finalize my install:</p>

<p>Move <code>$GOPATH/src/github.com/DataDog/datadog-agent/</code> into <code>/opt/</code> (to be compatible with normal package distributions).</p>

<pre><code class="language-shell">cd $GOPATH/src/github.com/DataDog/  
mv datadog-agent /opt/  
</code></pre>

<p>Copy the config files into <code>/etc/datadog-agent</code> and edit as necessary:  </p>

<pre><code class="language-shell">mkdir -p /etc/datadog-agent/

cd /opt/datadog-agent/bin/agent/dist  
cp -r . /etc/datadog-agent/  
</code></pre>

<p>Edit <code>/etc/datadog-agent/datadog.yaml</code> to add your API key.</p>

<h3 id="configureauser">Configure a user:</h3>

<p>I didn't need to do this part since I have a specific user/group setup for my development machines, but this is to illustrate how you might.</p>

<p>This is similar to how it's done in the <a href="https://github.com/DataDog/datadog-agent/blob/master/Dockerfiles/agent/Dockerfile">Dockerfile</a>.</p>

<p>The difference here is that I didn't add it to the root group, but you can choose to do what you'd like. </p>

<pre><code># create user if it does not yet exist
adduser --system --no-create-home --disabled-password --ingroup dd-agent dd-agent

# Give permissions to relevant directories
chown -R dd-agent:dd-agent /etc/datadog-agent/ /opt/datadog-agent/ /var/log/datadog/  
chmod g+r,g+w,g+X -R /etc/datadog-agent/ /var/log/datadog/  
</code></pre>

<p>(To add <code>dd-agent</code> to the root group, you can do the following instead of the above <code>adduser</code> command, then <code>chown</code> accordingly):</p>

<pre><code>adduser --system --no-create-home --disabled-password --ingroup root dd-agent  
chown -R dd-agent:root /etc/datadog-agent/ /opt/datadog-agent/ /var/log/datadog/  
</code></pre>

<p>With guidance from <a href="https://github.com/DataDog/datadog-agent/blob/master/omnibus/config/templates/datadog-puppy/systemd.service.erb">this file</a>, here's the systemd service for the puppy agent:</p>

<pre><code>root@pxpi:~# cat /lib/systemd/system/datadog-agent.service

[Unit]
Description="Datadog Agent"  
After=network.target

[Service]
Type=simple  
PIDFile=/opt/datadog-agent/run/agent.pid  
User=dd-agent  
Restart=on-failure  
ExecStart=/opt/datadog-agent/bin/agent/agent start -p /opt/datadog-agent/run/agent.pid

[Install]
WantedBy=multi-user.target  
</code></pre>

<p>For more post-install steps on Debian-ish systems, you can also take a look at the <a href="https://github.com/DataDog/datadog-agent/blob/master/omnibus/package-scripts/agent/postinst">omnibus postinstall script in the datadog agent repo</a>.</p>]]></content:encoded></item><item><title><![CDATA[Using Gmail as a Postfix relay on Ubuntu 16.04 for GitLab]]></title><description><![CDATA[<h3 id="aminiadventure">A mini-adventure</h3>

<p>This post assumes you have the packages <code>postfix</code> and <code>mailutils</code> installed and you're using Ubuntu 16.04 bc I don't know if it's the same in other distros. ¯\<em>(ツ)</em>/¯</p>

<p>Recently I installed GitLab at home. It uses Postfix to send you mail. I didn't really want to go</p>]]></description><link>https://biancatamayo.me/blog/using-gmail-as-a-postfix-relay-on-ubuntu-16-04/</link><guid isPermaLink="false">ebd332cb-30ce-49f9-8f6d-b01f7ba12707</guid><category><![CDATA[linux]]></category><category><![CDATA[homelab]]></category><category><![CDATA[smtp]]></category><category><![CDATA[privacy]]></category><category><![CDATA[postfix]]></category><category><![CDATA[draft]]></category><dc:creator><![CDATA[Bianca Tamayo]]></dc:creator><pubDate>Mon, 19 Feb 2018 23:25:06 GMT</pubDate><media:content url="https://biancatamayo.me/blog/content/images/2018/02/postfix.png" medium="image"/><content:encoded><![CDATA[<h3 id="aminiadventure">A mini-adventure</h3>

<img src="https://biancatamayo.me/blog/content/images/2018/02/postfix.png" alt="Using Gmail as a Postfix relay on Ubuntu 16.04 for GitLab"><p>This post assumes you have the packages <code>postfix</code> and <code>mailutils</code> installed and you're using Ubuntu 16.04 bc I don't know if it's the same in other distros. ¯\<em>(ツ)</em>/¯</p>

<p>Recently I installed GitLab at home. It uses Postfix to send you mail. I didn't really want to go through the <span class="htip" data-content="except ironically I did end up learning and doing almost the same amount of work when I was reading/validating/fact-checking for this post lol">hassle of setting up</span> a fully decked-out mail server. <strong>Anyway, here's how I set up Gmail as a relay.</strong></p>

<p>We know that their SMTP server is at smtp.gmail.com. SMTP's default port is 25, but my ISP is probably blocking it. I can verify that using <code>telnet</code>.</p>

<p>Checking if port 25 is blocked:</p>

<pre><code class="language-shell">btamayo@gitlab:~$ telnet smtp.gmail.com 25  
Trying 108.177.98.109...  
^C
</code></pre>

<p>It's blocked. Try 587.  </p>

<pre><code class="language-shell">btamayo@gitlab:~$ telnet smtp.gmail.com 587  
Trying 74.125.28.108...  
Connected to gmail-smtp-msa.l.google.com.  
Escape character is '^]'.  
220 smtp.gmail.com ESMTP b6sm53626027pfe.85 - gsmtp  
</code></pre>

<p>Success.</p>

<p><a href="http://blog.mailgun.com/25-465-587-what-port-should-i-use/">Here's a good summary on the history of SMTP ports (this is the material I like reading because reasons)</a>.</p>

<p><a href="https://community.letsencrypt.org/t/how-to-meet-gmails-new-2016-email-tls-requirement-red-lock/28097/23">Here's an interesting (but long)</a> debugging thread that reminded me that ISPs are shady as hell:</p>

<blockquote>
  <p>By stripping out [STARTTLS], these ISPs prevent the email servers from successfully encrypting their conversation, and by default the servers will proceed to send email unencrypted. [...] Unfortunately, this causes collateral damage: the sending server will proceed to transmit plaintext email over the public Internet, where it is subject to eavesdropping and interception. <a href="https://www.eff.org/deeplinks/2014/11/starttls-downgrade-attacks">[src via the EFF]</a></p>
</blockquote>

<p>💡 <strong>More links on privacy and <a href="https://biancatamayo.me/blog/using-gmail-as-a-postfix-relay-on-ubuntu-16-04/#isps_are_shady">ISPs and technical details</a> in the Goodies Section at the end.</strong></p>

<h3 id="importantdetour">IMPORTANT DETOUR:</h3>

<p>If you're going to use Gmail as a relay server, you <strong>most definitely should not use your main email address</strong> unless you're 100% sure of what you're doing and the security implications behind it (which I am no expert in). Create a separate account for this. Secure it with a good password, but do not send <strong>anything</strong> that contains sensitive or personal data to/from this address.</p>

<p><strong><del>Unless you're using a G Suite account,</del> You may need to turn on <a href="https://support.google.com/accounts/answer/6010255">Less Secure Apps (Gmail Help Article)</a></strong> (<a href="https://support.google.com/a/answer/6260879?hl=en">G-Suite Users use this link</a>) for the relay gmail account. I'm gonna call it <code>myrelaygmail@gmail.com</code>. Sign in to the account on gmail.com once and clear any CAPTCHAs or verification steps.</p>

<h3 id="okayback">Okay, back:</h3>

<p><strong>For the following sections just remember two things</strong>:</p>

<ol>
<li>Prompt starting with <strong>$</strong> is <strong>non-root sudo user</strong>  </li>
<li>Prompt starting with <strong>#</strong> is <strong>root</strong></li>
</ol>

<p>We now have a new gmail account now at <em>myrelaygmail@gmail.com</em>.</p>

<p>Edit <code>/etc/postfix/main.cf</code> and add the following lines to the bottom ensuring no duplicate or overriding keys:</p>

<pre><code class="language-ini">relayhost = [smtp.gmail.com]:587  
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd  
smtp_sasl_security_options = noanonymous  
smtp_tls_CAfile = /etc/postfix/cacert.pem  
smtp_use_tls = yes  
</code></pre>

<p>Cool. Now, even though this will be encrypted over TLS, I want to stress here that you should <strong>most definitely use a new, separate account that you just made for this</strong>. </p>

<p>So this isn't going to work still. We still need to validate the CA cert and provide Postfix access to the Gmail account.</p>

<p>Create or edit <code>/etc/postfix/sasl_passwd</code> to have the following contents (replace <em>myrelaygmail@gmail.com</em> with the email address you created) and replace <code>password</code> with its password:</p>

<p><code>/etc/postfix/sasl_passwd</code>:</p>

<pre><code class="language-ini">[smtp.gmail.com]:587    myrelaygmail@gmail.com:password
</code></pre>

<p>Secure the file (<code>0600</code> works too) then use <code>postmap</code> to hash it:</p>

<pre><code class="language-shell"># chmod 400 /etc/postfix/sasl_passwd
# postmap /etc/postfix/sasl_passwd
</code></pre>

<p>Last part is to validate their CA cert. We can use GlobalSign's cert, which is already in our machine. However, there are other ways of obtaining a valid cert if needed<sup id="fnref:1"><a href="https://biancatamayo.me/blog/using-gmail-as-a-postfix-relay-on-ubuntu-16-04/#fn:1" rel="footnote">1</a></sup>. In our config we had <span class="htip" data-content="More info in the Goodies Section. One day I'll get these tooltips to be useful.">specified</span> <code>smtp_tls_CAfile = /etc/postfix/cacert.pem</code>.</p>

<pre><code class="language-shell"># cat /etc/ssl/certs/GlobalSign_Root_CA.pem | tee -a /etc/postfix/cacert.pem
</code></pre>

<p>Restart the service and test it:</p>

<pre><code class="language-shell"># service postfix restart
# echo "Hello World" | mail -s "Test Message" mytestemail@gmail.com
</code></pre>

<p>Check the logs (could be in mail*.log, or syslog, mine was in syslog:</p>

<pre><code class="language-ini">Feb 19 14:43:36 gitlab postfix/smtp[14917]: A1D012609B8: to=&lt;mytestemail@gmail.com&gt;, relay=smtp.gmail.com[74.125.28.109]:587, delay=1.3, delays=0.01/0/0.55/0.71, dsn=2.0.0, status=sent (250 2.0.0 OK 1519080216 o135sm64453540pfg.45 - gsmtp)  
</code></pre>

<p><strong>250 2.0.0 OK</strong></p>

<h4 id="mistakesgrammarspellingcommentsyoucanalwaysahrefhttpstwittercom__biancatmeontwittera">Mistakes? Grammar/spelling? Comments? You can always <a href="https://twitter.com/__biancat">@ me on Twitter</a>!</h4>

<hr>

<h1 id="centergoodiessectioncenter"><center> ✨ GOODIES SECTION 🌈 </center></h1>

<p><center>For the extra nerdy/awesome/bored/nice of you all.</center></p>

<h4 id="anamesmtp_tls_cafileonsmtp_tls_cafilea"><a name="smtp_tls_CAfile">On <code>smtp_tls_CAfile</code>:</a></h4>

<p>If you open the <code>main.cf</code> file, there's this line:</p>

<pre><code>...
# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
# information on enabling SSL in the smtp client.
...
</code></pre>

<p>You can see the doc via:</p>

<pre><code class="language-shell">$ sudo apt-get install postfix-doc
$ zless /usr/share/doc/postfix/TLS_README.gz
</code></pre>

<p>(<strong>That's right</strong>. <strong>zless</strong> and <strong>zmore</strong> to <code>less</code> and <code>more</code> <code>.gz</code> files. 😎)</p>

<p>You'll find that it says:</p>

<pre><code class="language-ini">If you want the Postfix SMTP client to accept remote SMTP server certificates  
issued by these CAs, append the root certificate to $smtp_tls_CAfile or install  
it in the $smtp_tls_CApath directory.  
</code></pre>

<p>There's also a <a href="http://www.postfix.org/TLS_README.html">copy on the internet</a> which, yes, I realized out after I did all the steps above. It was 2am.</p>

<h3 id="footnotes">Footnotes:</h3>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p><a href="https://support.google.com/a/answer/6180220?hl=en&amp;ref_topic=2683824">Google KB: Use G Suite certificates for secure transport (TLS)</a> <a href="https://biancatamayo.me/blog/using-gmail-as-a-postfix-relay-on-ubuntu-16-04/#fnref:1" title="return to article">↩</a></p></li></ol></div>

<h4 id="furtherreadingongmailandsmtp">Further reading on Gmail and SMTP:</h4>

<ul>
<li><a href="https://support.google.com/a/answer/6260879?hl=en">Google KB: Less secure apps</a></li>
<li><a href="https://support.google.com/a/answer/176600?hl=en">Google KB: Send email from a printer, scanner, or app</a></li>
<li><a href="https://support.google.com/a/answer/2956491?hl=en">Google KB: SMTP relay: Route outgoing non-Gmail messages through Google</a></li>
</ul>

<h4 id="anameisps_are_shadyaispsareshady"><a name="isps_are_shady">🔗</a> ISPs are shady:</h4>

<p><a href="https://www.computerworld.com/article/2476444/mobile-security/mobile-security-comcast-xfinity-wifi-just-say-no.html">A post on Computerworld explaining in pretty good technical detail</a> the security implications of xfinity/comcast shenanigans with xfinity wifi. It also refers to:</p>

<ul>
<li><a href="https://arstechnica.com/information-technology/2014/06/free-wi-fi-from-xfinity-and-att-also-frees-you-to-be-hacked/">Ars Technica</a> + <a href="https://www.npr.org/sections/alltechconsidered/2014/06/10/320347267/project-eavesdrop-an-experiment-at-monitoring-my-home-office">NPR</a>'s joint experiment</li>
<li><a href="https://www.sfgate.com/business/article/Comcast-sued-for-turning-home-Wi-Fi-routers-into-5943750.php">SF Gate: Comcast sued for turning home Wi-Fi routers into public hotspots</a></li>
</ul>

<h3 id="funblogslinkstools">Fun blogs &amp; links &amp; tools!</h3>

<ol>
<li><a href="http://www.postfix.org/TLS_README.html">Postfix's TLS_README</a>  </li>
<li><a href="https://ssl-tools.net/mailservers">SSL-Tools Mailserver Test</a>  </li>
<li><a href="https://workaround.org/ispmail/jessie/relaying-smtp-authentication">How SMTP Works with relaying</a>  </li>
<li><a href="https://www.checktls.com/">Check TLS</a></li>
</ol>

<p>More things you can do with Postfix:</p>

<pre><code>   postalias(1), create/update/query alias database
   postcat(1), examine Postfix queue file
   postconf(1), Postfix configuration utility
   postfix(1), Postfix control program
   postfix-tls(1), Postfix TLS management
   postkick(1), trigger Postfix daemon
   postlock(1), Postfix-compatible locking
   postlog(1), Postfix-compatible logging
   postmap(1), Postfix lookup table manager
   postmulti(1), Postfix multi-instance manager
   postqueue(1), Postfix mail queue control
   postsuper(1), Postfix housekeeping
   mailq(1), Sendmail compatibility interface
   newaliases(1), Sendmail compatibility interface
   sendmail(1), Sendmail compatibility interface
</code></pre>]]></content:encoded></item><item><title><![CDATA[Tech Interviews Chat]]></title><description><![CDATA[<p><strong>TLDR: This post is about a community for folks that want to talk about administering and practicing going through tech interviews or want to solidify their CS/Data Structures/Algorithms concepts, join the Slack community here: <a href="https://join.slack.com/t/technical-interviews/shared_invite/enQtMjcxMDQ3MDI2NDk5LTdiMTI1MmI2NTBhNDc3OGFiMGVjZTVmNmNhYTYyMjM3YTJjMDIxMzEwOWJhY2EyYWY4Y2QwYjY0MmVkNWRjM2E">Tech Interviews Chat at Slack</a>!</strong></p>

<p>Tech interviewing is broken and we all know it.</p>]]></description><link>https://biancatamayo.me/blog/tech-interviews-chat/</link><guid isPermaLink="false">2385fcfa-7b24-4fac-b3d4-52ff7938ce8c</guid><category><![CDATA[miniblog]]></category><category><![CDATA[programming]]></category><category><![CDATA[learning]]></category><category><![CDATA[interviews]]></category><dc:creator><![CDATA[Bianca Tamayo]]></dc:creator><pubDate>Mon, 13 Nov 2017 05:08:35 GMT</pubDate><media:content url="https://biancatamayo.me/blog/content/images/2017/11/whiteboard-interview.png" medium="image"/><content:encoded><![CDATA[<img src="https://biancatamayo.me/blog/content/images/2017/11/whiteboard-interview.png" alt="Tech Interviews Chat"><p><strong>TLDR: This post is about a community for folks that want to talk about administering and practicing going through tech interviews or want to solidify their CS/Data Structures/Algorithms concepts, join the Slack community here: <a href="https://join.slack.com/t/technical-interviews/shared_invite/enQtMjcxMDQ3MDI2NDk5LTdiMTI1MmI2NTBhNDc3OGFiMGVjZTVmNmNhYTYyMjM3YTJjMDIxMzEwOWJhY2EyYWY4Y2QwYjY0MmVkNWRjM2E">Tech Interviews Chat at Slack</a>!</strong></p>

<p>Tech interviewing is broken and we all know it. It's broken for various reasons, and it's a problem I personally thought was interesting and difficult. I've tried a few times to kind of <a href="https://www.gitbook.com/book/btamayo/better-tech-interviews/details">see what I can do about it</a>. </p>

<p><img src="https://i.imgur.com/kt6VDD0.png" width="700px" alt="Tech Interviews Chat"></p>

<p>Anyway, that's not the point of this post. The point is: <a href="https://twitter.com/__biancat/status/923290182245269504">I love teaching people</a>, and also I like learning from others. I'm not the best at technical interviews, but I also know that this is something a lot of people struggle with: both when interviewing as an interviewee or an interviewer. I thought that maybe if I practice these problems more often, I wouldn't be as nervous when the time does come. This is something I've wanted to do for a while but never got around to until I was inspired by other teaching initiatives such as <a href="https://medium.com/basecs">baseCS</a> and <a href="https://www.codenewbie.org/">CodeNewbies</a>.</p>

<p><center><blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">I super appreciate well-made educational programming teaching channels! It&#39;s hard work and a LOT skill and effort 👩‍💻 to teach AND edit vids</p>&mdash; ✨ bcat (@__biancat) <a href="https://twitter.com/__biancat/status/906584915122073600?ref_src=twsrc%5Etfw">September 9, 2017</a></blockquote>  </center></p>

<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>  

<p></p>

<p>So I created a Slack community that for folks that want to chat about practice problems, pair on practicing, ask questions, etc. I especially want to hear about great experiences so those of us in the position to change the interview processes in our own workplaces can create better, more welcoming environments for interviewees.</p>

<p>You can join it right here: <a href="https://join.slack.com/t/technical-interviews/shared_invite/enQtMjcxMDQ3MDI2NDk5LTdiMTI1MmI2NTBhNDc3OGFiMGVjZTVmNmNhYTYyMjM3YTJjMDIxMzEwOWJhY2EyYWY4Y2QwYjY0MmVkNWRjM2E"><strong>Tech Interviews Chat at Slack</strong></a>!</p>

<p><strong>Note</strong>: I just made this, so it's definitely very new.</p>

<p>Also, here's an accompanying Notion page I made earlier: <a href="https://www.notion.so/interviews/Tech-Interviews-4bb13aff2c214e3cb971ec09b702e3b8"><strong>Tech Interviews, Notion</strong></a></p>]]></content:encoded></item><item><title><![CDATA[Two simple, quick methods to access the host network from a Docker container (updated for Docker 18.03, Linux & macOS)]]></title><description><![CDATA[<p><strong>Last updated:</strong> April 6, 2019</p>

<p>Last tested with version <strong>Docker version 18.09.3</strong> on <strong>Ubuntu 16.04, Debian 9 (stretch), macOS Mojave (10.14.4)</strong>.</p>

<p><strong>NOTE:</strong> If your problem is connecting <strong><em>TO</em></strong> containers <strong><em>FROM</em></strong> a macOS host, see this post: <strong><a href="https://biancatamayo.me/blog/docker-troubleshooting-macos-network/">https://biancatamayo.me/blog/docker-troubleshooting-macos-network/</a></strong> </p>

<hr>

<p>Sometimes you need to</p>]]></description><link>https://biancatamayo.me/blog/docker-add-host-ip/</link><guid isPermaLink="false">98a45989-d8de-4a6b-98b6-31c96769ea33</guid><category><![CDATA[tips]]></category><category><![CDATA[docker]]></category><category><![CDATA[networking]]></category><category><![CDATA[linux]]></category><dc:creator><![CDATA[Bianca Tamayo]]></dc:creator><pubDate>Sat, 04 Nov 2017 06:52:00 GMT</pubDate><content:encoded><![CDATA[<p><strong>Last updated:</strong> April 6, 2019</p>

<p>Last tested with version <strong>Docker version 18.09.3</strong> on <strong>Ubuntu 16.04, Debian 9 (stretch), macOS Mojave (10.14.4)</strong>.</p>

<p><strong>NOTE:</strong> If your problem is connecting <strong><em>TO</em></strong> containers <strong><em>FROM</em></strong> a macOS host, see this post: <strong><a href="https://biancatamayo.me/blog/docker-troubleshooting-macos-network/">https://biancatamayo.me/blog/docker-troubleshooting-macos-network/</a></strong> </p>

<hr>

<p>Sometimes you need to be able to connect to the host network from inside a Docker container. Could be for debugging, or small projects, or whatever reason. I ran into this need and after googling, it took me way too long to eventually find the answer in the docs. If you're in the same situation, I hope I can save you some time!</p>

<p><strong>Update April 2019: This won't work for Kubernetes pods/services. Kubernetes networking works differently from plain Docker networking.</strong></p>

<p><strong>Depending on your use case, a network of type <code>host</code> may not work (and requires some setup). Docker provides a way to add hosts into your container's <code>/etc/hosts</code> file.</strong> I'll show it here in two ways, one via CLI and one via docker-compose as a bonus.</p>

<h2 id="1cliusingtheaddhostparameterwithdockerrun">1. CLI: Using the <code>--add-host</code> parameter with <code>docker run</code></h2>

<p><strong>If you're on macOS using Docker Desktop, it's easier. Scroll down or skip to <a href="https://biancatamayo.me/blog/docker-add-host-ip/#noteformacos">note for macOS</a></strong>.</p>

<p><strong>Main idea: pass in the <code>docker0</code> IP address to the container via <code>--add-host</code>:</strong></p>

<p>Get the host machine's address for the <code>docker0</code> interface and put it in shell var (note that <code>docker0</code> doesn't exist on macOS, scroll to the next part if you're on a Mac). Of course, this part is optional if you just want to paste the IP in. </p>

<p>We put it in the <code>HOST_IP</code> shell var like so:</p>

<pre><code>$ HOST_IP=`ip -4 addr show scope global dev docker0 | grep inet | awk '{print \$2}' | cut -d / -f 1`
</code></pre>

<p>(To find the IP address without using the above snippet, you can use the command <code>ifconfig docker0</code>).</p>

<p>Then we execute <code>docker run</code> with <code>--add-host</code>, using the variable we set, e.g.:  </p>

<pre><code>$ docker run --add-host outside:$HOST_IP --name busybox -it busybox /bin/sh
</code></pre>

<p>Now, within the container, we can inspect <code>/etc/hosts</code>, we see an extra line has been added:</p>

<pre><code>bcat@remote:~$ docker run --add-host outside:$HOST_IP --name busybox -it busybox /bin/sh

## now we're in the container:

/ $ cat /etc/hosts
127.0.0.1    localhost  
::1    localhost ip6-localhost ip6-loopback
fe00::0    ip6-localnet  
ff00::0    ip6-mcastprefix  
ff02::1    ip6-allnodes  
ff02::2    ip6-allrouters  
172.17.0.1    outside &lt;---- THIS ONE!  
172.17.0.3    a8300156a695  
</code></pre>

<p>The IP address <code>172.17.0.1</code> which we passed in via <code>docker run</code> is now resolvable via the hostname "<code>outside</code>"!</p>

<p>We can verify it:  </p>

<pre><code>/ $ ping outside
PING outside (172.17.0.1): 56 data bytes  
64 bytes from 172.17.0.1: seq=0 ttl=64 time=0.326 ms  
64 bytes from 172.17.0.1: seq=1 ttl=64 time=0.118 ms  
64 bytes from 172.17.0.1: seq=2 ttl=64 time=0.098 ms  
64 bytes from 172.17.0.1: seq=3 ttl=64 time=0.137 ms  
^C
--- outside ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss  
round-trip min/avg/max = 0.098/0.169/0.326 ms
</code></pre>

<h4 id="noteformacosandwindows">note for macOS (and Windows):</h4>

<p><em>This supposedly also works on Windows</em>, but as I don't have a Windows dev machine, I can't test it.</p>

<p><strong>Docker for Mac does not have the <code>docker0</code> interface<sup id="fnref:1"><a href="https://biancatamayo.me/blog/docker-add-host-ip/#fn:1" rel="footnote">1</a></sup> that's used in the above example.</strong> Instead, you can actually <strong>simply go into the container and use a special macOS-only DNS name</strong> (<code>docker.for.mac.localhost</code>) that comes out of the box (version 17.06+):</p>

<p><strong>Update April 2019:</strong> The DNS name is now called <code>host.docker.internal</code> in the docs, though <code>docker.for.mac.localhost</code> works just as well for now in Docker 18.09.3. Additionally, you can also now also use <code>gateway.docker.internal</code> to access the host's gateway.</p>

<pre><code># In my macOS:

$ bianca@burritomac:~$ docker run -it busybox /bin/sh


## now we're in the container!:

## pinging docker.for.mac.localhost
/ $ ping docker.for.mac.localhost
PING docker.for.mac.localhost (192.168.65.2): 56 data bytes  
64 bytes from 192.168.65.2: seq=0 ttl=37 time=0.420 ms  
64 bytes from 192.168.65.2: seq=1 ttl=37 time=0.564 ms  
^C
--- docker.for.mac.localhost ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss  
round-trip min/avg/max = 0.420/0.492/0.564 ms


## pinging host.docker.internal

/ $ ping host.docker.internal
PING host.docker.internal (192.168.65.2): 56 data bytes  
64 bytes from 192.168.65.2: seq=0 ttl=37 time=0.289 ms  
^C
--- host.docker.internal ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss  
round-trip min/avg/max = 0.289/0.289/0.289 ms


## pinging the host's gateway:

/ $ ping gateway.docker.internal
PING gateway.docker.internal (192.168.65.1): 56 data bytes  
64 bytes from 192.168.65.1: seq=0 ttl=37 time=0.265 ms  
64 bytes from 192.168.65.1: seq=1 ttl=37 time=0.625 ms  
^C
--- gateway.docker.internal ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss  
round-trip min/avg/max = 0.265/0.445/0.625 ms  
</code></pre>

<p>Easy. 🍰</p>

<p>From the docs:</p>

<blockquote>
  <p>The host has a changing IP address (or none if you have no network access). From 18.03 onwards our recommendation is to connect to the special DNS name <code>host.docker.internal</code>, which resolves to the internal IP address used by the host. This is for development purpose and will not work in a production environment outside of Docker Desktop for Mac. </p>
  
  <p>The gateway is also reachable as <code>gateway.docker.internal</code>. <a href="https://docs.docker.com/docker-for-mac/networking/#there-is-no-docker0-bridge-on-macos">[src]</a></p>
</blockquote>

<h2 id="2dockercompose">2. docker-compose:</h2>

<p>This mechanism also available <code>docker-compose.yml</code> files as the <code>extra_hosts</code> key, like so:</p>

<p>docker-compose.yml:  </p>

<pre><code>version: '2'  
services:  
  samplev2:
    image: "redis:alpine"
    extra_hosts:
       - "outside:172.17.0.1"
</code></pre>

<p>And since we took advantage of variable substitution in CLI example, I'll show how we can do the same here:</p>

<pre><code>version: '2'  
services:  
  samplev2:
    image: "redis:alpine"
    extra_hosts:
       - "outside:${HOST_IP}"
</code></pre>

<p>In this example, we don't have an <code>.env</code> file, and <code>HOST_IP</code> is only a shell variable. To become an env var, we need <code>export</code> it first, and then run <code>docker-compose</code>:</p>

<pre><code>$ bcat@remote:~$ export HOST_IP=$HOST_IP &amp;&amp; docker-compose up -d
</code></pre>

<p><strong>Note</strong>: I don't run it as <code>sudo</code> here, but remember that <code>sudo</code> won't work with <code>export</code>!</p>

<p>The <code>-d</code> flag runs it in the background, and we can go into the container and check out our <code>/etc/hosts</code> file in almost the same way as we did earlier. Since we didn't name the container, we grab the container ID using <code>docker ps</code> first, which is <code>0dde138913ee</code>:</p>

<pre><code>bcat@remote:~$ docker exec -it 0dde138913ee /bin/sh

/# cat /etc/hosts
127.0.0.1    localhost  
::1    localhost ip6-localhost ip6-loopback
fe00::0    ip6-localnet  
ff00::0    ip6-mcastprefix  
ff02::1    ip6-allnodes  
ff02::2    ip6-allrouters  
172.17.0.1    outside &lt;---- IT'S HERE!  
172.21.0.2    0dde138913ee  
</code></pre>

<p>Success!</p>

<p>Anyway, hope this is helpful in some way. And if it is please let me know so I can write more! You can always <a href="https://twitter.com/__biancat">find me on Twitter</a>. :)</p>

<p><strong>Update December 2019:</strong> I understand there's a desire for the convenience hostname <code>host.docker.internal</code> to be made available on <strong>all</strong> platforms, and not just macOS or Windows. I don't have an opinion on that, but if you do you can <strong>follow the lengthy discussion here: <a href="https://github.com/docker/for-linux/issues/264">https://github.com/docker/for-linux/issues/264</a></strong></p>

<p>Again, if your problem is connecting <strong><em>TO</em></strong> containers <strong><em>FROM</em></strong> a macOS host, see this post: <strong><a href="https://biancatamayo.me/blog/docker-troubleshooting-macos-network/">https://biancatamayo.me/blog/docker-troubleshooting-macos-network/</a></strong> </p>

<h3 id="footnotesandsuch">Footnotes and such:</h3>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p><a href="https://docs.docker.com/docker-for-mac/networking/#there-is-no-docker0-bridge-on-macos">https://docs.docker.com/docker-for-mac/networking/#there-is-no-docker0-bridge-on-macos</a> <a href="https://biancatamayo.me/blog/docker-add-host-ip/#fnref:1" title="return to article">↩</a></p></li></ol></div>]]></content:encoded></item><item><title><![CDATA[Snippet: Finding brew-installed MySQL Variables]]></title><description><![CDATA[<p>(I seem to have a lot of trouble with Homebrew MySQL, huh?)</p>

<pre><code class="language-shell">$ mysql -se "SHOW VARIABLES"
</code></pre>

<p>e.g.:</p>

<pre><code class="language-shell">$ mysql -se "SHOW VARIABLES" | grep -e log_error -e general_log -e slow_query_log
</code></pre>

<p>Output (with the grep):</p>

<pre><code>binlog_error_action    ABORT_SERVER  
general_log    OFF  
general_log_file    /usr/local/</code></pre>]]></description><link>https://biancatamayo.me/blog/snippet/</link><guid isPermaLink="false">8edf241d-e812-4148-9909-6b38720e7e1b</guid><category><![CDATA[snippet]]></category><category><![CDATA[mysql]]></category><category><![CDATA[homebrew]]></category><category><![CDATA[brew]]></category><category><![CDATA[logs]]></category><category><![CDATA[mysql logs]]></category><dc:creator><![CDATA[Bianca Tamayo]]></dc:creator><pubDate>Sat, 30 Sep 2017 01:40:43 GMT</pubDate><content:encoded><![CDATA[<p>(I seem to have a lot of trouble with Homebrew MySQL, huh?)</p>

<pre><code class="language-shell">$ mysql -se "SHOW VARIABLES"
</code></pre>

<p>e.g.:</p>

<pre><code class="language-shell">$ mysql -se "SHOW VARIABLES" | grep -e log_error -e general_log -e slow_query_log
</code></pre>

<p>Output (with the grep):</p>

<pre><code>binlog_error_action    ABORT_SERVER  
general_log    OFF  
general_log_file    /usr/local/var/mysql/burritobear.log  
log_error    stderr  
log_error_verbosity    3  
slow_query_log    OFF  
slow_query_log_file    /usr/local/var/mysql/burritobear-slow.log  
</code></pre>]]></content:encoded></item><item><title><![CDATA[Blog Update! v1]]></title><description><![CDATA[<p>I updated the blog!</p>

<p>There will be a changelog IN THIS BLOG POST tomorrow (probably), but just a summary:</p>

<ul>
<li>This is now a customized version of <a href="https://ghost.org">Ghost</a> and <a href="https://github.com/TryGhost/Casper">Casper</a>! There will be bugs, for sure!</li>
<li>Please please report bugs to me <a href="https://twitter.com/__biancat">on Twitter or something</a></li>
<li>I am aware of perf</li></ul>]]></description><link>https://biancatamayo.me/blog/blog-update-v1/</link><guid isPermaLink="false">781101a8-d29c-4eb2-8e3f-7d4f0c675986</guid><category><![CDATA[meta]]></category><category><![CDATA[v1]]></category><category><![CDATA[blog]]></category><dc:creator><![CDATA[Bianca Tamayo]]></dc:creator><pubDate>Sun, 10 Sep 2017 07:59:48 GMT</pubDate><content:encoded><![CDATA[<p>I updated the blog!</p>

<p>There will be a changelog IN THIS BLOG POST tomorrow (probably), but just a summary:</p>

<ul>
<li>This is now a customized version of <a href="https://ghost.org">Ghost</a> and <a href="https://github.com/TryGhost/Casper">Casper</a>! There will be bugs, for sure!</li>
<li>Please please report bugs to me <a href="https://twitter.com/__biancat">on Twitter or something</a></li>
<li>I am aware of perf issues. It's not <em>slow</em> but its not IDEAL</li>
<li>I've also been messing with Google Analytics lately as a dataviz enthusiast and apparently it's a huge world so be aware of weird GA experiments</li>
<li>The site is much more colorful! Woo!</li>
<li>Think of everything as under 🛠 development all the time 🛠</li>
</ul>]]></content:encoded></item><item><title><![CDATA[[Concepts] HTTPS Connections: Overview]]></title><description><![CDATA[<h2 id="part1overviewintrotothetlshandshake">PART 1: Overview &amp; intro to the TLS handshake!</h2>

<p>Okay so today I'm going over HTTPS and trying to re-re-remember things I've forgotten and stuff. I thought it might be fun to try to explain it at a higher-level-ish.</p>

<h2 id="whatshttps">What's HTTPS?</h2>

<p>HTTPS is simply "HTTP over TLS", i.e. "HTTP/</p>]]></description><link>https://biancatamayo.me/blog/concepts-https-connections-overview/</link><guid isPermaLink="false">c2212845-1558-4e73-af94-87b435254331</guid><category><![CDATA[miniblog]]></category><category><![CDATA[draft]]></category><category><![CDATA[concepts]]></category><category><![CDATA[staycurious]]></category><category><![CDATA[writingimperfect]]></category><category><![CDATA[ssl]]></category><category><![CDATA[tls]]></category><category><![CDATA[https]]></category><dc:creator><![CDATA[Bianca Tamayo]]></dc:creator><pubDate>Fri, 08 Sep 2017 19:39:48 GMT</pubDate><media:content url="https://biancatamayo.me/blog/content/images/2017/09/Image005.png" medium="image"/><content:encoded><![CDATA[<h2 id="part1overviewintrotothetlshandshake">PART 1: Overview &amp; intro to the TLS handshake!</h2>

<img src="https://biancatamayo.me/blog/content/images/2017/09/Image005.png" alt="[Concepts] HTTPS Connections: Overview"><p>Okay so today I'm going over HTTPS and trying to re-re-remember things I've forgotten and stuff. I thought it might be fun to try to explain it at a higher-level-ish.</p>

<h2 id="whatshttps">What's HTTPS?</h2>

<p>HTTPS is simply "HTTP over TLS", i.e. "HTTP/TLS". It's the same ol' HTTP underneath, it's just wrapped in TLS which is like a protective layer on top that enables encrypted communication.</p>

<p><strong>SSL</strong> is a previous version of TLS, but many people refer to the two as the same thing.</p>

<p><strong>HTTP</strong> is a protocol, which describes <em>how</em> two computers can transfer data between each other (generally in a <em>request-response</em> format). </p>

<p>You can wrap HTTP communication in TLS. While the data carrying HTTP messages can't be understood by eavesdroppers because of TLS, the two parties communicating (the client and the server) have the means to decrypt and understand each other.</p>

<h2 id="whyhttps">Why HTTPS?</h2>

<p>The main advantages are:</p>

<ul>
<li><strong>Verifying identities:</strong> Verify clients are talking to the server they believe they are</li>
<li><strong>Securing communication:</strong> Given that you've verified your communication partner's identity, you can also ensure that no one else can understand your conversation except each other</li>
</ul>

<h2 id="whatsthedifferencebetweenhttpandhttpsconnections">What's the difference between HTTP and HTTPS connections?</h2>

<p>Well, let's look at HTTP communication. We'll pretend we're a sentient computer in someone's home network (an IoT door lock perhaps) and we're watching the foolish mortals browse the web. Someone goes on <a href="http://kitten.bianc.at">kitten.bianc.at</a> using Chrome:</p>

<p><img src="https://static.notion-static.com/6dfa619dbc0243338d2be2b1b6b85939/Screen_Shot_2017-09-07_at_12.40.34_PM.png" alt="[Concepts] HTTPS Connections: Overview"></p>

<p>This is plain HTTP. Here, the communication between the browser and the server is as follows:</p>

<ol>
<li>Browser initiates connection with server  </li>
<li>Browser sends HTTP request saying "please, <code>GET kitten.bianc.at/</code> for me"  </li>
<li>Server reads the request, then figures out what data to send to the client, then sends that data (the page) with also includes a status of <code>200 OK</code> </li>
</ol>

<p>Here's what we see with our super convenient <span class="htip" data-content="Wireshark, duh">internet traffic monitoring tool</span><sup id="fnref:1"><a href="https://biancatamayo.me/blog/concepts-https-connections-overview/#fn:1" rel="footnote">1</a></sup> (with the less relevant portions poorly whited out). We can see immediately that everything is sent in plain text. The convo below highlights steps 2 and 3:</p>

<p><img src="https://static.notion-static.com/ddeaa64ba95241cea745032622482ab5/Image006.png" alt="[Concepts] HTTPS Connections: Overview"></p>

<p>This web developer doesn't have TLS enabled on their site! As a watcher, we can read the exchanged messages; it's not encrypted or anything. Also, the browser then doesn't do any kind of verification that it's talking to who it thinks it's talking to (what if it's actually <a href="http://gooddog.com">gooddog.com</a> <em>pretending</em> to be kitten.bianc.at?)</p>

<p>With how monitored the internet is nowadays (e.g. by work, ISP, your parents, the government?) people don't feel super comfortable with that, and there's decreasing use cases for non-encrypted communication. In this example it's just sending a plain HTML "<em>Welcome to Nginx</em>" page, but what if that had more important info, like someone's tax return or emo poetry (or a password even)?</p>

<p>Thankfully, the webadmin of kitten.bianc.at realizes their folly, and uses <a href="https://letsencrypt.org/">letsencrypt</a> to handily-dandily add TLS to their site. HTTPS is now enabled.</p>

<h2 id="letswatchthesamerequestbutnowovermadeoverhttps">Let's watch the same request, but now over made over HTTPS:</h2>

<p><img src="https://static.notion-static.com/5a025e9b63af4847a894cda7b0fcd576/Screen_Shot_2017-09-07_at_7.54.17_PM.png" alt="[Concepts] HTTPS Connections: Overview"></p>

<p>See the "Secure" sign? 🔒 Chrome says everything is good and secure.</p>

<p>Here's what the packet monitoring sees (it's okay don't read it now it'll make sense later):</p>

<p><img src="https://static.notion-static.com/3d434cb200554bd9b8a69d817f88b0f4/Screen_Shot_2017-09-07_at_5.48.27_PM.png" alt="[Concepts] HTTPS Connections: Overview"></p>

<p>Suddenly there's <strong>tons</strong> more talking before we even get any HTML from the server (I'm guessing the HTML being sent is the one highlighted in a gold box but since I can't read it I can't be 100% sure). However, unlike before, we can't read the messages, since they're now encrypted. Looking at the contents just shows nonsensical data.</p>

<p>Here, the communication between the browser and the server is different, in that it starts with the <strong>TLS Handshake</strong>. The handshake is a set of steps that client and server take to establish <em>if and how to</em> create a secure communication, and it has three main steps:</p>

<ol>
<li>Hello  </li>
<li>Certificate Verification  </li>
<li>Key exchange</li>
</ol>

<p>Here's a terribly un-cropped version of the same image above with a (rough) outline of the steps occurring:</p>

<p><img src="https://static.notion-static.com/dc9bb82764a447b1a522bc5c2b6b1416/Image005.png" alt="[Concepts] HTTPS Connections: Overview"></p>

<h2 id="1hellostepstartingaconnection">1. Hello step (starting a connection):</h2>

<p>The hello step goes like this:</p>

<ol>
<li>The browser sends a <em>ClientHello</em> message, which includes information about what kind of cipher suites it supports (i.e. ways it knows how to encrypt stuff), TLS version, etc. Just introducing itself.  </li>
<li>The server sends its <em>ServerHello</em> and similar information, and also a decision on which cipher suite to use.</li>
</ol>

<p><img src="https://static.notion-static.com/c13dd807b285411dbfbc8ce72eb2946f/Screen_Shot_2017-09-07_at_8.09.16_PM.png" alt="[Concepts] HTTPS Connections: Overview"></p>

<p>A peek into the ClientHello message and it's translation. </p>

<h2 id="2certificateverificationverifyingidentity">2. Certificate Verification (verifying identity)</h2>

<p>This will be it's whole own post later (maybe) where we can go into it into more detail! This deserves a <span class="htip" data-content="It's a bit complicated and this post is already getting long and I'm lazy now">separate post</span>.</p>

<p>Anyway, the main idea is that now that the browser and the server have started talking, the server then sends a <strong>certificate, aka the "SSL Certificate"</strong>. This certificate contains important information for validating identity, and also a <strong>public key</strong>.</p>

<p>The client checks the certificate to see if it's <a href="https://hpbn.co/transport-layer-security-tls/#chain-of-trust-and-certificate-authorities">trustworthy</a> by checking the Certificate Authorities it trusts, or checking the legitimacy of "<span class="htip" data-content="Other certificates that vouch for its legitimacy, all the way down to a root certificate">intermediate certificates</span>", i.e. a <a href="https://hpbn.co/transport-layer-security-tls/#chain-of-trust-and-certificate-authorities">Chain of Trust</a>. If everything is all good, we move on to the third step.</p>

<h2 id="3keyexchangeenablingsecurecommunication">3. Key Exchange (enabling secure communication)</h2>

<p>Here's a very succinct description of this step (from the RFC):</p>

<pre><code> The general goal of the key exchange process is to create a
 pre_master_secret known to the communicating parties and not to
 attackers. The pre_master_secret will be used to generate the
 master_secret (see Section 8.1). The master_secret is required to
 generate the Finished messages, encryption keys, and MAC keys (see
 Sections 7.4.9 and 6.3). By sending a correct Finished message,
 parties thus prove that they know the correct pre_master_secret.
</code></pre>

<p>Okay, so all that means is:</p>

<ol>
<li>During the key exchange step, the parties need to create the <strong>pre-master secret</strong> (also called the pre-master key). This can be done in a couple of ways, the most common being the client generating a random sequence of a set length, and encrypting it under the server's <strong>public key</strong> which the client received earlier from the SSL Certificate.  </li>
<li>The client sends this <span class="htip" data-content="Some key generation algorithms can generate a shared key without needing to send this key, check the footnotes for more info"><strong>encrypted key to the server</strong></span><sup id="fnref:2"><a href="https://biancatamayo.me/blog/concepts-https-connections-overview/#fn:2" rel="footnote">2</a></sup>, and the server decrypts it with its <strong>private key</strong>. Therefore, if that encrypted key is intercepted during transmission, it can't be read unless the person intercepting (maybe some internet overseer) also has the server's private key. This is an example of asymmetric encryption.  </li>
<li>The <strong>pre-master key</strong> that both the parties can read is converted (using a math formula) into the <strong>master secret</strong> (or master key), which the browser and the server use to encrypt and decrypt their outgoing and incoming messages (respectively) for the rest of the session. This is an example of <strong>symmetric encryption</strong>.</li>
</ol>

<p>Once this step is completed, we're pretty much done establishing the secure connection and the rest of the messages are unreadable to outside parties (since they're encrypted). Woo! The keys are valid for the entire session. We're a good IoT lock with a clear conscience.</p>

<p>Later we'll get more into the different technical bits and pieces.</p>

<hr>

<h3 id="mistakesgrammarspellingcommentsyoucanalwaysahrefhttpstwittercom__biancatmeontwittera">Mistakes? Grammar/spelling? Comments? You can always <a href="https://twitter.com/__biancat">@ me on Twitter</a>!</h3>

<h2 id="footnotesandetc">Footnotes and etc:</h2>

<div class="footnotes"><ol><li class="footnote" id="fn:1"><p>If you've never used Wireshark before, you're in for such a treat. <a href="https://www.wireshark.org/">Download it right now and be fascinated.</a> <a href="https://biancatamayo.me/blog/concepts-https-connections-overview/#fnref:1" title="return to article">↩</a></p></li>
<li class="footnote" id="fn:2"><p>An example of a different kind of key exchange that doesn't send a key over the network is the <a href="https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange">Diffie-Hellman Key Exchange</a> <a href="https://biancatamayo.me/blog/concepts-https-connections-overview/#fnref:2" title="return to article">↩</a></p></li></ol></div>

<ul>
<li><a href="https://www.notion.so/Concepts-HTTPS-Connections-Overview-7cac0f33aba84790862ff1163c5d26e2">Here's this post on Notion</a></li>
</ul>

<h2 id="furtherreading">Further reading:</h2>

<ul>
<li><a href="https://www.ietf.org/rfc/rfc5246.txt">https://www.ietf.org/rfc/rfc5246.txt</a> (TLS 1.2 RFC)</li>
<li><a href="https://tools.ietf.org/html/rfc2616">https://tools.ietf.org/html/rfc2616</a> (HTTP 1.1 RFC)</li>
<li><a href="https://hpbn.co/transport-layer-security-tls/">https://hpbn.co/transport-layer-security-tls</a></li>
<li><a href="https://security.stackexchange.com/questions/41205/diffie-hellman-and-its-tls-ssl-usage">https://security.stackexchange.com/questions/41205/diffie-hellman-and-its-tls-ssl-usage</a></li>
</ul>

<h2 id="futurepossiblesimilarpostsmaybe">Future possible similar posts maybe:</h2>

<ul>
<li>What is HTTP?</li>
<li>Understanding packets</li>
<li>DTLS</li>
<li>Decrypting TLS through Wireshark</li>
</ul>

<h4 id="edits">Edits:</h4>

<ul>
<li>Sunday, September 10, 2017: Typo fixes</li>
</ul>]]></content:encoded></item><item><title><![CDATA[Mini-blog: How to run currently opened bash script in Sublime Text 3]]></title><description><![CDATA[<p><strong>Post skimmers</strong>: I <strong>bold</strong> information that should be enough if you're familiar with Sublime Text.</p>

<p>I wanted to write a really quick script in Sublime Text 3 that I could debug and run without having to switch windows. I was a bit surprised that there isn't a zero-config, quick-install package.</p>]]></description><link>https://biancatamayo.me/blog/mini-blog-how-to-run-currently-opened-bash-script-in-sublime-text-3/</link><guid isPermaLink="false">0a26a852-3bfa-49b3-b081-760c049d2a32</guid><category><![CDATA[miniblog]]></category><category><![CDATA[snippets]]></category><category><![CDATA[bash]]></category><category><![CDATA[sublime text]]></category><dc:creator><![CDATA[Bianca Tamayo]]></dc:creator><pubDate>Thu, 29 Jun 2017 22:45:26 GMT</pubDate><content:encoded><![CDATA[<p><strong>Post skimmers</strong>: I <strong>bold</strong> information that should be enough if you're familiar with Sublime Text.</p>

<p>I wanted to write a really quick script in Sublime Text 3 that I could debug and run without having to switch windows. I was a bit surprised that there isn't a zero-config, quick-install package. However, the config needed is very minimal.</p>

<p>Open the <strong>Command Palette (⌘⇧P)</strong> and choose "<strong>Build: New Build System</strong>",</p>

<p>In the file that then pops up, replace the contents with <a href="https://biancatamayo.me/blog/mini-blog-how-to-run-currently-opened-bash-script-in-sublime-text-3/#footnote1" id="footnote1ref"><sup>1</sup></a>:</p>

<pre><code class="language-json">{
    "cmd"       : ["bash", "$file"],
    "selector"  : "source.shell"
}
</code></pre>

<p><strong>Save it as <code>bash.sublime-build</code></strong> in the suggested directory, which should be anywhere in Sublime's <em>Packages</em> directory.</p>

<p>Now if you open up a shell script, you can execute it through Sublime Text and have the output pop up in Sublime Text's console by running <strong>"Build With: bash"</strong> in the Command Palette.</p>

<p><strong>Note</strong>: Sublime Text could have a different <code>$PATH</code> loaded, in which case you may have to pass in a <code>path</code> key into the config, e.g.:</p>

<pre><code class="language-json">{
    "cmd"       : ["bash", "$file"],
    "selector"  : "source.shell",
    "path"      : "/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin"
}
</code></pre>

<hr>

<h5 id="extralinks">Extra links:</h5>

<ul>
<li><a href="http://docs.sublimetext.info/en/latest/reference/build_systems.html">http://docs.sublimetext.info/en/latest/reference/build_systems.html</a> has more information on how to create more complex build systems</li>
</ul>

<hr>

<h5 id="footnotes">Footnotes:</h5>

<p><a id="footnote1" href="https://biancatamayo.me/blog/mini-blog-how-to-run-currently-opened-bash-script-in-sublime-text-3/#footnote1ref"><sup>1</sup></a> Credit for these snippets go to <a href="https://gist.github.com/typeoneerror/1596565#file-bash-sublime-build">this gist thread</a></p>]]></content:encoded></item><item><title><![CDATA[TIL: git extensions]]></title><description><![CDATA[<p>Miniblog!</p>

<p>I’ve been fiddling around with <a href="https://github.com/petervanderdoes/gitflow-avh"><code>git-flow</code></a> for a while now, trying to extend its functionality and customize some things. </p>

<p>Unfortunately, after a while of no success in trying to find where and in <em>which</em> documentation I could find <em>how</em> the git-flow executable turns into <code>git flow</code> (with a</p>]]></description><link>https://biancatamayo.me/blog/til-git-extensions/</link><guid isPermaLink="false">37ad120d-cec7-493c-a824-3961dd87c502</guid><category><![CDATA[shell]]></category><category><![CDATA[miniblog]]></category><category><![CDATA[braindump]]></category><category><![CDATA[TIL]]></category><category><![CDATA[git]]></category><dc:creator><![CDATA[Bianca Tamayo]]></dc:creator><pubDate>Thu, 22 Jun 2017 04:41:00 GMT</pubDate><content:encoded><![CDATA[<p>Miniblog!</p>

<p>I’ve been fiddling around with <a href="https://github.com/petervanderdoes/gitflow-avh"><code>git-flow</code></a> for a while now, trying to extend its functionality and customize some things. </p>

<p>Unfortunately, after a while of no success in trying to find where and in <em>which</em> documentation I could find <em>how</em> the git-flow executable turns into <code>git flow</code> (with a space), I gave up. </p>

<p>(I mean, is it a POSIX thing? Is it a submodule? Is it my shell? Do they change my path? Do they override git? Do they detect my shell and create aliases? What is it? I didn’t know what it was called, and Googling didn’t help me too much.)</p>

<p>Anyway, I had a hunch. And sometimes, proving hunches through trial and error is faster than actually finding documentation when you don’t know the terminology.</p>

<ul>
<li>Okay it wasn’t completely a hunch. I was looking through the <code>GITEXECPATH</code>  directory (<code>$(git —exec-path)</code>) which I found by reading the docs and saw that they were all basically scripts that had a similar filename pattern.</li>
</ul>

<p>So, TIL that if you create an executable in your path with a filename that starts with “<code>git-</code>”, git automatically treats it like a subcommand.</p>

<h2 id="example">Example:</h2>

<p>Here’s a script that’s in my <code>$PATH</code>: <br>
<code>/Users/btamayo/bin/git-echotest.sh</code>:</p>

<pre><code class="language-bash">#!/usr/bin/env bash

echo "Hello you!"  
</code></pre>

<p>Now, in your shell:  </p>

<pre><code>$ git echotest
Hello you!  
</code></pre>

<p>You can also do things such as source a git-provided utility script that lets you use functions such as <code>die</code> and <code>usage</code> . I won’t go into it here since this is a <tag>miniblog</tag> <braindump> but you can read more here: <a href="https://www.kernel.org/pub/software/scm/git/docs/git-sh-setup.html">https://www.kernel.org/pub/software/scm/git/docs/git-sh-setup.html</a></braindump></p>]]></content:encoded></item><item><title><![CDATA[Snippet: Restarting a brew-installed mysql server]]></title><description><![CDATA[<p>Getting this annoying error?</p>

<pre><code>ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)  
</code></pre>

<p>If you installed MySQL through Homebrew, you can restart the server via:</p>

<pre><code>$ /usr/local/Cellar/mysql/5.7.17/bin/mysql.server restart     

 ERROR! MySQL server PID file could not</code></pre>]]></description><link>https://biancatamayo.me/blog/snippet-restarting-a-brew-installed-mysql-server/</link><guid isPermaLink="false">d1d684fe-332c-4177-89c4-26b6481a3c5a</guid><category><![CDATA[snippets]]></category><category><![CDATA[tips]]></category><dc:creator><![CDATA[Bianca Tamayo]]></dc:creator><pubDate>Tue, 18 Apr 2017 23:58:24 GMT</pubDate><content:encoded><![CDATA[<p>Getting this annoying error?</p>

<pre><code>ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)  
</code></pre>

<p>If you installed MySQL through Homebrew, you can restart the server via:</p>

<pre><code>$ /usr/local/Cellar/mysql/5.7.17/bin/mysql.server restart     

 ERROR! MySQL server PID file could not be found!
Starting MySQL  
..... SUCCESS!
</code></pre>

<p>Or simply:</p>

<pre><code>$ mysql.server restart
</code></pre>

<p><center> <br>
<img src="http://i.imgur.com/fqtMPL7.png" style="height:100px;"></center></p>]]></content:encoded></item><item><title><![CDATA[Bash n' Hash]]></title><description><![CDATA[<p>The other day I was messing around with Python and pip installs on a new machine because I wanted to set up a ~super cool~ run config in PyCharm. Fun, right?</p>

<p>On this day, I was having a couple of issues with my <code>PATH</code>, and as far as I can</p>]]></description><link>https://biancatamayo.me/blog/bash-n-hash/</link><guid isPermaLink="false">ddf68636-1362-4a6c-ab85-8fafd4aa6039</guid><category><![CDATA[bash]]></category><category><![CDATA[shell]]></category><dc:creator><![CDATA[Bianca Tamayo]]></dc:creator><pubDate>Thu, 23 Mar 2017 23:48:12 GMT</pubDate><content:encoded><![CDATA[<p>The other day I was messing around with Python and pip installs on a new machine because I wanted to set up a ~super cool~ run config in PyCharm. Fun, right?</p>

<p>On this day, I was having a couple of issues with my <code>PATH</code>, and as far as I can tell, everything was fine. So it was a new problem I've never come across before in all my (fair amount!) of years as a developer. That's when I googled and learned that bash has an <strong>internal hash table</strong>. The tricksy clever thing.</p>

<p>From <code>bash(1)</code>:  </p>

<div class="ui message">  
  <p>Bash uses a hash  table  to remember  the  full pathnames of executable files. A full search of the directories in PATH is performed only if the command is not found in the hash table.</p>
</div>

<p>See that? <strong>"A full search of the directories in PATH is performed only if the command is not found in the hash table."</strong></p>

<p>If you didn't know about this before, you usually hit it like this, in the <em>"woah, which which"</em> scenario.</p>

<p>Here's the rundown:</p>

<pre><code class="language-bash"># I have a script, installed globally
$ cat /usr/local/bin/hello-world
#!/usr/bin/env bash

echo "Hello world! This is my global executable!"

# Let's try it out
$ hello-world
Hello world! This is my global executable!

# Cool, it works! I'd actually like my own version of this so I can modify it, in ~/bin. 
$ cp /usr/local/bin/hello-world ~/bin
$ ls -l ~/bin
-rwxr-xr-x  1 bianca  staff  71 Mar 23 14:13 hello-world

# Cool, checking $PATH real quick
$ echo $PATH
/Users/bianca/bin:/usr/local/bin:/usr/local/sbin

# Looks good! Let me edit my script now
$ sed -i "" 's/global/local/g' /Users/bianca/bin/hello-world
$ cat /Users/bianca/bin/hello-world
#!/usr/bin/env bash

echo "Hello world! This is my local executable!"

# Nice. Testing it out...
$ hello-world
Hello world! This is my global executable!

# ...Wat. That's not right! Troubleshoot.
$ which hello-world
/Users/bianca/bin/hello-world

# Wait it's pointing to the right thing! What is this madness!
# Oh, right! hash!
$ hash
hits command  
    1 /usr/bin/which
    1 /usr/local/bin/hello-world

# Reset it
$ hash hello-world # or `hash -r` to clear everything
hello-world  
Hello world! This is my local executable!

# Success! Wow, that could have have been really annoying to debug if this example wasn't contrived.
</code></pre>

<p>Congrats. You're now prepared for the <em>woah, which which</em> scenario!</p>

<h3 id="bonus">Bonus:</h3>

<p>JSYK, zsh's hash behavior is different:</p>

<h4 id="zsh">ZSH:</h4>

<pre><code class="language-bash"># ZSHELL

$ cat /usr/bin/hello-world
#!/usr/bin/env bash

echo "Hello world! This is my global executable!"

$ hello-world
Hello world! This is my global executable!

$ cp /usr/local/bin/hello-world ~/bin
$ sed -i "" 's/global/local/g' /Users/bianca/bin/hello-world
$ cat ~/bin/hello-world
#!/usr/bin/env bash

echo "Hello world! This is my local executable!"

$ hello-world
Hello world! This is my global executable!

$ hash hello-world # bash would re-read PATH here, zsh doesn't
$ hello-world
Hello world! This is my global executable!

$ hash -r # Drop the entire table

# Now it works!
$ hello-world
Hello world! This is my local executable!

# Will it work if we scrap it? (Doesn't seem like it in bash)
$ rm ~/bin/hello-world
$ hello-world
Hello world! This is my global executable!  
</code></pre>

<h4 id="bash">BASH:</h4>

<pre><code class="language-bash"># BASH

$ cat /usr/local/bin/hello-world
#!/usr/bin/env bash

echo "Hello world! This is my global executable!"

$ hello-world
Hello world! This is my local executable!

# No rebuilding tho
$ rm /Users/bianca/bin/hello-world
$ hello-world
-bash: /Users/bianca/bin/hello-world: No such file or directory

# But this works (Not in zshell)!
$ hash hello-world
$ hello-world
Hello world! This is my global executable!  
</code></pre>]]></content:encoded></item></channel></rss>