2 Commits

Author SHA1 Message Date
a3f59d1e0e wip 2022-11-28 00:52:18 -05:00
c66f8ef8d4 wip 2022-11-27 22:28:50 -05:00
159 changed files with 11236 additions and 3434 deletions

View File

@@ -3,9 +3,10 @@
### Source Layout
- `/common` - common configuration imported into all `/machines`
- `/boot` - config related to bootloaders, cpu microcode, and unlocking LUKS root disks over tor
- `/network` - config for tailscale, and NixOS container with automatic vpn tunneling via PIA
- `/network` - config for tailscale, zeroteir, and NixOS container with automatic vpn tunneling via PIA
- `/pc` - config that a graphical desktop computer should have. Use `de.enable = true;` to enable everthing.
- `/server` - config that creates new nixos services or extends existing ones to meet my needs
- `/ssh.nix` - all ssh public host and user keys for all `/machines`
- `/machines` - all my NixOS machines along with their machine unique configuration for hardware and services
- `/kexec` - a special machine for generating minimal kexec images. Does not import `/common`
- `/secrets` - encrypted shared secrets unlocked through `/machines` ssh host keys

51
TODO.md
View File

@@ -10,12 +10,24 @@
- https://nixos.wiki/wiki/Comparison_of_NixOS_setups
### Housekeeping
- Format everything here using nixfmt
- Cleanup the line between hardware-configuration.nix and configuration.nix in machine config
- CI https://gvolpe.com/blog/nixos-binary-cache-ci/
- remove `options.currentSystem`
- allow `hostname` option for webservices to be null to disable configuring nginx
### NAS
- helios64 extra led lights
- safely turn off NAS on power disconnect
- hardware de/encoding for rk3399 helios64 https://forum.pine64.org/showthread.php?tid=14018
- tor unlock
### bcachefs
- bcachefs health alerts via email
- bcachefs periodic snapshotting
- use mount.bcachefs command for mounting
- bcachefs native encryption
- just need a kernel module? https://github.com/firestack/bcachefs-tools-flake/blob/kf/dev/mvp/nixos/module/bcachefs.nix#L40
### Shell Comands
- tailexitnode = `sudo tailscale up --exit-node=<exit-node-ip> --exit-node-allow-lan-access=true`
@@ -40,7 +52,21 @@
- https://ampache.org/
- replace nextcloud with seafile
### VPN container
- use wireguard for vpn
- https://github.com/triffid/pia-wg/blob/master/pia-wg.sh
- https://github.com/pia-foss/manual-connections
- port forwarding for vpn
- transmission using forwarded port
- https://www.wireguard.com/netns/
- one way firewall for vpn container
### Networking
- tailscale for p2p connections
- remove all use of zerotier
### Archive
- https://www.backblaze.com/b2/cloud-storage.html
- email
- https://github.com/Disassembler0/dovecot-archive/blob/main/src/dovecot_archive.py
- http://kb.unixservertech.com/software/dovecot/archiveserver
@@ -49,32 +75,7 @@
- https://christine.website/blog/paranoid-nixos-2021-07-18
- https://nixos.wiki/wiki/Impermanence
# Setup CI
- CI
- hydra
- https://docs.cachix.org/continuous-integration-setup/
- Binary Cache
- Maybe use cachix https://gvolpe.com/blog/nixos-binary-cache-ci/
- Self hosted binary cache? https://www.tweag.io/blog/2019-11-21-untrusted-ci/
- https://github.com/edolstra/nix-serve
- https://nixos.wiki/wiki/Binary_Cache
- https://discourse.nixos.org/t/introducing-attic-a-self-hostable-nix-binary-cache-server/24343
- Both
- https://garnix.io/
- https://nixbuild.net
# Secrets
- consider using headscale
- Replace luks over tor for remote unlock with luks over tailscale using ephemeral keys
- Rollover luks FDE passwords
- /secrets on personal computers should only be readable using a trusted ssh key, preferably requiring a yubikey
- Rollover shared yubikey secrets
- offsite backup yubikey, pw db, and ssh key with /secrets access
### Misc
- for automated kernel upgrades on luks systems, need to kexec with initrd that contains luks key
- https://github.com/flowztul/keyexec/blob/master/etc/default/kexec-cryptroot
- https://github.com/pop-os/system76-scheduler
- improve email a little bit https://helloinbox.email
- remap razer keys https://github.com/sezanzeb/input-remapper

View File

@@ -1,25 +1,14 @@
{ config, pkgs, lib, ... }:
{ config, lib, ... }:
# Modify auto-update so that it pulls a flake
let
cfg = config.system.autoUpgrade;
in
{
config = lib.mkIf cfg.enable (lib.mkMerge [
{
in {
config = lib.mkIf cfg.enable {
system.autoUpgrade = {
flake = "git+https://git.neet.dev/zuckerberg/nix-config.git";
flags = [ "--recreate-lock-file" "--no-write-lock-file" ]; # ignore lock file, just pull the latest
# dates = "03:40";
# kexecWindow = lib.mkDefault { lower = "01:00"; upper = "05:00"; };
# randomizedDelaySec = "45min";
flags = [ "--recreate-lock-file" ]; # ignore lock file, just pull the latest
};
};
system.autoUpgrade.allowKexec = lib.mkDefault true;
luks.enableKexec = cfg.allowKexec && builtins.length config.luks.devices > 0;
}
]);
}

View File

@@ -1,78 +0,0 @@
{ config, lib, pkgs, ... }:
let
cfg = config.backup;
hostname = config.networking.hostName;
mkRespository = group: "s3:s3.us-west-004.backblazeb2.com/D22TgIt0-main-backup/${group}";
mkBackup = group: paths: {
repository = mkRespository group;
inherit paths;
initialize = true;
timerConfig = {
OnCalendar = "daily";
RandomizedDelaySec = "1h";
};
extraBackupArgs = [
''--exclude-if-present ".nobackup"''
];
pruneOpts = [
"--keep-daily 7" # one backup for each of the last n days
"--keep-weekly 5" # one backup for each of the last n weeks
"--keep-monthly 12" # one backup for each of the last n months
"--keep-yearly 75" # one backup for each of the last n years
];
environmentFile = "/run/agenix/backblaze-s3-backups";
passwordFile = "/run/agenix/restic-password";
};
# example usage: "sudo restic_samba unlock" (removes lockfile)
mkResticGroupCmd = group: pkgs.writeShellScriptBin "restic_${group}" ''
if [ "$EUID" -ne 0 ]
then echo "Run as root"
exit
fi
. /run/agenix/backblaze-s3-backups
export AWS_SECRET_ACCESS_KEY
export AWS_ACCESS_KEY_ID
export RESTIC_PASSWORD_FILE=/run/agenix/restic-password
export RESTIC_REPOSITORY="${mkRespository group}"
exec ${pkgs.restic}/bin/restic "$@"
'';
in
{
options.backup = {
group = lib.mkOption {
default = null;
type = lib.types.nullOr (lib.types.attrsOf (lib.types.submodule {
options = {
paths = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = ''
Paths to backup
'';
};
};
}));
};
};
config = lib.mkIf (cfg.group != null) {
services.restic.backups = lib.concatMapAttrs
(group: groupCfg: {
${group} = mkBackup group groupCfg.paths;
})
cfg.group;
age.secrets.backblaze-s3-backups.file = ../secrets/backblaze-s3-backups.age;
age.secrets.restic-password.file = ../secrets/restic-password.age;
environment.systemPackages = map mkResticGroupCmd (builtins.attrNames cfg.group);
};
}

View File

@@ -3,8 +3,7 @@
with lib;
let
cfg = config.bios;
in
{
in {
options.bios = {
enable = mkEnableOption "enable bios boot";
device = mkOption {

View File

@@ -5,8 +5,6 @@
./firmware.nix
./efi.nix
./bios.nix
./kexec-luks.nix
./luks.nix
./remote-luks-unlock.nix
];
}

View File

@@ -3,8 +3,7 @@
with lib;
let
cfg = config.efi;
in
{
in {
options.efi = {
enable = mkEnableOption "enable efi boot";
};

View File

@@ -3,8 +3,7 @@
with lib;
let
cfg = config.firmware;
in
{
in {
options.firmware.x86_64 = {
enable = mkEnableOption "enable x86_64 firmware";
};

View File

@@ -1,121 +0,0 @@
# Allows kexec'ing as an alternative to rebooting for machines that
# have luks encrypted partitions that need to be mounted at boot.
# These luks partitions will be automatically unlocked, no password,
# or any interaction needed whatsoever.
# This is accomplished by fetching the luks key(s) while the system is running,
# then building a temporary initrd that contains the luks key(s), and kexec'ing.
{ config, lib, pkgs, ... }:
{
options.luks = {
enableKexec = lib.mkEnableOption "Enable support for transparent passwordless kexec while using luks";
};
config = lib.mkIf config.luks.enableKexec {
luks.fallbackToPassword = true;
luks.disableKeyring = true;
boot.initrd.luks.devices = lib.listToAttrs
(builtins.map
(item:
{
name = item;
value = {
masterKeyFile = "/etc/${item}.key";
};
})
config.luks.deviceNames);
systemd.services.prepare-luks-kexec-image = {
description = "Prepare kexec automatic LUKS unlock on kexec reboot without a password";
wantedBy = [ "kexec.target" ];
unitConfig.DefaultDependencies = false;
serviceConfig.Type = "oneshot";
path = with pkgs; [ file kexec-tools coreutils-full cpio findutils gzip xz zstd lvm2 xxd gawk ];
# based on https://github.com/flowztul/keyexec
script = ''
system=/nix/var/nix/profiles/system
old_initrd=$(readlink -f "$system/initrd")
umask 0077
CRYPTROOT_TMPDIR="$(mktemp -d --tmpdir=/dev/shm)"
cleanup() {
shred -fu "$CRYPTROOT_TMPDIR/initrd_contents/etc/"*.key || true
shred -fu "$CRYPTROOT_TMPDIR/new_initrd" || true
shred -fu "$CRYPTROOT_TMPDIR/secret/"* || true
rm -rf "$CRYPTROOT_TMPDIR"
}
# trap cleanup INT TERM EXIT
mkdir -p "$CRYPTROOT_TMPDIR"
cd "$CRYPTROOT_TMPDIR"
# Determine the compression type of the initrd image
compression=$(file -b --mime-type "$old_initrd" | awk -F'/' '{print $2}')
# Decompress the initrd image based on its compression type
case "$compression" in
gzip)
gunzip -c "$old_initrd" > initrd.cpio
;;
xz)
unxz -c "$old_initrd" > initrd.cpio
;;
zstd)
zstd -d -c "$old_initrd" > initrd.cpio
;;
*)
echo "Unsupported compression type: $compression"
exit 1
;;
esac
# Extract the contents of the cpio archive
mkdir -p initrd_contents
cd initrd_contents
cpio -idv < ../initrd.cpio
# Generate keys and add them to the extracted initrd filesystem
luksDeviceNames=(${builtins.concatStringsSep " " config.luks.deviceNames})
for item in "''${luksDeviceNames[@]}"; do
dmsetup --showkeys table "$item" | cut -d ' ' -f5 | xxd -ps -g1 -r > "./etc/$item.key"
done
# Add normal initrd secrets too
${lib.concatStringsSep "\n" (lib.mapAttrsToList (dest: source:
let source' = if source == null then dest else builtins.toString source; in
''
mkdir -p $(dirname "./${dest}")
cp -a ${source'} "./${dest}"
''
) config.boot.initrd.secrets)
}
# Create a new cpio archive with the modified contents
find . | cpio -o -H newc -v > ../new_initrd.cpio
# Compress the new cpio archive using the original compression type
cd ..
case "$compression" in
gzip)
gunzip -c new_initrd.cpio > new_initrd
;;
xz)
unxz -c new_initrd.cpio > new_initrd
;;
zstd)
zstd -c new_initrd.cpio > new_initrd
;;
esac
kexec --load "$system/kernel" --append "init=$system/init ${builtins.concatStringsSep " " config.boot.kernelParams}" --initrd "$CRYPTROOT_TMPDIR/new_initrd"
'';
};
};
}

View File

@@ -1,74 +1,101 @@
# Makes it a little easier to configure luks partitions for boot
# Additionally, this solves a circular dependency between kexec luks
# and NixOS's luks module.
{ config, lib, ... }:
{ config, pkgs, lib, ... }:
let
cfg = config.luks;
deviceCount = builtins.length cfg.devices;
deviceMap = lib.imap
(i: item: {
device = item;
name =
if deviceCount == 1 then "enc-pv"
else "enc-pv${builtins.toString (i + 1)}";
})
cfg.devices;
in
{
in {
options.luks = {
devices = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
enable = lib.mkEnableOption "enable luks root remote decrypt over ssh/tor";
device = {
name = lib.mkOption {
type = lib.types.str;
default = "enc-pv";
};
path = lib.mkOption {
type = lib.types.either lib.types.str lib.types.path;
};
allowDiscards = lib.mkOption {
type = lib.types.bool;
default = true;
default = false;
};
fallbackToPassword = lib.mkEnableOption
"Fallback to interactive passphrase prompt if the cannot be found.";
disableKeyring = lib.mkEnableOption
"When opening LUKS2 devices, don't use the kernel keyring";
# set automatically, don't touch
deviceNames = lib.mkOption {
};
sshHostKeys = lib.mkOption {
type = lib.types.listOf (lib.types.either lib.types.str lib.types.path);
default = [
"/secret/ssh_host_rsa_key"
"/secret/ssh_host_ed25519_key"
];
};
sshAuthorizedKeys = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = config.users.users.googlebot.openssh.authorizedKeys.keys;
};
onionConfig = lib.mkOption {
type = lib.types.path;
default = /secret/onion;
};
kernelModules = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "e1000" "e1000e" "virtio_pci" "r8169" ];
};
};
config = lib.mkMerge [
{
assertions = [
{
assertion = deviceCount == builtins.length (builtins.attrNames config.boot.initrd.luks.devices);
message = ''
All luks devices must be specified using `luks.devices` not `boot.initrd.luks.devices`.
config = lib.mkIf cfg.enable {
boot.initrd.luks.devices.${cfg.device.name} = {
device = cfg.device.path;
allowDiscards = cfg.device.allowDiscards;
};
# Unlock LUKS disk over ssh
boot.initrd.network.enable = true;
boot.initrd.kernelModules = cfg.kernelModules;
boot.initrd.network.ssh = {
enable = true;
port = 22;
hostKeys = cfg.sshHostKeys;
authorizedKeys = cfg.sshAuthorizedKeys;
};
boot.initrd.postDeviceCommands = ''
echo 'waiting for root device to be opened...'
mkfifo /crypt-ramfs/passphrase
echo /crypt-ramfs/passphrase >> /dev/null
'';
}
];
}
(lib.mkIf (deviceCount != 0) {
luks.deviceNames = builtins.map (device: device.name) deviceMap;
boot.initrd.luks.devices = lib.listToAttrs (
builtins.map
(item:
{
name = item.name;
value = {
device = item.device;
allowDiscards = cfg.allowDiscards;
fallbackToPassword = cfg.fallbackToPassword;
disableKeyring = cfg.disableKeyring;
# Make machine accessable over tor for boot unlock
boot.initrd.secrets = {
"/etc/tor/onion/bootup" = cfg.onionConfig;
};
boot.initrd.extraUtilsCommands = ''
copy_bin_and_libs ${pkgs.tor}/bin/tor
copy_bin_and_libs ${pkgs.haveged}/bin/haveged
'';
# start tor during boot process
boot.initrd.network.postCommands = let
torRc = (pkgs.writeText "tor.rc" ''
DataDirectory /etc/tor
SOCKSPort 127.0.0.1:9050 IsolateDestAddr
SOCKSPort 127.0.0.1:9063
HiddenServiceDir /etc/tor/onion/bootup
HiddenServicePort 22 127.0.0.1:22
'');
in ''
# Add nice prompt for giving LUKS passphrase over ssh
echo 'read -s -p "Unlock Passphrase: " passphrase && echo $passphrase > /crypt-ramfs/passphrase && exit' >> /root/.profile
echo "tor: preparing onion folder"
# have to do this otherwise tor does not want to start
chmod -R 700 /etc/tor
echo "make sure localhost is up"
ip a a 127.0.0.1/8 dev lo
ip link set lo up
echo "haveged: starting haveged"
haveged -F &
echo "tor: starting tor"
tor -f ${torRc} --verify-config
tor -f ${torRc} &
'';
};
})
deviceMap);
})
];
}

View File

@@ -1,94 +0,0 @@
{ config, pkgs, lib, ... }:
let
cfg = config.remoteLuksUnlock;
in
{
options.remoteLuksUnlock = {
enable = lib.mkEnableOption "enable luks root remote decrypt over ssh/tor";
enableTorUnlock = lib.mkOption {
type = lib.types.bool;
default = cfg.enable;
description = "Make machine accessable over tor for ssh boot unlock";
};
sshHostKeys = lib.mkOption {
type = lib.types.listOf (lib.types.either lib.types.str lib.types.path);
default = [
"/secret/ssh_host_rsa_key"
"/secret/ssh_host_ed25519_key"
];
};
sshAuthorizedKeys = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = config.users.users.googlebot.openssh.authorizedKeys.keys;
};
onionConfig = lib.mkOption {
type = lib.types.path;
default = /secret/onion;
};
kernelModules = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "e1000" "e1000e" "virtio_pci" "r8169" ];
};
};
config = lib.mkIf cfg.enable {
# Unlock LUKS disk over ssh
boot.initrd.network.enable = true;
boot.initrd.kernelModules = cfg.kernelModules;
boot.initrd.network.ssh = {
enable = true;
port = 22;
hostKeys = cfg.sshHostKeys;
authorizedKeys = cfg.sshAuthorizedKeys;
};
boot.initrd.postDeviceCommands = ''
echo 'waiting for root device to be opened...'
mkfifo /crypt-ramfs/passphrase
echo /crypt-ramfs/passphrase >> /dev/null
'';
boot.initrd.secrets = lib.mkIf cfg.enableTorUnlock {
"/etc/tor/onion/bootup" = cfg.onionConfig;
};
boot.initrd.extraUtilsCommands = lib.mkIf cfg.enableTorUnlock ''
copy_bin_and_libs ${pkgs.tor}/bin/tor
copy_bin_and_libs ${pkgs.haveged}/bin/haveged
'';
boot.initrd.network.postCommands = lib.mkMerge [
(
''
# Add nice prompt for giving LUKS passphrase over ssh
echo 'read -s -p "Unlock Passphrase: " passphrase && echo $passphrase > /crypt-ramfs/passphrase && exit' >> /root/.profile
''
)
(
let torRc = (pkgs.writeText "tor.rc" ''
DataDirectory /etc/tor
SOCKSPort 127.0.0.1:9050 IsolateDestAddr
SOCKSPort 127.0.0.1:9063
HiddenServiceDir /etc/tor/onion/bootup
HiddenServicePort 22 127.0.0.1:22
''); in
lib.mkIf cfg.enableTorUnlock ''
echo "tor: preparing onion folder"
# have to do this otherwise tor does not want to start
chmod -R 700 /etc/tor
echo "make sure localhost is up"
ip a a 127.0.0.1/8 dev lo
ip link set lo up
echo "haveged: starting haveged"
haveged -F &
echo "tor: starting tor"
tor -f ${torRc} --verify-config
tor -f ${torRc} &
''
)
];
};
}

