This commit is contained in:
parent
bb2dee674f
commit
947c4eb571
3 changed files with 400 additions and 1 deletions
|
@ -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);
|
||||
|
|
381
log/002-soppy-wet-nix.doll
Normal file
381
log/002-soppy-wet-nix.doll
Normal file
|
@ -0,0 +1,381 @@
|
|||
---
|
||||
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
|
||||
- age: ??? # for machine-1
|
||||
- age: ??? # for machine-2
|
||||
- path_regex: secrets/machine-1/[^/]+\.(yaml|json|env|ini)$
|
||||
key_groups:
|
||||
- age: ??? # for doll
|
||||
- age: ??? # for machine-1
|
||||
- path_regex: secrets/machine-2/[^/]+\.(yaml|json|env|ini)$
|
||||
key_groups:
|
||||
- age: ??? # for doll
|
||||
- age: ??? # for machine-2
|
||||
]
|
||||
|
||||
[em:wait. age. that ag-] - shh. be Still.
|
||||
|
||||
&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.
|
|
@ -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>"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue