Compare changes

Choose any two refs to compare.

-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
+29 -3
creds/sops/koumakan.yaml
···
comment_unencrypted: See https://github.com/Mic92/sops-nix/issues/120 for synapse.yaml quirks
-
synapse.yaml: ENC[AES256_GCM,data:xc8HBDVconhPJHK/YKVSl62pCtnyca3LXcH/pidJkoTKBU5OCkbnYDjoY0O89SJUVTDVgdAw1ANxSWAT+fEjtKzJEZkFdO5EvuWUXAdjMVNrTDeYhoull21hPtBjXMV6jLWH+VjL5FfQiHhh7FkVXJb9B7GpUKTk0hR0vZ8JlfGizVWBnOSc22wl2nSVRXOu0zWST3wPPAqSIHFxBJQrFk/6LvCQ0xT7zw2dCQjK+sNK9OsPCg84AM/aWZrXPodYLAlFhma1TAIE79XRLJ6K+FIKEnCjI7gsw9NPttZESodOyeFCyZcLCY9Fo6/n3Cp/t+yqU6ZqmflM87FFx0HrUUUbQ4myK+BI1DOVf/4X8DprRVasFQkOkIFhL1aAkJorzmcOqMqjzB1kb62Agnrtnd28fQ83u1KXZPtlsvTBankrrPgUHyRUt7jyyBp2qWr9y36utAedcuv47oqGFrVqDZ+eux6Vd6ozfuma3zLla9Hah96ySR8O3gk/7We5/sN9ZlvXY0qysaNLxAxvlXeegSwDm8dYObWkyf+jswuBG8xReIOGkT6nt4PLobiacqwm2lKtzZtXhzLxiQGpq+hvTkLpfz3MxrcHcskPwOzjM/7CVXo3nqEPRSVyHnn5g+eJ9wnXEO50GMuIi7wNjpQPyNH5sljHU3M5SahDeW0G70yP3WBViG53i6+WrD9PuSz5GciXO3klIZhO5ljPaNcT19c/z8UcUdPDAuTpOl4UNcDqoVwl,iv:OAlQ+nweSZxW+Ybq2fKPxawikND/e3KcGgZGKMuaCKo=,tag:imghdy3fRkB+l5SpXC4/qg==,type:str]
+
synapse.yaml: ENC[AES256_GCM,data:eQsUW4mwbSPzsoi8HIEngfU5x/PEaqPxvx1AXXJJj7WDX9RsPtQOqQJjuTAEPtz32Hm1nob+VVYuzAXXfCO8Cd0CIHG+xlV4cWNc4JB8wj1YgtMJu9J3yC7iv3HMULT0TL8MxmwWYKqSPKIVarLVN5gQl1G1SAfxEK3ET6qtmkmCD8y6ZVM/84fTR5lxpLe82CaplbUHXEHKk51XwGYLSp5IZsPwtMMFhYuaoqCsEVr65IiSozbD4LKddYNQKUlYmkSfThQsoPpe9ixE7+HS11S+LklyP1q3usWTfOPsQS8k5NJdcGkrCH2A4ddjWkDBAzdav3qauYoQEvbMd8BpoDqD/67lKuvoyXqDoPf5sLrQcxcUl8ObTI1I8zWhabGa9uTuiaTd3wYdMhvMvs/KIfPXNDqcBuIY9sR6LQmY2Zb9+FtYiQVnrr0XmzjB59oN023y1xC/xqkgRyXysndhnj4P1Q+PYbOMQi7hwAjM5xMk/mLkpLn41Ju4u0MqDwHSxA/NXbsEBp/u3Tim2jsrMZqjRZwQHt1n/gtMjm2cMTminM99c/fV2fcvAmcNB9u1KWgEte1JjQSRftg7pkaznYlfrQLyCQYghVu3kgeAJCaSPMewHVWOF/onTauNHYFMlJ8VDUuYOnt2LNJUk1Wap5dGfKm7661mTeYB8aw0h3111YEViuGkSp+oM5A8X6CBI6YsYYOWq5Qd+A3fFJVpcf9hhhdx0JsRmR3EZOupIVx5Y4ruuA6ipj/xMDRNfTZjqukpn3Y1s5xTjUYEo3moLdgd1PYxAVIY/KjPn31pmRThGG1NZpYHZ1yjmptVydtqf/tTd32i3e0WjIjUEZD1NE7Z5AiWJ+XAEaE3S804C6QxN1jHZvqIokA9C4HO+R/es1uDyf2KDvxf1It0hMLg7CAnq1AQcbLCDqXXZaqibSRNfv09lO4jZ3vBe3owvd1wGbK513nqC6m2gh8YDYiHdw7UlXMbMP9+,iv:fvaZQ66VKU+uzvn5AwTIFgzz+F2kJ8/QR2AfmynRfGU=,tag:8c/cAMZ6c7h3J+shh7l7tw==,type:str]
+
matrix-signing-key: ENC[AES256_GCM,data:u6miE2oM3TUXaQ7wc776SwSMaOAxJOVlpl2kBW+AjI/aDH5vcGBp0L0uTpZbVfOtIe+RDNEv5E/mKA==,iv:abvwkrNe324QCbWLwiPY0UwqezS0wbyk2Fvi0vs3SI0=,tag:ZmpDB9LHbezQrxuwHNgpRg==,type:str]
+
akkoma:
+
dist:
+
cookie: ENC[AES256_GCM,data:5jpsa4KsOAoCMGAt9laK9ioVTJfuT9+viKva8wDWRnAimVY6jDoNr4+hxVty6yQAAfSJYA7ddTxaSCEjnJtneg==,iv:V8+MpX/IEc57zEfhNGX5f+eMyipraaXDKPDNDOy0Ieo=,tag:+xCy18Ni8F5wYkO/4NbSzw==,type:str]
+
endpoint:
+
secret_base: ENC[AES256_GCM,data:l34Rj4iIQIykgzTLJolqWLQQz5pcfa0o5U/ZMKeNc2CBQedxiMXYrLSNOx6OuV38aqoOolccJEOSiVjfbTawtg==,iv:/x0ydo2gOPrhIZI7at877bzfFgMpraauozfLq95aHCk=,tag:RQI4aeLiAkAcWYlwLaTj5w==,type:str]
+
salt: ENC[AES256_GCM,data:CP4805tG05A=,iv:aSun7ABJdbDQrFcrGQMM9H1/7d5lJqeMwO08gUYrD2A=,tag:ikhxbijsqyBFJs02j2j/vw==,type:str]
+
live_view:
+
salt: ENC[AES256_GCM,data:4fKLclucoV0=,iv:ZvWKutuMTOm2X8w8a0fOTq+ldrXemayIUY2PUcurY80=,tag:qkIB1gPCI5HO0G0mLEsV+w==,type:str]
+
joken_default_signer: ENC[AES256_GCM,data:myCEFUkf8s1YNQAigjxygRYvbwkpsv7cqgs00fARe9nxSFl2wveWM5JcfOnoVPwVBVV2GaAjFe4oMWXkaTPtqg==,iv:Yk1f/fzzbruW64mvTTeiyTlbrOO/G47CKKfr9BLtQ5g=,tag:QLpM22ec+VWtkjx5U/mzCw==,type:str]
+
search:
+
meili:
+
host_unencrypted: https://megumin.soopy.moe/
+
key: ENC[AES256_GCM,data:00TLCUneHn7NcSK1joURfIzxNFWyOBf/0/fceOn4RMcMt59dZz9LOvbs3F8B0vcH7tf/eUi3SnhYJNyRdPklyw==,iv:t0kQUCmjhFw8Z2CTmYOPUNFvyiYfsXETU8GSxhRR5KE=,tag:CPjtd7jQzgHJDrsIjHlVFQ==,type:str]
+
vapid:
+
pub: ENC[AES256_GCM,data:HYMKjhVCW/7DsMfPPssEduuwWnFezH4OOq4hfAovI82RUPsfVEKhgvkI9INY8hArAb/AIfyyxZhVx+bd2QkPlnASz51L7MxPtkPfZNUKqafjlMmK0nwH,iv:154BP5EmBqnKyf9BND2laKV3caVxa34MCRzrsg6/dik=,tag:wHLYdI6oQXPUzbw8dSxgwg==,type:str]
+
key: ENC[AES256_GCM,data:t+da4NLEPZBMvq3MQkFEr+Fsj3XMGPMFKUWwbHDWNJAyuUZuiVcn3zX0kw==,iv:yQLu5CFl73GCojMBa2II6OhLrNNinsiVG1aPOAx+HtM=,tag:n0oelXaNFvilyee+MRSB8A==,type:str]
+
postgres:
+
hostname: ENC[AES256_GCM,data:rFEnhnn/Bw85,iv:GM2SH4Gkvt8tLG8AYIKxfHTZvB1sT+hgIoqkiViH6Es=,tag:yyGY9/nS9WFcJTGXlYpz6Q==,type:str]
+
database_unencrypted: akkoma
+
username: ENC[AES256_GCM,data:6skzOqv1,iv:OQ6zNmDn0uqKqNKEqOHWY6VBuT/4/CHog7b0Pf0TAPM=,tag:8HLmGykXg2V4t4RHzB8yaA==,type:str]
+
password: ENC[AES256_GCM,data:J3OewVKr2A3TlT7ZUTk7tQr4olFs7bKx47Lus4LGbwGAZfNEmyk9coFTeQ8L/EJ0hpLfPfD1OcGBc+p0ZWK/XQ==,iv:UFe/3H/AfTgSlJikHqE1IED3zINjDuOs5niXpGWXGYE=,tag:MvT0z3TMs1dehg4gp54MyQ==,type:str]
+
smtp:
+
username: ENC[AES256_GCM,data:N7XbQkngWcUGzn/SR4AXCQ==,iv:wBXWtRYawOkjumsvTPcKfvL95CCB+RbsEyJv0YUG3WA=,tag:vGQREe+Cv0ITTxszl21J2g==,type:str]
+
password: ENC[AES256_GCM,data:oU/aWkVmDU8WJhmwqOcXJ/EngiF7hvfUzPwpdjwkyh7Dw50dyG5AY7b2+hh6LIv9RZrN4yU+fXPAYr1W21OG/A==,iv:ds8Bg9JSJdNHUXh0FvD5a4pquyRnIXowcsJcVV1TyB4=,tag:JoYqas2RGSv8xyvJT9wHAQ==,type:str]
+
relay: ENC[AES256_GCM,data:F2NnRLSTO5kmbWy4fx0=,iv:omnyn+Xa/cjqK+9l5bI573aR2p7UsUvqGX5ZQGf3CD0=,tag:t4u/jLQ1nZchyxf3WrhW6w==,type:str]
sops:
kms: []
gcp_kms: []
···
cHJ5aWIrQ2Zrb1dhbC9yZ1lIMU1jbzgK4mx+S5bF6KBMe6+TrSZfaBcuWEg9cHyd
tbJty1zxS9pndA/u3qz5EJxDouiAODvyAR07yeegtEcbw1FlG6W/gA==
-----END AGE ENCRYPTED FILE-----
-
lastmodified: "2023-09-10T15:22:34Z"
-
mac: ENC[AES256_GCM,data:DDYjyGhk0Cl4V9SuiqQyTUXYJeIusHLACrEZKU8vVLcMw9R9xyTyWLECoKH/+CLUxbJiLf1q6p/GgnTHtJgf18b+FmuHq4H/GgZrqvnMFOGxV+o47lJ20OV14e1fWASF7qq+lVhrhvI3hzWe2QchHlop2TZ9kh2BnGtrAwP6txA=,iv:cA6Nz5yK3AdjNjavi/T+XJ91eO7igLOhXnc+Bm8MKUM=,tag:3Ap/2pOkF9vYnylrnR5qDg==,type:str]
+
lastmodified: "2023-09-14T17:13:38Z"
+
mac: ENC[AES256_GCM,data:Jyx0f+w9fJ+B1lz4jVVkcKxd1xUh3FzxDhk+KaxJLVh0BG/1d8Nx0/cOnxZV1FfJkn5Z2wYiLzBPSvJKe8MjlExOSH1mIAnuXcSP6dvXp21bgX17CXM6OP91Ny6IvwSZriqs6EIpWOkZNdxsEnySwtECoQfgs09ZnA4qmbtb01U=,iv:XHnY20d0WsnaECF1/68eu2/xcGLGeGnzba+/kBxDcc0=,tag:alo+8B2fVHon0lHGsQSUyQ==,type:str]
pgp:
- created_at: "2023-09-08T14:11:19Z"
enc: |-
+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
+3 -3
flake.lock
···
]
},
"locked": {
-
"lastModified": 1693767481,
-
"narHash": "sha256-8B8f1RG5n9a2Tp0A3zzgCEVvtWjIDp+yNZfKxe73G10=",
+
"lastModified": 1694357290,
+
"narHash": "sha256-Mai5saiiuBWlQzTreLtIeUJQJN9zMH/UBIKJPOqnasM=",
"owner": "soopyc",
"repo": "mystia",
-
"rev": "baccc6213b5d6386e8b31ab39a9fde556ef9f082",
+
"rev": "cfe783698ccd18cb27fb2e422917f14b82f7afc4",
"type": "github"
},
"original": {
+1 -8
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;
-
-
# # 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;
+
}
+38 -17
global/utils.nix
···
# see /docs/utils.md for a usage guide
{
-
# inputs,
-
# system,
+
inputs,
+
system,
...
-
}: 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;
+
}: let
+
lib = inputs.nixpkgs.lib;
+
in rec {
+
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 (
+
map (x: namespace + lib.optionalString (lib.stringLength namespace != 0) "/" + x) files
+
) (_: value);
}
+8 -4
justfile
···
# friendship ended with Makefile
# I LOVE justFILE!!!!!!
-
default: build
+
# build the current configuration
+
build:
+
nixos-rebuild build --flake .#
+
# build and test the configuration, but don't switch
test:
nixos-rebuild test --flake .#
-
build:
-
nixos-rebuild build --flake .#
-
+
# switch to the current configuration
switch:
nixos-rebuild switch --flake .#
+
# run utility programs
utils recipe="list":
@echo "Running utils/{{recipe}}"
@cd utils && just {{recipe}}
+
# update an input in the flake lockfile
update-input input:
nix flake lock --update-input {{input}}
+
# build the flake on a non-nixos platform
ebuild system:
nix build -j8 .#nixosConfigurations."{{system}}".config.system.build.toplevel
+1
systems/koumakan/certificates/default.nix
···
imports = [
./global.nix
./postgresql.nix
+
./fediverse.nix
];
security.acme = {
+11
systems/koumakan/certificates/fediverse.nix
···
+
{...}: {
+
# Certificate for fedi services
+
security.acme.certs."fedi.c.soopy.moe" = {
+
group = "nginx";
+
extraDomainNames = [
+
"a.soopy.moe"
+
"m.soopy.moe"
+
"pixie.soopy.moe"
+
];
+
};
+
}
-1
systems/koumakan/configuration.nix
···
sops.age.sshKeyPaths = ["/etc/ssh/ssh_host_ed25519_key"];
sops.defaultSopsFile = ../../creds/sops/koumakan.yaml;
-
sops.secrets."synapse.yaml" = {}; # test only
# Just don't change this :p
system.stateVersion = "23.05"; # Did you read the comment?
+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;
'';
};
+
};
}
+6
systems/koumakan/services/databases/default.nix
···
+
{...}: {
+
imports = [
+
./postgresql.nix
+
./redis.nix
+
];
+
}
+40
systems/koumakan/services/databases/postgresql.nix
···
+
{pkgs, ...}: {
+
services.postgresql = {
+
enable = true;
+
+
package = pkgs.postgresql_15;
+
dataDir = "/var/lib/postgresql/15";
+
logLinePrefix = "%m [%p] %h ";
+
+
authentication = ''
+
# unix socket connection
+
local all all peer
+
# local ipv4/6 tcp connection
+
host all all 127.0.0.1/32 scram-sha-256
+
host all all ::1/128 scram-sha-256
+
# world (encrypted) tcp traffic
+
hostssl all all all scram-sha-256
+
'';
+
+
settings = let
+
credsDir = "/run/credentials/postgresql.service";
+
in {
+
listen_addresses = pkgs.lib.mkForce "*";
+
max_connections = 200;
+
password_encryption = "scram-sha-256";
+
+
ssl = "on";
+
ssl_cert_file = "${credsDir}/cert.pem";
+
ssl_key_file = "${credsDir}/key.pem";
+
+
log_hostname = true;
+
datestyle = "iso, dmy";
+
log_timezone = "Asia/Hong_Kong";
+
timezone = "Asia/Hong_Kong";
+
default_text_search_config = "pc_catalog.english";
+
+
max_wal_size = "2GB";
+
min_wal_size = "80MB";
+
};
+
};
+
}
+5
systems/koumakan/services/databases/redis.nix
···
+
{...}: {
+
services.redis.servers."" = {
+
enable = true;
+
};
+
}
+2 -3
systems/koumakan/services/default.nix
···
imports = [
./nginx.nix
-
# databases
-
./postgresql.nix
-
./redis.nix
+
./databases
./attic.nix
# fediverse
./matrix
+
./fediverse
./proxies
./static-sites
+150
systems/koumakan/services/fediverse/akkoma.nix
···
+
{
+
_utils,
+
pkgs,
+
config,
+
lib,
+
...
+
}: let
+
mkRaw = (pkgs.formats.elixirConf {}).lib.mkRaw;
+
# I don't know what i did but i made this abomination
+
mkSecret = file:
+
if !lib.elem file secrets
+
then throw "Provided secret file ${file} is not in the list of defined secrets."
+
else {_secret = "/run/secrets/akkoma/${file}";};
+
secrets = [
+
"joken_default_signer" # can't think of any better name spacing
+
"dist/cookie"
+
"search/meili/host_unencrypted"
+
"search/meili/key"
+
"endpoint/secret_base"
+
"endpoint/salt"
+
"endpoint/live_view/salt"
+
"vapid/pub"
+
"vapid/key"
+
"postgres/hostname"
+
"postgres/database_unencrypted"
+
"postgres/username"
+
"postgres/password"
+
"smtp/username"
+
"smtp/password"
+
"smtp/relay"
+
];
+
in {
+
# secrets definition
+
sops.secrets = _utils.genSecrets "akkoma" secrets {};
+
+
services.akkoma = {
+
enable = true;
+
initSecrets = false;
+
initDb.enable = false;
+
# TODO: figure out how to add swagger ui
+
# frontends = {
+
# swagger
+
# };
+
+
# TODO: Issue #5
+
dist.cookie = mkSecret "dist/cookie";
+
config = {
+
":joken".":default_signer" = mkSecret "joken_default_signer";
+
+
":pleroma" = {
+
":http_security" = {
+
sts = true;
+
};
+
+
":configurable_from_database" = true;
+
":instance" = {
+
name = "CassieAkko";
+
description = "You should not see this here...";
+
email = "me@soopy.moe";
+
notify_email = "noreply@a.soopy.moe";
+
limit = 5000;
+
registrations_open = true;
+
};
+
+
# TODO: add proper proxy support
+
# also refer to https://meta.akkoma.dev/t/another-vector-for-the-injection-vulnerability-found/483
+
":media_proxy" = {
+
enabled = true;
+
redirect_on_failure = true;
+
};
+
+
"Pleroma.Repo" = {
+
adapter = mkRaw "Ecto.Adapters.Postgres";
+
database = mkSecret "postgres/database_unencrypted";
+
hostname = mkSecret "postgres/hostname";
+
username = mkSecret "postgres/username";
+
password = mkSecret "postgres/password";
+
};
+
+
"Pleroma.Upload" = {
+
filters = [
+
(mkRaw "Pleroma.Upload.Filter.Exiftool")
+
(mkRaw "Pleroma.Upload.Filter.Dedupe")
+
];
+
};
+
+
"Pleroma.Web.Endpoint" = {
+
# We don't need to specify http ip/ports here because we use unix sockets
+
# ok we do because it's broken
+
http = {
+
ip = "127.0.0.1";
+
port = 35378;
+
};
+
url = {
+
host = "a.soopy.moe";
+
scheme = "https";
+
port = 443;
+
};
+
secure_cookie_flag = true;
+
+
secret_key_base = mkSecret "endpoint/secret_base";
+
signing_salt = mkSecret "endpoint/salt";
+
live_view = {
+
signing_salt = mkSecret "endpoint/live_view/salt";
+
};
+
};
+
+
"Pleroma.Emails.Mailer" = {
+
adapter = mkRaw "Swoosh.Adapters.SMTP";
+
relay = mkSecret "smtp/relay";
+
username = mkSecret "smtp/username";
+
password = mkSecret "smtp/password";
+
};
+
+
"Pleroma.Search.Meilisearch" = {
+
url = mkSecret "search/meili/host_unencrypted";
+
private_key = mkSecret "search/meili/key";
+
initial_indexing_chunk_size = 100000;
+
};
+
};
+
+
":web_push_encryption".":vapid_details" = {
+
subject = "mailto:me@soopy.moe";
+
public_key = mkSecret "vapid/pub";
+
private_key = mkSecret "vapid/key";
+
};
+
};
+
+
nginx = _utils.mkVhost {
+
useACMEHost = "fedi.c.soopy.moe";
+
extraConfig = ''
+
client_max_body_size 100M;
+
'';
+
};
+
+
extraStatic = {
+
"static/terms-of-service.html" = pkgs.writeText "terms-of-service.html" ''
+
<h1>Terms of Service</h1><p>Please refer to this ToS:
+
<a href="https://m.soopy.moe/@admin/pages/tos" rel="noopener noreferrer nofollow">
+
https://m.soopy.moe/@admin/pages/tos</a></p>
+
'';
+
# refer to https://github.com/NixOS/nixpkgs/blob/nixos-unstable/pkgs/servers/akkoma/emoji/blobs_gg.nix#L29
+
# "emoji/Cat_girls_Emoji" = ...
+
};
+
};
+
+
systemd.services.akkoma-config = {
+
serviceConfig.SupplementaryGroups = [config.users.groups.keys.name];
+
};
+
}
+5
systems/koumakan/services/fediverse/default.nix
···
+
{...}: {
+
imports = [
+
./akkoma.nix
+
];
+
}
+12 -2
systems/koumakan/services/matrix/synapse.nix
···
owner = config.users.users.matrix-synapse.name;
};
+
sops.secrets.matrix-signing-key = {
+
mode = "0400";
+
owner = config.users.users.matrix-synapse.name;
+
};
+
users.users.matrix-synapse.extraGroups = [config.users.groups.keys.name];
services.matrix-synapse = {
···
# TODO: Setup opentracing
url_preview_enabled = true;
enable_registration = false;
-
session_lifetime = "infinite";
+
session_lifetime = "99y";
max_upload_size = "100M";
+
signing_key_path = "/run/secrets/matrix-signing-key";
server_notices = {
system_mxid_localpart = "server";
···
'';
};
+
locations."= /.well-known/matrix/server" = {
+
return = "200 '{\"m.server\": \"nue.soopy.moe:443\"}'";
+
};
+
locations."~ ^(/_matrix|/_synapse/client)" = {
-
proxyPass = "http://localhost:8080";
+
proxyPass = "http://localhost:8008";
extraConfig = ''
client_max_body_size 100M;
'';
-40
systems/koumakan/services/postgresql.nix
···
-
{pkgs, ...}: {
-
services.postgresql = {
-
enable = true;
-
-
package = pkgs.postgresql_15;
-
dataDir = "/var/lib/postgresql/15";
-
logLinePrefix = "%m [%p] %h ";
-
-
authentication = ''
-
# unix socket connection
-
local all all peer
-
# local ipv4/6 tcp connection
-
host all all 127.0.0.1/32 scram-sha-256
-
host all all ::1/128 scram-sha-256
-
# world (encrypted) tcp traffic
-
hostssl all all all scram-sha-256
-
'';
-
-
settings = let
-
credsDir = "/run/credentials/postgresql.service";
-
in {
-
listen_addresses = pkgs.lib.mkForce "*";
-
max_connections = 200;
-
password_encryption = "scram-sha-256";
-
-
ssl = "on";
-
ssl_cert_file = "${credsDir}/cert.pem";
-
ssl_key_file = "${credsDir}/key.pem";
-
-
log_hostname = true;
-
datestyle = "iso, dmy";
-
log_timezone = "Asia/Hong_Kong";
-
timezone = "Asia/Hong_Kong";
-
default_text_search_config = "pc_catalog.english";
-
-
max_wal_size = "2GB";
-
min_wal_size = "80MB";
-
};
-
};
-
}
-5
systems/koumakan/services/redis.nix
···
-
{...}: {
-
services.redis.servers."" = {
-
enable = true;
-
};
-
}
+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";
+1
utils/justfile
···
list:
just -l
+
# grab a new nitter guest account and save it
nitter-token:
{{python_executable}} nitter-guest-account.py tokens.json
+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