Compare commits

..

No commits in common. "bb8f338c683cadd8d8cd6483788fa0bcb23dca59" and "7fd8d426fbf3757e86636b9ea635dca345aa1044" have entirely different histories.

19 changed files with 147 additions and 340 deletions

22
LICENSE
View file

@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2024 Kiana Sheibani
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.

View file

@ -14,71 +14,21 @@ automatically.
In short, it's my personal web server.
## Modules
As with all good NixOS configurations, Aether is split into *modules* that
each provide different functionality. These are stored in the `modules/` directory.
### Module Checklist
- [x] `wireless` - WiFi support
- [x] `ssh` - SSH configuration
- [x] `forgejo` - Code forge
- [x] `fail2ban` - IP moderation
- [ ] `site` - Personal website
- [ ] `mail` - Mail server
- [ ] `backup` - Automated backup system
- [ ] `cachix` - Nix build caching
## Deployment
Aether is designed to separate individual machine details from the abstract
specification of the system, allowing for deployment to several different
types of system. This is handled using *deployments* in the `deploy/` directory.
Currently, I deploy Aether physically to a
Aether is designed to separate deployment from the logical specification of the
system, and thus to support any machine that can run NixOS. Currently, it is
deployed physically to a
[Raspberry Pi 5](https://wiki.nixos.org/wiki/NixOS_on_ARM/Raspberry_Pi_5)
running a [modified UEFI bootloader](https://github.com/worproject/rpi5-uefi)
to provide Linux support. The NixOS code for this can be found in `deploy/rpi5/`.
to provide Linux support.
## External Usage
## Module Checklist
If you use NixOS and are interested in any of these modules, you can import
them for your own config!
Add this repository as a flake input:
``` nix
{
inputs.aether.url = "https://git.tokinanpa.dev/toki/aether/archive/main.tar.gz";
}
```
Aether modules are then exposed under `nixosModules.<name>` and deployments
under `nixosModules.deploy-<name>`. You can activate a module by adding it
to your `imports`:
``` nix
{
imports = with aether.nixosModules; [
# Deployment
deploy-rpi5
# Modules
forgejo
ssh
];
# Required by forgejo module
aether.domain = "...";
}
```
Any number of modules can be activated at once, but activating more than one
deployment will cause issues, so that should be avoided.
Some modules have options that can be used to configure their effects. If a
module has options, they can be found in the `options.nix` file inside the
module directory. More general options used by multiple modules are
documented in `modules/options.nix`.
- [x] `forgejo.nix` - Code forge
- [ ] `site.nix` - Personal website
- [ ] `mail.nix` - Mail server
- [ ] `backup.nix` - Automated backup system
- [ ] `cachix.nix` - Nix build caching
[^1]: Adapted from [Book II.1](http://classics.mit.edu/Aristotle/heavens.2.ii.html).

View file

@ -1,68 +0,0 @@
{ config, lib, pkgs, aether, ... }:
{
networking.hostName = "toki-aether";
time.timeZone = "America/New_York";
nix.package = pkgs.nixVersions.latest;
nix.settings.experimental-features = [ "nix-command" "flakes" ];
users.mutableUsers = false;
users.users.root = {
hashedPassword = "$y$j9T$LHeAgn5XytQM5DLfGSDT30$9OD3eIua5vEy4/GFBbT1oe1UnlNxDHt9thqsiqcGXy7";
openssh.authorizedKeys.keys = (import secrets/secrets.nix).keys;
};
environment.systemPackages = with pkgs; [
openssl
rsync
curl
git
wget
];
# Aether modules
imports = [
aether.aether
aether.deploy-rpi5
];
aether.domain = "tokinanpa.dev";
aether.acmeEmail = "kiana.a.sheibani@gmail.com";
aether.forgejo.templates = ./forgejo-templates;
services.forgejo.settings = {
DEFAULT.APP_NAME = "Code by toki!";
service.DISABLE_REGISTRATION = true;
repository = {
DEFAULT_REPO_UNITS = "repo.code,repo.issues,repo.pulls";
ENABLE_PUSH_CREATE_USER = true;
DEFAULT_PUSH_CREATE_PRIVATE = false;
PREFERRED_LICENSES = "MIT,GPL-3.0-or-later";
};
mirror.DEFAULT_INTERVAL = "1h";
indexer = {
REPO_INDEXER_ENABLED = true;
REPO_INDEXER_EXCLUDE = "**.pdf, **.png, **.jpg, **.jpeg, **.svg, **.web, **.gpg, **.age";
};
ui = {
DEFAULT_THEME = "forgejo-dark";
GRAPH_MAX_COMMIT_NUM = 250;
};
"ui.meta" = {
AUTHOR = "Kiana Sheibani";
DESCRIPTION = "Code by toki! Powered by Forgejo";
KEYWORDS = "git,forge,forgejo,toki,tokinanpa";
};
"service.explore".DISABLE_USERS_PAGE = true;
federation.ENABLED = true;
};
system.stateVersion = "24.05";
}

45
config.nix Normal file
View file

@ -0,0 +1,45 @@
{ config, lib, pkgs, rpi5-kernel, ... }:
{
imports = [ ./hardware-configuration.nix ./modules ];
boot.kernelPackages = rpi5-kernel.legacyPackages.aarch64-linux.linuxPackages_rpi5;
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = false;
nix.package = pkgs.nixVersions.latest;
nix.settings.experimental-features = [ "nix-command" "flakes" ];
time.timeZone = "America/New_York";
networking.hostName = "toki-aether";
networking.wireless.iwd.enable = true;
networking.wireless.iwd.settings = {
Settings.AutoConnect = true;
Network.EnableIPv6 = false;
General.EnableNetworkConfiguration = true;
};
services.openssh.enable = true;
services.openssh.settings = {
PasswordAuthentication = false;
PermitRootLogin = "yes";
};
users.mutableUsers = false;
users.users.root = {
hashedPassword = "$y$j9T$LHeAgn5XytQM5DLfGSDT30$9OD3eIua5vEy4/GFBbT1oe1UnlNxDHt9thqsiqcGXy7";
openssh.authorizedKeys.keys = (import secrets/secrets.nix).keys;
};
networking.firewall.allowedTCPPorts = [ 22 80 443 ];
environment.systemPackages = with pkgs; [
openssl
rsync
curl
git
wget
];
system.stateVersion = "24.05";
}

View file

@ -1,18 +0,0 @@
{ config, lib, ... }:
{
options.aether.deploy.rpi5 = {
kernelPackages = lib.mkOption {
type = lib.types.raw;
description = "Kernel package to use for Raspberry Pi 5 support";
};
};
config =
let cfg = config.aether.deploy.rpi5;
in {
nixpkgs.system = "aarch64-linux";
boot.kernelPackages = cfg.kernelPackages;
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = false;
};
}

View file

@ -1,5 +1,5 @@
{
description = "Aether - web server configuration";
description = "Server system conf";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
@ -12,36 +12,15 @@ inputs = {
agenix.inputs.darwin.follows = "";
};
outputs = inputs@{ self, nixpkgs, agenix, rpi5-kernel, ... }:
let
inherit (nixpkgs) lib;
moduleNames =
let sub = builtins.readDir ./modules;
in builtins.filter
(d: sub.${d} == "directory")
(builtins.attrNames sub);
modules = lib.genAttrs moduleNames (name: ./modules/${name});
in {
nixosModules =
modules
// {
aether.imports = lib.attrValues modules;
deploy-rpi5 = {
imports = [ ./deploy/rpi5 ];
aether.deploy.rpi5.kernelPackages =
rpi5-kernel.legacyPackages.aarch64-linux.linuxPackages_rpi5;
};
};
outputs = inputs@{ self, nixpkgs, agenix, ... }:
{
nixosConfigurations."toki-aether" =
nixpkgs.lib.nixosSystem {
specialArgs.aether = self.nixosModules;
system = "aarch64-linux";
modules = [
{ _module.args = inputs; }
agenix.nixosModules.default
./aether/hardware-configuration.nix
./aether/config.nix
./config.nix
];
};
nixosConfigurations.default = self.nixosConfigurations."toki-aether";

5
modules/acme.nix Normal file
View file

@ -0,0 +1,5 @@
{ ... }:
{
security.acme.acceptTerms = true;
security.acme.defaults.email = "kiana.a.sheibani@gmail.com";
}

8
modules/default.nix Normal file
View file

@ -0,0 +1,8 @@
{ ... }:
{
imports = [
./acme.nix
./fail2ban.nix
./forgejo.nix
];
}

73
modules/forgejo.nix Normal file
View file

@ -0,0 +1,73 @@
{ config, ... }:
let
cfg = config.services.forgejo;
srv = cfg.settings.server;
in {
services.nginx.enable = true;
services.nginx.virtualHosts.${srv.DOMAIN} = {
forceSSL = true;
enableACME = true;
extraConfig = ''
client_max_body_size 512M;
'';
locations."/".proxyPass = "http://localhost:${builtins.toString srv.HTTP_PORT}";
};
services.forgejo = {
enable = true;
user = "git";
group = cfg.user;
database.user = cfg.user;
settings = {
DEFAULT.APP_NAME = "Code by toki!";
server = {
DOMAIN = "git.tokinanpa.dev";
ROOT_URL = "https://${srv.DOMAIN}/";
};
service.DISABLE_REGISTRATION = true;
repository = {
DEFAULT_REPO_UNITS = "repo.code,repo.issues,repo.pulls";
ENABLE_PUSH_CREATE_USER = true;
DEFAULT_PUSH_CREATE_PRIVATE = false;
PREFERRED_LICENSES = "MIT,GPL-3.0-or-later";
};
mirror.DEFAULT_INTERVAL = "1h";
indexer = {
REPO_INDEXER_ENABLED = true;
REPO_INDEXER_EXCLUDE = "**.pdf, **.png, **.jpg, **.jpeg, **.svg, **.web, **.gpg, **.age";
};
ui = {
DEFAULT_THEME = "forgejo-dark";
GRAPH_MAX_COMMIT_NUM = 250;
};
"ui.meta" = {
AUTHOR = "Kiana Sheibani";
DESCRIPTION = "Code by toki! Powered by Forgejo";
KEYWORDS = "git,forge,forgejo,toki,tokinanpa";
};
"service.explore".DISABLE_USERS_PAGE = true;
federation.ENABLED = true;
};
};
systemd.tmpfiles.rules = [
"L+ ${cfg.stateDir}/custom/templates - - - - ${./forgejo-templates}"
];
users.users.${cfg.user} = {
home = cfg.stateDir;
useDefaultShell = true;
group = cfg.group;
isSystemUser = true;
};
users.groups.${cfg.group} = {};
}

View file

@ -1,55 +0,0 @@
{ config, lib, ... }:
let
cfg = config.aether.forgejo;
forgejo = config.services.forgejo;
srv = forgejo.settings.server;
in {
imports = [ ./options.nix ];
# Web server
services.nginx.enable = true;
services.nginx.virtualHosts.${srv.DOMAIN} = {
forceSSL = config.aether.https;
enableACME = config.aether.https;
extraConfig = ''
client_max_body_size 512M;
'';
locations."/".proxyPass = "http://localhost:${builtins.toString srv.HTTP_PORT}";
};
security.acme.acceptTerms = config.aether.https;
security.acme.defaults.email = cfg.acmeEmail;
networking.firewall.allowedTCPPorts =
[ 80 ] ++ lib.optional config.aether.https 443;
# Forgejo
services.forgejo = {
enable = true;
user = cfg.user;
group = forgejo.user;
database.user = forgejo.user;
settings.server = {
DOMAIN = lib.optionalString (!(builtins.isNull cfg.subdomain)) "${cfg.subdomain}."
+ config.aether.domain;
ROOT_URL = "https://${srv.DOMAIN}/";
};
};
systemd.tmpfiles.rules =
lib.optional
(!(builtins.isNull cfg.templates))
"L+ ${cfg.stateDir}/custom/templates - - - - ${cfg.templates}";
}
// lib.mkIf cfg.createUser {
users.users.${forgejo.user} = {
home = forgejo.stateDir;
useDefaultShell = true;
group = forgejo.group;
isSystemUser = true;
};
users.groups.${forgejo.group} = {};
}

View file

@ -1,52 +0,0 @@
args@{ config, lib, ... }:
{
options.aether = {
inherit (import ../options.nix args)
domain
https
acmeEmail;
forgejo = {
subdomain = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = "git";
description = ''
The subdomain to host the Forgejo instance under.
If null, then Forgejo is hosted at the domain itself.
'';
};
user = lib.mkOption {
type = lib.types.str;
default = "git";
description = ''
The user to run Forgejo with.
'';
};
createUser = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether to create the Forgejo user automatically.
'';
};
templates = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = ''
A directory of templates for customizing Forgejo's appearance.
'';
};
};
};
config.assertions = lib.mkIf config.aether.https [
{
assertion = !(builtins.isNull config.aether.acmeEmail);
message = "HTTPS support requires providing a contact email";
}
];
}

View file

@ -1,19 +0,0 @@
{ lib, ... }:
{
domain = lib.mkOption {
type = lib.types.str;
description = "The domain name the server is hosted on.";
};
https = lib.mkOption {
type = lib.types.boolByOr;
default = true;
description = "Whether to force HTTPS connections for websites.";
};
acmeEmail = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Email address for ACME.";
};
}

View file

@ -1,10 +0,0 @@
{ ... }:
{
services.openssh.enable = true;
services.openssh.settings = {
PasswordAuthentication = false;
PermitRootLogin = "yes";
};
networking.firewall.allowedTCPPorts = [ 22 ];
}

View file

@ -1,9 +0,0 @@
{ ... }:
{
networking.wireless.iwd.enable = true;
networking.wireless.iwd.settings = {
Settings.AutoConnect = true;
Network.EnableIPv6 = false;
General.EnableNetworkConfiguration = true;
};
}