Compare changes

Choose any two refs to compare.

Changed files
+169 -103
.git-crypt
docs
global
systems
koumakan
networking
security
services
static-sites
utils
-4
.git-crypt/.gitattributes
···
-
# Do not edit this file. To specify the files to encrypt, create your own
-
# .gitattributes file in the directory where your files are.
-
* !filter !diff
-
*.gpg binary
.git-crypt/keys/default/0/7595B36DF6C2E95E10E528662932BA0FA3DDD7D6.gpg

This is a binary file and will not be displayed.

-1
.gitattributes
···
-
*.cry filter=git-crypt diff=git-crypt
+42 -2
docs/utils.md
···
# utility functions
## `_utils.mkVhost`
-
make virtual host with sensible defaults
+
`attrset -> attrset`
+
make a virtual host with sensible defaults
pass in a set to override the defaults.
+
### Example
+
```nix
+
services.nginx.virtualHosts."balls.example" = _utils.mkVhost {};
+
```
+
## `_utils.mkSimpleProxy`
+
`attrset -> attrset`
+
make a simple reverse proxy
takes a set:
···
port,
protocol ? "http",
location ? "/",
-
websockets ? false
+
websockets ? false,
+
extraConfig ? {}
}
```
+
+
It is recommended to override/add attributes with `extraConfig` to
+
preserve defaults.
+
+
Items in `extraConfig` are merged verbatim to the base attrset with defaults.
+
They are overridden based on their order.
+
+
## `_utils.genSecrets`
+
`namespace[str] -> files[list[str]] -> value[attrset] -> attrset`
+
a
+
generate an attrset to be passed into sops.secrets.
+
+
### Example
+
```nix
+
{ _utils, ... }:
+
let
+
secrets = [
+
"secure_secret"
+
# this is a directory structure, so secrets will be stored as a file in /run/secrets/service/test/secret.
+
"service/test/secret"
+
];
+
in {
+
sops.secrets = _utils.genSecrets "" secrets {}; # it's recommended to use a namespace, but having none is still fine.
+
# -> sops.secrets."secure_secret" = {};
+
# sops.secrets."service/test/secret" = {};
+
sops.secrets = _utils.genSecrets "balls" ["balls_secret"] {owner = "balls"};
+
# -> sops.secrets."balls/balls_secret" = {owner = "balls";};
+
}
+
```
+
+
See https://github.com/soopyc/nix-on-koumakan/blob/b7983776143c15c91df69ef34ba4264a22047ec6/systems/koumakan/services/fedivese/akkoma.nix#L8-L34 for a more extensive example
+1 -9
global/core.nix
···
{pkgs, ...}: {
imports = [
./upgrade-diff.nix
+
./system
];
# Set default i18n configuration
i18n.defaultLocale = "en_US.UTF-8";
···
font = "Lat2-Terminus16";
keyMap = "us";
};
-
-
hardware.enableRedistributableFirmware = true;
-
services.fwupd.enable = true;
-
-
# # Enable crash dumps globally
-
# boot.crashDump = {
-
# enable = true;
-
# reservedMemory = "128M";
-
# };
time.timeZone = "Asia/Hong_Kong";
+1
global/programs/nix.nix
···
experimental-features = [
"nix-command"
"flakes"
+
"repl-flake"
];
substituters = [
+5
global/system/default.nix
···
+
{...}: {
+
imports = [
+
./firmware.nix
+
];
+
}
+4
global/system/firmware.nix
···
+
{...}: {
+
hardware.enableRedistributableFirmware = true;
+
services.fwupd.enable = true;
+
}
+29 -15
global/utils.nix
···
# see /docs/utils.md for a usage guide
{
inputs,
-
# system,
+
system,
...
}: let
lib = inputs.nixpkgs.lib;
in rec {
-
mkVhost = {...} @ opts:
-
{
-
# ideally mkOverride/mkDefault would be used, but i have 0 idea how it works.
-
forceSSL = true;
-
useACMEHost = "global.c.soopy.moe";
-
kTLS = true;
-
}
-
// opts;
+
mkVhost = opts:
+
lib.mkMerge [
+
{
+
forceSSL = lib.mkDefault true;
+
useACMEHost = lib.mkDefault "global.c.soopy.moe";
+
kTLS = lib.mkDefault true;
+
+
locations."/_cgi/error/" = {
+
alias = "${inputs.mystia.packages.${system}.staticly}/nginx_error_pages/";
+
};
+
extraConfig = ''
+
error_page 503 /_cgi/error/503.html;
+
error_page 502 /_cgi/error/502.html;
+
error_page 404 /_cgi/error/404.html;
+
'';
+
}
+
opts
+
];
mkSimpleProxy = {
port,
protocol ? "http",
location ? "/",
websockets ? false,
+
extraConfig ? {},
}:
-
mkVhost {
-
locations."${location}" = {
-
proxyPass = "${protocol}://localhost:${toString port}";
-
proxyWebsockets = websockets;
-
};
-
};
+
mkVhost (lib.mkMerge [
+
extraConfig
+
{
+
locations."${location}" = {
+
proxyPass = "${protocol}://localhost:${toString port}";
+
proxyWebsockets = websockets;
+
};
+
}
+
]);
genSecrets = namespace: files: value:
lib.genAttrs (
+1 -1
systems/koumakan/networking/interface.nix
···
{...}: {
-
networking.networkmanager.ethernet.macAddress = builtins.readFile ./nma.cry;
+
networking.networkmanager.ethernet.macAddress = "stable";
}
systems/koumakan/networking/nma.cry

This is a binary file and will not be displayed.

+1 -1
systems/koumakan/security/pam.nix
···
{...}: {
security.pam.yubico = {
enable = true;
-
id = builtins.readFile ./ykid.cry;
+
id = "91582";
mode = "client";
control = "requisite";
};
systems/koumakan/security/ykid.cry

This is a binary file and will not be displayed.

+4 -3
systems/koumakan/services/attic.nix
···
};
};
-
services.nginx.virtualHosts."nonbunary.soopy.moe" =
-
_utils.mkSimpleProxy {port = 38191;}
-
// {
+
services.nginx.virtualHosts."nonbunary.soopy.moe" = _utils.mkSimpleProxy {
+
port = 38191;
+
extraConfig = {
extraConfig = ''
client_max_body_size 1G;
proxy_read_timeout 3h;
···
proxy_send_timeout 3h;
'';
};
+
};
}
+3 -3
systems/koumakan/services/static-sites/keine.nix
···
-
{...}: {
-
services.nginx.virtualHosts."keine.soopy.moe" = {
-
useACMEHost = "global.c.soopy.moe";
+
{_utils, ...}: {
+
services.nginx.virtualHosts."keine.soopy.moe" = _utils.mkVhost {
+
forceSSL = false;
addSSL = true; # Don't force SSL on a mirror (implications TBD)
root = "/srv/www/keine";
+78 -64
utils/nitter-guest-account.py
···
# thank you!
import sys
import json
+
import time
+
import random
import typing
import traceback
from base64 import b64encode
···
success(f'flow token => {flow_token}')
info('Fetching final account object...')
-
tasks = send_req('post', TASKS_ENDPOINT, headers=request_headers, json=get_tasks_body(flow_token)).json()
-
try:
+
backoff_time = 0
+
exception = None
+
for attempt in range(0, 6):
+
debug(f"Attempt #{attempt + 1}")
+
tasks = send_req('post', TASKS_ENDPOINT, headers=request_headers, json=get_tasks_body(flow_token)).json()
try:
-
open_account_task = next(filter(lambda i: i.get('subtask_id') == "OpenAccount", tasks['subtasks']))
-
account = open_account_task['open_account']
-
except StopIteration:
-
debug("resulting tasks =>", format_json(tasks), override=True)
-
error("Unable to acquire guest account credentials as it isn't present in the API response.")
-
error("This might be because of a wide variety of reasons, but it most likely is due to your IP being rate-limited.")
-
error("Try again with a new IP address or in 24 hours after this attempt.")
-
return 1
+
try:
+
open_account_task = next(filter(lambda i: i.get('subtask_id') == "OpenAccount", tasks['subtasks']))
+
account = open_account_task['open_account']
+
except StopIteration as e:
+
backoff_time += random.randint(5000,10000) / 1000
+
exception = e
+
warn(f"attempt #{attempt + 1} failed to acquire token, retrying in {backoff_time}s.")
+
time.sleep(backoff_time)
+
continue
-
if args.outfile == "-":
-
debug("outfile is `-`, printing to stdout")
-
print(format_json(account))
-
return 0
+
info(f"Attempt #{attempt + 1} succeeded")
+
if args.outfile == "-":
+
debug("outfile is `-`, printing to stdout")
+
print(format_json(account))
+
return 0
-
# Sanity checks
-
try:
-
debug(f"attempting to read file: {args.outfile}")
-
with open(args.outfile) as f:
-
old_data = json.load(f)
-
except FileNotFoundError:
-
# that's okay, we might be able to create it later.
-
old_data = []
-
except PermissionError:
-
# that's not okay. we will need to access the file later anyways.
-
error("unable to read file due to a permission error.")
-
error("please make sure this script has read and write access to the file.")
-
print(format_json(account))
-
return 1
-
except json.JSONDecodeError:
-
error("could not parse the provided JSON file.")
-
if not prompt_bool("Do you want to overwrite the file?", default=None):
-
warn("Not overwriting file, printing to stdout instead.")
+
# Sanity checks
+
try:
+
debug(f"attempting to read file: {args.outfile}")
+
with open(args.outfile) as f:
+
old_data = json.load(f)
+
except FileNotFoundError:
+
# that's okay, we might be able to create it later.
+
old_data = []
+
except PermissionError:
+
# that's not okay. we will need to access the file later anyways.
+
error("unable to read file due to a permission error.")
+
error("please make sure this script has read and write access to the file.")
print(format_json(account))
return 1
-
debug('assuming old data is an empty array because we are overwriting')
-
old_data = []
-
if type(old_data) != list:
-
error("top-level object of the existing JSON file is not a list.")
-
error("due to the implementation, the file must be overwritten.")
-
if not (prompt_bool("Do you want to overwrite?", default=None)):
-
warn("Not overwriting existing data, printing to stdout instead.")
+
except json.JSONDecodeError:
+
error("could not parse the provided JSON file.")
+
if not prompt_bool("Do you want to overwrite the file?", default=None):
+
warn("Not overwriting file, printing to stdout instead.")
+
print(format_json(account))
+
return 1
+
debug('assuming old data is an empty array because we are overwriting')
+
old_data = []
+
if type(old_data) != list:
+
error("top-level object of the existing JSON file is not a list.")
+
error("due to the implementation, the file must be overwritten.")
+
if not (prompt_bool("Do you want to overwrite?", default=None)):
+
warn("Not overwriting existing data, printing to stdout instead.")
+
print(format_json(account))
+
return 1
+
debug("assuming old data is an empty array because we are overwriting")
+
old_data = []
+
+
old_data.append(account)
+
+
try:
+
debug("attempting to write file")
+
with open(args.outfile, 'w+') as f:
+
f.write(format_json(old_data))
+
success(f"successfully written to file {args.outfile}")
+
return 0
+
except PermissionError:
+
error("unable to write to file due to permission error.")
+
error("please make sure this script has write access to the file.")
print(format_json(account))
return 1
-
debug("assuming old data is an empty array because we are overwriting")
-
old_data = []
-
-
old_data.append(account)
+
except Exception as e:
+
error("Unable to write to file due to an uncaught error:", e)
+
tb = ''.join(traceback.TracebackException.from_exception(e).format())
+
debug("exception stacktrace\n" + tb)
+
print(format_json(account))
-
try:
-
debug("attempting to write file")
-
with open(args.outfile, 'w+') as f:
-
f.write(format_json(old_data))
-
success(f"successfully written to file {args.outfile}")
-
return 0
-
except PermissionError:
-
error("unable to write to file due to permission error.")
-
error("please make sure this script has write access to the file.")
-
print(format_json(account))
-
return 1
-
except Exception as e:
-
error("Unable to write to file due to an uncaught error:", e)
-
tb = ''.join(traceback.TracebackException.from_exception(e).format())
-
debug("exception stacktrace\n" + tb)
-
print(format_json(account))
+
except Exception:
+
debug("resulting tasks =>", format_json(tasks), override=True)
+
error("an unhandled error occurred. the tasks object is printed to avoid losing otherwise successful data.")
+
error("please file a bug report and attach the traceback below.")
+
raise
-
except Exception:
-
debug("resulting tasks =>", format_json(tasks), override=True)
-
error("an unhandled error occurred. the tasks object is printed to avoid losing otherwise successful data.")
-
error("please file a bug report and attach the traceback below.")
-
raise
+
if exception != None:
+
debug("resulting tasks =>", format_json(tasks))
+
error("Unable to acquire guest account credentials with 5 attempts as it isn't present in any of the API responses.")
+
error("This might be because of a wide variety of reasons, but it most likely is due to your IP being rate-limited.")
+
error("Try again with a new IP address or in 24 hours after this attempt.")
+
return 1
return 0