View File

@@ -2,7 +2,6 @@
{
imports = [
./backups.nix
./flakes.nix
./auto-update.nix
./shell.nix
@@ -10,8 +9,6 @@
./boot
./server
./pc
./machine-info
./ssh.nix
];
nix.flakes.enable = true;
@@ -23,23 +20,17 @@
networking.firewall.enable = true;
networking.firewall.allowPing = true;
time.timeZone = "America/Denver";
time.timeZone = "America/New_York";
i18n.defaultLocale = "en_US.UTF-8";
services.openssh = {
enable = true;
settings = {
PasswordAuthentication = false;
};
};
services.openssh.enable = true;
programs.mosh.enable = true;
environment.systemPackages = with pkgs; [
wget
kakoune
htop
git
git-lfs
git git-lfs
dnsutils
tmux
nethogs
@@ -51,8 +42,6 @@
micro
helix
lm_sensors
picocom
lf
];
nixpkgs.config.allowUnfree = true;
@@ -65,24 +54,11 @@
"dialout" # serial
];
shell = pkgs.fish;
openssh.authorizedKeys.keys = config.machines.ssh.userKeys;
openssh.authorizedKeys.keys = (import ./ssh.nix).users;
hashedPassword = "$6$TuDO46rILr$gkPUuLKZe3psexhs8WFZMpzgEBGksE.c3Tjh1f8sD0KMC4oV89K2pqAABfl.Lpxu2jVdr5bgvR5cWnZRnji/r/";
uid = 1000;
};
users.users.root = {
openssh.authorizedKeys.keys = config.machines.ssh.deployKeys;
};
nix.settings = {
trusted-users = [ "root" "googlebot" ];
};
# don't use sudo
security.doas.enable = true;
security.sudo.enable = false;
security.doas.extraRules = [
# don't ask for password every time
{ groups = [ "wheel" ]; persist = true; }
];
nix.trustedUsers = [ "root" "googlebot" ];
nix.gc.automatic = true;

View File

@@ -2,8 +2,7 @@
with lib;
let
cfg = config.nix.flakes;
in
{
in {
options.nix.flakes = {
enable = mkEnableOption "use nix flakes";
};

View File

@@ -1,200 +0,0 @@
# Gathers info about each machine to constuct overall configuration
# Ex: Each machine already trusts each others SSH fingerprint already
{ config, lib, pkgs, ... }:
let
machines = config.machines.hosts;
in
{
imports = [
./ssh.nix
./roles.nix
];
options.machines = {
hosts = lib.mkOption {
type = lib.types.attrsOf
(lib.types.submodule {
options = {
hostNames = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = ''
List of hostnames for this machine. The first one is the default so it is the target of deployments.
Used for automatically trusting hosts for ssh connections.
'';
};
arch = lib.mkOption {
type = lib.types.enum [ "x86_64-linux" "aarch64-linux" ];
description = ''
The architecture of this machine.
'';
};
systemRoles = lib.mkOption {
type = lib.types.listOf lib.types.str; # TODO: maybe use an enum?
description = ''
The set of roles this machine holds. Affects secrets available. (TODO add service config as well using this info)
'';
};
hostKey = lib.mkOption {
type = lib.types.str;
description = ''
The system ssh host key of this machine. Used for automatically trusting hosts for ssh connections
and for decrypting secrets with agenix.
'';
};
remoteUnlock = lib.mkOption {
default = null;
type = lib.types.nullOr (lib.types.submodule {
options = {
hostKey = lib.mkOption {
type = lib.types.str;
description = ''
The system ssh host key of this machine used for luks boot unlocking only.
'';
};
clearnetHost = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.str;
description = ''
The hostname resolvable over clearnet used to luks boot unlock this machine
'';
};
onionHost = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.str;
description = ''
The hostname resolvable over tor used to luks boot unlock this machine
'';
};
};
});
};
userKeys = lib.mkOption {
default = [ ];
type = lib.types.listOf lib.types.str;
description = ''
The list of user keys. Each key here can be used to log into all other systems as `googlebot`.
TODO: consider auto populating other programs that use ssh keys such as gitea
'';
};
deployKeys = lib.mkOption {
default = [ ];
type = lib.types.listOf lib.types.str;
description = ''
The list of deployment keys. Each key here can be used to log into all other systems as `root`.
'';
};
configurationPath = lib.mkOption {
type = lib.types.path;
description = ''
The path to this machine's configuration directory.
'';
};
};
});
};
};
config = {
assertions = (lib.concatLists (lib.mapAttrsToList
(
name: cfg: [
{
assertion = builtins.length cfg.hostNames > 0;
message = ''
Error with config for ${name}
There must be at least one hostname.
'';
}
{
assertion = builtins.length cfg.systemRoles > 0;
message = ''
Error with config for ${name}
There must be at least one system role.
'';
}
{
assertion = cfg.remoteUnlock == null || cfg.remoteUnlock.hostKey != cfg.hostKey;
message = ''
Error with config for ${name}
Unlock hostkey and hostkey cannot be the same because unlock hostkey is in /boot, unencrypted.
'';
}
{
assertion = cfg.remoteUnlock == null || (cfg.remoteUnlock.clearnetHost != null || cfg.remoteUnlock.onionHost != null);
message = ''
Error with config for ${name}
At least one of clearnet host or onion host must be defined.
'';
}
{
assertion = cfg.remoteUnlock == null || cfg.remoteUnlock.clearnetHost == null || builtins.elem cfg.remoteUnlock.clearnetHost cfg.hostNames == false;
message = ''
Error with config for ${name}
Clearnet unlock hostname cannot be in the list of hostnames for security reasons.
'';
}
{
assertion = cfg.remoteUnlock == null || cfg.remoteUnlock.onionHost == null || lib.strings.hasSuffix ".onion" cfg.remoteUnlock.onionHost;
message = ''
Error with config for ${name}
Tor unlock hostname must be an onion address.
'';
}
{
assertion = builtins.elem "personal" cfg.systemRoles || builtins.length cfg.userKeys == 0;
message = ''
Error with config for ${name}
There must be at least one userkey defined for personal machines.
'';
}
{
assertion = builtins.elem "deploy" cfg.systemRoles || builtins.length cfg.deployKeys == 0;
message = ''
Error with config for ${name}
Only deploy machines are allowed to have deploy keys for security reasons.
'';
}
]
)
machines));
# Set per machine properties automatically using each of their `properties.nix` files respectively
machines.hosts =
let
properties = dir: lib.concatMapAttrs
(name: path: {
${name} =
import path
//
{ configurationPath = builtins.dirOf path; };
})
(propertiesFiles dir);
propertiesFiles = dir:
lib.foldl (lib.mergeAttrs) { } (propertiesFiles' dir);
propertiesFiles' = dir:
let
propFiles = lib.filter (p: baseNameOf p == "properties.nix") (lib.filesystem.listFilesRecursive dir);
dirName = path: builtins.baseNameOf (builtins.dirOf path);
in
builtins.map (p: { "${dirName p}" = p; }) propFiles;
in
properties ../../machines;
};
}

View File

@@ -1,15 +0,0 @@
# Allows getting machine-info outside the scope of nixos configuration
{ nixpkgs ? import <nixpkgs> { }
, assertionsModule ? <nixpkgs/nixos/modules/misc/assertions.nix>
}:
{
machines =
(nixpkgs.lib.evalModules {
modules = [
./default.nix
assertionsModule
];
}).config.machines;
}

View File

@@ -1,19 +0,0 @@
{ config, lib, ... }:
# Maps roles to their hosts
{
options.machines.roles = lib.mkOption {
type = lib.types.attrsOf (lib.types.listOf lib.types.str);
};
config = {
machines.roles = lib.zipAttrs
(lib.mapAttrsToList
(host: cfg:
lib.foldl (lib.mergeAttrs) { }
(builtins.map (role: { ${role} = host; })
cfg.systemRoles))
config.machines.hosts);
};
}

View File

@@ -1,44 +0,0 @@
{ config, lib, ... }:
let
machines = config.machines;
sshkeys = keyType: lib.foldl (l: cfg: l ++ cfg.${keyType}) [ ] (builtins.attrValues machines.hosts);
in
{
options.machines.ssh = {
userKeys = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = ''
List of user keys aggregated from all machines.
'';
};
deployKeys = lib.mkOption {
default = [ ];
type = lib.types.listOf lib.types.str;
description = ''
List of deploy keys aggregated from all machines.
'';
};
hostKeysByRole = lib.mkOption {
type = lib.types.attrsOf (lib.types.listOf lib.types.str);
description = ''
Machine host keys divided into their roles.
'';
};
};
config = {
machines.ssh.userKeys = sshkeys "userKeys";
machines.ssh.deployKeys = sshkeys "deployKeys";
machines.ssh.hostKeysByRole = lib.mapAttrs
(role: hosts:
builtins.map
(host: machines.hosts.${host}.hostKey)
hosts)
machines.roles;
};
}

View File

@@ -7,11 +7,11 @@ let
in
{
imports = [
./hosts.nix
./pia-openvpn.nix
./pia-wireguard.nix
./ping.nix
./tailscale.nix
./vpn.nix
./zerotier.nix
];
options.networking.ip_forward = mkEnableOption "Enable ip forwarding";

63
common/network/hosts.nix Normal file
View File

@@ -0,0 +1,63 @@
{ config, lib, ... }:
let
system = (import ../ssh.nix).system;
in {
networking.hosts = {
# some DNS providers filter local ip results from DNS request
"172.30.145.180" = [ "s0.zt.neet.dev" ];
"172.30.109.9" = [ "ponyo.zt.neet.dev" ];
"172.30.189.212" = [ "ray.zt.neet.dev" ];
};
programs.ssh.knownHosts = {
liza = {
hostNames = [ "liza" "liza.neet.dev" ];
publicKey = system.liza;
};
ponyo = {
hostNames = [ "ponyo" "ponyo.neet.dev" "ponyo.zt.neet.dev" "git.neet.dev" ];
publicKey = system.ponyo;
};
ponyo-unlock = {
hostNames = [ "unlock.ponyo.neet.dev" "cfamr6artx75qvt7ho3rrbsc7mkucmv5aawebwflsfuorusayacffryd.onion" ];
publicKey = system.ponyo-unlock;
};
ray = {
hostNames = [ "ray" "ray.zt.neet.dev" ];
publicKey = system.ray;
};
s0 = {
hostNames = [ "s0" "s0.zt.neet.dev" ];
publicKey = system.s0;
};
n1 = {
hostNames = [ "n1" ];
publicKey = system.n1;
};
n2 = {
hostNames = [ "n2" ];
publicKey = system.n2;
};
n3 = {
hostNames = [ "n3" ];
publicKey = system.n3;
};
n4 = {
hostNames = [ "n4" ];
publicKey = system.n4;
};
n5 = {
hostNames = [ "n5" ];
publicKey = system.n5;
};
n6 = {
hostNames = [ "n6" ];
publicKey = system.n6;
};
n7 = {
hostNames = [ "n7" ];
publicKey = system.n7;
};
};
}

View File

@@ -1,7 +1,7 @@
{ config, pkgs, lib, ... }:
let
cfg = config.pia.openvpn;
cfg = config.pia;
vpnfailsafe = pkgs.stdenv.mkDerivation {
pname = "vpnfailsafe";
version = "0.0.1";
@@ -14,7 +14,7 @@ let
};
in
{
options.pia.openvpn = {
options.pia = {
enable = lib.mkEnableOption "Enable private internet access";
server = lib.mkOption {
type = lib.types.str;
@@ -108,6 +108,6 @@ in
};
};
};
age.secrets."pia-login.conf".file = ../../secrets/pia-login.age;
age.secrets."pia-login.conf".file = ../../secrets/pia-login.conf;
};
}

View File

