diff options
Diffstat (limited to 'users/wpcarro/website/blog')
-rw-r--r-- | users/wpcarro/website/blog/default.nix | 7 | ||||
-rw-r--r-- | users/wpcarro/website/blog/posts.nix | 56 | ||||
-rw-r--r-- | users/wpcarro/website/blog/posts/cell-phone-experiment.md | 6 | ||||
-rw-r--r-- | users/wpcarro/website/blog/posts/git-filter-repo-note.md | 59 | ||||
-rw-r--r-- | users/wpcarro/website/blog/posts/git-rev-refs.md | 85 | ||||
-rw-r--r-- | users/wpcarro/website/blog/posts/importing-subtrees.md | 147 | ||||
-rw-r--r-- | users/wpcarro/website/blog/posts/nginx-curl-note.md | 5 | ||||
-rw-r--r-- | users/wpcarro/website/blog/posts/nix-env-note.md | 33 | ||||
-rw-r--r-- | users/wpcarro/website/blog/posts/nix-shell-note.md | 50 | ||||
-rw-r--r-- | users/wpcarro/website/blog/posts/nixos-disk-full-note.md | 113 | ||||
-rw-r--r-- | users/wpcarro/website/blog/posts/tcp-tunneling-note.md | 63 |
11 files changed, 618 insertions, 6 deletions
diff --git a/users/wpcarro/website/blog/default.nix b/users/wpcarro/website/blog/default.nix index d87b714b6f..27541b0f39 100644 --- a/users/wpcarro/website/blog/default.nix +++ b/users/wpcarro/website/blog/default.nix @@ -11,13 +11,14 @@ let config = { name = "bill and his blog"; baseUrl = "https://${domain}/blog"; + staticUrl = "https://static.tvl.fyi/latest"; footer = ""; }; posts = sort (x: y: x.date > y.date) (filter includePost (list post (import ./posts.nix))); - rendered = pkgs.runCommandNoCC "blog-posts" { } '' + rendered = pkgs.runCommand "blog-posts" { } '' mkdir -p $out ${lib.concatStringsSep "\n" (map (post: @@ -25,7 +26,7 @@ let ) posts)} ''; - formatDate = date: readFile (pkgs.runCommandNoCC "date" { } '' + formatDate = date: readFile (pkgs.runCommand "date" { } '' date --date='@${toString date}' '+%B %e, %Y' > $out ''); @@ -39,7 +40,7 @@ let postDate = formatDate post.date; }); in -pkgs.runCommandNoCC "blog" { } '' +pkgs.runCommand "blog" { } '' mkdir -p $out cp ${withBrand (readFile postsHtml)} $out/index.html cp -r ${rendered} $out/posts diff --git a/users/wpcarro/website/blog/posts.nix b/users/wpcarro/website/blog/posts.nix index 7766dabd60..31fb0c83d8 100644 --- a/users/wpcarro/website/blog/posts.nix +++ b/users/wpcarro/website/blog/posts.nix @@ -57,4 +57,60 @@ content = ./posts/ssh-oddities.md; draft = false; } + { + key = "nix-shell"; + title = "nix-shell (note to self)"; + date = 1664902186; + content = ./posts/nix-shell-note.md; + draft = false; + } + { + key = "git-filter-repo-note"; + title = "git-filter-repo (note to self)"; + date = 1665163559; + content = ./posts/git-filter-repo-note.md; + draft = false; + } + { + key = "nixos-disk-full-note"; + title = "disk full (note to self)"; + date = 1666801882; + content = ./posts/nixos-disk-full-note.md; + draft = false; + } + { + key = "git-rev-refs"; + title = "git revision numbers as refs (note to self)"; + date = 1666823030; + content = ./posts/git-rev-refs.md; + draft = false; + } + { + key = "import-subtree-checklist"; + title = "Checklist for Importing Subtrees"; + date = 1666903846; + content = ./posts/importing-subtrees.md; + draft = false; + } + { + key = "nix-env-note"; + title = "nix-env (note to self)"; + date = 1667343279; + content = ./posts/nix-env-note.md; + draft = false; + } + { + key = "nginx-virtual-host-note"; + title = "Nginx Virtual Host (note to self)"; + date = 1668448541; + content = ./posts/nginx-curl-note.md; + draft = false; + } + { + key = "tcp-tunneling-note"; + title = "TCP Tunneling (note to self)"; + date = 1668709613; + content = ./posts/tcp-tunneling-note.md; + draft = false; + } ] diff --git a/users/wpcarro/website/blog/posts/cell-phone-experiment.md b/users/wpcarro/website/blog/posts/cell-phone-experiment.md index c289954a58..f781a60873 100644 --- a/users/wpcarro/website/blog/posts/cell-phone-experiment.md +++ b/users/wpcarro/website/blog/posts/cell-phone-experiment.md @@ -5,16 +5,16 @@ on it. ### Explore/Exploit -Ever since I read Charles Duhigg's book, [The Power of Habit](poh), I try to +Ever since I read Charles Duhigg's book, [The Power of Habit][poh], I try to habituate as many aspects of my life as I can. Making my bed every morning is an example of a habit -- so too is flossing at night before bed. -The *exploit* axis of the [explore/exploit tradeoff](exp-exp) endows habits with +The *exploit* axis of the [explore/exploit tradeoff][exp-exp] endows habits with their power. Brian Christian and Tom Griffiths explain this concept more clearly than I can in Chapter 2 of their exceptional book, [Algorithms to Live -By](algos). +By][algos]. Habits are powerful, but if I overly exploit an activity, I may settle on a local optimum in lieu of settling on a global optimum; these are the opportunity diff --git a/users/wpcarro/website/blog/posts/git-filter-repo-note.md b/users/wpcarro/website/blog/posts/git-filter-repo-note.md new file mode 100644 index 0000000000..e5fbb05f5c --- /dev/null +++ b/users/wpcarro/website/blog/posts/git-filter-repo-note.md @@ -0,0 +1,59 @@ +## Background + +- I recently used `git-filter-repo` to scrub cleartext secrets from a + repository. +- We pin some services' deployments to commit SHAs. +- These commit SHAs are no longer reachable from `origin/main`. + +## Problem + +If `git` garbage-collects any of the commits to which services are pinned, and +that service attempts to redeploy, the deployment will fail. + +`git for-each-ref --contains $SHA` will report all of the refs that can reach +some commit, `$SHA`. This may report things like: +- `refs/replace` (i.e. `git-filter-repo` artifacts) +- `refs/stash` +- some local branches +- some remote branches + +One solution might involve creating references to avoid garbage-collection. But +if any of our pinned commits contains sensitive cleartext we *want* to ensure +that `git` purges these. + +Instead let's find the SHAs of the new, rewritten commits and replace the pinned +versions with those. + +## Solution + +Essentially we want to find a commit with the same *tree* state as the currently +pinned commit. Here are two ways to get that info... + +This way is indirect, but provides more context about the change: + +```shell +λ git cat-file -p $SHA +tree d011a1dd4a3c5c4c6455ab3592fa2bf71d551d22 # <-- copy this tree info +parent ba88bbf8de61be932184631244d2ec0ec8205cb8 +author William Carroll <wpcarro@gmail.com> 1664993052 -0700 +committer William Carroll <wpcarro@gmail.com> 1665116042 -0700 + +feat(florp): Florp can now flarp + +You're welcome :) +``` + +This way is more direct (read: code-golf-friendly): + +```shell +λ git log -1 --format=%T $SHA +``` + +Now that we have the SHA of the desired tree state, let's use it to query `git` +for commits with the same tree SHA. + +```shell +λ git log --format='%H %T' | grep $(git log --format=%T -1 $SHA) | awk '{ print $1 }' +``` + +Hopefully this helps! diff --git a/users/wpcarro/website/blog/posts/git-rev-refs.md b/users/wpcarro/website/blog/posts/git-rev-refs.md new file mode 100644 index 0000000000..fdc0aaf5cc --- /dev/null +++ b/users/wpcarro/website/blog/posts/git-rev-refs.md @@ -0,0 +1,85 @@ +## Credit + +Credit goes to `tazjin@` for this idea :) + +## Background + +Using `git` revisions to pin versions is nice, but git SHAs aren't very +human-friendly: + +- They're difficult to type. +- They're difficult to say in conversation. +- They're difficult to compare. e.g. Which is newer? `2911fcd` or `db6ac90`? + +## Solution + +Let's assign monotonically increasing natural numbers to each of +our repo's mainline commits and create `git` refs so we can use references like +`r/123` instead of `2911fcd`. + +- They're easy to type: `r/123` +- They're easy to say in conversion: "Check-out rev one-twenty-three." +- They're easy to compare: `r/123` is an earlier version than `r/147`. + +## Backfilling + +Let's start-off by assigning "revision numbers" as refs for each of the mainline +commits: + +```shell +for commit in $(git rev-list --first-parent HEAD); do + git update-ref "refs/r/$(git rev-list --count --first-parent $commit)" $commit +done +``` + +We can verify with: + +```shell +λ git log --first-parent --oneline +``` + +If everything looks good, we can publish the refs to the remote: + +```shell +λ git push origin 'refs/r/*:refs/r/*' +``` + +## Staying Current + +In order to make sure that any newly merged commits have an associated revision +number as a ref, add something like the following to your CI system to run on +the builds of your repo's mainline branch: + +```shell +λ git push origin "HEAD:refs/r/$(git rev-list --count --first-parent HEAD)" +``` + +## Summary + +To verify that the remote now has the expected refs, we can use: + +```shell +λ git ls-remote origin | less # grep for refs/r +``` + +If that looks good, you should now be able to *manually* fetch the refs with: + +```shell +λ git fetch origin 'refs/r/*:refs/r/*' +``` + +Or you can use `git config` to automate this: + +```shell +λ git config --add remote.origin.fetch '+refs/r/*:refs/r/*' +λ git fetch origin +``` + +Now you can run fun commands like: + +```shell +λ git show r/1234 +λ git diff r/123{4,8} # see changes from 1234 -> 1238 +``` + +Thanks for reading! diff --git a/users/wpcarro/website/blog/posts/importing-subtrees.md b/users/wpcarro/website/blog/posts/importing-subtrees.md new file mode 100644 index 0000000000..e1070fc3b9 --- /dev/null +++ b/users/wpcarro/website/blog/posts/importing-subtrees.md @@ -0,0 +1,147 @@ +## Background + +Sometimes you need to merge one Git repo into another. This is a common task +when administrating a monorepo. + +Here's a checklist that I follow: + +1. Detect leaked secrets. +1. Rotate leaked secrets. +1. Purge leaked secrets from repo history. +1. Create mainline references to branches (for deployments). +1. Subtree-merge into the target repo. +1. Format the code. +1. Celebrate! + +## Secrets + +**Note:** If you notice any leaked secrets, first and foremost rotate them +before moving on... + +`gitleaks` supports `gitleaks protect`, but that doesn't seem to work for `WRN` +level leaks, which in my experience often contain sensitive cleartext. We can +use `git-filter-repo` to purge the cleartext from our repo history. + +Let's make a `secrets.txt` file that we can feed `git-filter-repo`: + +```shell +λ gitleaks detect -r /tmp/secrets.json +λ jq -r 'map_values(.Secret) | .[]' /tmp/secrets.txt +``` + +Now for the redacting... + +```shell +λ git-filter-repo --force --replace-text /tmp/secrets.txt +``` + +Verify that the secrets were removed. + +```shell +λ rg --hidden '\*\*\*REMOVED\*\*\*' +λ gitleaks detect -v +``` + +Looks good! Let's move on to support the adopted repo's deploy strategy. + +## Supporting Deploys + +While deploying services when someone pushes to a given branch is a common +deployment strategy, branch-based deployment don't make a whole lot of sense in +a monorepo. + +When adopting another repo, you'll typically encounter a Github Action +configuration that contains a section like this: + +```yaml +on: + push: + - staging + - production +``` + +In our monorepo, `staging` and `production` don't exist. And I don't think we +want to support them either. `staging` and `production` are ambiguous in a +monorepo that hosts multiple services each of which likely having its own notion +of `staging` and `production`. + +Doing "pinned releases" where a service is deployed from a `git` revision from +the mainline branch works well in these scenarios. In order to support this we +need to make sure the adopted repo has references to + +`git subtree add` asks us to define which branch it should use when grafting the +repository onto our monorepo. We'll use `main` (or whatever the mainline branch +is). + +In order to support the *current* deployments while migrating to a pinned +release strategy, we have to ensure that `main` has a commit containing the same +tree state as `staging` *and* another commit containing the same tree state as +`production`. Let's do that! + +```shell +λ git checkout main # ensure you're on the main branch +λ git diff main staging >/tmp/main-to-staging.patch +λ git diff main production >/tmp/main-to-production.patch +``` + +### staging + +```shell +λ git apply /tmp/main-to-staging.patch +λ git add . && git commit # chore: main -> staging +λ git revert HEAD +λ git commit --amend # revert: staging -> main +``` + +### production + +```shell +λ git apply /tmp/main-to-production.patch +λ git add . && git commit # chore: main -> production +λ git revert HEAD +λ git commit --amend # revert: production -> main +``` + +Now let's check our work: + +```shell +λ git log --oneline +38f4422 revert: production -> main +f071a9f chore: main -> production +02ea731 revert: staging -> main +308ed90 chore: main -> staging +``` + +When we go to support pinned releases we can do something like so: + +```json +{ + "staging": "308ed90", + "production": "f071a9f" +} +``` + +## Subtree Merge + +Now the repo is ready to be merged. + +```shell +λ git subtree add --prefix=foo/bar/baz path/to/baz main +λ git commit --amend # subtree: Dock baz into monorepo! +``` + +## Formatting + +Some CI enforces code formatting standards, so you may need to run that: + +```shell +λ repofmt +λ git add . && git commit # chore(fmt): Format the codes +``` + +Lastly, if you need the latest monorepo code from `origin/main` before opening a +pull request, the following should work: + +```shell +λ git fetch origin main && git rebase origin/main --rebase-merges --strategy=subtree +``` diff --git a/users/wpcarro/website/blog/posts/nginx-curl-note.md b/users/wpcarro/website/blog/posts/nginx-curl-note.md new file mode 100644 index 0000000000..e2f4341f54 --- /dev/null +++ b/users/wpcarro/website/blog/posts/nginx-curl-note.md @@ -0,0 +1,5 @@ +Use the following to make requests to Nginx virtual hosts from the host itself: + +```shell +$ curl -H 'Host: trace.website.internal' localhost:8000 +``` diff --git a/users/wpcarro/website/blog/posts/nix-env-note.md b/users/wpcarro/website/blog/posts/nix-env-note.md new file mode 100644 index 0000000000..8683c52e8f --- /dev/null +++ b/users/wpcarro/website/blog/posts/nix-env-note.md @@ -0,0 +1,33 @@ +## Background + +Much in the same vain as my [nix-shell (note to self)][nix-shell-note], I'm +going to leave a note to my future self on how to install packages using +`nix-env`, which is something I do once in a blue moon. + +## Solution + +```shell +λ nix-env -iA tvix.eval -f /depot +``` + +Looks like I was forgetting the `-f /depot` option all this time: + +> --file / -f path +> Specifies the Nix expression (designated below as the active Nix +> expression) used by the --install, --upgrade, and --query --available +> operations to obtain derivations. The default is ~/.nix-defexpr. +> - `man nix-env` + +## Failed Attempts (don't try these at home) + +This section is brought to you by my shell's `Ctrl-r`! + +```shell +λ nix-env -I depot=/depot -iA depot.tvix.eval +λ NIX_PATH=depot=/depot nix-env -iA depot.tvix.eval +λ nix-env -iE '(import /depot {}).tvix.eval' +``` + +Thanks for reading! + +[nix-shell-note]: https://billandhiscomputer.com/blog/posts/nix-shell.html diff --git a/users/wpcarro/website/blog/posts/nix-shell-note.md b/users/wpcarro/website/blog/posts/nix-shell-note.md new file mode 100644 index 0000000000..da33c846ce --- /dev/null +++ b/users/wpcarro/website/blog/posts/nix-shell-note.md @@ -0,0 +1,50 @@ +## Background + +I rarely use `nix-shell` for its originally intended purpose of "reproducing the +environment of a derivation for development". Instead, I often use it to put +some executable on my `PATH` for some ad hoc task. + +What's `nix-shell`'s "intended purpose"? Let's ask The Man (`man nix-shell`): + +> The command nix-shell will build the dependencies of the specified derivation, +> but not the derivation itself. It will then start an interactive shell in +> which all environment variables defined by the derivation path have been set +> to their corresponding values, and the script $stdenv/setup has been +> sourced. This is useful for reproducing the environment of a derivation for +> development. + +Because I'm abusing `nix-shell` in this way, I'm liable to forget that +`nix-shell` puts `buildInputs` on `PATH` and *not* the derivation itself. But I +often only want the derivation! + +## Solution + +Pass the Nix expression to `nix-shell -p`: + +```shell +λ nix-shell -p '(import /depot {}).tvix.eval' +``` + +## Explanation + +This works because Nix forwards the arguments passed to `-p` (i.e. `--packages`) +and interpolates them into this expression here: [source][nix-src] + +```nix +{ ... }@args: + +with import <nixpkgs> args; + +(pkgs.runCommandCC or pkgs.runCommand) "shell" { + buildInputs = [ + # --packages go here + ]; +} +``` + +So really you can pass-in *any* valid Nix expression that produces a derivation +and `nix-shell` will put its outputs on your `PATH`. + +Enjoy! + +[nix-src]: https://sourcegraph.com/github.com/NixOS/nix@3ae9467d57188f9db41f85b0e5c41c0c9d141955/-/blob/src/nix-build/nix-build.cc?L266 diff --git a/users/wpcarro/website/blog/posts/nixos-disk-full-note.md b/users/wpcarro/website/blog/posts/nixos-disk-full-note.md new file mode 100644 index 0000000000..4bbd3f58e2 --- /dev/null +++ b/users/wpcarro/website/blog/posts/nixos-disk-full-note.md @@ -0,0 +1,113 @@ +## Background + +Every now and then NixOS hosts runs out of disk space. This happened to my IRC +server recently... + +> No problem. Let's free-up some space with Nix's garbage-collection: +> - me + +```shell +λ nix-collect-garbage -d # failed due lack of disk space +``` + +Ironically Nix needs to do an SQLite transaction before deleting stuff and +SQLite can't do that if there's no space. This is especially funny because the +SQLite is probably a `DELETE`. + +## Solution + +First let's verify that our disk is indeed at capacity: + +```shell +λ df -h +Filesystem Size Used Avail Use% Mounted on +devtmpfs 399M 0 399M 0% /dev +tmpfs 3.9G 0 3.9G 0% /dev/shm +tmpfs 2.0G 3.7M 2.0G 1% /run +tmpfs 3.9G 408K 3.9G 1% /run/wrappers +/dev/disk/by-label/nixos 9.9G 9.9G 0G 100% / +tmpfs 4.0M 0 4.0M 0% /sys/fs/cgroup +tmpfs 797M 0 797M 0% /run/user/0 +``` + +Looks like `/dev/disk/by-label/nixos` is at `100%`. Now let's find some easy +targets to free-up space so that we can run `nix-collect-garbage -d`... + +```shell +λ du -hs /* 2>/dev/null +8.0K /bin +12M /boot +0 /dev +200K /etc +68K /home +16K /lost+found +9.0G /nix +0 /proc +1.2M /root +2.9M /run +4.0K /srv +0 /sys +44K /tmp +12K /usr +1.2G /var +``` + +Okay: `/var` looks like an easy candidate. Let's recurse into that directory: + +```shell +λ du -hs /var/* +40K /var/cache +12K /var/db +4.0K /var/empty +4.0K /var/google-users.d +211M /var/lib +0 /var/lock +918M /var/log +0 /var/run +4.0K /var/spool +44K /var/tmp +λ du -hs /var/log/* # /var/log looks promising +60M /var/log/btmp +82M /var/log/btmp.1 +776M /var/log/journal # ah-ha! journald. Let's clean-up some logs +8.0K /var/log/lastlog +1.1M /var/log/nginx +4.0K /var/log/private +12K /var/log/wtmp +``` + +To retain at most 1w's worth of logs: + +```shell +λ journalctl --vacuum-time=1w +``` + +...or if you'd prefer to retain only 100M's worth of logs: + +```shell +λ journalctl --vacuum-size=100M +``` + +Now Nix should be able to garbage-collect! + +```shell +λ nix-collect-garbage -d +``` + +And lastly verify that it WAI'd: + +``` +λ df -h +Filesystem Size Used Avail Use% Mounted on +devtmpfs 399M 0 399M 0% /dev +tmpfs 3.9G 0 3.9G 0% /dev/shm +tmpfs 2.0G 3.7M 2.0G 1% /run +tmpfs 3.9G 408K 3.9G 1% /run/wrappers +/dev/disk/by-label/nixos 9.9G 5.1G 4.3G 55% / +tmpfs 4.0M 0 4.0M 0% /sys/fs/cgroup +tmpfs 797M 0 797M 0% /run/user/0 +``` + +## Closing Thoughts + +Why doesn't Nix just reserve enough space to be able to GC itself? Not sure... diff --git a/users/wpcarro/website/blog/posts/tcp-tunneling-note.md b/users/wpcarro/website/blog/posts/tcp-tunneling-note.md new file mode 100644 index 0000000000..06f6469aff --- /dev/null +++ b/users/wpcarro/website/blog/posts/tcp-tunneling-note.md @@ -0,0 +1,63 @@ +## Background + +Let's say we'd like to debug a remote machine but use some of the debugging +tools we have on our local machine like wireshark. + +You *can* run `tcpdump` on the remote and then `scp` the file to your local +machine to analyze the traffic, but after doing that a few times you may want a +workflow with a tighter feedback loop. For this we'll forward traffic from a +remote machine to our local machine. + +**Note:** There's also `termshark`, which is a `wireshark` TUI that you can run +on the remote. It's quite cool! + +## Local + +Run the following on your local machine to forward your remote's traffic: + +```shell +λ ssh -R 4317:127.0.0.1:4317 -N -f user@remote +``` + +Here is an abridged explanation of the flags we're passing from `man ssh`: + +``` +-N Do not execute a remote command. This is useful for just forwarding ports. +-f Requests ssh to go to background just before command execution. +``` + +**Note:** I couldn't find a good explanation for the `-R` option, so I tried +removing it and re-running the command, but that results in a resolution error: + +``` +ssh: Could not resolve hostname 4317:127.0.0.1:4317: Name or service not known +``` + +The remote should now be forwarding traffic from port `4317` to our +machine. + +## Testing + +Let's generate some traffic on the remote: + +```shell +λ telnet localhost 4317 +Trying ::1... +Connected to localhost. +Escape character is '^]'. +hello +world +``` + +Locally you should see: + +```shell +λ nc -l 4317 -k # run this *before* running the above command +hello +world +``` + +You should now be able to `tcpdump -i lo port 4317` or just use `wireshark` +locally. + +Happy debugging! |