Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
22ec729583
|
|||
|
c5579fbd9c
|
|||
|
c830cc1f96
|
|||
|
00e815c003
|
|||
|
3910f14eef
|
|||
|
82c76dc390
|
|||
| edf7aab2f0 | |||
|
760c5c8284
|
|||
|
3a5d5d27f4
|
|||
|
015bbc3d66
|
|||
|
dc374e8c04
|
|||
|
a708c04a9a
|
|||
| a2534a3dab | |||
| b78c41a5f7 | |||
| e1f2dc3161 | |||
|
f1c5f038ec
|
|||
|
ae0d4f5958
|
|||
|
ded0374c57
|
|||
|
ab0f9a2ff2
|
|||
| fb9526fec2 | |||
|
0692e680b8
|
|||
|
3a940586d5
|
|||
| 7e7097f457 | |||
| 22b3a95bf8 | |||
| de4b8833bd | |||
| ac9b80573f | |||
| 66a15a5d19 | |||
| 7e331f5e1a | |||
| a8374e231f | |||
| 27b861d5a5 | |||
| 6a29ac005c | |||
|
0b9f76dcb4
|
|||
| cc52b0e6cb | |||
|
d4d560c30f
|
+2
-1
@@ -1,3 +1,4 @@
|
||||
# Nix
|
||||
/result
|
||||
/*.qcow2
|
||||
/.direnv
|
||||
/result
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
age-encryption.org/v1
|
||||
-> ssh-ed25519 Wl2fDA sLlStq5Hzb2JNQubLtMk5/kyIp81aTyjUB/Ysv1gRR0
|
||||
lTLWsvT5Oxk8ut/g+o9o0DMOIQVDmi9o4EO0fYeIToo
|
||||
-> ssh-ed25519 zNC8SA G0XcS2gzF0RopI2DqkWaTYXwjUpkVtdrxSQ+p8bfE0w
|
||||
pZVIg/1P8BbnpRfV5F0FG3xgLSiA8M+nosQ8iNeYmcU
|
||||
-> ssh-ed25519 EiAAKw qWmO0IjKoUFVxbxFUx36JIhME2PU7lkD+3agKO7+6nA
|
||||
yEIw8IzQmM8C9dZoPajtvdUOF5kJ/C+rtgLczcmP1bs
|
||||
-> ssh-rsa eFi+Zw
|
||||
O5XRvS+Y/1mm9nQ7IZmxEp7RmFjAH0OTKPkRTME7BybnePPZLL0l6wMP26hx88Nv
|
||||
dOqdaS07Xb26EIgCS/4xCY4sPWZNEfAfnDVoF4/SNbmfbN0XpNpR981AWcxiTL35
|
||||
Fngk0lPa1NtuUH4S4zTda21kXHE0zv2mYLNMuek8dTrUd2piC+Z0WJJdrG1LK0hN
|
||||
dDuLzX/mNibNXDvYxyD6mtkO2S1wO9QL88ucNZptT29vcaD48EZM/SsAwgf3OoqH
|
||||
kd7jSTTdZ/yk8ccTMiT5eskQ3ZZcqc7JaF+M2d88DP6LcSaJnNzyVSEMAHHfpoY1
|
||||
/kHxZ88/ehwPDXrp0bL448jdPuWqPSerzCWyyZFbc8Jj6zRUtC5joL0Vq2Rqs+EH
|
||||
rmKMfi1l2+utleGYfCyHI5/czsMhJ2jXLGPguWQQdixNtb/RWFw6DeRP9xdO9QJR
|
||||
LkoAFgv0ykP+L+C6sA7bpJqIGNftl4x8OUQxrKtf3YQ8K2LhUZb23JPn4Ob/QXo/
|
||||
|
||||
--- W7eUDhB/RBUYV1gaM4ktPEOVU6l5IRgOoRDpKKpvAnM
|
||||
O\òŒMúOýJä ÍÅ!ÒHûËÄpK¥Õ:gßÙ[6™M¡VÞÃbYæÌí;è€F'¡#ä7&Žs1„ 󨱉6º�²5_nÂ’mõÔ.¼qñ'à˜ƒq®6Êf-ÖÄW™w›½ß#5³¯6ˆ¼ŸêªrÒð�„Ð#œÅÄ€=�4EÎËlžÕÿ[
|
||||
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
age-encryption.org/v1
|
||||
-> ssh-ed25519 Wl2fDA 3CWPYLgoTMGb9gBbDzZIQxYJ9Gfm49g6lqQyqlegUDQ
|
||||
ryhsPP5+Byus2e5GSXDJlKYX1o3HfQ87CLRv2htU4n4
|
||||
-> ssh-ed25519 EiAAKw B2uGdkeC3OZISN2iH2DR1J7L3/mbuFvebzqaTdAURCw
|
||||
ze0X/MmHP78rRqAn0O3VBtnMJsiOXPk8RIe82tdQMeg
|
||||
--- kLBxPuJdbPmJ1Lz3iBu8EPItdZtpNHIyV6pz1QzhcUY
|
||||
ä3ÛÿÉèŸP>gòh@ö•AZ’üz-í6R€¸zèÚ¢[ÇÝÍPÂòㆿy?•ÉŽU�SNÝ©&ú#}ÝR+o?.B¶&´5]ÇW€OΉPuh‹½ŽÞ=t¶5|¿×“s×€ú&!‰Î-�æTÝSÆfÕ™-j"#žiÂwzºš›ãjö¯“HŒí�
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+17
-31
@@ -1,34 +1,20 @@
|
||||
age-encryption.org/v1
|
||||
-> ssh-ed25519 Wl2fDA dM0TgKtswZcbEV9tGGY26YCksV2xadHWXv7D/KksAWk
|
||||
1vCcuHmVP2xiHd/7hh0z2Hiq/EeA8uvdsRtQReC5hNY
|
||||
-> ssh-ed25519 zNC8SA uTO/3ePjgiKqk3jeRGZX5D3LjzhSBlp2rD2ZakKmfX0
|
||||
tVkEEcP/KfD9x52l7iz5F3hKK0LSckjXWK5YP2aeBt4
|
||||
-> ssh-ed25519 EiAAKw Etu0I4IzJ3BB2SzCeiexx+dhcLUO5d2Ws+WiJyLk/Sw
|
||||
9GBcZPsIXO3mXbri3lFYjtBBu0wFYul6hKsCvBKVLFs
|
||||
-> ssh-ed25519 Wl2fDA 8rfiRx7+Gr9BtiSXsVEs2W+pXoms6ynODC1TL90+Wi4
|
||||
/uMnYMJovbaPjwX1qCAtIokov40RYIAm2Mup5XKBJvw
|
||||
-> ssh-ed25519 zNC8SA FlxMK7kMYnKHY9MBJ+HYDI4GNS0nSgZxVuRe4yTWBgg
|
||||
HPOV31k8Ueb1W5usG7iLXDQxyAlISrgHThddHpGY2+s
|
||||
-> ssh-ed25519 EiAAKw Bu7+NJXivoRA07glNWUlBGu03J0ueth7XDU7SWQYT30
|
||||
r/DBmf4TRDJBgFF0KdeHuKL5hLdU1z6HtfAAVbc6Y0I
|
||||
-> ssh-rsa eFi+Zw
|
||||
uOZsBC+IMHdX2h9Jq/CF/L3BsxDW+dULk04JQbDeM85Mrxxdrv2X3w7AW8YU2KS+
|
||||
Xg8LnzH01z4Nfs89uysM/lsWptc9qMeaK9o0oHC+tSJH4Ch43MejbmFYjFibHaCm
|
||||
krQM7dAGIJwc/o0+ykaCrbXSvXAyfd6Nw1izou2ZcDRI7mTipOZO8F949SIk//Rc
|
||||
UJgPLqpGwScEfrHf4f6tySC4LmD0bPIV1xDpmmXcS7c83E9+iVOtb5Y1In6CQrF1
|
||||
XZQCb9MkPySbuicwR022CySb+lc7Ru44RdqBgV1e+wphyZCoqCk09i18egV3hNs6
|
||||
iEul3M8dqV27yRKrWIUD5jT2tUszTNJfreiuZl9eDmLkcVWExkWzqWPUFJ48hQiZ
|
||||
89Z4Evn04vZGoeL67K5q93lSRHz109zT/KIJSQMZpbaecGAoiZDM8Mdq3KzawGSG
|
||||
ENQazx6lnGoMccvxFhjrVqfYj3U4S/pnCow5fatvkBQSyysL63UxE5ivcFUHHppB
|
||||
Nu4gAM/vbh0kpEUIaT4P6iTe9qFFM/9IVxiiKPYHdPnCmPJHrug1afLLFrrrpqkd
|
||||
o1NrfYIM9gW6jl5QMCcP5DpzMTppokX0P1Tz1ZeOEtZUVtGeZ7Q2wmL4zftwmG9J
|
||||
qoDjsCd0z6MPDUdU46qc7kjQBhOwGLfHXTfGLXGNZxqj0oLvEoEKpdvFNBvMSyxK
|
||||
oGZRwGsHQcUXKhCPtf6PVtSkHMABzpUAhgS8oqjp4RVurD0lcrPgsx8pSRRarfyE
|
||||
ll1QbFCjftuJfeIEshgRkaLGjIQpZDFA3w2XMqDddFz5H/9Ak+F8/rkNnUrN2x4M
|
||||
amca8s4Sbls6RjyysarIytilCtpaKEI2sgkD2fERao6ayTSnWF45qqh635OLaP5A
|
||||
b7qcru9gO0C3Ik+UuiZMgovxo/+yBYe3+8x8q/uKR4apPAkt/2q28Uilw1WboIEB
|
||||
rIjBr0BN1JeHvkiyljJGcvGf5jHdmOrpQu/L1xuSDjsTnh+U6BshQC8bbkJNsVoL
|
||||
|
||||
-> ssh-ed25519 QovoLQ wgg0cFlYEVafE3rXK4GrID3RTatZdKPYzsjT18WskFM
|
||||
bgv+7an3xgdqf6WaiB1FFkXObcykUnvH6lJmX5gFJkQ
|
||||
-> ssh-rsa OFkEIg
|
||||
IIQbFB6VUwbB+ZtKR7Ayg9Im6vMU1AzqHT8CBagA5fwJ7Vp1GuX1X9SxL9hMPkd3
|
||||
4osEbSu3JJDMwfC6AfFtcEjmxjmRYyiYlzmIjhVEsaTlwyeucAPd+fdj+TPjHidZ
|
||||
dffizNEOiENY49jlmWTjMqYKnBsSP9GfH4ZsKpCaWMm2h9p687weuXFfbYfjYMII
|
||||
a3C4iG8m+mZ4crYTKZu6WPbnHn9g0pMxZBs4v6MnBHk6eEJ0uiJvrzYApoFE5om7
|
||||
9AknL27ra/+A1UQl+1kzLT+IivJa8FCfZ+zF1RYLRvSATlIzCqCiBiayAsVtQg5O
|
||||
girBRnlAJTPisszyoAhsqbECvD6bJfwlTW0STg/M1u3ZPMTGL4V0gJgynANmjb7Y
|
||||
TXd11zuhjRYgOBAj09trQFTmmwIgPvvu8+VXNDNPAp02ffBT8kMUvSEik98/35x1
|
||||
Dwvm38t05O6nqyHUF957CRVTzPQPAnb5Cd+Rw/joID2YPyFN9IZwE4mi2Bf3zdZo
|
||||
roxtqCupmWkpxMNN7GZJrmCE/Lh6YV4DgUd6VNQc7QlGsq5K4XRT7aa+s+17cC8e
|
||||
HCxQfGM8sMe9T6IK+K4p6qTqluyI/X0r95kGfzhNmgzufc44i6X497i3fDSVoLpx
|
||||
Uo7Ao3QRNPyaUXcqTTIg8Kx9YiLQC3tDblVJjIZU89o
|
||||
--- Vb9o/bhuN6XXjfK04haEEUXnuIA02j4GH9PmAh0ayN8
|
||||
óE¬dGs;’ްÀ±��ü
|
||||
ñ,OHˆÿœˆ{²¶>ú*wAÃLÌÄ\©0SQöÖ*{6fô‰+Xš¨.
|
||||
--- GCTLfa/BICL9AWTaqGC13M101Z8sqSqPP4ysJVv5zvg
|
||||
]
|
||||
ý¢Ôÿi¹‡7c·f`b@%X”¿J�)û[<+;x-ÇKmTõ@ãÌ„�ýŸK]7sc*럇¼2Ý®5
|
||||
+17
-7
@@ -1,15 +1,25 @@
|
||||
let
|
||||
users = import ../lib/ssh/keys.nix;
|
||||
inherit (import ../lib)
|
||||
users
|
||||
adminSSHKeys
|
||||
attrsToList
|
||||
getSSHKeys
|
||||
;
|
||||
|
||||
tuxcord-ca = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPxiko5Csyq9UODglYzLBvRfxkhQu9GXP7SH2BpC8G/7";
|
||||
in
|
||||
{
|
||||
"ntfy.age".publicKeys = [ tuxcord-ca ] ++ builtins.attrValues users;
|
||||
"ntfy.age".publicKeys = [ tuxcord-ca ] ++ adminSSHKeys;
|
||||
|
||||
# tsig-keygen etc.sub.domain.tld.
|
||||
"dns/tuxcord.net/tuxcord.net.key.age".publicKeys = [ tuxcord-ca ] ++ [ users.error users.javalsai ];
|
||||
# "dns/tuxcord.net/XXX.tuxcord.net.key.age".publicKeys = [ tuxcord-ca ] ++ [ users.XXX ];
|
||||
|
||||
"dns/tuxcord.test/tuxcord.test.key.age".publicKeys = [ tuxcord-ca ] ++ builtins.attrValues users;
|
||||
"dns/tuxcord.test/sub.tuxcord.test.key.age".publicKeys = [ tuxcord-ca ] ++ builtins.attrValues users;
|
||||
"dns/tuxcord.net/tuxcord.net.key.age".publicKeys = [ tuxcord-ca ] ++ adminSSHKeys;
|
||||
"dns/nix.tuxcord.net/nix.tuxcord.net.key.age".publicKeys = [ tuxcord-ca ] ++ adminSSHKeys;
|
||||
"dns/tuxcord.test/tuxcord.test.key.age".publicKeys = [ tuxcord-ca ] ++ adminSSHKeys;
|
||||
"dns/tuxcord.test/sub.tuxcord.test.key.age".publicKeys = [ tuxcord-ca ] ++ adminSSHKeys;
|
||||
}
|
||||
// builtins.listToAttrs (
|
||||
map (user: {
|
||||
name = "dns/tuxcord.net/${user.name}.tuxcord.net.key.age";
|
||||
value.publicKeys = [ tuxcord-ca ] ++ getSSHKeys user.name;
|
||||
}) (builtins.filter (user: user.value.ddns or false) (attrsToList users))
|
||||
)
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 13 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,12 @@
|
||||
{{template "base/head" .}}
|
||||
<div role="main" aria-label="{{if .IsSigned}}{{ctx.Locale.Tr "dashboard"}}{{else}}{{ctx.Locale.Tr "home"}}{{end}}"
|
||||
class="page-content home tw-mb-8 tw-px-8 center" style="height: 100%; flex-direction: column; display: flex; align-items: center; justify-content: space-around;">
|
||||
<img width="500" height="500" src="{{AssetUrlPrefix}}/images/full-logo.svg" alt="{{ctx.Locale.Tr "logo"}}">
|
||||
<div class="hero">
|
||||
<h1 class="ui icon header title">
|
||||
{{AppName}}
|
||||
</h1>
|
||||
<!-- <h2>My own gitea server 😎</h2> -->
|
||||
</div>
|
||||
</div>
|
||||
{{template "base/footer" .}}
|
||||
@@ -0,0 +1,51 @@
|
||||
@import "/assets/css/theme-forgejo-dark.css";
|
||||
|
||||
/* :root { */
|
||||
/* --is-dark-theme: true; */
|
||||
|
||||
/* --accent-color: 221, 85, 85; */
|
||||
/* /1* #d55 *1/ */
|
||||
/* --gitea-color-primary-dark-4: 221, 85, 85; */
|
||||
/* --accent-color-secondary: 96, 72, 10; */
|
||||
/* --accent-color-hover: 170, 68, 68; */
|
||||
/* --color-primary: rgb(var(--accent-color)); */
|
||||
/* --color-secondary: rgb(var(--accent-color-secondary)); */
|
||||
/* --button-color: rgb(var(--accent-color)); */
|
||||
/* --button-color-hover: rgb(var(--accent-color-hover)); */
|
||||
/* } */
|
||||
|
||||
:root {
|
||||
--is-dark-theme: true;
|
||||
|
||||
--color-primary: #d162a4;
|
||||
--color-primary-contrast: #fff;
|
||||
--color-primary-dark-1: #ba3283;
|
||||
--color-primary-dark-2: #b55690;
|
||||
--color-primary-dark-3: #ac2c79;
|
||||
--color-primary-dark-4: #a30262;
|
||||
--color-primary-dark-5: #8a0253;
|
||||
--color-primary-dark-6: #710144;
|
||||
--color-primary-dark-7: #570135;
|
||||
--color-primary-light-1: #d776af;
|
||||
--color-primary-light-2: #dd89bb;
|
||||
--color-primary-light-3: #e29dc6;
|
||||
--color-primary-light-4: #e8b1d2;
|
||||
--color-primary-light-5: #eec5dd;
|
||||
--color-primary-light-6: #f4d8e9;
|
||||
--color-primary-light-7: #f9ecf4;
|
||||
--color-primary-alpha-10: #d162a419;
|
||||
--color-primary-alpha-20: #d162a433;
|
||||
--color-primary-alpha-30: #d162a44b;
|
||||
--color-primary-alpha-40: #d162a466;
|
||||
--color-primary-alpha-50: #d162a480;
|
||||
--color-primary-alpha-60: #d162a499;
|
||||
--color-primary-alpha-70: #d162a4b3;
|
||||
--color-primary-alpha-80: #d162a4cc;
|
||||
--color-primary-alpha-90: #d162a4e1;
|
||||
}
|
||||
|
||||
.navbar-left > #navbar-logo.item,
|
||||
.navbar-right > #navbar-logo.item,
|
||||
.navbar-mobile-right > #navbar-logo.item {
|
||||
padding: 3px;
|
||||
}
|
||||
@@ -16,13 +16,19 @@ To test the environment, you can launch a virtualized NixOS system derived from
|
||||
nix run '.#nixosConfigurations.<system>.config.system.build.vm'
|
||||
```
|
||||
|
||||
Here, `<system>` refers to the hostname of the system you want to test (e.g., tuxcord-ca).
|
||||
Here, `<system>` refers to the hostname of the system you want to test (e.g., tuxcord-test).
|
||||
|
||||
Note that this will create a `qcow2` image file in the current directory. Nix will automatically manage changes to the configuration and update the image file accordingly while keeping part of its mutable state (e.g., root bash history).
|
||||
|
||||
> [!WARNING]
|
||||
> Not all changes are applied automatically. Updates such as user passwords changes or modifications to the filesystem layout will require deleting the image file so that Nix can re-create it from scratch.
|
||||
|
||||
# Access
|
||||
|
||||
The initial password for the `root` account is `tuxcord`.
|
||||
|
||||
SSH login is enabled for the configured user keys, if using the VM test configuration, yo will have to use the bridged IP.
|
||||
|
||||
# Tooling
|
||||
|
||||
Tooling used to aid in development.
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ Host specific configuration can be found at `nixos/hosts/tuxcord-XX`. This is us
|
||||
|
||||
To learn how to get started, refer to the [Getting Started guide](./GETTING_STARTED.md).
|
||||
|
||||
The guide contains basic instructions as to how to use Nix for this repository, as well as tools to help in certain tasks, some of this tools might be assumed accross document resources.
|
||||
The guide contains basic instructions as to how to use Nix for this repository, as well as tools to help in certain tasks, some of this tools might be assumed across document resources.
|
||||
|
||||
It might also be useful to read the [installation section](#installation) to learn how to configure your testing environment.
|
||||
|
||||
|
||||
+9
-3
@@ -6,14 +6,20 @@ Secrets are managed with `agenix` in the `agenix/` directory. This allows to dec
|
||||
|
||||
The `agenix` help menu is already very helpful, but here you have a survival guide:
|
||||
|
||||
- `agenix` commands should run relative to the `agenix/` direcotry.
|
||||
- `agenix` commands should run relative to the `agenix/` directory.
|
||||
- `agenix -d` allows you to descrypt such file if you possess any of the decryption keys.
|
||||
- `agenix -e` decrypts (if present) and opens the file in your editor to re-encrypt when exited.
|
||||
- `agenix -r` re-encypts `*.age` files in the case you ever change its decryption keys.
|
||||
|
||||
# Secrets
|
||||
|
||||
<!-- TODO: missing ntfy.sh secret docs -->
|
||||
There is a `ntfy.age` secret file which contents look like:
|
||||
|
||||
```sh
|
||||
NTFY_TOPIC=readable-name_XXXXXXXXXX
|
||||
```
|
||||
|
||||
This secret file is meant to be sources by shells before using [ntfy.sh](<https://ntfy.sh/>) to push important notifications. This topic could contain sensitive information and must be kept secret amongst administrators.
|
||||
|
||||
## DNS TSIG Keys
|
||||
|
||||
@@ -24,5 +30,5 @@ These keys can be generated using `tsig-keygen <key-name>` (historically they we
|
||||
When DNS is enabled for a host, it will look for `dns/${fqdn}/${zone}.key` secrets.
|
||||
|
||||
- The key whose zone matches the `${fqdn}` will be allowed to tramit updates for all the domain.
|
||||
- Keys restrained to a specific `${subdomain}` will only be allowed to edit records of such subdomain.
|
||||
- Keys restrained to a specific `${zone}` will only be allowed to edit records of such zone.
|
||||
- All keys must be named with the zone they affect, final dot included, so that (e.g. `tuxcord.net/javalsai.tuxcord.net.key` must be generated by `tsig-keygen javalsai.tuxcord.net.`).
|
||||
|
||||
+7
-1
@@ -2,7 +2,13 @@
|
||||
|
||||
The first configuration of the server needs some configuration of its mutable state:
|
||||
|
||||
Setup also heavily relies on the secrets configured, make sure you [undestand agenix](./SECRETS.md) good enough.
|
||||
Setup also heavily relies on the secrets configured, make sure you [understand agenix](./SECRETS.md) good enough.
|
||||
|
||||
# Root Password
|
||||
|
||||
The `root` password is `tuxcord` by default on all system configurations. For security, it's important to remember to change it as soon as an installation is done.
|
||||
|
||||
The root account is intended to be kept active in case there ever is the need to perform a TTY login. But this will be rare so do keep a security complex password saved somewhere and don't share it beyond the necessary amount.
|
||||
|
||||
# SSH Keys
|
||||
|
||||
|
||||
@@ -52,6 +52,11 @@
|
||||
formatter = pkgs.nixfmt;
|
||||
};
|
||||
|
||||
flake = {
|
||||
lib = import ./lib;
|
||||
pins = import ./npins;
|
||||
};
|
||||
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
rec {
|
||||
toList = x: if builtins.isList x then x else [ x ];
|
||||
|
||||
nameValuePair = name: value: { inherit name value; };
|
||||
|
||||
mapAttrsToList = f: attrs: builtins.attrValues (builtins.mapAttrs f attrs);
|
||||
|
||||
attrsToList = mapAttrsToList nameValuePair;
|
||||
|
||||
getSSHKeys =
|
||||
username:
|
||||
if (builtins.hasAttr "ssh" users.${username}) then
|
||||
toList users.${username}.ssh
|
||||
else
|
||||
builtins.warn "user ${username} declared without ssh keys" [ ];
|
||||
|
||||
users = import ./users.nix;
|
||||
|
||||
adminSSHKeys = builtins.concatLists (
|
||||
map (user: getSSHKeys user.name) (
|
||||
builtins.filter (user: user.value.admin or false) (attrsToList users)
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
error = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDzdpxex2GlFVf5G2qsh3Ixa/XCMjnbq4JSTmAev7WYJ error.nointernet@gmail.com";
|
||||
javalsai = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDFjavnLqxIzFLIUpUWDOwhlYeoII4Qk1/9e0yWWxD/P";
|
||||
max = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDxVfJhzPDZ108UjB3Vj/akzlzYn27kyAw29AuYAr7gvG5vrqhLUYYmK8t+ZVWVpc1g6cK7OF1oUn2E5Qfmy6wqyZQXftAZ4OcRS0MB71W1bAcRq3rGe6KQDm8RSEeygX+zO+2Z6zQmVWgPr/I+JFQZ8wiWdP8X8djqTRdhqUD+SR3ZgTcnY3aLmeB/I56rcZQ3lKIeg/pEsyQ8weptlV0rTWamna6Z7Nw48VwWNSI+6EqfW2/4/edm0Ue8jMNqNZ0yx+kHJbudPgZgSR1SiR2rqlEEUaiQJQQV3VdY4DhGm7143ZSKUxyKlfTuQ7qR1zSIg6f5V71A37ik9YiSbBlOZO86swR4qHESoMNf608IuqRt2NdALHwozFPUCu16qnhu5JTk8twSAzrAhOk5zWQj1LYMoQEBhcFSmwir/1gE71NSjYtqXGVAdfkVmZ4uqG5+a1D7H3VXWOqu/j839M045O1ZBY6X3lKDsEJ1Z1+LCl/NojWnvPtJUHYI6+SdQ6k=";
|
||||
|
||||
vectorum = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCwfPaylCSN7ZqB6Trz8CmQlyzf0NUIy06uschdIOkdzjUNe/dPGbyEFZy/4SDBhg585x8hwfhfYjLGrneYq+O42DBDTDKxduWnIdl5zgPRqt7jB59jkukf9WUdpUdZsKCM5K97HCnizNEKGRnYllVPVQSapPhOm5dZlUD9YVv1UqbDuxtWOLvArkL52e9T+yL6FagRg6NPqA70MXPMk+S2H7lotFVxP2Eg//BCaQ0/H1vhNy6P4N6LLq3sVK1DSJyd2v8zHkdb2Zo0/Ygukol10KizSsEcihm8+bXp699uSgWIsaIQgDZlE1yx2iabmzQST1kL9+USnZZBZ+KxwtLCCI8mpCv6sxlhq2Zzim5HvsyMYM+zdHWIn1s2c6mEl4ntBAB4s5wAggS5Gjh/BfJLSvGsTFMC/XYX4gWFXynY5NlcopeENL2Afg4gbQvKkxYkWB/TMZWuqj5c5kCy+7F0881DpYxapq6kQ6IE4gkGiEQdhVFGWEFoV/k9iHrl6aanqvFtvuBHHgkXAGPpHAZDVZdp9lU0tqNQIM/eGINq4Or6wd9XDYyj5ezDEBxx1pPgweUDZrNe9+vKR+3AwbzB/XQPxTCcjd4d7Yx58jPLflFP1dDYT+3bvp+vA7UpHcJnISbVNu0SiSaIqLYhwDj1od5l6JDfRCqnMF1T59nRCQ==";
|
||||
pickzelle = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPUYQUWoL8iGc+PSrRrHyNwcOcmgGwPvJAM9HRJkPqcW pixel@DOOM-Machine";
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
error = {
|
||||
ssh = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDzdpxex2GlFVf5G2qsh3Ixa/XCMjnbq4JSTmAev7WYJ error.nointernet@gmail.com";
|
||||
admin = true;
|
||||
ddns = true;
|
||||
};
|
||||
|
||||
javalsai = {
|
||||
ssh = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDFjavnLqxIzFLIUpUWDOwhlYeoII4Qk1/9e0yWWxD/P";
|
||||
admin = true;
|
||||
ddns = true;
|
||||
};
|
||||
|
||||
max = {
|
||||
ssh = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDxVfJhzPDZ108UjB3Vj/akzlzYn27kyAw29AuYAr7gvG5vrqhLUYYmK8t+ZVWVpc1g6cK7OF1oUn2E5Qfmy6wqyZQXftAZ4OcRS0MB71W1bAcRq3rGe6KQDm8RSEeygX+zO+2Z6zQmVWgPr/I+JFQZ8wiWdP8X8djqTRdhqUD+SR3ZgTcnY3aLmeB/I56rcZQ3lKIeg/pEsyQ8weptlV0rTWamna6Z7Nw48VwWNSI+6EqfW2/4/edm0Ue8jMNqNZ0yx+kHJbudPgZgSR1SiR2rqlEEUaiQJQQV3VdY4DhGm7143ZSKUxyKlfTuQ7qR1zSIg6f5V71A37ik9YiSbBlOZO86swR4qHESoMNf608IuqRt2NdALHwozFPUCu16qnhu5JTk8twSAzrAhOk5zWQj1LYMoQEBhcFSmwir/1gE71NSjYtqXGVAdfkVmZ4uqG5+a1D7H3VXWOqu/j839M045O1ZBY6X3lKDsEJ1Z1+LCl/NojWnvPtJUHYI6+SdQ6k=";
|
||||
admin = true;
|
||||
};
|
||||
|
||||
vectorum = {
|
||||
ssh = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCwfPaylCSN7ZqB6Trz8CmQlyzf0NUIy06uschdIOkdzjUNe/dPGbyEFZy/4SDBhg585x8hwfhfYjLGrneYq+O42DBDTDKxduWnIdl5zgPRqt7jB59jkukf9WUdpUdZsKCM5K97HCnizNEKGRnYllVPVQSapPhOm5dZlUD9YVv1UqbDuxtWOLvArkL52e9T+yL6FagRg6NPqA70MXPMk+S2H7lotFVxP2Eg//BCaQ0/H1vhNy6P4N6LLq3sVK1DSJyd2v8zHkdb2Zo0/Ygukol10KizSsEcihm8+bXp699uSgWIsaIQgDZlE1yx2iabmzQST1kL9+USnZZBZ+KxwtLCCI8mpCv6sxlhq2Zzim5HvsyMYM+zdHWIn1s2c6mEl4ntBAB4s5wAggS5Gjh/BfJLSvGsTFMC/XYX4gWFXynY5NlcopeENL2Afg4gbQvKkxYkWB/TMZWuqj5c5kCy+7F0881DpYxapq6kQ6IE4gkGiEQdhVFGWEFoV/k9iHrl6aanqvFtvuBHHgkXAGPpHAZDVZdp9lU0tqNQIM/eGINq4Or6wd9XDYyj5ezDEBxx1pPgweUDZrNe9+vKR+3AwbzB/XQPxTCcjd4d7Yx58jPLflFP1dDYT+3bvp+vA7UpHcJnISbVNu0SiSaIqLYhwDj1od5l6JDfRCqnMF1T59nRCQ==";
|
||||
};
|
||||
|
||||
pickzelle = {
|
||||
ssh = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPUYQUWoL8iGc+PSrRrHyNwcOcmgGwPvJAM9HRJkPqcW pixel@DOOM-Machine";
|
||||
};
|
||||
}
|
||||
+10
-15
@@ -28,6 +28,8 @@ in
|
||||
./vm.nix
|
||||
];
|
||||
|
||||
age.secrets.ntfy.file = "${self}/agenix/ntfy.age";
|
||||
|
||||
nix = {
|
||||
package = inputs'.nix-super.packages.default;
|
||||
|
||||
@@ -97,22 +99,15 @@ in
|
||||
|
||||
extraHosts =
|
||||
let
|
||||
subdomains = [
|
||||
""
|
||||
".git"
|
||||
];
|
||||
subdomains = [ "git" ];
|
||||
|
||||
inherit (config.networking) fqdn;
|
||||
hosts = [ fqdn ] ++ map (sub: "${sub}.${fqdn}") subdomains;
|
||||
in
|
||||
builtins.foldl' (
|
||||
hosts-acc: domain-prefix:
|
||||
let
|
||||
host = "${domain-prefix}${config.networking.fqdn}";
|
||||
in
|
||||
hosts-acc
|
||||
+ ''
|
||||
127.0.0.1 ${host}
|
||||
::1 ${host}
|
||||
''
|
||||
) "" subdomains;
|
||||
lib.concatMapStrings (host: ''
|
||||
127.0.0.1 ${host}
|
||||
::1 ${host}
|
||||
'') hosts;
|
||||
};
|
||||
|
||||
virtualisation.podman.enable = true;
|
||||
|
||||
@@ -33,5 +33,6 @@ in
|
||||
tuxcord-ca = mkSystem "tuxcord-ca" "x86_64-linux";
|
||||
|
||||
tuxcord-test = mkSystem "tuxcord-test" "x86_64-linux";
|
||||
tuxcord-acmetest = mkSystem "tuxcord-acmetest" "x86_64-linux";
|
||||
};
|
||||
}
|
||||
|
||||
+4
-1
@@ -15,7 +15,10 @@
|
||||
"xhci_pci"
|
||||
];
|
||||
|
||||
kernelModules = [ "kvm-intel" ];
|
||||
kernelModules = [
|
||||
"kvm-amd"
|
||||
"kvm-intel"
|
||||
];
|
||||
};
|
||||
|
||||
hardware = {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
imports = [
|
||||
./storage.nix
|
||||
];
|
||||
|
||||
networking.fqdn = "nix.tuxcord.net";
|
||||
|
||||
acme.rfc2136.nameserver = "tuxcord.net";
|
||||
dns.enable = true;
|
||||
|
||||
services.getty.autologinUser = "root";
|
||||
|
||||
time.timeZone = "Europe/Madrid";
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
fileSystems."/" = {
|
||||
device = "/dev/vda";
|
||||
fsType = "ext4";
|
||||
};
|
||||
}
|
||||
@@ -4,7 +4,13 @@
|
||||
./storage.nix
|
||||
];
|
||||
|
||||
acme = {
|
||||
enable = true;
|
||||
useSelfDns = true;
|
||||
};
|
||||
|
||||
dns.enable = true;
|
||||
networking.fqdn = "tuxcord.net";
|
||||
|
||||
time.timeZone = "Canada/Eastern";
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
device = "/dev/xvda2";
|
||||
fsType = "btrfs";
|
||||
options = [ "subvol=@persist" ] ++ defaultOptions;
|
||||
neededForBoot = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
{
|
||||
dns.enable = true;
|
||||
imports = [
|
||||
./storage.nix
|
||||
];
|
||||
|
||||
networking.fqdn = "tuxcord.test";
|
||||
|
||||
acme.enable = false;
|
||||
dns.enable = true;
|
||||
|
||||
services.getty.autologinUser = "root";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
fileSystems."/" = {
|
||||
device = "/dev/vda";
|
||||
fsType = "ext4";
|
||||
};
|
||||
}
|
||||
@@ -55,8 +55,6 @@
|
||||
};
|
||||
};
|
||||
|
||||
fileSystems."/persist".neededForBoot = true;
|
||||
|
||||
environment.persistence."/persist" = {
|
||||
enable = true;
|
||||
hideMounts = true;
|
||||
@@ -72,10 +70,6 @@
|
||||
];
|
||||
files = [
|
||||
"/etc/machine-id"
|
||||
"/etc/ssh/ssh_host_ed25519_key"
|
||||
"/etc/ssh/ssh_host_ed25519_key.pub"
|
||||
"/etc/ssh/ssh_host_rsa_key"
|
||||
"/etc/ssh/ssh_host_rsa_key.pub"
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.acme;
|
||||
|
||||
inherit (lib)
|
||||
mkIf
|
||||
mkEnableOption
|
||||
mkOption
|
||||
types
|
||||
;
|
||||
|
||||
inherit (config.networking) fqdn;
|
||||
in
|
||||
{
|
||||
# we'll only support rfc2136 based challenges
|
||||
options.acme = {
|
||||
enable = mkEnableOption "" // {
|
||||
default = true;
|
||||
};
|
||||
|
||||
useSelfDns = mkOption {
|
||||
default = false;
|
||||
description = "Sets values of the self DNS if enabled, otherwise requires manual `rfc2136` nameserver and key values.";
|
||||
};
|
||||
|
||||
rfc2136 = {
|
||||
key = mkOption {
|
||||
type = types.path;
|
||||
default = config.age.secrets."dns/${fqdn}.key.age".path;
|
||||
};
|
||||
|
||||
nameserver = mkOption {
|
||||
type = types.str;
|
||||
default = if cfg.useSelfDns then fqdn else null;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = with cfg.rfc2136; nameserver != null && key != null;
|
||||
message = "ACME needs rfc2136 parameters to work, consider using `useSelfDns` option.";
|
||||
}
|
||||
];
|
||||
|
||||
environment.persistence."/persist".directories = [
|
||||
{
|
||||
directory = "/var/lib/acme";
|
||||
group = "acme";
|
||||
user = "acme";
|
||||
}
|
||||
];
|
||||
|
||||
security.acme = {
|
||||
acceptTerms = true;
|
||||
|
||||
defaults = {
|
||||
email = "error@tuxcord.net";
|
||||
reloadServices = [ "nginx" ];
|
||||
postRun = ''
|
||||
source ${config.age.secrets.ntfy.path}
|
||||
${pkgs.ntfy-sh}/bin/ntfy publish -T recycle -t "${config.host.name}" "HTTPS certificate has been renewed"
|
||||
'';
|
||||
};
|
||||
|
||||
certs."${fqdn}" = {
|
||||
dnsProvider = "rfc2136";
|
||||
environmentFile =
|
||||
with cfg.rfc2136;
|
||||
builtins.toFile "dns-01-challenge.cfg" ''
|
||||
RFC2136_NAMESERVER=${nameserver}
|
||||
RFC2136_TSIG_FILE="${key}"
|
||||
'';
|
||||
extraDomainNames = [
|
||||
"*.${fqdn}"
|
||||
"${fqdn}"
|
||||
];
|
||||
|
||||
inherit (config.services.nginx) group;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
{
|
||||
imports = [
|
||||
./acme.nix
|
||||
./dns.nix
|
||||
./fail2ban.nix
|
||||
./gitea.nix
|
||||
./forgejo.nix
|
||||
./host.nix
|
||||
./nginx.nix
|
||||
./snapper.nix
|
||||
|
||||
+34
-32
@@ -1,6 +1,21 @@
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
self,
|
||||
...
|
||||
}:
|
||||
let
|
||||
agenixDnsDir = ../../agenix/dns + "/${config.dns.domain}";
|
||||
cfg = config.dns;
|
||||
|
||||
inherit (lib)
|
||||
mkEnableOption
|
||||
mkIf
|
||||
strings
|
||||
;
|
||||
|
||||
inherit (config.networking) fqdn;
|
||||
|
||||
agenixDnsDir = "${self}/agenix/dns/${fqdn}";
|
||||
agenixKeys = builtins.attrNames (builtins.readDir agenixDnsDir);
|
||||
|
||||
keys = map (
|
||||
@@ -10,35 +25,23 @@ let
|
||||
subdomain = name: "subdomain ${name}";
|
||||
|
||||
zoneDomain =
|
||||
if lib.strings.hasSuffix ".key.age" filename then
|
||||
lib.strings.removeSuffix ".key.age" filename
|
||||
if strings.hasSuffix ".key.age" filename then
|
||||
strings.removeSuffix ".key.age" filename
|
||||
else
|
||||
throw "${filename} is not a `.key.age` file";
|
||||
in
|
||||
{
|
||||
inherit (config.age.secrets."dns/${filename}") path;
|
||||
name = zoneDomain;
|
||||
path = config.age.secrets."dns/${filename}".path;
|
||||
type = if zoneDomain == config.dns.domain then zonesub else subdomain;
|
||||
type = if zoneDomain == fqdn then zonesub else subdomain;
|
||||
}
|
||||
) agenixKeys;
|
||||
|
||||
cfg = config.dns;
|
||||
inherit (lib)
|
||||
mkEnableOption
|
||||
mkOption
|
||||
mkIf
|
||||
;
|
||||
in
|
||||
{
|
||||
options.dns = {
|
||||
enable = mkEnableOption "" // {
|
||||
default = true;
|
||||
};
|
||||
|
||||
domain = mkOption {
|
||||
type = with lib.types; str;
|
||||
default = config.networking.fqdn;
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
@@ -53,7 +56,8 @@ in
|
||||
value = {
|
||||
file = path;
|
||||
group = "named";
|
||||
owner = "named";
|
||||
owner = if config.acme.enable then "acme" else "named";
|
||||
mode = "440";
|
||||
};
|
||||
}
|
||||
) agenixKeys
|
||||
@@ -64,19 +68,17 @@ in
|
||||
|
||||
extraConfig = builtins.concatStringsSep "\n" (map (key: "include \"${key.path}\";") keys);
|
||||
|
||||
zones = {
|
||||
"${config.dns.domain}" = {
|
||||
# grant "tuxcord.net" zonesub ANY;
|
||||
extraConfig = ''
|
||||
update-policy {
|
||||
${builtins.concatStringsSep "\n" (
|
||||
map (key: "grant \"${key.name}\" ${key.type key.name} ANY;") keys
|
||||
)}
|
||||
};
|
||||
'';
|
||||
file = "/var/dns/${config.dns.domain}.zone"; # need to put default stuff
|
||||
master = true;
|
||||
};
|
||||
zones."${fqdn}" = {
|
||||
# grant "tuxcord.net" zonesub ANY;
|
||||
extraConfig = ''
|
||||
update-policy {
|
||||
${builtins.concatStringsSep "\n" (
|
||||
map (key: "grant \"${key.name}\" ${key.type key.name} ANY;") keys
|
||||
)}
|
||||
};
|
||||
'';
|
||||
file = "/var/dns/${fqdn}.zone"; # need to put default stuff
|
||||
master = true;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
inherit (config.networking) fqdn;
|
||||
|
||||
acmeEnabled = config.acme.enable;
|
||||
|
||||
themeName = "tuxcord";
|
||||
forgejoPublic = pkgs.linkFarm "forgejo-public" [
|
||||
{
|
||||
name = "assets/css/theme-${themeName}.css";
|
||||
path = ../../assets/forgejo/theme.css;
|
||||
}
|
||||
{
|
||||
name = "assets/img";
|
||||
path = makeForgejoImages {
|
||||
big = ../../assets/branding/logo.svg;
|
||||
small = ../../assets/branding/logo-head.svg;
|
||||
rasterWidth = 1024;
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "assets/images/full-logo.svg";
|
||||
path = ../../assets/branding/logo.svg;
|
||||
}
|
||||
];
|
||||
forgejoTemplates = ../../assets/forgejo/templates;
|
||||
|
||||
makeForgejoImages =
|
||||
{
|
||||
big,
|
||||
small,
|
||||
rasterWidth,
|
||||
}:
|
||||
pkgs.stdenv.mkDerivation rec {
|
||||
name = "forgejo-images";
|
||||
|
||||
srcs = [
|
||||
big
|
||||
small
|
||||
];
|
||||
unpackPhase = "true";
|
||||
|
||||
buildInputs = with pkgs; [
|
||||
inkscape
|
||||
];
|
||||
|
||||
# https://forgejo.org/docs/next/contributor/customization/#changing-the-logo
|
||||
buildPhase = ''
|
||||
mkdir -p $out
|
||||
|
||||
cp "${big}" $out/logo.svg
|
||||
inkscape -w ${toString rasterWidth} ${big} -o $out/logo.png
|
||||
|
||||
cp "${small}" $out/favicon.svg
|
||||
inkscape -w ${toString rasterWidth} ${small} -o $out/favicon.png
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
services.forgejo = {
|
||||
enable = true;
|
||||
|
||||
database.type = "mysql";
|
||||
|
||||
lfs.enable = true;
|
||||
|
||||
settings = {
|
||||
DEFAULT = {
|
||||
APP_NAME = "TuxCord Code Forge";
|
||||
};
|
||||
|
||||
server = {
|
||||
DOMAIN = fqdn;
|
||||
ROOT_URL = "${if acmeEnabled then "https" else "http"}://${fqdn}/";
|
||||
HTTP_PORT = 3000;
|
||||
};
|
||||
|
||||
service = {
|
||||
DISABLE_REGISTRATION = true;
|
||||
REQUIRE_SIGNIN_VIEW = false;
|
||||
};
|
||||
|
||||
repository = {
|
||||
ENABLE_PUSH_CREATE_USER = true;
|
||||
ENABLE_PUSH_CREATE_ORG = true;
|
||||
DEFAULT_BRANCH = "main";
|
||||
};
|
||||
|
||||
ui = {
|
||||
DEFAULT_THEME = themeName;
|
||||
};
|
||||
|
||||
# TODO: once we have email setup this would be nice
|
||||
mailer.ENABLED = true;
|
||||
|
||||
actions = {
|
||||
ENABLED = true;
|
||||
# DEFAULT_ACTIONS_URL = "github";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.forgejo-branding = {
|
||||
enable = true;
|
||||
|
||||
wantedBy = [ "forgejo.service" ];
|
||||
before = [ "forgejo.service" ];
|
||||
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = pkgs.writeShellScript "forgejo-branding.oneshot" ''
|
||||
${pkgs.rsync}/bin/rsync -rl --chown forgejo:forgejo --delete ${forgejoPublic}/ ${config.services.forgejo.customDir}/public
|
||||
${pkgs.rsync}/bin/rsync -rl --chown forgejo:forgejo --delete ${forgejoTemplates}/ ${config.services.forgejo.customDir}/templates
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
environment.persistence."/persist".directories = [
|
||||
{
|
||||
directory = config.services.forgejo.stateDir;
|
||||
group = "forgejo";
|
||||
user = "forgejo";
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
services.gitea = {
|
||||
enable = true;
|
||||
|
||||
appName = "Tuxcord's Gitea";
|
||||
database.type = "mysql";
|
||||
|
||||
lfs.enable = true;
|
||||
|
||||
settings.server.DOMAIN = config.networking.fqdn;
|
||||
# settings.server.ROOT_URL = "https://git.tuxcord.net/"; ? would also depend on ssl status
|
||||
settings.server.HTTP_PORT = 3000;
|
||||
|
||||
settings.service.DISABLE_REGISTRATION = true;
|
||||
settings.service.REQUIRE_SIGNIN_VIEW = false;
|
||||
|
||||
settings.repository.ENABLE_PUSH_CREATE_USER = true;
|
||||
settings.repository.ENABLE_PUSH_CREATE_ORG = true;
|
||||
settings.repository.DEFAULT_BRANCH = "main";
|
||||
|
||||
# settings.ui.DEFAULT_THEME = "...";
|
||||
|
||||
# TODO: once we have email setup this would be nice
|
||||
settings.mailer.ENABLED = true;
|
||||
};
|
||||
}
|
||||
+39
-10
@@ -1,17 +1,46 @@
|
||||
{ config, ... }:
|
||||
{ config, self, ... }:
|
||||
let
|
||||
inherit (config.networking) fqdn;
|
||||
|
||||
mkVhost =
|
||||
attrs:
|
||||
attrs: locations:
|
||||
let
|
||||
acmeEnabled = config.acme.enable;
|
||||
in
|
||||
{
|
||||
forceSSL = false; # TODO: tweak per host
|
||||
forceSSL = acmeEnabled;
|
||||
useACMEHost = if acmeEnabled then fqdn else null;
|
||||
|
||||
locations = {
|
||||
"= /robots.txt" = {
|
||||
alias = disallowedRobotsTxt;
|
||||
};
|
||||
}
|
||||
// locations;
|
||||
}
|
||||
// attrs;
|
||||
|
||||
mkProxy = port: {
|
||||
proxyPass = "http://127.0.0.1:${toString port}/";
|
||||
|
||||
extraConfig = ''
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
'';
|
||||
};
|
||||
|
||||
mkSsi = webRoot: {
|
||||
root = webRoot;
|
||||
|
||||
extraConfig = ''
|
||||
ssi on;
|
||||
'';
|
||||
};
|
||||
|
||||
disallowedRobotsTxt = builtins.toFile "robots.txt" ''
|
||||
User-agent: *
|
||||
Disallow: /
|
||||
'';
|
||||
in
|
||||
{
|
||||
services.nginx = {
|
||||
@@ -23,14 +52,14 @@ in
|
||||
recommendedGzipSettings = true;
|
||||
recommendedOptimisation = true;
|
||||
|
||||
# services.nginx.virtualHosts."${fqdn}" = {
|
||||
# addSSL = true;
|
||||
# root = "/var/www/myhost.org";
|
||||
# default = true;
|
||||
# };
|
||||
virtualHosts = {
|
||||
"${fqdn}" = mkVhost { default = true; } {
|
||||
"/" = mkSsi "${self.pins.website}/web-root";
|
||||
};
|
||||
|
||||
virtualHosts."git.${fqdn}" = mkVhost {
|
||||
locations."/" = mkProxy config.services.gitea.settings.server.HTTP_PORT;
|
||||
"git.${fqdn}" = mkVhost { } {
|
||||
"/" = mkProxy config.services.forgejo.settings.server.HTTP_PORT;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
+3
-1
@@ -1,9 +1,11 @@
|
||||
{ config, ... }:
|
||||
{
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
|
||||
settings = {
|
||||
ClientAliveInterval = 300;
|
||||
X11Forwarding = true;
|
||||
|
||||
KbdInteractiveAuthentication = false;
|
||||
PasswordAuthentication = false;
|
||||
@@ -11,5 +13,5 @@
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 22 ];
|
||||
networking.firewall.allowedTCPPorts = config.services.openssh.ports;
|
||||
}
|
||||
|
||||
+37
-61
@@ -1,25 +1,6 @@
|
||||
{ lib, self, ... }:
|
||||
let
|
||||
users = [
|
||||
{
|
||||
name = "error";
|
||||
options.admin = true;
|
||||
}
|
||||
{
|
||||
name = "javalsai";
|
||||
options.admin = true;
|
||||
}
|
||||
{
|
||||
name = "max";
|
||||
options.admin = true;
|
||||
}
|
||||
{
|
||||
name = "vectorum";
|
||||
}
|
||||
{
|
||||
name = "pickzelle";
|
||||
}
|
||||
];
|
||||
inherit (self.lib) users;
|
||||
|
||||
adminGroups = [
|
||||
"adm"
|
||||
@@ -30,60 +11,55 @@ let
|
||||
"wheel"
|
||||
];
|
||||
|
||||
getSSHKeys =
|
||||
username:
|
||||
let
|
||||
sshKeys = import "${self}/lib/ssh/keys.nix";
|
||||
in
|
||||
if (builtins.hasAttr username sshKeys) then
|
||||
lib.lists.toList sshKeys.${username}
|
||||
else
|
||||
lib.warn "user ${username} declared without ssh key" [ ];
|
||||
|
||||
mkUser =
|
||||
name: uid: options:
|
||||
let
|
||||
admin = options.admin or false;
|
||||
|
||||
in
|
||||
{
|
||||
users.users.${name} = {
|
||||
isNormalUser = true;
|
||||
extraGroups = lib.optionals admin adminGroups;
|
||||
inherit uid;
|
||||
|
||||
openssh.authorizedKeys.keys = getSSHKeys name;
|
||||
};
|
||||
|
||||
systemd.slices."user-${builtins.toString uid}".sliceConfig = {
|
||||
CPUQuota = "50%";
|
||||
CPUWeight = "10";
|
||||
IOAccounting = true;
|
||||
IOWeight = "10";
|
||||
MemoryMax = "2G";
|
||||
MemorySwapMax = "1G";
|
||||
TasksMax = "100";
|
||||
};
|
||||
mkUser = name: uid: admin: {
|
||||
users.users.${name} = {
|
||||
inherit uid;
|
||||
isNormalUser = true;
|
||||
extraGroups = lib.optionals admin adminGroups;
|
||||
openssh.authorizedKeys.keys = self.lib.getSSHKeys name;
|
||||
};
|
||||
|
||||
systemd.slices."user-${builtins.toString uid}".sliceConfig = {
|
||||
CPUQuota = "50%";
|
||||
CPUWeight = "10";
|
||||
IOAccounting = true;
|
||||
IOWeight = "10";
|
||||
MemoryMax = "2G";
|
||||
MemorySwapMax = "1G";
|
||||
TasksMax = "100";
|
||||
};
|
||||
};
|
||||
in
|
||||
lib.recursiveUpdate
|
||||
(builtins.foldl'
|
||||
(attrs: user: {
|
||||
options = lib.recursiveUpdate attrs.options (mkUser user.name attrs.uid (user.options or { }));
|
||||
options = lib.recursiveUpdate attrs.options (
|
||||
mkUser user.name attrs.uid (user.value.admin or false)
|
||||
);
|
||||
uid = attrs.uid + 1;
|
||||
})
|
||||
{
|
||||
options = { };
|
||||
uid = 1000;
|
||||
}
|
||||
users
|
||||
(lib.attrsToList users)
|
||||
).options
|
||||
{
|
||||
users.users.root = {
|
||||
initialPassword = "tuxcord";
|
||||
users = {
|
||||
motd = ''
|
||||
__ __ __
|
||||
---------/\ \__ /\ \ /\ \__
|
||||
---------\ \ ,_\ __ __ __ _ ___ ___ _ __ \_\ \ ___ __\ \ ,_\
|
||||
----------\ \ \/ /\ \/\ \/\ \/'\ /'___\ / __`\/\`'__\/'_` \ /'_ `\ /'__`\ \ \/
|
||||
-----------\ \ \_\ \ \_\ \/> <//\ \__//\ \L\ \ \ \//\ \L\ \ __/\ \/\ \/\ __/\ \ \_
|
||||
------------\ \__\\ \____//\_/\_\ \____\ \____/\ \_\\ \___,_\/\_\ \_\ \_\ \____\\ \__\
|
||||
-------------\/__/ \/___/ \//\/_/\/____/\/___/ \/_/ \/__,_ /\/_/\/_/\/_/\/____/ \/__/
|
||||
A friendly Linux community - est. July 2023
|
||||
'';
|
||||
|
||||
openssh.authorizedKeys.keys = lib.lists.concatLists (
|
||||
map (user: getSSHKeys user.name) (builtins.filter (user: user.options.admin or false) users)
|
||||
);
|
||||
users.root = {
|
||||
initialPassword = "tuxcord";
|
||||
openssh.authorizedKeys.keys = self.lib.adminSSHKeys;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+2
-2
@@ -7,8 +7,8 @@
|
||||
memorySize = 4096;
|
||||
|
||||
qemu.networkingOptions = lib.mkForce [
|
||||
"-net nic,netdev=user.0,model=virtio"
|
||||
"-netdev user,id=user.0,\${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}"
|
||||
"-nic bridge,br=virbr0,id=hn0,model=virt-net-pci,helper=\${QEMU_BRIDGE_HELPER_PATH}"
|
||||
"-device virtio-net-pci,netdev=hn0,id=nic1,\${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}"
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
This file is provided under the MIT licence:
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
# Generated by npins. Do not modify; will be overwritten regularly
|
||||
let
|
||||
# Backwards-compatibly make something that previously didn't take any arguments take some
|
||||
# The function must return an attrset, and will unfortunately be eagerly evaluated
|
||||
# Same thing, but it catches eval errors on the default argument so that one may still call it with other arguments
|
||||
mkFunctor =
|
||||
fn:
|
||||
let
|
||||
e = builtins.tryEval (fn { });
|
||||
in
|
||||
(if e.success then e.value else { error = fn { }; }) // { __functor = _self: fn; };
|
||||
|
||||
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295
|
||||
range =
|
||||
first: last: if first > last then [ ] else builtins.genList (n: first + n) (last - first + 1);
|
||||
|
||||
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257
|
||||
stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1));
|
||||
|
||||
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
|
||||
stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
|
||||
concatStrings = builtins.concatStringsSep "";
|
||||
|
||||
# If the environment variable NPINS_OVERRIDE_${name} is set, then use
|
||||
# the path directly as opposed to the fetched source.
|
||||
# (Taken from Niv for compatibility)
|
||||
mayOverride =
|
||||
name: path:
|
||||
let
|
||||
envVarName = "NPINS_OVERRIDE_${saneName}";
|
||||
saneName = stringAsChars (c: if (builtins.match "[a-zA-Z0-9]" c) == null then "_" else c) name;
|
||||
ersatz = builtins.getEnv envVarName;
|
||||
in
|
||||
if ersatz == "" then
|
||||
path
|
||||
else
|
||||
# this turns the string into an actual Nix path (for both absolute and
|
||||
# relative paths)
|
||||
builtins.trace "Overriding path of \"${name}\" with \"${ersatz}\" due to set \"${envVarName}\"" (
|
||||
if builtins.substring 0 1 ersatz == "/" then
|
||||
/. + ersatz
|
||||
else
|
||||
/. + builtins.getEnv "PWD" + "/${ersatz}"
|
||||
);
|
||||
|
||||
mkSource =
|
||||
name: spec:
|
||||
{
|
||||
pkgs ? null,
|
||||
}:
|
||||
assert spec ? type;
|
||||
let
|
||||
# Unify across builtin and pkgs fetchers.
|
||||
# `fetchGit` requires a wrapper because of slight API differences.
|
||||
fetchers =
|
||||
if pkgs == null then
|
||||
{
|
||||
inherit (builtins) fetchTarball fetchurl;
|
||||
# Frustratingly, due to flakes and `fetchTree`, `fetchGit`
|
||||
# has a different signature than the other builtin
|
||||
# fetchers
|
||||
fetchGit = args: (builtins.fetchGit args).outPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
fetchTarball =
|
||||
{
|
||||
url,
|
||||
sha256,
|
||||
}:
|
||||
pkgs.fetchzip {
|
||||
inherit url sha256;
|
||||
extension = "tar";
|
||||
};
|
||||
inherit (pkgs) fetchurl;
|
||||
fetchGit =
|
||||
{
|
||||
url,
|
||||
submodules,
|
||||
rev,
|
||||
name,
|
||||
lfs,
|
||||
narHash,
|
||||
}:
|
||||
pkgs.fetchgit {
|
||||
inherit url rev name lfs;
|
||||
fetchSubmodules = submodules;
|
||||
hash = narHash;
|
||||
};
|
||||
};
|
||||
|
||||
path =
|
||||
if spec.type == "Git" then
|
||||
mkGitSource fetchers spec
|
||||
else if spec.type == "GitRelease" then
|
||||
mkGitSource fetchers spec
|
||||
else if spec.type == "PyPi" then
|
||||
mkPyPiSource fetchers spec
|
||||
else if spec.type == "Channel" then
|
||||
mkChannelSource fetchers spec
|
||||
else if spec.type == "Url" || spec.type == "MutableUrl" then
|
||||
mkUrlSource fetchers spec
|
||||
else if spec.type == "Container" then
|
||||
mkContainerSource pkgs spec
|
||||
else
|
||||
builtins.throw "Unknown source type ${spec.type}";
|
||||
in
|
||||
spec // { outPath = mayOverride name path; };
|
||||
|
||||
mkGitSource =
|
||||
{
|
||||
fetchTarball,
|
||||
fetchGit,
|
||||
...
|
||||
}:
|
||||
{
|
||||
repository,
|
||||
revision,
|
||||
url ? null,
|
||||
submodules,
|
||||
hash,
|
||||
lfs,
|
||||
...
|
||||
}:
|
||||
assert repository ? type;
|
||||
# At the moment, either it is a plain git repository (which has an url), or it is a GitHub/GitLab repository
|
||||
# In the latter case, there we will always be an url to the tarball
|
||||
if url != null && !submodules then
|
||||
fetchTarball {
|
||||
inherit url;
|
||||
sha256 = hash;
|
||||
}
|
||||
else
|
||||
let
|
||||
url =
|
||||
if repository.type == "Git" then
|
||||
repository.url
|
||||
else if repository.type == "GitHub" then
|
||||
"https://github.com/${repository.owner}/${repository.repo}.git"
|
||||
else if repository.type == "GitLab" then
|
||||
"${repository.server}/${repository.repo_path}.git"
|
||||
else if repository.type == "Forgejo" then
|
||||
"${repository.server}/${repository.owner}/${repository.repo}.git"
|
||||
else
|
||||
throw "Unrecognized repository type ${repository.type}";
|
||||
urlToName =
|
||||
url: rev:
|
||||
let
|
||||
matched = builtins.match "^.*/([^/]*)(\\.git)?$" url;
|
||||
|
||||
short = builtins.substring 0 7 rev;
|
||||
|
||||
appendShort = if (builtins.match "[a-f0-9]*" rev) != null then "-${short}" else "";
|
||||
in
|
||||
"${if matched == null then "source" else builtins.head matched}${appendShort}";
|
||||
name = urlToName url revision;
|
||||
in
|
||||
fetchGit {
|
||||
rev = revision;
|
||||
narHash = hash;
|
||||
|
||||
inherit name submodules url lfs;
|
||||
};
|
||||
|
||||
mkPyPiSource =
|
||||
{ fetchurl, ... }:
|
||||
{
|
||||
url,
|
||||
hash,
|
||||
...
|
||||
}:
|
||||
fetchurl {
|
||||
inherit url;
|
||||
sha256 = hash;
|
||||
};
|
||||
|
||||
mkChannelSource =
|
||||
{ fetchTarball, ... }:
|
||||
{
|
||||
url,
|
||||
hash,
|
||||
...
|
||||
}:
|
||||
fetchTarball {
|
||||
inherit url;
|
||||
sha256 = hash;
|
||||
};
|
||||
|
||||
mkUrlSource =
|
||||
{
|
||||
fetchTarball,
|
||||
fetchurl,
|
||||
...
|
||||
}:
|
||||
{
|
||||
url,
|
||||
hash,
|
||||
unpack,
|
||||
...
|
||||
}:
|
||||
(if unpack then fetchTarball else fetchurl) {
|
||||
inherit url;
|
||||
sha256 = hash;
|
||||
};
|
||||
|
||||
mkContainerSource =
|
||||
pkgs:
|
||||
{
|
||||
image_name,
|
||||
image_tag,
|
||||
image_digest,
|
||||
hash,
|
||||
...
|
||||
}:
|
||||
if pkgs == null then
|
||||
builtins.throw "container sources require passing in a Nixpkgs value: https://github.com/andir/npins/blob/master/README.md#using-the-nixpkgs-fetchers"
|
||||
else
|
||||
pkgs.dockerTools.pullImage {
|
||||
imageName = image_name;
|
||||
imageDigest = image_digest;
|
||||
finalImageTag = image_tag;
|
||||
hash = hash;
|
||||
};
|
||||
|
||||
in
|
||||
mkFunctor (
|
||||
{
|
||||
input ? ./sources.json,
|
||||
}:
|
||||
let
|
||||
data =
|
||||
if builtins.isPath input then
|
||||
# while `readFile` will throw an error anyways if the path doesn't exist,
|
||||
# we still need to check beforehand because *our* error can be caught but not the one from the builtin
|
||||
# See: <https://git.lix.systems/lix-project/lix/issues/1098>
|
||||
if builtins.pathExists input then
|
||||
builtins.fromJSON (builtins.readFile input)
|
||||
else
|
||||
throw "Input path ${toString input} does not exist"
|
||||
else if builtins.isAttrs input then
|
||||
input
|
||||
else
|
||||
throw "Unsupported input type ${builtins.typeOf input}, must be a path or an attrset";
|
||||
version = data.version;
|
||||
in
|
||||
if version == 8 then
|
||||
builtins.mapAttrs (name: spec: mkFunctor (mkSource name spec)) data.pins
|
||||
else
|
||||
throw "Unsupported format version ${toString version} in sources.json. Try running `npins upgrade`"
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"pins": {
|
||||
"website": {
|
||||
"type": "Git",
|
||||
"repository": {
|
||||
"type": "Git",
|
||||
"url": "https://git.javalsai.tuxcord.net/tuxcord/website.git"
|
||||
},
|
||||
"branch": "main",
|
||||
"submodules": false,
|
||||
"lfs": true,
|
||||
"revision": "b18dd7b863644debb0a843a5b21bb490bfe7d048",
|
||||
"url": null,
|
||||
"hash": "sha256-xYH9RXYZDVotUW8fKIEC9u0GJeEg2nV/23aQlEyeQso="
|
||||
}
|
||||
},
|
||||
"version": 8
|
||||
}
|
||||
+39
-2
@@ -2,9 +2,47 @@
|
||||
{
|
||||
perSystem =
|
||||
{ pkgs, ... }:
|
||||
let
|
||||
npins' = pkgs.npins.overrideAttrs (
|
||||
final: old: {
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "javalsai";
|
||||
repo = "npins";
|
||||
rev = "f3def7dfeecc16884cb0601f6c904d5142f47383";
|
||||
hash = "sha256-ejeOGmDw+D4KddFJ5OAPradqoS+p2eAhhS4fOLQOWOk=";
|
||||
};
|
||||
|
||||
cargoHash = null;
|
||||
cargoDeps = pkgs.rustPlatform.fetchCargoVendor {
|
||||
src = final.src;
|
||||
hash = "sha256-mG4UClFBgiWBraWQ12N3CSyapaIpuXI9F8wQGwh/ooQ=";
|
||||
};
|
||||
|
||||
cargoBuildFeatures = [ ];
|
||||
}
|
||||
);
|
||||
in
|
||||
{
|
||||
devShells.default = pkgs.mkShell {
|
||||
name = "configuration.nix";
|
||||
|
||||
shellHook = ''
|
||||
for path in \
|
||||
/usr/lib/qemu/qemu-bridge-helper \
|
||||
/run/wrappers/bin/qemu-bridge-helper
|
||||
do
|
||||
if [ -x "$path" ]; then
|
||||
export QEMU_BRIDGE_HELPER_PATH="$path"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$QEMU_BRIDGE_HELPER_PATH" ]; then
|
||||
printf "\033[1;33m%s\033[0m\n" \
|
||||
"WARN: 'qemu-bridge-helper' not found, make sure it is installed and the nix shell hook is looking for it" >&2
|
||||
fi
|
||||
'';
|
||||
|
||||
packages = with pkgs; [
|
||||
bat
|
||||
cachix
|
||||
@@ -13,10 +51,9 @@
|
||||
git
|
||||
inputs.agenix.packages.${stdenv.hostPlatform.system}.default
|
||||
jujutsu
|
||||
neovim
|
||||
nix-output-monitor
|
||||
nixfmt
|
||||
npins
|
||||
npins'
|
||||
parted
|
||||
smartmontools
|
||||
statix
|
||||
|
||||
Reference in New Issue
Block a user