@@ -1,357 +0,0 @@
{ config, lib, pkgs, ... }:
# Server list:
# https://serverlist.piaservers.net/vpninfo/servers/v6
# Reference materials:
# https://github.com/pia-foss/manual-connections
# https://github.com/thrnz/docker-wireguard-pia/blob/master/extra/wg-gen.sh
# TODO handle potential errors (or at least print status, success, and failures to the console)
# TODO parameterize names of systemd services so that multiple wg VPNs could coexist in theory easier
# TODO implement this module such that the wireguard VPN doesn't have to live in a container
# TODO don't add forward rules if the PIA port is the same as cfg.forwardedPort
# TODO verify signatures of PIA responses
with builtins;
with lib;
let
cfg = config.pia.wireguard;
getPIAToken = ''
PIA_USER=`sed '1q;d' /run/agenix/pia-login.conf`
PIA_PASS=`sed '2q;d' /run/agenix/pia-login.conf`
# PIA_TOKEN only lasts 24hrs
PIA_TOKEN=`curl -s -u "$PIA_USER:$PIA_PASS" https://www.privateinternetaccess.com/gtoken/generateToken | jq -r '.token'`
'';
chooseWireguardServer = ''
servers=$(mktemp)
servers_json=$(mktemp)
curl -s "https://serverlist.piaservers.net/vpninfo/servers/v6" > "$servers"
# extract json part only
head -n 1 "$servers" | tr -d '\n' > "$servers_json"
echo "Available location ids:" && jq '.regions | .[] | {name, id, port_forward}' "$servers_json"
# Some locations have multiple servers available. Pick a random one.
totalservers=$(jq -r '.regions | .[] | select(.id=="'${cfg.serverLocation}'") | .servers.wg | length' "$servers_json")
if ! [[ "$totalservers" =~ ^[0-9]+$ ]] || [ "$totalservers" -eq 0 ] 2>/dev/null; then
echo "Location \"${cfg.serverLocation}\" not found."
exit 1
fi
serverindex=$(( RANDOM % totalservers))
WG_HOSTNAME=$(jq -r '.regions | .[] | select(.id=="'${cfg.serverLocation}'") | .servers.wg | .['$serverindex'].cn' "$servers_json")
WG_SERVER_IP=$(jq -r '.regions | .[] | select(.id=="'${cfg.serverLocation}'") | .servers.wg | .['$serverindex'].ip' "$servers_json")
WG_SERVER_PORT=$(jq -r '.groups.wg | .[0] | .ports | .[0]' "$servers_json")
# write chosen server
rm -f /tmp/${cfg.interfaceName}-server.conf
touch /tmp/${cfg.interfaceName}-server.conf
chmod 700 /tmp/${cfg.interfaceName}-server.conf
echo "$WG_HOSTNAME" >> /tmp/${cfg.interfaceName}-server.conf
echo "$WG_SERVER_IP" >> /tmp/${cfg.interfaceName}-server.conf
echo "$WG_SERVER_PORT" >> /tmp/${cfg.interfaceName}-server.conf
rm $servers_json $servers
'';
getChosenWireguardServer = ''
WG_HOSTNAME=`sed '1q;d' /tmp/${cfg.interfaceName}-server.conf`
WG_SERVER_IP=`sed '2q;d' /tmp/${cfg.interfaceName}-server.conf`
WG_SERVER_PORT=`sed '3q;d' /tmp/${cfg.interfaceName}-server.conf`
'';
refreshPIAPort = ''
${getChosenWireguardServer}
signature=`sed '1q;d' /tmp/${cfg.interfaceName}-port-renewal`
payload=`sed '2q;d' /tmp/${cfg.interfaceName}-port-renewal`
bind_port_response=`curl -Gs -m 5 --connect-to "$WG_HOSTNAME::$WG_SERVER_IP:" --cacert "${./ca.rsa.4096.crt}" --data-urlencode "payload=$payload" --data-urlencode "signature=$signature" "https://$WG_HOSTNAME:19999/bindPort"`
'';
portForwarding = cfg.forwardPortForTransmission || cfg.forwardedPort != null;
containerServiceName = "container@${config.vpn-container.containerName}.service";
in
{
options.pia.wireguard = {
enable = mkEnableOption "Enable private internet access";
badPortForwardPorts = mkOption {
type = types.listOf types.port;
description = ''
Ports that will not be accepted from PIA.
If PIA assigns a port from this list, the connection is aborted since we cannot ask for a different port.
This is used to guarantee we are not assigned a port that is used by a service we do not want exposed.
'';
};
wireguardListenPort = mkOption {
type = types.port;
description = "The port wireguard listens on for this VPN connection";
default = 51820;
};
serverLocation = mkOption {
type = types.str;
default = "swiss";
};
interfaceName = mkOption {
type = types.str;
default = "piaw";
};
forwardedPort = mkOption {
type = types.nullOr types.port;
description = "The port to redirect port forwarded TCP VPN traffic too";
default = null;
};
forwardPortForTransmission = mkEnableOption "PIA port forwarding for transmission should be performed.";
};
config = mkIf cfg.enable {
assertions = [
{
assertion = cfg.forwardPortForTransmission != (cfg.forwardedPort != null);
message = ''
The PIA forwarded port cannot simultaneously be used by transmission and redirected to another port.
'';
}
];
# mounts used to pass the connection parameters to the container
# the container doesn't have internet until it uses these parameters so it cannot fetch them itself
vpn-container.mounts = [
"/tmp/${cfg.interfaceName}.conf"
"/tmp/${cfg.interfaceName}-server.conf"
"/tmp/${cfg.interfaceName}-address.conf"
];
# The container takes ownership of the wireguard interface on its startup
containers.vpn.interfaces = [ cfg.interfaceName ];
# TODO: while this is much better than "loose" networking, it seems to have issues with firewall restarts
# allow traffic for wireguard interface to pass since wireguard trips up rpfilter
# networking.firewall = {
# extraCommands = ''
# ip46tables -t raw -I nixos-fw-rpfilter -p udp -m udp --sport ${toString cfg.wireguardListenPort} -j RETURN
# ip46tables -t raw -I nixos-fw-rpfilter -p udp -m udp --dport ${toString cfg.wireguardListenPort} -j RETURN
# '';
# extraStopCommands = ''
# ip46tables -t raw -D nixos-fw-rpfilter -p udp -m udp --sport ${toString cfg.wireguardListenPort} -j RETURN || true
# ip46tables -t raw -D nixos-fw-rpfilter -p udp -m udp --dport ${toString cfg.wireguardListenPort} -j RETURN || true
# '';
# };
networking.firewall.checkReversePath = "loose";
systemd.services.pia-vpn-wireguard-init = {
description = "Creates PIA VPN Wireguard Interface";
requires = [ "network-online.target" ];
after = [ "network.target" "network-online.target" ];
before = [ containerServiceName ];
requiredBy = [ containerServiceName ];
partOf = [ containerServiceName ];
wantedBy = [ "multi-user.target" ];
path = with pkgs; [ wireguard-tools jq curl iproute ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
# restart once a month; PIA forwarded port expires after two months
# because the container is "PartOf" this unit, it gets restarted too
RuntimeMaxSec = "30d";
};
script = ''
# Prepare to connect by generating wg secrets and auth'ing with PIA since the container
# cannot do without internet to start with. NAT'ing the host's internet would address this
# issue but is not ideal because then leaking network outside of the VPN is more likely.
${chooseWireguardServer}
${getPIAToken}
# generate wireguard keys
privKey=$(wg genkey)
pubKey=$(echo "$privKey" | wg pubkey)
# authorize our WG keys with the PIA server we are about to connect to
wireguard_json=`curl -s -G --connect-to "$WG_HOSTNAME::$WG_SERVER_IP:" --cacert "${./ca.rsa.4096.crt}" --data-urlencode "pt=$PIA_TOKEN" --data-urlencode "pubkey=$pubKey" https://$WG_HOSTNAME:$WG_SERVER_PORT/addKey`
# create wg-quick config file
rm -f /tmp/${cfg.interfaceName}.conf /tmp/${cfg.interfaceName}-address.conf
touch /tmp/${cfg.interfaceName}.conf /tmp/${cfg.interfaceName}-address.conf
chmod 700 /tmp/${cfg.interfaceName}.conf /tmp/${cfg.interfaceName}-address.conf
echo "
[Interface]
# Address = $(echo "$wireguard_json" | jq -r '.peer_ip')
PrivateKey = $privKey
ListenPort = ${toString cfg.wireguardListenPort}
[Peer]
PersistentKeepalive = 25
PublicKey = $(echo "$wireguard_json" | jq -r '.server_key')
AllowedIPs = 0.0.0.0/0
Endpoint = $WG_SERVER_IP:$(echo "$wireguard_json" | jq -r '.server_port')
" >> /tmp/${cfg.interfaceName}.conf
# create file storing the VPN ip address PIA assigned to us
echo "$wireguard_json" | jq -r '.peer_ip' >> /tmp/${cfg.interfaceName}-address.conf
# Create wg interface now so it inherits from the namespace with internet access
# the container will handle actually connecting the interface since that info is
# not preserved upon moving into the container's networking namespace
# Roughly following this guide https://www.wireguard.com/netns/#ordinary-containerization
[[ -z $(ip link show dev ${cfg.interfaceName} 2>/dev/null) ]] || exit
ip link add ${cfg.interfaceName} type wireguard
'';
preStop = ''
# cleanup wireguard interface
ip link del ${cfg.interfaceName}
rm -f /tmp/${cfg.interfaceName}.conf /tmp/${cfg.interfaceName}-address.conf
'';
};
vpn-container.config.systemd.services.pia-vpn-wireguard = {
description = "Initializes the PIA VPN WireGuard Tunnel";
requires = [ "network-online.target" ];
after = [ "network.target" "network-online.target" ];
wantedBy = [ "multi-user.target" ];
path = with pkgs; [ wireguard-tools iproute curl jq iptables ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
# pseudo calls wg-quick
# Near equivalent of "wg-quick up /tmp/${cfg.interfaceName}.conf"
# cannot actually call wg-quick because the interface has to be already
# created before the container taken ownership of the interface
# Thus, assumes wg interface was already created:
# ip link add ${cfg.interfaceName} type wireguard
${getChosenWireguardServer}
myaddress=`cat /tmp/${cfg.interfaceName}-address.conf`
wg setconf ${cfg.interfaceName} /tmp/${cfg.interfaceName}.conf
ip -4 address add $myaddress dev ${cfg.interfaceName}
ip link set mtu 1420 up dev ${cfg.interfaceName}
wg set ${cfg.interfaceName} fwmark ${toString cfg.wireguardListenPort}
ip -4 route add 0.0.0.0/0 dev ${cfg.interfaceName} table ${toString cfg.wireguardListenPort}
# TODO is this needed?
ip -4 rule add not fwmark ${toString cfg.wireguardListenPort} table ${toString cfg.wireguardListenPort}
ip -4 rule add table main suppress_prefixlength 0
# The rest of the script is only for only for port forwarding skip if not needed
if [ ${boolToString portForwarding} == false ]; then exit 0; fi
# Reserve port
${getPIAToken}
payload_and_signature=`curl -s -m 5 --connect-to "$WG_HOSTNAME::$WG_SERVER_IP:" --cacert "${./ca.rsa.4096.crt}" -G --data-urlencode "token=$PIA_TOKEN" "https://$WG_HOSTNAME:19999/getSignature"`
signature=$(echo "$payload_and_signature" | jq -r '.signature')
payload=$(echo "$payload_and_signature" | jq -r '.payload')
port=$(echo "$payload" | base64 -d | jq -r '.port')
# Check if the port is acceptable
notallowed=(${concatStringsSep " " (map toString cfg.badPortForwardPorts)})
if [[ " ''${notallowed[*]} " =~ " $port " ]]; then
# the port PIA assigned is not allowed, kill the connection
wg-quick down /tmp/${cfg.interfaceName}.conf
exit 1
fi
# write reserved port to file readable for all users
echo $port > /tmp/${cfg.interfaceName}-port
chmod 644 /tmp/${cfg.interfaceName}-port
# write payload and signature info needed to allow refreshing allocated forwarded port
rm -f /tmp/${cfg.interfaceName}-port-renewal
touch /tmp/${cfg.interfaceName}-port-renewal
chmod 700 /tmp/${cfg.interfaceName}-port-renewal
echo $signature >> /tmp/${cfg.interfaceName}-port-renewal
echo $payload >> /tmp/${cfg.interfaceName}-port-renewal
# Block all traffic from VPN interface except for traffic that is from the forwarded port
iptables -I nixos-fw -p tcp --dport $port -j nixos-fw-accept -i ${cfg.interfaceName}
iptables -I nixos-fw -p udp --dport $port -j nixos-fw-accept -i ${cfg.interfaceName}
# The first port refresh triggers the port to be actually allocated
${refreshPIAPort}
${optionalString (cfg.forwardedPort != null) ''
# redirect the fowarded port
iptables -A INPUT -i ${cfg.interfaceName} -p tcp --dport $port -j ACCEPT
iptables -A INPUT -i ${cfg.interfaceName} -p udp --dport $port -j ACCEPT
iptables -A INPUT -i ${cfg.interfaceName} -p tcp --dport ${toString cfg.forwardedPort} -j ACCEPT
iptables -A INPUT -i ${cfg.interfaceName} -p udp --dport ${toString cfg.forwardedPort} -j ACCEPT
iptables -A PREROUTING -t nat -i ${cfg.interfaceName} -p tcp --dport $port -j REDIRECT --to-port ${toString cfg.forwardedPort}
iptables -A PREROUTING -t nat -i ${cfg.interfaceName} -p udp --dport $port -j REDIRECT --to-port ${toString cfg.forwardedPort}
''}
${optionalString cfg.forwardPortForTransmission ''
# assumes no auth needed for transmission
curlout=$(curl localhost:9091/transmission/rpc 2>/dev/null)
regex='X-Transmission-Session-Id\: (\w*)'
if [[ $curlout =~ $regex ]]; then
sessionId=''${BASH_REMATCH[1]}
else
exit 1
fi
# set the port in transmission
data='{"method": "session-set", "arguments": { "peer-port" :'$port' } }'
curl http://localhost:9091/transmission/rpc -d "$data" -H "X-Transmission-Session-Id: $sessionId"
''}
'';
preStop = ''
wg-quick down /tmp/${cfg.interfaceName}.conf
# The rest of the script is only for only for port forwarding skip if not needed
if [ ${boolToString portForwarding} == false ]; then exit 0; fi
${optionalString (cfg.forwardedPort != null) ''
# stop redirecting the forwarded port
iptables -D INPUT -i ${cfg.interfaceName} -p tcp --dport $port -j ACCEPT
iptables -D INPUT -i ${cfg.interfaceName} -p udp --dport $port -j ACCEPT
iptables -D INPUT -i ${cfg.interfaceName} -p tcp --dport ${toString cfg.forwardedPort} -j ACCEPT
iptables -D INPUT -i ${cfg.interfaceName} -p udp --dport ${toString cfg.forwardedPort} -j ACCEPT
iptables -D PREROUTING -t nat -i ${cfg.interfaceName} -p tcp --dport $port -j REDIRECT --to-port ${toString cfg.forwardedPort}
iptables -D PREROUTING -t nat -i ${cfg.interfaceName} -p udp --dport $port -j REDIRECT --to-port ${toString cfg.forwardedPort}
''}
'';
};
vpn-container.config.systemd.services.pia-vpn-wireguard-forward-port = {
enable = portForwarding;
description = "PIA VPN WireGuard Tunnel Port Forwarding";
after = [ "pia-vpn-wireguard.service" ];
requires = [ "pia-vpn-wireguard.service" ];
path = with pkgs; [ curl ];
serviceConfig = {
Type = "oneshot";
};
script = refreshPIAPort;
};
vpn-container.config.systemd.timers.pia-vpn-wireguard-forward-port = {
enable = portForwarding;
partOf = [ "pia-vpn-wireguard-forward-port.service" ];
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "*:0/10"; # 10 minutes
RandomizedDelaySec = "1m"; # vary by 1 min to give PIA servers some relief
};
};
age.secrets."pia-login.conf".file = ../../secrets/pia-login.age;
};
}

View File

@@ -1,59 +0,0 @@
{ config, pkgs, lib, ... }:
# keeps peer to peer connections alive with a periodic ping
with lib;
with builtins;
# todo auto restart
let
cfg = config.keepalive-ping;
serviceTemplate = host:
{
"keepalive-ping@${host}" = {
description = "Periodic ping keep alive for ${host} connection";
requires = [ "network-online.target" ];
after = [ "network.target" "network-online.target" ];
wantedBy = [ "multi-user.target" ];
serviceConfig.Restart = "always";
path = with pkgs; [ iputils ];
script = ''
ping -i ${cfg.delay} ${host} &>/dev/null
'';
};
};
combineAttrs = foldl recursiveUpdate { };
serviceList = map serviceTemplate cfg.hosts;
services = combineAttrs serviceList;
in
{
options.keepalive-ping = {
enable = mkEnableOption "Enable keep alive ping task";
hosts = mkOption {
type = types.listOf types.str;
default = [ ];
description = ''
Hosts to ping periodically
'';
};
delay = mkOption {
type = types.str;
default = "60";
description = ''
Ping interval in seconds of periodic ping per host being pinged
'';
};
};
config = mkIf cfg.enable {
systemd.services = services;
};
}

View File

