use sops for secret management

This commit is contained in:
Seaswimmer 2024-12-02 20:50:24 -05:00
parent 6eeea660f0
commit cd820411c4
Signed by: cswimr
GPG key ID: 0EC431A8DA8F8087
9 changed files with 95 additions and 25 deletions

9
.sops.yaml Normal file
View file

@ -0,0 +1,9 @@
keys:
- &cswimr age1q9f9zhkfjn2c3a8qtmfqh0rtls3542jukqpt7t93jca6hc947f3sm9ujhx
- &eclipse age184ude6fyak8z4nnndq4nzcpe2d89zxf3r4paty7j2tenkwa6zgtqrz60lq
creation_rules:
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
key_groups:
- age:
- *cswimr
- *eclipse

View file

@ -1,7 +0,0 @@
keys:
- $eclipse age1q9f9zhkfjn2c3a8qtmfqh0rtls3542jukqpt7t93jca6hc947f3sm9ujhx
creation_rules:
- path_regex: secrets/[^/]+\.(yaml|json|env|ini|sops)$
key_groups:
- age:
- *eclipse

View file

@ -532,9 +532,30 @@
"nixpkgs": "nixpkgs_3", "nixpkgs": "nixpkgs_3",
"nixvim": "nixvim", "nixvim": "nixvim",
"plasma-manager": "plasma-manager", "plasma-manager": "plasma-manager",
"sops-nix": "sops-nix",
"zen-browser": "zen-browser" "zen-browser": "zen-browser"
} }
}, },
"sops-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1733128155,
"narHash": "sha256-m6/qwJAJYcidGMEdLqjKzRIjapK4nUfMq7rDCTmZajc=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "c6134b6fff6bda95a1ac872a2a9d5f32e3c37856",
"type": "github"
},
"original": {
"owner": "Mic92",
"repo": "sops-nix",
"type": "github"
}
},
"systems": { "systems": {
"locked": { "locked": {
"lastModified": 1681028828, "lastModified": 1681028828,

View file

@ -11,6 +11,10 @@
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
inputs.home-manager.follows = "home-manager"; inputs.home-manager.follows = "home-manager";
}; };
sops-nix = {
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
nix-flatpak.url = "github:gmodena/nix-flatpak"; nix-flatpak.url = "github:gmodena/nix-flatpak";
nixvim = { nixvim = {
url = "github:nix-community/nixvim"; url = "github:nix-community/nixvim";
@ -44,6 +48,7 @@
inherit pkgs; inherit pkgs;
system = system; system = system;
hostname = "eclipse"; hostname = "eclipse";
user = user;
}; };
modules = [ modules = [
# imports # imports
@ -60,6 +65,7 @@
./nixos/nvim.nix ./nixos/nvim.nix
./nixos/pkg.nix ./nixos/pkg.nix
./nixos/shell.nix ./nixos/shell.nix
./nixos/sops.nix
./nixos/sudo.nix ./nixos/sudo.nix
./nixos/symlinks.nix ./nixos/symlinks.nix
./nixos/tailscale.nix ./nixos/tailscale.nix
@ -69,6 +75,7 @@
hardware.bluetooth.enable = true; hardware.bluetooth.enable = true;
} }
inputs.sops-nix.nixosModules.sops
inputs.nixvim.nixosModules.nixvim inputs.nixvim.nixosModules.nixvim
inputs.nix-flatpak.nixosModules.nix-flatpak inputs.nix-flatpak.nixosModules.nix-flatpak

13
nixos/sops.nix Normal file
View file

@ -0,0 +1,13 @@
{ user, ... }:
{
sops = {
defaultSopsFile = ../secrets/secrets.yaml;
age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
secrets = {
"zipline" = {
owner = user;
path = "/home/${user}/.secrets/zipline";
};
};
};
}

View file

@ -13,6 +13,8 @@ def notify(
icon: Path | None = None, icon: Path | None = None,
desktop_entry: str | None = None, desktop_entry: str | None = None,
) -> None: ) -> None:
if not which("notify-send"):
raise FileNotFoundError("notify-send is not installed.")
args = ["notify-send", "-a", application_name, "-u", urgency] args = ["notify-send", "-a", application_name, "-u", urgency]
if category: if category:
args.append("-c") args.append("-c")
@ -21,6 +23,8 @@ def notify(
args.append("-i") args.append("-i")
args.append(str(icon)) args.append(str(icon))
if desktop_entry: if desktop_entry:
if not does_desktop_entry_exist(desktop_entry=desktop_entry):
raise FileNotFoundError("Desktop entry does not exist.")
args.append("-h") args.append("-h")
args.append(f"string:desktop-entry:{desktop_entry}") args.append(f"string:desktop-entry:{desktop_entry}")
args.append(title) args.append(title)
@ -29,12 +33,15 @@ def notify(
subprocess.run(args) subprocess.run(args)
def read_secret_file(secret: str) -> str: def read_secret_file(secret: str, home: bool = False) -> str:
path = f"/var/secrets/{secret}" if home:
path = os.path.expanduser(f"~/.secrets/{secret}")
else:
path = f"/var/secrets/{secret}"
if not os.path.exists(path): if not os.path.exists(path):
raise FileNotFoundError(f"Secret file {path} does not exist or cannot be read.") raise FileNotFoundError(f"Secret file {path} does not exist or cannot be read.")
with open(f"/var/secrets/{secret}", "r") as f: with open(file=path, mode="r") as secret_file:
secret = f.read().strip() secret = secret_file.read().strip()
if not secret: if not secret:
raise ValueError(f"Secret file {path} is empty.") raise ValueError(f"Secret file {path} is empty.")
return secret return secret

View file

@ -60,7 +60,7 @@ def spectacle_screenshot(
try: try:
opts = [ opts = [
"zipline.py", "/etc/nixos/scripts/py/zipline.py",
file_path, file_path,
"--application-name", "--application-name",
"Spectacle", "Spectacle",

View file

@ -5,12 +5,10 @@ import argparse
import mimetypes import mimetypes
import os import os
from pathlib import Path from pathlib import Path
from shutil import which
from typing import Any from typing import Any
import requests # type: ignore import requests # type: ignore
from common.common import ( # type: ignore from common.common import ( # type: ignore
does_desktop_entry_exist,
notify, notify,
read_secret_file, read_secret_file,
) )
@ -23,19 +21,11 @@ def zipline(
application_name: str | None = None, application_name: str | None = None,
desktop_entry: str | None = None, desktop_entry: str | None = None,
) -> Any: ) -> Any:
token = read_secret_file("zipline") token = read_secret_file(secret="zipline", home=True)
if not os.path.isfile(file_path): if not os.path.isfile(file_path):
raise FileNotFoundError(f"File at {file_path} does not exist.") raise FileNotFoundError(f"File at {file_path} does not exist.")
use_notify_send = False
if application_name and desktop_entry:
if not does_desktop_entry_exist(desktop_entry=desktop_entry):
raise FileNotFoundError("Desktop entry does not exist.")
if not which("notify-send"):
raise FileNotFoundError("notify-send is not installed.")
use_notify_send = True
content_type = mimetypes.guess_type(file_path)[0] or "application/octet-stream" content_type = mimetypes.guess_type(file_path)[0] or "application/octet-stream"
try: try:
@ -55,7 +45,7 @@ def zipline(
copy(text=link) copy(text=link)
print(f"Link copied to clipboard: {link}") print(f"Link copied to clipboard: {link}")
if use_notify_send: if application_name and desktop_entry:
notify( notify(
application_name=application_name, application_name=application_name,
title="Upload Successful", title="Upload Successful",
@ -71,7 +61,7 @@ def zipline(
raise Exception(error_message) raise Exception(error_message)
except BaseException as e: except BaseException as e:
if use_notify_send: if application_name and desktop_entry:
notify( notify(
application_name=application_name, application_name=application_name,
title="Upload Failed", title="Upload Failed",

30
secrets/secrets.yaml Normal file
View file

@ -0,0 +1,30 @@
zipline: ENC[AES256_GCM,data:YQMdw1cJy9wFnJsX6fPWBXK0rPEnuJJwJysVh0vggcnySFjl5Dmolaqxhw==,iv:RKB+rNz76ZxqzmyATLcpHmaap1f6aWWm7smBTieMZ8M=,tag:GN967VhwqZwMA6uzshKBmQ==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age1q9f9zhkfjn2c3a8qtmfqh0rtls3542jukqpt7t93jca6hc947f3sm9ujhx
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBabXF4b2dBelo1THZHOG9u
UWxNaEMwTlErTU1ZYmhISnVubXBBY3JaZkFjClFpblNFeWtwN0hrOGVTdThaQzFE
R0lqS2lYcDBWck9Pb3dmTk1yRS9rNFUKLS0tIFRUaUx2QlRmb0NFRlV3RndYbUpn
RHlURzRGL0grRFRDVGlVMTNpMlU5eUkKWzoCPqrcJBqM0H+Ap0v5Jsf69y7xV9gA
d9ubH84yMrOjLPs/K2FmLm+0zr7/epGE02nceiGzgWDHczQGp7qhzA==
-----END AGE ENCRYPTED FILE-----
- recipient: age184ude6fyak8z4nnndq4nzcpe2d89zxf3r4paty7j2tenkwa6zgtqrz60lq
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1ZVdIRkMzMjErTjRpSC82
Rk04SmxVM3FiUys1VVViUmhQTkJKTW1MSjNNClg2KzA1M3lXTjVqVm52aXlxNDE1
cHl0VDB1MCtpdnVabStZRkoxQjlHQmcKLS0tIGs5WTZMUFNKcU5qQXVqUjFNTGVF
b1JvNi9YODZPN1FObWpOVHN3aU85NFEK1dN5pV8g3nG3D2l482z1JCRzmJ/9m495
YEobjXbEqQDhvA47ueWojoMjvQ3CgrPyiL6v+DLj7VfI5cyuo+89IQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2024-12-03T01:29:28Z"
mac: ENC[AES256_GCM,data:NzE6V3kb9hiA9WAs7GFK5GqFoOUP5U/EOskWq0qdCo6GMewkK8TqrY+lFgjkEhY39PobgVTICBT8MGhY9eiEINYdBl7DuQGb3cR/puV+iCPEgUemzVcmcGkd24ktzUO2DsWet1EFC84oOu50XzYfR9VqW3z7+7UbpzWuOxIdvAA=,iv:pxNCxKPevqg8QxsIfL6+2pEB5cUmKhmLhmdiO+nB/Ac=,tag:uFA84z0XjzNgp3NLBu8AfQ==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.9.1