From ed9e687dbe9cd696215bb22052a3a437dda0b3bf Mon Sep 17 00:00:00 2001 From: Aaron Bieber Date: Thu, 25 Aug 2022 12:21:35 -0600 Subject: [PATCH] hello world! --- .allowed_signers | 3 + .envrc | 1 + .gitignore | 4 + LICENSE | 15 + README.md | 82 + bins/check-restart.nix | 22 + bins/default.nix | 12 + bins/ix.nix | 38 + bins/rage.nix | 87 ++ bins/sfetch.nix | 23 + boot | 13 + check-restart | 38 + common.sh | 56 + configs/colemak.nix | 22 + configs/develop.nix | 15 + configs/dns.nix | 32 + configs/doas.nix | 23 + configs/emacs.nix | 60 + configs/emacs.org | 1488 ++++++++++++++++++ configs/git.nix | 65 + configs/gitmux.nix | 35 + configs/manager.nix | 24 + configs/neomutt.nix | 145 ++ configs/neovim.lua | 38 + configs/neovim.nix | 35 + configs/net-overlay.nix | 38 + configs/tmux.nix | 60 + configs/zsh.nix | 57 + dbuild/build-consumer.nix | 36 + dbuild/build-server.nix | 18 + dbuild/default.nix | 4 + default.nix | 142 ++ deploy | 112 ++ flake.lock | 262 ++++ flake.nix | 155 ++ fmt | 6 + gui/arcan.nix | 11 + gui/default.nix | 72 + gui/gnome.nix | 9 + gui/kde.nix | 29 + gui/xfce.nix | 9 + hosts/.hass/default.nix | 35 + hosts/.hass/hardware-configuration.nix | 47 + hosts/.nerm/default.nix | 61 + hosts/.nerm/hardware-configuration.nix | 23 + hosts/box/default.nix | 740 +++++++++ hosts/box/hardware-configuration.nix | 76 + hosts/europa/default.nix | 202 +++ hosts/europa/hardware-configuration.nix | 71 + hosts/faf/default.nix | 112 ++ hosts/faf/hardware-configuration.nix | 72 + hosts/h/alias | 1 + hosts/h/default.nix | 435 ++++++ hosts/h/hardware-configuration.nix | 25 + hosts/litr/default.nix | 128 ++ hosts/litr/hardware-configuration.nix | 37 + hosts/plq/default.nix | 63 + hosts/weather/default.nix | 197 +++ hosts/weather/hardware-configuration.nix | 24 + installer.nix | 133 ++ overlays/default.nix | 43 + pkgs/athens.nix | 45 + pkgs/cinny-desktop.nix | 58 + pkgs/default.nix | 8 + pkgs/gitmux.nix | 35 + pkgs/got-sigs.diff | 1751 ++++++++++++++++++++++ pkgs/got.nix | 56 + pkgs/gqrss.nix | 33 + pkgs/icbirc.diff | 212 +++ pkgs/icbirc.nix | 27 + pkgs/mcchunkie.nix | 35 + pkgs/mudita-center.nix | 37 + pkgs/nheko.nix | 28 + pkgs/secretive.nix | 28 + services/config-manager.nix | 147 ++ services/default.nix | 4 + services/ssh-fido-agent.nix | 68 + system/nix-config.nix | 22 + system/nix-lockdown.nix | 25 + system/update.nix | 20 + users/default.nix | 51 + 81 files changed, 8611 insertions(+) create mode 100644 .allowed_signers create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 bins/check-restart.nix create mode 100644 bins/default.nix create mode 100644 bins/ix.nix create mode 100644 bins/rage.nix create mode 100644 bins/sfetch.nix create mode 100755 boot create mode 100755 check-restart create mode 100644 common.sh create mode 100644 configs/colemak.nix create mode 100644 configs/develop.nix create mode 100644 configs/dns.nix create mode 100644 configs/doas.nix create mode 100644 configs/emacs.nix create mode 100644 configs/emacs.org create mode 100644 configs/git.nix create mode 100644 configs/gitmux.nix create mode 100644 configs/manager.nix create mode 100644 configs/neomutt.nix create mode 100644 configs/neovim.lua create mode 100644 configs/neovim.nix create mode 100644 configs/net-overlay.nix create mode 100644 configs/tmux.nix create mode 100644 configs/zsh.nix create mode 100644 dbuild/build-consumer.nix create mode 100644 dbuild/build-server.nix create mode 100644 dbuild/default.nix create mode 100644 default.nix create mode 100755 deploy create mode 100644 flake.lock create mode 100644 flake.nix create mode 100755 fmt create mode 100644 gui/arcan.nix create mode 100644 gui/default.nix create mode 100644 gui/gnome.nix create mode 100644 gui/kde.nix create mode 100644 gui/xfce.nix create mode 100644 hosts/.hass/default.nix create mode 100644 hosts/.hass/hardware-configuration.nix create mode 100644 hosts/.nerm/default.nix create mode 100644 hosts/.nerm/hardware-configuration.nix create mode 100644 hosts/box/default.nix create mode 100644 hosts/box/hardware-configuration.nix create mode 100644 hosts/europa/default.nix create mode 100644 hosts/europa/hardware-configuration.nix create mode 100644 hosts/faf/default.nix create mode 100644 hosts/faf/hardware-configuration.nix create mode 100644 hosts/h/alias create mode 100644 hosts/h/default.nix create mode 100644 hosts/h/hardware-configuration.nix create mode 100644 hosts/litr/default.nix create mode 100644 hosts/litr/hardware-configuration.nix create mode 100644 hosts/plq/default.nix create mode 100644 hosts/weather/default.nix create mode 100644 hosts/weather/hardware-configuration.nix create mode 100644 installer.nix create mode 100644 overlays/default.nix create mode 100644 pkgs/athens.nix create mode 100644 pkgs/cinny-desktop.nix create mode 100644 pkgs/default.nix create mode 100644 pkgs/gitmux.nix create mode 100644 pkgs/got-sigs.diff create mode 100644 pkgs/got.nix create mode 100644 pkgs/gqrss.nix create mode 100644 pkgs/icbirc.diff create mode 100644 pkgs/icbirc.nix create mode 100644 pkgs/mcchunkie.nix create mode 100644 pkgs/mudita-center.nix create mode 100644 pkgs/nheko.nix create mode 100644 pkgs/secretive.nix create mode 100644 services/config-manager.nix create mode 100644 services/default.nix create mode 100644 services/ssh-fido-agent.nix create mode 100644 system/nix-config.nix create mode 100644 system/nix-lockdown.nix create mode 100644 system/update.nix create mode 100644 users/default.nix diff --git a/.allowed_signers b/.allowed_signers new file mode 100644 index 0000000..2e9588f --- /dev/null +++ b/.allowed_signers @@ -0,0 +1,3 @@ +aaron@bolddaemon.com sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIB1cBO17AFcS2NtIT+rIxR2Fhdu3HD4de4+IsFyKKuGQAAAACnNzaDpsZXNzZXI= +aaron@bolddaemon.com sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIHrYWbbgBkGcOntDqdMaWVZ9xn+dHM+Ap6s1HSAalL28AAAACHNzaDptYWlu + diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b4a1eaa --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.gcroots/ +.direnv +result +.DS_Store diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d22c571 --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2021 Aaron Bieber + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/README.md b/README.md index e69de29..c2ad0a0 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,82 @@ +nix-conf +======== + +``` +. +├── bins +│   ├── default.nix +│   ├── ix.nix +│   ├── rage.nix +│   └── sfetch.nix +├── boot +├── configs +│   ├── colemak.nix +│   ├── develop.nix +│   ├── dns.nix +│   ├── doas.nix +│   ├── emacs.nix +│   ├── gitmux.nix +│   ├── git.nix +│   ├── neovim.lua +│   ├── neovim.nix +│   ├── tmux.nix +│   ├── ts.nix +│   └── zsh.nix +├── dbuild +│   ├── build-consumer.nix +│   ├── build-server.nix +│   └── default.nix +├── default.nix +├── deploy +├── flake.lock +├── flake.nix +├── fmt +├── gui +│   ├── default.nix +│   ├── gnome.nix +│   ├── kde.nix +│   └── xfce.nix +├── hosts +│   ├── box +│   │   ├── default.nix +│   │   ├── hardware-configuration.nix +│   │   └── secrets +│   │   └── services.yaml +│   ├── europa +│   │   ├── default.nix +│   │   └── hardware-configuration.nix +│   ├── faf +│   │   ├── default.nix +│   │   └── hardware-configuration.nix +│   ├── hass +│   ├── litr +│   │   ├── default.nix +│   │   └── hardware-configuration.nix +│   ├── nerm +│   │   ├── default.nix +│   │   └── hardware-configuration.nix +│   └── weather +│   ├── default.nix +│   └── hardware-configuration.nix +├── installer.nix +├── LICENSE +├── pkgs +│   ├── cinny-desktop.nix +│   ├── default.nix +│   ├── gitmux.nix +│   └── mudita-center.nix +├── README.md +├── services +│   ├── config-manager.nix +│   ├── default.nix +│   └── ssh-fido-agent.nix +├── shell.nix +├── system +│   ├── nix-config.nix +│   ├── nix-lockdown.nix +│   └── update.nix +└── users + └── default.nix + +17 directories, 57 files +``` diff --git a/bins/check-restart.nix b/bins/check-restart.nix new file mode 100644 index 0000000..7b8435d --- /dev/null +++ b/bins/check-restart.nix @@ -0,0 +1,22 @@ +{ perl }: + +'' + #!${perl}/bin/perl + + use strict; + use warnings; + + sub say { print @_, "\n"; } + + my @booted = split("/", `readlink -f /run/booted-system/kernel`); + my @current = split("/", `readlink -f /run/current-system/kernel`); + + if ($booted[3] ne $current[3]) { + say "Restart required!"; + say "old: $booted[3]"; + say "new: $current[3]"; + exit 1; + } else { + say "system is clean.."; + } +'' diff --git a/bins/default.nix b/bins/default.nix new file mode 100644 index 0000000..3ba69c5 --- /dev/null +++ b/bins/default.nix @@ -0,0 +1,12 @@ +{ pkgs, lib, isUnstable, ... }: +let + oathPkg = pkgs.oath-toolkit or pkgs.oathToolkit; + ix = pkgs.writeScriptBin "ix" (import ./ix.nix { inherit (pkgs) perl; }); + rage = pkgs.writeScriptBin "rage" (import ./rage.nix { inherit pkgs; }); + sfetch = pkgs.writeScriptBin "sfetch" + (import ./sfetch.nix { inherit (pkgs) minisign curl; }); + checkRestart = pkgs.writeScriptBin "check-restart" + (import ./check-restart.nix { inherit (pkgs) perl; }); +in { + environment.systemPackages = with pkgs; [ rage ix sfetch xclip checkRestart ]; +} diff --git a/bins/ix.nix b/bins/ix.nix new file mode 100644 index 0000000..9eee4be --- /dev/null +++ b/bins/ix.nix @@ -0,0 +1,38 @@ +{ perl }: + +'' + #!${perl}/bin/perl + + use strict; + use warnings; + + use HTTP::Tiny; + if ($^O eq "openbsd") { + require OpenBSD::Pledge; + require OpenBSD::Unveil; + + OpenBSD::Unveil::unveil("/", "") or die; + OpenBSD::Pledge::pledge(qw( stdio dns inet rpath )) or die; + } + + my $http = HTTP::Tiny->new(); + + sub slurp { + my ($fh) = @_; + local $/; + <$fh>; + } + + sub sprunge { + my ($input) = @_; + my $url = "http://sprunge.us"; + my $form = [ sprunge => $input ]; + my $resp = $http->post_form($url, $form) + or die "could not POST: $!"; + $resp->{content}; + } + + my $input = slurp('STDIN'); + my $url = sprunge($input); + print $url; +'' diff --git a/bins/rage.nix b/bins/rage.nix new file mode 100644 index 0000000..d564265 --- /dev/null +++ b/bins/rage.nix @@ -0,0 +1,87 @@ +{ pkgs }: + +let oathPkg = pkgs.oath-toolkit or pkgs.oathToolkit; +in '' + #!/usr/bin/env sh + + set -e + + rage_dir=~/.rage + + . ''${rage_dir}/config + + cmd=$1 + + list() { + find $rage_dir -type f -name \*.age + } + + if [ -z $cmd ]; then + list + exit + fi + + case $cmd in + ls) + list + ;; + re) + F="" + if [ -f $2 ]; then + F=$2 + else + F=$(list | grep $2) + fi + + echo "Re-encrypting: '$F'" + pass="$(${pkgs.age}/bin/age -i $identity -d "$F")" + echo "$pass" | ${pkgs.age}/bin/age -a -R "$recipients" > "$F" + ;; + en) + printf 'Password: ' + stty -echo + read pass + stty echo + echo "" + printf 'Location: ' + read loc + echo "" + mkdir -p "$(dirname ~/.rage/$loc)" + echo "$pass" | ${pkgs.age}/bin/age -a -R "$recipients" > ~/.rage/''${loc}.age + ;; + de) + if [ -f $2 ]; then + ${pkgs.age}/bin/age -i $identity -d $2 + else + F=$(list | grep $2) + ${pkgs.age}/bin/age -i $identity -d "$F" + fi + ;; + cp) + if [ -f $2 ]; then + ${pkgs.age}/bin/age -i $identity -d $2 | ${pkgs.xclip}/bin/xclip + else + F=$(list | grep $2) + ${pkgs.age}/bin/age -i $identity -d "$F" | ${pkgs.xclip}/bin/xclip + fi + ;; + otp) + if [ -f $2 ]; then + ${pkgs.age}/bin/age -i $identity -d $2 | ${oathPkg}/bin/oathtool -b --totp - + else + F=$(list | grep $2) + ${pkgs.age}/bin/age -i $identity -d "$F" | ${oathPkg}/bin/oathtool -b --totp - + fi + ;; + push) + cd $rage_dir + git push + ;; + sync) + cd $rage_dir + git sync + ;; + default) + list + esac +'' diff --git a/bins/sfetch.nix b/bins/sfetch.nix new file mode 100644 index 0000000..261d04c --- /dev/null +++ b/bins/sfetch.nix @@ -0,0 +1,23 @@ +{ minisign, curl }: + +'' + #!/usr/bin/env sh + + set -e + + SERVER=cdn.openbsd.org + ITEM=$1 + MACHINE=amd64 + VER=snapshots + V=7.1 + [[ ! -z $2 ]] && MACHINE=$2 + if [[ ! -z $3 ]]; then + VER=$3 + V=$(echo $VER | sed 's/\.//') + fi + ${curl}/bin/curl -o "$PWD/$ITEM" "https://$SERVER/pub/OpenBSD/$VER/$MACHINE/$ITEM" && \ + ${curl}/bin/curl -o "$PWD/SHA256.sig" "https://$SERVER/pub/OpenBSD/$VER/$MACHINE/SHA256.sig" + + ${minisign}/bin/minisign -C -p "/etc/signify/openbsd-$V-base.pub" -x SHA256.sig "$ITEM" + +'' diff --git a/boot b/boot new file mode 100755 index 0000000..39d9e22 --- /dev/null +++ b/boot @@ -0,0 +1,13 @@ +#!/usr/bin/env sh + + +case $1 in + weather) + nix build .#nixosConfigurations.weatherInstall.config.system.build.sdImage + ;; + haas) + nix build .#nixosConfigurations.hassInstall.config.system.build.isoImage + ;; + *) + echo "Usage: boot [weather|hass]" +esac diff --git a/check-restart b/check-restart new file mode 100755 index 0000000..06c86ab --- /dev/null +++ b/check-restart @@ -0,0 +1,38 @@ +#!/usr/bin/env sh + +. ./common.sh + +while getopts "r" arg; do + case $arg in + r) + REBOOT=1 + ;; + esac +done + +start + +trap finish EXIT INT HUP + +for i in $(ls hosts); do + host=$(resolveAlias $i) + echo -n "===> $i: " + if tsAlive $host; then + if ${SSH} root@$host 'check-restart' >/dev/null 2>&1; then + echo -e "\tOK" + else + if [ ! -z $REBOOT ]; then + if isRunHost $i; then + echo -e "\tskipping runhost..." + else + echo -e "\trebooting..." + ${SSH} root@$host 'reboot' >/dev/null 2>&1 + fi + else + echo -e "\tREBOOT" + fi + fi + else + echo -e "\tDOWN" + fi +done diff --git a/common.sh b/common.sh new file mode 100644 index 0000000..5d25619 --- /dev/null +++ b/common.sh @@ -0,0 +1,56 @@ +NIX_SSHOPTS="-i /run/secrets/manager_pubkey -oIdentitiesOnly=yes -oControlPath=/tmp/manager-ssh-%r@%h:%p" +SSH="ssh ${NIX_SSHOPTS}" +CurrentVersion="$(git rev-parse HEAD)" +AgentKeys="$(ssh-add -L | awk '{print $2}')" +RunHost="$(uname -n)" + +msg() { + echo "===> $@" +} + +resolveAlias() { + host="${1}" + if [ -f hosts/${host}/alias ]; then + cat "hosts/${host}/alias" + return + fi + echo "$host" +} + +agentHasKey() { + checkKey="$(echo $1 | awk '{print $NF}')" + for i in $AgentKeys; do + if [[ "$i" == $checkKey ]]; then + return 0 + fi + done + return 1 +} + +isRunHost() { + if [ "$1" = "$RunHost" ]; then + return 0 + fi + return 1 +} + +tsAlive() { + ping -c 1 -w 2 $1 >/dev/null 2>&1 && return 0 + tailscale ping --timeout 2s --c 1 --until-direct=false $1 >/dev/null 2>&1 && return 0 + return 1 +} + +error() { + msg "Something went wrong!" + exit 1 +} + +start() { + agentHasKey "$(cat /run/secrets/manager_pubkey | awk '{print $2}')" || \ + ssh-add /run/secrets/manager_key +} + +finish() { + ssh-add -d /run/secrets/manager_key + exit 0 +} diff --git a/configs/colemak.nix b/configs/colemak.nix new file mode 100644 index 0000000..959053c --- /dev/null +++ b/configs/colemak.nix @@ -0,0 +1,22 @@ +{ config, lib, ... }: +with lib; { + options = { + colemak = { + enable = mkOption { + description = "Enable colemak keyboard layout"; + default = true; + example = true; + type = lib.types.bool; + }; + }; + }; + + config = mkIf config.colemak.enable { + console = { keyMap = "colemak"; }; + services.xserver = { + layout = "us"; + xkbVariant = "colemak"; + xkbOptions = "ctrl:swapcaps"; + }; + }; +} diff --git a/configs/develop.nix b/configs/develop.nix new file mode 100644 index 0000000..d1856b5 --- /dev/null +++ b/configs/develop.nix @@ -0,0 +1,15 @@ +{ config, lib, pkgs, ... }: +with lib; { + options = { + jetbrains = { enable = mkEnableOption "Install JetBrains editors"; }; + }; + + config = mkMerge [ + (mkIf config.jetbrains.enable { + nixpkgs.config.allowUnfreePredicate = pkg: + builtins.elem (lib.getName pkg) [ "idea-ultimate" ]; + + environment.systemPackages = with pkgs; [ jetbrains.idea-ultimate sshfs ]; + }) + ]; +} diff --git a/configs/dns.nix b/configs/dns.nix new file mode 100644 index 0000000..6ac880c --- /dev/null +++ b/configs/dns.nix @@ -0,0 +1,32 @@ +{ config, lib, ... }: +with lib; { + options = { + preDNS = { + enable = mkOption { + description = "Enable DNSSEC"; + default = true; + example = true; + type = lib.types.bool; + }; + }; + }; + + config = mkIf config.preDNS.enable { + services = { + openntpd.enable = true; + resolved = { + enable = true; + dnssec = "allow-downgrade"; + # TODO: Enable a toggle for ipv6 + extraConfig = '' + [Resolve] + DNS=45.90.28.0#8436c6.dns1.nextdns.io + DNS=2a07:a8c0::#8436c6.dns1.nextdns.io + DNS=45.90.30.0#8436c6.dns2.nextdns.io + DNS=2a07:a8c1::#8436c6.dns2.nextdns.io + DNSOverTLS=yes + ''; + }; + }; + }; # tailscale and what not have no preDNS +} diff --git a/configs/doas.nix b/configs/doas.nix new file mode 100644 index 0000000..3baf1c0 --- /dev/null +++ b/configs/doas.nix @@ -0,0 +1,23 @@ +{ config, lib, ... }: +with lib; { + options = { + doas = { enable = mkEnableOption "Enable doas for priv-escie"; }; + }; + + config = mkIf config.doas.enable { + nixpkgs.config.packageOverrides = pkgs: { + doas = pkgs.doas.override { withPAM = false; }; + }; + security = { + doas = { + enable = true; + extraRules = [{ + groups = [ "wheel" ]; + persist = true; + }]; + + }; + sudo.enable = false; + }; + }; +} diff --git a/configs/emacs.nix b/configs/emacs.nix new file mode 100644 index 0000000..d866cc1 --- /dev/null +++ b/configs/emacs.nix @@ -0,0 +1,60 @@ +{ runCommand, emacsWithPackagesFromUsePackage, pkgs, lib, makeWrapper, mu +, writeTextDir, emacs, emacsPkg ? pkgs.emacsPgtkNativeComp, ... }: + +let + muDir = "${mu}/share/emacs/site-lisp/mu4e"; + + # Generate a .el file from our emacs.org. + emacsConfig = runCommand "emacsConfig" { } '' + mkdir -p $out + cp -v ${./emacs.org} $out/emacs.org + cd $out + ${emacs}/bin/emacs --batch -Q -q \ + --debug-init \ + -l org emacs.org \ + -f org-babel-tangle + if [ $? != 0 ]; then + echo "Generating failed!" + exit 1; + else + echo "Generated org config!" + fi + ''; + + # init.el to load my config and other dependencies. + emacsInit = writeTextDir "share/emacs/site-lisp/init.el" '' + (message "Loading my 'mu4e' from: ${muDir}") + (add-to-list 'load-path "${muDir}") + (load "${muDir}/mu4e.el") + + (message "Loading my 'emacs.org' config from: ${emacsConfig}") + (load "${emacsConfig}/emacs.el") + ''; + emacsInitDir = "${emacsInit}/share/emacs/site-lisp"; + + # Binaries that are needed in emacs + emacsDepList = with pkgs; [ + go-font + graphviz + ispell + isync + mu + texlive.combined.scheme-full + ]; + +in emacsWithPackagesFromUsePackage { + config = ./emacs.org; + + alwaysEnsure = true; + alwaysTangle = true; + + package = emacsPkg.overrideAttrs (oa: { + nativeBuildInputs = oa.nativeBuildInputs ++ [ makeWrapper emacsConfig ]; + postInstall = '' + ${oa.postInstall} + wrapProgram $out/bin/emacs \ + --prefix PATH : ${pkgs.lib.makeBinPath emacsDepList} \ + --add-flags '--init-directory ${emacsInitDir}' + ''; + }); +} diff --git a/configs/emacs.org b/configs/emacs.org new file mode 100644 index 0000000..4f361d5 --- /dev/null +++ b/configs/emacs.org @@ -0,0 +1,1488 @@ +#+PROPERTY: header-args:emacs-lisp :tangle yes +#+TITLE: Emacs Configuration +* Literate emacs configuration + +I can never rembember what various configs do. Jumping on this literate emacs +config to see if it helps! + +** Set some ENV vars + +These are needed to make emacs aware of various things (like Go binaries, +etc). I have them here because sometimes when emacs is launched from ~rofi~ +or similar it doesn't have the same ENV as it does when launching from the +shell. + +#+begin_src emacs-lisp + (defun expand-if-exists (d) + ;; expand-if-exists(d) expands d only if d exists + (if (file-exists-p d) + (expand-file-name d))) + + (let ((paths + '( + "/usr/local/bin" + "/usr/local/go/bin" + "/usr/local/MacGPG2/bin" + "/Library/TeX/texbin" + "/usr/local/jdk-11/bin" + "/usr/ports/infrastructure/bin" + "~/bin" + "~/.nix-profile/bin" + "~/go/bin" + "~/opt/bin" + "/usr/local/plan9/bin" + ))) + + (setenv "PATH" (concat (getenv "PATH") (mapconcat 'expand-if-exists (remove nil paths) ":"))) + (setq exec-path (append exec-path (remove "" (split-string (getenv "PATH") ":"))))) +#+end_src + +** Start the emacs server + +Starting as a server lets me connect externally to do things like change +themes, save buffers via cron and other such dumbary! + +#+begin_src emacs-lisp +(load "server") +(unless (server-running-p) (server-start)) +#+end_src + +** OpenBSD Lockups + +For some reason OpenBSD hangs, these seem to help a bit +#+begin_src emacs-lisp +(setq x-select-enable-clipboard-manager nil) +(setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)) +(setf x-selection-timeout 500) +#+end_src + +** Interface and Behavior +*** Interface + +Global font +#+begin_src emacs-lisp + ;; This is currently set in ~/.Xresources - for some reason emacs doesn't like the line below + ;;(set-default-font "Go Regular") +#+end_src + +Use 80 columns, this helps keep things readable when windows are split +#+begin_src emacs-lisp +(setq whitespace-style '(trailing lines space-before-tab) + whitespace-line-column 80) +(setq-default fill-column 80) +#+end_src + +I know I am in emacs, don't need to see the startup screen. +#+begin_src emacs-lisp +(setq inhibit-startup-screen t) +#+end_src + +If we are on OpenBSD, fill the scratch buffer with fortune \o/. + +#+begin_src emacs-lisp +(if (file-executable-p "/usr/games/fortune") + (setq initial-scratch-message + (concat + (shell-command-to-string "fortune | sed -e 's/^/;; /g'") + "\n\n"))) +#+end_src + +**** Use UTF8 where ever possible +#+begin_src emacs-lisp +(prefer-coding-system 'utf-8) +(set-default-coding-systems 'utf-8) +(set-terminal-coding-system 'utf-8) +(set-keyboard-coding-system 'utf-8) +#+end_src + +**** Change various UI bits +#+begin_src emacs-lisp +(tool-bar-mode -1) +(menu-bar-mode -1) +(scroll-bar-mode -1) +(column-number-mode +1) +(global-font-lock-mode 1) +#+end_src + +**** No GNU ls here +#+begin_src emacs-lisp + (setq ls-lisp-use-insert-directory-program nil) + (require 'ls-lisp) +#+end_src + +*** Behavior + +Switch various defaults to be more comfortable for myself. + +#+begin_src emacs-lisp + (fset 'yes-or-no-p 'y-or-n-p) + (show-paren-mode t) + + (setq desktop-dirname "~/.emacs.d/" + desktop-base-file-name "emacs.desktop" + desktop-base-lock-name "lock" + desktop-path (list desktop-dirname) + desktop-save t + desktop-files-not-to-save "^$" ;reload tramp paths + desktop-load-locked-desktop nil + desktop-auto-save-timeout 30) + (desktop-save-mode 1) + + (setq backup-directory-alist '(("." . "~/.emacs-saves"))) + (setq auto-mode-alist + (append + (list + '("\\.gpg$" . sensitive-minor-mode) + ) + auto-mode-alist)) + (setq auth-sources + '((:source "~/.netrc"))) +#+end_src + +Use spelling and auto-fill when we are in text mode. + +#+begin_src emacs-lisp +(add-hook 'text-mode-hook (lambda () + (auto-fill-mode 1) + (turn-on-flyspell))) +#+end_src + +This fixes some tramp "waiting for prompt" errors. +#+begin_src emacs-lisp + ;;(setq trarmp-shell-prompt-pattern "\\(?:^\\|\r\\)[^]#$%>λ\n]*#?[]#$%>λ].* *\\(^[\\[[0-9;]*[a-zA-Z] *\\)*") + ;;(require 'tramp-sh nil t) + ;;(setf tramp-ssh-controlmaster-options + ;; (concat + ;; "-o ControlPath=/tmp/ssh-%%r@%%h:%%p " + ;; "-o ControlMaster=auto -o ControlPersist=yes")) +#+end_src + +If things _aren't_ working the way we want: + +#+begin_src emacs-lisp +(setq tramp-verbose 6) +#+end_src + +** Include ports site-lisp + +On OpenBSD various packages (mu, git.. etc) install elisp things into a global +directory, this makes sure we include it. + +#+begin_src emacs-lisp +(if (file-directory-p "/usr/local/share/emacs/site-lisp") + (add-to-list 'load-path "/usr/local/share/emacs/site-lisp/")) +#+end_src + +** Unset custom-file + +The customization file mostly just causes churn in the SCM so we disable it +here. +#+begin_src emacs-lisp +(setq custom-file (make-temp-file "")) +#+end_src + +** Ensure packages are pinned and installed + +This makes sure ~use-package~ installs things (and makes it so we don't need +~:ensure t~ set for every package. + +#+begin_src emacs-lisp +(setq use-package-always-ensure t) +;;(setq use-package-always-pin "melpa-stable") +#+end_src + +* Packages +** parchment-theme +This is a nice theme that resembles acme in plan9. Minimal. + +#+begin_src emacs-lisp +(use-package parchment-theme + :config (load-theme 'parchment t)) +#+end_src + +** keychain-environment + +I make heavy use of ~ssh-agent~ this lets emacs pickup / use the existing +agents I have running. + +#+begin_src emacs-lisp +(use-package keychain-environment + ;;:pin "melpa" + :init + (keychain-refresh-environment)) +#+end_src + +** ivy + +~ivy~ is fantastic. It gives me nice visual search for buffers, +code.. etc. Combined with ~smex~ for sorting (shows last used things first) and +~counsel~ (extends ivy into various areas like the help stuff). + +#+begin_src emacs-lisp + (use-package counsel) + (setq smex-save-file (expand-file-name "~/.emacs.d/smex.save")) + (use-package smex) + (use-package ivy + :hook (after-init . ivy-mode) + :bind + ("C-s" . swiper) + ("M-x" . counsel-M-x) + ("C-x C-f" . counsel-find-file) + ("C-x b" . ivy-switch-buffer)) +#+end_src + +** magit + +Magit is a awesome. Not sure what else to say about it. :P + +#+begin_src emacs-lisp +(use-package magit + :bind ("C-c m" . magit-status) + :init + (setq magit-completing-read-function 'ivy-completing-read)) +#+end_src + +** flycheck + +~flycheck~ does automatic syntax checking for most things + +#+begin_src emacs-lisp +(use-package flycheck + :init (global-flycheck-mode)) +#+end_src + +- [2020-05-29 Fri] Unfortunately it clobbers the "C-c !" prefix, so we need + to add this to get it back: + +#+begin_src emacs-lisp +(define-key flycheck-mode-map (kbd "C-c !") 'org-time-stamp-inactive) +#+end_src + +** lsp-mode + +~lsp-mode~ supports language servers for various things. I pretty much only +care about Go and Ruby. + +#+begin_src emacs-lisp + (use-package lsp-mode + :hook ((go-mode . lsp-deferred) + (ruby-mode . lsp)) + :commands (lsp lsp-deferred)) +#+end_src + +** company and friends + +~company~ allows for auto-completion of various things. It can interface with ~lsp-mode~ to complete +things like Go. + +#+begin_src emacs-lisp +(use-package company + :config + (setq company-tooltip-limit 20 + company-minimum-prefix-length 1 + company-idle-delay .3 + company-echo-delay 0) + :hook (prog-mode . company-mode)) +#+end_src + +** gitgutter +This gives me a nice in-ui way to see modifications and what not. + +#+begin_src emacs-lisp +(use-package git-gutter + :hook + (after-init . global-git-gutter-mode)) +#+end_src + +** nix + +Add support for nix files. I don't use nix much atm, but it was recently +ported to OpenBSD, so I am hopeful I can start using it there more! + +#+begin_src emacs-lisp +(use-package nix-mode + :mode "\\.nix\\'") +#+end_src + +** shell + +I don't often use the shell from emacs, but when I do these bits make it +easier for me to treat it like a regular shell. + +#+begin_src emacs-lisp + ;; Kill terminal buffers on exit so I din't have to kill the buffer after I exit. + (defadvice term-handle-exit + (after term-kill-buffer-on-exit activate) + (kill-buffer)) +#+end_src + +** pinboard + +A pinboard.in client + +#+begin_src emacs-lisp +(use-package pinboard) +#+end_src + +** restclient + +#+begin_src emacs-lisp +(use-package restclient + ;;:pin "melpa" + :mode (("\\.http$" . restclient-mode))) +#+end_src + +** sr-speedbar + +Speedbar is almost perfect.. If it only ran in the current frame!! :D + +**** Enter sr-speedbar +#+begin_src emacs-lisp +;;; sr-speedbar.el --- Same frame speedbar + +;; Author: Sebastian Rose +;; Maintainer: Sebastian Rose +;; Peter Lunicks +;; Copyright (C) 2008, 2009, Sebastian Rose, all rights reserved. +;; Copyright (C) 2008, 2009, Andy Stewart, all rights reserved. +;; Copyright (C) 2009, Peter Lunicks, all rights reversed. +;; Created: 2008 +;; Version: 20200616 +;; X-Original-Version: 0.1.10 +;; Last-Updated: 2020-06-16 +;; URL: http://www.emacswiki.org/emacs/download/sr-speedbar.el +;; Keywords: speedbar, sr-speedbar.el +;; Compatibility: GNU Emacs 22 ~ GNU Emacs 25 +;; +;; Features required by this library: +;; +;; `speedbar' `advice' `cl' +;; + +;;; This file is NOT part of GNU Emacs + +;;; License +;; +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth +;; Floor, Boston, MA 02110-1301, USA. + +;;; Commentary: +;; +;; The sr-speedbar.el was created just because I could not believe what I +;; read on http://www.emacswiki.org/cgi-bin/wiki/Speedbar. They wrote there +;; that it is not possible to show the speedbar in the same frame. But, as +;; we all know, ecb had this already. So I started as some kind of joke :) +;; But when I found it useful and use it all the time. +;; +;; Now you type windows key with 's' (`s-s' in Emacs) will show the speedbar +;; in an extra window, same frame. You can customize the initial width of the +;; speedbar window. +;; +;; Below are commands you can use: +;; +;; `sr-speedbar-open' Open `sr-speedbar' window. +;; `sr-speedbar-close' Close `sr-speedbar' window. +;; `sr-speedbar-toggle' Toggle `sr-speedbar' window. +;; `sr-speedbar-select-window' Select `sr-speedbar' window. +;; `sr-speedbar-refresh-turn-on' Turn on refresh speedbar content. +;; `sr-speedbar-refresh-turn-off' Turn off refresh speedbar content. +;; `sr-speedbar-refresh-toggle' Toggle refresh speedbar content. +;; +;; Enjoy! ;) +;; + +;;; Installation: +;; +;; Copy sr-speedbar.el to your load-path and add to your ~/.emacs +;; +;; (require 'sr-speedbar) +;; (global-set-key (kbd "s-s") 'sr-speedbar-toggle) +;; +;; ... or any key binding you like. +;; + +;;; Customize: +;; +;; M-x customize-group RET sr-speedbar RET + +;;; Change log: +;; * 07 Jan 2021: +;; * Jacob First +;; * Fix inconsistent window selection when opening speedbar on the right side vs. on the left. +;; +;; * 16 Jun 2020: +;; * Bo Yao (submitted by him on 16 Jul 2018 to the Emacs Orphanage mirror version at GitHub) +;; * Always open file in most recently selected window (the one before switching to +;; sr-speedbar). +;; +;; * 25 Oct 2016: +;; * Hong Xu +;; * Fix compilation warning when `helm-alive-p' is not defined. +;; +;; * 04 Aug 2015: +;; * Tamas Levai : +;; * fix compilation warnings +;; +;; * 15 Sep 2014: +;; * Tu, Do Hoang +;; * define `sr-speedbar-handle-other-window-advice' and `ad-advised-definition-p' +;; before defining `sr-speedbar-skip-other-window-p'. Othewise, `sr-speedbar' +;; fails to load at this stage. +;; +;; * Do not used advised `pop-to-buffer' when helm window is +;; alive. Otherwise another horizontal buffer is created inside +;; Helm buffer. +;; +;; * Uwe Koloska +;; * define `ad-advised-definition-p' only if it's not defined +;; fixes an error on Emacs 24.3 where `macrop' ist still named +;; `ad-macro-p' +;; +;; * 03 Aug 2014: +;; * Reuben Thomas : +;; * Reduce to a single width preference, and make it work properly on +;; startup. +;; * Miscellaneous tidying of documentation and comments. +;; * Remove version constant; should be using the package header, and it +;; was already way out of date. +;; +;; * 08 Jun 2014: +;; * Gregor Zattler: +;; * test if symbol `ad-advised-definition-p' is defined, +;; since Christian Brassats version test failed on emacs +;; 23.3.91.1 +;; +;; * 05 May 2014: +;; * Christian Brassat: +;; * `ad-advised-definition-p' is not supported since Emacs 24.4. +;; +;; * 09 Mar 2013: +;; * Tharre: +;; * Remove Emacs 21 compatibility code as it fails to compile on Emacs 24. +;; +;; * 20 July 2009: +;; * Peter Lunicks: +;; * Add new option `sr-speedbar-right-side' to control which +;; side of the frame the speedbar appears on. +;; +;; * 18 Feb 2009: +;; * Andy Stewart: +;; * Fix bug between ECB and `sr-speedbar-close'. +;; +;; * 29 Jan 2009: +;; * Andy Stewart: +;; * Fix doc. +;; +;; * 13 Jan 2009: +;; * Andy Stewart: +;; * Use `emacs-major-version' instead comment for Emacs 21 compatibility. +;; * Rewrite advice for `pop-to-buffer' to avoid `pop-to-buffer' not effect +;; when have many dedicated window in current frame. +;; * Rewrite advice for `delete-other-windows' to avoid use common variable +;; `delete-protected-window-list' and use `window-dedicated-p' instead. +;; Remove variable `delete-protected-window-list' and function +;; `sr-speedbar-dedicated-match-protected-window-p'. +;; +;; * 04 Jan 2009: +;; * Andy Stewart: +;; * Add new option `sr-speedbar-auto-refresh' control refresh content. +;; * Add new functions: +;; `sr-speedbar-refresh-turn-on', +;; `sr-speedbar-refresh-turn-off', +;; `sr-speedbar-refresh-toggle'. +;; * Fix doc. +;; +;; * 30 Dec 2008: +;; * Andy Stewart: +;; * Rewrite advice for `delete-other-windows' for fix the bug +;; with window configuration save and revert. +;; * Rewrite advice for `delete-window', now just remember window +;; width before deleted, and can use `delete-window' do same effect +;; as command `sr-speedbar-close'. +;; * Add new option `sr-speedbar-max-width'. +;; Remember window width before hide, except larger than value of +;; `sr-speedbar-max-width'. +;; * Add new variable `delete-protected-window-list', for protected +;; special window don't deleted. +;; This variable is common for any extension that use dedicated +;; window. +;; * Fix doc. +;; +;; * 29 Dec 2008: +;; * Andy Stewart: +;; * Pick-up and refactory code that use `buffer-live-p' or `window-live-p', +;; and replace with `sr-speedbar-buffer-exist-p' and +;; `sr-speedbar-window-exist-p'. +;; * Rename some function with prefix `sr-speedbar-' to avoid +;; conflict with other functions. +;; * Pick-up the code that handle advice for `other-window', +;; and replace with function `sr-speedbar-handle-other-window-advice'. +;; * Clean up code, make more clear. +;; +;; * 21 Dec 2008: +;; * Andy Stewart: +;; * Fix the bug `sr-speedbar-open' and `sr-speedbar-close'. +;; * Fix doc. +;; +;; * 20 Dec 2008 +;; * Andy Stewart: +;; * Fix `ad-advised-definition-p' error. +;; * Fix doc. +;; +;; * 17 Dec 2008 +;; * Andy Stewart: +;; * Add new option `sr-speedbar-skip-other-window-p' and new advice +;; for `other-window', make user skip select `sr-speedbar' window +;; when use command `other-window'. +;; * Fix the name of advice, make more clear. +;; * Fix the bug `sr-speedbar-select-window' when no live window exist. +;; * Fix doc. +;; +;; * 16 Dec 2008: +;; * Andy Stewart: +;; * Fix the bug of `sr-speedbar-refresh', use `default-directory' +;; get refresh directory instead through function in `dired'. +;; * Fix `window-live-p' bug, check window valid value before use +;; `window-live-p' test `sr-speedbar-window'. +;; * Fix `buffer-live-p' bug, check buffer valid value before use +;; `buffer-live-p' test `speedbar-buffer'. +;; * Add advice `pop-to-buffer' to make function `display-buffer' +;; can pop-up window when just have two windows (one is `sr-speedbar' +;; window) in current frame. +;; * Add group `sr-speedbar'. +;; More better customize interface through `customize-group'. +;; +;; * 28 Sep 2008: +;; * Andy Stewart: +;; * Fix a bug, when `sr-speedbar-toggle' many times, window width +;; will increment automatically. +;; * Use around advices replace, make code simple. +;; * Use `sr-speedbar-open' replace `sr-speedbar-no-separate-frame'. +;; * Clean up code. +;; +;; * 28 Sep 2008: +;; * Sebastian: +;; * set `sr-speedbar-delete-windows' to nil to avoid +;; the removal of other windows. +;; +;; * 26 Jun 2008: +;; * Sebastian: +;; * Added Andy Stewart's patch to refresh the speedbar's contents. +;; Thanks for this one! +;; +;; * Init: +;; * Sebastian: +;; * Added some lines to get it working: +;; * splitting the window and remember it, +;; * changing the way speedbar finds a file. +;; * File view of speedbar is now working all right. +;; * C-x 1 in other window deletes speedbar-window, just calling +;; M-x sr-speedbar-no-separate-frame again is fine now. +;; * Toggle speedbar works, width is save when toggling. +;; * Recalculate speedbar width if window-width - speedbar-width <= 0 +;; * Speedbar window is now dedicated to speedbar-buffer. +;; + +;;; Acknowledgements: +;; +;; All emacsers ... :) +;; + +;;; Bug +;; +;; + +;;; TODO +;; +;; +;; + +;;; Require +(require 'speedbar) +(require 'advice) +(require 'cl-lib) +(eval-when-compile + (require 'cl)) + +;;; Code: + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; User Customization ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defgroup sr-speedbar nil + "Same frame speedbar." + :group 'speedbar) + +(defcustom sr-speedbar-default-width 40 + "Initial width of `sr-speedbar-window' under window system." + :type 'integer + :group 'sr-speedbar) + +(defcustom sr-speedbar-max-width 50 + "The max width limit that window allowed. +Default, if hide `sr-speedbar' window will remember +window width, except the window width larger than +this value." + :type 'integer + :group 'sr-speedbar) + +(defcustom sr-speedbar-auto-refresh t + "Automatically refresh speedbar content when changed directory. +Default is t." + :type 'boolean + :set (lambda (symbol value) + (set symbol value)) + :group 'sr-speedbar) + +(defcustom sr-speedbar-right-side t + "Show the speedbar to the right side of the current window. +If nil, the speedbar will appear on the left. +Default is t." + :type 'boolean + :set (lambda (symbol value) + (set symbol value)) + :group 'sr-speedbar) + +(defcustom sr-speedbar-delete-windows nil + "Allow the speedbar to delete other windows before showing up. +If nil, speedbar will not touch your window configuration. +Otherwise `delete-other-windows' will be called before showing +the speedbar. + +Default is nil." + :type 'boolean + :group 'sr-speedbar) + +(if (not (fboundp 'ad-advised-definition-p)) + (defun ad-advised-definition-p (definition) + "Return non-nil if DEFINITION was generated from advice information." + (if (or (ad-lambda-p definition) + (macrop definition) + (ad-compiled-p definition)) + (let ((docstring (ad-docstring definition))) + (and (stringp docstring) + (get-text-property 0 'dynamic-docstring-function docstring)))))) + +(defun sr-speedbar-handle-other-window-advice (activate) + "Handle advice for function `other-window'. +If ACTIVATE is `non-nil' enable advice `sr-speedbar-other-window-advice'. +Otherwise disable it." + (if activate + (ad-enable-advice 'other-window 'after 'sr-speedbar-other-window-advice) + (ad-disable-advice 'other-window 'after 'sr-speedbar-other-window-advice)) + (ad-activate 'other-window)) + +(defcustom sr-speedbar-skip-other-window-p nil + "Whether skip `sr-speedbar' window with `other-window'. +Default, can use `other-window' select window in cyclic +ordering of windows. But sometimes we don't want select +`sr-speedbar' window use `other-window'. +Just want make `sr-speedbar' window as a view sidebar. + +So please turn on this option if you want skip +`sr-speedbar' window with `other-window'. + +Default is nil." + :type 'boolean + :set (lambda (symbol value) + (set symbol value) + (if (fboundp 'ad-advised-definition-p) + (when (ad-advised-definition-p 'other-window) + (sr-speedbar-handle-other-window-advice value)) + (when (ad-is-advised 'other-window) + (sr-speedbar-handle-other-window-advice value)))) + :group 'sr-speedbar) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Constant ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defconst sr-speedbar-buffer-name "*SPEEDBAR*" + "The buffer name of sr-speedbar.") + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defvar sr-speedbar-width sr-speedbar-default-width + "Initial width of speedbar-window.") + +(defvar sr-speedbar-window nil + "Speedbar window.") + +(defvar sr-speedbar-last-refresh-dictionary nil + "The last refresh dictionary record of 'sr-speedbar-refresh'.") + +(eval-when-compile + (defvar ecb-activated-window-configuration nil) + (defun ecb-activate ()) + (defun ecb-deactivate ())) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Interactive functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;###autoload +(defun sr-speedbar-toggle () + "Toggle sr-speedbar window. +Toggle visibility of sr-speedbar by resizing +the `sr-speedbar-window' to a minimal width +or the last width when visible. +Use this function to create or toggle visibility +of a speedbar-window. It will be created if necessary." + (interactive) + (if (sr-speedbar-exist-p) + (sr-speedbar-close) + (sr-speedbar-open))) + +;;;###autoload +(defun sr-speedbar-open () + "Create `sr-speedbar' window." + (interactive) + (if (not (sr-speedbar-exist-p)) + (let ((current-window (selected-window))) + ;; Ensure only one window is there + ;; when `sr-speedbar-delete-windows' is non-nil + (if sr-speedbar-delete-windows + (delete-other-windows)) + ;; Whether activate `other-window' advice + ;; to skip `sr-speedbar' window when use `other-window'. + (sr-speedbar-handle-other-window-advice sr-speedbar-skip-other-window-p) + ;; Switch buffer + (if (sr-speedbar-buffer-exist-p speedbar-buffer) + (unless (sr-speedbar-window-exist-p sr-speedbar-window) + (sr-speedbar-get-window)) + (if (<= (sr-speedbar-current-window-take-width) sr-speedbar-width) + (setq sr-speedbar-width sr-speedbar-default-width)) + (sr-speedbar-get-window) ;get `sr-speedbar' window that split current window + (setq speedbar-buffer (get-buffer-create sr-speedbar-buffer-name) + speedbar-frame (selected-frame) + dframe-attached-frame (selected-frame) + speedbar-select-frame-method 'attached + speedbar-verbosity-level 0 ;don't say anything, i don't like ... :) + speedbar-last-selected-file nil) + (set-buffer speedbar-buffer) + (buffer-disable-undo speedbar-buffer) ;make disable in speedbar buffer, otherwise will occur `undo-outer-limit' error + (speedbar-mode) + (speedbar-reconfigure-keymaps) + (speedbar-update-contents) + (speedbar-set-timer 1) + ;; Add speedbar hook. + (add-hook 'speedbar-before-visiting-file-hook 'sr-speedbar-before-visiting-file-hook t) + (add-hook 'speedbar-before-visiting-tag-hook 'sr-speedbar-before-visiting-tag-hook t) + (add-hook 'speedbar-visiting-file-hook 'sr-speedbar-visiting-file-hook t) + (add-hook 'speedbar-visiting-tag-hook 'sr-speedbar-visiting-tag-hook t) + ;; Add `kill-buffer-hook'. + (add-hook 'kill-buffer-hook 'sr-speedbar-kill-buffer-hook) ;add `kill-buffer-hook' + ;; Auto refresh speedbar content + ;; if option `sr-speedbar-auto-refresh' is non-nil + (sr-speedbar-handle-auto-refresh sr-speedbar-auto-refresh)) + (set-window-buffer sr-speedbar-window (get-buffer sr-speedbar-buffer-name)) + (set-window-dedicated-p sr-speedbar-window t) ;make `sr-speedbar-window' dedicated to speedbar-buffer. + (select-window current-window)) + (message "`sr-speedbar' window has exist."))) + +(defun sr-speedbar-close () + "Close `sr-speedbar' window and save window width." + (interactive) + (if (sr-speedbar-exist-p) + (let ((current-window (selected-window))) + ;; Remember window width. + (sr-speedbar-select-window) + (sr-speedbar-remember-window-width) + ;; Close window. + (if (and (require 'ecb nil t) + ecb-activated-window-configuration) + ;; Toggle ECB window when ECB window activated. + (progn + (ecb-deactivate) + (ecb-activate)) + ;; Otherwise delete dedicated window. + (delete-window sr-speedbar-window) + (if (sr-speedbar-window-exist-p current-window) + (select-window current-window)))) + (message "`sr-speedbar' window is not exist."))) + +(defun sr-speedbar-select-window () + "Force the windows that contain `sr-speedbar'." + (interactive) + (if (sr-speedbar-exist-p) + (select-window sr-speedbar-window) + (message "`sr-speedbar' window is not exist."))) + +(defun sr-speedbar-refresh-turn-on () + "Turn on refresh content automatically." + (interactive) + (setq sr-speedbar-auto-refresh t) + (sr-speedbar-handle-auto-refresh sr-speedbar-auto-refresh t)) + +(defun sr-speedbar-refresh-turn-off () + "Turn off refresh content automatically." + (interactive) + (setq sr-speedbar-auto-refresh nil) + (sr-speedbar-handle-auto-refresh sr-speedbar-auto-refresh t)) + +(defun sr-speedbar-refresh-toggle () + "Toggle refresh content status." + (interactive) + (setq sr-speedbar-auto-refresh (not sr-speedbar-auto-refresh)) + (sr-speedbar-handle-auto-refresh sr-speedbar-auto-refresh t)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; utilise functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defun sr-speedbar-exist-p () + "Return `non-nil' if `sr-speedbar' is exist. +Otherwise return nil." + (and (sr-speedbar-buffer-exist-p speedbar-buffer) + (sr-speedbar-window-exist-p sr-speedbar-window))) + +(defun sr-speedbar-window-p () + "Return `non-nil' if current window is `sr-speedbar' window. +Otherwise return nil." + (equal sr-speedbar-buffer-name (buffer-name (window-buffer)))) + +(defun sr-speedbar-remember-window-width () + "Remember window width." + (let ((win-width (sr-speedbar-current-window-take-width))) + (if (and (sr-speedbar-window-p) + (> win-width 1) + (<= win-width sr-speedbar-max-width)) + (setq sr-speedbar-width win-width)))) + +(defun sr-speedbar-get-window () + "Get `sr-speedbar' window." + (setq sr-speedbar-window + (split-window (selected-window) + (- sr-speedbar-width) + (if sr-speedbar-right-side 'right 'left)))) + +(defun sr-speedbar-before-visiting-file-hook () + "Function that hook `speedbar-before-visiting-file-hook'." + (select-window (get-mru-window))) + +(defun sr-speedbar-before-visiting-tag-hook () + "Function that hook `speedbar-before-visiting-tag-hook'." + (select-window (get-mru-window))) + +(defun sr-speedbar-visiting-file-hook () + "Function that hook `speedbar-visiting-file-hook'." + (select-window (get-mru-window))) + +(defun sr-speedbar-visiting-tag-hook () + "Function that hook `speedbar-visiting-tag-hook'." + (select-window (get-mru-window))) + +(defun sr-speedbar-kill-buffer-hook () + "Function that hook `kill-buffer-hook'." + (when (eq (current-buffer) speedbar-buffer) + (setq speedbar-frame nil + dframe-attached-frame nil + speedbar-buffer nil) + (speedbar-set-timer nil) + (remove-hook 'speedbar-before-visiting-file-hook 'sr-speedbar-before-visiting-file-hook) + (remove-hook 'speedbar-before-visiting-tag-hook 'sr-speedbar-before-visiting-tag-hook) + (remove-hook 'speedbar-visiting-file-hook 'sr-speedbar-visiting-file-hook) + (remove-hook 'speedbar-visiting-tag-hook 'sr-speedbar-visiting-tag-hook))) + +(defun sr-speedbar-refresh () + "Refresh the context of speedbar." + (when (and (not (equal default-directory sr-speedbar-last-refresh-dictionary)) ;if directory is change + (not (sr-speedbar-window-p))) ;and is not in speedbar buffer + (setq sr-speedbar-last-refresh-dictionary default-directory) + (speedbar-refresh))) + +(defun sr-speedbar-handle-auto-refresh (activate &optional echo-show) + "Automatically refresh speedbar content when changed directory. +Do nothing if option ACTIVATE is nil. +Will display message if ECHO-SHOW is non-nil." + (if activate + (progn + (add-hook 'speedbar-timer-hook 'sr-speedbar-refresh) + (if echo-show (message "Turn on speedbar content refresh automatically."))) + (remove-hook 'speedbar-timer-hook 'sr-speedbar-refresh) + (if echo-show (message "Turn off speedbar content refresh automatically.")))) + +(defun sr-speedbar-current-window-take-width (&optional window) + "Return the width that WINDOW take up. +If WINDOW is nil, get current window." + (let ((edges (window-edges window))) + (- (nth 2 edges) (nth 0 edges)))) + +(defun sr-speedbar-window-dedicated-only-one-p () + "Only have one non-dedicated window." + (interactive) + (let ((window-number 0) + (dedicated-window-number 0)) + (walk-windows + (lambda (w) + (with-selected-window w + (incf window-number) + (if (window-dedicated-p w) + (incf dedicated-window-number))))) + (if (and (> dedicated-window-number 0) + (= (- window-number dedicated-window-number) 1)) + t nil))) + +(defun sr-speedbar-window-exist-p (window) + "Return `non-nil' if WINDOW is exist. +Otherwise return nil." + (and window (window-live-p window))) + +(defun sr-speedbar-buffer-exist-p (buffer) + "Return `non-nil' if BUFFER is exist. +Otherwise return nil." + (and buffer (buffer-live-p buffer))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Advices ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defadvice delete-other-windows (around sr-speedbar-delete-other-window-advice activate) + "This advice to make `sr-speedbar' window can't deleted by command `delete-other-windows'." + (let ((sr-speedbar-active-p (sr-speedbar-window-exist-p sr-speedbar-window))) + (if sr-speedbar-active-p + (let ((current-window (selected-window))) + (dolist (win (window-list)) + (when (and (window-live-p win) + (not (eq current-window win)) + (not (window-dedicated-p win))) + (delete-window win)))) + ad-do-it))) + +(defadvice delete-window (before sr-speedbar-delete-window-advice activate) + "This advice to remember `sr-speedbar' window width before deleted. +Use `delete-window' delete `sr-speedbar' window have same effect as `sr-speedbar-close'." + ;; Remember window width before deleted. + (sr-speedbar-remember-window-width)) + +(defadvice pop-to-buffer (before sr-speedbar-pop-to-buffer-advice activate) + "This advice is to fix `pop-to-buffer' problem with dedicated window. +Default, function `display-buffer' can't display buffer in select window +if current window is `dedicated'. + +So function `display-buffer' conflict with `sr-speedbar' window, because +`sr-speedbar' window is `dedicated' window. + +That is to say, when current frame just have one `non-dedicated' window, +any functions that use `display-buffer' can't split windows +to display buffer, even option `pop-up-windows' is enable. + +And the example function that can occur above problem is `pop-to-buffer'." + (when (and pop-up-windows ;`pop-up-windows' is enable + (sr-speedbar-window-dedicated-only-one-p) ;just have one `non-dedicated' window + (sr-speedbar-window-exist-p sr-speedbar-window) + (not (sr-speedbar-window-p)) ;not in `sr-speedbar' window + (not (bound-and-true-p helm-alive-p))) + (split-window-vertically) + (windmove-down))) + +(defadvice other-window (after sr-speedbar-other-window-advice) + "Default, can use `other-window' select window in cyclic ordering of windows. +But sometimes we don't want select `sr-speedbar' window use `other-window'. +Just want make `sr-speedbar' window as a view sidebar. + +This advice can make `other-window' skip `sr-speedbar' window." + (let ((count (or (ad-get-arg 0) 1))) + (when (and (sr-speedbar-window-exist-p sr-speedbar-window) + (eq sr-speedbar-window (selected-window))) + (other-window count)))) + +(provide 'sr-speedbar) + +;;; sr-speedbar.el ends here +#+end_src + +*** Speedbar options +#+begin_src emacs-lisp + (setq + speedbar-show-unknown-files t + sr-speedbar-right-side nil) + + (global-set-key (kbd "C-x C-n") 'sr-speedbar-toggle) +#+end_src +** plantuml + +plantuml is a pretty easy way to make decent looking flow chart sorta things. + +#+begin_src emacs-lisp + ;(use-package plantuml-mode + ; :config + ; (progn + ; (setq org-plantuml-jar-path (expand-file-name "~/Docs/plantuml.jar")) + ; (add-to-list 'org-src-lang-modes '("plantuml" . plantuml)))) +#+end_src + +** Elpher + +Elpher is a nice little gemini / gopher client. + +#+begin_src emacs-lisp + (use-package elpher) +#+end_src + +* Language Configs +** Ada +#+begin_src emacs-lisp + ;;(use-package ada-mode) +#+end_src +** Go configuration +*** go-add-tags + +This lets one select a ~struct~ or similar and auto add the ~`json:"NAME"`~ bits. + +#+begin_src emacs-lisp +(use-package go-add-tags + :bind + ("C-c t" . go-add-tags)) +#+end_src + +*** go-mode + +This allows for things like ~gofmt~ and auto adding / removing of imports. + +#+begin_src emacs-lisp + (use-package go-mode + :after (go-add-tags lsp-mode) + :bind + ("C-c t" . go-add-tags)) + (defun lsp-go-install-save-hooks () + (add-hook 'before-save-hook #'lsp-format-buffer t t) + (add-hook 'before-save-hook #'lsp-organize-imports t t)) + (add-hook 'go-mode-hook #'lsp-go-install-save-hooks) +#+end_src + +*** go-eldoc + +This extends eldoc to be able to speak Go - quite handy for quickly looking +up what things do. + +#+begin_src emacs-lisp +(use-package go-eldoc + :after (go-mode lsp-mode) + :hook + (go-mode . go-eldoc-setup)) +#+end_src + +*** yasnippet + +Some go tools use this. + +#+begin_src emacs-lisp +(use-package yasnippet + :commands yas-minor-mode + :hook (go-mode . yas-minor-mode)) +#+end_src + +** Zig configuration +#+begin_src emacs-lisp + (use-package zig-mode) +#+end_src + +** Lua + +#+begin_src emacs-lisp + (use-package lua-mode) +#+end_src +* Mail + +~mu~ has been the best mail client for me on emacs. + +** Initializing mu + +The defaults ~mu~ uses make no sense. ~~/.cache~ is for .. caching data, not +persistent databases.. So we init things with sane defaults: + +#+begin_src shell +mu init --muhome=/home/qbit/.mu -m /home/qbit/Maildir/fastmail/ --my-address="aaron@bolddaemon.com" +#+end_src + +** General mail configuration + +#+begin_src emacs-lisp + (require 'smtpmail) + (setq user-mail-address "aaron@bolddaemon.com" + user-full-name "Aaron Bieber" + message-send-mail-function 'smtpmail-send-it + message-kill-buffer-on-exit t + smtpmail-smtp-user "qbit@fastmail.com" + smtpmail-smtp-server "smtp.fastmail.com" + smtpmail-smtp-service 465 + smtpmail-default-smtp-server "smtp.fastmail.com" + smtpmail-stream-type 'ssl) +#+end_src + +** mu4e specific configs +#+begin_src emacs-lisp + (require 'mu4e) + (require 'mu4e-speedbar) + (require 'org-mu4e) + (setq mail-user-agent 'mu4e-user-agent + mu4e-get-mail-command "mbsync fastmail" + mu4e-update-interval 420 + mu4e-compose-context-policy nil + mu4e-context-policy 'pick-first + mu4e-drafts-folder "/Drafts" + mu4e-sent-folder "/Sent Items" + mu4e-trash-folder "/Trash" + mu4e-maildir-shortcuts + '( ("/INBOX" . ?i) + ("/Archive" . ?a) + ("/Sent Items" . ?s)) + org-mu4e-link-query-in-headers-mode nil + mu4e-attachment-dir + (lambda (fname mtype) + (cond + ((and fname (string-match "\\.diff$" fname)) "~/patches") + ((and fname (string-match "\\.patch$" fname)) "~/patches") + ((and fname (string-match "\\.diff.gz$" fname)) "~/patches") + (t "~/Downloads"))) + mu4e-bookmarks + `(( :name "Inbox" + :query "maildir:/Inbox AND NOT flag:trashed" + :key ?i) + ( :name "TODO" + :query "maildir:/TODO AND NOT flag:trashed" + :key ?T) + ( :name "Unread messages" + :query "flag:unread AND NOT flag:trashed AND NOT list:ports-changes.openbsd.org AND NOT list:source-changes.openbsd.org" + :key ?u) + ( :name "Today's messages" + :query (concat + "date:today..now" + " AND NOT flag:trashed" + " AND NOT list:ports-changes.openbsd.org" + " AND NOT list:source-changes.openbsd.org") + :key ?d) + ( :name "Last 7 days" + :query (concat + "date:6d..now" + " AND NOT flag:trashed" + " AND NOT list:ports-changes.openbsd.org" + " AND NOT list:source-changes.openbsd.org") + :key ?w) + ( :name "Hackers" + :query "list:hackers.openbsd.org AND NOT flag:trashed" + :key ?h) + ( :name "Bugs" + :query "list:bugs.openbsd.org AND NOT flag:trashed" + :key ?b) + ( :name "Tech" + :query "list:tech.openbsd.org AND NOT flag:trashed" + :key ?t) + ( :name "Ports" + :query "list:ports.openbsd.org AND NOT flag:trashed" + :key ?p) + ( :name "Misc" + :query "list:misc.openbsd.org AND NOT flag:trashed" + :key ?m) + ( :name "9front" + :query "list:9front.9front.org AND NOT flag:trashed" + :key ?9) + ( :name "GOT" + :query "list:gameoftrees.openbsd.org AND NOT flag:trashed" + :key ?g))) +#+end_src + +* org-mode + +Oh ~org-mode~. It's the reason I started using emacs.. and it's the reason I +can't quit! + +** Config +#+begin_src emacs-lisp + (org-babel-do-load-languages + 'org-babel-load-languages + '((plantuml . t) + (dot . t) + (latex . t))) +#+end_src +** Publish bits + +I publish some of my notes [[https://suah.dev/p][on suah.dev/p]]. Also some recipes. + +#+begin_src emacs-lisp + (setq my-org-publish-alist + '(("notes" :components ("org-notes" "notes-static" "notes-rss")) + ("deftly" :components ("deftly-blog" "deftly-static")) + ("ohmyksh" :components ("ohmy-web" "ohmy-static")) + ("org-notes" + :auto-preamble t + :auto-sitemap t + :headline-levels 4 + :publishing-directory "/ssh:suah.dev:/var/www/suah.dev/p/" + :publishing-function org-html-publish-to-html + :recursive t + :section-numbers nil + :html-head "" + :html-link-home "http://suah.dev/p/" + :html-link-up "../" + :style-include-default nil + :sitemap-filename "index.org" + :sitemap-title "Notes" + :with-title t + :author-info nil + :creator-info nil + :base-directory "~/org/notes") + ("deftly-blog" + :auto-preamble t + :auto-sitemap t + :headline-levels 1 + :publishing-directory "/ssh:suah.dev:/var/www/deftly.net/new/" + :publishing-function org-html-publish-to-html + :recursive t + :section-numbers nil + :html-head "" + :html-link-home "http://deftly.net/new" + :html-link-up "../" + :style-include-default nil + :sitemap-title "Deftly.net" + :with-title t + :author-info t + :creator-info nil + :base-directory "~/org/deftly") + ("ohmy-web" + :auto-preamble t + :auto-sitemap nil + :headline-levels 2 + :publishing-directory "/ssh:suah.dev:/var/www/deftly.net/ohmyksh/" + :publishing-function org-html-publish-to-html + :recursive t + :section-numbers nil + :html-head "" + :html-link-home "http://deftly.net/ohmyksh" + :html-link-up "../" + :style-include-default nil + :with-title t + :author-info t + :creator-info nil + :base-directory "~/src/ohmyksh") + ("notes-static" + :base-directory "~/org/notes" + :publishing-directory "/ssh:suah.dev:/var/www/suah.dev/p/" + :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|svg" + :recursive t + :publishing-function org-publish-attachment) + ("deftly-static" + :base-directory "~/org/deftly" + :publishing-directory "/ssh:suah.dev:/var/www/deftly.net/new/" + :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg" + :recursive t + :publishing-function org-publish-attachment) + ("ohmy-static" + :base-directory "~/src/ohmyksh" + :publishing-directory "/ssh:suah.dev:/var/www/deftly.net/ohmyksh/" + :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg" + :recursive t + :publishing-function org-publish-attachment) + ("notes-rss" + :publishing-directory "/ssh:suah.dev:/var/www/suah.dev/p/" + :publishing-function org-rss-publish-to-rss + :recursive t + :rss-extension "xml" + :section-numbers nil + :exclude ".*" + :include ("index.org") + :table-of-contents nil + :base-directory "~/org/notes") + ("mammoth" + :publishing-directory "/ssh:suah.dev:/var/www/mammothcircus.com/" + :publishing-function org-html-publish-to-html + :author-info nil + :creator-info nil + :section-numbers nil + :recursive t + :base-directory "~/org/mammoth") + ("recipes" + :auto-preamble t + :auto-sitemap t + :headline-levels 4 + :publishing-directory "/ssh:suah.dev:/var/www/suah.dev/recipes/" + :publishing-function org-html-publish-to-html + :recursive t + :section-numbers nil + :html-head "" + :html-link-home "http://suah.dev/recipes/" + :html-link-up "../" + :style-include-default nil + :sitemap-filename "index.org" + :sitemap-title "Recipes" + :with-title t + :author-info nil + :creator-info nil + :base-directory "~/org/recipes") + )) +#+end_src + +** Capture templates + +#+begin_src emacs-lisp +(setq my-org-capture-templates + `(("t" "TODO" + entry (file+headline "~/org/todo.org" "TODOs") + ,(concat + "* TODO %?\n" + ":PROPERTIES:\n" + ":LOGGING: TODO(!) WAIT(!) DONE(!) CANCELED(!)\n" + ":END:\n") :prepend t) + ("f" "TODO with File" + entry (file+headline "~/org/todo.org" "TODOs") + ,(concat + "* TODO %?\n" + ":PROPERTIES:\n" + ":LOGGING: TODO(!) WAIT(!) DONE(!) CANCELED(!)\n" + ":END:\n" + "%i\n %a") :prepend t) + ("b" "Bug" + entry (file+olp+datetree "~/org/bugs.org" "Bugs") + "* BUG %?\nEntered on %U\n :PROPERTIES:\n :FILE: %a\n :END:\n" :prepend t) + ("p" "Protocol" + entry (file+headline "~/org/links.org" "Links") + "* %^{Title}\nSource: %u, %c\n #+BEGIN_QUOTE\n%i\n#+END_QUOTE\n\n\n%?") + ("L" "Protocol Link" entry (file+headline "~/org/links.org" "Links") + "* %? %:link\n%:description\n") + ("j" "Journal" + entry (file+olp+datetree "~/org/journal.org") + "* %?\nEntered on %U\n %i\n"))) +#+end_src + +** org + +#+begin_src emacs-lisp + (use-package org + :hook + (org-mode . (lambda () + (turn-on-flyspell) + (auto-revert-mode) + (auto-fill-mode 1))) + :bind + ("C-c c" . org-capture) + ("C-c p" . org-publish) + ("C-c l" . org-store-link) + ("C-c a" . org-agenda) + ("C-c b" . org-iswitchb) + :config + (load-library "find-lisp") + (setq org-directory "~/org" + org-agenda-files (find-lisp-find-files "~/org" "\.org$") + org-startup-indented t + org-log-done 'time + org-export-with-sub-superscripts nil + org-html-inline-images t + org-log-into-drawer t + org-src-tab-acts-natively t + org-agenda-skip-scheduled-if-deadline-is-shown t + org-todo-keywords '((sequence "TODO(t)" "|" "DONE(d)") + (sequence "REPORT(r)" "BUG(b)" "KNOWNCAUSE(k)" "|" "FIXED(f)") + (sequence "|" "CANCELED(c)"))) + (setq org-publish-project-alist my-org-publish-alist) + (setq org-capture-templates my-org-capture-templates)) + +#+end_src + +** org-roam + +#+begin_src emacs-lisp + (use-package org-roam + :ensure t + :init + (setq org-roam-v2-ack t) + :custom + (org-roam-directory "~/roam") + (org-roam-dailies-directory "journal/") + (org-roam-completion-everywhere t) + (org-roam-dailies-capture-templates + '(("d" "default" entry "* %<%I:%M %p>: %?" + :if-new (file+head "%<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n")))) + :bind (("C-c n l" . org-roam-buffer-toggle) + ("C-c n f" . org-roam-node-find) + ("C-c n i" . org-roam-node-insert) + :map org-mode-map + ("C-M-i" . completion-at-point) + :map org-roam-dailies-map + ("Y" . org-roam-dailies-capture-yesterday) + ("T" . org-roam-dailies-capture-tomorrow)) + :bind-keymap + ("C-c n d" . org-roam-dailies-map) + :config + (require 'org-roam-dailies) + (org-roam-db-autosync-mode)) +#+end_src + +** org-brain +#+begin_src emacs-lisp + ;; (use-package org-brain + ;; :init + ;; (setq org-brain-path "~/org/brain") + ;; :config + ;; (bind-key "C-c b" 'org-brain-prefix-map org-mode-map) + ;; (setq org-id-track-globally t) + ;; (setq org-id-locations-file "~/org/.org-id-locations") + ;; (add-hook 'before-save-hook #'org-brain-ensure-ids-in-buffer) + ;; (push '("b" "Brain" plain (function org-brain-goto-end) + ;; "* %i%?" :empty-lines 1) + ;; org-capture-templates) + ;; (setq org-brain-visualize-default-choices 'all) + ;; (setq org-brain-title-max-length 12) + ;; (setq org-brain-include-file-entries nil + ;; org-brain-file-entries-use-title nil)) +#+end_src +** Extra bits +#+begin_src emacs-lisp +(use-package org-journal + :defer t + :config + (setq org-journal-dir "~/org/journal/" + org-journal-file-format "%Y/%m-%d" + org-journal-date-format "%A, %d %B %Y")) +#+end_src + +Add in some org-mode helpers: + +- ~org-habit~ lets me keep track of TODOs and other things. +- ~org-checklist~ lets me reset checklists for reoccurring tasks. + - This requires one to ~pkg_add a2ps~. + - ~RESET_CHECK_BOXES~ property to be set to ~t~ on a task + headline. (properties can be set via ~C-c C-x d~ +#+begin_src emacs-lisp +(require 'org-habit) +;(require 'org-checklist) +#+end_src + +Found this bad boy to integrate pinboard with org-mode: +- https://gist.github.com/khinsen/7ed357eed9b27f142e4fa6f5c4ad45dd +#+begin_src emacs-lisp +(defun org-pinboard-store-link () + "Store a link taken from a pinboard buffer." + (when (eq major-mode 'pinboard-mode) + (pinboard-with-current-pin pin + (org-store-link-props + :type "pinboard" + :link (alist-get 'href pin) + :description (alist-get 'description pin))))) + +(org-link-set-parameters "pinboard" + :follow #'browse-url + :store #'org-pinboard-store-link) +#+end_src + +Custom agenda commands for various things. + +- ~Daily habits~ shows how well I am keeping track of daily things. +#+begin_src emacs-lisp +(setq org-agenda-custom-commands + '(("h" "Daily habits" + ((agenda "")) + ((org-agenda-show-log t) + (org-agenda-ndays 7) + (org-agenda-log-mode-items '(state)))))) +#+end_src + +** GOT + +#+begin_src emacs-lisp + (setq vc-got-dir (expand-file-name "~/.emacs.d/site-lisp/vc-got-1.0")) + (if (file-directory-p vc-got-dir) + (use-package vc-got + :load-path vc-got-dir + :defer t + :init + (add-to-list 'vc-handled-backends 'Got) + (add-to-list 'vc-directory-exclusion-list ".got"))) +#+end_src diff --git a/configs/git.nix b/configs/git.nix new file mode 100644 index 0000000..4ca59ce --- /dev/null +++ b/configs/git.nix @@ -0,0 +1,65 @@ +{ config, pkgs, isUnstable, ... }: + +{ + programs.git = { + enable = true; + lfs.enable = true; + config = { + init = { defaultBranch = "main"; }; + + user = { + name = "Aaron Bieber"; + email = "aaron@bolddaemon.com"; + signingKey = if isUnstable then + "key::sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIHrYWbbgBkGcOntDqdMaWVZ9xn+dHM+Ap6s1HSAalL28AAAACHNzaDptYWlu" + else + "35863350BFEAC101DB1A4AF01F81112D62A9ADCE"; + }; + + branch = { sort = "-committerdate"; }; + alias = { + log = "log --color=never"; + diff = "diff --color=always"; + pr = ''"!f() { git fetch-pr upstream $1; git checkout pr/$1; }; f"''; + fetch-pr = + ''"!f() { git fetch $1 refs/pull/$2/head:refs/remotes/pr/$2; }; f"''; + }; + push = { default = "current"; }; + + gpg = if isUnstable then { format = "ssh"; } else { }; + commit = if isUnstable then { gpgsign = true; } else { }; + + color = { + branch = false; + interactive = false; + log = false; + status = false; + ui = false; + }; + + safe = { directory = "/home/qbit/src/nix-conf"; }; + + transfer = { fsckobjects = true; }; + fetch = { fsckobjects = true; }; + github = { user = "qbit"; }; + + url = { + "ssh://git@github.com/" = { insteadOf = "https://github.com/"; }; + }; + + sendmail = { + smtpserver = "mail.messagingengine.com"; + smtpuser = "qbit@fastmail.com"; + smtpauth = "PLAIN"; + smtpencryption = "tls"; + smtpserverport = 587; + cc = "aaron@bolddaemon.com"; + confirm = "auto"; + }; + + pull = { rebase = false; }; + include = { path = "~/work/git/gitconfig"; }; + }; + }; +} + diff --git a/configs/gitmux.nix b/configs/gitmux.nix new file mode 100644 index 0000000..ba2d708 --- /dev/null +++ b/configs/gitmux.nix @@ -0,0 +1,35 @@ +{ config, lib, pkgs, ... }: { + #environment.systemPackages = with pkgs; [ gitmux ]; + environment.etc."gitmux.conf" = { + text = '' + tmux: + symbols: + branch: '⎇ ' + hashprefix: ':' + ahead: ↑· + behind: ↓· + staged: '● ' + conflict: '✖ ' + modified: '✚ ' + untracked: '… ' + stashed: '⚑ ' + clean: ✔ + styles: + clear: '#[fg=default]' + state: '#[fg=default]' + branch: '#[fg=default]' + remote: '#[fg=default]' + staged: '#[fg=default]' + conflict: '#[fg=default]' + modified: '#[fg=default]' + untracked: '#[fg=default]' + stashed: '#[fg=default]' + clean: '#[fg=default]' + divergence: '#[fg=default]' + layout: [branch, .., remote-branch, divergence, ' - ', flags] + options: + branch_max_len: 0 + branch_trim: right + ''; + }; +} diff --git a/configs/manager.nix b/configs/manager.nix new file mode 100644 index 0000000..3d67ea3 --- /dev/null +++ b/configs/manager.nix @@ -0,0 +1,24 @@ +{ config, lib, ... }: +with lib; { + options = { + nixManager = { + enable = mkEnableOption "Configure host as nix-conf manager."; + user = mkOption { + type = types.str; + default = "root"; + description = '' + User who will own the private key. + ''; + }; + }; + }; + + config = mkIf config.nixManager.enable { + #sops.defaultSopsFile = ../manager.yaml; + sops.defaultSopsFile = config.xin-secrets.manager; + sops.secrets = { + manager_key = { owner = config.nixManager.user; }; + manager_pubkey = { owner = config.nixManager.user; }; + }; + }; +} diff --git a/configs/neomutt.nix b/configs/neomutt.nix new file mode 100644 index 0000000..d2e785e --- /dev/null +++ b/configs/neomutt.nix @@ -0,0 +1,145 @@ +{ config, lib, pkgs, ... }: { + environment.systemPackages = with pkgs; [ neomutt urlview ]; + environment.etc."neomuttrc" = { + text = '' + ignore * + unignore from: subject to cc date x-mailer x-url user-agent + + set from = "aaron@bolddaemon.com" + set realname = "Aaron Bieber" + + set imap_user = "qbit@fastmail.com" + set imap_pass = `cat /run/secrets/fastmail` + + set smtp_url = "smtps://$imap_user@mail.messagingengine.com" + set smtp_pass = $imap_pass + + set folder = "imaps://mail.messagingengine.com:993" + set spoolfile = "+INBOX" + + set header_cache = ~/.mutt/cache/fm/headers + set message_cachedir = ~/.mutt/cache/fm/bodies + + folder-hook . set from="aaron@bolddaemon.com" + + unmailboxes * + named-mailboxes Inbox "=INBOX" + named-mailboxes git "=INBOX.git" + named-mailboxes OpenBSD/ "=INBOX.OpenBSD" + named-mailboxes OpenBSD/Hackers "=INBOX.OpenBSD.Hackers" + named-mailboxes OpenBSD/Tech "=INBOX.OpenBSD.Tech" + named-mailboxes OpenBSD/Ports "=INBOX.OpenBSD.Ports" + named-mailboxes OpenBSD/GOT "=INBOX.OpenBSD.GOT" + named-mailboxes OpenBSD/Bugs "=INBOX.OpenBSD.Bugs" + named-mailboxes OpenBSD/Misc "=INBOX.OpenBSD.Misc" + named-mailboxes OpenBSD/ARM "=INBOX.OpenBSD.Arm" + named-mailboxes OpenBSD/PPC "=INBOX.OpenBSD.ppc" + named-mailboxes OpenBSD/src-ch "=INBOX.OpenBSD.src-changes" + named-mailboxes OpenBSD/ports-ch "=INBOX.OpenBSD.ports-changes" + named-mailboxes 9front "=INBOX.9front" + named-mailboxes OSS-Sec "=INBOX.OSS-Sec" + named-mailboxes Archive "=INBOX.Archive" + named-mailboxes Sent "=INBOX.Sent Items" + named-mailboxes Drafts "=INBOX.Drafts" + named-mailboxes Trash "=INBOX.Trash" + named-mailboxes JunkCan "=INBOX.JunkCan + + set editor = "nvim" + + set certificate_file = ~/.mutt/certificates + + set mail_check = 120 + set mail_check_stats = yes + set timeout = 300 + set imap_keepalive = 300 + set imap_passive + set imap_check_subscribed = yes + set ispell = "aspell --mode=email --add-email-quote=%,#,:,} --check" + set message_cache_clean = yes + set user_agent = no + set smart_wrap = yes + + set attach_format="%u%D%I %t%2n %T%.20d %> [%.7m/%.10M, %.6e%?C?, %C?, %s] " + set date_format="!%a, %d %b %Y at %H:%M:%S %Z" + set forward_format="fwd: %s" + set index_format="%[%m-%d] [%Z] %-54.54s %F" + set pager_format=" %f: %s" + set sidebar_format="%B%* %?N?(%N)?" + set status_format=" %h: %f (msgs:%?M?%M/?%m %l%?n? new:%n?%?o? old:%o?%?d? del:%d?%?F? flag:%F?%?t? tag:%t?%?p? post:%p?%?b? inc:%b?%?l??) %> %_v " + + set move = no + + set askcc + + set sort = 'threads' + set sort_aux = 'last-date-received' + + set mailcap_path="~/.mailcap" + + set sidebar_visible = yes + set sidebar_width = 30 + set sidebar_format = "%B%?F? [%F]?%* %?N?%N/?%S" + + bind index,pager \Ck sidebar-prev + bind index,pager \Cj sidebar-next + bind index,pager \Co sidebar-open + + set pager_index_lines=10 + + set spoolfile = "=" + set record="=INBOX.Sent Items" + set postponed="=INBOX.Drafts" + set trash = "=INBOX.Trash" + + mono attachment bold + mono body underline "(https?|t?ftp|mailto|gopher|ssh|telnet|finger)://[^ ]+" + mono body underline "[-a-z_0-9.]+@[-a-z_0-9.]+[a-z]" # email addresses + mono body bold "-----Original Message-----" + mono body bold "[;:]-[)/(|]" + mono header none . + mono header bold "^From: " + mono header bold "^Resent-From: " + mono header bold "^To: " + mono header bold "^Subject: " + mono header bold "^Organi[zs]ation: " + mono header bold "^Priority: Urgent" + mono header bold "^Importance: high" + mono index bold '~U' + mono index bold '~F' + mono signature bold + mono tilde bold + mono tree bold + mono quoted bold + + color normal default default + color attachment brightdefault default + color body brightdefault default "(http|https|ftp|mailto|gopher|telnet|finger)://[^ ]+" + color body brightdefault default "[-a-z_0-9.]+@[-a-z_0-9.]+[a-z]" + color body brightdefault default "-----Original Message-----" + color body brightdefault default "[;:]-[)/(|]" + color header default default . + color header brightdefault default "^From: " + color header brightdefault default "^Resent-From: " + color header brightdefault default "^To: " + color header brightdefault default "^Subject: " + color header brightdefault default "^Organi[zs]ation: " + color header brightdefault default "^Priority: Urgent" + color header brightdefault default "^Importance: high" + color header brightdefault default '~U' + color header brightdefault default '~F' + color signature brightdefault default + color tilde brightblack default + color quoted brightblack default + + color index red default '~F' + color index brightblack default '~D' + color index default default '~U' + color index red default '~z 500000-' + + # make diffs pop + color body brightblack default '^(Index: |\+\+\+ |--- |diff ).*$' + color body red default '^-.*$' + color body green default '^\+.*$' + ''; + }; +} diff --git a/configs/neovim.lua b/configs/neovim.lua new file mode 100644 index 0000000..1756a6b --- /dev/null +++ b/configs/neovim.lua @@ -0,0 +1,38 @@ +local map = vim.api.nvim_set_keymap +local o = vim.o +local cmd = vim.cmd + +cmd("syntax off"); +cmd("set nolist"); +cmd("set ruler"); +cmd("set mouse-=a"); + +require("compe").setup { + enabled = true; + autocomplete = true; + source = { + path = true; + buffer = true; + calc = true; + nvim_lsp = true; + nvim_lua = true; + vsnip = true; + ultisnips = true; + luasnip = true; + }; +} + +local lspc = require("lspconfig") +lspc.gopls.setup {}; + +o.hlsearch = true; + +map('n', '', ':NvimTreeToggle', {noremap = true}) +map('n', 'r', ':NvimTreeRefresh', {noremap = true}) +map('n', 'n', ':NvimTreeFindFile', {noremap = true}) +map('n', 's', ':%s/\\s\\+$//e', {noremap = true}) + +map('n', '1', ':GitGutterToggle', {noremap = true}) +map('n', '2', ':set list!', {noremap = true}) +map('n', '3', ':set nu!', {noremap = true}) +map('n', '4', ':set paste!', {noremap = true}) diff --git a/configs/neovim.nix b/configs/neovim.nix new file mode 100644 index 0000000..50046a9 --- /dev/null +++ b/configs/neovim.nix @@ -0,0 +1,35 @@ +{ config, lib, pkgs, ... }: +with pkgs; +let + baseVimPackages = with vimPlugins; [ + fugitive + nvim-compe + nvim-lspconfig + vim-gitgutter + vim-nix + zig-vim + vimagit + rust-vim + ]; + myVimPackages = if pkgs.system == "aarch64-linux" then + baseVimPackages + else + baseVimPackages ++ [ vimPlugins.vim-go ]; +in { + programs.neovim = { + enable = true; + defaultEditor = true; + configure = { + packages.myVimPackage = { start = myVimPackages; }; + customRC = '' + " Restore cursor position + autocmd BufReadPost * + \ if line("'\"") > 1 && line("'\"") <= line("$") | + \ exe "normal! g`\"" | + \ endif + + luafile ${./neovim.lua} + ''; + }; + }; +} diff --git a/configs/net-overlay.nix b/configs/net-overlay.nix new file mode 100644 index 0000000..9791710 --- /dev/null +++ b/configs/net-overlay.nix @@ -0,0 +1,38 @@ +{ config, lib, pkgs, ... }: +with lib; { + options = { + zerotier = { + enable = mkOption { + description = "Enable ZeroTier"; + default = false; + example = true; + type = lib.types.bool; + }; + }; + tailscale = { + enable = mkOption { + description = "Enable TailScale"; + default = true; + example = true; + type = lib.types.bool; + }; + }; + }; + + config = mkMerge [ + (mkIf config.tailscale.enable { + services = { tailscale = { enable = true; }; }; + networking.firewall.checkReversePath = "loose"; + }) + (mkIf config.zerotier.enable { + environment.systemPackages = with pkgs; [ zerotierone ]; + services = { + zerotierone = { + enable = true; + joinNetworks = [ "db64858fedd3b256" ]; + }; + }; + networking.firewall.checkReversePath = "loose"; + }) + ]; +} diff --git a/configs/tmux.nix b/configs/tmux.nix new file mode 100644 index 0000000..7a91560 --- /dev/null +++ b/configs/tmux.nix @@ -0,0 +1,60 @@ +{ config, lib, ... }: +with lib; { + programs.tmux = { + enable = true; + extraConfig = '' + unbind C-b + set-option -g prefix C-o + + set-window-option -g mode-keys emacs + set-window-option -g automatic-rename off + set-window-option -g base-index 1 + + bind-key \\ split-window -h -c '#{pane_current_path}' # vertical pane + bind-key - split-window -v -c '#{pane_current_path}' # horizontal pane + + bind-key C-r source-file /etc/tmux.conf \; \ + display-message "source-file done" + + bind-key m set mouse \; \ + display-message "toggle mouse" + + bind-key C-s set synchronize-panes \; \ + display-message "toggle synchronize-panes" + + # stolen from jca + bind o send-prefix + bind C-o last-window + + bind-key h select-pane -L + bind-key j select-pane -D + bind-key k select-pane -U + bind-key l select-pane -R + + set -g bell-action any + + set -g default-terminal "tmux-256color" + + set -g set-titles on + + set -g automatic-rename + set-option -g status-bg colour253 + set-window-option -g clock-mode-colour colour246 + set -g clock-mode-style 12 + set-window-option -g window-status-bell-style fg=white,bg=red + + # Change the default escape-time to 0 (from 500) so emacs will work right + set -g escape-time 0 + + set -g window-status-current-format '#[bg=colour250]#I:#W•' + + set -g status-left '#[fg=green][#[fg=red]#S:#(~/bin/beat)#[fg=black,dim]#[fg=green]] ' + set -g status-right-length 50 + + set -g status-right '#[fg=green][#[fg=black]#(basename "#{pane_current_path}")#[fg=green]][#[fg=black]%Y-%m-%d #[fg=black]%I:%M %p#[default]#[fg=green]]' + + set -g window-style 'bg=#DEDEFF' + set -g window-active-style 'bg=terminal' + ''; + }; +} diff --git a/configs/zsh.nix b/configs/zsh.nix new file mode 100644 index 0000000..65da43d --- /dev/null +++ b/configs/zsh.nix @@ -0,0 +1,57 @@ +{ config, lib, ... }: { + config = { + programs.zsh.interactiveShellInit = '' + export NO_COLOR=1 + # That sweet sweet ^W + WORDCHARS='*?_-.[]~=&;!#$%^(){}<>' + + autoload -Uz compinit && compinit + + set -o emacs + + ''; + programs.zsh.promptInit = '' + autoload -U promptinit && promptinit + autoload -Uz vcs_info + autoload -Uz colors && colors + + setopt prompt_subst + #setopt prompt_sp + + zstyle ':vcs_info:*' enable git hg cvs + zstyle ':vcs_info:*' get-revision true + zstyle ':vcs_info:git:*' check-for-changes true + zstyle ':vcs_info:git:*' formats '(%b)' + + precmd_vcs_info() { vcs_info } + precmd_functions+=( precmd_vcs_info ) + + prompt_char() { + if [ -z "$IN_NIX_SHELL" ]; then + echo -n "%#" + else + echo -n ";" + fi + } + + PROMPT='%n@%m[%(?.%{$fg[default]%}.%{$fg[red]%})%?%{$reset_color%}]:%~$vcs_info_msg_0_$(prompt_char) ' + + k() { + ''${K_DEBUG} + if [ -z $1 ]; then + echo $PWD >> ~/.k + else + K=~/.k + case $1 in + clean) sort -u $K -o ''${K};; + rm) sed -i -E "\#^''${2:-''${PWD}}\$#d" ''${K};; + ls) cat ''${K};; + *) cd "$(grep -e "$1" ''${K} | head -n 1)";; + esac + fi + } + + eval "$(direnv hook zsh)" + ''; + }; +} diff --git a/dbuild/build-consumer.nix b/dbuild/build-consumer.nix new file mode 100644 index 0000000..283c0a4 --- /dev/null +++ b/dbuild/build-consumer.nix @@ -0,0 +1,36 @@ +{ config, lib, ... }: + +with lib; { + options = { + buildConsumer = { enable = mkEnableOption "Use remote build machines"; }; + }; + + config = mkIf config.buildConsumer.enable { + programs.ssh.knownHosts = { + pcake = { + hostNames = [ "pcake" "pcake.tapenet.org" "10.6.0.202" ]; + publicKey = + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHgqVw3QWNG6Ty5o2HwW+25Eh59W3lZ30+wMqTEkUZVH"; + }; + }; + programs.ssh.extraConfig = '' + Host pcake + HostName 10.6.0.202 + IdentitiesOnly yes + IdentityFile /root/.ssh/nix_remote + ''; + nix.buildMachines = [{ + hostName = "pcake"; + systems = [ "x86_64-linux" "aarch64-linux" ]; + maxJobs = 2; + speedFactor = 4; + supportedFeatures = [ "kvm" "big-parallel" "nixos-test" "benchmark" ]; + mandatoryFeatures = [ ]; + }]; + + nix.distributedBuilds = true; + nix.extraOptions = '' + builders-use-substitutes = true + ''; + }; +} diff --git a/dbuild/build-server.nix b/dbuild/build-server.nix new file mode 100644 index 0000000..be5c034 --- /dev/null +++ b/dbuild/build-server.nix @@ -0,0 +1,18 @@ +{ config, lib, ... }: + +with lib; { + options = { + buildServer = { + enable = mkEnableOption "Server will be used as part of the build infra"; + }; + }; + + config = mkIf config.buildServer.enable { + boot.binfmt.emulatedSystems = [ "aarch64-linux" ]; + + users.users.root.openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICEtoU6ObMP7wmglT7rXMg0HEnh7cGBo6COL7BpmRC/o" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGyOQdqfm7mG+5rOGIqPHSaZQdG/4L03dVJnuE1lO1fo" + ]; + }; +} diff --git a/dbuild/default.nix b/dbuild/default.nix new file mode 100644 index 0000000..89bbee8 --- /dev/null +++ b/dbuild/default.nix @@ -0,0 +1,4 @@ +{ config, lib, ... }: +with lib; { + imports = [ ./build-consumer.nix ./build-server.nix ]; +} diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..bb9a21b --- /dev/null +++ b/default.nix @@ -0,0 +1,142 @@ +{ config, lib, options, pkgs, isUnstable, ... }: + +let + managementKey = + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDM2k2C6Ufx5RNf4qWA9BdQHJfAkskOaqEWf8yjpySwH Nix Manager"; +in { + imports = [ + ./configs/colemak.nix + ./configs/develop.nix + ./configs/dns.nix + ./configs/doas.nix + ./configs/gitmux.nix + ./configs/git.nix + ./configs/neovim.nix + ./configs/manager.nix + ./configs/tmux.nix + ./configs/net-overlay.nix + ./configs/zsh.nix + ./dbuild + ./gui + #./overlays + ./services + ./system/nix-config.nix + ./system/nix-lockdown.nix + #./system/update.nix + ./users + + ./bins + ]; + + options.myconf = { + hwPubKeys = lib.mkOption rec { + type = lib.types.listOf lib.types.str; + default = [ + managementKey + "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIB1cBO17AFcS2NtIT+rIxR2Fhdu3HD4de4+IsFyKKuGQAAAACnNzaDpsZXNzZXI=" + "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIDEKElNAm/BhLnk4Tlo00eHN5bO131daqt2DIeikw0b2AAAABHNzaDo=" + "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBB/V8N5fqlSGgRCtLJMLDJ8Hd3JcJcY8skI0l+byLNRgQLZfTQRxlZ1yymRs36rXj+ASTnyw5ZDv+q2aXP7Lj0=" + "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIHrYWbbgBkGcOntDqdMaWVZ9xn+dHM+Ap6s1HSAalL28AAAACHNzaDptYWlu" + ]; + example = default; + description = "List of hardwar public keys to use"; + }; + }; + + config = { + sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; + + security.pki.certificates = ['' + -----BEGIN CERTIFICATE----- + MIIBrjCCAVOgAwIBAgIIKUKZ6zcNut8wCgYIKoZIzj0EAwIwFzEVMBMGA1UEAxMM + Qm9sZDo6RGFlbW9uMCAXDTIyMDEyOTAxMDMxOVoYDzIxMjIwMTI5MDEwMzE5WjAX + MRUwEwYDVQQDEwxCb2xkOjpEYWVtb24wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC + AARYgIn1RWf059Hb964JEaiU3G248k2ZpBHtrACMmLRRO9reKr/prEJ2ltKrjCaX + +98ButRNIn78U8pL+H+aeE0Zo4GGMIGDMA4GA1UdDwEB/wQEAwIChDAdBgNVHSUE + FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNV + HQ4EFgQUiUdCcaNy3E2bFzO9I76TPlMJ4w4wHwYDVR0jBBgwFoAUiUdCcaNy3E2b + FzO9I76TPlMJ4w4wCgYIKoZIzj0EAwIDSQAwRgIhAOd6ejqevrYAH5JtDdy2Mh9M + OTIx9nDZd+AOAg0wzlzfAiEAvG5taCm14H+qdWbEZVn+vqj6ChtxjH7fqOHv3Xla + HWw= + -----END CERTIFICATE----- + '']; + + # from https://github.com/dylanaraps/neofetch + users.motd = '' + + ::::. '::::: ::::' + '::::: ':::::. ::::' + ::::: '::::.::::: + .......:::::..... :::::::: + ::::::::::::::::::. :::::: ::::. + ::::::::::::::::::::: :::::. ::::' + ..... ::::' :::::' + ::::: '::' :::::' + ........::::: ' :::::::::::. + ::::::::::::: ::::::::::::: + ::::::::::: .. ::::: + .::::: .::: ::::: + .::::: ..... + ::::: :::::. ......:::::::::::::' + ::: ::::::. ':::::::::::::::::' + .:::::::: ':::::::::: + .::::'''::::. '::::. + .::::' ::::. '::::. + .:::: :::: '::::. + + ''; + boot.cleanTmpDir = true; + + environment.systemPackages = with pkgs; [ + (callPackage ./pkgs/got.nix { inherit isUnstable; }) + + age + apg + bind + btop + direnv + git-sync + jq + lz4 + minisign + mosh + nix-diff + nix-top + nixfmt + nix-index + pass + rbw + tmux + ]; + + environment.interactiveShellInit = '' + alias vi=nvim + ''; + + time.timeZone = "US/Mountain"; + + documentation.enable = true; + documentation.man.enable = true; + + networking.timeServers = options.networking.timeServers.default; + + programs = { + zsh.enable = true; + gnupg.agent.enable = true; + ssh = { + knownHosts."[namish.humpback-trout.ts.net]:2222".publicKey = + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF9jlU5XATs8N90mXuCqrflwOJ+s3s7LefDmFZBx8cCk"; + startAgent = true; + extraConfig = ""; + }; + }; + + services = { + openssh = { + enable = true; + permitRootLogin = "prohibit-password"; + passwordAuthentication = false; + }; + }; + }; +} diff --git a/deploy b/deploy new file mode 100755 index 0000000..9bfcddd --- /dev/null +++ b/deploy @@ -0,0 +1,112 @@ +#!/usr/bin/env sh + +. ./common.sh + +trap error INT TERM + +rebuild() { + host="$(resolveAlias $1)" + skip_check=$2 + + msg "Rebuilding: ${host}" + + #if [ "$host" = "$(uname -n)" ]; then + # # Don't use ssh for the machine we are running on. Assume it's a manager machine and needs to + # # be bootstrapped. + # if [ "$(nixos-version --json | jq -r .configurationRevision)" = "$CurrentVersion" ] && [ $skip_check = false ]; then + # msg "Up-to-date: ${host}" + # return 0 + # else + # sudo nixos-rebuild --flake .#${1} switch + # fi + # return 0 + #fi + + if ! tsAlive $host; then + msg "can't reach ${host}.. skipping.." + return + fi + + hostVersion=$(${SSH} root@${host} 'nixos-version --json | jq -r .configurationRevision') + if [ $? != 0 ]; then + return $? + fi + + if [ "$hostVersion" = "$CurrentVersion" ] && [ $skip_check = false ]; then + msg "Up-to-date: ${host}" + return 0 + fi + + nixos-rebuild --flake .#${1} --build-host root@${host} --target-host root@${host} switch + return $? +} + +if [ "$1" = "install" ]; then + host="$(resolveAlias $2)" + + start + + if [ ! -d hosts/${host} ]; then + msg "No config found for $host" + exit 1 + fi + + set -eu + mkdir -p .gcroots + out=$(nix build -o .gcroots/${host} --json .#nixosConfigurations.${host}.config.system.build.toplevel | jq -r '.[0].outputs.out') + + nix copy -s --to "ssh://root@${host}" "$out" + nix copy -s --derivation --to "ssh://root@${host}" "$out" + + ${SSH} "root@${host}" nix build --profile /nix/var/nix/profiles/system "$out" + ${SSH} "root@${host}" nix shell -vv "$out" -c switch-to-configuration "$@" + exit 0 +fi + +if [ "$1" = "update" ]; then + can_sign=0 + for i in $(ssh-add -L | awk '{print $NF}'); do + grep -q $i .allowed_signers && can_sign=1 + done + + if [ $can_sign = 1 ]; then + nix flake update --commit-lock-file + exit + else + echo "Can't find signing key." + exit 1 + fi +fi + +if [ "$1" = "installer" ]; then + nix build .#nixosConfigurations.isoInstall.config.system.build.isoImage + exit $? +fi + +start + +if [ "$1" = "diff" ]; then + set -x + host="$(resolveAlias $2)" + mkdir -p .gcroots + out=$(nix build -o .gcroots/${host} --json .#nixosConfigurations.${2}.config.system.build.toplevel | jq -r '.[0].outputs.out') + nix copy -s --to "ssh://root@$host" "$out" + nix copy -s --derivation --to "ssh://root@$host" "$out" + ${SSH} "root@$host" "nix-store -qd /run/current-system $out | xargs nix-diff --color=always" | less + exit $? +fi + +ret=0 +if [ ${#@} = 1 ]; then + rebuild $1 true || ret=1 +else + for host in $(ls hosts); do + rebuild $host false || ret=1 + done +fi + +if [ $ret = 0 ]; then + finish +else + msg "WARNING: Management key retained!" +fi diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..f4983ed --- /dev/null +++ b/flake.lock @@ -0,0 +1,262 @@ +{ + "nodes": { + "darwin": { + "inputs": { + "nixpkgs": [ + "stable" + ] + }, + "locked": { + "lastModified": 1661329936, + "narHash": "sha256-dafFjAcJPo0SdegK3E+SnTI8CNMgV/bBm/6CeDf82f8=", + "owner": "lnl7", + "repo": "nix-darwin", + "rev": "ef0e7f41cdf8fae1d2390c4df246c90a364ed8d9", + "type": "github" + }, + "original": { + "owner": "lnl7", + "repo": "nix-darwin", + "type": "github" + } + }, + "emacs-overlay": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": [ + "stable" + ] + }, + "locked": { + "lastModified": 1661431289, + "narHash": "sha256-LnUTdQeJ/eaGhxYBwDXVAfroHnGqt+TXjxHG2EDvDPE=", + "owner": "nix-community", + "repo": "emacs-overlay", + "rev": "f9ae61e7793b2dd0a2beef59270fc4b4e9f54a46", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "emacs-overlay", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gqrss": { + "flake": false, + "locked": { + "lastModified": 1643674728, + "narHash": "sha256-1ZGjifDgqA9yk9l0YB4rLpcvwaq9lWxDgItJ7lCVj2I=", + "owner": "qbit", + "repo": "gqrss", + "rev": "107278bcd497501475435d9a36f0aa91d1f2e1f9", + "type": "github" + }, + "original": { + "owner": "qbit", + "repo": "gqrss", + "type": "github" + } + }, + "mcchunkie": { + "flake": false, + "locked": { + "lastModified": 1660353113, + "narHash": "sha256-UNPv9QXFJeNx+3RleseNVSKBZGNc3eiMsEKnfIVyoeA=", + "owner": "qbit", + "repo": "mcchunkie", + "rev": "aaa3bc6958a2a99fbc061afadb968e1fa8160cba", + "type": "github" + }, + "original": { + "owner": "qbit", + "repo": "mcchunkie", + "type": "github" + } + }, + "microca": { + "flake": false, + "locked": { + "lastModified": 1647132345, + "narHash": "sha256-3lkT/b9vIf4nMGKnS14sWr5GhcgUFK/xsCgooM60SiU=", + "owner": "qbit", + "repo": "microca", + "rev": "8e175431c2027751704e74347f0842a5af372f53", + "type": "github" + }, + "original": { + "owner": "qbit", + "repo": "microca", + "type": "github" + } + }, + "nixos-hardware": { + "locked": { + "lastModified": 1660407119, + "narHash": "sha256-04lWO0pDbhAXFdL4v2VzzwgxrZ5IefKn+TmZPiPeKxg=", + "owner": "NixOS", + "repo": "nixos-hardware", + "rev": "12620020f76b1b5d2b0e6fbbda831ed4f5fe56e1", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "master", + "repo": "nixos-hardware", + "type": "github" + } + }, + "nixpkgs-22_05": { + "locked": { + "lastModified": 1661009065, + "narHash": "sha256-i+Q2ttGp4uOL3j0wEYP3MXLcu/4L/WbChxGQogiNSZo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9a91318fffec81ad009b73fd3b640d2541d87909", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "release-22.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "darwin": "darwin", + "emacs-overlay": "emacs-overlay", + "gqrss": "gqrss", + "mcchunkie": "mcchunkie", + "microca": "microca", + "nixos-hardware": "nixos-hardware", + "sshKnownHosts": "sshKnownHosts", + "stable": "stable", + "unstable": "unstable", + "xin-secrets": "xin-secrets" + } + }, + "sops-nix": { + "inputs": { + "nixpkgs": [ + "xin-secrets", + "stable" + ], + "nixpkgs-22_05": "nixpkgs-22_05" + }, + "locked": { + "lastModified": 1661054796, + "narHash": "sha256-SWiWmENiim8liUNOZ1oxjc5yKb/fNpcyfSRo41bsEy0=", + "owner": "Mic92", + "repo": "sops-nix", + "rev": "6068774a8e85fea4b0177efcc90afb3c3b74430b", + "type": "github" + }, + "original": { + "owner": "Mic92", + "repo": "sops-nix", + "type": "github" + } + }, + "sshKnownHosts": { + "flake": false, + "locked": { + "lastModified": 1656701928, + "narHash": "sha256-wxSl4azf1nQNcUZVNOn6zDzk31sQ1NNoiFDEVUqdGmk=", + "owner": "qbit", + "repo": "ssh_known_hosts", + "rev": "3b18047443bfe259497de7584cf389c72c5afec2", + "type": "github" + }, + "original": { + "owner": "qbit", + "repo": "ssh_known_hosts", + "type": "github" + } + }, + "stable": { + "locked": { + "lastModified": 1661405040, + "narHash": "sha256-bubG0NFaLT9sj7dCCFGrp9CQcTkXyWRxGRLwZNF5oro=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5f17353d51c38d56df382517b038f37b8fc02f93", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-22.05-small", + "repo": "nixpkgs", + "type": "github" + } + }, + "stable_2": { + "locked": { + "lastModified": 1661405040, + "narHash": "sha256-bubG0NFaLT9sj7dCCFGrp9CQcTkXyWRxGRLwZNF5oro=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5f17353d51c38d56df382517b038f37b8fc02f93", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-22.05-small", + "repo": "nixpkgs", + "type": "github" + } + }, + "unstable": { + "locked": { + "lastModified": 1661328374, + "narHash": "sha256-GGMupfk/lGzPBQ/dRrcQEhiFZ0F5KPg0j5Q4Fb5coxc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "f034b5693a26625f56068af983ed7727a60b5f8b", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "xin-secrets": { + "inputs": { + "sops-nix": "sops-nix", + "stable": "stable_2" + }, + "locked": { + "lastModified": 1661447779, + "narHash": "sha256-J3+gFXbSzRiyROS01+jycpwmJQ2ElJKRE/SoQk335qk=", + "ref": "main", + "rev": "b1377c8cd7e0eedaec8002f8a12c82205e532a1f", + "revCount": 23, + "type": "git", + "url": "ssh://gitea@git.tapenet.org:2222/qbit/xin-secrets.git" + }, + "original": { + "ref": "main", + "type": "git", + "url": "ssh://gitea@git.tapenet.org:2222/qbit/xin-secrets.git" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..a3173eb --- /dev/null +++ b/flake.nix @@ -0,0 +1,155 @@ +{ + description = "bold.daemon"; + + inputs = { + xin-secrets = { + url = + "git+ssh://gitea@git.tapenet.org:2222/qbit/xin-secrets.git?ref=main"; + }; + unstable.url = "github:NixOS/nixpkgs/nixos-unstable"; + stable.url = "github:NixOS/nixpkgs/nixos-22.05-small"; + nixos-hardware = { + url = "github:NixOS/nixos-hardware/master"; + inputs.nixpkgs.follows = "unstable"; + inputs.nixpkgs-22_05.follows = "stable"; + }; + + emacs-overlay = { + url = "github:nix-community/emacs-overlay"; + inputs.nixpkgs.follows = "stable"; + }; + + darwin = { + url = "github:lnl7/nix-darwin"; + inputs.nixpkgs.follows = "stable"; + }; + + sshKnownHosts = { + url = "github:qbit/ssh_known_hosts"; + flake = false; + }; + + microca = { + url = "github:qbit/microca"; + flake = false; + }; + + mcchunkie = { + url = "github:qbit/mcchunkie"; + flake = false; + }; + + gqrss = { + url = "github:qbit/gqrss"; + flake = false; + }; + }; + + outputs = { self, unstable, stable, nixos-hardware, sshKnownHosts, microca + , mcchunkie, gqrss, darwin, xin-secrets, ... }@flakes: + let + hostBase = { + overlays = [ flakes.emacs-overlay.overlay ]; + modules = [ + # Common config stuffs + (import (./default.nix)) + (import "${sshKnownHosts}") + + xin-secrets.nixosModules.sops + xin-secrets.nixosModules.xin-secrets + ]; + }; + + overlays = [ flakes.emacs-overlay.overlay ]; + + buildVer = { system.configurationRevision = self.rev or "DIRTY"; }; + buildShell = pkgs: + pkgs.mkShell { + shellHook = '' + PS1='\u@\h:\w; ' + ''; + nativeBuildInputs = with pkgs; [ + tree + go + jq + statix + sops + nix-diff + nixfmt + git + ssh-to-age + ssh-to-pgp + ]; + }; + buildSys = sys: sysBase: extraMods: name: + sysBase.lib.nixosSystem { + system = sys; + modules = hostBase.modules ++ extraMods ++ [{ + nix = { + registry.nixpkgs.flake = sysBase; + nixPath = [ "nixpkgs=${sysBase}" ]; + }; + }] ++ [ buildVer (./. + "/hosts/${name}") ] + ++ [{ nixpkgs.overlays = overlays; }]; + }; + pkgs = unstable.legacyPackages.x86_64-linux; + darwinPkgs = unstable.legacyPackages.aarch64-darwin; + in { + darwinConfigurations = { + plq = darwin.lib.darwinSystem { + system = "aarch64-darwin"; + modules = [ + xin-secrets.nixosModules.sops + (import "${sshKnownHosts}") + ./overlays + + ./hosts/plq + ]; + }; + }; + + devShells.x86_64-linux.default = buildShell pkgs; + devShells.aarch64-darwin.default = buildShell darwinPkgs; + + nixosConfigurations = { + europa = buildSys "x86_64-linux" unstable [ + "${nixos-hardware}/common/cpu/intel" + "${nixos-hardware}/common/pc/laptop" + "${nixos-hardware}/common/pc/laptop/ssd" + ] "europa"; + box = buildSys "x86_64-linux" stable [ ] "box"; + h = buildSys "x86_64-linux" stable [ ] "h"; + faf = buildSys "x86_64-linux" stable [ ] "faf"; + litr = buildSys "x86_64-linux" unstable [ ] "litr"; + #nerm = buildSys "x86_64-linux" unstable [ ] "nerm"; + hass = buildSys "x86_64-linux" stable [ ] "hass"; + weather = buildSys "aarch64-linux" stable + [ nixos-hardware.nixosModules.raspberry-pi-4 ] "weather"; + + weatherInstall = stable.lib.nixosSystem { + system = "aarch64-linux"; + + modules = [ + (import (./installer.nix)) + xin-secrets.nixosModules.sops + (import "${sshKnownHosts}") + + "${stable}/nixos/modules/installer/sd-card/sd-image-aarch64-installer.nix" + ]; + }; + + isoInstall = stable.lib.nixosSystem { + system = "x86_64-linux"; + + modules = [ + buildVer + (import (./installer.nix)) + xin-secrets.nixosModules.sops + (import "${sshKnownHosts}") + + "${stable}/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix" + ]; + }; + }; + }; +} diff --git a/fmt b/fmt new file mode 100755 index 0000000..780ca6a --- /dev/null +++ b/fmt @@ -0,0 +1,6 @@ +#!/bin/sh + +for i in $(find . -name \*.nix ); do + nixfmt $i +done +statix check . diff --git a/gui/arcan.nix b/gui/arcan.nix new file mode 100644 index 0000000..aaf1d50 --- /dev/null +++ b/gui/arcan.nix @@ -0,0 +1,11 @@ +{ config, lib, pkgs, ... }: +let myArcan = pkgs.arcanPackages or pkgs.arcan; +in with lib; { + options = { + arcan = { enable = mkEnableOption "Enable Arcan/Durden desktop."; }; + }; + + config = mkIf config.arcan.enable { + environment.systemPackages = with pkgs; [ myArcan.all-wrapped ]; + }; +} diff --git a/gui/default.nix b/gui/default.nix new file mode 100644 index 0000000..60d1058 --- /dev/null +++ b/gui/default.nix @@ -0,0 +1,72 @@ +{ config, lib, pkgs, ... }: +with lib; { + imports = [ ./gnome.nix ./kde.nix ./xfce.nix ./arcan.nix ]; + + options = { + pulse = { + enable = mkOption { + description = "Enable PulseAudio"; + default = false; + example = true; + type = types.bool; + }; + }; + pipewire = { + enable = mkOption { + description = "Enable PipeWire"; + default = true; + example = true; + type = types.bool; + }; + }; + }; + + config = mkMerge [ + (mkIf config.arcan.enable { + sound.enable = true; + services = { xserver.enable = false; }; + environment.systemPackages = with pkgs; [ brave go-font vlc pcsctools ]; + }) + (mkIf (config.kde.enable || config.gnome.enable || config.xfce.enable) { + + services = { + xserver.enable = true; + pcscd.enable = true; + }; + + # TODO: TEMP FIX + systemd.services.NetworkManager-wait-online.serviceConfig.ExecStart = + lib.mkForce [ "" "${pkgs.networkmanager}/bin/nm-online -q" ]; + + sound.enable = true; + security.rtkit.enable = true; + environment.systemPackages = with pkgs; [ brave go-font vlc pcsctools ]; + + programs = { + firejail = { + enable = true; + wrappedBinaries = { + firefox = { + executable = "${lib.getBin pkgs.firefox}/bin/firefox"; + profile = "${pkgs.firejail}/etc/firejail/firefox.profile"; + }; + #brave = { + # executable = "${lib.getBin pkgs.brave}/bin/brave"; + # profile = "${pkgs.firejail}/etc/firejail/brave.profile"; + #}; + }; + }; + }; + + }) + (mkIf config.pulse.enable { hardware.pulseaudio = { enable = true; }; }) + (mkIf config.pipewire.enable { + services.pipewire = { + enable = true; + pulse.enable = true; + jack.enable = true; + alsa.enable = true; + }; + }) + ]; +} diff --git a/gui/gnome.nix b/gui/gnome.nix new file mode 100644 index 0000000..ff5ac97 --- /dev/null +++ b/gui/gnome.nix @@ -0,0 +1,9 @@ +{ config, lib, ... }: +with lib; { + options = { gnome = { enable = mkEnableOption "Enable GNOME desktop."; }; }; + + config = mkIf config.gnome.enable { + services.xserver.displayManager.gdm.enable = true; + services.xserver.desktopManager.gnome.enable = true; + }; +} diff --git a/gui/kde.nix b/gui/kde.nix new file mode 100644 index 0000000..ed23d21 --- /dev/null +++ b/gui/kde.nix @@ -0,0 +1,29 @@ +{ config, lib, pkgs, ... }: +with lib; { + options = { kde = { enable = mkEnableOption "Enable KDE desktop."; }; }; + + config = mkIf config.kde.enable { + services.xserver.displayManager.sddm.enable = true; + services.xserver.desktopManager.plasma5.enable = true; + + # Listen for KDE Connect connections on the tailnet + networking.firewall.interfaces = { + "tailscale0" = { + allowedTCPPorts = range 1714 1764; + allowedUDPPorts = range 1714 1764; + }; + }; + + environment.systemPackages = with pkgs; [ + akonadi + plasma5Packages.akonadiconsole + plasma5Packages.akonadi-contacts + plasma5Packages.akonadi-search + plasma5Packages.akonadi-mime + libsForQt5.bismuth + kdeconnect + kmail + plasma-pass + ]; + }; +} diff --git a/gui/xfce.nix b/gui/xfce.nix new file mode 100644 index 0000000..e8f31c1 --- /dev/null +++ b/gui/xfce.nix @@ -0,0 +1,9 @@ +{ config, lib, ... }: +with lib; { + options = { xfce = { enable = mkEnableOption "Enable XFCE desktop."; }; }; + + config = mkIf config.xfce.enable { + services.xserver.displayManager.sddm.enable = true; + services.xserver.desktopManager.xfce = { enable = true; }; + }; +} diff --git a/hosts/.hass/default.nix b/hosts/.hass/default.nix new file mode 100644 index 0000000..71d33cb --- /dev/null +++ b/hosts/.hass/default.nix @@ -0,0 +1,35 @@ +{ config, pkgs, ... }: +let + pubKeys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFbj3DNho0T/SLcuKPzxT2/r8QNdEQ/ms6tRiX6YraJk root@tal.tapenet.org" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIPMaAm4rDxyU975Z54YiNw3itC2fGc3SaE2VaS1fai8 root@box" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIITjFpmWZVWixv2i9902R+g5B8umVhaqmjYEKs2nF3Lu qbit@tal.tapenet.org" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILnaC1v+VoVNnK04D32H+euiCyWPXU8nX6w+4UoFfjA3 qbit@plq" + ]; + userBase = { openssh.authorizedKeys.keys = pubKeys; }; +in { + _module.args.isUnstable = false; + imports = [ ./hardware-configuration.nix ]; + + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + #boot.supportedFilesystems = [ "zfs" ]; + #boot.zfs.devNodes = "/dev/"; + + networking.hostName = "hass"; + networking.hostId = "cd47baaf"; + + networking.useDHCP = false; + #networking.interfaces.enp1s0.useDHCP = true; + #networking.interfaces.enp2s0.useDHCP = true; + + networking.firewall.allowedTCPPorts = [ 22 ]; + + users.users.root = userBase; + users.users.qbit = userBase; + + dnsOverTLS.enable = true; + system.stateVersion = "22.05"; # Did you read the comment? +} + diff --git a/hosts/.hass/hardware-configuration.nix b/hosts/.hass/hardware-configuration.nix new file mode 100644 index 0000000..3e63f80 --- /dev/null +++ b/hosts/.hass/hardware-configuration.nix @@ -0,0 +1,47 @@ +# 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 + "/installer/scan/not-detected.nix") ]; + + boot.initrd.availableKernelModules = [ + "ehci_pci" + "ahci" + "megaraid_sas" + "usb_storage" + "usbhid" + "sd_mod" + "sr_mod" + ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = { + device = "/dev/disk/by-uuid/38318896-9ce1-4ede-a599-9a7d2feb31a1"; + fsType = "ext4"; + }; + + fileSystems."/boot" = { + device = "/dev/disk/by-uuid/57D7-FFCF"; + fsType = "vfat"; + }; + + swapDevices = + [{ device = "/dev/disk/by-uuid/b3d27f92-fbf2-4560-a113-2165201fa8b8"; }]; + + # 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..useDHCP`. + networking.useDHCP = lib.mkDefault true; + # networking.interfaces.eno1.useDHCP = lib.mkDefault true; + # networking.interfaces.eno2.useDHCP = lib.mkDefault true; + # networking.interfaces.enp65s0f0.useDHCP = lib.mkDefault true; + # networking.interfaces.enp65s0f1.useDHCP = lib.mkDefault true; + + hardware.cpu.intel.updateMicrocode = + lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/hosts/.nerm/default.nix b/hosts/.nerm/default.nix new file mode 100644 index 0000000..ae98626 --- /dev/null +++ b/hosts/.nerm/default.nix @@ -0,0 +1,61 @@ +{ config, lib, options, pkgs, fetchFromGitHub, kernel, kmod, ... }: + +let + pubKeys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBZExBj4QByLZSyKJ5+fPQnqDNrbsFz1IQWbFqCDcq9g qbit@ren.bold.daemon" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIITjFpmWZVWixv2i9902R+g5B8umVhaqmjYEKs2nF3Lu qbit@tal.tapenet.org" + ]; + + userBase = { openssh.authorizedKeys.keys = pubKeys; }; + +in { + _module.args.isUnstable = true; + imports = [ ./hardware-configuration.nix ]; + + boot.loader.grub.enable = true; + boot.loader.grub.version = 2; + boot.loader.grub.device = "/dev/vda"; + + buildConsumer.enable = true; + + boot.kernelModules = [ "vmm_clock" "virtio_vmmci" ]; + boot.extraModulePackages = + [ pkgs.linuxPackages.vmm_clock pkgs.linuxPackages.virtio_vmmci ]; + boot.kernelParams = [ "console=ttyS0,115200n8" ]; + + networking.hostName = "nerm"; + + # No IPv6 + networking.enableIPv6 = false; + + networking.useDHCP = false; + networking.interfaces.enp0s2.useDHCP = false; + networking.defaultGateway = "10.10.10.1"; + networking.interfaces.enp0s3.ipv4.addresses = [{ + address = "10.10.10.21"; + prefixLength = 24; + }]; + + nixpkgs.overlays = [ + (self: super: + { + #bitwarden_rs = unstable.bitwarden_rs; + }) + ]; + + environment.systemPackages = with pkgs; [ + ssb-patchwork + signal-desktop + neochat + ]; + + services = { openssh.forwardX11 = true; }; + + networking.firewall.allowedTCPPorts = [ 22 ]; + + users.users.root = userBase; + users.users.qbit = userBase; + + system.stateVersion = "20.03"; +} + diff --git a/hosts/.nerm/hardware-configuration.nix b/hosts/.nerm/hardware-configuration.nix new file mode 100644 index 0000000..800ccac --- /dev/null +++ b/hosts/.nerm/hardware-configuration.nix @@ -0,0 +1,23 @@ +# 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, ... }: + +{ + imports = [ ]; + + boot.initrd.availableKernelModules = [ "virtio_pci" "sr_mod" "virtio_blk" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = { + device = "/dev/disk/by-uuid/be72669d-5454-4602-86cd-3a939d1f4c0f"; + fsType = "ext4"; + }; + + swapDevices = + [{ device = "/dev/disk/by-uuid/b2cd835b-0544-40a8-9c7f-5d9d789a05fc"; }]; + + nix.maxJobs = lib.mkDefault 1; +} diff --git a/hosts/box/default.nix b/hosts/box/default.nix new file mode 100644 index 0000000..5827497 --- /dev/null +++ b/hosts/box/default.nix @@ -0,0 +1,740 @@ +{ lib, config, pkgs, isUnstable, ... }: + +let + photoPrismTag = "220302-buster"; + httpCacheTime = "720m"; + httpAllow = '' + allow 10.6.0.0/24; + allow 100.64.0.0/10; + allow 10.20.30.1/32; + ''; + openbsdPub = { + extraConfig = '' + proxy_cache my_cache; + proxy_cache_revalidate on; + proxy_cache_min_uses 1; + proxy_cache_use_stale error timeout updating http_500 http_502 + http_503 http_504; + proxy_cache_background_update on; + proxy_cache_lock on; + + proxy_ignore_headers Cache-Control; + proxy_cache_valid any ${httpCacheTime}; + + # from jeremy + proxy_set_header Connection ""; + proxy_http_version 1.1; + + proxy_pass http://ftp.usa.openbsd.org; + ''; + }; + + pubKeys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIITjFpmWZVWixv2i9902R+g5B8umVhaqmjYEKs2nF3Lu qbit@tal.tapenet.org" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILnaC1v+VoVNnK04D32H+euiCyWPXU8nX6w+4UoFfjA3 qbit@plq" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFbj3DNho0T/SLcuKPzxT2/r8QNdEQ/ms6tRiX6YraJk root@tal.tapenet.org" + ]; + userBase = { openssh.authorizedKeys.keys = pubKeys; }; + mkNginxSecret = { + sopsFile = config.xin-secrets.box.certs; + owner = config.users.users.nginx.name; + mode = "400"; + }; + +in { + disabledModules = [ + #"services/security/step-ca.nix" + #"services/matrix/mjolnir.nix" + ]; + + _module.args.isUnstable = false; + imports = [ + ./hardware-configuration.nix + #(import "${ + # toString unstableSrc.path + # }/nixos/modules/services/security/step-ca.nix") + #(import + # "${toString unstableSrc.path}/nixos/modules/services/matrix/mjolnir.nix") + ]; + + sops.secrets = { + photoprism_admin_password = { sopsFile = config.xin-secrets.box.services; }; + gitea_db_pass = { + owner = config.users.users.gitea.name; + sopsFile = config.xin-secrets.box.services; + }; + }; + + sops.secrets.jelly_cert = mkNginxSecret; + sops.secrets.jelly_key = mkNginxSecret; + sops.secrets.reddit_cert = mkNginxSecret; + sops.secrets.reddit_key = mkNginxSecret; + sops.secrets.sonarr_cert = mkNginxSecret; + sops.secrets.sonarr_key = mkNginxSecret; + sops.secrets.radarr_cert = mkNginxSecret; + sops.secrets.radarr_key = mkNginxSecret; + sops.secrets.prowlarr_cert = mkNginxSecret; + sops.secrets.prowlarr_key = mkNginxSecret; + sops.secrets.nzb_cert = mkNginxSecret; + sops.secrets.nzb_key = mkNginxSecret; + sops.secrets.lidarr_cert = mkNginxSecret; + sops.secrets.lidarr_key = mkNginxSecret; + + #nixpkgs.config = { + # packageOverrides = super: + # let self = super.pkgs; + # in { + # step-ca = unstableSrc.step-ca; + # mjolnir = unstableSrc.mjolnir; + # }; + #}; + + boot.supportedFilesystems = [ "zfs" ]; + boot.loader.grub.copyKernels = true; + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + boot.kernelPackages = pkgs.linuxPackages; + + doas.enable = true; + + networking.hostName = "box"; + networking.hostId = "9a2d2563"; + + networking.useDHCP = false; + networking.enableIPv6 = false; + + networking = { + defaultGateway = "10.20.30.1"; + nameservers = [ "10.20.30.1" ]; + interfaces.enp7s0 = { + ipv4 = { + routes = [{ + address = "10.6.0.0"; + prefixLength = 24; + via = "10.6.0.1"; + }]; + addresses = [{ + address = "10.6.0.15"; + prefixLength = 24; + }]; + }; + }; + interfaces.enp8s0 = { + ipv4.addresses = [{ + address = "10.20.30.15"; + prefixLength = 24; + }]; + }; + }; + + nixpkgs.config.allowUnfree = true; + + environment.systemPackages = with pkgs; [ + nixfmt + tmux + mosh + apg + git + signify + glowing-bear + + (callPackage ../../pkgs/athens.nix { inherit isUnstable; }) + ]; + + security.acme = { + acceptTerms = true; + defaults.email = "aaron@bolddaemon.com"; + }; + + # for photoprism + users.groups.photoprism = { + name = "photoprism"; + gid = 986; + }; + users.users.photoprism = { + uid = 991; + name = "photoprism"; + isSystemUser = true; + hashedPassword = null; + group = "photoprism"; + shell = "/bin/sh"; + openssh.authorizedKeys.keys = pubKeys; + }; + + virtualisation.podman = { + enable = true; + #dockerCompat = true; + }; + virtualisation.oci-containers.backend = "podman"; + virtualisation.oci-containers.containers = { + kativa = { + autoStart = true; + ports = [ "127.0.0.1:5000:5000" ]; + image = "kizaing/kavita:0.5.2"; + volumes = [ "/media/books:/books" "/media/books/config:/kativa/config" ]; + }; + photoprism = { + #user = "${toString config.users.users.photoprism.name}:${toString config.users.groups.photoprism.name}"; + autoStart = true; + ports = [ "127.0.0.1:2343:2343" ]; + image = "photoprism/photoprism:${photoPrismTag}"; + workdir = "/photoprism"; + volumes = [ + "/media/pictures/photoprism/storage:/photoprism/storage" + "/media/pictures/photoprism/originals:/photoprism/originals" + "/media/pictures/photoprism/import:/photoprism/import" + ]; + environment = { + PHOTOPRISM_HTTP_PORT = "2343"; + PHOTOPRISM_UPLOAD_NSFW = "true"; + PHOTOPRISM_DETECT_NSFW = "false"; + PHOTOPRISM_UID = "${toString config.users.users.photoprism.uid}"; + PHOTOPRISM_GID = "${toString config.users.groups.photoprism.gid}"; + #PHOTOPRISM_SITE_URL = "https://photos.tapenet.org/"; + PHOTOPRISM_SITE_URL = "https://box.humpback-trout.ts.net/photos"; + PHOTOPRISM_SETTINGS_HIDDEN = "false"; + PHOTOPRISM_DATABASE_DRIVER = "sqlite"; + }; + }; + }; + + users.groups.media = { + name = "media"; + members = + [ "qbit" "sonarr" "radarr" "lidarr" "nzbget" "jellyfin" "headphones" ]; + }; + + services = { + cron = { + enable = true; + systemCronJobs = let + tsCertsScript = pkgs.writeScriptBin "ts-certs.sh" '' + #!/usr/bin/env sh + . /etc/profile; + ( + mkdir -p /etc/nixos/secrets; + chown root /etc/nixos/secrets/box.humpback-trout.ts.net.*; + tailscale cert \ + --cert-file /etc/nixos/secrets/box.humpback-trout.ts.net.crt \ + --key-file=/etc/nixos/secrets/box.humpback-trout.ts.net.key \ + box.humpback-trout.ts.net; + chown nginx /etc/nixos/secrets/box.humpback-trout.ts.net.* + ) >/dev/null 2>&1 + ''; + in [ "@daily root ${tsCertsScript}/bin/ts-certs.sh" ]; + }; + openssh.forwardX11 = true; + + tor.enable = true; + + #step-ca = { + # enable = true; + # intermediatePasswordFile = "/var/data/step-ca/secrets/password"; + # settings = { + # dnsNames = [ "box.bold.daemon" ]; + # root = "/var/lib/step-ca/certs/root_ca.crt"; + # crt = "/var/lib/step-ca/certs/intermediate_ca.crt"; + # key = "/var/lib/step-ca/secrets/intermediate_ca_key"; + # db = { + # type = "badger"; + # dataSource = "/var/lib/step-ca/db"; + # }; + # authority = { + # provisioners = [{ + # type = "ACME"; + # name = "acme"; + # }]; + # }; + # }; + # address = "127.0.0.1"; + # port = 8435; + #}; + + sonarr.enable = true; + radarr.enable = true; + lidarr.enable = true; + jackett.enable = true; + prowlarr.enable = true; + headphones.enable = false; + nzbget = { + enable = true; + group = "media"; + settings = { MainDir = "/media/downloads"; }; + }; + + fwupd.enable = true; + zfs = { + autoSnapshot.enable = true; + autoReplication = { + enable = true; + host = "10.6.0.245"; + identityFilePath = "/etc/ssh/ssh_host_ed25519_key"; + localFilesystem = "rpool"; + recursive = true; + remoteFilesystem = "tank/backups/box"; + username = "root"; + }; + }; + + jellyfin = { + enable = true; + openFirewall = true; + }; + + grafana = { + enable = true; + domain = "graph.tapenet.org"; + port = 2342; + addr = "127.0.0.1"; + }; + + prometheus = { + enable = true; + port = 9001; + + exporters = { + node = { + enable = true; + enabledCollectors = [ "systemd" ]; + port = 9002; + }; + + nginx = { enable = true; }; + }; + + scrapeConfigs = [ + { + job_name = "box"; + static_configs = [{ + targets = [ + "127.0.0.1:${ + toString config.services.prometheus.exporters.node.port + }" + ]; + }]; + } + { + job_name = "greenhouse"; + static_configs = [{ targets = [ "10.6.0.20:80" ]; }]; + } + { + job_name = "house"; + static_configs = [{ targets = [ "10.6.0.21:80" ]; }]; + } + { + job_name = "outside"; + static_configs = [{ targets = [ "10.6.0.22:8811" ]; }]; + } + { + job_name = "tal"; + static_configs = [{ targets = [ "10.6.0.110:9100" ]; }]; + } + { + job_name = "namish"; + static_configs = [{ targets = [ "10.6.0.2:9100" ]; }]; + } + { + job_name = "nginx"; + static_configs = [{ + targets = [ + "127.0.0.1:${ + toString config.services.prometheus.exporters.nginx.port + }" + ]; + }]; + } + + ]; + }; + + vaultwarden = { + enable = true; + backupDir = "/backups/bitwarden_rs"; + config = { + domain = "https://bw.tapenet.org"; + signupsAllowed = false; + rocketPort = 8222; + rocketLog = "critical"; + environmentFile = "/root/bitwarden_rs.env"; + }; + }; + + gitea = { + enable = true; + domain = "git.tapenet.org"; + rootUrl = "https://git.tapenet.org"; + stateDir = "/media/git"; + appName = "Tape:neT"; + + lfs.enable = true; + ssh.enable = true; + ssh.clonePort = 2222; + + settings = { + server = { + START_SSH_SERVER = true; + SSH_SERVER_HOST_KEYS = "ssh/gitea-ed25519"; + }; + }; + + disableRegistration = true; + + cookieSecure = true; + + database = { + type = "postgres"; + passwordFile = "${config.sops.secrets.gitea_db_pass.path}"; + socket = "/run/postgresql"; + }; + }; + + #nextcloud = { + # enable = true; + # hostName = "box.tapenet.org"; + # package = pkgs.nextcloud22; + # home = "/media/nextcloud"; + # https = true; + # autoUpdateApps = { enable = true; }; + + # config = { + # overwriteProtocol = "https"; + + # dbtype = "pgsql"; + # dbuser = "nextcloud"; + # dbhost = "/run/postgresql"; + # dbname = "nextcloud"; + # dbpassFile = "${config.sops.secrets.nextcloud_db_pass.path}"; + + # adminpassFile = "${config.sops.secrets.nextcloud_admin_pass.path}"; + # adminuser = "admin"; + # }; + #}; + + rsnapshot = { + enable = false; + enableManualRsnapshot = true; + extraConfig = '' + snapshot_root /backups/snapshots/ + retain daily 7 + retain manual 3 + backup_exec date "+ backup of suah.dev started at %c" + backup root@suah.dev:/home/ suah.dev/ + backup root@suah.dev:/etc/ suah.dev/ + backup root@suah.dev:/var/synapse/ suah.dev/ + backup root@suah.dev:/var/dendrite/ suah.dev/ + backup root@suah.dev:/var/hammer/ suah.dev/ + backup root@suah.dev:/var/go-ipfs/ suah.dev/ + backup root@suah.dev:/var/gopher/ suah.dev/ + backup root@suah.dev:/var/honk/ suah.dev/ + backup root@suah.dev:/var/mcchunkie/ suah.dev/ + backup root@suah.dev:/var/www/ suah.dev/ + backup_exec date "+ backup of suah.dev ended at %c" + ''; + cronIntervals = { daily = "50 21 * * *"; }; + }; + + libreddit = { + enable = true; + port = 8482; + redirect = true; + }; + + nginx = { + enable = true; + package = pkgs.openresty; + + statusPage = true; + + recommendedTlsSettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; + recommendedProxySettings = true; + + clientMaxBodySize = "512M"; + + commonHttpConfig = '' + proxy_cache_path /backups/nginx_cache levels=1:2 keys_zone=my_cache:10m max_size=10g + inactive=${httpCacheTime} use_temp_path=off; + ''; + + virtualHosts = { + "box.humpback-trout.ts.net" = { + forceSSL = true; + sslCertificateKey = + "/etc/nixos/secrets/box.humpback-trout.ts.net.key"; + sslCertificate = "/etc/nixos/secrets/box.humpback-trout.ts.net.crt"; + + locations."/photos" = { + proxyPass = "http://localhost:2343"; + proxyWebsockets = true; + }; + + locations."/pub" = openbsdPub; + }; + + "photos.tapenet.org" = { + forceSSL = true; + enableACME = true; + + locations."/" = { + proxyPass = "http://localhost:2343"; + proxyWebsockets = true; + }; + }; + "bw.tapenet.org" = { + forceSSL = true; + enableACME = true; + + locations."/" = { + proxyPass = "http://localhost:${ + toString config.services.vaultwarden.config.rocketPort + }"; + proxyWebsockets = true; + }; + + # For push notifications. Unfortunately the ports are not set in a config + locations."/notifications/hub" = { + proxyPass = "http://localhost:3012"; + proxyWebsockets = true; + }; + locations."/notifications/hub/negotiate" = { + proxyPass = "http://localhost:8812"; + proxyWebsockets = true; + }; + }; + + "bear.tapenet.org" = { + forceSSL = true; + enableACME = true; + + locations."/" = { root = "${pkgs.glowing-bear}"; }; + }; + + "jelly.bold.daemon" = { + forceSSL = true; + sslCertificateKey = "${config.sops.secrets.jelly_key.path}"; + sslCertificate = "${config.sops.secrets.jelly_cert.path}"; + + locations."/" = { + # TODO: jellyfin.nix doesn't expose the port being used. + proxyPass = "http://localhost:8096"; + proxyWebsockets = true; + extraConfig = '' + ${httpAllow} + deny all; + ''; + }; + }; + + "reddit.bold.daemon" = { + sslCertificateKey = "${config.sops.secrets.reddit_key.path}"; + sslCertificate = "${config.sops.secrets.reddit_cert.path}"; + forceSSL = true; + locations."/" = { + proxyPass = + "http://localhost:${toString config.services.libreddit.port}"; + proxyWebsockets = true; + extraConfig = '' + ${httpAllow} + deny all; + ''; + }; + }; + + "sonarr.bold.daemon" = { + sslCertificateKey = "${config.sops.secrets.sonarr_key.path}"; + sslCertificate = "${config.sops.secrets.sonarr_cert.path}"; + forceSSL = true; + locations."/" = { + proxyPass = "http://localhost:8989"; + proxyWebsockets = true; + extraConfig = '' + ${httpAllow} + deny all; + ''; + }; + }; + "radarr.bold.daemon" = { + sslCertificateKey = "${config.sops.secrets.radarr_key.path}"; + sslCertificate = "${config.sops.secrets.radarr_cert.path}"; + forceSSL = true; + locations."/" = { + proxyPass = "http://localhost:7878"; + proxyWebsockets = true; + extraConfig = '' + ${httpAllow} + deny all; + ''; + }; + }; + "prowlarr.bold.daemon" = { + sslCertificateKey = "${config.sops.secrets.prowlarr_key.path}"; + sslCertificate = "${config.sops.secrets.prowlarr_cert.path}"; + forceSSL = true; + locations."/" = { + proxyPass = "http://localhost:9696"; + proxyWebsockets = true; + extraConfig = '' + ${httpAllow} + deny all; + ''; + }; + }; + "nzb.bold.daemon" = { + sslCertificateKey = "${config.sops.secrets.nzb_key.path}"; + sslCertificate = "${config.sops.secrets.nzb_cert.path}"; + forceSSL = true; + locations."/" = { + proxyPass = "http://localhost:6789"; + proxyWebsockets = true; + extraConfig = '' + ${httpAllow} + deny all; + ''; + }; + }; + "headphones.bold.daemon" = { + locations."/" = { + proxyPass = "http://localhost:8181"; + proxyWebsockets = true; + extraConfig = '' + ${httpAllow} + deny all; + ''; + }; + }; + "lidarr.bold.daemon" = { + sslCertificateKey = "${config.sops.secrets.lidarr_key.path}"; + sslCertificate = "${config.sops.secrets.lidarr_cert.path}"; + forceSSL = true; + locations."/" = { + proxyPass = "http://localhost:8686"; + proxyWebsockets = true; + extraConfig = '' + ${httpAllow} + deny all; + ''; + }; + }; + + ${config.services.grafana.domain} = { + forceSSL = true; + enableACME = true; + + locations."/" = { + proxyPass = + "http://127.0.0.1:${toString config.services.grafana.port}"; + proxyWebsockets = true; + extraConfig = '' + ${httpAllow} + deny all; + ''; + }; + + locations."/_pub" = { + extraConfig = '' + default_type 'application/json'; + + content_by_lua_block { + function lsplit (str, sep) + sep = "\n" + local t={} + for str in string.gmatch(str, "([^"..sep.."]+)") do + table.insert(t, str) + end + return t + end + + local sock = ngx.socket.tcp() + local ok, err = sock:connect("127.0.0.1", ${ + toString config.services.prometheus.port + }) + if not ok then + ngx.say("failed to connect to backend: ", err) + return + end + + local bytes = sock:send("GET /api/v1/query?query=wstation_temp_c HTTP/1.1\nHost: 127.0.0.1:${ + toString config.services.prometheus.port + }\n\n") + + sock:settimeouts(1000, 1000, 1000) + + local data, err = sock:receiveany(10 * 1024) + if not data then + ngx.say("failed to read weather data: ", err) + return + end + + local b = lsplit(data) + ngx.say(b[#b]) + + sock:close() + } + ''; + }; + }; + + "git.tapenet.org" = { + forceSSL = true; + enableACME = true; + + locations."/" = { + proxyPass = + "http://localhost:${toString config.services.gitea.httpPort}"; + proxyWebsockets = true; + priority = 1000; + }; + }; + }; + }; + + postgresqlBackup = { + enable = true; + location = "/backups/postgresql"; + }; + postgresql = { + enable = true; + dataDir = "/db/postgres"; + + ensureDatabases = [ "nextcloud" "gitea" ]; + ensureUsers = [ + { + name = "nextcloud"; + ensurePermissions."DATABASE nextcloud" = "ALL PRIVILEGES"; + } + { + name = "gitea"; + ensurePermissions."DATABASE gitea" = "ALL PRIVILEGES"; + } + ]; + }; + + }; + + systemd.services.nginx.serviceConfig = { + ReadWritePaths = [ "/backups/nginx_cache" ]; + ReadOnlyPaths = [ "/etc/nixos/secrets" ]; + }; + + #systemd.services."nextcloud-setup" = { + # requires = [ "postgresql.service" ]; + # after = [ "postgresql.service" ]; + #}; + + networking.firewall.allowedTCPPorts = config.services.openssh.ports + ++ [ 80 443 config.services.gitea.ssh.clonePort ]; + networking.firewall.allowedUDPPortRanges = [{ + from = 60000; + to = 61000; + }]; + + users.users.qbit = userBase; + users.users.root = userBase; + + programs.zsh.enable = true; + + system.stateVersion = "20.03"; +} + diff --git a/hosts/box/hardware-configuration.nix b/hosts/box/hardware-configuration.nix new file mode 100644 index 0000000..56a79a8 --- /dev/null +++ b/hosts/box/hardware-configuration.nix @@ -0,0 +1,76 @@ +{ config, lib, pkgs, ... }: + +{ + boot.initrd.availableKernelModules = + [ "ehci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + hardware.enableRedistributableFirmware = true; + + fileSystems."/" = { + device = "/dev/disk/by-uuid/248dfcf7-999b-4dba-bfbf-0b10dbb376b1"; + fsType = "ext4"; + }; + + fileSystems."/home" = { + device = "rpool/home"; + fsType = "zfs"; + }; + + fileSystems."/backups" = { + device = "rpool/backups"; + fsType = "zfs"; + }; + + fileSystems."/media/music" = { + device = "rpool/media/music"; + fsType = "zfs"; + }; + + fileSystems."/media/movies" = { + device = "rpool/media/movies"; + fsType = "zfs"; + }; + + fileSystems."/media/pictures" = { + device = "rpool/pictures"; + fsType = "zfs"; + }; + + fileSystems."/media/tv" = { + device = "rpool/media/tv"; + fsType = "zfs"; + }; + + fileSystems."/media/nextcloud" = { + device = "rpool/nextcloud"; + fsType = "zfs"; + }; + + fileSystems."/media/git" = { + device = "rpool/git"; + fsType = "zfs"; + }; + + fileSystems."/media/downloads" = { + device = "rpool/downloads"; + fsType = "zfs"; + }; + + fileSystems."/db/postgres" = { + device = "rpool/db/postgres"; + fsType = "zfs"; + }; + + fileSystems."/boot" = { + device = "/dev/disk/by-uuid/2AC3-DB6C"; + fsType = "vfat"; + }; + + swapDevices = + [{ device = "/dev/disk/by-uuid/97d6ef56-ea18-493b-aac0-e58e773ced30"; }]; + + nix.maxJobs = lib.mkDefault 8; +} diff --git a/hosts/europa/default.nix b/hosts/europa/default.nix new file mode 100644 index 0000000..1243f13 --- /dev/null +++ b/hosts/europa/default.nix @@ -0,0 +1,202 @@ +{ config, pkgs, lib, modulesPath, ... }: +let + myEmacs = pkgs.callPackage ../../configs/emacs.nix { }; +in { + _module.args.isUnstable = true; + + imports = [ + ./hardware-configuration.nix + ../../pkgs + ../../configs/neomutt.nix + ../../overlays/default.nix + ]; + + sops.secrets = { + fastmail = { + sopsFile = config.xin-secrets.europa.qbit; + owner = "qbit"; + group = "wheel"; + mode = "400"; + }; + fastmail_user = { + sopsFile = config.xin-secrets.europa.qbit; + owner = "qbit"; + group = "wheel"; + mode = "400"; + }; + nix_review = { + sopsFile = config.xin-secrets.europa.qbit; + owner = "qbit"; + group = "wheel"; + mode = "400"; + }; + }; + + boot.binfmt.emulatedSystems = [ "aarch64-linux" ]; + nixpkgs.config.allowUnsupportedSystem = true; + + boot = { + initrd.availableKernelModules = + [ "xhci_pci" "thunderbolt" "nvme" "usb_storage" "usbhid" "sd_mod" ]; + initrd.kernelModules = [ ]; + extraModulePackages = [ ]; + loader = { + systemd-boot.enable = true; + efi.canTouchEfiVariables = true; + }; + kernelPackages = pkgs.linuxPackages; + kernelParams = [ "boot.shell_on_fail" "nvme.noacpi=1" ]; + kernelModules = [ "kvm-intel" ]; + extraModprobeConfig = '' + options snd-hda-intel model=dell-headset-multi + ''; + }; + + programs.zsh.shellAliases = { + "nix-review" = "GITHUB_TOKEN=$(cat /run/secrets/nix_review) nix-review"; + "neomutt" = "neomutt -F /etc/neomuttrc"; + "mutt" = "neomutt -F /etc/neomuttrc"; + }; + + sshFidoAgent.enable = true; + configManager = { + enable = true; + router = { + enable = true; + + hostName = "10.6.0.1"; + pfAllowUnifi = false; + + interfaces = { + em0 = { + text = '' + inet autoconf + inet6 autoconf + ''; + }; + em1 = { + text = '' + inet 10.99.99.1 255.255.255.0 10.99.99.255 + description "Trunk" + up + ''; + }; + vlan2 = { + text = '' + inet 10.3.0.1 255.255.255.0 10.3.0.255 vnetid 2 parent em1 description "Lab" up''; + }; + vlan10 = { + text = '' + inet 10.10.0.1 255.255.255.0 10.10.0.255 vnetid 10 parent em1 description "Untrusted WiFi" up''; + }; + vlan11 = { + text = '' + inet 10.12.0.1 255.255.255.0 10.12.0.255 vnetid 11 parent em1 description "Trusted WiFi" up''; + }; + }; + }; + }; + + nixManager = { + enable = true; + user = "qbit"; + }; + + kde.enable = true; + jetbrains.enable = true; + + virtualisation.libvirtd.enable = true; + programs.dconf.enable = true; + + networking.hosts."100.120.151.126" = [ "graph.tapenet.org" ]; + networking = { + hostName = "europa"; + hostId = "87703c3e"; + wireless.userControlled.enable = true; + networkmanager.enable = true; + + firewall = { + enable = true; + allowedTCPPorts = [ 22 ]; + checkReversePath = "loose"; + }; + }; + + programs.steam.enable = true; + services = { + emacs = { + enable = false; + package = myEmacs; + install = true; + }; + tor = { + enable = true; + client.enable = true; + }; + #blueman.enable = true; + cron = { + enable = true; + systemCronJobs = [ + "*/2 * * * * qbit . /etc/profile; (cd ~/Notes && git sync) >/dev/null 2>&1" + "*/5 * * * * qbit . /etc/profile; (cd ~/org && git sync) >/dev/null 2>&1" + ]; + }; + fprintd.enable = true; + #logind = { + # lidSwitch = "suspend-then-hibernate"; + # lidSwitchExternalPower = "lock"; + #}; + tlp = { + enable = false; + settings = { + CPU_BOOST_ON_BAT = 0; + CPU_SCALING_GOVERNOR_ON_BATTERY = "powersave"; + START_CHARGE_THRESH_BAT0 = 90; + STOP_CHARGE_THRESH_BAT0 = 97; + RUNTIME_PM_ON_BAT = "auto"; + }; + }; + fwupd = { + enable = true; + enableTestRemote = true; + }; + + udev.extraRules = '' + SUBSYSTEM=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="5bf0", GROUP="users", TAG+="uaccess" + SUBSYSTEM=="pci", ATTR{vendor}=="0x8086", ATTR{device}=="0xa0e0", ATTR{power/control}="on" + ''; + }; + + users.users.qbit.extraGroups = [ "libvirtd" ]; + + nixpkgs.config.allowUnfree = true; + + environment.systemPackages = with pkgs; [ + arcanPackages.all-wrapped + barrier + cider + drawterm + element-desktop + exercism + isync + klavaro + libfprint-2-tod1-goodix + linphone + logseq + mu + nheko + nix-index + nix-review + nix-top + rofi + signal-desktop + thunderbird + tidal-hifi + tigervnc + virt-manager + yt-dlp + ]; + + system.stateVersion = "21.11"; +} + diff --git a/hosts/europa/hardware-configuration.nix b/hosts/europa/hardware-configuration.nix new file mode 100644 index 0000000..7db2b69 --- /dev/null +++ b/hosts/europa/hardware-configuration.nix @@ -0,0 +1,71 @@ +# 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 + "/installer/scan/not-detected.nix") ]; + + boot.initrd.availableKernelModules = + [ "xhci_pci" "thunderbolt" "nvme" "usb_storage" "usbhid" "uas" "sd_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-intel" ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = { + device = "rpool/nixos"; + fsType = "zfs"; + }; + + fileSystems."/nix" = { + device = "rpool/nixos/nix"; + fsType = "zfs"; + }; + + fileSystems."/etc" = { + device = "rpool/nixos/etc"; + fsType = "zfs"; + }; + + fileSystems."/var" = { + device = "rpool/nixos/var"; + fsType = "zfs"; + }; + + fileSystems."/var/lib" = { + device = "rpool/nixos/var/lib"; + fsType = "zfs"; + }; + + fileSystems."/var/log" = { + device = "rpool/nixos/var/log"; + fsType = "zfs"; + }; + + fileSystems."/var/spool" = { + device = "rpool/nixos/var/spool"; + fsType = "zfs"; + }; + + fileSystems."/home" = { + device = "rpool/nixos/home"; + fsType = "zfs"; + }; + + fileSystems."/boot" = { + device = "/dev/disk/by-uuid/5250-11DE"; + fsType = "vfat"; + }; + + swapDevices = [ ]; + #swapDevices = + # [{ device = "/dev/disk/by-uuid/6e56876e-bd04-4fbb-9ff7-5202cbf5eaa4"; }]; + + powerManagement.cpuFreqGovernor = lib.mkDefault "powersave"; + hardware = { + cpu.intel.updateMicrocode = config.hardware.enableRedistributableFirmware; + acpilight.enable = true; + video.hidpi.enable = true; + bluetooth.enable = true; + }; +} diff --git a/hosts/faf/default.nix b/hosts/faf/default.nix new file mode 100644 index 0000000..6b977c2 --- /dev/null +++ b/hosts/faf/default.nix @@ -0,0 +1,112 @@ +{ config, pkgs, ... }: +let + pubKeys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFbj3DNho0T/SLcuKPzxT2/r8QNdEQ/ms6tRiX6YraJk root@tal.tapenet.org" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIPMaAm4rDxyU975Z54YiNw3itC2fGc3SaE2VaS1fai8 root@box" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIITjFpmWZVWixv2i9902R+g5B8umVhaqmjYEKs2nF3Lu qbit@tal.tapenet.org" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILnaC1v+VoVNnK04D32H+euiCyWPXU8nX6w+4UoFfjA3 qbit@plq" + ]; + userBase = { openssh.authorizedKeys.keys = pubKeys; }; +in { + _module.args.isUnstable = false; + imports = [ ./hardware-configuration.nix ]; + + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + boot.supportedFilesystems = [ "zfs" ]; + boot.zfs.devNodes = "/dev/"; + + networking.hostName = "faf"; + networking.hostId = "12963a2a"; + + networking.useDHCP = false; + networking.interfaces.enp1s0.useDHCP = true; + networking.interfaces.enp2s0.useDHCP = true; + + networking.firewall.allowedTCPPorts = [ 22 53 ]; + networking.firewall.allowedUDPPorts = [ 53 ]; + + users.users.root = userBase; + users.users.qbit = userBase; + + services = { + adguardhome = { + enable = false; + port = 3000; + openFirewall = true; + settings = { + user_rules = [ + "# Stuff from kyle" + "# some google stuff that wasn't being blocked" + "||googleadservices.com^" + "||imasdk.googleapis.com^" + "# some advertising stuff I saw on my network" + "||adjust.com^" + "||appsflyer.com^" + "||doubleclick.net^" + "||googleadservices.com^" + "||raygun.io^" + "||pizzaseo.com^" + "||scorecardresearch.com^" + "# annoying website 'features'" + "||drift.com^" + "||driftcdn.com^" + "||driftt.com^" + "||driftt.imgix.net^" + "||intercomcdn.com^" + "||intercom.io^" + "||salesforceliveagent.com^" + "||viafoura.co^" + "||viafoura.com^" + ]; + filters = [ + { + name = "AdGuard DNS filter"; + url = + "https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt"; + enabled = true; + } + { + name = "AdaAway Default Blocklist"; + url = "https://adaway.org/hosts.txt"; + enabled = true; + } + { + name = "OISD"; + url = "https://abp.oisd.nl"; + enabled = true; + } + ]; + dns = { + statistics_interval = 90; + bind_host = "10.6.0.245"; + bootstrap_dns = "10.6.0.1"; + }; + }; + }; + unbound = { + enable = true; + settings = { + server = { + interface = [ "100.64.130.122" ]; + access-control = [ "100.64.0.0/10 allow" ]; + }; + local-zone = ''"bold.daemon." static''; + local-data = [ + ''"reddit.bold.daemon. IN A 100.120.151.126"'' + ''"jelly.bold.daemon. IN A 100.120.151.126"'' + ''"sonarr.bold.daemon. IN A 100.120.151.126"'' + ''"radarr.bold.daemon. IN A 100.120.151.126"'' + ''"prowlarr.bold.daemon. IN A 100.120.151.126"'' + ''"headphones.bold.daemon. IN A 100.120.151.126"'' + ''"lidarr.bold.daemon. IN A 100.120.151.126"'' + ''"nzb.bold.daemon. IN A 100.120.151.126"'' + ]; + }; + }; + }; + + system.stateVersion = "21.11"; # Did you read the comment? +} + diff --git a/hosts/faf/hardware-configuration.nix b/hosts/faf/hardware-configuration.nix new file mode 100644 index 0000000..aacfcce --- /dev/null +++ b/hosts/faf/hardware-configuration.nix @@ -0,0 +1,72 @@ +# 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 + "/installer/scan/not-detected.nix") ]; + + boot.initrd.availableKernelModules = [ + "uhci_hcd" + "ehci_pci" + "ahci" + "xhci_pci" + "sata_sil24" + "usb_storage" + "usbhid" + "sd_mod" + ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = { + device = "tank/nixos"; + fsType = "zfs"; + }; + + fileSystems."/nix" = { + device = "tank/nixos/nix"; + fsType = "zfs"; + }; + + fileSystems."/etc" = { + device = "tank/nixos/etc"; + fsType = "zfs"; + }; + + fileSystems."/var" = { + device = "tank/nixos/var"; + fsType = "zfs"; + }; + + fileSystems."/var/lib" = { + device = "tank/nixos/var/lib"; + fsType = "zfs"; + }; + + fileSystems."/var/log" = { + device = "tank/nixos/var/log"; + fsType = "zfs"; + }; + + fileSystems."/var/spool" = { + device = "tank/nixos/var/spool"; + fsType = "zfs"; + }; + + fileSystems."/home" = { + device = "tank/userdata/home"; + fsType = "zfs"; + }; + + fileSystems."/boot" = { + device = "/dev/disk/by-uuid/5851-DEF2"; + fsType = "vfat"; + }; + + swapDevices = [ ]; + + hardware.cpu.intel.updateMicrocode = + lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/hosts/h/alias b/hosts/h/alias new file mode 100644 index 0000000..2a9853c --- /dev/null +++ b/hosts/h/alias @@ -0,0 +1 @@ +h.suah.dev diff --git a/hosts/h/default.nix b/hosts/h/default.nix new file mode 100644 index 0000000..66ef440 --- /dev/null +++ b/hosts/h/default.nix @@ -0,0 +1,435 @@ +{ config, pkgs, lib, isUnstable, ... }: +with pkgs; +let + gqrss = callPackage ../../pkgs/gqrss.nix { inherit isUnstable; }; + icbirc = callPackage ../../pkgs/icbirc.nix { inherit isUnstable; }; + mcchunkie = callPackage ../../pkgs/mcchunkie.nix { inherit isUnstable; }; + pgBackupDir = "/var/backups/postgresql"; + pubKeys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIITjFpmWZVWixv2i9902R+g5B8umVhaqmjYEKs2nF3Lu qbit@tal.tapenet.org" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILnaC1v+VoVNnK04D32H+euiCyWPXU8nX6w+4UoFfjA3 qbit@plq" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO7v+/xS8832iMqJHCWsxUZ8zYoMWoZhjj++e26g1fLT europa" + ]; + userBase = { openssh.authorizedKeys.keys = pubKeys; }; + +in { + _module.args.isUnstable = false; + imports = [ ./hardware-configuration.nix ]; + + boot.loader.grub.enable = true; + boot.loader.grub.version = 2; + boot.loader.grub.device = "/dev/sda"; + + boot.kernelParams = [ "net.ifnames=0" ]; + + tailscale.enable = false; + + sops.secrets = { + synapse_signing_key = { + owner = config.users.users.matrix-synapse.name; + mode = "600"; + sopsFile = config.xin-secrets.h.services; + }; + hammer_access_token = { + owner = config.users.users.mjolnir.name; + mode = "600"; + sopsFile = config.xin-secrets.h.services; + }; + gqrss_token = { + owner = config.users.users.qbit.name; + mode = "400"; + sopsFile = config.xin-secrets.h.services; + }; + restic_env_file = { + owner = config.users.users.root.name; + mode = "400"; + sopsFile = config.xin-secrets.h.services; + }; + restic_password_file = { + owner = config.users.users.root.name; + mode = "400"; + sopsFile = config.xin-secrets.h.services; + }; + }; + + networking = { + hostName = "h"; + enableIPv6 = true; + useDHCP = false; + defaultGateway = "23.29.118.1"; + defaultGateway6 = "2602:ff16:3::1"; + nameservers = [ "9.9.9.9" ]; + interfaces.eth0 = { + ipv4.addresses = [{ + address = "23.29.118.127"; + prefixLength = 24; + }]; + ipv6 = { + addresses = [{ + address = "2602:ff16:3:0:1:3a0:0:1"; + prefixLength = 64; + }]; + }; + }; + firewall = { + allowedTCPPorts = [ 22 80 443 53 ]; + allowedUDPPorts = [ 53 ]; + allowedUDPPortRanges = [{ + from = 60000; + to = 61000; + }]; + }; + }; + + environment.systemPackages = with pkgs; [ + inetutils + + # irc + weechat + weechatScripts.highmon + aspell + icbirc + + # matrix things + matrix-synapse-tools.synadm + matrix-synapse-tools.rust-synapse-compress-state + mcchunkie + + restic + ]; + + security.acme = { + acceptTerms = true; + defaults.email = "aaron@bolddaemon.com"; + }; + + users.groups.mcchunkie = { }; + + users.users.mcchunkie = { + createHome = true; + isSystemUser = true; + home = "/var/lib/mcchunkie"; + group = "mcchunkie"; + }; + + systemd.services.mcchunkie = { + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = "mcchunkie"; + Group = "mcchunkie"; + Restart = "always"; + WorkingDirectory = "/var/lib/mcchunkie"; + RuntimeDirectory = "/var/lib/mcchunkie"; + ExecStart = "${mcchunkie}/bin/mcchunkie"; + }; + }; + + services = { + cron = { + enable = true; + systemCronJobs = [ + '' + @hourly qbit (export GH_AUTH_TOKEN=$(cat /run/secrets/gqrss_token); cd /var/www/suah.dev/rss; ${gqrss}/bin/gqrss ; ${gqrss}/bin/gqrss -search "LibreSSL" -prefix libressl_ ) >/dev/null 2>&1'' + ]; + }; + + restic = { + backups = { + b2 = { + initialize = true; + repository = "b2:cyaspanJicyeemJedMarlEjcasOmos"; + environmentFile = "${config.sops.secrets.restic_env_file.path}"; + passwordFile = "${config.sops.secrets.restic_password_file.path}"; + + paths = [ pgBackupDir "/var/lib/synapse/media_store" "/var/www" ]; + + timerConfig = { OnCalendar = "00:05"; }; + + pruneOpts = [ "--keep-daily 7" "--keep-weekly 5" "--keep-yearly 10" ]; + }; + }; + }; + + nginx = { + enable = true; + + recommendedTlsSettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; + recommendedProxySettings = true; + + clientMaxBodySize = "50M"; + + commonHttpConfig = '' + # Add HSTS header with preloading to HTTPS requests. + # Adding this header to HTTP requests is discouraged + map $scheme $hsts_header { + https "max-age=31536000; includeSubdomains; preload"; + } + add_header Strict-Transport-Security $hsts_header; + + # Enable CSP for your services. + #add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always; + + # Minimize information leaked to other domains + add_header 'Referrer-Policy' 'origin-when-cross-origin'; + + # Disable embedding as a frame + add_header X-Frame-Options DENY; + + # Prevent injection of code in other mime types (XSS Attacks) + add_header X-Content-Type-Options nosniff; + + # This might create errors + proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict"; + ''; + + virtualHosts = { + "deftly.net" = { + forceSSL = true; + enableACME = true; + root = "/var/www/deftly.net"; + extraConfig = '' + location ~ ^/pub|^/patches|^/dist|^/pbp|^/screenshots|^/pharo|^/fw { + autoindex on; + index index.php index.html index.htm; + } + ''; + }; + "bolddaemon.com" = { + forceSSL = true; + enableACME = true; + root = "/var/www/bolddaemon.com"; + }; + "relay.bolddaemon.com" = { + forceSSL = true; + enableACME = true; + root = "/var/www/bolddaemon.com"; + locations."/weechat" = { + proxyWebsockets = true; + proxyPass = "http://localhost:9009/weechat"; + }; + }; + "suah.dev" = { + forceSSL = true; + enableACME = true; + root = "/var/www/suah.dev"; + extraConfig = '' + location ~ ^/_got { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host:$server_port; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Ssl on; + proxy_read_timeout 300; + proxy_connect_timeout 300; + proxy_pass http://127.0.0.1:8043; + } + + location ~ ^/_sms { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host:$server_port; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Ssl on; + proxy_read_timeout 300; + proxy_connect_timeout 300; + proxy_pass http://127.0.0.1:8044; + } + location ~ ^/p/ { + autoindex on; + } + + location ~ ^/recipes/ { + autoindex on; + } + + location ~* .(xml)$ { + autoindex on; + root /var/www/suah.dev/rss; + } + + location ~ "([^/\s]+)(/.*)?" { + set $not_serving 1; + + if ($request_filename = 'index.html') { + set $not_serving 0; + } + + if (-f $request_filename) { + set $not_serving 0; + } + + if ($args = "go-get=1") { + add_header Strict-Transport-Security $hsts_header; + add_header Referrer-Policy origin-when-cross-origin; + add_header X-Frame-Options DENY; + add_header X-Content-Type-Options nosniff; + add_header Content-Type text/html; + return 200 ' + + + + + + Redirecting to docs at pkg.go.dev/mod/suah.dev/$1... + + '; + } + if ($not_serving) { + add_header Strict-Transport-Security $hsts_header; + add_header Referrer-Policy origin-when-cross-origin; + add_header X-Frame-Options DENY; + add_header X-Content-Type-Options nosniff; + add_header Content-Type text/html; + return 200 ' + + + + + + Redirecting to docs at pkg.go.dev/mod/suah.dev/$1... + + '; + } + } + ''; + }; + "qbit.io" = { + forceSSL = true; + enableACME = true; + root = "/var/www/qbit.io"; + }; + "mammothcircus.com" = { + forceSSL = true; + enableACME = true; + root = "/var/www/mammothcircus.com"; + }; + "akb.io" = { + forceSSL = true; + enableACME = true; + root = "/var/www/akb.io"; + }; + "tapenet.org" = { + forceSSL = true; + enableACME = true; + root = "/var/www/tapenet.org"; + locations."/_matrix" = { + proxyWebsockets = true; + proxyPass = "http://127.0.0.1:8009"; + }; + locations."/_synapse/client" = { + proxyWebsockets = true; + proxyPass = "http://127.0.0.1:8009"; + }; + }; + }; + }; + + postgresqlBackup = { + enable = true; + location = pgBackupDir; + }; + + postgresql = { + enable = true; + package = pkgs.postgresql_14; + + settings = { }; + + enableTCPIP = true; + authentication = pkgs.lib.mkOverride 14 '' + local all all trust + host all all 127.0.0.1/32 trust + host all all ::1/128 trust + ''; + + initialScript = pkgs.writeText "synapse-init.sql" '' + CREATE ROLE "synapse-user" LOGIN; + CREATE DATABASE "synapse" WITH OWNER "synapse-user" + TEMPLATE template0 + LC_COLLATE = "C" + LC_CTYPE = "C"; + ''; + ensureDatabases = [ "synapse" ]; + ensureUsers = [{ + name = "synapse_user"; + ensurePermissions."DATABASE synapse" = "ALL PRIVILEGES"; + }]; + }; + + mjolnir = { + enable = true; + pantalaimon.enable = false; + pantalaimon.username = "hammer"; + accessTokenFile = "${config.sops.secrets.hammer_access_token.path}"; + homeserverUrl = "https://tapenet.org"; + protectedRooms = [ + "https://matrix.to/#/#openbsd:matrix.org" + "https://matrix.to/#/#go-lang:matrix.org" + "https://matrix.to/#/#plan9:matrix.org" + "https://matrix.to/#/#nix-openbsd:tapenet.org" + ]; + settings = { + verboseLogging = false; + protections = { + wordlist = { + words = [ + "^https://libera.chat <-- visit!$" + "^@.*@.*@.*@.*@.*@.*@.*@.*@.*@.*" + ]; + }; + }; + managementRoom = "#moderation:tapenet.org"; + automaticallyRedactForReasons = [ "spam" "racism" "advertising" ]; + automaticallyReactForReasons = + [ "spam" "advertising" "trolling" "racism" ]; + aditionalPrefixes = [ "hammer" ]; + confirmWildcardBan = false; + }; + }; + + matrix-synapse = { + enable = true; + dataDir = "/var/lib/synapse"; + settings = { + enable_registration = false; + media_store_path = "/var/lib/synapse/media_store"; + presence.enabled = false; + public_baseurl = "https://tapenet.org"; + server_name = "tapenet.org"; + signing_key_path = "${config.sops.secrets.synapse_signing_key.path}"; + url_preview_enabled = false; + plugins = with config.services.matrix-synapse.package.plugins; + [ matrix-synapse-mjolnir-antispam ]; + database = { + name = "psycopg2"; + args = { + database = "synapse"; + user = "synapse_user"; + }; + }; + listeners = [{ + bind_addresses = [ "127.0.0.1" ]; + port = 8009; + resources = [ + { + compress = true; + names = [ "client" ]; + } + { + compress = false; + names = [ "federation" ]; + } + ]; + tls = false; + type = "http"; + x_forwarded = true; + }]; + }; + }; + }; + + users.users.qbit = userBase; + + system.stateVersion = "22.11"; +} + diff --git a/hosts/h/hardware-configuration.nix b/hosts/h/hardware-configuration.nix new file mode 100644 index 0000000..c3f1def --- /dev/null +++ b/hosts/h/hardware-configuration.nix @@ -0,0 +1,25 @@ +# 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 = + [ "ahci" "xhci_pci" "virtio_pci" "sd_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ ]; + boot.extraModulePackages = [ ]; + + fileSystems."/" = { + device = "/dev/disk/by-uuid/b3caa6ff-5610-4ae2-999d-f8f0b1599c4f"; + fsType = "ext4"; + }; + + swapDevices = + [{ device = "/dev/disk/by-uuid/610a3dbc-59d5-4e5b-b5de-b31402135d44"; }]; + + hardware.cpu.intel.updateMicrocode = + lib.mkDefault config.hardware.enableRedistributableFirmware; +} diff --git a/hosts/litr/default.nix b/hosts/litr/default.nix new file mode 100644 index 0000000..ac6bd36 --- /dev/null +++ b/hosts/litr/default.nix @@ -0,0 +1,128 @@ +{ config, pkgs, lib, ... }: + +let + pubKeys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIITjFpmWZVWixv2i9902R+g5B8umVhaqmjYEKs2nF3Lu qbit@tal.tapenet.org" + "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIA7khawMK6P0fXjhXXPEUTA2rF2tYB2VhzseZA/EQ/OtAAAAC3NzaDpncmVhdGVy qbit@litr.bold.daemon" + "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIB1cBO17AFcS2NtIT+rIxR2Fhdu3HD4de4+IsFyKKuGQAAAACnNzaDpsZXNzZXI= qbit@litr.bold.daemon" + "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBB/V8N5fqlSGgRCtLJMLDJ8Hd3JcJcY8skI0l+byLNRgQLZfTQRxlZ1yymRs36rXj+ASTnyw5ZDv+q2aXP7Lj0= hosts@secretive.plq.local" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO7v+/xS8832iMqJHCWsxUZ8zYoMWoZhjj++e26g1fLT europa" + ]; + + userBase = { openssh.authorizedKeys.keys = pubKeys; }; + +in { + _module.args.isUnstable = true; + imports = [ ./hardware-configuration.nix ../../overlays/default.nix ]; + + doas.enable = true; + kde.enable = true; + jetbrains.enable = true; + sshFidoAgent.enable = true; + + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + boot.blacklistedKernelModules = [ "dvb_usb_rtl28xxu" ]; + + boot.kernelPackages = pkgs.linuxPackages_latest; + + networking.hostName = "litr"; + networking.hosts."172.16.30.253" = [ "proxmox-02.vm.calyptix.local" ]; + networking.hosts."127.0.0.1" = [ "borg.calyptix.dev" "localhost" ]; + networking.hosts."192.168.122.133" = [ "arst.arst" "vm" ]; + + networking.networkmanager.enable = true; + + preDNS.enable = false; + + sops.secrets = { + tskey = { + sopsFile = config.xin-secrets.litr.secrets; + owner = "root"; + mode = "400"; + }; + }; + + systemd.services = { + "tailscale-init" = { + wantedBy = [ "tailscaled.service" ]; + after = [ "tailscaled.service" ]; + serviceConfig = { + ExecStart = + "${pkgs.tailscale}/bin/tailscale up --auth-key file://${config.sops.secrets.tskey.path}"; + }; + }; + }; + + environment.systemPackages = with pkgs; [ + arcanPackages.all-wrapped + aircrack-ng + apg + barrier + barrier + firefox + fzf + gnome.gnome-keyring + ispell + jitsi-meet-electron + keychain + kismet + matterhorn + mercurial + mosh + mupdf + nfs-utils + nmap + nodejs + notejot + oathToolkit + obs-studio + openvpn + rbw + rust-analyzer + silver-searcher + sshfs + tcpdump + teams + tor + uucp + vlc + vscode + wireshark + virt-manager + + google-chrome-dev + ]; + + nixpkgs.config.allowUnfree = true; + + virtualisation.libvirtd.enable = true; + programs.dconf.enable = true; + + services = { + fwupd.enable = true; + unifi.enable = true; + openntpd.enable = true; + resolved = { + enable = true; + dnssec = "allow-downgrade"; + }; + }; + + networking.firewall = { + allowedTCPPorts = [ 22 ]; + checkReversePath = "loose"; + }; + + users.users.root = userBase; + users.users.abieber = userBase // { + isNormalUser = true; + shell = pkgs.zsh; + extraGroups = [ "wheel" "networkmanager" "libvirtd" ]; + }; + + programs.zsh.enable = true; + + system.stateVersion = "20.03"; # Did you read the comment? +} + diff --git a/hosts/litr/hardware-configuration.nix b/hosts/litr/hardware-configuration.nix new file mode 100644 index 0000000..ca831b5 --- /dev/null +++ b/hosts/litr/hardware-configuration.nix @@ -0,0 +1,37 @@ +{ config, lib, pkgs, ... }: + +{ + boot.initrd.availableKernelModules = [ + "nvme" + "ehci_pci" + "xhci_pci" + "ahci" + "usb_storage" + "sd_mod" + "rtsx_pci_sdmmc" + ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ "kvm-amd" ]; + boot.extraModulePackages = [ ]; + + hardware = { + enableRedistributableFirmware = true; + bluetooth.enable = true; + #rtl-sdr.enable = true; + }; + + fileSystems."/" = { + device = "/dev/disk/by-uuid/90420d7b-15a7-404b-b3cf-ac9a1bc418de"; + fsType = "ext4"; + }; + + fileSystems."/boot" = { + device = "/dev/disk/by-uuid/4378-1665"; + fsType = "vfat"; + }; + + swapDevices = + [{ device = "/dev/disk/by-uuid/5d0c92f0-c812-432f-a199-acce01673ffe"; }]; + + nix.settings.max-jobs = lib.mkDefault 8; +} diff --git a/hosts/plq/default.nix b/hosts/plq/default.nix new file mode 100644 index 0000000..24821df --- /dev/null +++ b/hosts/plq/default.nix @@ -0,0 +1,63 @@ +{ config, pkgs, emacs, isUnstable, ... }: +let + pubKeys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFbj3DNho0T/SLcuKPzxT2/r8QNdEQ/ms6tRiX6YraJk root@tal.tapenet.org" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIPMaAm4rDxyU975Z54YiNw3itC2fGc3SaE2VaS1fai8 root@box" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIITjFpmWZVWixv2i9902R+g5B8umVhaqmjYEKs2nF3Lu qbit@tal.tapenet.org" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILnaC1v+VoVNnK04D32H+euiCyWPXU8nX6w+4UoFfjA3 qbit@plq" + ]; + userBase = { openssh.authorizedKeys.keys = pubKeys; }; + secretAgent = + "Contents/Library/LoginItems/SecretAgent.app/Contents/MacOS/SecretAgent"; +in { + _module.args.isUnstable = false; + imports = [ ../../configs/tmux.nix ../../configs/zsh.nix ../../bins ]; + + networking.hostName = "plq"; + + programs.zsh.enable = true; + services.nix-daemon.enable = true; + nix.package = pkgs.nix; + + services.emacs.package = pkgs.emacsUnstable; + + system = { + keyboard = { + enableKeyMapping = true; + remapCapsLockToControl = true; + }; + defaults = { + dock.orientation = "left"; + SoftwareUpdate.AutomaticallyInstallMacOSUpdates = true; + }; + }; + + launchd.user.agents."SecretAgent" = { + command = + ''"/Users/qbit/Applications/Nix Apps/Secretive.app/${secretAgent}"''; + serviceConfig = rec { + KeepAlive = true; + StandardErrorPath = StandardOutPath; + StandardOutPath = "/Users/qbit/Library/Logs/SecretAgent.log"; + }; + }; + + environment.variables = { + SSH_AUTH_SOCK = + "$HOME/Library/Containers/com.maxgoedjen.Secretive.SecretAgent/Data/socket.ssh"; + }; + + environment.systemPackages = with pkgs; [ + (callPackage ../../pkgs/nheko.nix { inherit isUnstable; }) + (callPackage ../../pkgs/secretive.nix { inherit isUnstable; }) + + direnv + go + mosh + neovim + nixfmt + nmap + statix + ]; +} + diff --git a/hosts/weather/default.nix b/hosts/weather/default.nix new file mode 100644 index 0000000..ab9d171 --- /dev/null +++ b/hosts/weather/default.nix @@ -0,0 +1,197 @@ +{ config, pkgs, lib, ... }: + +let + pubKeys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDM2k2C6Ufx5RNf4qWA9BdQHJfAkskOaqEWf8yjpySwH Nix Manager" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO7v+/xS8832iMqJHCWsxUZ8zYoMWoZhjj++e26g1fLT europa" + "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIA7khawMK6P0fXjhXXPEUTA2rF2tYB2VhzseZA/EQ/OtAAAAC3NzaDpncmVhdGVy qbit@litr.bold.daemon" + "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIB1cBO17AFcS2NtIT+rIxR2Fhdu3HD4de4+IsFyKKuGQAAAACnNzaDpsZXNzZXI= qbit@litr.bold.daemon" + "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBB/V8N5fqlSGgRCtLJMLDJ8Hd3JcJcY8skI0l+byLNRgQLZfTQRxlZ1yymRs36rXj+ASTnyw5ZDv+q2aXP7Lj0= hosts@secretive.plq.local" + ]; + userBase = { openssh.authorizedKeys.keys = pubKeys; }; +in { + _module.args.isUnstable = false; + imports = [ ./hardware-configuration.nix ]; + + defaultUsers.enable = false; + + boot = { + initrd.availableKernelModules = + [ "usbhid" "usb_storage" "vc4" "rtc-ds3232" "rtc-ds1307" ]; + kernelPackages = pkgs.linuxPackages_rpi4; + kernelModules = [ "raspberrypi_ts" "rtc-ds3232" "rtc-ds1307" ]; + #kernelPatches = [{ + # name = "touchscreen"; + # patch = null; + # extraConfig = '' + # CONFIG_TOUCHSCREEN_RASPBERRYPI_FW m + # CONFIG_RTC_DRV_DS1307 m + # CONFIG_RTC_DRV_DS3232 m + # ''; + #}]; + loader = { + grub.enable = false; + generic-extlinux-compatible.enable = true; + }; + }; + + networking = { + hostName = "weather"; + networkmanager = { enable = true; }; + wireless.userControlled.enable = true; + hosts."100.120.151.126" = [ "graph.tapenet.org" ]; + }; + + users.users.weather = { + shell = pkgs.zsh; + isNormalUser = true; + description = "Weather"; + extraGroups = [ "wheel" ]; + }; + + preDNS.enable = false; + services.xserver = { + enable = true; + + libinput.enable = true; + + windowManager.xmonad = { + enable = true; + extraPackages = haskellPackages: [ haskellPackages.xmonad-contrib ]; + config = '' + {-# LANGUAGE QuasiQuotes #-} + + import qualified Data.Map as M + import Data.Monoid + import XMonad + import XMonad.Actions.CycleWS + import XMonad.Hooks.EwmhDesktops + import XMonad.Hooks.ManageDocks + import XMonad.Hooks.UrgencyHook + import XMonad.Layout.Decoration + import XMonad.Layout.LayoutModifier + import XMonad.Layout.Simplest (Simplest(..)) + import XMonad.Layout.Spacing + import XMonad.Layout.SubLayouts + import XMonad.Layout.Tabbed + import XMonad.Layout.WindowNavigation + import qualified XMonad.StackSet as W + import XMonad.Util.EZConfig + import XMonad.Util.NamedWindows + import XMonad.Util.Run + import XMonad.Util.SpawnOnce + + data LibNotifyUrgencyHook = + LibNotifyUrgencyHook + deriving (Read, Show) + + instance UrgencyHook LibNotifyUrgencyHook where + urgencyHook LibNotifyUrgencyHook w = do + name <- getName w + Just idx <- fmap (W.findTag w) $ gets windowset + safeSpawn "notify-send" [show name, "workspace " ++ idx] + + main :: IO () + main = do + xmonad $ + ewmh $ + withUrgencyHook LibNotifyUrgencyHook $ + def + { normalBorderColor = "#666666" + , focusedBorderColor = "darkgrey" + , focusFollowsMouse = False + , terminal = "xterm" + , workspaces = myWorkspaces + , startupHook = myStartupHook + , layoutHook = myLayoutHook + , keys = \c -> myKeys c `M.union` XMonad.keys def c + , manageHook = manageDocks <+> myManageHook <+> manageHook def + } `removeKeysP` + ["M-p"] -- don't clober emacs. + + myKeys :: XConfig t -> M.Map (KeyMask, KeySym) (X ()) + myKeys (XConfig {XMonad.modMask = modm}) = + M.fromList + [ ((modm .|. shiftMask, xK_Right), shiftToNext) + , ((modm .|. shiftMask, xK_Left), shiftToPrev) + , ((modm, xK_r), spawn "rofi -show run") + , ((modm .|. controlMask, xK_h), sendMessage $ pullGroup L) + , ((modm .|. controlMask, xK_l), sendMessage $ pullGroup R) + , ((modm .|. controlMask, xK_k), sendMessage $ pullGroup U) + , ((modm .|. controlMask, xK_j), sendMessage $ pullGroup D) + , ((modm .|. controlMask, xK_m), withFocused (sendMessage . MergeAll)) + , ((modm .|. controlMask, xK_u), withFocused (sendMessage . UnMerge)) + , ((modm .|. controlMask, xK_period), onGroup W.focusUp') + , ((modm .|. controlMask, xK_comma), onGroup W.focusDown') + ] + + myWorkspaces :: [String] + myWorkspaces = + clickable $ ["main", "2", "3", "4", "5", "6", "7", "8", "console"] + where + clickable l = + [ "%{A1:xdotool key alt+" ++ show (n) ++ "&:}" ++ ws ++ "%{A}" + | (i, ws) <- zip [1 :: Int .. 9 :: Int] l + , let n = i + ] + + myTabTheme :: Theme + myTabTheme = + def + { activeTextColor = "#000" + , activeColor = "#ffffea" + , inactiveColor = "#dedeff" + , urgentBorderColor = "red" + } + + myLayoutHook :: + XMonad.Layout.LayoutModifier.ModifiedLayout WindowNavigation (XMonad.Layout.LayoutModifier.ModifiedLayout (XMonad.Layout.Decoration.Decoration XMonad.Layout.Tabbed.TabbedDecoration XMonad.Layout.Decoration.DefaultShrinker) (XMonad.Layout.LayoutModifier.ModifiedLayout (Sublayout Simplest) (XMonad.Layout.LayoutModifier.ModifiedLayout Spacing (Choose (XMonad.Layout.LayoutModifier.ModifiedLayout (XMonad.Layout.Decoration.Decoration XMonad.Layout.Tabbed.TabbedDecoration XMonad.Layout.Decoration.DefaultShrinker) (XMonad.Layout.LayoutModifier.ModifiedLayout (Sublayout Simplest) Tall)) (Choose (Mirror (XMonad.Layout.LayoutModifier.ModifiedLayout (XMonad.Layout.Decoration.Decoration XMonad.Layout.Tabbed.TabbedDecoration XMonad.Layout.Decoration.DefaultShrinker) (XMonad.Layout.LayoutModifier.ModifiedLayout (Sublayout Simplest) Tall))) Full))))) Window + myLayoutHook = + windowNavigation $ + subTabbed $ + spacingRaw True (Border 20 5 5 5) True (Border 10 10 10 10) True $ + (tiled ||| Mirror tiled ||| Full) + where + tiled = + addTabs shrinkText myTabTheme . subLayout [] Simplest $ + Tall nmaster delta ratio + nmaster = 1 + ratio = 0.5 + delta = 0.03 + + myManageHook :: Query (Data.Monoid.Endo WindowSet) + myManageHook = + composeAll + [ className =? "mpv" --> doFloat + , className =? "VLC" --> doFloat + , className =? "Pinentry-gtk-2" --> doFloat + , className =? "Pinentry-gnome3" --> doFloat + , className =? "XConsole" --> doF (W.shift (myWorkspaces !! 8)) + ] + + myStartupHook :: X () + myStartupHook = do + spawn "pkill polybar; polybar" + spawnOnce "firefox --kiosk https://graph.tapenet.org" + ''; + }; + #desktopManager.xfce.enable = true; + displayManager.autoLogin = { + enable = true; + user = "weather"; + }; + }; + + users.users.root = userBase; + + environment.systemPackages = with pkgs; [ + qutebrowser + firefox + dtc + rofi + polybar + nix-top + ]; + + system.stateVersion = "21.11"; +} diff --git a/hosts/weather/hardware-configuration.nix b/hosts/weather/hardware-configuration.nix new file mode 100644 index 0000000..a7fd6f4 --- /dev/null +++ b/hosts/weather/hardware-configuration.nix @@ -0,0 +1,24 @@ +{ config, lib, pkgs, ... }: + +{ + fileSystems = { + "/" = { + device = "/dev/disk/by-label/NIXOS_SD"; + fsType = "ext4"; + }; + "/tmp" = { + device = "/dev/disk/by-label/nix-extra"; + fsType = "ext4"; + }; + }; + + hardware.enableRedistributableFirmware = true; + + hardware.deviceTree = { + overlays = [ + "${pkgs.raspberrypifw}/share/raspberrypi/boot/overlays/rpi-ft5406.dtbo" + ]; + }; + + hardware.raspberry-pi."4".fkms-3d.enable = true; +} diff --git a/installer.nix b/installer.nix new file mode 100644 index 0000000..4b33c46 --- /dev/null +++ b/installer.nix @@ -0,0 +1,133 @@ +{ config, lib, options, pkgs, ... }: + +let + managementKey = + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDM2k2C6Ufx5RNf4qWA9BdQHJfAkskOaqEWf8yjpySwH Nix Manager"; +in { + imports = [ ./configs/colemak.nix ./configs/tmux.nix ./configs/neovim.nix ]; + + options.myconf = { + hwPubKeys = lib.mkOption rec { + type = lib.types.listOf lib.types.str; + default = [ + managementKey + "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIB1cBO17AFcS2NtIT+rIxR2Fhdu3HD4de4+IsFyKKuGQAAAACnNzaDpsZXNzZXI=" + "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIDEKElNAm/BhLnk4Tlo00eHN5bO131daqt2DIeikw0b2AAAABHNzaDo=" + "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBB/V8N5fqlSGgRCtLJMLDJ8Hd3JcJcY8skI0l+byLNRgQLZfTQRxlZ1yymRs36rXj+ASTnyw5ZDv+q2aXP7Lj0=" + "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIHrYWbbgBkGcOntDqdMaWVZ9xn+dHM+Ap6s1HSAalL28AAAACHNzaDptYWlu" + ]; + example = default; + description = "List of hardwar public keys to use"; + }; + zshPrompt = lib.mkOption rec { + type = lib.types.lines; + example = default; + description = "Base zsh prompt"; + default = '' + autoload -U promptinit && promptinit + autoload -Uz vcs_info + autoload -Uz colors && colors + + setopt prompt_subst + #setopt prompt_sp + + zstyle ':vcs_info:*' enable git hg cvs + zstyle ':vcs_info:*' get-revision true + zstyle ':vcs_info:git:*' check-for-changes true + zstyle ':vcs_info:git:*' formats '(%b)' + + precmd_vcs_info() { vcs_info } + precmd_functions+=( precmd_vcs_info ) + + prompt_char() { + if [ -z "$IN_NIX_SHELL" ]; then + echo -n "%#" + else + echo -n ";" + fi + } + + PROMPT='%n@%m[%(?.%{$fg[default]%}.%{$fg[red]%})%?%{$reset_color%}]:%~$vcs_info_msg_0_$(prompt_char) ' + + eval "$(direnv hook zsh)" + + ''; + }; + zshConf = lib.mkOption rec { + type = lib.types.lines; + example = default; + description = "Base zsh config"; + default = '' + export NO_COLOR=1 + # That sweet sweet ^W + WORDCHARS='*?_-.[]~=&;!#$%^(){}<>' + + autoload -Uz compinit && compinit + + set -o emacs + + ''; + }; + }; + + config = { + sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; + + # from https://github.com/dylanaraps/neofetch + users.motd = '' + + ::::. '::::: ::::' + '::::: ':::::. ::::' + ::::: '::::.::::: + .......:::::..... :::::::: + ::::::::::::::::::. :::::: ::::. + ::::::::::::::::::::: :::::. ::::' + ..... ::::' :::::' + ::::: '::' :::::' + ........::::: ' :::::::::::. + ::::::::::::: ::::::::::::: + ::::::::::: .. ::::: + .::::: .::: ::::: + .::::: ..... + ::::: :::::. ......:::::::::::::' + ::: ::::::. ':::::::::::::::::' + .:::::::: ':::::::::: + .::::'''::::. '::::. + .::::' ::::. '::::. + .:::: :::: '::::. + + ''; + boot.cleanTmpDir = true; + + environment.systemPackages = with pkgs; [ apg inetutils nixfmt ]; + + environment.interactiveShellInit = '' + alias vi=nvim + ''; + + time.timeZone = "US/Mountain"; + + programs = { + zsh.enable = true; + ssh = { + startAgent = true; + extraConfig = ""; + }; + }; + + users.users.root = { + openssh.authorizedKeys.keys = config.myconf.hwPubKeys; + }; + + services = { + openntpd.enable = true; + pcscd.enable = true; + openssh = { + enable = true; + # This is set in modules/profiles/installation-device.nix, but it is set to 'yes' :( + #permitRootLogin = "prohibit-password"; + passwordAuthentication = false; + }; + }; + }; +} diff --git a/overlays/default.nix b/overlays/default.nix new file mode 100644 index 0000000..9f26224 --- /dev/null +++ b/overlays/default.nix @@ -0,0 +1,43 @@ +{ self, config, pkgs, lib, isUnstable, ... }: + +{ + nixpkgs.overlays = if isUnstable then [ + # https://github.com/NixOS/nixpkgs/pull/186130 + (self: super: { + tidal-hifi = super.tidal-hifi.overrideAttrs (old: { + buildInputs = (old.buildInputs or [ ]) ++ [ pkgs.imagemagick ]; + postFixup = '' + makeWrapper $out/opt/tidal-hifi/tidal-hifi $out/bin/tidal-hifi \ + --prefix LD_LIBRARY_PATH : "${ + lib.makeLibraryPath super.tidal-hifi.buildInputs + }" \ + "''${gappsWrapperArgs[@]}" + + substituteInPlace $out/share/applications/tidal-hifi.desktop --replace \ + "/opt/tidal-hifi/tidal-hifi" "tidal-hifi" + + for size in 48 64 128 256 512; do + mkdir -p $out/share/icons/hicolor/''${size}x''${size}/apps/ + convert $out/share/icons/hicolor/0x0/apps/tidal-hifi.png \ + -resize ''${size}x''${size} \ + $out/share/icons/hicolor/''${size}x''${size}/apps/tidal-hifi.png + done + ''; + + }); + }) + + (self: super: { + wireplumber = super.wireplumber.overrideAttrs (old: { + patches = (old.patches or [ ]) ++ [ + (super.fetchpatch { + url = + "https://gitlab.freedesktop.org/pipewire/wireplumber/-/merge_requests/398.patch"; + sha256 = "sha256-rEp/3fjBRbkFuw4rBW6h8O5hcy/oBP3DW7bPu5rVfNY="; + }) + ]; + }); + }) + ] else + [ ]; +} diff --git a/pkgs/athens.nix b/pkgs/athens.nix new file mode 100644 index 0000000..d3d25d1 --- /dev/null +++ b/pkgs/athens.nix @@ -0,0 +1,45 @@ +{ stdenv, lib, buildGoModule, fetchFromGitHub, isUnstable, makeWrapper, go, git +, ... }: + +let + vendorHash = if isUnstable then + "" + else + "sha256-7CnkKMZ1so1lflmp4D9EAESR6/u9ys5CTuVOsYetp0I="; + +in with lib; +buildGoModule rec { + pname = "athens"; + version = "0.11.0"; + + src = fetchFromGitHub { + owner = "gomods"; + repo = pname; + rev = "v${version}"; + sha256 = "sha256-hkewZ21ElkoDsbPPiCZNmWu4MBlKTlnrK72/xCX06Sk="; + }; + + doCheck = false; + + ldflags = [ "-X github.com/gomods/athens/pkg/build.version=${version}" ]; + + nativeBuildInputs = lib.optionals stdenv.isLinux [ makeWrapper go ]; + + proxyVendor = true; + + subPackages = [ "cmd/proxy" ]; + + vendorSha256 = vendorHash; + + postInstall = lib.optionalString stdenv.isLinux '' + mv $out/bin/proxy $out/bin/athens + wrapProgram $out/bin/athens --prefix PATH : ${lib.makeBinPath [ git ]} + ''; + + meta = { + description = "A Go module datastore and proxy"; + homepage = "https://github.com/gomods/athens"; + license = licenses.mit; + maintainers = with maintainers; [ qbit ]; + }; +} diff --git a/pkgs/cinny-desktop.nix b/pkgs/cinny-desktop.nix new file mode 100644 index 0000000..12219fd --- /dev/null +++ b/pkgs/cinny-desktop.nix @@ -0,0 +1,58 @@ +{ lib, fetchurl, appimageTools, makeDesktopItem, isUnstable, desktop-file-utils +, ... }: + +let + name = "cinny-desktop"; + version = "2.0.4"; + + src = fetchurl { + name = "cinny_${version}_amd64.AppImage"; + url = + "https://github.com/cinnyapp/cinny-desktop/releases/download/v${version}/cinny_${version}_amd64.AppImage"; + sha256 = "sha256-9ZQyVcTsHja67DhuIyniTK/xr0C6qN7fiCmjt8enUd8="; + }; + + appimageContents = appimageTools.extract { inherit name src; }; + +in appimageTools.wrapType2 rec { + inherit name src; + + extraInstallCommands = '' + cp -r ${appimageContents}/* $out + cd $out + chmod -R +w $out + + ${desktop-file-utils}/bin/desktop-file-install --dir $out/share/applications \ + --set-key Exec --set-value ${name} "cinny.desktop" + + mv usr/bin/cinny $out/${name} + #mv usr/share share + + rm -rf usr/lib/* AppRun* *.desktop + ''; + + extraPkgs = pkgs: + with pkgs; [ + atk + avahi + brotli + cairo + fontconfig + freetype + fribidi + glew-egl + gobject-introspection + gst_all_1.gstreamer + harfbuzz + icu + libdrm + libGLU + libgpg-error + librsvg + libthai + pango + xorg.libX11 + xorg.libxcb + zlib + ]; +} diff --git a/pkgs/default.nix b/pkgs/default.nix new file mode 100644 index 0000000..11bbf05 --- /dev/null +++ b/pkgs/default.nix @@ -0,0 +1,8 @@ +{ config, lib, pkgs, isUnstable, ... }: + +with pkgs; { + environment.systemPackages = with pkgs; [ + (callPackage ./cinny-desktop.nix { inherit isUnstable; }) + (callPackage ./mudita-center.nix { inherit isUnstable; }) + ]; +} diff --git a/pkgs/gitmux.nix b/pkgs/gitmux.nix new file mode 100644 index 0000000..823238e --- /dev/null +++ b/pkgs/gitmux.nix @@ -0,0 +1,35 @@ +{ lib, buildGoModule, fetchFromGitHub, isUnstable, ... }: + +let + vendorHash = if isUnstable then + "sha256-lUVngyYnLwCmNXFBMEDO7ecFZNkSi9GGDNTIG4Mk1Zw=" + else + "sha256-oBZaMS7O6MvvznVn9kQ7h0srWvD3VvxerXgghj0CIzM="; + +in with lib; +buildGoModule rec { + pname = "gitmux"; + version = "0.7.9"; + + src = fetchFromGitHub { + owner = "arl"; + repo = pname; + rev = "v${version}"; + sha256 = "sha256-tB/HPOJQEgs3/rHFn7ezi6R9C3HceASLU3WjjKDii9o="; + }; + + vendorSha256 = vendorHash; + + ldflags = [ "-X main.version=${version}" ]; + + proxyVendor = true; + + doCheck = false; + + meta = { + description = "Gitmux shows git status in your tmux status bar"; + homepage = "https://github.com/arl/gitmux"; + license = licenses.mit; + maintainers = with maintainers; [ qbit ]; + }; +} diff --git a/pkgs/got-sigs.diff b/pkgs/got-sigs.diff new file mode 100644 index 0000000..849c379 --- /dev/null +++ b/pkgs/got-sigs.diff @@ -0,0 +1,1751 @@ +From owner-gameoftrees+M2377@openbsd.org Fri Jul 1 15:32:58 2022 +Return-Path: +Received: from compute4.internal (compute4.nyi.internal [10.202.2.44]) + by sloti51n17 (Cyrus 3.7.0-alpha0-713-g1f035dc716-fm-20220617.001-g1f035dc7) with LMTPA; + Fri, 01 Jul 2022 17:32:58 -0400 +X-Cyrus-Session-Id: sloti51n17-1656711178-2859225-2-6045727934994432308 +X-Sieve: CMU Sieve 3.0 +X-Spam-known-sender: no +X-Spam-sender-reputation: 500 (none) +X-Spam-score: 0.0 +X-Spam-hits: BAYES_50 0.8, DCC_REPUT_70_89 0.1, HEADER_FROM_DIFFERENT_DOMAINS 0.25, + MAILING_LIST_MULTI -1, ME_HAS_VSSU 0.001, ME_SENDERREP_NEUTRAL 0.001, + RCVD_IN_DNSWL_MED -2.3, SPF_HELO_NONE 0.001, SPF_PASS -0.001, + T_SCC_BODY_TEXT_LINE -0.01, LANGUAGES en, BAYES_USED user, + SA_VERSION 3.4.6 +X-Spam-source: IP='199.185.178.25', Host='mail.openbsd.org', Country='CA', + FromHeader='com', MailFrom='org' +X-Spam-charsets: plain='us-ascii' +X-Resolved-to: qbit@fastmail.com +X-Delivered-to: aaron@bolddaemon.com +X-Mail-from: owner-gameoftrees+M2377@openbsd.org +Received: from mx4 ([10.202.2.203]) + by compute4.internal (LMTPProxy); Fri, 01 Jul 2022 17:32:58 -0400 +Received: from mx4.messagingengine.com (localhost [127.0.0.1]) + by mailmx.nyi.internal (Postfix) with ESMTP id 4F9E51F2015B + for ; Fri, 1 Jul 2022 17:32:57 -0400 (EDT) +Received: from mx4.messagingengine.com (localhost [127.0.0.1]) + by mx4.messagingengine.com (Authentication Milter) with ESMTP + id 1E136C19FFD; + Fri, 1 Jul 2022 17:32:57 -0400 +ARC-Seal: i=1; a=rsa-sha256; cv=none; d=messagingengine.com; s=fm2; t= + 1656711177; b=MECs9vhH51TZw6R0i0ZmSqoUz/9WJw3n0hR1h0RQXJNTCEIzOk + uAp9CC7hiENzB20jLY/XdRv8uquFgcsi/WGyB0Doh0COt6ZFqSSR3gXxUtcqq8sY + Zp/gm5EcdmlxKgrj6VGUB9R2DxyMSKSaKDntt9THmvAgyQBwfMoMzgCO+8tDMtJ1 + /JxK+pVO8/j66X3qJ9DfklfDMlC/FukJXcvZmrfH3+c544A+h3+jHE5itknD5D8B + ju4qFSEqAs4OVC/qO9uSUCCPxaVH0IvPPhQJVTbo1kkaiTA9+XDsrd6OZvuBsqEB + yjPeFsDb98chOTF8jP3qwK4WC2TheFpbBFLQ== +ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d= + messagingengine.com; h=date:from:to:subject:message-id + :references:mime-version:content-type:in-reply-to:list-help + :list-id:list-owner:list-post:list-subscribe:list-unsubscribe + :sender; s=fm2; t=1656711177; bh=Eo9QrjLTSve5/c6ZYTKt8UqZNUWrN8y + Pr0Y92XB6bLY=; b=g2yT9DM8+qgIbu8pi1s2kp05tmhUGLr+bHYoAJnfv1o1A/6 + Y47l+aJoSAoorAGUtUhQl8AcYMvMOXdL2GI1Uq8Ft3F3pBoysvCuM+22EDkkHmKx + aUQ1zBCNjFaUuJHAryNIlO0DOK+hipWozGji26qBG3j9NumkQ0gAqhjh8Y0QZgOc + YY+ncg5c+1hNKEkzbP76rgHQJz1ajTYggXi/s4L8479i9Joq6IIuuUZcvQFwefqN + N37CiCMfJb6kviCHpEsAUFibZvCQYOC4ayxHyNfZxFARuQfFk47wRRdQFbZ034Jq + u6dgeeBJha9HM5MVH0mpkxk8srtAIu8pnTbOLKw== +ARC-Authentication-Results: i=1; mx4.messagingengine.com; + x-csa=none; + x-me-sender=none; + x-ptr=pass smtp.helo=mail.openbsd.org policy.ptr=mail.openbsd.org; + bimi=skipped (DMARC did not pass); + arc=none (no signatures found); + dkim=none (no signatures found); + dmarc=fail policy.published-domain-policy=quarantine + policy.applied-disposition=none + policy.evaluated-disposition=quarantine + policy.override-reason=trusted_forwarder policy.arc-aware-result=fail + (p=quarantine,has-list-id=yes,d=none,d.eval=quarantine,override=trusted_forwarder,arc_aware_result=fail) + policy.policy-from=p header.from=zettaport.com; + iprev=pass smtp.remote-ip=199.185.178.25 (mail.openbsd.org); + spf=pass smtp.mailfrom=owner-gameoftrees+M2377@openbsd.org + smtp.helo=mail.openbsd.org +X-ME-Authentication-Results: mx4.messagingengine.com; + x-aligned-from=fail; + x-return-mx=pass header.domain=zettaport.com policy.is_org=yes + (MX Records found: europa.zettaport.com); + x-return-mx=pass smtp.domain=openbsd.org policy.is_org=yes + (MX Records found: mail.openbsd.org); + x-tls=pass smtp.version=TLSv1.3 smtp.cipher=TLS_AES_256_GCM_SHA384 + smtp.bits=256/256; + x-vs=clean score=0 state=0 +Authentication-Results: mx4.messagingengine.com; + x-csa=none; + x-me-sender=none; + x-ptr=pass smtp.helo=mail.openbsd.org policy.ptr=mail.openbsd.org +Authentication-Results: mx4.messagingengine.com; + bimi=skipped (DMARC did not pass) +Authentication-Results: mx4.messagingengine.com; + arc=none (no signatures found) +Authentication-Results: mx4.messagingengine.com; + dkim=none (no signatures found); + dmarc=fail policy.published-domain-policy=quarantine + policy.applied-disposition=none + policy.evaluated-disposition=quarantine + policy.override-reason=trusted_forwarder policy.arc-aware-result=fail + (p=quarantine,has-list-id=yes,d=none,d.eval=quarantine,override=trusted_forwarder,arc_aware_result=fail) + policy.policy-from=p header.from=zettaport.com; + iprev=pass smtp.remote-ip=199.185.178.25 (mail.openbsd.org); + spf=pass smtp.mailfrom=owner-gameoftrees+M2377@openbsd.org + smtp.helo=mail.openbsd.org +X-ME-VSSU: VW5zdWI9bWFpbHRvOm1ham9yZG9tb0BvcGVuYnNkLm9yZz9ib2R5PXVuc3ViJTIwZ2FtZW + 9mdHJlZXM +X-ME-VSCause: gggruggvucftvghtrhhoucdtuddrgedvfedrudehfedgudeifecutefuodetggdotefrod + ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpggftfghnshhusghstghrihgsvgdp + uffrtefokffrpgfnqfghnecuuegrihhlohhuthemuceftddtnecunecujfgurhepfffhvf + fukfhfgggtuggjfeejvddutdfjphhssehttddttddttddvnecuhfhrohhmpeflohhshhcu + tfhitghkmhgrrhcuoehophgvnhgsshguodhlihhsthhsseiivghtthgrphhorhhtrdgtoh + hmqeenucggtffrrghtthgvrhhnpefgvdeghfdtiedvteekjeeugfevgfdtgeffleelleff + leevgeejheetffekkeejfeenucffohhmrghinhepfihtrdhgohhtnecukfhppeduleelrd + dukeehrddujeekrddvhedpudegledrvdekrddvfedvrddvgedunecuvehluhhsthgvrhfu + ihiivgeptdenucfrrghrrghmpehinhgvthepudelledrudekhedrudejkedrvdehpdhhvg + hlohepmhgrihhlrdhophgvnhgsshgurdhorhhgpdhmrghilhhfrhhomhepoehofihnvghr + qdhgrghmvghofhhtrhgvvghsodfovdefjeejsehophgvnhgsshgurdhorhhgqe +X-ME-VSScore: 0 +X-ME-VSCategory: clean +X-ME-CSA: none +Received-SPF: pass + (openbsd.org: 199.185.178.25 is authorized to use 'owner-gameoftrees+M2377@openbsd.org' in 'mfrom' identity (mechanism 'mx' matched)) + receiver=mx4.messagingengine.com; + identity=mailfrom; + envelope-from="owner-gameoftrees+M2377@openbsd.org"; + helo=mail.openbsd.org; + client-ip=199.185.178.25 +Received: from mail.openbsd.org (mail.openbsd.org [199.185.178.25]) + (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) + key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) + (No client certificate requested) + by mx4.messagingengine.com (Postfix) with ESMTPS + for ; Fri, 1 Jul 2022 17:32:56 -0400 (EDT) +Received: from openbsd.org (localhost [127.0.0.1]) + by mail.openbsd.org (OpenSMTPD) with ESMTP id 12cabdb7; + Fri, 1 Jul 2022 15:32:47 -0600 (MDT) +Received: from europa.zettaport.com (europa.zettaport.com [149.28.232.241]) + by mail.openbsd.org (OpenSMTPD) with ESMTPS id 9cf59a50 (TLSv1.3:AEAD-AES256-GCM-SHA384:256:NO) + for ; + Fri, 1 Jul 2022 15:32:20 -0600 (MDT) +Received: + by europa.zettaport.com (OpenSMTPD) with ESMTPSA id 774f76fa (TLSv1.3:TLS_AES_256_GCM_SHA384:256:NO) + for ; + Fri, 1 Jul 2022 21:32:19 +0000 (UTC) +Received: from localhost (mail.i.zettaport.com [local]) + by mail.i.zettaport.com (OpenSMTPD) with ESMTPA id 869f35b9 + for ; + Fri, 1 Jul 2022 17:32:15 -0400 (EDT) +Date: Fri, 1 Jul 2022 17:32:15 -0400 +From: Josh Rickmar +To: gameoftrees@openbsd.org +Subject: Re: Tag signing with SSH signatures +Message-ID: +References: +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Content-Disposition: inline +In-Reply-To: +List-Help: +List-ID: +List-Owner: +List-Post: +List-Subscribe: +List-Unsubscribe: +X-Loop: gameoftrees@openbsd.org +Precedence: list +Sender: owner-gameoftrees@openbsd.org + +Updated diff with improvements and suggestions from stsp@ and tracey@ +as well as a regress test. + +diff refs/heads/main refs/heads/ssh_sigs +commit - 917d79a766c47414055c6901624816a41f13597b +commit + bccbae8a0342ccf5043ac731e3b2c17135aabc02 +blob - 8dfd844f3da472b6ed040a62acaf85403cbc07ea +blob + 1b45b53a4efff9977dcd3c2e2e33c499adc94533 +--- got/Makefile ++++ got/Makefile +@@ -13,7 +13,7 @@ SRCS= got.c blame.c commit_graph.c delta.c diff.c \ + diff_myers.c diff_output.c diff_output_plain.c \ + diff_output_unidiff.c diff_output_edscript.c \ + diff_patience.c send.c deltify.c pack_create.c dial.c \ +- bloom.c murmurhash2.c ratelimit.c patch.c ++ bloom.c murmurhash2.c ratelimit.c patch.c sigs.c date.c + + MAN = ${PROG}.1 got-worktree.5 git-repository.5 got.conf.5 + +blob - 5f7f00007937a048564e685aac0a8c6b9e98adab +blob + 92561637b53431f406c97459c28b0ff81903a35d +--- got/got.c ++++ got/got.c +@@ -58,6 +58,8 @@ + #include "got_gotconfig.h" + #include "got_dial.h" + #include "got_patch.h" ++#include "got_sigs.h" ++#include "got_date.h" + + #ifndef nitems + #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +@@ -687,6 +689,76 @@ get_author(char **author, struct got_repository *repo, + } + + static const struct got_error * ++get_allowed_signers(char **allowed_signers, struct got_repository *repo, ++ struct got_worktree *worktree) ++{ ++ const char *got_allowed_signers = NULL; ++ const struct got_gotconfig *worktree_conf = NULL, *repo_conf = NULL; ++ ++ *allowed_signers = NULL; ++ ++ if (worktree) ++ worktree_conf = got_worktree_get_gotconfig(worktree); ++ repo_conf = got_repo_get_gotconfig(repo); ++ ++ /* ++ * Priority of potential author information sources, from most ++ * significant to least significant: ++ * 1) work tree's .got/got.conf file ++ * 2) repository's got.conf file ++ */ ++ ++ if (worktree_conf) ++ got_allowed_signers = got_gotconfig_get_allowed_signers_file( ++ worktree_conf); ++ if (got_allowed_signers == NULL) ++ got_allowed_signers = got_gotconfig_get_allowed_signers_file( ++ repo_conf); ++ ++ if (got_allowed_signers) { ++ *allowed_signers = strdup(got_allowed_signers); ++ if (*allowed_signers == NULL) ++ return got_error_from_errno("strdup"); ++ } ++ return NULL; ++} ++ ++static const struct got_error * ++get_revoked_signers(char **revoked_signers, struct got_repository *repo, ++ struct got_worktree *worktree) ++{ ++ const char *got_revoked_signers = NULL; ++ const struct got_gotconfig *worktree_conf = NULL, *repo_conf = NULL; ++ ++ *revoked_signers = NULL; ++ ++ if (worktree) ++ worktree_conf = got_worktree_get_gotconfig(worktree); ++ repo_conf = got_repo_get_gotconfig(repo); ++ ++ /* ++ * Priority of potential author information sources, from most ++ * significant to least significant: ++ * 1) work tree's .got/got.conf file ++ * 2) repository's got.conf file ++ */ ++ ++ if (worktree_conf) ++ got_revoked_signers = got_gotconfig_get_revoked_signers_file( ++ worktree_conf); ++ if (got_revoked_signers == NULL) ++ got_revoked_signers = got_gotconfig_get_revoked_signers_file( ++ repo_conf); ++ ++ if (got_revoked_signers) { ++ *revoked_signers = strdup(got_revoked_signers); ++ if (*revoked_signers == NULL) ++ return got_error_from_errno("strdup"); ++ } ++ return NULL; ++} ++ ++static const struct got_error * + get_gitconfig_path(char **gitconfig_path) + { + const char *homedir = getenv("HOME"); +@@ -6837,7 +6909,8 @@ usage_tag(void) + { + fprintf(stderr, + "usage: %s tag [-c commit] [-r repository] [-l] " +- "[-m message] name\n", getprogname()); ++ "[-m message] [-s signer_id] name\n", ++ getprogname()); + exit(1); + } + +@@ -6917,12 +6990,14 @@ get_tag_refname(char **refname, const char *tag_name) + } + + static const struct got_error * +-list_tags(struct got_repository *repo, const char *tag_name) ++list_tags(struct got_repository *repo, const char *tag_name, int verify_tags, ++ const char *allowed_signers, const char *revoked_signers, int verbosity) + { + static const struct got_error *err = NULL; + struct got_reflist_head refs; + struct got_reflist_entry *re; + char *wanted_refname = NULL; ++ int bad_sigs = 0; + + TAILQ_INIT(&refs); + +@@ -6946,7 +7021,8 @@ list_tags(struct got_repository *repo, const char *tag + const char *refname; + char *refstr, *tagmsg0, *tagmsg, *line, *id_str, *datestr; + char datebuf[26]; +- const char *tagger; ++ const char *tagger, *ssh_sig = NULL; ++ char *sig_msg = NULL; + time_t tagger_time; + struct got_object_id *id; + struct got_tag_object *tag; +@@ -6962,8 +7038,6 @@ list_tags(struct got_repository *repo, const char *tag + err = got_error_from_errno("got_ref_to_str"); + break; + } +- printf("%stag %s %s\n", GOT_COMMIT_SEP_STR, refname, refstr); +- free(refstr); + + err = got_ref_resolve(&id, repo, re->ref); + if (err) +@@ -6996,6 +7070,22 @@ list_tags(struct got_repository *repo, const char *tag + if (err) + break; + } ++ ++ if (verify_tags) { ++ ssh_sig = got_sigs_get_tagmsg_ssh_signature( ++ got_object_tag_get_message(tag)); ++ if (ssh_sig && allowed_signers == NULL) { ++ err = got_error_msg( ++ GOT_ERR_VERIFY_TAG_SIGNATURE, ++ "SSH signature verification requires " ++ "setting allowed_signers in " ++ "got.conf(5)"); ++ break; ++ } ++ } ++ ++ printf("%stag %s %s\n", GOT_COMMIT_SEP_STR, refname, refstr); ++ free(refstr); + printf("from: %s\n", tagger); + datestr = get_datestr(&tagger_time, datebuf); + if (datestr) +@@ -7025,6 +7115,19 @@ list_tags(struct got_repository *repo, const char *tag + } + } + free(id_str); ++ ++ if (ssh_sig) { ++ err = got_sigs_verify_tag_ssh(&sig_msg, tag, ssh_sig, ++ allowed_signers, revoked_signers, verbosity); ++ if (err && err->code == GOT_ERR_BAD_TAG_SIGNATURE) ++ bad_sigs = 1; ++ else if (err) ++ break; ++ printf("signature: %s", sig_msg); ++ free(sig_msg); ++ sig_msg = NULL; ++ } ++ + if (commit) { + err = got_object_commit_get_logmsg(&tagmsg0, commit); + if (err) +@@ -7050,6 +7153,9 @@ list_tags(struct got_repository *repo, const char *tag + done: + got_ref_list_free(&refs); + free(wanted_refname); ++ ++ if (err == NULL && bad_sigs) ++ err = got_error(GOT_ERR_BAD_TAG_SIGNATURE); + return err; + } + +@@ -7098,9 +7204,6 @@ done: + if (fd != -1 && close(fd) == -1 && err == NULL) + err = got_error_from_errno2("close", *tagmsg_path); + +- /* Editor is done; we can now apply unveil(2) */ +- if (err == NULL) +- err = apply_unveil(repo_path, 0, NULL); + if (err) { + free(*tagmsg); + *tagmsg = NULL; +@@ -7110,7 +7213,8 @@ done: + + static const struct got_error * + add_tag(struct got_repository *repo, const char *tagger, +- const char *tag_name, const char *commit_arg, const char *tagmsg_arg) ++ const char *tag_name, const char *commit_arg, const char *tagmsg_arg, ++ const char *key_file, int verbosity) + { + const struct got_error *err = NULL; + struct got_object_id *commit_id = NULL, *tag_id = NULL; +@@ -7166,10 +7270,18 @@ add_tag(struct got_repository *repo, const char *tagge + preserve_tagmsg = 1; + goto done; + } ++ /* Editor is done; we can now apply unveil(2) */ ++ err = got_sigs_apply_unveil(); ++ if (err) ++ goto done; ++ err = apply_unveil(got_repo_get_path(repo), 0, NULL); ++ if (err) ++ goto done; + } + + err = got_object_tag_create(&tag_id, tag_name, commit_id, +- tagger, time(NULL), tagmsg ? tagmsg : tagmsg_arg, repo); ++ tagger, time(NULL), tagmsg ? tagmsg : tagmsg_arg, key_file, repo, ++ verbosity); + if (err) { + if (tagmsg_path) + preserve_tagmsg = 1; +@@ -7223,11 +7335,13 @@ cmd_tag(int argc, char *argv[]) + struct got_worktree *worktree = NULL; + char *cwd = NULL, *repo_path = NULL, *commit_id_str = NULL; + char *gitconfig_path = NULL, *tagger = NULL; ++ char *allowed_signers = NULL, *revoked_signers = NULL; + const char *tag_name = NULL, *commit_id_arg = NULL, *tagmsg = NULL; +- int ch, do_list = 0; ++ int ch, do_list = 0, verify_tags = 0, verbosity = 0; ++ const char *signer_id = NULL; + int *pack_fds = NULL; + +- while ((ch = getopt(argc, argv, "c:m:r:l")) != -1) { ++ while ((ch = getopt(argc, argv, "c:m:r:ls:Vv")) != -1) { + switch (ch) { + case 'c': + commit_id_arg = optarg; +@@ -7245,6 +7359,18 @@ cmd_tag(int argc, char *argv[]) + case 'l': + do_list = 1; + break; ++ case 's': ++ signer_id = optarg; ++ break; ++ case 'V': ++ verify_tags = 1; ++ break; ++ case 'v': ++ if (verbosity < 0) ++ verbosity = 0; ++ else if (verbosity < 3) ++ verbosity++; ++ break; + default: + usage_tag(); + /* NOTREACHED */ +@@ -7305,26 +7431,40 @@ cmd_tag(int argc, char *argv[]) + } + } + +- if (do_list) { ++ if (do_list || verify_tags) { ++ error = got_repo_open(&repo, repo_path, NULL, pack_fds); ++ if (error != NULL) ++ goto done; ++ error = get_allowed_signers(&allowed_signers, repo, worktree); ++ if (error) ++ goto done; ++ error = get_revoked_signers(&revoked_signers, repo, worktree); ++ if (error) ++ goto done; + if (worktree) { + /* Release work tree lock. */ + got_worktree_close(worktree); + worktree = NULL; + } +- error = got_repo_open(&repo, repo_path, NULL, pack_fds); +- if (error != NULL) +- goto done; + ++ /* ++ * Remove "cpath" promise unless needed for signature tmpfile ++ * creation. ++ */ ++ if (verify_tags) ++ got_sigs_apply_unveil(); ++ else { + #ifndef PROFILE +- /* Remove "cpath" promise. */ +- if (pledge("stdio rpath wpath flock proc exec sendfd unveil", +- NULL) == -1) +- err(1, "pledge"); ++ if (pledge("stdio rpath wpath flock proc exec sendfd " ++ "unveil", NULL) == -1) ++ err(1, "pledge"); + #endif ++ } + error = apply_unveil(got_repo_get_path(repo), 1, NULL); + if (error) + goto done; +- error = list_tags(repo, tag_name); ++ error = list_tags(repo, tag_name, verify_tags, allowed_signers, ++ revoked_signers, verbosity); + } else { + error = get_gitconfig_path(&gitconfig_path); + if (error) +@@ -7344,6 +7484,11 @@ cmd_tag(int argc, char *argv[]) + } + + if (tagmsg) { ++ if (signer_id) { ++ error = got_sigs_apply_unveil(); ++ if (error) ++ goto done; ++ } + error = apply_unveil(got_repo_get_path(repo), 0, NULL); + if (error) + goto done; +@@ -7368,7 +7513,8 @@ cmd_tag(int argc, char *argv[]) + } + + error = add_tag(repo, tagger, tag_name, +- commit_id_str ? commit_id_str : commit_id_arg, tagmsg); ++ commit_id_str ? commit_id_str : commit_id_arg, tagmsg, ++ signer_id, verbosity); + } + done: + if (repo) { +@@ -7389,6 +7535,8 @@ done: + free(gitconfig_path); + free(commit_id_str); + free(tagger); ++ free(allowed_signers); ++ free(revoked_signers); + return error; + } + +@@ -12418,22 +12566,6 @@ cat_tree(struct got_object_id *id, struct got_reposito + return err; + } + +-static void +-format_gmtoff(char *buf, size_t sz, time_t gmtoff) +-{ +- long long h, m; +- char sign = '+'; +- +- if (gmtoff < 0) { +- sign = '-'; +- gmtoff = -gmtoff; +- } +- +- h = (long long)gmtoff / 3600; +- m = ((long long)gmtoff - h*3600) / 60; +- snprintf(buf, sz, "%c%02lld%02lld", sign, h, m); +-} +- + static const struct got_error * + cat_commit(struct got_object_id *id, struct got_repository *repo, FILE *outfile) + { +@@ -12465,14 +12597,14 @@ cat_commit(struct got_object_id *id, struct got_reposi + fprintf(outfile, "%s%s\n", GOT_COMMIT_LABEL_PARENT, pid_str); + free(pid_str); + } +- format_gmtoff(gmtoff, sizeof(gmtoff), ++ got_date_format_gmtoff(gmtoff, sizeof(gmtoff), + got_object_commit_get_author_gmtoff(commit)); + fprintf(outfile, "%s%s %lld %s\n", GOT_COMMIT_LABEL_AUTHOR, + got_object_commit_get_author(commit), + (long long)got_object_commit_get_author_time(commit), + gmtoff); + +- format_gmtoff(gmtoff, sizeof(gmtoff), ++ got_date_format_gmtoff(gmtoff, sizeof(gmtoff), + got_object_commit_get_committer_gmtoff(commit)); + fprintf(outfile, "%s%s %lld %s\n", GOT_COMMIT_LABEL_COMMITTER, + got_object_commit_get_author(commit), +@@ -12531,7 +12663,7 @@ cat_tag(struct got_object_id *id, struct got_repositor + fprintf(outfile, "%s%s\n", GOT_TAG_LABEL_TAG, + got_object_tag_get_name(tag)); + +- format_gmtoff(gmtoff, sizeof(gmtoff), ++ got_date_format_gmtoff(gmtoff, sizeof(gmtoff), + got_object_tag_get_tagger_gmtoff(tag)); + fprintf(outfile, "%s%s %lld %s\n", GOT_TAG_LABEL_TAGGER, + got_object_tag_get_tagger(tag), +blob - 781133bbc9837ad999231c521ae9da3239c0232b +blob + bf9729a9216142455edfd253fb05cd98c0b4b1f1 +--- gotadmin/Makefile ++++ gotadmin/Makefile +@@ -8,7 +8,8 @@ SRCS= gotadmin.c \ + inflate.c lockfile.c object.c object_cache.c object_create.c \ + object_idset.c object_parse.c opentemp.c pack.c pack_create.c \ + path.c privsep.c reference.c repository.c repository_admin.c \ +- worktree_open.c sha1.c bloom.c murmurhash2.c ratelimit.c ++ worktree_open.c sha1.c bloom.c murmurhash2.c ratelimit.c \ ++ sigs.c buf.c date.c + MAN = ${PROG}.1 + + CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib +blob - aa54a17419d407bb09bbc7b00af392c74aa8801f +blob + 948b9b8fc5270878f0eb2aa61a579197663e4827 +--- gotweb/Makefile ++++ gotweb/Makefile +@@ -15,7 +15,7 @@ SRCS = gotweb.c parse.y blame.c commit_graph.c delta. + diff_main.c diff_atomize_text.c diff_myers.c diff_output.c \ + diff_output_plain.c diff_output_unidiff.c \ + diff_output_edscript.c diff_patience.c \ +- bloom.c murmurhash2.c ++ bloom.c murmurhash2.c sigs.c date.c + MAN = ${PROG}.conf.5 ${PROG}.8 + + CPPFLAGS += -I${.CURDIR}/../include -I${.CURDIR}/../lib -I${.CURDIR} \ +blob - /dev/null +blob + b005c2c948e0b4b35147550b1b23fef240ddf8b4 (mode 644) +--- /dev/null ++++ include/got_date.h +@@ -0,0 +1,18 @@ ++/* ++ * Copyright (c) 2022 Josh Rickmar ++ * ++ * Permission to use, copy, modify, and distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++void ++got_date_format_gmtoff(char *, size_t, time_t); +blob - 22a9264b9f8d0c0b20b48895dd8ea59708e61d48 +blob + 4bfaed588e2d81e1b0578a939dd96553de6bc11b +--- include/got_error.h ++++ include/got_error.h +@@ -169,6 +169,8 @@ + #define GOT_ERR_PATCH_FAILED 151 + #define GOT_ERR_FILEIDX_DUP_ENTRY 152 + #define GOT_ERR_PIN_PACK 153 ++#define GOT_ERR_BAD_TAG_SIGNATURE 154 ++#define GOT_ERR_VERIFY_TAG_SIGNATURE 155 + + struct got_error { + int code; +blob - 3dbe5d7d43cf45ec0e7997d43f266c3ce0c9fcbe +blob + 26e15d93b91bc42ee028fa8ecf60a8d1ac4dfdc9 +--- include/got_gotconfig.h ++++ include/got_gotconfig.h +@@ -29,3 +29,19 @@ const char *got_gotconfig_get_author(const struct got_ + */ + void got_gotconfig_get_remotes(int *, const struct got_remote_repo **, + const struct got_gotconfig *); ++ ++/* ++ * Obtain the filename of the allowed signers file. ++ * Returns NULL if no configuration file is found or no allowed signers file ++ * is configured. ++ */ ++const char * ++got_gotconfig_get_allowed_signers_file(const struct got_gotconfig *); ++ ++/* ++ * Obtain the filename of the revoked signers file. ++ * Returns NULL if no configuration file is found or no revoked signers file ++ * is configured. ++ */ ++const char * ++got_gotconfig_get_revoked_signers_file(const struct got_gotconfig *); +blob - a8d0318ceaa7152627e8c8718ba039f8517bc3e4 +blob + 1cd6f349912d3e03ebbdccfd4beeeb54663af7fb +--- include/got_object.h ++++ include/got_object.h +@@ -351,4 +351,4 @@ const struct got_error *got_object_commit_add_parent(s + /* Create a new tag object in the repository. */ + const struct got_error *got_object_tag_create(struct got_object_id **, + const char *, struct got_object_id *, const char *, +- time_t, const char *, struct got_repository *); ++ time_t, const char *, const char *, struct got_repository *, int verbosity); +blob - /dev/null +blob + 204a6265963d6dcbf4d6f3de13f4bdbafaafc6fa (mode 644) +--- /dev/null ++++ include/got_sigs.h +@@ -0,0 +1,28 @@ ++/* ++ * Copyright (c) 2022 Josh Rickmar ++ * ++ * Permission to use, copy, modify, and distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++const struct got_error * ++got_sigs_apply_unveil(void); ++ ++const struct got_error * ++got_sigs_sign_tag_ssh(pid_t *, int *, int *, const char *, int); ++ ++const char * ++got_sigs_get_tagmsg_ssh_signature(const char *); ++ ++const struct got_error * ++got_sigs_verify_tag_ssh(char **, struct got_tag_object *, const char *, ++ const char *, const char *, int); +blob - /dev/null +blob + 815b291ce868d18136ce8f45fa2f890b6f6c08f9 (mode 644) +--- /dev/null ++++ lib/date.c +@@ -0,0 +1,35 @@ ++/* ++ * Copyright (c) 2022 Josh Rickmar ++ * ++ * Permission to use, copy, modify, and distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++#include ++ ++#include "got_date.h" ++ ++void ++got_date_format_gmtoff(char *buf, size_t sz, time_t gmtoff) ++{ ++ long long h, m; ++ char sign = '+'; ++ ++ if (gmtoff < 0) { ++ sign = '-'; ++ gmtoff = -gmtoff; ++ } ++ ++ h = (long long)gmtoff / 3600; ++ m = ((long long)gmtoff - h*3600) / 60; ++ snprintf(buf, sz, "%c%02lld%02lld", sign, h, m); ++} +blob - 3ffd653ef429fab490d06ba6a953185254e7c117 +blob + 3c092e61bab70845c184eb10f359eb6df3ee01ce +--- lib/error.c ++++ lib/error.c +@@ -217,6 +217,8 @@ static const struct got_error got_errors[] = { + { GOT_ERR_PATCH_FAILED, "patch failed to apply" }, + { GOT_ERR_FILEIDX_DUP_ENTRY, "duplicate file index entry" }, + { GOT_ERR_PIN_PACK, "could not pin pack file" }, ++ { GOT_ERR_BAD_TAG_SIGNATURE, "invalid tag signature" }, ++ { GOT_ERR_VERIFY_TAG_SIGNATURE, "cannot verify signature" }, + }; + + static struct got_custom_error { +blob - 5e02aa1efeff0dd226e617da410a4663d8376d9a +blob + 39337ed4d9cbe7dfa5939b3f4dcb38793ccddfbd +--- lib/got_lib_gotconfig.h ++++ lib/got_lib_gotconfig.h +@@ -20,6 +20,8 @@ struct got_gotconfig { + char *author; + int nremotes; + struct got_remote_repo *remotes; ++ char *allowed_signers_file; ++ char *revoked_signers_file; + }; + + const struct got_error *got_gotconfig_read(struct got_gotconfig **, +blob - 6ffe646e98676cf9a0d19fe3ad27f3e63ab04fcc +blob + dac4ab973b68243e262fd1ae6482fffb6dc2bc57 +--- lib/got_lib_privsep.h ++++ lib/got_lib_privsep.h +@@ -172,6 +172,8 @@ enum got_imsg_type { + /* Messages related to gotconfig files. */ + GOT_IMSG_GOTCONFIG_PARSE_REQUEST, + GOT_IMSG_GOTCONFIG_AUTHOR_REQUEST, ++ GOT_IMSG_GOTCONFIG_ALLOWEDSIGNERS_REQUEST, ++ GOT_IMSG_GOTCONFIG_REVOKEDSIGNERS_REQUEST, + GOT_IMSG_GOTCONFIG_REMOTES_REQUEST, + GOT_IMSG_GOTCONFIG_INT_VAL, + GOT_IMSG_GOTCONFIG_STR_VAL, +@@ -760,6 +762,10 @@ const struct got_error *got_privsep_recv_gitconfig_rem + const struct got_error *got_privsep_send_gotconfig_parse_req(struct imsgbuf *, + int); + const struct got_error *got_privsep_send_gotconfig_author_req(struct imsgbuf *); ++const struct got_error *got_privsep_send_gotconfig_allowed_signers_req( ++ struct imsgbuf *); ++const struct got_error *got_privsep_send_gotconfig_revoked_signers_req( ++ struct imsgbuf *); + const struct got_error *got_privsep_send_gotconfig_remotes_req( + struct imsgbuf *); + const struct got_error *got_privsep_recv_gotconfig_str(char **, +blob - 5b602c9f5513aee64b98ca608535d5b85280ec42 +blob + 7fae8306f7aa444e25b71f0a95f8f151ec324a7f +--- lib/gotconfig.c ++++ lib/gotconfig.c +@@ -101,6 +101,24 @@ got_gotconfig_read(struct got_gotconfig **conf, const + if (err) + goto done; + ++ err = got_privsep_send_gotconfig_allowed_signers_req(ibuf); ++ if (err) ++ goto done; ++ ++ err = got_privsep_recv_gotconfig_str(&(*conf)->allowed_signers_file, ++ ibuf); ++ if (err) ++ goto done; ++ ++ err = got_privsep_send_gotconfig_revoked_signers_req(ibuf); ++ if (err) ++ goto done; ++ ++ err = got_privsep_recv_gotconfig_str(&(*conf)->revoked_signers_file, ++ ibuf); ++ if (err) ++ goto done; ++ + err = got_privsep_send_gotconfig_remotes_req(ibuf); + if (err) + goto done; +@@ -158,3 +176,15 @@ got_gotconfig_get_remotes(int *nremotes, const struct + *nremotes = conf->nremotes; + *remotes = conf->remotes; + } ++ ++const char * ++got_gotconfig_get_allowed_signers_file(const struct got_gotconfig *conf) ++{ ++ return conf->allowed_signers_file; ++} ++ ++const char * ++got_gotconfig_get_revoked_signers_file(const struct got_gotconfig *conf) ++{ ++ return conf->revoked_signers_file; ++} +blob - 5036de1b9a6b491a1fc7c0358a03dcd9574f6cf3 +blob + 8f33d6ba0309e1d8f43ef3b0c35b661bd6045211 +--- lib/object_create.c ++++ lib/object_create.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -35,6 +36,7 @@ + #include "got_repository.h" + #include "got_opentemp.h" + #include "got_path.h" ++#include "got_sigs.h" + + #include "got_lib_sha1.h" + #include "got_lib_deflate.h" +@@ -45,6 +47,8 @@ + + #include "got_lib_object_create.h" + ++#include "buf.h" ++ + #ifndef nitems + #define nitems(_a) (sizeof(_a) / sizeof((_a)[0])) + #endif +@@ -608,19 +612,21 @@ done: + const struct got_error * + got_object_tag_create(struct got_object_id **id, + const char *tag_name, struct got_object_id *object_id, const char *tagger, +- time_t tagger_time, const char *tagmsg, struct got_repository *repo) ++ time_t tagger_time, const char *tagmsg, const char *key_file, ++ struct got_repository *repo, int verbosity) + { + const struct got_error *err = NULL; + SHA1_CTX sha1_ctx; + char *header = NULL; + char *tag_str = NULL, *tagger_str = NULL; + char *id_str = NULL, *obj_str = NULL, *type_str = NULL; +- size_t headerlen, len = 0, n; ++ size_t headerlen, len = 0, sig_len = 0, n; + FILE *tagfile = NULL; + off_t tagsize = 0; + char *msg0 = NULL, *msg; + const char *obj_type_str; + int obj_type; ++ BUF *buf = NULL; + + *id = NULL; + +@@ -681,9 +687,79 @@ got_object_tag_create(struct got_object_id **id, + while (isspace((unsigned char)msg[0])) + msg++; + ++ if (key_file) { ++ FILE *out; ++ pid_t pid; ++ size_t len; ++ int in_fd, out_fd; ++ int status; ++ ++ err = buf_alloc(&buf, 0); ++ if (err) ++ goto done; ++ ++ /* signed message */ ++ err = buf_puts(&len, buf, obj_str); ++ if (err) ++ goto done; ++ err = buf_puts(&len, buf, type_str); ++ if (err) ++ goto done; ++ err = buf_puts(&len, buf, tag_str); ++ if (err) ++ goto done; ++ err = buf_puts(&len, buf, tagger_str); ++ if (err) ++ goto done; ++ err = buf_putc(buf, '\n'); ++ if (err) ++ goto done; ++ err = buf_puts(&len, buf, msg); ++ if (err) ++ goto done; ++ err = buf_putc(buf, '\n'); ++ if (err) ++ goto done; ++ ++ err = got_sigs_sign_tag_ssh(&pid, &in_fd, &out_fd, key_file, ++ verbosity); ++ if (err) ++ goto done; ++ if (buf_write_fd(buf, in_fd) == -1) { ++ err = got_error_from_errno("write"); ++ goto done; ++ } ++ if (close(in_fd) == -1) { ++ err = got_error_from_errno("close"); ++ goto done; ++ } ++ ++ if (waitpid(pid, &status, 0) == -1) { ++ err = got_error_from_errno("waitpid"); ++ goto done; ++ } ++ ++ out = fdopen(out_fd, "r"); ++ if (out == NULL) { ++ err = got_error_from_errno("fdopen"); ++ goto done; ++ } ++ buf_empty(buf); ++ err = buf_load(&buf, out); ++ if (err) ++ goto done; ++ sig_len = buf_len(buf) + 1; ++ err = buf_putc(buf, '\0'); ++ if (err) ++ goto done; ++ if (close(out_fd) == -1) { ++ err = got_error_from_errno("close"); ++ goto done; ++ } ++ } ++ + len = strlen(obj_str) + strlen(type_str) + strlen(tag_str) + +- strlen(tagger_str) + 1 + strlen(msg) + 1; +- ++ strlen(tagger_str) + 1 + strlen(msg) + 1 + sig_len; + if (asprintf(&header, "%s %zd", GOT_OBJ_LABEL_TAG, len) == -1) { + err = got_error_from_errno("asprintf"); + goto done; +@@ -764,6 +840,17 @@ got_object_tag_create(struct got_object_id **id, + } + tagsize += n; + ++ if (key_file && buf_len(buf) > 0) { ++ len = buf_len(buf); ++ SHA1Update(&sha1_ctx, buf_get(buf), len); ++ n = fwrite(buf_get(buf), 1, len, tagfile); ++ if (n != len) { ++ err = got_ferror(tagfile, GOT_ERR_IO); ++ goto done; ++ } ++ tagsize += n; ++ } ++ + *id = malloc(sizeof(**id)); + if (*id == NULL) { + err = got_error_from_errno("malloc"); +@@ -783,6 +870,8 @@ done: + free(header); + free(obj_str); + free(tagger_str); ++ if (buf) ++ buf_release(buf); + if (tagfile && fclose(tagfile) == EOF && err == NULL) + err = got_error_from_errno("fclose"); + if (err) { +blob - c0bdac7221a79c5ec97d1728e862406152d51eb9 +blob + 07acf70c2c9103549d8dd9f40e6a173d0bb20401 +--- lib/privsep.c ++++ lib/privsep.c +@@ -2365,6 +2365,28 @@ got_privsep_send_gotconfig_author_req(struct imsgbuf * + } + + const struct got_error * ++got_privsep_send_gotconfig_allowed_signers_req(struct imsgbuf *ibuf) ++{ ++ if (imsg_compose(ibuf, ++ GOT_IMSG_GOTCONFIG_ALLOWEDSIGNERS_REQUEST, 0, 0, -1, NULL, 0) == -1) ++ return got_error_from_errno("imsg_compose " ++ "GOTCONFIG_ALLOWEDSIGNERS_REQUEST"); ++ ++ return flush_imsg(ibuf); ++} ++ ++const struct got_error * ++got_privsep_send_gotconfig_revoked_signers_req(struct imsgbuf *ibuf) ++{ ++ if (imsg_compose(ibuf, ++ GOT_IMSG_GOTCONFIG_REVOKEDSIGNERS_REQUEST, 0, 0, -1, NULL, 0) == -1) ++ return got_error_from_errno("imsg_compose " ++ "GOTCONFIG_REVOKEDSIGNERS_REQUEST"); ++ ++ return flush_imsg(ibuf); ++} ++ ++const struct got_error * + got_privsep_send_gotconfig_remotes_req(struct imsgbuf *ibuf) + { + if (imsg_compose(ibuf, +blob - /dev/null +blob + 0b04e3f959ed7aab07534ee9bb5cb4a63e0dd93f (mode 644) +--- /dev/null ++++ lib/sigs.c +@@ -0,0 +1,407 @@ ++/* ++ * Copyright (c) 2022 Josh Rickmar ++ * ++ * Permission to use, copy, modify, and distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "got_error.h" ++#include "got_date.h" ++#include "got_object.h" ++#include "got_opentemp.h" ++ ++#include "got_sigs.h" ++ ++#include "buf.h" ++ ++#ifndef MIN ++#define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b)) ++#endif ++ ++#ifndef nitems ++#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) ++#endif ++ ++#ifndef GOT_TAG_PATH_SSH_KEYGEN ++#define GOT_TAG_PATH_SSH_KEYGEN "/usr/bin/ssh-keygen" ++#endif ++ ++#ifndef GOT_TAG_PATH_SIGNIFY ++#define GOT_TAG_PATH_SIGNIFY "/usr/bin/signify" ++#endif ++ ++const struct got_error * ++got_sigs_apply_unveil() ++{ ++ if (unveil(GOT_TAG_PATH_SSH_KEYGEN, "x") != 0) { ++ return got_error_from_errno2("unveil", ++ GOT_TAG_PATH_SSH_KEYGEN); ++ } ++ if (unveil(GOT_TAG_PATH_SIGNIFY, "x") != 0) { ++ return got_error_from_errno2("unveil", ++ GOT_TAG_PATH_SIGNIFY); ++ } ++ ++ return NULL; ++} ++ ++const struct got_error * ++got_sigs_sign_tag_ssh(pid_t *newpid, int *in_fd, int *out_fd, ++ const char* key_file, int verbosity) ++{ ++ const struct got_error *error = NULL; ++ int pid, in_pfd[2], out_pfd[2]; ++ const char* argv[11]; ++ int i = 0, j; ++ ++ *newpid = -1; ++ *in_fd = -1; ++ *out_fd = -1; ++ ++ argv[i++] = GOT_TAG_PATH_SSH_KEYGEN; ++ argv[i++] = "-Y"; ++ argv[i++] = "sign"; ++ argv[i++] = "-f"; ++ argv[i++] = key_file; ++ argv[i++] = "-n"; ++ argv[i++] = "git"; ++ if (verbosity <= 0) { ++ argv[i++] = "-q"; ++ } else { ++ /* ssh(1) allows up to 3 "-v" options. */ ++ for (j = 0; j < MIN(3, verbosity); j++) ++ argv[i++] = "-v"; ++ } ++ argv[i++] = NULL; ++ assert(i <= nitems(argv)); ++ ++ if (pipe2(in_pfd, 0) == -1) ++ return got_error_from_errno("pipe2"); ++ if (pipe2(out_pfd, 0) == -1) ++ return got_error_from_errno("pipe2"); ++ ++ pid = fork(); ++ if (pid == -1) { ++ error = got_error_from_errno("fork"); ++ close(in_pfd[0]); ++ close(in_pfd[1]); ++ close(out_pfd[0]); ++ close(out_pfd[1]); ++ return error; ++ } else if (pid == 0) { ++ if (close(in_pfd[1]) == -1) ++ err(1, "close"); ++ if (close(out_pfd[1]) == -1) ++ err(1, "close"); ++ if (dup2(in_pfd[0], 0) == -1) ++ err(1, "dup2"); ++ if (dup2(out_pfd[0], 1) == -1) ++ err(1, "dup2"); ++ if (execv(GOT_TAG_PATH_SSH_KEYGEN, (char **const)argv) == -1) ++ err(1, "execv"); ++ abort(); /* not reached */ ++ } ++ if (close(in_pfd[0]) == -1) ++ return got_error_from_errno("close"); ++ if (close(out_pfd[0]) == -1) ++ return got_error_from_errno("close"); ++ *newpid = pid; ++ *in_fd = in_pfd[1]; ++ *out_fd = out_pfd[1]; ++ return NULL; ++} ++ ++static char * ++signer_identity(const char *tagger) ++{ ++ char *lt, *gt; ++ ++ lt = strstr(tagger, " <"); ++ gt = strrchr(tagger, '>'); ++ if (lt && gt && lt+1 < gt) ++ return strndup(lt+2, gt-lt-2); ++ return NULL; ++} ++ ++static const char* BEGIN_SSH_SIG = "-----BEGIN SSH SIGNATURE-----\n"; ++static const char* END_SSH_SIG = "-----END SSH SIGNATURE-----\n"; ++ ++const char * ++got_sigs_get_tagmsg_ssh_signature(const char *tagmsg) ++{ ++ const char *s = tagmsg, *begin = NULL, *end = NULL; ++ ++ while ((s = strstr(s, BEGIN_SSH_SIG)) != NULL) { ++ begin = s; ++ s += strlen(BEGIN_SSH_SIG); ++ } ++ if (begin) ++ end = strstr(begin+strlen(BEGIN_SSH_SIG), END_SSH_SIG); ++ if (end == NULL) ++ return NULL; ++ return (end[strlen(END_SSH_SIG)] == '\0') ? begin : NULL; ++} ++ ++static const struct got_error * ++got_tag_write_signed_data(BUF *buf, struct got_tag_object *tag, ++ const char *start_sig) ++{ ++ const struct got_error *err = NULL; ++ struct got_object_id *id; ++ char *id_str = NULL; ++ char *tagger = NULL; ++ const char *tagmsg; ++ char gmtoff[6]; ++ size_t len; ++ ++ id = got_object_tag_get_object_id(tag); ++ err = got_object_id_str(&id_str, id); ++ if (err) ++ goto done; ++ ++ const char *type_label = NULL; ++ switch (got_object_tag_get_object_type(tag)) { ++ case GOT_OBJ_TYPE_BLOB: ++ type_label = GOT_OBJ_LABEL_BLOB; ++ break; ++ case GOT_OBJ_TYPE_TREE: ++ type_label = GOT_OBJ_LABEL_TREE; ++ break; ++ case GOT_OBJ_TYPE_COMMIT: ++ type_label = GOT_OBJ_LABEL_COMMIT; ++ break; ++ case GOT_OBJ_TYPE_TAG: ++ type_label = GOT_OBJ_LABEL_TAG; ++ break; ++ default: ++ break; ++ } ++ got_date_format_gmtoff(gmtoff, sizeof(gmtoff), ++ got_object_tag_get_tagger_gmtoff(tag)); ++ if (asprintf(&tagger, "%s %lld %s", got_object_tag_get_tagger(tag), ++ got_object_tag_get_tagger_time(tag), gmtoff) == -1) { ++ err = got_error_from_errno("asprintf"); ++ goto done; ++ } ++ ++ err = buf_puts(&len, buf, GOT_TAG_LABEL_OBJECT); ++ if (err) ++ goto done; ++ err = buf_puts(&len, buf, id_str); ++ if (err) ++ goto done; ++ err = buf_putc(buf, '\n'); ++ if (err) ++ goto done; ++ err = buf_puts(&len, buf, GOT_TAG_LABEL_TYPE); ++ if (err) ++ goto done; ++ err = buf_puts(&len, buf, type_label); ++ if (err) ++ goto done; ++ err = buf_putc(buf, '\n'); ++ if (err) ++ goto done; ++ err = buf_puts(&len, buf, GOT_TAG_LABEL_TAG); ++ if (err) ++ goto done; ++ err = buf_puts(&len, buf, got_object_tag_get_name(tag)); ++ if (err) ++ goto done; ++ err = buf_putc(buf, '\n'); ++ if (err) ++ goto done; ++ err = buf_puts(&len, buf, GOT_TAG_LABEL_TAGGER); ++ if (err) ++ goto done; ++ err = buf_puts(&len, buf, tagger); ++ if (err) ++ goto done; ++ err = buf_puts(&len, buf, "\n"); ++ if (err) ++ goto done; ++ tagmsg = got_object_tag_get_message(tag); ++ err = buf_append(&len, buf, tagmsg, start_sig-tagmsg); ++ if (err) ++ goto done; ++ ++done: ++ free(id_str); ++ free(tagger); ++ return err; ++} ++ ++const struct got_error * ++got_sigs_verify_tag_ssh(char **msg, struct got_tag_object *tag, ++ const char *start_sig, const char* allowed_signers, const char* revoked, ++ int verbosity) ++{ ++ const struct got_error *error = NULL; ++ const char* argv[17]; ++ int pid, status, in_pfd[2], out_pfd[2]; ++ char* parsed_identity = NULL; ++ const char *identity; ++ char* tmppath = NULL; ++ FILE *tmpsig, *out = NULL; ++ BUF *buf; ++ int i = 0, j; ++ ++ *msg = NULL; ++ ++ error = got_opentemp_named(&tmppath, &tmpsig, ++ GOT_TMPDIR_STR "/got-tagsig"); ++ if (error) ++ goto done; ++ ++ identity = got_object_tag_get_tagger(tag); ++ parsed_identity = signer_identity(identity); ++ if (parsed_identity != NULL) ++ identity = parsed_identity; ++ ++ if (fputs(start_sig, tmpsig) == EOF) { ++ error = got_error_from_errno("fputs"); ++ goto done; ++ } ++ if (fflush(tmpsig) == EOF) { ++ error = got_error_from_errno("fflush"); ++ goto done; ++ } ++ ++ error = buf_alloc(&buf, 0); ++ if (error) ++ goto done; ++ error = got_tag_write_signed_data(buf, tag, start_sig); ++ if (error) ++ goto done; ++ ++ argv[i++] = GOT_TAG_PATH_SSH_KEYGEN; ++ argv[i++] = "-Y"; ++ argv[i++] = "verify"; ++ argv[i++] = "-f"; ++ argv[i++] = allowed_signers; ++ argv[i++] = "-I"; ++ argv[i++] = identity; ++ argv[i++] = "-n"; ++ argv[i++] = "git"; ++ argv[i++] = "-s"; ++ argv[i++] = tmppath; ++ if (revoked) { ++ argv[i++] = "-r"; ++ argv[i++] = revoked; ++ } ++ if (verbosity > 0) { ++ /* ssh(1) allows up to 3 "-v" options. */ ++ for (j = 0; j < MIN(3, verbosity); j++) ++ argv[i++] = "-v"; ++ } ++ argv[i++] = NULL; ++ assert(i <= nitems(argv)); ++ ++ if (pipe2(in_pfd, 0) == -1) { ++ error = got_error_from_errno("pipe2"); ++ goto done; ++ } ++ if (pipe2(out_pfd, 0) == -1) { ++ error = got_error_from_errno("pipe2"); ++ goto done; ++ } ++ ++ pid = fork(); ++ if (pid == -1) { ++ error = got_error_from_errno("fork"); ++ close(in_pfd[0]); ++ close(in_pfd[1]); ++ close(out_pfd[0]); ++ close(out_pfd[1]); ++ return error; ++ } else if (pid == 0) { ++ if (close(in_pfd[1]) == -1) ++ err(1, "close"); ++ if (close(out_pfd[1]) == -1) ++ err(1, "close"); ++ if (dup2(in_pfd[0], 0) == -1) ++ err(1, "dup2"); ++ if (dup2(out_pfd[0], 1) == -1) ++ err(1, "dup2"); ++ if (execv(GOT_TAG_PATH_SSH_KEYGEN, (char **const)argv) == -1) ++ err(1, "execv"); ++ abort(); /* not reached */ ++ } ++ if (close(in_pfd[0]) == -1) { ++ error = got_error_from_errno("close"); ++ goto done; ++ } ++ if (close(out_pfd[0]) == -1) { ++ error = got_error_from_errno("close"); ++ goto done; ++ } ++ if (buf_write_fd(buf, in_pfd[1]) == -1) { ++ error = got_error_from_errno("write"); ++ goto done; ++ } ++ if (close(in_pfd[1]) == -1) { ++ error = got_error_from_errno("close"); ++ goto done; ++ } ++ if (waitpid(pid, &status, 0) == -1) { ++ error = got_error_from_errno("waitpid"); ++ goto done; ++ } ++ if (!WIFEXITED(status)) { ++ error = got_error(GOT_ERR_BAD_TAG_SIGNATURE); ++ goto done; ++ } ++ ++ out = fdopen(out_pfd[1], "r"); ++ if (out == NULL) { ++ error = got_error_from_errno("fdopen"); ++ goto done; ++ } ++ error = buf_load(&buf, out); ++ if (error) ++ goto done; ++ error = buf_putc(buf, '\0'); ++ if (error) ++ goto done; ++ if (close(out_pfd[1]) == -1) { ++ error = got_error_from_errno("close"); ++ goto done; ++ } ++ out = NULL; ++ *msg = buf_get(buf); ++ if (WEXITSTATUS(status) != 0) ++ error = got_error(GOT_ERR_BAD_TAG_SIGNATURE); ++ ++done: ++ free(parsed_identity); ++ free(tmppath); ++ if (tmpsig && fclose(tmpsig) == EOF && error == NULL) ++ error = got_error_from_errno("fclose"); ++ if (out && fclose(out) == EOF && error == NULL) ++ error = got_error_from_errno("fclose"); ++ return error; ++} +blob - aa2c97552358174249a7361aba78c785626d6b7f +blob + be0d93073a8d7779e487b6a2d12bad1e6c9721d4 +--- libexec/got-read-gotconfig/got-read-gotconfig.c ++++ libexec/got-read-gotconfig/got-read-gotconfig.c +@@ -548,6 +548,24 @@ main(int argc, char *argv[]) + err = send_gotconfig_str(&ibuf, + gotconfig->author ? gotconfig->author : ""); + break; ++ case GOT_IMSG_GOTCONFIG_ALLOWEDSIGNERS_REQUEST: ++ if (gotconfig == NULL) { ++ err = got_error(GOT_ERR_PRIVSEP_MSG); ++ break; ++ } ++ err = send_gotconfig_str(&ibuf, ++ gotconfig->allowed_signers_file ? ++ gotconfig->allowed_signers_file : ""); ++ break; ++ case GOT_IMSG_GOTCONFIG_REVOKEDSIGNERS_REQUEST: ++ if (gotconfig == NULL) { ++ err = got_error(GOT_ERR_PRIVSEP_MSG); ++ break; ++ } ++ err = send_gotconfig_str(&ibuf, ++ gotconfig->revoked_signers_file ? ++ gotconfig->revoked_signers_file : ""); ++ break; + case GOT_IMSG_GOTCONFIG_REMOTES_REQUEST: + if (gotconfig == NULL) { + err = got_error(GOT_ERR_PRIVSEP_MSG); +blob - 1ce499222101a45de399bd433825c767df869d91 +blob + 504e691250732f7b2baee47695fc1794127b2adb +--- libexec/got-read-gotconfig/gotconfig.h ++++ libexec/got-read-gotconfig/gotconfig.h +@@ -1,4 +1,5 @@ + /* ++ * Copyright (c) 2022 Josh Rickmar + * Copyright (c) 2020, 2021 Tracey Emery + * Copyright (c) 2020 Stefan Sperling + * +@@ -66,6 +67,8 @@ struct gotconfig { + char *author; + struct gotconfig_remote_repo_list remotes; + int nremotes; ++ char *allowed_signers_file; ++ char *revoked_signers_file; + }; + + /* +blob - b9a0bd38cabe5d893cbbb04c482578a895a094ed +blob + 85fc623c3bd3ebda367919af6ac405ae817a88fc +--- libexec/got-read-gotconfig/parse.y ++++ libexec/got-read-gotconfig/parse.y +@@ -99,7 +99,8 @@ typedef struct { + + %token ERROR + %token REMOTE REPOSITORY SERVER PORT PROTOCOL MIRROR_REFERENCES BRANCH +-%token AUTHOR FETCH_ALL_BRANCHES REFERENCE FETCH SEND ++%token AUTHOR ALLOWED_SIGNERS REVOKED_SIGNERS FETCH_ALL_BRANCHES REFERENCE ++%token FETCH SEND + %token STRING + %token NUMBER + %type boolean portplain +@@ -113,6 +114,7 @@ grammar : /* empty */ + | grammar '\n' + | grammar author '\n' + | grammar remote '\n' ++ | grammar allowed_signers '\n' + ; + boolean : STRING { + if (strcasecmp($1, "true") == 0 || +@@ -306,6 +308,14 @@ author : AUTHOR STRING { + gotconfig.author = $2; + } + ; ++allowed_signers : ALLOWED_SIGNERS STRING { ++ gotconfig.allowed_signers_file = $2; ++ } ++ ; ++revoked_signers : REVOKED_SIGNERS STRING { ++ gotconfig.revoked_signers_file = $2; ++ } ++ ; + optnl : '\n' optnl + | /* empty */ + ; +@@ -354,6 +364,7 @@ lookup(char *s) + { + /* This has to be sorted always. */ + static const struct keywords keywords[] = { ++ {"allowed_signers", ALLOWED_SIGNERS}, + {"author", AUTHOR}, + {"branch", BRANCH}, + {"fetch", FETCH}, +@@ -364,6 +375,7 @@ lookup(char *s) + {"reference", REFERENCE}, + {"remote", REMOTE}, + {"repository", REPOSITORY}, ++ {"revoked_signers", REVOKED_SIGNERS}, + {"send", SEND}, + {"server", SERVER}, + }; +@@ -791,6 +803,8 @@ gotconfig_free(struct gotconfig *conf) + struct gotconfig_remote_repo *remote; + + free(conf->author); ++ free(conf->allowed_signers_file); ++ free(conf->revoked_signers_file); + while (!TAILQ_EMPTY(&conf->remotes)) { + remote = TAILQ_FIRST(&conf->remotes); + TAILQ_REMOVE(&conf->remotes, remote, entry); +blob - 53325e40ea937187e8814d7b18dd3a6a2f5c40f5 +blob + b39af2be74c1e13b37e5bb89219e62eed8046e23 +--- regress/cmdline/tag.sh ++++ regress/cmdline/tag.sh +@@ -257,7 +257,168 @@ test_tag_list_lightweight() { + test_done "$testroot" "$ret" + } + ++test_tag_create_ssh_signed() { ++ local testroot=`test_init tag_create` ++ local commit_id=`git_show_head $testroot/repo` ++ local tag=1.0.0 ++ local tag2=2.0.0 ++ ++ ssh-keygen -q -N '' -t ed25519 -f $testroot/id_ed25519 ++ ret=$? ++ if [ $ret -ne 0 ]; then ++ echo "ssh-keygen failed unexpectedly" ++ test_done "$testroot" "$ret" ++ return 1 ++ fi ++ touch $testroot/allowed_signers ++ echo "allowed_signers \"$testroot/allowed_signers\"" > \ ++ $testroot/repo/.git/got.conf ++ ++ # Create a signed tag based on repository's HEAD reference ++ got tag -s $testroot/id_ed25519 -m 'test' -r $testroot/repo -c HEAD \ ++ $tag > $testroot/stdout ++ ret=$? ++ if [ $ret -ne 0 ]; then ++ echo "got tag command failed unexpectedly" ++ test_done "$testroot" "$ret" ++ return 1 ++ fi ++ ++ tag_id=`got ref -r $testroot/repo -l \ ++ | grep "^refs/tags/$tag" | tr -d ' ' | cut -d: -f2` ++ echo "Created tag $tag_id" > $testroot/stdout.expected ++ cmp -s $testroot/stdout $testroot/stdout.expected ++ ret=$? ++ if [ $ret -ne 0 ]; then ++ diff -u $testroot/stdout.expected $testroot/stdout ++ test_done "$testroot" "$ret" ++ return 1 ++ fi ++ ++ # Ensure validation fails when the key is not allowed ++ echo "signature: Could not verify signature." > \ ++ $testroot/stdout.expected ++ VERIFY_STDOUT=$(got tag -r $testroot/repo -V $tag 2> $testroot/stderr) ++ ret=$? ++ echo "$VERIFY_STDOUT" | grep '^signature: ' > $testroot/stdout ++ if [ $ret -eq 0 ]; then ++ diff -u $testroot/stdout.expected $testroot/stdout ++ test_done "$testroot" "1" ++ return 1 ++ fi ++ ++ GOOD_SIG='Good "git" signature for flan_hacker@openbsd.org with ED25519 key SHA256:' ++ ++ # Validate the signature with the key allowed ++ echo -n 'flan_hacker@openbsd.org ' > $testroot/allowed_signers ++ cat $testroot/id_ed25519.pub >> $testroot/allowed_signers ++ GOT_STDOUT=$(got tag -r $testroot/repo -V $tag 2> $testroot/stderr) ++ ret=$? ++ if [ $ret -ne 0 ]; then ++ echo "got tag command failed unexpectedly" ++ diff -u $testroot/stdout.expected $testroot/stdout ++ test_done "$testroot" "$ret" ++ return 1 ++ fi ++ ++ if ! echo "$GOT_STDOUT" | grep -q "^signature: $GOOD_SIG"; then ++ echo "got tag command failed to validate signature" ++ test_done "$testroot" "1" ++ return 1 ++ fi ++ ++ # Ensure that Git recognizes and verifies the tag Got has created ++ (cd $testroot/repo && git checkout -q $tag) ++ ret=$? ++ if [ $ret -ne 0 ]; then ++ echo "git checkout command failed unexpectedly" ++ test_done "$testroot" "$ret" ++ return 1 ++ fi ++ (cd $testroot/repo && git config --local gpg.ssh.allowedSignersFile \ ++ $testroot/allowed_signers) ++ GIT_STDERR=$(cd $testroot/repo && git tag -v $tag 2>&1 1>/dev/null) ++ if ! echo "$GIT_STDERR" | grep -q "^$GOOD_SIG"; then ++ echo "git tag command failed to validate signature" ++ test_done "$testroot" "1" ++ return 1 ++ fi ++ ++ # Ensure Got recognizes the new tag ++ got checkout -c $tag $testroot/repo $testroot/wt >/dev/null ++ ret=$? ++ if [ $ret -ne 0 ]; then ++ echo "got checkout command failed unexpectedly" ++ test_done "$testroot" "$ret" ++ return 1 ++ fi ++ ++ # Create a tag based on implied worktree HEAD ref ++ (cd $testroot/wt && got tag -m 'test' $tag2 > $testroot/stdout) ++ ret=$? ++ if [ $ret -ne 0 ]; then ++ test_done "$testroot" "$ret" ++ return 1 ++ fi ++ ++ tag_id2=`got ref -r $testroot/repo -l \ ++ | grep "^refs/tags/$tag2" | tr -d ' ' | cut -d: -f2` ++ echo "Created tag $tag_id2" > $testroot/stdout.expected ++ cmp -s $testroot/stdout $testroot/stdout.expected ++ ret=$? ++ if [ $ret -ne 0 ]; then ++ diff -u $testroot/stdout.expected $testroot/stdout ++ test_done "$testroot" "$ret" ++ return 1 ++ fi ++ ++ (cd $testroot/repo && git checkout -q $tag2) ++ ret=$? ++ if [ $ret -ne 0 ]; then ++ echo "git checkout command failed unexpectedly" ++ test_done "$testroot" "$ret" ++ return 1 ++ fi ++ ++ # Attempt to create a tag pointing at a non-commit ++ local tree_id=`git_show_tree $testroot/repo` ++ (cd $testroot/wt && got tag -m 'test' -c $tree_id foobar \ ++ 2> $testroot/stderr) ++ ret=$? ++ if [ $ret -eq 0 ]; then ++ echo "git tag command succeeded unexpectedly" ++ test_done "$testroot" "1" ++ return 1 ++ fi ++ ++ echo "got: commit $tree_id: object not found" \ ++ > $testroot/stderr.expected ++ cmp -s $testroot/stderr $testroot/stderr.expected ++ ret=$? ++ if [ $ret -ne 0 ]; then ++ diff -u $testroot/stderr.expected $testroot/stderr ++ test_done "$testroot" "$ret" ++ return 1 ++ fi ++ ++ got ref -r $testroot/repo -l > $testroot/stdout ++ echo "HEAD: $commit_id" > $testroot/stdout.expected ++ echo -n "refs/got/worktree/base-" >> $testroot/stdout.expected ++ cat $testroot/wt/.got/uuid | tr -d '\n' >> $testroot/stdout.expected ++ echo ": $commit_id" >> $testroot/stdout.expected ++ echo "refs/heads/master: $commit_id" >> $testroot/stdout.expected ++ echo "refs/tags/$tag: $tag_id" >> $testroot/stdout.expected ++ echo "refs/tags/$tag2: $tag_id2" >> $testroot/stdout.expected ++ cmp -s $testroot/stdout $testroot/stdout.expected ++ ret=$? ++ if [ $ret -ne 0 ]; then ++ diff -u $testroot/stdout.expected $testroot/stdout ++ fi ++ test_done "$testroot" "$ret" ++} ++ + test_parseargs "$@" + run_test test_tag_create + run_test test_tag_list + run_test test_tag_list_lightweight ++run_test test_tag_create_ssh_signed +blob - 0215869fd1a3678fe92c416a609faf3e875f0a34 +blob + f835a2398bf16bf81771722cceeaea81aaa9423b +--- regress/fetch/Makefile ++++ regress/fetch/Makefile +@@ -4,7 +4,8 @@ PROG = fetch_test + SRCS = error.c privsep.c reference.c sha1.c object.c object_parse.c path.c \ + opentemp.c repository.c lockfile.c object_cache.c pack.c inflate.c \ + deflate.c delta.c delta_cache.c object_idset.c object_create.c \ +- fetch.c gotconfig.c dial.c fetch_test.c bloom.c murmurhash2.c ++ fetch.c gotconfig.c dial.c fetch_test.c bloom.c murmurhash2.c sigs.c \ ++ buf.c date.c + + CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib + LDADD = -lutil -lz -lm +blob - ba79d5e787ada9939dea4f62aae062cea501f845 +blob + 7379d7e77fb9e190eb44211cdf722939696a6cbe +--- tog/Makefile ++++ tog/Makefile +@@ -12,7 +12,7 @@ SRCS= tog.c blame.c commit_graph.c delta.c diff.c \ + gotconfig.c diff_main.c diff_atomize_text.c \ + diff_myers.c diff_output.c diff_output_plain.c \ + diff_output_unidiff.c diff_output_edscript.c \ +- diff_patience.c bloom.c murmurhash2.c ++ diff_patience.c bloom.c murmurhash2.c sigs.c date.c + MAN = ${PROG}.1 + + CPPFLAGS = -I${.CURDIR}/../include -I${.CURDIR}/../lib + diff --git a/pkgs/got.nix b/pkgs/got.nix new file mode 100644 index 0000000..de56c3e --- /dev/null +++ b/pkgs/got.nix @@ -0,0 +1,56 @@ +{ lib, stdenv, fetchpatch, fetchgit, bison, pkg-config, libressl, libbsd +, libuuid, libmd, zlib, ncurses, isUnstable, openssh, autoreconfHook +, sshKeyGenPath ? "/run/current-system/sw/bin/ssh-keygen" }: + +stdenv.mkDerivation rec { + pname = "got"; + rev = "a8fa2ba8469e013475c403304989843b7fc17ae8"; + version = "0.74"; + + src = fetchgit { + inherit rev; + + url = "git://git.gameoftrees.org/got-portable.git"; + sha256 = "sha256-oQofGknpCyRFyNuUZYpLcZ57JCl04wuaxM1RpIXp1LE="; + }; + + patches = [ + (fetchpatch { + url = "http://sprunge.us/sEDCV2"; + sha256 = "sha256-oondY/IMU6YMnx5+lIGpN43/tQ/tkCarmveMykQc24c="; + }) + ]; + + nativeBuildInputs = [ pkg-config libressl libbsd libmd zlib autoreconfHook ]; + + buildInputs = [ bison libressl libbsd libuuid libmd zlib ncurses ]; + + CFLAGS = ''-DGOT_TAG_PATH_SSH_KEYGEN=\"${sshKeyGenPath}\"''; + + doInstallCheck = true; + + installCheckPhase = '' + runHook preInstallCheck + test "$($out/bin/got --version)" = '${pname} ${version}' + runHook postInstallCheck + ''; + + meta = with lib; { + description = + "A version control system which prioritizes ease of use and simplicity over flexibility"; + longDescription = '' + Game of Trees (Got) is a version control system which prioritizes + ease of use and simplicity over flexibility. + + Got uses Git repositories to store versioned data. Git can be used + for any functionality which has not yet been implemented in + Got. It will always remain possible to work with both Got and Git + on the same repository. + ''; + homepage = "https://gameoftrees.org"; + license = licenses.isc; + platforms = platforms.linux; + maintainers = with maintainers; [ qbit ]; + }; +} + diff --git a/pkgs/gqrss.nix b/pkgs/gqrss.nix new file mode 100644 index 0000000..87142a3 --- /dev/null +++ b/pkgs/gqrss.nix @@ -0,0 +1,33 @@ +{ lib, buildGoModule, fetchFromGitHub, isUnstable, ... }: + +let + vendorHash = if isUnstable then + "" + else + "sha256-NIAJKq7TiMessqaohkdHy+j/vBKvMsiPgmnaiNAsGeE="; + +in with lib; +buildGoModule rec { + pname = "gqrss"; + version = "1.0.0"; + + src = fetchFromGitHub { + owner = "qbit"; + repo = pname; + rev = "v${version}"; + sha256 = "sha256-1ZGjifDgqA9yk9l0YB4rLpcvwaq9lWxDgItJ7lCVj2I="; + }; + + vendorSha256 = vendorHash; + + proxyVendor = true; + + doCheck = false; + + meta = { + description = "Simple github query tool"; + homepage = "https://github.com/qbit/gqrss"; + license = licenses.isc; + maintainers = with maintainers; [ qbit ]; + }; +} diff --git a/pkgs/icbirc.diff b/pkgs/icbirc.diff new file mode 100644 index 0000000..3b12646 --- /dev/null +++ b/pkgs/icbirc.diff @@ -0,0 +1,212 @@ +diff --git a/Makefile b/Makefile +index 60b96d5..d894cc9 100644 +--- a/Makefile ++++ b/Makefile +@@ -4,6 +4,7 @@ PROG= icbirc + SRCS= icbirc.c icb.c irc.c + MAN= icbirc.8 + +-CFLAGS+= -Wall -Werror -Wstrict-prototypes -ansi ++CFLAGS+= -Wall -Wstrict-prototypes -std=gnu99 ++LDFLAGS+= -lbsd + + .include +diff --git a/irc.c b/irc.c +index 239b7eb..2cb47c7 100644 +--- a/irc.c ++++ b/irc.c +@@ -42,7 +42,7 @@ extern void scan(const char **, char *, size_t, const char *, + const char *); + extern int sync_write(int, const char *, int); + +-static void irc_cmd(const char *, int, int); ++static void irc_cmd(char *, int, int); + + static void irc_send_pong(int, const char *); + +@@ -93,44 +93,55 @@ irc_recv(const char *buf, unsigned len, int client_fd, int server_fd) + } + + static void +-irc_cmd(const char *cmd, int client_fd, int server_fd) ++irc_cmd(char *cmd, int client_fd, int server_fd) + { +- if (!strncasecmp(cmd, "PASS ", 5)) { +- cmd += 5; +- scan(&cmd, irc_pass, sizeof(irc_pass), " ", " "); +- } else if (!strncasecmp(cmd, "USER ", 5)) { +- cmd += 5; +- scan(&cmd, irc_ident, sizeof(irc_ident), " ", " "); ++ if (!strncasecmp(cmd, "RAWICB ", 7)) { ++ icb_send_raw(server_fd, cmd + 7); ++ return; ++ } ++ ++ char *argv[10], *p; ++ int argc = 1; ++ ++ for (p = cmd, argv[0] = p; argc < 10 && (p = strchr(p, ' ')) != NULL; ++ argc++) { ++ *p = 0; ++ p++; ++ while (*p == ' ') ++ p++; ++ if (*p == ':') { ++ argv[argc] = p + 1; ++ argc++; ++ break; ++ } ++ argv[argc] = p; ++ } ++ ++ if (!strcasecmp(argv[0], "PASS")) { ++ strlcpy(irc_pass, argv[1], sizeof(irc_pass)); ++ } else if (!strcasecmp(argv[0], "USER")) { ++ strlcpy(irc_ident, argv[1], sizeof(irc_ident)); + if (!icb_logged_in && irc_nick[0] && irc_ident[0]) + icb_send_login(server_fd, irc_nick, + irc_ident, irc_pass); +- } else if (!strncasecmp(cmd, "NICK ", 5)) { +- cmd += 5; +- scan(&cmd, irc_nick, sizeof(irc_nick), " ", " "); ++ } else if (!strcasecmp(argv[0], "NICK")) { ++ strlcpy(irc_nick, argv[1], sizeof(irc_nick)); + if (icb_logged_in) + icb_send_name(server_fd, irc_nick); + else if (irc_nick[0] && irc_ident[0]) + icb_send_login(server_fd, irc_nick, + irc_ident, irc_pass); +- } else if (!strncasecmp(cmd, "JOIN ", 5)) { +- char group[128]; +- +- cmd += 5; +- if (*cmd == '#') +- cmd++; +- scan(&cmd, group, sizeof(group), " ", " "); +- icb_send_group(server_fd, group); +- } else if (!strncasecmp(cmd, "PART ", 5)) { ++ } else if (!strcasecmp(argv[0], "JOIN")) { ++ icb_send_group(server_fd, ++ argv[1] + (argv[1][0] == '#' ? 1 : 0)); ++ } else if (!strcasecmp(argv[0], "PART")) { + in_irc_channel = 0; +- } else if (!strncasecmp(cmd, "PRIVMSG ", 8) || +- !strncasecmp(cmd, "NOTICE ", 7)) { +- char dst[128]; ++ } else if (!strcasecmp(argv[0], "PRIVMSG") || ++ !strcasecmp(argv[0], "NOTICE")) { + char msg[8192]; + unsigned i, j; + +- cmd += strncasecmp(cmd, "NOTICE ", 7) ? 8 : 7; +- scan(&cmd, dst, sizeof(dst), " ", " "); +- scan(&cmd, msg, sizeof(msg), " ", ""); ++ strlcpy(msg, argv[2], sizeof(msg)); + /* strip \001 found in CTCP messages */ + i = 0; + while (msg[i]) { +@@ -141,73 +152,52 @@ irc_cmd(const char *cmd, int client_fd, int server_fd) + } else + i++; + } +- if (!strcmp(dst, irc_channel)) +- icb_send_openmsg(server_fd, +- msg + (msg[0] == ':' ? 1 : 0)); ++ if (!strcmp(argv[1], irc_channel)) ++ icb_send_openmsg(server_fd, msg); + else +- icb_send_privmsg(server_fd, dst, +- msg + (msg[0] == ':' ? 1 : 0)); +- } else if (!strncasecmp(cmd, "MODE ", 5)) { +- cmd += 5; +- if (!strcmp(cmd, irc_channel)) ++ icb_send_privmsg(server_fd, argv[1], msg); ++ } else if (!strcasecmp(argv[0], "MODE")) { ++ if (strcmp(argv[1], irc_channel)) ++ return; ++ if (argc == 2) + icb_send_names(server_fd, irc_channel); +- else if (!strncmp(cmd, irc_channel, strlen(irc_channel))) { +- cmd += strlen(irc_channel); +- if (strncmp(cmd, " +o ", 4)) { ++ else { ++ if (strcmp(argv[2], "+o")) { + printf("irc_cmd: invalid MODE args '%s'\n", +- cmd); ++ argv[2]); + return; + } +- cmd += 4; +- icb_send_pass(server_fd, cmd); ++ icb_send_pass(server_fd, argv[3]); + } +- } else if (!strncasecmp(cmd, "TOPIC ", 6)) { +- cmd += 6; +- if (strncmp(cmd, irc_channel, strlen(irc_channel))) { +- printf("irc_cmd: invalid TOPIC args '%s'\n", cmd); ++ } else if (!strcasecmp(argv[0], "TOPIC")) { ++ if (strcmp(argv[1], irc_channel)) { ++ printf("irc_cmd: invalid TOPIC channel '%s'\n", ++ argv[1]); + return; + } +- cmd += strlen(irc_channel); +- if (strncmp(cmd, " :", 2)) { +- printf("irc_cmd: invalid TOPIC args '%s'\n", cmd); +- return; +- } +- cmd += 2; +- icb_send_topic(server_fd, cmd); +- } else if (!strcasecmp(cmd, "LIST")) { ++ icb_send_topic(server_fd, argv[2]); ++ } else if (!strcasecmp(argv[0], "LIST")) { + icb_send_list(server_fd); +- } else if (!strncasecmp(cmd, "NAMES ", 6)) { +- cmd += 6; +- icb_send_names(server_fd, cmd); +- } else if (!strncasecmp(cmd, "WHOIS ", 6)) { +- cmd += 6; +- icb_send_whois(server_fd, cmd); +- } else if (!strncasecmp(cmd, "WHO ", 4)) { +- cmd += 4; +- icb_send_who(server_fd, cmd); +- } else if (!strncasecmp(cmd, "KICK ", 5)) { +- char channel[128], nick[128]; +- +- cmd += 5; +- scan(&cmd, channel, sizeof(channel), " ", " "); +- scan(&cmd, nick, sizeof(nick), " ", " "); +- if (strcmp(channel, irc_channel)) { +- printf("irc_cmd: invalid KICK args '%s'\n", cmd); ++ } else if (!strcasecmp(argv[0], "NAMES")) { ++ icb_send_names(server_fd, argv[1]); ++ } else if (!strcasecmp(argv[0], "WHOIS")) { ++ icb_send_whois(server_fd, argv[1]); ++ } else if (!strcasecmp(argv[0], "WHO")) { ++ icb_send_who(server_fd, argv[1]); ++ } else if (!strcasecmp(argv[0], "KICK")) { ++ if (strcmp(argv[1], irc_channel)) { ++ printf("irc_cmd: invalid KICK args '%s'\n", argv[1]); + return; + } +- icb_send_boot(server_fd, nick); +- } else if (!strncasecmp(cmd, "PING ", 5)) { ++ icb_send_boot(server_fd, argv[2]); ++ } else if (!strcasecmp(argv[0], "PING")) { + icb_send_noop(server_fd); +- cmd += 5; +- irc_send_pong(client_fd, cmd); +- } else if (!strncasecmp(cmd, "RAWICB ", 7)) { +- cmd += 7; +- icb_send_raw(server_fd, cmd); +- } else if (!strncasecmp(cmd, "QUIT ", 5)) { ++ irc_send_pong(client_fd, argv[1]); ++ } else if (!strcasecmp(argv[0], "QUIT")) { + printf("client QUIT\n"); + terminate_client = 1; + } else +- printf("irc_cmd: unknown cmd '%s'\n", cmd); ++ printf("irc_cmd: unknown command '%s'\n", argv[0]); + } + + void diff --git a/pkgs/icbirc.nix b/pkgs/icbirc.nix new file mode 100644 index 0000000..972f9e0 --- /dev/null +++ b/pkgs/icbirc.nix @@ -0,0 +1,27 @@ +{ lib, stdenv, fetchpatch, fetchurl, pkgs, ... }: + +stdenv.mkDerivation rec { + pname = "icbirc"; + version = "2.1"; + + src = fetchurl { + url = "http://www.benzedrine.ch/icbirc-2.1.tar.gz"; + sha256 = "sha256-aDk0TZPABNqX7Gu12AWh234Kee/DhwRFeIBDYnFiu7E="; + }; + + patches = [ ./icbirc.diff ]; + + buildInputs = with pkgs; [ libbsd bsdbuild bmake ]; + + meta = with lib; { + description = "proxy IRC client with ICB server"; + longDescription = '' + icbirc is a proxy that allows to connect an IRC client to an ICB server. + ''; + homepage = "http://www.benzedrine.ch/icbirc.html"; + license = licenses.bsd2; + platforms = platforms.linux; + maintainers = with maintainers; [ qbit ]; + }; +} + diff --git a/pkgs/mcchunkie.nix b/pkgs/mcchunkie.nix new file mode 100644 index 0000000..46b82a8 --- /dev/null +++ b/pkgs/mcchunkie.nix @@ -0,0 +1,35 @@ +{ lib, buildGo118Module, fetchFromGitHub, isUnstable, ... }: + +let + vendorHash = if isUnstable then + "" + else + "sha256-d8YeLD/BQAB6IC4jvBke9EIKAe+7/MnPgVYztqjU5c4="; + +in with lib; +buildGo118Module rec { + pname = "mcchunkie"; + version = "1.0.8"; + + src = fetchFromGitHub { + owner = "qbit"; + repo = pname; + rev = "v${version}"; + sha256 = "sha256-UNPv9QXFJeNx+3RleseNVSKBZGNc3eiMsEKnfIVyoeA="; + }; + + vendorSha256 = vendorHash; + + ldflags = [ "-X suah.dev/mcchunkie/plugins.version=${version}" ]; + + proxyVendor = true; + + doCheck = false; + + meta = { + description = "Matrix Bot"; + homepage = "https://github.com/qbit/mcchunkie"; + license = licenses.mit; + maintainers = with maintainers; [ qbit ]; + }; +} diff --git a/pkgs/mudita-center.nix b/pkgs/mudita-center.nix new file mode 100644 index 0000000..941d410 --- /dev/null +++ b/pkgs/mudita-center.nix @@ -0,0 +1,37 @@ +{ lib, fetchurl, appimageTools, makeDesktopItem, isUnstable, desktop-file-utils +, ... }: + +let + name = "mudita-center"; + version = "1.3.0"; + + src = fetchurl { + name = "mudita-center.AppImage"; + url = + "https://github.com/mudita/mudita-center/releases/download/${version}/Mudita-Center.AppImage"; + sha256 = "1cqrrs5ycl5lrla8mprx443dpiz99a63f4i3da43vxh1xxl0ki4n"; + }; + + appimageContents = appimageTools.extract { inherit name src; }; + +in appimageTools.wrapType1 rec { + inherit name src; + + extraInstallCommands = '' + cp -r ${appimageContents}/* $out + cd $out + chmod -R +w $out + + mv "Mudita Center" $out/${name} + + # TODO: + #${desktop-file-utils}/bin/desktop-file-install --dir $out/share/applications \ + # --set-key Exec --set-value ${name} "Mudita Center.desktop" + + mv usr/share/icons share + + rm usr/lib/* AppRun *.desktop + ''; + + #extraPkgs = pkgs: with pkgs; [ ]; +} diff --git a/pkgs/nheko.nix b/pkgs/nheko.nix new file mode 100644 index 0000000..2e279f0 --- /dev/null +++ b/pkgs/nheko.nix @@ -0,0 +1,28 @@ +{ lib, fetchurl, stdenv, undmg, isUnstable }: + +stdenv.mkDerivation rec { + pname = "nheko"; + version = "0.10.0"; + + src = fetchurl { + url = + "https://github.com/Nheko-Reborn/nheko/releases/download/v${version}/nheko-v${version}.dmg"; + hash = "sha256-t7evlvb+ueJZhtmt4KrOeXv2BZV8/fY4vj4GAmoCR2w="; + }; + + nativeBuildInputs = [ undmg ]; + + sourceRoot = "."; + + installPhase = '' + mkdir -p $out/Applications + cp -a Nheko.app $out/Applications/ + ''; + + meta = { + description = "Desktop client for Matrix using Qt and C++17"; + homepage = "https://github.com/Nheko-Reborn/nheko"; + license = lib.licenses.gpl3; + platforms = lib.platforms.darwin; + }; +} diff --git a/pkgs/secretive.nix b/pkgs/secretive.nix new file mode 100644 index 0000000..4159897 --- /dev/null +++ b/pkgs/secretive.nix @@ -0,0 +1,28 @@ +{ lib, fetchurl, stdenv, unzip, isUnstable }: + +stdenv.mkDerivation rec { + pname = "secretive"; + version = "2.2.0"; + + src = fetchurl { + name = "Secretive-${version}.zip"; + url = + "https://github.com/maxgoedjen/secretive/releases/download/v${version}/Secretive.zip"; + hash = "sha256-gjB8bevzbgYZ1GtAVMK+IBp9eP+Y79s8RhK/sdg7AI8="; + }; + + buildInputs = [ unzip ]; + + installPhase = '' + mkdir -p $out/Applications + cp -R ../*.app $out/Applications + ''; + + meta = { + description = + "Secretive is an app for storing and managing SSH keys in the Secure Enclave. It is inspired by the sekey project, but rewritten in Swift with no external dependencies and with a handy native management app."; + homepage = "https://github.com/maxgoedjen/secretive"; + license = lib.licenses.mit; + platforms = lib.platforms.darwin; + }; +} diff --git a/services/config-manager.nix b/services/config-manager.nix new file mode 100644 index 0000000..2833c54 --- /dev/null +++ b/services/config-manager.nix @@ -0,0 +1,147 @@ +{ config, lib, pkgs, ... }: +with lib; +let + cfgMgr = config.configManager; + cfgRouter = config.configManager.router; + pfConf = pkgs.writeTextFile { + name = "pf.conf"; + text = '' + # Auto generated pf.conf for ${cfgRouter.hostName} + + table { 0.0.0.0/8 10.0.0.0/8 127.0.0.0/8 169.254.0.0/16 \ + 172.16.0.0/12 192.0.0.0/24 192.0.2.0/24 224.0.0.0/3 \ + 192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 \ + 203.0.113.0/24 } + # Tables defined in `extraTables`; + ${cfgRouter.extraTables} + + set block-policy drop + set loginterface egress + set skip on lo + set optimization aggressive + + match in all scrub (no-df random-id max-mss 1440) + + match out on egress inet from !(egress:network) to any nat-to (egress:0) + + block out on vlan10 from !vlan10:network + block out on vlan6 from !vlan6:network + + antispoof quick for { egress, em, vlan, wg } + + block in quick on egress from to any + block return out quick on egress from any to + block all + + pass out quick inet + + #pass in on { em1, vlan2, vlan5, vlan6, vlan10, vlan11, wg0 } inet + pass in on { vlan20, vlan2, vlan5, vlan6, vlan10, vlan11, wg0 } inet + + ${optionalString cfgRouter.pfAllowUnifi '' + # cfgRouter.pfAllowUnifi.enabled = true; + pass in on { em1 } inet + pass proto tcp from em1:network to vlan5:network + ''} + + pass in on egress proto udp from any to port 7121 + + pass proto tcp from vlan20:network to vlan5:network + pass proto tcp from wg0:network to vlan5:network + + pass in on egress inet proto tcp from any to (egress) port { 80, 443, 2222 } rdr-to 10.20.30.15 + pass in log proto tcp from vlan5:network to (egress) port 2222 divert-to 127.0.0.1 port 2222 + pass in log proto tcp from vlan5:network to (egress) port 443 divert-to 127.0.0.1 port 443 + pass in log proto tcp from vlan5:network to (egress) port 80 divert-to 127.0.0.1 port 80 + + anchor "relayd/*" + ''; + }; + + interfaceOptions = mkOptionType { name = "interface text"; }; + + interfaceFiles = mapAttrs' (name: value: + nameValuePair "configManager/router/hostname.${name}" { + text = value.text + "\n"; + }) cfgRouter.interfaces; + +in { + options = { + configManager = { + + enable = lib.mkEnableOption "Manage configurations for non-nix machines."; + + router = { + enable = lib.mkEnableOption "Manage an OpenBSD router."; + hostName = mkOption { + type = types.str; + description = '' + Host to sync router configs with. + ''; + }; + + extraTables = mkOption { + type = types.lines; + default = ""; + description = '' + Extra pf.conf tables to add to the generated pf.conf. + ''; + }; + + services = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "dhcpd" "unbound" ]; + description = '' + Services to run on the router (rcctl enable XXX, rcctl start XXX). + ''; + }; + + keepClean = mkOption { + type = types.bool; + default = true; + description = '' + Keep host configuration clean. This means any non-managed hostname.if files will be + removed, non-managed services will be stopped and disabled, non-managed packages will + be removed.. etc. + ''; + }; + + interfaces = mkOption { + default = { }; + type = types.attrsOf interfaceOptions; + description = '' + Interfaces to create hostname.if files for. + ''; + example = literalExpression '' + { + em0 = { + text = "inet autoconf inet6 autoconf"; + }; + vlan1 { + text = "inet 10.12.0.1 255.255.255.0 10.12.0.255 vnetid 1 parent em1 up"; + }; + } + ''; + }; + + pfAllowUnifi = mkOption { + type = types.bool; + description = '' + Whether to allow the Ubiquiti Unifi stuff to have access to the greater internet. + ''; + }; + }; + }; + }; + + config = lib.mkIf cfgMgr.enable { + environment.etc = { + "configManager/router/pf.conf".text = builtins.readFile pfConf; + "configManager/router/managed_interfaces".text = + (concatMapStringsSep "\n") (h: "hostname.${h}") + (builtins.attrNames config.configManager.router.interfaces) + "\n"; + } // interfaceFiles; + }; +} + diff --git a/services/default.nix b/services/default.nix new file mode 100644 index 0000000..03e69b4 --- /dev/null +++ b/services/default.nix @@ -0,0 +1,4 @@ +{ config, lib, pkgs, ... }: +with lib; { + imports = [ ./ssh-fido-agent.nix ./config-manager.nix ]; +} diff --git a/services/ssh-fido-agent.nix b/services/ssh-fido-agent.nix new file mode 100644 index 0000000..f5ed9cb --- /dev/null +++ b/services/ssh-fido-agent.nix @@ -0,0 +1,68 @@ +{ config, lib, pkgs, ... }: +let + perl = "${pkgs.perl}/bin/perl"; + sshAdd = "${pkgs.openssh}/bin/ssh-add"; + pKill = "${pkgs.procps}/bin/pkill"; + awk = "${pkgs.gawk}/bin/awk"; + + # fido-add-device is started by a systemd unit. It runs continuously waiting for a USR1 signal + # that is triggered by inserting a Yubikey. Once it receives the signal, it executes 'ssh-add -K' + # which when run without a terminal will use SSH_ASKPASS to prompt the user for the unlock + # phrase for their YK FIDO setup. + fidoAddDevice = pkgs.writeScriptBin "fido-add-device" '' + #!${perl} + + use strict; + use warnings; + + $ENV{'SSH_AUTH_SOCK'} = "$ENV{'XDG_RUNTIME_DIR'}/ssh-agent"; + $ENV{'DISPLAY'} = `systemctl --user show-environment | ${awk} -F= '/^DISPLAY/ {print \$NF}'`; + + $SIG{USR1} = sub { system("${sshAdd}", "-K") }; + + while (1) { + sleep; + } + ''; + + # fido-send-sig is called by a udev rule when a YK is attached. It sends SIGUSR1 to fido-add-device. + fidoSendSig = pkgs.writeScriptBin "fido-send-sig" '' + #! ${pkgs.runtimeShell} -e + + ${pKill} -USR1 -xf "${perl} ${fidoAddDevice}/bin/fido-add-device" + ''; + + # my-ssh-askpass-wrapper wraps programs.ssh.askPassword in order to supply user-specific environment + # variables. + # TODO: replace this with makeWrapper + askPassWrapper = pkgs.writeScript "my-ssh-askpass-wrapper" '' + #! ${pkgs.runtimeShell} -e + export DISPLAY="$(systemctl --user show-environment | ${awk} -F= '/^DISPLAY/ {print $NF}')" + export SSH_AUTH_SOCK="$(echo $XDG_RUNTIME_DIR/ssh-agent)"; + exec ${config.programs.ssh.askPassword} "$@" + ''; +in { + options = { + sshFidoAgent = { + enable = lib.mkEnableOption "Add FIDO keys to ssh-agent when attached."; + }; + }; + + config = lib.mkIf config.sshFidoAgent.enable { + environment.systemPackages = with pkgs; [ fidoAddDevice ]; + systemd.user.services.sshfidoagent = { + script = '' + ${fidoAddDevice}/bin/fido-add-device + ''; + wantedBy = [ "graphical-session.target" ]; + partOf = [ "graphical-session.target" ]; + after = [ "graphical-session.target" ]; + environment.DISPLAY = "fake"; + environment.SSH_ASKPASS = askPassWrapper; + #serviceConfig = { Restart = "on-failure"; }; + }; + services.udev.extraRules = '' + SUBSYSTEM=="hidraw", ACTION=="add", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0407|0402", RUN+="${fidoSendSig}/bin/fido-send-sig" + ''; + }; +} diff --git a/system/nix-config.nix b/system/nix-config.nix new file mode 100644 index 0000000..e5d99a3 --- /dev/null +++ b/system/nix-config.nix @@ -0,0 +1,22 @@ +{ config, pkgs, lib, isUnstable, ... }: + +let + nixOptions = { + gc = { + automatic = true; + dates = "daily"; + options = "--delete-older-than 10d"; + }; + + # Enable flakes + package = pkgs.nixUnstable; + extraOptions = '' + experimental-features = nix-command flakes + ''; + }; +in { + nix = if isUnstable then + { settings.auto-optimise-store = true; } // nixOptions + else + { autoOptimiseStore = true; } // nixOptions; +} diff --git a/system/nix-lockdown.nix b/system/nix-lockdown.nix new file mode 100644 index 0000000..9d1b1e4 --- /dev/null +++ b/system/nix-lockdown.nix @@ -0,0 +1,25 @@ +{ config, lib, isUnstable, ... }: +with lib; { + options = { + nixLockdown = { + enable = mkOption { + description = "Lockdown Nix"; + default = true; + example = true; + type = lib.types.bool; + }; + }; + }; + config = mkIf config.nixLockdown.enable { + nix = if isUnstable then { + settings.sandbox = true; + settings.trusted-users = [ "@wheel" ]; + settings.allowed-users = [ "root" "qbit" ]; + } else { + allowedUsers = [ "@wheel" ]; + trustedUsers = [ "root" "qbit" ]; + useSandbox = true; + }; + + }; +} diff --git a/system/update.nix b/system/update.nix new file mode 100644 index 0000000..0c96c44 --- /dev/null +++ b/system/update.nix @@ -0,0 +1,20 @@ +{ config, lib, ... }: +with lib; { + options = { + autoUpdate = { + enable = mkOption { + description = "Enable Auto Update"; + default = true; + example = true; + type = lib.types.bool; + }; + }; + }; + + config = mkIf config.autoUpdate.enable { + system.autoUpgrade = { + enable = true; + allowReboot = false; + }; + }; +} diff --git a/users/default.nix b/users/default.nix new file mode 100644 index 0000000..d5f3c4c --- /dev/null +++ b/users/default.nix @@ -0,0 +1,51 @@ +{ config, lib, pkgs, isUnstable, ... }: + +with lib; + +let + userBase = { + shell = pkgs.zsh; + openssh.authorizedKeys.keys = config.myconf.hwPubKeys; + }; + goVersion = pkgs.go_1_18; +in { + options = { + defaultUsers = { + enable = mkOption { + description = "Enable regular set of users"; + default = true; + example = true; + type = lib.types.bool; + }; + }; + }; + + config = mkIf config.defaultUsers.enable { + users.users.root = userBase; + users.users.qbit = userBase // { + isNormalUser = true; + description = "Aaron Bieber"; + home = "/home/qbit"; + extraGroups = [ "wheel" ]; + }; + + environment.systemPackages = + if isUnstable then [ goVersion pkgs.yash ] else [ goVersion ]; + + programs.ssh = { + startAgent = true; + agentTimeout = "100m"; + extraConfig = '' + VerifyHostKeyDNS yes + AddKeysToAgent confirm 90m + CanonicalizeHostname always + + Host * + controlmaster auto + controlpath /tmp/ssh-%r@%h:%p + + Include /home/qbit/.ssh/host_config + ''; + }; + }; +}