@@ -8,11 +8,7 @@ in
{
options.services.tailscale.exitNode = mkEnableOption "Enable exit node support";
config.services.tailscale.enable = mkDefault (!config.boot.isContainer);
# MagicDNS
config.networking.nameservers = mkIf cfg.enable [ "1.1.1.1" "8.8.8.8" ];
config.networking.search = mkIf cfg.enable [ "koi-bebop.ts.net" ];
config.services.tailscale.enable = !config.boot.isContainer;
# exit node
config.networking.firewall.checkReversePath = mkIf cfg.exitNode "loose";

View File

@@ -26,8 +26,6 @@ in
'';
};
useOpenVPN = mkEnableOption "Uses OpenVPN instead of wireguard for PIA VPN connection";
config = mkOption {
type = types.anything;
default = {};
@@ -43,9 +41,6 @@ in
};
config = mkIf cfg.enable {
pia.wireguard.enable = !cfg.useOpenVPN;
pia.wireguard.forwardPortForTransmission = !cfg.useOpenVPN;
containers.${cfg.containerName} = {
ephemeral = true;
autoStart = true;
@@ -64,7 +59,7 @@ in
}
)));
enableTun = cfg.useOpenVPN;
enableTun = true;
privateNetwork = true;
hostAddress = "172.16.100.1";
localAddress = "172.16.100.2";
@@ -72,35 +67,28 @@ in
config = {
imports = allModules ++ [cfg.config];
# speeds up evaluation
nixpkgs.pkgs = pkgs;
# networking.firewall.enable = mkForce false;
networking.firewall.trustedInterfaces = [
# completely trust internal interface to host
"eth0"
];
networking.firewall.enable = mkForce false;
pia.openvpn.enable = cfg.useOpenVPN;
pia.openvpn.server = "swiss.privacy.network"; # swiss vpn
pia.enable = true;
pia.server = "swiss.privacy.network"; # swiss vpn
# TODO fix so it does run it's own resolver again
# run it's own DNS resolver
networking.useHostResolvConf = false;
# services.resolved.enable = true;
networking.nameservers = [ "1.1.1.1" "8.8.8.8" ];
services.resolved.enable = true;
};
};
# load secrets the container needs
age.secrets = config.containers.${cfg.containerName}.config.age.secrets;
# forwarding for vpn container (only for OpenVPN)
networking.nat.enable = mkIf cfg.useOpenVPN true;
networking.nat.internalInterfaces = mkIf cfg.useOpenVPN [
# forwarding for vpn container
networking.nat.enable = true;
networking.nat.internalInterfaces = [
"ve-${cfg.containerName}"
];
networking.ip_forward = mkIf cfg.useOpenVPN true;
networking.ip_forward = true;
# assumes only one potential interface
networking.usePredictableInterfaceNames = false;

View File

@@ -0,0 +1,14 @@
{ lib, config, ... }:
let
cfg = config.services.zerotierone;
in {
config = lib.mkIf cfg.enable {
services.zerotierone.joinNetworks = [
"565799d8f6d654c0"
];
networking.firewall.allowedUDPPorts = [
9993
];
};
}

View File

@@ -2,8 +2,7 @@
let
cfg = config.de;
in
{
in {
config = lib.mkIf cfg.enable {
# enable pulseaudio support for packages
nixpkgs.config.pulseaudio = true;
@@ -17,6 +16,45 @@ in
alsa.support32Bit = true;
pulse.enable = true;
jack.enable = true;
# use the example session manager (no others are packaged yet so this is enabled by default,
# no need to redefine it in your config for now)
#media-session.enable = true;
config.pipewire = {
"context.objects" = [
{
# A default dummy driver. This handles nodes marked with the "node.always-driver"
# properyty when no other driver is currently active. JACK clients need this.
factory = "spa-node-factory";
args = {
"factory.name" = "support.node.driver";
"node.name" = "Dummy-Driver";
"priority.driver" = 8000;
};
}
{
factory = "adapter";
args = {
"factory.name" = "support.null-audio-sink";
"node.name" = "Microphone-Proxy";
"node.description" = "Microphone";
"media.class" = "Audio/Source/Virtual";
"audio.position" = "MONO";
};
}
{
factory = "adapter";
args = {
"factory.name" = "support.null-audio-sink";
"node.name" = "Main-Output-Proxy";
"node.description" = "Main Output";
"media.class" = "Audio/Sink";
"audio.position" = "FL,FR";
};
}
];
};
};
users.users.googlebot.extraGroups = [ "audio" ];

View File

@@ -49,8 +49,7 @@ let
];
};
in
{
in {
config = lib.mkIf cfg.enable {
# chromium with specific extensions + settings
programs.chromium = {
@@ -61,6 +60,7 @@ in
"oboonakemofpalcgghocfoadofidjkkk" # keepassxc plugin
"cimiefiiaegbelhefglklhhakcgmhkai" # plasma integration
"hkgfoiooedgoejojocmhlaklaeopbecg" # picture in picture
"fihnjjcciajhdojfnbdddfaoknhalnja" # I don't care about cookies
"mnjggcdmjocbbbhaepdhchncahnbgone" # SponsorBlock
"dhdgffkkebhmkfjojejmpbldmpobfkfo" # Tampermonkey
# "ehpdicggenhgapiikfpnmppdonadlnmp" # Disable Scroll Jacking
@@ -80,7 +80,6 @@ in
nixpkgs.config.packageOverrides = pkgs: {
vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; };
chromium = pkgs.chromium.override {
enableWideVine = true;
# ungoogled = true;
# --enable-native-gpu-memory-buffers # fails on AMD APU
# --enable-webrtc-vp9-support
@@ -90,7 +89,7 @@ in
# todo vulkan in chrome
# todo video encoding in chrome
hardware.opengl = {
enable = true;
enable = cfg.enableAcceleration;
extraPackages = with pkgs; [
intel-media-driver # LIBVA_DRIVER_NAME=iHD
vaapiIntel # LIBVA_DRIVER_NAME=i965 (older but works better for Firefox/Chromium)

View File

@@ -2,8 +2,7 @@
let
cfg = config.de;
in
{
in {
imports = [
./kde.nix
./xfce.nix
@@ -15,20 +14,25 @@ in
./pithos.nix
./spotify.nix
./vscodium.nix
./discord.nix
./steam.nix
# FIXME make optional
# ./discord.nix
# ./steam.nix
./touchpad.nix
./mount-samba.nix
];
options.de = {
enable = lib.mkEnableOption "enable desktop environment";
enableAcceleration = lib.mkOption {
type = lib.types.bool;
default = true;
};
};
config = lib.mkIf cfg.enable {
# vulkan
hardware.opengl.driSupport = true;
hardware.opengl.driSupport32Bit = true;
hardware.opengl.driSupport = cfg.enableAcceleration;
hardware.opengl.driSupport32Bit = cfg.enableAcceleration;
# Applications
users.users.googlebot.packages = with pkgs; [
@@ -41,8 +45,7 @@ in
element-desktop
mpv
nextcloud-client
signal-desktop
minecraft
# signal-desktop # FIXME
gparted
libreoffice-fresh
thunderbird
@@ -51,12 +54,6 @@ in
arduino
yt-dlp
jellyfin-media-player
joplin-desktop
config.inputs.deploy-rs.packages.${config.currentSystem}.deploy-rs
# For Nix IDE
nixpkgs-fmt
rnix-lsp
];
# Networking

View File

@@ -2,8 +2,7 @@
let
cfg = config.de;
in
{
in {
config = lib.mkIf cfg.enable {
users.users.googlebot.packages = [
pkgs.discord

View File

@@ -2,8 +2,7 @@
let
cfg = config.de;
in
{
in {
config = lib.mkIf cfg.enable {
# kde plasma
services.xserver = {

View File

@@ -1,48 +1,36 @@
# mounts the samba share on s0 over tailscale
# mounts the samba share on s0 over zeroteir
{ config, lib, pkgs, ... }:
{ config, lib, ... }:
let
cfg = config.services.mount-samba;
# prevents hanging on network split and other similar niceties to ensure a stable connection
network_opts = "nostrictsync,cache=strict,handlecache,handletimeout=30000,rwpidforward,mapposix,soft,resilienthandles,echo_interval=10,noblocksend,fsc";
# prevents hanging on network split
network_opts = "x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s,nostrictsync,cache=loose,handlecache,handletimeout=30000,rwpidforward,mapposix,soft,resilienthandles,echo_interval=10,noblocksend";
systemd_opts = "x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s";
user_opts = "uid=${toString config.users.users.googlebot.uid},file_mode=0660,dir_mode=0770,user";
auth_opts = "sec=ntlmv2i,credentials=/run/agenix/smb-secrets";
version_opts = "vers=3.1.1";
auth_opts = "credentials=/run/agenix/smb-secrets";
version_opts = "vers=2.1";
opts = "${systemd_opts},${network_opts},${user_opts},${version_opts},${auth_opts}";
in
{
opts = "${network_opts},${user_opts},${version_opts},${auth_opts}";
in {
options.services.mount-samba = {
enable = lib.mkEnableOption "enable mounting samba shares";
};
config = lib.mkIf (cfg.enable && config.services.tailscale.enable) {
config = lib.mkIf (cfg.enable && config.services.zerotierone.enable) {
fileSystems."/mnt/public" = {
device = "//s0.koi-bebop.ts.net/public";
device = "//s0.zt.neet.dev/public";
fsType = "cifs";
options = [ opts ];
};
fileSystems."/mnt/private" = {
device = "//s0.koi-bebop.ts.net/googlebot";
device = "//s0.zt.neet.dev/googlebot";
fsType = "cifs";
options = [ opts ];
};
age.secrets.smb-secrets.file = ../../secrets/smb-secrets.age;
environment.shellAliases = {
# remount storage
remount_public = "sudo systemctl restart mnt-public.mount";
remount_private = "sudo systemctl restart mnt-private.mount";
# Encrypted Vault
vault_unlock = "${pkgs.gocryptfs}/bin/gocryptfs /mnt/private/.vault/ /mnt/vault/";
vault_lock = "umount /mnt/vault/";
};
};
}

View File

@@ -2,8 +2,7 @@
let
cfg = config.de;
in
{
in {
config = lib.mkIf cfg.enable {
nixpkgs.overlays = [
(self: super: {

View File

@@ -2,8 +2,7 @@
let
cfg = config.de;
in
{
in {
config = lib.mkIf cfg.enable {
programs.steam.enable = true;
hardware.steam-hardware.enable = true; # steam controller

View File

@@ -2,8 +2,7 @@
let
cfg = config.de;
in
{
in {
config = lib.mkIf cfg.enable {
nixpkgs.overlays = [
(self: super: {

View File

@@ -2,8 +2,7 @@
let
cfg = config.de.touchpad;
in
{
in {
options.de.touchpad = {
enable = lib.mkEnableOption "enable touchpad";
};

View File

@@ -2,8 +2,7 @@
let
cfg = config.de;
in
{
in {
config = lib.mkIf cfg.enable {
services.xserver = {
enable = true;

View File

@@ -2,8 +2,7 @@
let
cfg = config.de;
in
{
in {
config = lib.mkIf cfg.enable {
# yubikey
services.pcscd.enable = true;

View File

@@ -3,9 +3,9 @@
with lib;
let
cfg = config.ceph;
in
{
options.ceph = { };
in {
options.ceph = {
};
config = mkIf cfg.enable {
# ceph.enable = true;

View File

@@ -0,0 +1,58 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.cloudflared;
settingsFormat = pkgs.formats.yaml { };
in
{
meta.maintainers = with maintainers; [ pmc ];
options = {
services.cloudflared = {
enable = mkEnableOption "cloudflared";
package = mkOption {
type = types.package;
default = pkgs.cloudflared;
description = "The cloudflared package to use";
example = literalExpression ''pkgs.cloudflared'';
};
config = mkOption {
type = settingsFormat.type;
description = "Contents of the config.yaml as an attrset; see https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/configuration-file for documentation on the contents";
example = literalExpression ''
{
url = "http://localhost:3000";
tunnel = "505c8dd1-e4fb-4ea4-b909-26b8f61ceaaf";
credentials-file = "/var/lib/cloudflared/505c8dd1-e4fb-4ea4-b909-26b8f61ceaaf.json";
}
'';
};
configFile = mkOption {
type = types.path;
description = "Path to cloudflared config.yaml.";
example = literalExpression ''"/etc/cloudflared/config.yaml"'';
};
};
};
config = mkIf cfg.enable ({
# Prefer the config file over settings if both are set.
services.cloudflared.configFile = mkDefault (settingsFormat.generate "cloudflared.yaml" cfg.config);
systemd.services.cloudflared = {
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
description = "Cloudflare Argo Tunnel";
serviceConfig = {
TimeoutStartSec = 0;
Type = "notify";
ExecStart = "${cfg.package}/bin/cloudflared --config ${cfg.configFile} --no-autoupdate tunnel run";
Restart = "on-failure";
RestartSec = "5s";
};
};
});
}

View File

@@ -10,14 +10,10 @@
./matrix.nix
./zerobin.nix
./gitea.nix
./gitea-runner.nix
./privatebin/privatebin.nix
./radio.nix
./samba.nix
./cloudflared.nix
./owncast.nix
./mailserver.nix
./nextcloud.nix
./iodine.nix
./searx.nix
];
}

View File

@@ -1,98 +0,0 @@
{ config, pkgs, lib, ... }:
let
cfg = config.services.gitea-runner;
in
{
options.services.gitea-runner = {
enable = lib.mkEnableOption "Enables gitea runner";
dataDir = lib.mkOption {
default = "/var/lib/gitea-runner";
type = lib.types.str;
description = lib.mdDoc "gitea runner data directory.";
};
instanceUrl = lib.mkOption {
type = lib.types.str;
};
registrationTokenFile = lib.mkOption {
type = lib.types.path;
};
};
config = lib.mkIf cfg.enable {
virtualisation.docker.enable = true;
users.users.gitea-runner = {
description = "Gitea Runner Service";
home = cfg.dataDir;
useDefaultShell = true;
group = "gitea-runner";
isSystemUser = true;
createHome = true;
extraGroups = [
"docker" # allow creating docker containers
];
};
users.groups.gitea-runner = { };
# registration token
services.gitea-runner.registrationTokenFile = "/run/agenix/gitea-runner-registration-token";
age.secrets.gitea-runner-registration-token = {
file = ../../secrets/gitea-runner-registration-token.age;
owner = "gitea-runner";
};
systemd.services.gitea-runner = {
description = "Gitea Runner";
serviceConfig = {
WorkingDirectory = cfg.dataDir;
User = "gitea-runner";
Group = "gitea-runner";
};
requires = [ "network-online.target" ];
after = [ "network.target" "network-online.target" ];
wantedBy = [ "multi-user.target" ];
path = with pkgs; [ gitea-actions-runner ];
# based on https://gitea.com/gitea/act_runner/src/branch/main/run.sh
script = ''
. ${cfg.registrationTokenFile}
if [[ ! -s .runner ]]; then
try=$((try + 1))
success=0
LOGFILE="$(mktemp)"
# The point of this loop is to make it simple, when running both act_runner and gitea in docker,
# for the act_runner to wait a moment for gitea to become available before erroring out. Within
# the context of a single docker-compose, something similar could be done via healthchecks, but
# this is more flexible.
while [[ $success -eq 0 ]] && [[ $try -lt ''${10:-10} ]]; do
act_runner register \
--instance "${cfg.instanceUrl}" \
--token "$GITEA_RUNNER_REGISTRATION_TOKEN" \
--name "${config.networking.hostName}" \
--no-interactive > $LOGFILE 2>&1
cat $LOGFILE
cat $LOGFILE | grep 'Runner registered successfully' > /dev/null
if [[ $? -eq 0 ]]; then
echo "SUCCESS"
success=1
else
echo "Waiting to retry ..."
sleep 5
fi
done
fi
exec act_runner daemon
'';
};
};
}

View File

@@ -1,9 +1,8 @@
{ lib, pkgs, config, ... }:
{ lib, config, ... }:
let
cfg = config.services.gitea;
in
{
in {
options.services.gitea = {
hostname = lib.mkOption {
type = lib.types.str;
@@ -15,8 +14,11 @@ in
domain = cfg.hostname;
rootUrl = "https://${cfg.hostname}/";
appName = cfg.hostname;
ssh.enable = true;
# lfs.enable = true;
# dump.enable = true;
dump.enable = true;
cookieSecure = true;
disableRegistration = true;
settings = {
other = {
SHOW_FOOTER_VERSION = false;
@@ -24,37 +26,8 @@ in
ui = {
DEFAULT_THEME = "arc-green";
};
service = {
DISABLE_REGISTRATION = true;
};
session = {
COOKIE_SECURE = true;
};
mailer = {
ENABLED = true;
MAILER_TYPE = "smtp";
SMTP_ADDR = "mail.neet.dev";
SMTP_PORT = "465";
IS_TLS_ENABLED = true;
USER = "robot@runyan.org";
FROM = "no-reply@neet.dev";
};
actions = {
ENABLED = true;
};
};
mailerPasswordFile = "/run/agenix/robots-email-pw";
};
age.secrets.robots-email-pw = {
file = ../../secrets/robots-email-pw.age;
owner = config.services.gitea.user;
};
# backups
backup.group."gitea".paths = [
config.services.gitea.stateDir
];
services.nginx.enable = true;
services.nginx.virtualHosts.${cfg.hostname} = {
enableACME = true;

View File

@@ -7,8 +7,7 @@
let
cfg = config.services.icecast;
in
{
in {
options.services.icecast = {
mount = lib.mkOption {
type = lib.types.str;

View File

@@ -1,21 +0,0 @@
{ config, pkgs, lib, ... }:
let
cfg = config.services.iodine.server;
in
{
config = lib.mkIf cfg.enable {
# iodine DNS-based vpn
services.iodine.server = {
ip = "192.168.99.1";
domain = "tun.neet.dev";
passwordFile = "/run/agenix/iodine";
};
age.secrets.iodine.file = ../../secrets/iodine.age;
networking.firewall.allowedUDPPorts = [ 53 ];
networking.nat.internalInterfaces = [
"dns0" # iodine
];
};
}

View File

@@ -1,100 +0,0 @@
{ config, pkgs, lib, ... }:
with builtins;
let
cfg = config.mailserver;
domains = [
"neet.space"
"neet.dev"
"neet.cloud"
"runyan.org"
"runyan.rocks"
"thunderhex.com"
"tar.ninja"
"bsd.ninja"
"bsd.rocks"
];
in
{
config = lib.mkIf cfg.enable {
# kresd doesn't work with tailscale MagicDNS
mailserver.localDnsResolver = false;
services.resolved.enable = true;
mailserver = {
fqdn = "mail.neet.dev";
dkimKeyBits = 2048;
indexDir = "/var/lib/mailindex";
enableManageSieve = true;
fullTextSearch.enable = true;
fullTextSearch.indexAttachments = true;
fullTextSearch.memoryLimit = 500;
inherit domains;
loginAccounts = {
"jeremy@runyan.org" = {
hashedPasswordFile = "/run/agenix/hashed-email-pw";
# catchall for all domains
aliases = map (domain: "@${domain}") domains;
};
"robot@runyan.org" = {
aliases = [
"no-reply@neet.dev"
"robot@neet.dev"
];
sendOnly = true;
hashedPasswordFile = "/run/agenix/hashed-robots-email-pw";
};
};
rejectRecipients = [
"george@runyan.org"
"joslyn@runyan.org"
"damon@runyan.org"
"jonas@runyan.org"
];
certificateScheme = 3; # use let's encrypt for certs
};
age.secrets.hashed-email-pw.file = ../../secrets/hashed-email-pw.age;
age.secrets.hashed-robots-email-pw.file = ../../secrets/hashed-robots-email-pw.age;
# sendmail to use xxx@domain instead of xxx@mail.domain
services.postfix.origin = "$mydomain";
# relay sent mail through mailgun
# https://www.howtoforge.com/community/threads/different-smtp-relays-for-different-domains-in-postfix.82711/#post-392620
services.postfix.config = {
smtp_sasl_auth_enable = "yes";
smtp_sasl_security_options = "noanonymous";
smtp_sasl_password_maps = "hash:/var/lib/postfix/conf/sasl_relay_passwd";
smtp_use_tls = "yes";
sender_dependent_relayhost_maps = "hash:/var/lib/postfix/conf/sender_relay";
smtp_sender_dependent_authentication = "yes";
};
services.postfix.mapFiles.sender_relay =
let
relayHost = "[smtp.mailgun.org]:587";
in
pkgs.writeText "sender_relay"
(concatStringsSep "\n" (map (domain: "@${domain} ${relayHost}") domains));
services.postfix.mapFiles.sasl_relay_passwd = "/run/agenix/sasl_relay_passwd";
age.secrets.sasl_relay_passwd.file = ../../secrets/sasl_relay_passwd.age;
# webmail
services.nginx.enable = true;
services.roundcube = {
enable = true;
hostName = config.mailserver.fqdn;
extraConfig = ''
# starttls needed for authentication, so the fqdn required to match the certificate
$config['smtp_server'] = "tls://${config.mailserver.fqdn}";
$config['smtp_user'] = "%u";
$config['smtp_pass'] = "%p";
'';
};
# backups
backup.group."email".paths = [
config.mailserver.mailDirectory
];
};
}

View File

@@ -3,8 +3,7 @@
let
cfg = config.services.matrix;
certs = config.security.acme.certs;
in
{
in {
options.services.matrix = {
enable = lib.mkEnableOption "enable matrix";
element-web = {
@@ -138,8 +137,7 @@ in
];
locations."/".proxyPass = "http://localhost:${toString cfg.port}";
};
virtualHosts.${cfg.turn.host} = {
# get TLS cert for TURN server
virtualHosts.${cfg.turn.host} = { # get TLS cert for TURN server
enableACME = true;
forceSSL = true;
};

View File

@@ -3,8 +3,7 @@
let
cfg = config.services.murmur;
certs = config.security.acme.certs;
in
{
in {
options.services.murmur.domain = lib.mkOption {
type = lib.types.str;
};

View File

@@ -1,34 +0,0 @@
{ config, pkgs, lib, ... }:
let
cfg = config.services.nextcloud;
in
{
config = lib.mkIf cfg.enable {
services.nextcloud = {
https = true;
package = pkgs.nextcloud25;
hostName = "neet.cloud";
config.dbtype = "sqlite";
config.adminuser = "jeremy";
config.adminpassFile = "/run/agenix/nextcloud-pw";
autoUpdateApps.enable = true;
enableBrokenCiphersForSSE = false;
};
age.secrets.nextcloud-pw = {
file = ../../secrets/nextcloud-pw.age;
owner = "nextcloud";
};
# backups
backup.group."nextcloud".paths = [
config.services.nextcloud.home
];
services.nginx.virtualHosts.${config.services.nextcloud.hostName} = {
enableACME = true;
forceSSL = true;
};
};
}

View File

@@ -5,8 +5,7 @@ let
nginxWithRTMP = pkgs.nginx.override {
modules = [ pkgs.nginxModules.rtmp ];
};
in
{
in {
options.services.nginx.stream = {
enable = lib.mkEnableOption "enable nginx rtmp/hls/dash video streaming";
port = lib.mkOption {

View File

@@ -2,8 +2,7 @@
let
cfg = config.services.nginx;
in
{
in {
config = lib.mkIf cfg.enable {
services.nginx = {
recommendedGzipSettings = true;

View File

@@ -4,8 +4,7 @@ with lib;
let
cfg = config.services.owncast;
in
{
in {
options.services.owncast = {
hostname = lib.mkOption {
type = types.str;

View File

@@ -14,8 +14,7 @@ let
cp -ar $src $out
'';
};
in
{
in {
options.services.privatebin = {
enable = lib.mkEnableOption "enable privatebin";
host = lib.mkOption {

View File

@@ -3,8 +3,7 @@
let
cfg = config.services.radio;
radioPackage = config.inputs.radio.packages.${config.currentSystem}.radio;
in
{
in {
options.services.radio = {
enable = lib.mkEnableOption "enable radio";
user = lib.mkOption {

View File

@@ -25,7 +25,9 @@
printing = cups
printcap name = cups
hide files = /.nobackup/.DS_Store/._.DS_Store/
# horrible files
veto files = /._*/.DS_Store/ /._*/._.DS_Store/
delete veto files = yes
'';
shares = {
@@ -75,13 +77,6 @@
};
};
# backups
backup.group."samba".paths = [
config.services.samba.shares.googlebot.path
config.services.samba.shares.cris.path
config.services.samba.shares.public.path
];
# Windows discovery of samba server
services.samba-wsdd = {
enable = true;

View File

@@ -1,30 +0,0 @@
{ config, pkgs, lib, ... }:
let
cfg = config.services.searx;
in
{
config = lib.mkIf cfg.enable {
services.searx = {
environmentFile = "/run/agenix/searx";
settings = {
server.port = 43254;
server.secret_key = "@SEARX_SECRET_KEY@";
engines = [{
name = "wolframalpha";
shortcut = "wa";
api_key = "@WOLFRAM_API_KEY@";
engine = "wolframalpha_api";
}];
};
};
services.nginx.virtualHosts."search.neet.space" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:${toString config.services.searx.settings.server.port}";
};
};
age.secrets.searx.file = ../../secrets/searx.age;
};
}

View File

@@ -2,8 +2,7 @@
let
cfg = config.services.thelounge;
in
{
in {
options.services.thelounge = {
fileUploadBaseUrl = lib.mkOption {
type = lib.types.str;
@@ -43,10 +42,6 @@ in
};
};
backup.group."thelounge".paths = [
"/var/lib/thelounge/"
];
# the lounge client
services.nginx.virtualHosts.${cfg.host} = {
enableACME = true;

View File

@@ -79,11 +79,8 @@ in
"${toStr webrtc-peer-lower-port}-${toStr webrtc-peer-upper-port}:${toStr webrtc-peer-lower-port}-${toStr webrtc-peer-upper-port}/udp"
];
cmd = [
"lightspeed-webrtc"
"--addr=0.0.0.0"
"--ip=${domain}"
"--ports=${toStr webrtc-peer-lower-port}-${toStr webrtc-peer-upper-port}"
"run"
"lightspeed-webrtc" "--addr=0.0.0.0" "--ip=${domain}"
"--ports=${toStr webrtc-peer-lower-port}-${toStr webrtc-peer-upper-port}" "run"
];
# imageFile = pkgs.dockerTools.pullImage {
# imageName = "projectlightspeed/webrtc";

View File

@@ -2,8 +2,7 @@
let
cfg = config.services.zerobin;
in
{
in {
options.services.zerobin = {
host = lib.mkOption {
type = lib.types.str;

View File

@@ -1,28 +1,36 @@
{ config, lib, pkgs, ... }:
{ config, pkgs, ... }:
# Improvements to the default shell
# - use nix-index for command-not-found
# - use nix-locate for command-not-found
# - disable fish's annoying greeting message
# - add some handy shell commands
{
environment.systemPackages = with pkgs; [
comma
];
# nix-index
programs.nix-index.enable = true;
programs.nix-index.enableFishIntegration = true;
let
nix-locate = config.inputs.nix-locate.defaultPackage.${config.currentSystem};
in {
programs.command-not-found.enable = false;
environment.systemPackages = [
nix-locate
];
programs.fish = {
enable = true;
shellInit = ''
shellInit = let
wrapper = pkgs.writeScript "command-not-found" ''
#!${pkgs.bash}/bin/bash
source ${nix-locate}/etc/profile.d/command-not-found.sh
command_not_found_handle "$@"
'';
in ''
# use nix-locate for command-not-found functionality
function __fish_command_not_found_handler --on-event fish_command_not_found
${wrapper} $argv
end
# disable annoying fish shell greeting
set fish_greeting
alias sudo="doas"
'';
};
@@ -30,23 +38,9 @@
myip = "dig +short myip.opendns.com @resolver1.opendns.com";
# https://linuxreviews.org/HOWTO_Test_Disk_I/O_Performance
io_seq_read = "${pkgs.fio}/bin/fio --name TEST --eta-newline=5s --filename=temp.file --rw=read --size=2g --io_size=10g --blocksize=1024k --ioengine=libaio --fsync=10000 --iodepth=32 --direct=1 --numjobs=1 --runtime=60 --group_reporting; rm temp.file";
io_seq_write = "${pkgs.fio}/bin/fio --name TEST --eta-newline=5s --filename=temp.file --rw=write --size=2g --io_size=10g --blocksize=1024k --ioengine=libaio --fsync=10000 --iodepth=32 --direct=1 --numjobs=1 --runtime=60 --group_reporting; rm temp.file";
io_rand_read = "${pkgs.fio}/bin/fio --name TEST --eta-newline=5s --filename=temp.file --rw=randread --size=2g --io_size=10g --blocksize=4k --ioengine=libaio --fsync=1 --iodepth=1 --direct=1 --numjobs=32 --runtime=60 --group_reporting; rm temp.file";
io_rand_write = "${pkgs.fio}/bin/fio --name TEST --eta-newline=5s --filename=temp.file --rw=randrw --size=2g --io_size=10g --blocksize=4k --ioengine=libaio --fsync=1 --iodepth=1 --direct=1 --numjobs=1 --runtime=60 --group_reporting; rm temp.file";
io_seq_read = "nix run nixpkgs#fio -- --name TEST --eta-newline=5s --filename=temp.file --rw=read --size=2g --io_size=10g --blocksize=1024k --ioengine=libaio --fsync=10000 --iodepth=32 --direct=1 --numjobs=1 --runtime=60 --group_reporting; rm temp.file";
io_seq_write = "nix run nixpkgs#fio -- --name TEST --eta-newline=5s --filename=temp.file --rw=write --size=2g --io_size=10g --blocksize=1024k --ioengine=libaio --fsync=10000 --iodepth=32 --direct=1 --numjobs=1 --runtime=60 --group_reporting; rm temp.file";
io_rand_read = "nix run nixpkgs#fio -- --name TEST --eta-newline=5s --filename=temp.file --rw=randread --size=2g --io_size=10g --blocksize=4k --ioengine=libaio --fsync=1 --iodepth=1 --direct=1 --numjobs=32 --runtime=60 --group_reporting; rm temp.file";
io_rand_write = "nix run nixpkgs#fio -- --name TEST --eta-newline=5s --filename=temp.file --rw=randrw --size=2g --io_size=10g --blocksize=4k --ioengine=libaio --fsync=1 --iodepth=1 --direct=1 --numjobs=1 --runtime=60 --group_reporting; rm temp.file";
};
nixpkgs.overlays = [
(final: prev: {
# comma uses the "nix-index" package built into nixpkgs by default.
# That package doesn't use the prebuilt nix-index database so it needs to be changed.
comma = prev.comma.overrideAttrs (old: {
postInstall = ''
wrapProgram $out/bin/comma \
--prefix PATH : ${lib.makeBinPath [ prev.fzy config.programs.nix-index.package ]}
ln -s $out/bin/comma $out/bin/,
'';
});
})
];
}

View File

@@ -1,38 +1,69 @@
{ config, lib, pkgs, ... }:
{
programs.ssh.knownHosts = lib.filterAttrs (n: v: v != null) (lib.concatMapAttrs
(host: cfg: {
${host} = {
hostNames = cfg.hostNames;
publicKey = cfg.hostKey;
};
"${host}-remote-unlock" =
if cfg.remoteUnlock != null then {
hostNames = builtins.filter (h: h != null) [ cfg.remoteUnlock.clearnetHost cfg.remoteUnlock.onionHost ];
publicKey = cfg.remoteUnlock.hostKey;
} else null;
})
config.machines.hosts);
# prebuilt cmds for easy ssh LUKS unlock
environment.shellAliases =
let
unlockHosts = unlockType: lib.concatMapAttrs
(host: cfg:
if cfg.remoteUnlock != null && cfg.remoteUnlock.${unlockType} != null then {
${host} = cfg.remoteUnlock.${unlockType};
} else { })
config.machines.hosts;
in
lib.concatMapAttrs (host: addr: { "unlock-over-tor_${host}" = "torsocks ssh root@${addr}"; }) (unlockHosts "onionHost")
//
lib.concatMapAttrs (host: addr: { "unlock_${host}" = "ssh root@${addr}"; }) (unlockHosts "clearnetHost");
# TODO: Old ssh keys I will remove some day...
machines.ssh.userKeys = [
rec {
users = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMVR/R3ZOsv7TZbICGBCHdjh1NDT8SnswUyINeJOC7QG"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE0dcqL/FhHmv+a1iz3f9LJ48xubO7MZHy35rW9SZOYM"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO0VFnn3+Mh0nWeN92jov81qNE9fpzTAHYBphNoY7HUx" # reg
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHSkKiRUUmnErOKGx81nyge/9KqjkPh8BfDk0D3oP586" # nat
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFeTK1iARlNIKP/DS8/ObBm9yUM/3L1Ub4XI5A2r9OzP" # ray
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKXc9PX3uTYVrgvKdztk+LBh5WMNBUzbXlAo50SCAeNw" # nat 2
];
system = {
liza = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDY/pNyWedEfU7Tq9ikGbriRuF1ZWkHhegGS17L0Vcdl";
ponyo = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMBBlTAIp38RhErU1wNNV5MBeb+WGH0mhF/dxh5RsAXN";
ponyo-unlock = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC9LQuuImgWlkjDhEEIbM1wOd+HqRv1RxvYZuLXPSdRi";
ray = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDQM8hwKRgl8cZj7UVYATSLYu4LhG7I0WFJ9m2iWowiB";
s0 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAwiXcUFtAvZCayhu4+AIcF+Ktrdgv9ee/mXSIhJbp4q";
nat = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGVgZc5Z2Oh426z7lEftcFUwCFcrZy8bvqS09Tj49GWE";
n1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPWlhd1Oid5Xf2zdcBrcdrR0TlhObutwcJ8piobRTpRt";
n2 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ7bRiRutnI7Bmyt/I238E3Fp5DqiClIXiVibsccipOr";
n3 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB+rJEaRrFDGirQC2UoWQkmpzLg4qgTjGJgVqiipWiU5";
n4 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINYm2ROIfCeGz6QtDwqAmcj2DX9tq2CZn0eLhskdvB4Z";
n5 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE5Qhvwq3PiHEKf+2/4w5ZJkSMNzFLhIRrPOR98m7wW4";
n6 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID/P/pa9+qhKAPfvvd8xSO2komJqDW0M1nCK7ZrP6PO7";
n7 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPtOlOvTlMX2mxPaXDJ6VlMe5rmroUXpKmJVNxgV32xL";
};
# groups
systems = with system; [
liza
ponyo
ray
nat
s0
n1
n2
n3
n4
n5
n6
n7
];
personal = with system; [
ray
nat
];
servers = with system; [
liza
ponyo
s0
n1
n2
n3
n4
n5
n6
n7
];
compute = with system; [
n1
n2
n3
n4
n5
n6
n7
];
storage = with system; [
s0
];
}

138
flake.lock generated
View File

@@ -2,17 +2,16 @@
"nodes": {
"agenix": {
"inputs": {
"darwin": "darwin",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1682101079,
"narHash": "sha256-MdAhtjrLKnk2uiqun1FWABbKpLH090oeqCSiWemtuck=",
"lastModified": 1665870395,
"narHash": "sha256-Tsbqb27LDNxOoPLh0gw2hIb6L/6Ow/6lIBvqcHzEKBI=",
"owner": "ryantm",
"repo": "agenix",
"rev": "2994d002dcff5353ca1ac48ec584c7f6589fe447",
"rev": "a630400067c6d03c9b3e0455347dc8559db14288",
"type": "github"
},
"original": {
@@ -33,7 +32,7 @@
"locked": {
"lastModified": 1648612759,
"narHash": "sha256-SJwlpD2Wz3zFoX2mIYCQfwIOYHaOdeiWGFeDXsLGM84=",
"ref": "refs/heads/master",
"ref": "master",
"rev": "39d338b9b24159d8ef3309eecc0d32a2a9f102b5",
"revCount": 2,
"type": "git",
@@ -72,7 +71,7 @@
"locked": {
"lastModified": 1651719222,
"narHash": "sha256-p/GY5vOP+HUlxNL4OtEhmBNEVQsedOHXEmjfCGONVmE=",
"ref": "refs/heads/master",
"ref": "master",
"rev": "1290ddd9a2ff2bf2d0f702750768312b80efcd34",
"revCount": 19,
"type": "git",
@@ -83,61 +82,14 @@
"url": "https://git.neet.dev/zuckerberg/dailybuild_modules.git"
}
},
"darwin": {
"inputs": {
"nixpkgs": [
"agenix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1673295039,
"narHash": "sha256-AsdYgE8/GPwcelGgrntlijMg4t3hLFJFCRF3tL5WVjA=",
"owner": "lnl7",
"repo": "nix-darwin",
"rev": "87b9d090ad39b25b2400029c64825fc2a8868943",
"type": "github"
},
"original": {
"owner": "lnl7",
"ref": "master",
"repo": "nix-darwin",
"type": "github"
}
},
"deploy-rs": {
"inputs": {
"flake-compat": "flake-compat",
"nixpkgs": [
"nixpkgs"
],
"utils": [
"simple-nixos-mailserver",
"utils"
]
},
"locked": {
"lastModified": 1682063650,
"narHash": "sha256-VaDHh2z6xlnTHaONlNVHP7qEMcK5rZ8Js3sT6mKb2XY=",
"owner": "serokell",
"repo": "deploy-rs",
"rev": "c2ea4e642dc50fd44b537e9860ec95867af30d39",
"type": "github"
},
"original": {
"owner": "serokell",
"repo": "deploy-rs",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1668681692,
"narHash": "sha256-Ht91NGdewz8IQLtWZ9LCeNXMSXHUss+9COoqu6JLmXU=",
"lastModified": 1641205782,
"narHash": "sha256-4jY7RCWUoZ9cKD8co0/4tFARpWB+57+r1bLLvXNJliY=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "009399224d5e398d03b22badca40a37ac85412a1",
"rev": "b7547d3eed6f32d06102ead8991ec52ab0a4f1a7",
"type": "github"
},
"original": {
@@ -147,15 +99,12 @@
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1681202837,
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
@@ -164,39 +113,40 @@
"type": "github"
}
},
"nix-index-database": {
"nix-locate": {
"inputs": {
"flake-compat": "flake-compat",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1681591833,
"narHash": "sha256-lW+xOELafAs29yw56FG4MzNOFkh8VHC/X/tRs1wsGn8=",
"owner": "Mic92",
"repo": "nix-index-database",
"rev": "68ec961c51f48768f72d2bbdb396ce65a316677e",
"lastModified": 1652819416,
"narHash": "sha256-OzYSb66kQUVP1FM0E7Z0ij13mm14DkJi79FAMprAavo=",
"owner": "googlebot42",
"repo": "nix-index",
"rev": "a28bb3175d370c6cb9569e6d4b5570e9ca016a3e",
"type": "github"
},
"original": {
"owner": "Mic92",
"repo": "nix-index-database",
"owner": "googlebot42",
"repo": "nix-index",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1682133240,
"narHash": "sha256-s6yRsI/7V+k/+rckp0+/2cs/UXnea3SEfMpy95QiGcc=",
"lastModified": 1668994630,
"narHash": "sha256-1lqx6HLyw6fMNX/hXrrETG1vMvZRGm2XVC9O/Jt0T6c=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "8dafae7c03d6aa8c2ae0a0612fbcb47e994e3fb8",
"rev": "af50806f7c6ab40df3e6b239099e8f8385f6c78b",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "master",
"repo": "nixpkgs",
"rev": "af50806f7c6ab40df3e6b239099e8f8385f6c78b",
"type": "github"
}
},
@@ -215,16 +165,20 @@
"type": "indirect"
}
},
"nixpkgs-hostapd-pr": {
"flake": false,
"nixpkgs-unstable": {
"locked": {
"narHash": "sha256-1rGQKcB1jeRPc1n021ulyOVkA6L6xmNYKmeqQ94+iRc=",
"type": "file",
"url": "https://github.com/NixOS/nixpkgs/pull/222536.patch"
"lastModified": 1669411043,
"narHash": "sha256-LfPd3+EY+jaIHTRIEOUtHXuanxm59YKgUacmSzaqMLc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "5dc7114b7b256d217fe7752f1614be2514e61bb8",
"type": "github"
},
"original": {
"type": "file",
"url": "https://github.com/NixOS/nixpkgs/pull/222536.patch"
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"radio": {
@@ -257,7 +211,7 @@
"locked": {
"lastModified": 1652121792,
"narHash": "sha256-j1Y9MAjUVNgyFSeGzPoqibAnEysJDjZSXukVfQ7+bsQ=",
"ref": "refs/heads/master",
"ref": "master",
"rev": "72e7a9e80b780c84ed8d4a6374bfbb242701f900",
"revCount": 5,
"type": "git",
@@ -273,11 +227,10 @@
"agenix": "agenix",
"archivebox": "archivebox",
"dailybuild_modules": "dailybuild_modules",
"deploy-rs": "deploy-rs",
"flake-utils": "flake-utils",
"nix-index-database": "nix-index-database",
"nix-locate": "nix-locate",
"nixpkgs": "nixpkgs",
"nixpkgs-hostapd-pr": "nixpkgs-hostapd-pr",
"nixpkgs-unstable": "nixpkgs-unstable",
"radio": "radio",
"radio-web": "radio-web",
"simple-nixos-mailserver": "simple-nixos-mailserver"
@@ -307,21 +260,6 @@
"type": "gitlab"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": {
"locked": {
"lastModified": 1605370193,

120
flake.nix
View File

@@ -1,14 +1,17 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/master";
# nixpkgs-patch-howdy.url = "https://github.com/NixOS/nixpkgs/pull/216245.diff";
# nixpkgs-patch-howdy.flake = false;
nixpkgs.url = "github:NixOS/nixpkgs/af50806f7c6ab40df3e6b239099e8f8385f6c78b";
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
nix-locate.url = "github:googlebot42/nix-index";
nix-locate.inputs.nixpkgs.follows = "nixpkgs";
# mail server
simple-nixos-mailserver.url = "gitlab:simple-nixos-mailserver/nixos-mailserver/nixos-22.05";
simple-nixos-mailserver.inputs.nixpkgs.follows = "nixpkgs";
simple-nixos-mailserver.inputs.nixpkgs-21_11.follows = "nixpkgs";
# agenix
agenix.url = "github:ryantm/agenix";
@@ -30,70 +33,33 @@
archivebox.url = "git+https://git.neet.dev/zuckerberg/archivebox.git";
archivebox.inputs.nixpkgs.follows = "nixpkgs";
archivebox.inputs.flake-utils.follows = "flake-utils";
# nixos config deployment
deploy-rs.url = "github:serokell/deploy-rs";
deploy-rs.inputs.nixpkgs.follows = "nixpkgs";
deploy-rs.inputs.utils.follows = "simple-nixos-mailserver/utils";
# prebuilt nix-index database
nix-index-database.url = "github:Mic92/nix-index-database";
nix-index-database.inputs.nixpkgs.follows = "nixpkgs";
nixpkgs-hostapd-pr.url = "https://github.com/NixOS/nixpkgs/pull/222536.patch";
nixpkgs-hostapd-pr.flake = false;
};
outputs = { self, nixpkgs, ... }@inputs:
let
machines = (import ./common/machine-info/moduleless.nix
{
inherit nixpkgs;
assertionsModule = "${nixpkgs}/nixos/modules/misc/assertions.nix";
}).machines.hosts;
in
{
outputs = { self, nixpkgs, nixpkgs-unstable, ... }@inputs: {
nixosConfigurations =
let
modules = system: hostname: with inputs; [
modules = system: [
./common
simple-nixos-mailserver.nixosModule
agenix.nixosModules.default
dailybuild_modules.nixosModule
archivebox.nixosModule
nix-index-database.nixosModules.nix-index
inputs.simple-nixos-mailserver.nixosModule
inputs.agenix.nixosModule
inputs.dailybuild_modules.nixosModule
inputs.archivebox.nixosModule
({ lib, ... }: {
config = {
environment.systemPackages = [
agenix.packages.${system}.agenix
config.environment.systemPackages = [
inputs.agenix.defaultPackage.${system}
];
networking.hostName = hostname;
};
# because nixos specialArgs doesn't work for containers... need to pass in inputs a different way
options.inputs = lib.mkOption { default = inputs; };
options.currentSystem = lib.mkOption { default = system; };
})
];
mkSystem = system: nixpkgs: path: hostname:
mkSystem = system: nixpkgs: path:
let
allModules = modules system hostname;
# allow patching nixpkgs, remove this hack once this is solved: https://github.com/NixOS/nix/issues/3920
patchedNixpkgsSrc = nixpkgs.legacyPackages.${system}.applyPatches {
name = "nixpkgs-patched";
src = nixpkgs;
patches = [
inputs.nixpkgs-hostapd-pr
./patches/kexec-luks.patch
];
};
patchedNixpkgs = nixpkgs.lib.fix (self: (import "${patchedNixpkgsSrc}/flake.nix").outputs { self = nixpkgs; });
in
patchedNixpkgs.lib.nixosSystem {
allModules = modules system;
in nixpkgs.lib.nixosSystem {
inherit system;
modules = allModules ++ [path];
@@ -102,45 +68,31 @@
};
};
in
nixpkgs.lib.mapAttrs
(hostname: cfg:
mkSystem cfg.arch nixpkgs cfg.configurationPath hostname)
machines;
{
"reg" = mkSystem "x86_64-linux" nixpkgs ./machines/reg/configuration.nix;
"ray" = mkSystem "x86_64-linux" nixpkgs ./machines/ray/configuration.nix;
"nat" = mkSystem "aarch64-linux" nixpkgs ./machines/nat/configuration.nix;
"liza" = mkSystem "x86_64-linux" nixpkgs ./machines/liza/configuration.nix;
"ponyo" = mkSystem "x86_64-linux" nixpkgs ./machines/ponyo/configuration.nix;
"s0" = mkSystem "aarch64-linux" nixpkgs-unstable ./machines/storage/s0/configuration.nix;
"n1" = mkSystem "aarch64-linux" nixpkgs ./machines/compute/n1/configuration.nix;
"n2" = mkSystem "aarch64-linux" nixpkgs ./machines/compute/n2/configuration.nix;
"n3" = mkSystem "aarch64-linux" nixpkgs ./machines/compute/n3/configuration.nix;
"n4" = mkSystem "aarch64-linux" nixpkgs ./machines/compute/n4/configuration.nix;
"n5" = mkSystem "aarch64-linux" nixpkgs ./machines/compute/n5/configuration.nix;
"n6" = mkSystem "aarch64-linux" nixpkgs ./machines/compute/n6/configuration.nix;
"n7" = mkSystem "aarch64-linux" nixpkgs ./machines/compute/n7/configuration.nix;
};
packages =
let
packages = let
mkKexec = system:
(nixpkgs.lib.nixosSystem {
inherit system;
modules = [ ./machines/ephemeral/kexec.nix ];
modules = [ ./machines/kexec.nix ];
}).config.system.build.kexec_tarball;
mkIso = system:
(nixpkgs.lib.nixosSystem {
inherit system;
modules = [ ./machines/ephemeral/iso.nix ];
}).config.system.build.isoImage;
in
{
in {
"x86_64-linux"."kexec" = mkKexec "x86_64-linux";
"x86_64-linux"."iso" = mkIso "x86_64-linux";
"aarch64-linux"."kexec" = mkKexec "aarch64-linux";
"aarch64-linux"."iso" = mkIso "aarch64-linux";
};
deploy.nodes =
let
mkDeploy = configName: arch: hostname: {
inherit hostname;
magicRollback = false;
sshUser = "root";
profiles.system.path = inputs.deploy-rs.lib.${arch}.activate.nixos self.nixosConfigurations.${configName};
};
in
nixpkgs.lib.mapAttrs
(hostname: cfg:
mkDeploy hostname cfg.arch (builtins.head cfg.hostNames))
machines;
checks = builtins.mapAttrs (system: deployLib: deployLib.deployChecks self.deploy) inputs.deploy-rs.lib;
};
}

View File

@@ -0,0 +1,24 @@
{ config, ... }:
{
# NixOS wants to enable GRUB by default
boot.loader.grub.enable = false;
# Enables the generation of /boot/extlinux/extlinux.conf
boot.loader.generic-extlinux-compatible.enable = true;
fileSystems = {
"/" = {
device = "/dev/disk/by-label/NIXOS_SD";
fsType = "ext4";
};
};
system.autoUpgrade.enable = true;
networking.interfaces.eth0.useDHCP = true;
hardware.deviceTree.enable = true;
hardware.deviceTree.overlays = [
./sopine-baseboard-ethernet.dtbo # fix pine64 clusterboard ethernet
];
}

View File

@@ -0,0 +1,9 @@
{ config, ... }:
{
imports = [
../common.nix
];
networking.hostName = "n1";
}

View File

@@ -0,0 +1,9 @@
{ config, ... }:
{
imports = [
../common.nix
];
networking.hostName = "n2";
}

View File

@@ -0,0 +1,9 @@
{ config, ... }:
{
imports = [
../common.nix
];
networking.hostName = "n3";
}

View File

@@ -0,0 +1,9 @@
{ config, ... }:
{
imports = [
../common.nix
];
networking.hostName = "n4";
}

View File

@@ -0,0 +1,9 @@
{ config, ... }:
{
imports = [
../common.nix
];
networking.hostName = "n5";
}

View File

@@ -0,0 +1,9 @@
{ config, ... }:
{
imports = [
../common.nix
];
networking.hostName = "n6";
}

View File

@@ -0,0 +1,9 @@
{ config, ... }:
{
imports = [
../common.nix
];
networking.hostName = "n7";
}

Binary file not shown.

View File

@@ -0,0 +1,15 @@
/dts-v1/;
/ {
model = "SoPine with baseboard";
compatible = "pine64,sopine-baseboard\0pine64,sopine\0allwinner,sun50i-a64";
fragment@0 {
/* target = <&ethernet@1c30000>; */
target-path = "/soc/ethernet@1c30000";
__overlay__ {
allwinner,tx-delay-ps = <500>;
};
};
};

View File

@@ -1,12 +0,0 @@
{ modulesPath, ... }:
{
imports = [
(modulesPath + "/installer/cd-dvd/iso-image.nix")
./minimal.nix
];
isoImage.makeUsbBootable = true;
networking.hostName = "iso";
}

View File

@@ -1,53 +0,0 @@
{ config, pkgs, modulesPath, ... }:
{
imports = [
(modulesPath + "/installer/cd-dvd/channel.nix")
../../common/machine-info
../../common/ssh.nix
];
boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "e1000" "e1000e" "virtio_pci" "r8169" ];
boot.kernelParams = [
"panic=30"
"boot.panic_on_fail" # reboot the machine upon fatal boot issues
"console=ttyS0,115200" # enable serial console
"console=tty1"
];
boot.kernel.sysctl."vm.overcommit_memory" = "1";
boot.kernelPackages = pkgs.linuxPackages_latest;
system.stateVersion = "21.11";
# hardware.enableAllFirmware = true;
# nixpkgs.config.allowUnfree = true;
environment.systemPackages = with pkgs; [
cryptsetup
btrfs-progs
git
git-lfs
wget
htop
dnsutils
pciutils
usbutils
lm_sensors
];
environment.variables.GC_INITIAL_HEAP_SIZE = "1M";
networking.useDHCP = true;
services.openssh = {
enable = true;
settings = {
KbdInteractiveAuthentication = false;
PasswordAuthentication = false;
};
};
services.getty.autologinUser = "root";
users.users.root.openssh.authorizedKeys.keys = config.machines.ssh.userKeys;
}

View File

@@ -6,11 +6,8 @@
imports = [
(modulesPath + "/installer/netboot/netboot.nix")
(modulesPath + "/profiles/qemu-guest.nix")
./minimal.nix
];
networking.hostName = "kexec";
# stripped down version of https://github.com/cleverca22/nix-tests/tree/master/kexec
system.build = rec {
image = pkgs.runCommand "image" { buildInputs = [ pkgs.nukeReferences ]; } ''
@@ -45,4 +42,31 @@
contents = [ ];
};
};
boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "e1000" "e1000e" "virtio_pci" "r8169" ];
boot.kernelParams = [
"panic=30" "boot.panic_on_fail" # reboot the machine upon fatal boot issues
"console=ttyS0" # enable serial console
"console=tty1"
];
boot.kernel.sysctl."vm.overcommit_memory" = "1";
environment.systemPackages = with pkgs; [
cryptsetup
btrfs-progs
];
environment.variables.GC_INITIAL_HEAP_SIZE = "1M";
networking.useDHCP = true;
networking.hostName = "kexec";
services.openssh = {
enable = true;
challengeResponseAuthentication = false;
passwordAuthentication = false;
};
services.getty.autologinUser = "root";
users.users.root.openssh.authorizedKeys.keys = (import ../common/ssh.nix).users;
}

View File

@@ -0,0 +1,110 @@
{ config, pkgs, lib, ... }:
{
imports =[
./hardware-configuration.nix
];
# 5synsrjgvfzywruomjsfvfwhhlgxqhyofkzeqt2eisyijvjvebnu2xyd.onion
firmware.x86_64.enable = true;
bios = {
enable = true;
device = "/dev/sda";
};
luks = {
enable = true;
device.path = "/dev/disk/by-uuid/2f736fba-8a0c-4fb5-8041-c849fb5e1297";
};
system.autoUpgrade.enable = true;
networking.hostName = "liza";
networking.interfaces.enp1s0.useDHCP = true;
mailserver = {
enable = true;
fqdn = "mail.neet.dev";
dkimKeyBits = 2048;
indexDir = "/var/lib/mailindex";
enableManageSieve = true;
fullTextSearch.enable = true;
fullTextSearch.indexAttachments = true;
fullTextSearch.memoryLimit = 500;
domains = [
"neet.space" "neet.dev" "neet.cloud"
"runyan.org" "runyan.rocks"
"thunderhex.com" "tar.ninja"
"bsd.ninja" "bsd.rocks"
];
loginAccounts = {
"jeremy@runyan.org" = {
hashedPasswordFile = "/run/agenix/email-pw";
aliases = [
"@neet.space" "@neet.cloud" "@neet.dev"
"@runyan.org" "@runyan.rocks"
"@thunderhex.com" "@tar.ninja"
"@bsd.ninja" "@bsd.rocks"
];
};
};
rejectRecipients = [
"george@runyan.org"
"joslyn@runyan.org"
"damon@runyan.org"
"jonas@runyan.org"
];
certificateScheme = 3; # use let's encrypt for certs
};
age.secrets.email-pw.file = ../../secrets/email-pw.age;
# sendmail to use xxx@domain instead of xxx@mail.domain
services.postfix.origin = "$mydomain";
# relay sent mail through mailgun
# https://www.howtoforge.com/community/threads/different-smtp-relays-for-different-domains-in-postfix.82711/#post-392620
services.postfix.config = {
smtp_sasl_auth_enable = "yes";
smtp_sasl_security_options = "noanonymous";
smtp_sasl_password_maps = "hash:/var/lib/postfix/conf/sasl_relay_passwd";
smtp_use_tls = "yes";
sender_dependent_relayhost_maps = "hash:/var/lib/postfix/conf/sender_relay";
smtp_sender_dependent_authentication = "yes";
};
services.postfix.mapFiles.sender_relay = let
relayHost = "[smtp.mailgun.org]:587";
in pkgs.writeText "sender_relay" ''
@neet.space ${relayHost}
@neet.cloud ${relayHost}
@neet.dev ${relayHost}
@runyan.org ${relayHost}
@runyan.rocks ${relayHost}
@thunderhex.com ${relayHost}
@tar.ninja ${relayHost}
@bsd.ninja ${relayHost}
@bsd.rocks ${relayHost}
'';
services.postfix.mapFiles.sasl_relay_passwd = "/run/agenix/sasl_relay_passwd";
age.secrets.sasl_relay_passwd.file = ../../secrets/sasl_relay_passwd.age;
services.nextcloud = {
enable = true;
https = true;
package = pkgs.nextcloud22;
hostName = "neet.cloud";
config.dbtype = "sqlite";
config.adminuser = "jeremy";
config.adminpassFile = "/run/agenix/nextcloud-pw";
autoUpdateApps.enable = true;
};
age.secrets.nextcloud-pw = {
file = ../../secrets/nextcloud-pw.age;
owner = "nextcloud";
};
services.nginx.virtualHosts.${config.services.nextcloud.hostName} = {
enableACME = true;
forceSSL = true;
};
}

View File

@@ -0,0 +1,36 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
imports =
[ (modulesPath + "/profiles/qemu-guest.nix")
];
boot.initrd.availableKernelModules = [ "ata_piix" "virtio_pci" "floppy" "sr_mod" "virtio_blk" ];
boot.initrd.kernelModules = [ "dm-snapshot" ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/disk/by-uuid/b90eaf3c-2f91-499a-a066-861e0f4478df";
fsType = "btrfs";
};
fileSystems."/home" =
{ device = "/dev/disk/by-uuid/b90eaf3c-2f91-499a-a066-861e0f4478df";
fsType = "btrfs";
options = [ "subvol=home" ];
};
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/2b8f6f6d-9358-4d30-8341-7426574e0819";
fsType = "ext3";
};
swapDevices =
[ { device = "/dev/disk/by-uuid/ef7a83db-4b33-41d1-85fc-cff69e480352"; }
];
}

View File

@@ -1,15 +1,52 @@
{ config, pkgs, fetchurl, lib, ... }:
{ config, lib, pkgs, ... }:
{
imports = [
./hardware-configuration.nix
./m1-support
];
efi.enable = true;
networking.hostName = "nat";
networking.interfaces.ens160.useDHCP = true;
de.enable = true;
de.enableAcceleration = false;
de.touchpad.enable = true;
# nixpkgs.overlays = [
# (final: prev: {
# signal-desktop = prev.signal-desktop.overrideAttrs (old: {
# version = "5.50.1";
# src = final.fetchurl {
# url = "https://github.com/0mniteck/Signal-Desktop-Builder/raw/2610eaded94b3c717a63fdff3cb872dbbaf16383/builds/release/signal-desktop_5.50.1_arm64.deb";
# sha256 = "sha256-++xG3fCMvU+nwlkBwjZ0d0wfWiNDSUhyCfzTirsY2xs=";
# };
# #buildInputs = old.buildInputs ++ [ final.openssl_3_0 ];
# preFixup = ''
# gappsWrapperArgs+=(
# --prefix LD_LIBRARY_PATH : "${lib.makeLibraryPath [ final.stdenv.cc.cc ] }"
# --add-flags "\''${NIXOS_OZONE_WL:+\''${WAYLAND_DISPLAY:+--enable-features=UseOzonePlatform --ozone-platform=wayland}}"
# --suffix PATH : ${lib.makeBinPath [ final.xdg-utils ]}
# )
# # Fix the desktop link
# substituteInPlace $out/share/applications/signal-desktop.desktop \
# --replace /opt/Signal/signal-desktop $out/bin/signal-desktop
# autoPatchelf --no-recurse -- $out/lib/Signal/
# patchelf --add-needed ${final.libpulseaudio}/lib/libpulse.so $out/lib/Signal/resources/app.asar.unpacked/node_modules/ringrtc/build/linux/libringrtc-arm64.node
# patchelf --add-needed ${final.openssl_3_0}/lib/libcrypto.so.3 $out/lib/Signal/resources/app.asar.unpacked/node_modules/ringrtc/build/linux/libringrtc-arm64.node
# '';
# meta.platforms = [ "aarch64-linux" ];
# });
# })
# ];
nixpkgs.overlays = [
(final: prev: {
jellyfin-media-player = prev.jellyfin-media-player.overrideAttrs (old: {
meta.platforms = [ "aarch64-linux" ];
});
})
];
}

View File

@@ -4,24 +4,66 @@
{ config, lib, pkgs, modulesPath, ... }:
{
imports = [ ];
imports =
[ (modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "uhci_hcd" "ahci" "nvme" "usbhid" ];
boot.initrd.kernelModules = [ ];
efi.enable = true;
# 4k kernel for m1
hardware.asahi.use4KPages = false;
boot.initrd.availableKernelModules = [ "usb_storage" ];
boot.initrd.kernelModules = [ "dm-snapshot" ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
# fix keys
boot.extraModprobeConfig = ''
options hid-apple swap_fn_leftctrl=1 swap_opt_cmd=1
'';
boot.initrd.luks.devices."enc-pv" = {
device = "/dev/nvme0n1p5";
allowDiscards = true;
};
fileSystems."/" =
{
device = "/dev/disk/by-uuid/02a8c0c7-fd4e-4443-a83c-2d0b63848779";
{ device = "/dev/disk/by-uuid/f3021c34-2034-4bf0-bf3f-64d6d02c0eff";
fsType = "btrfs";
options = [ "subvol=root" ];
};
fileSystems."/home" =
{ device = "/dev/disk/by-uuid/f3021c34-2034-4bf0-bf3f-64d6d02c0eff";
fsType = "btrfs";
options = [ "subvol=home" ];
};
fileSystems."/nix" =
{ device = "/dev/disk/by-uuid/f3021c34-2034-4bf0-bf3f-64d6d02c0eff";
fsType = "btrfs";
options = [ "subvol=nix" ];
};
fileSystems."/boot" =
{
device = "/dev/disk/by-uuid/0C95-1290";
{ device = "/dev/disk/by-uuid/D33C-18EE";
fsType = "vfat";
};
swapDevices = [ ];
swapDevices =
[ { device = "/dev/disk/by-uuid/98e875e4-4c34-42e9-8c71-404dfe137ba7"; }
];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.wlp1s0f0.useDHCP = lib.mkDefault true;
#nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux";
powerManagement.cpuFreqGovernor = lib.mkDefault "ondemand";
# high-resolution display
hardware.video.hidpi.enable = lib.mkDefault true;
}

View File

@@ -0,0 +1,29 @@
{ lib
, python3
, python3Packages
, fetchFromGitHub
, gzip
, gnutar
, lzfse
}:
python3Packages.buildPythonApplication rec {
pname = "asahi-fwextract";
version = "0.5pre10";
# tracking version: https://github.com/AsahiLinux/PKGBUILDs/blob/main/asahi-fwextract/PKGBUILD
src = fetchFromGitHub {
owner = "AsahiLinux";
repo = "asahi-installer";
rev = "v${version}";
hash = "sha256-93dTRrNNo7yilSGpSNjXir+DhQe29DeoZHXusrk9PN8=";
};
postPatch = ''
substituteInPlace asahi_firmware/img4.py \
--replace 'liblzfse.so' '${lzfse}/lib/liblzfse.so'
substituteInPlace asahi_firmware/update.py \
--replace '"tar"' '"${gnutar}/bin/tar"' \
--replace '"xf"' '"-x", "-I", "${gzip}/bin/gzip", "-f"'
'';
}

View File

@@ -0,0 +1,53 @@
{ config, pkgs, lib, ... }:
let
bootM1n1 = config.hardware.asahi.pkgs.callPackage ../m1n1 {
isRelease = true;
withTools = false;
customLogo = config.boot.m1n1CustomLogo;
};
bootUBoot = config.hardware.asahi.pkgs.callPackage ../u-boot {
m1n1 = bootM1n1;
};
bootFiles = {
"m1n1/boot.bin" = pkgs.runCommand "boot.bin" {} ''
cat ${bootM1n1}/build/m1n1.bin > $out
cat ${config.boot.kernelPackages.kernel}/dtbs/apple/*.dtb >> $out
cat ${bootUBoot}/u-boot-nodtb.bin.gz >> $out
if [ -n "${config.boot.m1n1ExtraOptions}" ]; then
echo '${config.boot.m1n1ExtraOptions}' >> $out
fi
'';
};
in {
config = {
# install m1n1 with the boot loader
boot.loader.grub.extraFiles = bootFiles;
boot.loader.systemd-boot.extraFiles = bootFiles;
# ensure the installer has m1n1 in the image
system.extraDependencies = lib.mkForce [ bootM1n1 bootUBoot ];
system.build.m1n1 = bootFiles."m1n1/boot.bin";
};
options.boot = {
m1n1ExtraOptions = lib.mkOption {
type = lib.types.str;
default = "";
description = ''
Append extra options to the m1n1 boot binary. Might be useful for fixing
display problems on Mac minis.
https://github.com/AsahiLinux/m1n1/issues/159
'';
};
m1n1CustomLogo = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = ''
Custom logo to build into m1n1. The path must point to a 256x256 PNG.
'';
};
};
}

View File

@@ -0,0 +1,40 @@
{ config, pkgs, lib, ... }:
{
imports = [
./kernel
./peripheral-firmware
./boot-m1n1
];
config = {
hardware.asahi.pkgs = if config.hardware.asahi.pkgsSystem != "aarch64-linux"
then import (pkgs.path) {
system = config.hardware.asahi.pkgsSystem;
crossSystem.system = "aarch64-linux";
}
else pkgs;
};
options.hardware.asahi = {
pkgsSystem = lib.mkOption {
type = lib.types.str;
default = "aarch64-linux";
description = ''
System architecture that should be used to build the major Asahi
packages, if not the default aarch64-linux. This allows installing from
a cross-built ISO without rebuilding them during installation.
'';
};
pkgs = lib.mkOption {
type = lib.types.raw;
description = ''
Package set used to build the major Asahi packages. Defaults to the
ambient set if not cross-built, otherwise re-imports the ambient set
with the system defined by `hardware.asahi.pkgsSystem`.
'';
};
};
}

View File

@@ -0,0 +1,28 @@
From 1c60b7662b82d7d5d54aca1cd24f9517a8c4595f Mon Sep 17 00:00:00 2001
From: Thomas Watson <twatson52@icloud.com>
Date: Thu, 24 Nov 2022 11:00:43 -0600
Subject: [PATCH] drivers/usb/dwc3: remove apple dr_mode check
This check prevents the driver from probing with old device trees.
Allegedly this check is incorrect anyway as the dr_mode should default
to OTG but this is not true at the time of the check.
---
drivers/usb/dwc3/core.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 1d88119cdbb8..b92401c1ee0c 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1882,8 +1882,7 @@ static int dwc3_probe(struct platform_device *pdev)
}
if (of_device_is_compatible(dev->of_node, "apple,dwc3")) {
- if (!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) ||
- dwc->dr_mode != USB_DR_MODE_OTG) {
+ if (!IS_ENABLED(CONFIG_USB_ROLE_SWITCH)) {
dev_err(dev,
"Apple DWC3 requires role switch support.\n"
);
--
2.17.1

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index a1eb6572ecd2..b94fbd9b3d70 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1036,7 +1036,7 @@ endmenu
choice
prompt "Page size"
- default ARM64_4K_PAGES
+ default ARM64_16K_PAGES
help
Page size (translation granule) configuration.

View File

@@ -0,0 +1,98 @@
# the Asahi Linux kernel and options that must go along with it
{ config, pkgs, lib, ... }:
{
config = {
boot.kernelPackages = config.hardware.asahi.pkgs.callPackage ./package.nix {
_4KBuild = config.hardware.asahi.use4KPages;
};
# we definitely want to use CONFIG_ENERGY_MODEL, and
# schedutil is a prerequisite for using it
# source: https://www.kernel.org/doc/html/latest/scheduler/sched-energy.html
powerManagement.cpuFreqGovernor = lib.mkOverride 800 "schedutil";
boot.initrd.includeDefaultModules = false;
boot.initrd.availableKernelModules = [
# list of initrd modules stolen from
# https://github.com/AsahiLinux/asahi-scripts/blob/e4d6151a7dcb63ae5e3779c3cf57362eb37d908a/initcpio/install/asahi
"apple-mailbox"
"nvme_apple"
"pinctrl-apple-gpio"
"macsmc"
"macsmc-rtkit"
"i2c-apple"
"tps6598x"
"apple-dart"
"dwc3"
"dwc3-of-simple"
"xhci-pci"
"pcie-apple"
"gpio_macsmc"
"spi-apple"
"spi-hid-apple"
"spi-hid-apple-of"
"rtc-macsmc"
"simple-mfd-spmi"
"spmi-apple-controller"
"nvmem_spmi_mfd"
"apple-dockchannel"
"dockchannel-hid"
"apple-rtkit-helper"
"dm_crypt"
# additional stuff necessary to boot off USB for the installer
# and if the initrd (i.e. stage 1) goes wrong
"usb-storage"
"xhci-plat-hcd"
"usbhid"
"hid_generic"
];
boot.kernelParams = [
"earlycon"
"console=ttySAC0,1500000"
"console=tty0"
"boot.shell_on_fail"
# Apple's SSDs are slow (~dozens of ms) at processing flush requests which
# slows down programs that make a lot of fsync calls. This parameter sets
# a delay in ms before actually flushing so that such requests can be
# coalesced. Be warned that increasing this parameter above zero (default
# is 1000) has the potential, though admittedly unlikely, risk of
# UNBOUNDED data corruption in case of power loss!!!! Don't even think
# about it on desktops!!
"nvme_apple.flush_interval=1000"
];
# U-Boot does not support EFI variables
boot.loader.efi.canTouchEfiVariables = lib.mkForce false;
# GRUB has to be installed as removable if the user chooses to use it
boot.loader.grub = lib.mkDefault {
version = 2;
efiSupport = true;
efiInstallAsRemovable = true;
device = "nodev";
};
};
imports = [
(lib.mkRemovedOptionModule [ "boot" "kernelBuildIsCross" ] ''
If it should still be true (which is unlikely), replace it
with 'hardware.asahi.pkgsSystem = "x86_64-linux"'. Otherwise, delete it.
'')
(lib.mkRemovedOptionModule [ "boot" "kernelBuildIs16K" ] ''
Replaced with 'hardware.asahi.use4KPages' which defaults to false.
'')
];
options.hardware.asahi.use4KPages = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Build the Asahi Linux kernel with 4K pages to improve compatibility in
some cases at the cost of performance in others.
'';
};
}

View File

@@ -0,0 +1,63 @@
{ pkgs, _4KBuild ? false }: let
localPkgs =
# we do this so the config can be read on any system and not affect
# the output hash
if builtins ? currentSystem then import (pkgs.path) { system = builtins.currentSystem; }
else pkgs;
readConfig = configfile: import (localPkgs.runCommand "config.nix" {} ''
echo "{" > "$out"
while IFS='=' read key val; do
[ "x''${key#CONFIG_}" != "x$key" ] || continue
no_firstquote="''${val#\"}";
echo ' "'"$key"'" = "'"''${no_firstquote%\"}"'";' >> "$out"
done < "${configfile}"
echo "}" >> $out
'').outPath;
linux_asahi_pkg = { stdenv, lib, fetchFromGitHub, fetchpatch, linuxKernel, ... } @ args:
linuxKernel.manualConfig rec {
inherit stdenv lib;
version = "6.1.0-rc6-asahi";
modDirVersion = version;
src = fetchFromGitHub {
# tracking: https://github.com/AsahiLinux/PKGBUILDs/blob/stable/linux-asahi/PKGBUILD
owner = "AsahiLinux";
repo = "linux";
rev = "asahi-6.1-rc6-5";
hash = "sha256-HHPfAtNh5wR0TCsEYuMdSbp55p1IVhF07tg4dlfgXk0=";
};
kernelPatches = [
# sven says this is okay since our kernel config supports it, and that
# it will be fixed at some point to not be necessary. but this allows
# new kernels to get USB up with old device trees
{ name = "0001-drivers-usb-dwc3-remove-apple-dr_mode-check";
patch = ./0001-drivers-usb-dwc3-remove-apple-dr_mode-check.patch;
}
] ++ lib.optionals _4KBuild [
# thanks to Sven Peter
# https://lore.kernel.org/linux-iommu/20211019163737.46269-1-sven@svenpeter.dev/
{ name = "sven-iommu-4k";
patch = ./sven-iommu-4k.patch;
}
] ++ lib.optionals (!_4KBuild) [
# patch the kernel to set the default size to 16k instead of modifying
# the config so we don't need to convert our config to the nixos
# infrastructure or patch it and thus introduce a dependency on the host
# system architecture
{ name = "default-pagesize-16k";
patch = ./default-pagesize-16k.patch;
}
];
configfile = ./config;
config = readConfig configfile;
extraMeta.branch = "6.1";
} // (args.argsOverride or {});
linux_asahi = (pkgs.callPackage linux_asahi_pkg { });
in pkgs.recurseIntoAttrs (pkgs.linuxPackagesFor linux_asahi)

View File

@@ -0,0 +1,449 @@
diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c
index 4f1a37bdd42d..c8c3ea81d818 100644
--- a/drivers/iommu/apple-dart.c
+++ b/drivers/iommu/apple-dart.c
@@ -97,7 +97,6 @@ struct apple_dart_hw {
* @lock: lock for hardware operations involving this dart
* @pgsize: pagesize supported by this DART
* @supports_bypass: indicates if this DART supports bypass mode
- * @force_bypass: force bypass mode due to pagesize mismatch?
* @sid2group: maps stream ids to iommu_groups
* @iommu: iommu core device
*/
@@ -115,7 +114,6 @@ struct apple_dart {
u32 pgsize;
u32 supports_bypass : 1;
- u32 force_bypass : 1;
struct iommu_group *sid2group[DART_MAX_STREAMS];
struct iommu_device iommu;
@@ -499,9 +497,6 @@ static int apple_dart_attach_dev(struct iommu_domain *domain,
struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
struct apple_dart_domain *dart_domain = to_dart_domain(domain);
- if (cfg->stream_maps[0].dart->force_bypass &&
- domain->type != IOMMU_DOMAIN_IDENTITY)
- return -EINVAL;
if (!cfg->stream_maps[0].dart->supports_bypass &&
domain->type == IOMMU_DOMAIN_IDENTITY)
return -EINVAL;
@@ -630,8 +625,6 @@ static int apple_dart_of_xlate(struct device *dev, struct of_phandle_args *args)
if (cfg_dart) {
if (cfg_dart->supports_bypass != dart->supports_bypass)
return -EINVAL;
- if (cfg_dart->force_bypass != dart->force_bypass)
- return -EINVAL;
if (cfg_dart->pgsize != dart->pgsize)
return -EINVAL;
}
@@ -736,8 +729,6 @@ static int apple_dart_def_domain_type(struct device *dev)
{
struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
- if (cfg->stream_maps[0].dart->force_bypass)
- return IOMMU_DOMAIN_IDENTITY;
if (!cfg->stream_maps[0].dart->supports_bypass)
return IOMMU_DOMAIN_DMA;
@@ -1121,8 +1121,6 @@ static int apple_dart_probe(struct platform_device *pdev)
goto err_clk_disable;
}
- dart->force_bypass = dart->pgsize > PAGE_SIZE;
-
ret = apple_dart_hw_reset(dart);
if (ret)
goto err_clk_disable;
@@ -1149,8 +1147,8 @@ static int apple_dart_probe(struct platform_device *pdev)
dev_info(
&pdev->dev,
- "DART [pagesize %x, %d streams, bypass support: %d, bypass forced: %d] initialized\n",
- dart->pgsize, dart->num_streams, dart->supports_bypass, dart->force_bypass);
+ "DART [pagesize %x, %d streams, bypass support: %d] initialized\n",
+ dart->pgsize, dart->num_streams, dart->supports_bypass);
return 0;
err_sysfs_remove:
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 09f6e1c0f9c0..094592751cfa 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -20,9 +20,11 @@
#include <linux/iommu.h>
#include <linux/iova.h>
#include <linux/irq.h>
+#include <linux/kernel.h>
#include <linux/list_sort.h>
#include <linux/mm.h>
#include <linux/mutex.h>
+#include <linux/pfn.h>
#include <linux/pci.h>
#include <linux/scatterlist.h>
#include <linux/spinlock.h>
@@ -710,6 +712,9 @@ static struct page **__iommu_dma_alloc_pages(struct device *dev,
{
struct page **pages;
unsigned int i = 0, nid = dev_to_node(dev);
+ unsigned int j;
+ unsigned long min_order = __fls(order_mask);
+ unsigned int min_order_size = 1U << min_order;
order_mask &= (2U << MAX_ORDER) - 1;
if (!order_mask)
@@ -749,15 +754,37 @@ static struct page **__iommu_dma_alloc_pages(struct device *dev,
split_page(page, order);
break;
}
- if (!page) {
- __iommu_dma_free_pages(pages, i);
- return NULL;
+
+ /*
+ * If we have no valid page here we might be trying to allocate
+ * the last block consisting of 1<<order pages (to guarantee
+ * alignment) but actually need less pages than that.
+ * In that case we just try to allocate the entire block and
+ * directly free the spillover pages again.
+ */
+ if (!page && !order_mask && count < min_order_size) {
+ page = alloc_pages_node(nid, gfp, min_order);
+ if (!page)
+ goto free_pages;
+ split_page(page, min_order);
+
+ for (j = count; j < min_order_size; ++j)
+ __free_page(page + j);
+
+ order_size = count;
}
+
+ if (!page)
+ goto free_pages;
count -= order_size;
while (order_size--)
pages[i++] = page++;
}
return pages;
+
+free_pages:
+ __iommu_dma_free_pages(pages, i);
+ return NULL;
}
/*
@@ -785,16 +787,28 @@ static struct page **__iommu_dma_alloc_noncontiguous(struct device *dev,
bool coherent = dev_is_dma_coherent(dev);
int ioprot = dma_info_to_prot(DMA_BIDIRECTIONAL, coherent, attrs);
unsigned int count, min_size, alloc_sizes = domain->pgsize_bitmap;
+ struct sg_append_table sgt_append = {};
+ struct scatterlist *last_sg;
struct page **pages;
dma_addr_t iova;
ssize_t ret;
+ phys_addr_t orig_s_phys;
+ size_t orig_s_len, orig_s_off, s_iova_off, iova_size;
if (static_branch_unlikely(&iommu_deferred_attach_enabled) &&
iommu_deferred_attach(dev, domain))
return NULL;
min_size = alloc_sizes & -alloc_sizes;
- if (min_size < PAGE_SIZE) {
+ if (iovad->granule > PAGE_SIZE) {
+ if (size < iovad->granule) {
+ /* ensure a single contiguous allocation */
+ min_size = ALIGN(size, PAGE_SIZE*(1U<<get_order(size)));
+ alloc_sizes = min_size;
+ }
+
+ size = PAGE_ALIGN(size);
+ } else if (min_size < PAGE_SIZE) {
min_size = PAGE_SIZE;
alloc_sizes |= PAGE_SIZE;
} else {
@@ -797,13 +836,17 @@ static struct page **__iommu_dma_alloc_noncontiguous(struct device *dev,
if (!pages)
return NULL;
- size = iova_align(iovad, size);
- iova = iommu_dma_alloc_iova(domain, size, dev->coherent_dma_mask, dev);
+ iova_size = iova_align(iovad, size);
+ iova = iommu_dma_alloc_iova(domain, iova_size, dev->coherent_dma_mask, dev);
if (!iova)
goto out_free_pages;
- if (sg_alloc_table_from_pages(sgt, pages, count, 0, size, GFP_KERNEL))
+ /* append_table is only used to get a pointer to the last entry */
+ if (sg_alloc_append_table_from_pages(&sgt_append, pages, count, 0,
+ iova_size, UINT_MAX, 0, GFP_KERNEL))
goto out_free_iova;
+ memcpy(sgt, &sgt_append.sgt, sizeof(*sgt));
+ last_sg = sgt_append.prv;
if (!(ioprot & IOMMU_CACHE)) {
struct scatterlist *sg;
@@ -825,18 +839,59 @@ static struct page **__iommu_dma_alloc_noncontiguous(struct device *dev,
arch_dma_prep_coherent(sg_page(sg), sg->length);
}
+ if (iovad->granule > PAGE_SIZE) {
+ if (size < iovad->granule) {
+ /*
+ * we only have a single sg list entry here that is
+ * likely not aligned to iovad->granule. adjust the
+ * entry to represent the encapsulating IOMMU page
+ * and then later restore everything to its original
+ * values, similar to the impedance matching done in
+ * iommu_dma_map_sg.
+ */
+ orig_s_phys = sg_phys(sgt->sgl);
+ orig_s_len = sgt->sgl->length;
+ orig_s_off = sgt->sgl->offset;
+ s_iova_off = iova_offset(iovad, orig_s_phys);
+
+ sg_set_page(sgt->sgl,
+ pfn_to_page(PHYS_PFN(orig_s_phys - s_iova_off)),
+ iova_align(iovad, orig_s_len + s_iova_off),
+ sgt->sgl->offset & ~s_iova_off);
+ } else {
+ /*
+ * convince iommu_map_sg_atomic to map the last block
+ * even though it may be too small.
+ */
+ orig_s_len = last_sg->length;
+ last_sg->length = iova_align(iovad, last_sg->length);
+ }
+ }
+
ret = iommu_map_sg_atomic(domain, iova, sgt->sgl, sgt->orig_nents, ioprot);
- if (ret < 0 || ret < size)
+ if (ret < 0 || ret < iova_size)
goto out_free_sg;
+ if (iovad->granule > PAGE_SIZE) {
+ if (size < iovad->granule) {
+ sg_set_page(sgt->sgl,
+ pfn_to_page(PHYS_PFN(orig_s_phys)),
+ orig_s_len, orig_s_off);
+
+ iova += s_iova_off;
+ } else {
+ last_sg->length = orig_s_len;
+ }
+ }
+
sgt->sgl->dma_address = iova;
- sgt->sgl->dma_length = size;
+ sgt->sgl->dma_length = iova_size;
return pages;
out_free_sg:
sg_free_table(sgt);
out_free_iova:
- iommu_dma_free_iova(cookie, iova, size, NULL);
+ iommu_dma_free_iova(cookie, iova, iova_size, NULL);
out_free_pages:
__iommu_dma_free_pages(pages, count);
return NULL;
@@ -1040,8 +1124,9 @@ static int __finalise_sg(struct device *dev, struct scatterlist *sg, int nents,
unsigned int s_length = sg_dma_len(s);
unsigned int s_iova_len = s->length;
- s->offset += s_iova_off;
- s->length = s_length;
+ sg_set_page(s,
+ pfn_to_page(PHYS_PFN(sg_phys(s) + s_iova_off)),
+ s_length, s_iova_off & ~PAGE_MASK);
sg_dma_address(s) = DMA_MAPPING_ERROR;
sg_dma_len(s) = 0;
@@ -1082,13 +1167,17 @@ static int __finalise_sg(struct device *dev, struct scatterlist *sg, int nents,
static void __invalidate_sg(struct scatterlist *sg, int nents)
{
struct scatterlist *s;
+ phys_addr_t orig_paddr;
int i;
for_each_sg(sg, s, nents, i) {
- if (sg_dma_address(s) != DMA_MAPPING_ERROR)
- s->offset += sg_dma_address(s);
- if (sg_dma_len(s))
- s->length = sg_dma_len(s);
+ if (sg_dma_len(s)) {
+ orig_paddr = sg_phys(s) + sg_dma_address(s);
+ sg_set_page(s,
+ pfn_to_page(PHYS_PFN(orig_paddr)),
+ sg_dma_len(s),
+ sg_dma_address(s) & ~PAGE_MASK);
+ }
sg_dma_address(s) = DMA_MAPPING_ERROR;
sg_dma_len(s) = 0;
}
@@ -1166,15 +1255,16 @@ static int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
* stashing the unaligned parts in the as-yet-unused DMA fields.
*/
for_each_sg(sg, s, nents, i) {
- size_t s_iova_off = iova_offset(iovad, s->offset);
+ phys_addr_t s_phys = sg_phys(s);
+ size_t s_iova_off = iova_offset(iovad, s_phys);
size_t s_length = s->length;
size_t pad_len = (mask - iova_len + 1) & mask;
sg_dma_address(s) = s_iova_off;
sg_dma_len(s) = s_length;
- s->offset -= s_iova_off;
s_length = iova_align(iovad, s_length + s_iova_off);
- s->length = s_length;
+ sg_set_page(s, pfn_to_page(PHYS_PFN(s_phys - s_iova_off)),
+ s_length, s->offset & ~s_iova_off);
/*
* Due to the alignment of our single IOVA allocation, we can
@@ -1412,9 +1502,15 @@ static int iommu_dma_get_sgtable(struct device *dev, struct sg_table *sgt,
void *cpu_addr, dma_addr_t dma_addr, size_t size,
unsigned long attrs)
{
+ struct iommu_domain *domain = iommu_get_dma_domain(dev);
+ struct iommu_dma_cookie *cookie = domain->iova_cookie;
+ struct iova_domain *iovad = &cookie->iovad;
struct page *page;
int ret;
+ if (iovad->granule > PAGE_SIZE)
+ return -ENXIO;
+
if (is_vmalloc_addr(cpu_addr)) {
struct page **pages = dma_common_find_pages(cpu_addr);
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index f2c45b85b9fc..0c370e486d6e 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -80,6 +80,8 @@ static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus,
unsigned type);
static int __iommu_attach_device(struct iommu_domain *domain,
struct device *dev);
+static void __iommu_detach_device(struct iommu_domain *domain,
+ struct device *dev);
static int __iommu_attach_group(struct iommu_domain *domain,
struct iommu_group *group);
static void __iommu_detach_group(struct iommu_domain *domain,
@@ -1976,6 +1978,24 @@ void iommu_domain_free(struct iommu_domain *domain)
}
EXPORT_SYMBOL_GPL(iommu_domain_free);
+static int iommu_check_page_size(struct iommu_domain *domain,
+ struct device *dev)
+{
+ bool trusted = !(dev_is_pci(dev) && to_pci_dev(dev)->untrusted);
+
+ if (!iommu_is_paging_domain(domain))
+ return 0;
+ if (iommu_is_large_pages_domain(domain) && trusted)
+ return 0;
+
+ if (!(domain->pgsize_bitmap & (PAGE_SIZE | (PAGE_SIZE - 1)))) {
+ pr_warn("IOMMU pages cannot exactly represent CPU pages.\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
static int __iommu_attach_device(struct iommu_domain *domain,
struct device *dev)
{
@@ -1985,9 +2005,23 @@ static int __iommu_attach_device(struct iommu_domain *domain,
return -ENODEV;
ret = domain->ops->attach_dev(domain, dev);
- if (!ret)
- trace_attach_device_to_domain(dev);
- return ret;
+ if (ret)
+ return ret;
+
+ /*
+ * Check that CPU pages can be represented by the IOVA granularity.
+ * This has to be done after ops->attach_dev since many IOMMU drivers
+ * only limit domain->pgsize_bitmap after having attached the first
+ * device.
+ */
+ ret = iommu_check_page_size(domain, dev);
+ if (ret) {
+ __iommu_detach_device(domain, dev);
+ return ret;
+ }
+
+ trace_attach_device_to_domain(dev);
+ return 0;
}
int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
index db77aa675145..180ce65a6789 100644
--- a/drivers/iommu/iova.c
+++ b/drivers/iommu/iova.c
@@ -49,10 +49,11 @@ init_iova_domain(struct iova_domain *iovad, unsigned long granule,
{
/*
* IOVA granularity will normally be equal to the smallest
- * supported IOMMU page size; both *must* be capable of
- * representing individual CPU pages exactly.
+ * supported IOMMU page size; while both usually are capable of
+ * representing individual CPU pages exactly the IOVA allocator
+ * supports any granularities that are an exact power of two.
*/
- BUG_ON((granule > PAGE_SIZE) || !is_power_of_2(granule));
+ BUG_ON(!is_power_of_2(granule));
spin_lock_init(&iovad->iova_rbtree_lock);
iovad->rbroot = RB_ROOT;
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 9208eca4b0d1..dec2dd70a876 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -63,6 +63,8 @@ struct iommu_domain_geometry {
implementation */
#define __IOMMU_DOMAIN_PT (1U << 2) /* Domain is identity mapped */
#define __IOMMU_DOMAIN_DMA_FQ (1U << 3) /* DMA-API uses flush queue */
+#define __IOMMU_DOMAIN_LP (1U << 4) /* Support for PAGE_SIZE smaller
+ than IOMMU page size */
/*
* This are the possible domain-types
@@ -82,10 +84,12 @@ struct iommu_domain_geometry {
#define IOMMU_DOMAIN_IDENTITY (__IOMMU_DOMAIN_PT)
#define IOMMU_DOMAIN_UNMANAGED (__IOMMU_DOMAIN_PAGING)
#define IOMMU_DOMAIN_DMA (__IOMMU_DOMAIN_PAGING | \
- __IOMMU_DOMAIN_DMA_API)
+ __IOMMU_DOMAIN_DMA_API | \
+ __IOMMU_DOMAIN_LP)
#define IOMMU_DOMAIN_DMA_FQ (__IOMMU_DOMAIN_PAGING | \
__IOMMU_DOMAIN_DMA_API | \
- __IOMMU_DOMAIN_DMA_FQ)
+ __IOMMU_DOMAIN_DMA_FQ | \
+ __IOMMU_DOMAIN_LP)
struct iommu_domain {
unsigned type;
@@ -102,6 +106,16 @@ static inline bool iommu_is_dma_domain(struct iommu_domain *domain)
return domain->type & __IOMMU_DOMAIN_DMA_API;
}
+static inline bool iommu_is_paging_domain(struct iommu_domain *domain)
+{
+ return domain->type & __IOMMU_DOMAIN_PAGING;
+}
+
+static inline bool iommu_is_large_pages_domain(struct iommu_domain *domain)
+{
+ return domain->type & __IOMMU_DOMAIN_LP;
+}
+
enum iommu_cap {
IOMMU_CAP_CACHE_COHERENCY, /* IOMMU can enforce cache coherent DMA
transactions */

View File

@@ -0,0 +1,96 @@
{ stdenv
, lib
, fetchFromGitHub
, pkgsCross
, python3
, dtc
, imagemagick
, isRelease ? false
, withTools ? true
, withChainloading ? false
, rust-bin ? null
, customLogo ? null
}:
assert withChainloading -> rust-bin != null;
let
pyenv = python3.withPackages (p: with p; [
construct
pyserial
]);
rustenv = rust-bin.selectLatestNightlyWith (toolchain: toolchain.minimal.override {
targets = [ "aarch64-unknown-none-softfloat" ];
});
in stdenv.mkDerivation rec {
pname = "m1n1";
version = "1.1.8";
src = fetchFromGitHub {
# tracking: https://github.com/AsahiLinux/PKGBUILDs/blob/stable/m1n1/PKGBUILD
owner = "AsahiLinux";
repo = "m1n1";
rev = "v${version}";
hash = "sha256-4Ykh+EzOCRtZQD1upUDJpi5ikMOCnxLwvLWajtMo7LU=";
fetchSubmodules = true;
};
makeFlags = [ "ARCH=aarch64-unknown-linux-gnu-" ]
++ lib.optional isRelease "RELEASE=1"
++ lib.optional withChainloading "CHAINLOADING=1";
nativeBuildInputs = [
dtc
pkgsCross.aarch64-multiplatform.buildPackages.gcc
] ++ lib.optional withChainloading rustenv
++ lib.optional (customLogo != null) imagemagick;
postPatch = ''
substituteInPlace proxyclient/m1n1/asm.py \
--replace 'aarch64-linux-gnu-' 'aarch64-unknown-linux-gnu-' \
--replace 'TOOLCHAIN = ""' 'TOOLCHAIN = "'$out'/toolchain-bin/"'
'';
preConfigure = lib.optionalString (customLogo != null) ''
pushd data &>/dev/null
ln -fs ${customLogo} bootlogo_256.png
if [[ "$(magick identify bootlogo_256.png)" != 'bootlogo_256.png PNG 256x256'* ]]; then
echo "Custom logo is not a 256x256 PNG"
exit 1
fi
rm bootlogo_128.png
convert bootlogo_256.png -resize 128x128 bootlogo_128.png
./makelogo.sh
popd &>/dev/null
'';
installPhase = ''
runHook preInstall
mkdir -p $out/build
cp build/m1n1.macho $out/build
cp build/m1n1.bin $out/build
'' + (lib.optionalString withTools ''
mkdir -p $out/{bin,script,toolchain-bin}
cp -r proxyclient $out/script
cp -r tools $out/script
for toolpath in $out/script/proxyclient/tools/*.py; do
tool=$(basename $toolpath .py)
script=$out/bin/m1n1-$tool
cat > $script <<EOF
#!/bin/sh
${pyenv}/bin/python $toolpath "\$@"
EOF
chmod +x $script
done
GCC=${pkgsCross.aarch64-multiplatform.buildPackages.gcc}
BINUTILS=${pkgsCross.aarch64-multiplatform.buildPackages.binutils-unwrapped}
ln -s $GCC/bin/*-gcc $out/toolchain-bin/
ln -s $GCC/bin/*-ld $out/toolchain-bin/
ln -s $BINUTILS/bin/*-objcopy $out/toolchain-bin/
ln -s $BINUTILS/bin/*-objdump $out/toolchain-bin/
ln -s $GCC/bin/*-nm $out/toolchain-bin/
'') + ''
runHook postInstall
'';
}

View File

@@ -0,0 +1,70 @@
{ config, pkgs, lib, ... }:
{
config = {
assertions = lib.mkIf config.hardware.asahi.extractPeripheralFirmware [
{ assertion = config.hardware.asahi.peripheralFirmwareDirectory != null;
message = ''
Asahi peripheral firmware extraction is enabled but the firmware
location appears incorrect.
'';
}
];
hardware.firmware = let
asahi-fwextract = pkgs.callPackage ../asahi-fwextract {};
in lib.mkIf ((config.hardware.asahi.peripheralFirmwareDirectory != null)
&& config.hardware.asahi.extractPeripheralFirmware) [
(pkgs.stdenv.mkDerivation {
name = "asahi-peripheral-firmware";
nativeBuildInputs = [ asahi-fwextract pkgs.cpio ];
buildCommand = ''
mkdir extracted
asahi-fwextract ${/. + config.hardware.asahi.peripheralFirmwareDirectory} extracted
mkdir -p $out/lib/firmware
cat extracted/firmware.cpio | cpio -id --quiet --no-absolute-filenames
mv vendorfw/* $out/lib/firmware
'';
})
];
};
options.hardware.asahi = {
extractPeripheralFirmware = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Automatically extract the non-free non-redistributable peripheral
firmware necessary for features like Wi-Fi.
'';
};
peripheralFirmwareDirectory = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = let
paths = [
# path when the system is operating normally
"/boot/asahi"
# path when the system is mounted in the installer
"/mnt/boot/asahi"
./.
];
validPaths = (builtins.filter
(p: builtins.pathExists (p + "/all_firmware.tar.gz"))
paths) ++ [ null ];
in builtins.elemAt validPaths 0;
description = ''
Path to the directory containing the non-free non-redistributable
peripheral firmware necessary for features like Wi-Fi. Ordinarily, this
will automatically point to the appropriate location on the ESP. Flake
users and those interested in maximum purity will want to copy those
files elsewhere and specify this manually.
Currently, this consists of the files `all-firmware.tar.gz` and
`kernelcache*`. The official Asahi Linux installer places these files
in the `asahi` directory of the EFI system partition when creating it.
'';
};
};
}

View File

@@ -0,0 +1,40 @@
{ lib
, fetchFromGitHub
, pkgs
, pkgsCross
, m1n1
}: let
# u-boot's buildInputs get a different hash and don't build right if we try to
# cross-build for aarch64 on itself for whatever reason
buildPkgs = if pkgs.stdenv.system == "aarch64-linux" then pkgs else pkgsCross.aarch64-multiplatform;
in (buildPkgs.buildUBoot rec {
src = fetchFromGitHub {
# tracking: https://github.com/AsahiLinux/PKGBUILDs/blob/stable/uboot-asahi/PKGBUILD
owner = "AsahiLinux";
repo = "u-boot";
rev = "asahi-v2022.10-1";
hash = "sha256-/dtTJ+GxC2GFlqduAa2WWPGwktLjM7tUKus6/aUyPNQ=";
};
version = "2022.10.asahi1-1";
defconfig = "apple_m1_defconfig";
extraMeta.platforms = [ "aarch64-linux" ];
filesToInstall = [
"u-boot-nodtb.bin.gz"
"m1n1-u-boot.macho"
"m1n1-u-boot.bin"
];
extraConfig = ''
CONFIG_IDENT_STRING=" ${version}"
'';
}).overrideAttrs (o: {
# nixos's downstream patches are not applicable
patches = [ ];
preInstall = ''
# compress so that m1n1 knows U-Boot's size and can find things after it
gzip -n u-boot-nodtb.bin
cat ${m1n1}/build/m1n1.macho arch/arm/dts/t[68]*.dtb u-boot-nodtb.bin.gz > m1n1-u-boot.macho
cat ${m1n1}/build/m1n1.bin arch/arm/dts/t[68]*.dtb u-boot-nodtb.bin.gz > m1n1-u-boot.bin
'';
})

View File

@@ -1,14 +0,0 @@
{ config, pkgs, lib, ... }:
{
imports = [
./hardware-configuration.nix
];
services.gitea-runner = {
enable = true;
instanceUrl = "https://git.neet.dev";
};
system.autoUpgrade.enable = true;
}

View File

@@ -1,43 +0,0 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
imports =
[
(modulesPath + "/profiles/qemu-guest.nix")
];
# because grub just doesn't work for some reason
boot.loader.systemd-boot.enable = true;
remoteLuksUnlock.enable = true;
remoteLuksUnlock.enableTorUnlock = false;
boot.initrd.availableKernelModules = [ "xhci_pci" ];
boot.initrd.kernelModules = [ "dm-snapshot" ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
luks.devices = [ "/dev/disk/by-uuid/d26c1820-4c39-4615-98c2-51442504e194" ];
fileSystems."/" =
{
device = "/dev/disk/by-uuid/851bfde6-93cd-439e-9380-de28aa87eda9";
fsType = "btrfs";
};
fileSystems."/boot" =
{
device = "/dev/disk/by-uuid/F185-C4E5";
fsType = "vfat";
};
swapDevices =
[{ device = "/dev/disk/by-uuid/d809e3a1-3915-405a-a200-4429c5efdf87"; }];
networking.interfaces.enp0s6.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux";
}

View File

@@ -1,20 +0,0 @@
{
hostNames = [
"phil"
"phil.neet.dev"
];
arch = "aarch64-linux";
systemRoles = [
"server"
"gitea-runner"
];
hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBlgRPpuUkZqe8/lHugRPm/m2vcN9psYhh5tENHZt9I2";
remoteUnlock = {
hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK0RodotOXLMy/w70aa096gaNqPBnfgiXR5ZAH4+wGzd";
clearnetHost = "unlock.phil.neet.dev";
};
}

View File

@@ -5,27 +5,29 @@
./hardware-configuration.nix
];
networking.hostName = "ponyo";
firmware.x86_64.enable = true;
bios = {
enable = true;
device = "/dev/sda";
};
luks = {
enable = true;
device.path = "/dev/disk/by-uuid/4cc36be4-dbff-4afe-927d-69bf4637bae2";
};
system.autoUpgrade.enable = true;
# I want to manually trigger kexec updates for now on ponyo
system.autoUpgrade.allowKexec = false;
luks.enableKexec = true;
# p2p mesh network
services.tailscale.exitNode = true;
services.zerotierone.enable = true;
# email server
mailserver.enable = true;
# nextcloud
services.nextcloud.enable = true;
# git
services.gitea = {
enable = true;
hostname = "git.neet.dev";
disableRegistration = true;
};
# IRC
services.thelounge = {
enable = true;
port = 9000;
@@ -37,14 +39,12 @@
};
};
# mumble
services.murmur = {
enable = true;
port = 23563;
domain = "voice.neet.space";
};
# IRC bot
services.drastikbot = {
enable = true;
wolframAppIdFile = "/run/agenix/wolframalpha";
@@ -53,11 +53,8 @@
file = ../../secrets/wolframalpha.age;
owner = config.services.drastikbot.user;
};
backup.group."dailybot".paths = [
config.services.drastikbot.dataDir
];
# music radio
# wrap radio in a VPN
vpn-container.enable = true;
vpn-container.config = {
services.radio = {
@@ -65,7 +62,11 @@
host = "radio.runyan.org";
};
};
pia.wireguard.badPortForwardPorts = [ ];
# tailscale
services.tailscale.exitNode = true;
# icecast endpoint + website
services.nginx.virtualHosts."radio.runyan.org" = {
enableACME = true;
forceSSL = true;
@@ -80,7 +81,6 @@
};
};
# matrix home server
services.matrix = {
enable = true;
host = "neet.space";
@@ -98,36 +98,67 @@
secret = "a8369a0e96922abf72494bb888c85831b";
};
};
# pin postgresql for matrix (will need to migrate eventually)
services.postgresql.package = pkgs.postgresql_11;
# iodine DNS-based vpn
services.iodine.server.enable = true;
services.searx = {
enable = true;
environmentFile = "/run/agenix/searx";
settings = {
server.port = 43254;
server.secret_key = "@SEARX_SECRET_KEY@";
engines = [ {
name = "wolframalpha";
shortcut = "wa";
api_key = "@WOLFRAM_API_KEY@";
engine = "wolframalpha_api";
} ];
};
};
services.nginx.virtualHosts."search.neet.space" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:${toString config.services.searx.settings.server.port}";
};
};
age.secrets.searx.file = ../../secrets/searx.age;
# iodine DNS-based vpn
services.iodine.server = {
enable = true;
ip = "192.168.99.1";
domain = "tun.neet.dev";
passwordFile = "/run/agenix/iodine";
};
age.secrets.iodine.file = ../../secrets/iodine.age;
networking.firewall.allowedUDPPorts = [ 53 ];
networking.nat.internalInterfaces = [
"dns0" # iodine
];
# proxied web services
services.nginx.enable = true;
services.nginx.virtualHosts."jellyfin.neet.cloud" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://s0.koi-bebop.ts.net";
proxyPass = "http://s0.zt.neet.dev";
proxyWebsockets = true;
};
};
services.nginx.virtualHosts."navidrome.neet.cloud" = {
enableACME = true;
forceSSL = true;
locations."/".proxyPass = "http://s0.koi-bebop.ts.net:4533";
locations."/".proxyPass = "http://s0.zt.neet.dev:4533";
};
# TODO replace with a proper file hosting service
services.nginx.virtualHosts."tmp.neet.dev" = {
enableACME = true;
forceSSL = true;
root = "/var/www/tmp";
};
# redirect runyan.org to github
# redirect to github
services.nginx.virtualHosts."runyan.org" = {
enableACME = true;
forceSSL = true;
@@ -136,7 +167,6 @@
'';
};
# owncast live streaming
services.owncast.enable = true;
services.owncast.hostname = "live.neet.dev";
}

View File

@@ -2,8 +2,7 @@
{
imports =
[
(modulesPath + "/profiles/qemu-guest.nix")
[ (modulesPath + "/profiles/qemu-guest.nix")
];
boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "virtio_pci" "virtio_scsi" "sd_mod" ];
@@ -11,29 +10,13 @@
boot.kernelModules = [ "kvm-intel" "nvme" ];
boot.extraModulePackages = [ ];
firmware.x86_64.enable = true;
bios = {
enable = true;
device = "/dev/sda";
};
remoteLuksUnlock.enable = true;
luks.devices = [
"/dev/disk/by-uuid/4cc36be4-dbff-4afe-927d-69bf4637bae2"
"/dev/disk/by-uuid/e52b01b3-81c8-4bb2-ae7e-a3d9c793cb00"
];
fileSystems."/" =
{
device = "/dev/mapper/enc-pv1";
{ device = "/dev/mapper/enc-pv";
fsType = "btrfs";
};
fileSystems."/boot" =
{
device = "/dev/disk/by-uuid/d3a3777d-1e70-47fa-a274-804dc70ee7fd";
{ device = "/dev/disk/by-uuid/d3a3777d-1e70-47fa-a274-804dc70ee7fd";
fsType = "ext4";
};
@@ -44,5 +27,11 @@
}
];
networking.interfaces.eth0.useDHCP = true;
# The global useDHCP flag is deprecated, therefore explicitly set to false here.
# Per-interface useDHCP will be mandatory in the future, so this generated config
# replicates the default behaviour.
networking.useDHCP = lib.mkDefault false;
networking.interfaces.eth0.useDHCP = lib.mkDefault true;
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View File

@@ -1,28 +0,0 @@
{
hostNames = [
"ponyo"
"ponyo.neet.dev"
"git.neet.dev"
];
arch = "x86_64-linux";
systemRoles = [
"server"
"email-server"
"iodine"
"pia"
"nextcloud"
"dailybot"
"gitea"
];
hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMBBlTAIp38RhErU1wNNV5MBeb+WGH0mhF/dxh5RsAXN";
remoteUnlock = {
hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC9LQuuImgWlkjDhEEIbM1wOd+HqRv1RxvYZuLXPSdRi";
clearnetHost = "unlock.ponyo.neet.dev";
onionHost = "cfamr6artx75qvt7ho3rrbsc7mkucmv5aawebwflsfuorusayacffryd.onion";
};
}

Some files were not shown because too many files have changed in this diff Show More