add 002
Some checks failed
ci/woodpecker/push/build-deploy Pipeline failed

This commit is contained in:
41666 2025-05-16 14:22:07 -07:00
parent bb2dee674f
commit f9db24c502
3 changed files with 403 additions and 1 deletions

View file

@ -79,6 +79,23 @@
tab-size: 2;
}
.doll-quote {
margin: 1rem;
margin-left: 0;
padding-left: 1rem;
width: fit-content;
}
.doll-quote blockquote {
color: var(--bright1);
background-color: var(--dark1);
margin: 0;
border-left: 2px solid var(--middle1);
border-bottom: 1px dashed var(--middle1);
padding: 0.5rem 1.15rem;
width: fit-content;
}
.mermaid {
width: fit-content;
color: var(--middle1);

384
log/002-soppy-wet-nix.doll Normal file
View file

@ -0,0 +1,384 @@
---
title: soppy_wet_nix log[002]
description: how it stores secrets alongside its code
---
&soppy wet nix (16MAY2025) ^a
what if you read [code:/secrets/dont_leak] but it said [codeblock::
dont_leak: ENC[AES256_GCM,data:psyelHNBMy+xglw=,iv:UhxfqAqVbCgMRMqRMA1MmvgIO18zTrVtQdFywupZyYA=,tag:Legj+njC3z8jX16n1pZszg==,type:str]
]
that's cool but what?
let's face it; sometimes stuff is best left to one's own eyes. like auth tokens, passwords, private keys...
most of the time, handling these manually once somewhere, dropping it in a file somewhere on a
remote machine or UI and that's the end of it.
but what if you're like. the type of lazy that doing a lot of work now means not doing the work a second time?
[em:that's like this doll.]
lets talk about our bestie [em(b):[link(https://github.com/Mic92/sops-nix):sops-nix]]
&but first, sops.
sops-nix is based on [link(https://github.com/getsops/sops):sops], so unfortunately we need to start there.
[em(b):sops] is a way to automate encryption and decryption of secret data you might want tightly coupled to a use case.
irl, one might pair this to AWS KMS or another type of distributed keystore; and that's cool. but. this isn't irl.
this is a video game, dolly.
instead we'll focus on sops's [link(https://github.com/FiloSottile/age):age] encryption method, which is based on ed25519 SSH keys you already use (right?)
[quote::
it can hear those thoughts,
[em:> aki wtf, this is 3 whole things, this one is getting overwhelmed]
]
yeah real
we're going to store all of our secrets in this sort of tree,
[codeblock::
project
├── secrets
│ ├── global.yaml
│ ├── machine-1
│ │ └── secrets.yaml
│ ├── machine-2
│ │ └── secrets.yaml
└── .sops.yaml
]
each machine gets a folder under secrets, and there's a global one for every machine too.
there's also a [code:.sops.yaml] file to mention, to support our needs, we'll do...
[codeblock::
creation_rules:
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
key_groups:
- age:
- ??? # for doll
- ??? # for machine-1
- ??? # for machine-2
- path_regex: secrets/machine-1/[^/]+\.(yaml|json|env|ini)$
key_groups:
- age:
- ??? # for doll
- ??? # for machine-1
- path_regex: secrets/machine-2/[^/]+\.(yaml|json|env|ini)$
key_groups:
- age:
- ??? # for doll
- ??? # for machine-2
]
[em:wait. age. that ag-] - shh.
&hold on ,...
dolly,,, listen. yaml is. [em(b):[em:v]e[em:r]y [em:f]u[em:n]]. and so lets make it fun.
[codeblock::
keys: &all
- &op_doll ???
- &m_machine-1 ???
- &m_machine-2 ???
creation_rules:
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
key_groups:
- age: *all
- path_regex: secrets/machine-1/[^/]+\.(yaml|json|env|ini)$
key_groups:
- age: *op_doll
- age: *m_machine-1
- path_regex: secrets/machine-2/[^/]+\.(yaml|json|env|ini)$
key_groups:
- age: *op_doll
- age: *m_machine-2
]
now this isn't a sops thing, this is a yaml thing. yaml has anchors......
sops doesn't know what that [code:keys] key is, it gracefully ignores stuff it doesn't care about, so we can exploit this!!!
this solves three huge problems with sops + age
- which key is machine [code:age1lq5q5g5qjsdcc3k] anyway? - we solve this by naming every key
- the "global" group should always be every key... so we have that too
- a secret third problem
ok. back to whatever we had before
&how old is one's encryption
age answers that question by letting you use ed25519 keys to encrypt and decrypt your data.
[quote:ed25519 would be a good name for a doll... ^n]
since we're deploying to machines with presumably an SSH server running, and presumably
the doll also has an SSH client, all of these ends have SSH keys we can use
&lets start with the doll end,
[codeblock::
$> ssh-to-age -i ~/.ssh/id_ed25519.pub
#|> age13c5wv623jxjja5mjz7fajg9qqwvypzgsfqrs4tmk7rpgyzu7aufs4ul9f9
]
wow that's a string! yay!
we'll plop that in our keys section.
[codeblock::
keys: &all
- &op_doll age13c5wv623jxjja5mjz7fajg9qqwvypzgsfqrs4tmk7rpgyzu7aufs4ul9f9
- &m_machine-1 ???
- &m_machine-2 ???
]
sops will automatically use the [code:~/.ssh/id_ed25519] file, we're good to go!!!
&the machines,,
so SSH servers also have host keys, so when sops is ran on a specific host, it
can also decrypt like the doll can.
we'll fetch machine-1's ssh public keys and slap those in too.
[codeblock::
$> ssh-keyscan -qt ed25519 machine-1.local | ssh-to-age
#=> machine-1 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOGae8gFqSsfezM/ul7LnCmv9GAk6AIah18+tc212LQW
#=> age1jc6ghxfgxe3gx53xa55azxan447cfxaqfqeh5y5yzqapj7mw7ajql8kv02
]
and yeet into the keys block, and machine-2's too.
[codeblock::
keys: &all
- &op_doll age13c5wv623jxjja5mjz7fajg9qqwvypzgsfqrs4tmk7rpgyzu7aufs4ul9f9
- &m_machine-1 age1jc6ghxfgxe3gx53xa55azxan447cfxaqfqeh5y5yzqapj7mw7ajql8kv02
- &m_machine-2 age1438lvn7gh4he0rnj0xnvnx56l97mpz0vsv3wktj8utk65kqs8ycqftcxze
]
&so.. lets sops!!!!!!!!!
we did all the hard parts. lets do the secret parts.
[codeblock::
$> sops secrets/global.yaml
# secret, set EDITOR to something..
# like `EDITOR="code -w"` will open a vscode tab.... (close tab to save)
]
this usually shows a nice default yaml file, yeet all that into the garbage.
we wanna store something useful like... an authentication token to a certain doll's
multicelluar digital cortex.
[codeblock::
doll_token: awawawa
]
lets save that file, and go inspect it.
[codeblock::
doll_token: ENC[AES256_GCM,data:DryFfMa/dQ==,iv:IdqvGEKKTSOaATlqMqyHQ3PEAwC6mJqjHbO3KwfTlHc=,tag:5ByySoYZu6qZexHBmnQBKA==,type:str]
sops:
age:
- recipient: age13c5wv623jxjja5mjz7fajg9qqwvypzgsfqrs4tmk7rpgyzu7aufs4ul9f9
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvdTBVb0FvM2xocWFVclJt
L05aRmpKOHh6d3hXcE9Vem9SL1hrSjFMRng4CmNrTmROTFVMRlc4QjllWVo1S3lv
MXo5UVFuazVoeWQ5TEE1NXJIUzl0K1EKLS0tICtrSTdoWHIrSlJLRTUweWpoVWlY
ZTNXNUM1ZEpsQlMzRlNoZHpkU2lhVzAKpYJAfihRyG/ok0tgJg4FjnN8vj6Bz/+z
0Y/P21JJp/SnXq4LjivBCT4XJ0s6XoffUEqw/uxLzsY1wwox003pOA==
-----END AGE ENCRYPTED FILE-----
- recipient: age1jc6ghxfgxe3gx53xa55azxan447cfxaqfqeh5y5yzqapj7mw7ajql8kv02
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGWVUrWmIvOXUzYmJyWm9G
dmhNQ0lUNFh5ZTdXMmMxNWJENXlMU1daN3lzCnh5cmQyMFlSWDBQV1VSOCtoTUcv
anpmWVFVUmpmZ2svUFpFMUI0YUlwclkKLS0tIFc5NDRQWTdseXdyUE5BbXUwSFk4
VXptdjk3cHFEV0twaXhSYzY2R0JrUzQK5RNS2XdR1m7/SGfpNFh5Z9Q2YGsJT1Cw
iJyW+7EseiuWEkFa2JFul6nsP8W1TmDobk2VXiYpc/BTm78hBlUhyg==
-----END AGE ENCRYPTED FILE-----
- recipient: age1438lvn7gh4he0rnj0xnvnx56l97mpz0vsv3wktj8utk65kqs8ycqftcxze
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtOUtYUXhOTzJlS2lHUTFz
TGwyQ2Y2aEZWWnNmWjVqWWtXeHdmQlFPTFFzCnNSU2FDVFdsTC9hWWV4NEtNQ1FF
bDRvOXJiR3hRb1Ryc0VuTkExQkxoMHMKLS0tIE1ETGJZc04yOHJkaTkwaTlyZW1j
N1pacGpGZGtnNWdFSjZxbjVuVlRZVzAKjDaMO/oKlq4D1QHTlD9lBY+0w81/gybv
2+BSo93LG+bN+cNI9jYc9FU+t4GdlyhEcKQ4MTczgZyMaMOYkhgKcg==
-----END AGE ENCRYPTED FILE-----
]
sops completely obfuscates this file.
this is now safe to send into git. or even over the internet.
or even a website like this one. haha.
really, it is. these are real keys, and good encryption, and hopefully only intended targets can decrypt them.
the reader can even send this doll encrypted messages with that op_doll age key, if one wanted.
what's happening is each "recipient" is getting an individual copy of the file encrypted with
the recipient's public key.
the only way to get the data back out is to have one of these machine's private keys.
&further sopping
we can do [code: sops secrets/machine-1/secrets.yaml] and set up machine-specific secrets too.
those won't be able to be decrypted by machine-2, nor vice versa, due to how we
set up [code:.sops.yaml] earlier.
also, if one happens to rotate SSH keys or. delete/remake a server or somethin',
we can ask sops to redo its work with
[codeblock::
$> sops updatekeys -y secrets/global.yaml
]
[quote(protip)::
put in a op_doll_2 as a new key in all the same spots, then run the command above
finally, remove the old key, and run the above command again.
get rotated !
]
&nix sauce
260 or so lines in and we haven't even talked about nix yet.
sops-nix lets us use sops. from nix. incredible.
we use flakes, so we add [code:github:Mic92/sops-nix] as an input, and work it into our machines.
[codeblock::
{ inputs, ... }: {
imports = [
inputs.sops-nix.nixosModules.sops
];
# lets us shorthand for global secrets
sops.defaultSopsFile = ../path/to/../secrets/global.yaml;
# allows sops-nix to decrypt using machine keys
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
}
]
now say we wanna use that token... there's a few different ways.
&reference as a file directly
nix is. weird. strings aren't strings. strings are files. and files are strings. and files are files. and secrets are files...
for example, if we want to set up an openvpn client, doll's provider would give an .ovpn file. toss that in sops as a
yaml value (or json or whatever it guess)!! make it sopping. instantly.
our [code:secrets/machine-1/secrets.yaml]:
[codeblock::
ovpn_file: |
client
dev tun
proto udp
remote 41.66.66.66 41666
#...
]
our [code:modules/openvpn.nix]:
[codeblock::
{ config, ... }: {
# the "ovpn_file" here matches the YAML key above.
sops.secrets.ovpn_file = {
sopsFile = ../machine-1/secrets.yaml
};
services.openvpn.servers.doll = {
config = ''
config ${config.sops.secrets.ovpn_file.path}
'';
}
}
]
&templates!
but wait haha there's usually like a login username and password right?
good catch, lets add those to our [code:secrets/machine-1/secrets.yaml]:
[codeblock::
ovpn_username: doll
ovpn_password: witchesRme4n
ovpn_file: |
#...
]
and lets make a template!
[codeblock::
{ config, ... }: {
sops.secrets.ovpn_file = {
sopsFile = ../machine-1/secrets.yaml
};
# our two new friends
sops.secrets.ovpn_username = {
sopsFile = ../machine-1/secrets.yaml
};
sops.secrets.ovpn_password = {
sopsFile = ../machine-1/secrets.yaml
};
# this is just how openvpn likes it. no typos.
sops.templates.ovpn_credentials = {
content = ''
${config.sops.placeholder.ovpn_username}
${config.sops.placeholder.ovpn_password}
'';
};
services.openvpn.servers.doll = {
config = ''
config ${config.sops.secrets.ovpn_file.path}
auth-user-pass ${config.sops.templates.ovpn_credentials.path}
'';
}
}
]
wow. it can do everything.
doll might notice the "consumer" side of sops-nix uses [code:config] a lot. yep. its a little self-referential.
&secrets........ but what about automation
that silly [code:.sops.yaml] thing huh, c:
why did we tag the age keys with [code:op_] and [code:m_]? so we can find them. in a script.
and then generate the yaml so when one adds a new machine its like one command. :>
we did this with some simple JS script
[link(https://git.sapphic.engineer/noe/nixos/src/branch/main/tools/onboard-machine.js):doll could reference.]
(it runs via bun so no node_modules!!!)
this allows us to run a little [code:onboard-machine.js 41.66.66.66 machine-1] and pull the right
key from a remote machine
&is doll soppy yet
sure hope so.

View file

@ -4,7 +4,8 @@ cd log
echo "<ul>"
for file in "$(ls -1 ???-*.doll)"; do
files=$(ls ???-*.doll)
for file in $files; do
echo "<li>"
echo "<a href=\"${file//.doll/.html}\">${file//.doll/}</a>"