├── .gitignore ├── LICENSE ├── flexo ├── aarch64-netboot.nix ├── bgp.nix ├── default.nix ├── everyaws.nix ├── github-webhook.nix ├── grahams-websites.nix ├── gsc.io.nix ├── hardware.nix ├── hound.nix ├── hound │ ├── 0001-Fail-to-start-if-any-repos-fail-to-index.patch │ ├── 0002-Custom-branch-specifier-PR-275.patch │ ├── 0003-PR-275-p1-Replace-master-in-the-default-base-URL-with-a-rev.patch │ ├── hound.json │ ├── open-search.xml │ └── update-hound.py ├── irc.nix ├── ircbot.nix ├── nix-channel-monitor │ ├── changes.sh │ └── default.nix ├── periodic-reminders.nix ├── periodic-reminders.py ├── r13y.nix ├── rabbitmq.nix └── wireguard.nix ├── intake.sh ├── intake ├── default.nix ├── flexo.json ├── lord-nibbler.json └── ogden.json ├── keys ├── elazar.keys ├── grahamc.keys ├── kylechristensen.keys └── update-keys.sh ├── kif ├── buildkite.nix ├── default.nix ├── dns.nix ├── dns.sh ├── hardware.nix └── vault.nix ├── lord-nibbler ├── default.nix ├── hardware.nix ├── pxe-image.nix ├── router.nix └── webroot │ └── .gitignore ├── modules ├── default.nix ├── learn │ ├── default.nix │ ├── learn.py │ └── service.nix ├── ofborg.nix ├── role.nix ├── standard │ └── default.nix └── wireguard.nix ├── network.nix ├── ogden ├── default.nix ├── dns.nix ├── dns.sh ├── hardware.nix ├── hydra-machine-status.py ├── hydra-queue-status.py ├── prometheus.nix ├── rtl.sh ├── sdr.nix ├── smartmon.sh └── wireguard.nix ├── shell.nix └── zoidberg ├── default-vhost-config.nix ├── default.nix ├── gcofborgpkg.nix ├── micro-ci.nix ├── nix └── webroot │ └── index.html ├── packet-type-0.nix ├── queue-monitor ├── index.html ├── prometheus.php └── stats.php ├── url-shortener-root └── index.php └── wireguard.nix /.gitignore: -------------------------------------------------------------------------------- 1 | .bash_hist 2 | secrets 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Graham Christensen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /flexo/aarch64-netboot.nix: -------------------------------------------------------------------------------- 1 | { secrets }: 2 | { config, pkgs, ... }: 3 | { 4 | systemd.tmpfiles.rules = [ 5 | "d ${config.services.nginx.virtualHosts."netboot.gsc.io".root} 0755 netboot nginx" 6 | ]; 7 | 8 | networking.firewall.allowedTCPPorts = [ 61616 ]; # nc/openssl recv 9 | 10 | users.users.netboot = { 11 | description = "Netboot"; 12 | group = "netboot"; 13 | uid = 406; 14 | openssh.authorizedKeys.keyFiles = [ secrets.aarch64.public ]; 15 | shell = pkgs.bash; 16 | }; 17 | users.groups.netboot.gid = 406; 18 | 19 | services.nginx.virtualHosts = { 20 | "netboot.gsc.io" = { 21 | root = "/var/lib/nginx/netboot/netboot.gsc.io"; 22 | #enableACME = true; 23 | #forceSSL = true; 24 | }; 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /flexo/bgp.nix: -------------------------------------------------------------------------------- 1 | { secrets }: 2 | { 3 | boot.kernel.sysctl."net.ipv4.forwarding" = 1; 4 | networking.interfaces.lo.ipv4.addresses = [ { 5 | # BGP ^.^ 6 | address = "147.75.96.102"; 7 | prefixLength = 32; 8 | } ]; 9 | services.bird = { 10 | enable = true; 11 | config = '' 12 | filter packetdns { 13 | # IPs to announce (the elastic ip in our case) 14 | # Doesn't have to be /32. Can be lower 15 | if net = 147.75.96.102/32 then accept; 16 | } 17 | 18 | # your (Private) bond0 IP below here 19 | router id 10.100.5.3; 20 | protocol direct { 21 | interface "lo"; # Restrict network interfaces it works with 22 | } 23 | protocol kernel { 24 | # learn; # Learn all alien routes from the kernel 25 | persist; # Don't remove routes on bird shutdown 26 | scan time 20; # Scan kernel routing table every 20 seconds 27 | import all; # Default is import all 28 | export all; # Default is export none 29 | # kernel table 5; # Kernel table to synchronize with (default: main) 30 | } 31 | 32 | # This pseudo-protocol watches all interface up/down events. 33 | protocol device { 34 | scan time 10; # Scan interfaces every 10 seconds 35 | } 36 | 37 | # your default gateway IP below here 38 | protocol bgp { 39 | export filter packetdns; 40 | local as 65000; 41 | neighbor 10.100.5.2 as 65530; 42 | password "${secrets.zoidberg_bgp_password}"; 43 | } 44 | ''; 45 | }; 46 | 47 | } 48 | -------------------------------------------------------------------------------- /flexo/default.nix: -------------------------------------------------------------------------------- 1 | { secrets }: 2 | { config, lib, ... }: 3 | { 4 | imports = [ 5 | ./ircbot.nix 6 | ./everyaws.nix 7 | ./periodic-reminders.nix 8 | ./hardware.nix 9 | ./hound.nix 10 | ./grahams-websites.nix 11 | (import ./rabbitmq.nix { inherit secrets; }) 12 | ./wireguard.nix 13 | ./github-webhook.nix 14 | (import ./nix-channel-monitor { inherit secrets; }) 15 | (import ./r13y.nix { inherit secrets; }) 16 | (import ./aarch64-netboot.nix { inherit secrets; }) 17 | ./irc.nix 18 | ./gsc.io.nix 19 | (import ./bgp.nix { inherit secrets; }) 20 | ./weather.nix 21 | ]; 22 | 23 | options.security.acme.certs = lib.mkOption { 24 | type = lib.types.attrsOf (lib.types.submodule { 25 | config.email = lib.mkDefault "graham@grahamc.com"; 26 | }); 27 | }; 28 | 29 | config = { 30 | services.nginx = { 31 | logError = "syslog:server=unix:/dev/log"; 32 | 33 | appendHttpConfig = '' 34 | log_format combined_host '$host $remote_addr - $remote_user [$time_local] ' 35 | '"$request" $status $bytes_sent ' 36 | '"$http_referer" "$http_user_agent" "$gzip_ratio"'; 37 | 38 | access_log syslog:server=unix:/dev/log combined_host; 39 | ''; 40 | }; 41 | 42 | services.phpfpm.pools.main = { 43 | listen = "/run/php-fpm.sock"; 44 | user = "nginx"; 45 | group = "nginx"; 46 | extraConfig = '' 47 | listen.owner = nginx 48 | listen.group = nginx 49 | listen.mode = 0600 50 | user = nginx 51 | pm = dynamic 52 | pm.max_children = 75 53 | pm.start_servers = 10 54 | pm.min_spare_servers = 5 55 | pm.max_spare_servers = 20 56 | pm.max_requests = 500 57 | catch_workers_output = yes 58 | ''; 59 | }; 60 | 61 | services.znapzend = { 62 | enable = true; 63 | autoCreation = true; 64 | pure = true; 65 | zetup.npool = { 66 | enable = true; 67 | plan = "15min=>5min,4hour=>15min,2day=>1hour,4day=>1day,3week=>1week"; 68 | recursive = true; 69 | timestampFormat = "%Y-%m-%d--%H%M%SZ"; 70 | destinations.ogden = { 71 | plan = "1hour=>5min,4day=>1hour,1week=>1day,1year=>1week,10year=>1month"; 72 | host = "ogden"; 73 | dataset = "mass/${config.networking.hostName}"; 74 | }; 75 | }; 76 | }; 77 | }; 78 | } 79 | -------------------------------------------------------------------------------- /flexo/everyaws.nix: -------------------------------------------------------------------------------- 1 | { pkgs, config, ... }: 2 | { 3 | systemd = { 4 | services = { 5 | everyaws = { 6 | after = [ "network.target" "network-online.target" ]; 7 | wants = [ "network-online.target" ]; 8 | path = [ 9 | (pkgs.python3.withPackages (ps: [ ps.tweepy ])) 10 | ]; 11 | 12 | serviceConfig = { 13 | User = "grahamc"; 14 | Group = "users"; 15 | Type = "oneshot"; 16 | PrivateTmp = true; 17 | WorkingDirectory = "/home/grahamc/everyaws"; 18 | }; 19 | 20 | script = '' 21 | ./tweet.sh 22 | ''; 23 | }; 24 | }; 25 | 26 | timers = { 27 | everyaws = { 28 | description = "Announces a new AWS service"; 29 | wantedBy = [ "timers.target" ]; 30 | partOf = [ "everyaws.service" ]; 31 | enable = true; 32 | timerConfig = { 33 | OnCalendar = "*:0/15"; 34 | Unit = "everyaws.service"; 35 | Persistent = "yes"; 36 | AccuracySec = "1m"; 37 | RandomizedDelaySec = "30s"; 38 | }; 39 | }; 40 | }; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /flexo/github-webhook.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | let 3 | vhostPHPLocations = root: { 4 | "/" = { 5 | index = "index.php index.html"; 6 | 7 | extraConfig = '' 8 | try_files $uri $uri/ /index.php$is_args$args; 9 | ''; 10 | }; 11 | 12 | "~ \.php$" = { 13 | extraConfig = '' 14 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 15 | fastcgi_pass unix:/run/php-fpm.sock; 16 | fastcgi_index index.php; 17 | fastcgi_param SCRIPT_FILENAME ${root}/$fastcgi_script_name; 18 | include ${pkgs.nginx}/conf/fastcgi_params; 19 | ''; 20 | }; 21 | }; 22 | in { 23 | services.nginx.enable = true; 24 | services.nginx.virtualHosts."webhook.nix.gsc.io" = let 25 | src = import ./../../ircbot/ofborg {}; 26 | in { 27 | root = "${src.ofborg.php}/web"; 28 | enableACME = true; 29 | forceSSL = true; 30 | 31 | extraConfig = '' 32 | rewrite ^/(\d+)$ index.php?n=$1 last; 33 | ''; 34 | 35 | locations = (vhostPHPLocations "${src.ofborg.php}/web"); 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /flexo/grahams-websites.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | networking.firewall.allowedTCPPorts = [ 80 443 ]; 4 | 5 | services.nginx = { 6 | enable = true; 7 | 8 | virtualHosts = { 9 | "grahamc.com" = { 10 | enableACME = true; 11 | forceSSL = true; 12 | root = pkgs.callPackage ../../grahamc.github.com {}; 13 | locations."/" = { 14 | index = "index.html index.xml"; 15 | tryFiles = "$uri $uri/ $uri.html $uri.xml =404"; 16 | }; 17 | }; 18 | 19 | "docbook.rocks" = rec { 20 | enableACME = true; 21 | forceSSL = true; 22 | root = pkgs.callPackage ../../docbook.rocks {}; 23 | locations."/" = { 24 | index = "index.html index.xml"; 25 | tryFiles = "$uri $uri/ $uri.html $uri.xml =404"; 26 | }; 27 | 28 | extraConfig = '' 29 | etag off; 30 | add_header ETag ${builtins.replaceStrings ["/nix/store/"] [""] (builtins.toString root)}; 31 | add_header Last-Modified ""; 32 | ''; 33 | }; 34 | 35 | "www.docbook.rocks" = { 36 | enableACME = true; 37 | forceSSL = true; 38 | globalRedirect = "docbook.rocks"; 39 | }; 40 | 41 | "www.grahamc.com" = { 42 | enableACME = true; 43 | forceSSL = true; 44 | globalRedirect = "grahamc.com"; 45 | }; 46 | }; 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /flexo/gsc.io.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | { 3 | systemd.tmpfiles.rules = [ 4 | "d ${config.services.nginx.virtualHosts."gsc.io".root} 0755 grahamc nginx" 5 | "L ${config.users.users.grahamc.home}/gsc.io - - - - ${config.services.nginx.virtualHosts."gsc.io".root}" 6 | ]; 7 | 8 | services.nginx.virtualHosts = { 9 | "gsc.io" = { 10 | root = "/var/lib/nginx/grahamc/gsc.io"; 11 | #enableACME = true; 12 | #forceSSL = true; 13 | }; 14 | 15 | "www.gsc.io" = { 16 | #enableACME = true; 17 | #forceSSL = true; 18 | globalRedirect = "gsc.io"; 19 | }; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /flexo/hardware.nix: -------------------------------------------------------------------------------- 1 | { lib , ... }: { 2 | 3 | services.openssh.enable = true; 4 | 5 | boot.loader.grub.devices = [ "/dev/sda" ]; 6 | boot.loader.grub.extraConfig = '' 7 | serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1 8 | terminal_output serial console 9 | terminal_input serial console 10 | ''; 11 | 12 | networking.hostId = "ffa10674"; 13 | 14 | nixpkgs.config.allowUnfree = true; 15 | 16 | boot.initrd.availableKernelModules = [ 17 | "ehci_pci" "ahci" "usbhid" "sd_mod" 18 | ]; 19 | 20 | boot.kernelModules = [ "dm_multipath" "dm_round_robin" "ipmi_watchdog" "kvm-intel" ]; 21 | boot.kernelParams = [ "console=ttyS1,115200n8" ]; 22 | boot.extraModulePackages = [ ]; 23 | 24 | networking.hostName = "flexo.gsc.io"; 25 | networking.domain = "gsc.io"; 26 | networking.dhcpcd.enable = false; 27 | networking.defaultGateway = { 28 | address = "147.75.105.136"; 29 | interface = "bond0"; 30 | }; 31 | networking.defaultGateway6 = { 32 | address = "2604:1380:0:d00::2"; 33 | interface = "bond0"; 34 | }; 35 | networking.nameservers = [ 36 | "147.75.207.207" 37 | "147.75.207.208" 38 | ]; 39 | 40 | networking.bonds.bond0 = { 41 | driverOptions = { 42 | mode = "balance-tlb"; 43 | xmit_hash_policy = "layer3+4"; 44 | downdelay = "200"; 45 | updelay = "200"; 46 | miimon = "100"; 47 | }; 48 | 49 | interfaces = [ 50 | "enp0s20f0" "enp0s20f1" 51 | ]; 52 | }; 53 | networking.interfaces.bond0 = { 54 | useDHCP = false; 55 | 56 | ipv4 = { 57 | routes = [ 58 | { 59 | address = "10.0.0.0"; 60 | prefixLength = 8; 61 | via = "10.100.5.2"; 62 | } 63 | ]; 64 | addresses = [ 65 | { 66 | address = "147.75.105.137"; 67 | prefixLength = 31; 68 | } 69 | { 70 | address = "10.100.5.3"; 71 | prefixLength = 31; 72 | } 73 | ]; 74 | }; 75 | 76 | ipv6 = { 77 | addresses = [ 78 | { 79 | address = "2604:1380:0:d00::3"; 80 | prefixLength = 127; 81 | } 82 | ]; 83 | }; 84 | }; 85 | boot.loader.grub.enable = true; 86 | boot.loader.grub.version = 2; 87 | system.stateVersion = "19.03"; # Did you read the comment? 88 | 89 | 90 | fileSystems."/" = { 91 | device = "npool/root"; 92 | fsType = "zfs"; 93 | }; 94 | 95 | fileSystems."/home" = { 96 | device = "npool/home"; 97 | fsType = "zfs"; 98 | }; 99 | 100 | fileSystems."/nix" = { 101 | device = "npool/nix"; 102 | fsType = "zfs"; 103 | }; 104 | 105 | swapDevices = [ 106 | { device = "/dev/disk/by-uuid/7ea56025-7807-4241-831c-87972e180778"; } 107 | ]; 108 | 109 | nix.maxJobs = lib.mkDefault 4; 110 | } 111 | -------------------------------------------------------------------------------- /flexo/hound.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: { 2 | networking.firewall.allowedTCPPorts = [ 80 443 ]; 3 | services.nginx = { 4 | enable = true; 5 | 6 | virtualHosts."search.nix.gsc.io" = { 7 | 8 | enableACME = true; 9 | forceSSL = true; 10 | locations = { 11 | "=/open_search.xml".alias = "${./hound/open-search.xml}"; 12 | "/".proxyPass = "http://127.0.0.1:6080/"; 13 | }; 14 | }; 15 | }; 16 | 17 | services.hound = { 18 | enable = true; 19 | listen = "127.0.0.1:6080"; 20 | config = builtins.readFile ./hound/hound.json; 21 | package = pkgs.hound.overrideAttrs (x: { 22 | patches = [ 23 | ./hound/0001-Fail-to-start-if-any-repos-fail-to-index.patch 24 | ./hound/0002-Custom-branch-specifier-PR-275.patch 25 | ./hound/0003-PR-275-p1-Replace-master-in-the-default-base-URL-with-a-rev.patch 26 | ]; 27 | }); 28 | }; 29 | 30 | systemd.services.hound.serviceConfig = { 31 | Restart = "always"; 32 | RestartSec = 5; 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /flexo/hound/0001-Fail-to-start-if-any-repos-fail-to-index.patch: -------------------------------------------------------------------------------- 1 | From f976792420843bd079cea3aa85e70cc2cdbe98c2 Mon Sep 17 00:00:00 2001 2 | From: Graham Christensen 3 | Date: Sun, 8 Oct 2017 10:59:05 -0400 4 | Subject: [PATCH] Fail to start if any repos fail to index 5 | 6 | --- 7 | cmds/houndd/main.go | 2 +- 8 | 1 file changed, 1 insertion(+), 1 deletion(-) 9 | 10 | diff --git a/cmds/houndd/main.go b/cmds/houndd/main.go 11 | index 60152ca..dfba0fc 100644 12 | --- a/cmds/houndd/main.go 13 | +++ b/cmds/houndd/main.go 14 | @@ -132,7 +132,7 @@ func main() { 15 | log.Panic(err) 16 | } 17 | if !ok { 18 | - info_log.Println("Some repos failed to index, see output above") 19 | + info_log.Fatalf("Some repos failed to index, see output above") 20 | } else { 21 | info_log.Println("All indexes built!") 22 | } 23 | -- 24 | 2.14.2 25 | 26 | -------------------------------------------------------------------------------- /flexo/hound/0002-Custom-branch-specifier-PR-275.patch: -------------------------------------------------------------------------------- 1 | From e2fe2b0e4720a74e38258f1bb6bd9ee746bdc6ee Mon Sep 17 00:00:00 2001 2 | From: Paul Boutes 3 | Date: Fri, 5 Jan 2018 01:52:46 +0100 4 | Subject: [PATCH 1/3] feat(vcs/git): add ability to use custom branch from vcs 5 | config 6 | 7 | --- 8 | vcs/git.go | 29 +++++++++++++++++++++++++---- 9 | vcs/git_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 10 | 2 files changed, 63 insertions(+), 4 deletions(-) 11 | create mode 100644 vcs/git_test.go 12 | 13 | diff --git a/vcs/git.go b/vcs/git.go 14 | index f8c6682..3626f53 100644 15 | --- a/vcs/git.go 16 | +++ b/vcs/git.go 17 | @@ -8,6 +8,7 @@ import ( 18 | "os/exec" 19 | "path/filepath" 20 | "strings" 21 | + "encoding/json" 22 | ) 23 | 24 | const defaultRef = "master" 25 | @@ -16,10 +17,29 @@ func init() { 26 | Register(newGit, "git") 27 | } 28 | 29 | -type GitDriver struct{} 30 | +type GitDriver struct { 31 | + Ref string `json:"ref"` 32 | +} 33 | 34 | func newGit(b []byte) (Driver, error) { 35 | - return &GitDriver{}, nil 36 | + d := &GitDriver{} 37 | + if e := getRef(b, d); e != nil { 38 | + return nil, e 39 | + } 40 | + return d, nil 41 | +} 42 | + 43 | +func getRef(b []byte, d *GitDriver) error { 44 | + if b != nil { 45 | + if e := json.Unmarshal(b, d); e != nil { 46 | + return e 47 | + } 48 | + } 49 | + if d.Ref == "" { 50 | + d.Ref = defaultRef 51 | + return nil 52 | + } 53 | + return nil 54 | } 55 | 56 | func (g *GitDriver) HeadRev(dir string) (string, error) { 57 | @@ -69,7 +89,7 @@ func (g *GitDriver) Pull(dir string) (string, error) { 58 | "--no-tags", 59 | "--depth", "1", 60 | "origin", 61 | - fmt.Sprintf("+%s:remotes/origin/%s", defaultRef, defaultRef)); err != nil { 62 | + fmt.Sprintf("+%s:remotes/origin/%s", g.Ref, g.Ref)); err != nil { 63 | return "", err 64 | } 65 | 66 | @@ -77,7 +97,7 @@ func (g *GitDriver) Pull(dir string) (string, error) { 67 | "git", 68 | "reset", 69 | "--hard", 70 | - fmt.Sprintf("origin/%s", defaultRef)); err != nil { 71 | + fmt.Sprintf("origin/%s", g.Ref)); err != nil { 72 | return "", err 73 | } 74 | 75 | @@ -90,6 +110,7 @@ func (g *GitDriver) Clone(dir, url string) (string, error) { 76 | "git", 77 | "clone", 78 | "--depth", "1", 79 | + "--branch", g.Ref, 80 | url, 81 | rep) 82 | cmd.Dir = par 83 | diff --git a/vcs/git_test.go b/vcs/git_test.go 84 | new file mode 100644 85 | index 0000000..4aa69ad 86 | --- /dev/null 87 | +++ b/vcs/git_test.go 88 | @@ -0,0 +1,38 @@ 89 | +package vcs 90 | + 91 | +import "testing" 92 | + 93 | +func TestGitConfigWithCustomRef(t *testing.T) { 94 | + cfg := `{"ref": "custom"}` 95 | + d, err := New("git", []byte(cfg)) 96 | + if err != nil { 97 | + t.Fatal(err) 98 | + } 99 | + git := d.Driver.(*GitDriver) 100 | + if git.Ref != "custom" { 101 | + t.Fatalf("expected branch of \"custom\", got %s", git.Ref) 102 | + } 103 | +} 104 | + 105 | +func TestGitConfigWithoutRef(t *testing.T) { 106 | + cfg := `{"option": "option"}` 107 | + d, err := New("git", []byte(cfg)) 108 | + if err != nil { 109 | + t.Fatal(err) 110 | + } 111 | + git := d.Driver.(*GitDriver) 112 | + if git.Ref != "master" { 113 | + t.Fatalf("expected branch of \"master\", got %s", git.Ref) 114 | + } 115 | +} 116 | + 117 | +func TestGitConfigWithoutAdditionalConfig(t *testing.T) { 118 | + d, err := New("git", nil) 119 | + if err != nil { 120 | + t.Fatal(err) 121 | + } 122 | + git := d.Driver.(*GitDriver) 123 | + if git.Ref != "master" { 124 | + t.Fatalf("expected branch of \"master\", got %s", git.Ref) 125 | + } 126 | +} 127 | 128 | From efafc6a34ab914e0988398abfa5b0e9eb56f54c5 Mon Sep 17 00:00:00 2001 129 | From: Paul Boutes 130 | Date: Tue, 9 Jan 2018 00:47:27 +0100 131 | Subject: [PATCH 2/3] feat: default ref value for GitDriver struct 132 | 133 | --- 134 | vcs/git.go | 18 +++++++----------- 135 | 1 file changed, 7 insertions(+), 11 deletions(-) 136 | 137 | diff --git a/vcs/git.go b/vcs/git.go 138 | index 3626f53..5b255e5 100644 139 | --- a/vcs/git.go 140 | +++ b/vcs/git.go 141 | @@ -2,13 +2,13 @@ package vcs 142 | 143 | import ( 144 | "bytes" 145 | + "encoding/json" 146 | "fmt" 147 | "io" 148 | "log" 149 | "os/exec" 150 | "path/filepath" 151 | "strings" 152 | - "encoding/json" 153 | ) 154 | 155 | const defaultRef = "master" 156 | @@ -22,22 +22,18 @@ type GitDriver struct { 157 | } 158 | 159 | func newGit(b []byte) (Driver, error) { 160 | - d := &GitDriver{} 161 | - if e := getRef(b, d); e != nil { 162 | + d := &GitDriver{ 163 | + Ref: defaultRef, 164 | + } 165 | + if e := setRefFromConfig(b, d); e != nil { 166 | return nil, e 167 | } 168 | return d, nil 169 | } 170 | 171 | -func getRef(b []byte, d *GitDriver) error { 172 | +func setRefFromConfig(b []byte, d *GitDriver) error { 173 | if b != nil { 174 | - if e := json.Unmarshal(b, d); e != nil { 175 | - return e 176 | - } 177 | - } 178 | - if d.Ref == "" { 179 | - d.Ref = defaultRef 180 | - return nil 181 | + return json.Unmarshal(b, d) 182 | } 183 | return nil 184 | } 185 | 186 | From 4faf7e7492763208d2337d0102c00f74c950c405 Mon Sep 17 00:00:00 2001 187 | From: Paul Boutes 188 | Date: Tue, 9 Jan 2018 01:03:08 +0100 189 | Subject: [PATCH 3/3] feat: remove unecessary function 190 | 191 | --- 192 | vcs/git.go | 14 ++++++-------- 193 | 1 file changed, 6 insertions(+), 8 deletions(-) 194 | 195 | diff --git a/vcs/git.go b/vcs/git.go 196 | index 5b255e5..364e1d3 100644 197 | --- a/vcs/git.go 198 | +++ b/vcs/git.go 199 | @@ -25,17 +25,15 @@ func newGit(b []byte) (Driver, error) { 200 | d := &GitDriver{ 201 | Ref: defaultRef, 202 | } 203 | - if e := setRefFromConfig(b, d); e != nil { 204 | - return nil, e 205 | + 206 | + if b == nil { 207 | + return d, nil 208 | } 209 | - return d, nil 210 | -} 211 | 212 | -func setRefFromConfig(b []byte, d *GitDriver) error { 213 | - if b != nil { 214 | - return json.Unmarshal(b, d) 215 | + if e := json.Unmarshal(b, d); e != nil { 216 | + return nil, e 217 | } 218 | - return nil 219 | + return d, nil 220 | } 221 | 222 | func (g *GitDriver) HeadRev(dir string) (string, error) { 223 | -------------------------------------------------------------------------------- /flexo/hound/0003-PR-275-p1-Replace-master-in-the-default-base-URL-with-a-rev.patch: -------------------------------------------------------------------------------- 1 | From 6b4c532f46e88f93d3619f7e3c5983f573510714 Mon Sep 17 00:00:00 2001 2 | From: Graham Christensen 3 | Date: Tue, 10 Jul 2018 12:55:59 -0400 4 | Subject: [PATCH] Replace master in the default base URL with a rev 5 | 6 | --- 7 | config/config.go | 2 +- 8 | 1 file changed, 1 insertion(+), 1 deletion(-) 9 | 10 | diff --git a/config/config.go b/config/config.go 11 | index ebda60d..b4a29fb 100644 12 | --- a/config/config.go 13 | +++ b/config/config.go 14 | @@ -13,7 +13,7 @@ const ( 15 | defaultPushEnabled = false 16 | defaultPollEnabled = true 17 | defaultVcs = "git" 18 | - defaultBaseUrl = "{url}/blob/master/{path}{anchor}" 19 | + defaultBaseUrl = "{url}/blob/{rev}/{path}{anchor}" 20 | defaultAnchor = "#L{line}" 21 | ) 22 | 23 | -- 24 | 2.16.4 25 | 26 | -------------------------------------------------------------------------------- /flexo/hound/hound.json: -------------------------------------------------------------------------------- 1 | { 2 | "dbpath": "/var/lib/hound/data", 3 | "max-concurrent-indexers": 1, 4 | "repos": { 5 | "NixOS-.github": { 6 | "url": "https://github.com/NixOS/.github.git", 7 | "vcs-config": { 8 | "ref": "master" 9 | } 10 | }, 11 | "NixOS-cabal2nix": { 12 | "url": "https://github.com/NixOS/cabal2nix.git", 13 | "vcs-config": { 14 | "ref": "master" 15 | } 16 | }, 17 | "NixOS-flake-registry": { 18 | "url": "https://github.com/NixOS/flake-registry.git", 19 | "vcs-config": { 20 | "ref": "master" 21 | } 22 | }, 23 | "NixOS-hydra": { 24 | "url": "https://github.com/NixOS/hydra.git", 25 | "vcs-config": { 26 | "ref": "master" 27 | } 28 | }, 29 | "NixOS-hydra-ant-logger": { 30 | "url": "https://github.com/NixOS/hydra-ant-logger.git", 31 | "vcs-config": { 32 | "ref": "master" 33 | } 34 | }, 35 | "NixOS-hydra-provisioner": { 36 | "url": "https://github.com/NixOS/hydra-provisioner.git", 37 | "vcs-config": { 38 | "ref": "master" 39 | } 40 | }, 41 | "NixOS-mobile-nixos": { 42 | "url": "https://github.com/NixOS/mobile-nixos.git", 43 | "vcs-config": { 44 | "ref": "master" 45 | } 46 | }, 47 | "NixOS-mobile-nixos-website": { 48 | "url": "https://github.com/NixOS/mobile-nixos-website.git", 49 | "vcs-config": { 50 | "ref": "master" 51 | } 52 | }, 53 | "NixOS-mvn2nix-maven-plugin": { 54 | "url": "https://github.com/NixOS/mvn2nix-maven-plugin.git", 55 | "vcs-config": { 56 | "ref": "master" 57 | } 58 | }, 59 | "NixOS-nix": { 60 | "url": "https://github.com/NixOS/nix.git", 61 | "vcs-config": { 62 | "ref": "master" 63 | } 64 | }, 65 | "NixOS-nix-eclipse": { 66 | "url": "https://github.com/NixOS/nix-eclipse.git", 67 | "vcs-config": { 68 | "ref": "master" 69 | } 70 | }, 71 | "NixOS-nix-idea": { 72 | "url": "https://github.com/NixOS/nix-idea.git", 73 | "vcs-config": { 74 | "ref": "master" 75 | } 76 | }, 77 | "NixOS-nix-mode": { 78 | "url": "https://github.com/NixOS/nix-mode.git", 79 | "vcs-config": { 80 | "ref": "master" 81 | } 82 | }, 83 | "NixOS-nix-pills": { 84 | "url": "https://github.com/NixOS/nix-pills.git", 85 | "vcs-config": { 86 | "ref": "master" 87 | } 88 | }, 89 | "NixOS-nixops": { 90 | "url": "https://github.com/NixOS/nixops.git", 91 | "vcs-config": { 92 | "ref": "master" 93 | } 94 | }, 95 | "NixOS-nixops-aws": { 96 | "url": "https://github.com/NixOS/nixops-aws.git", 97 | "vcs-config": { 98 | "ref": "master" 99 | } 100 | }, 101 | "NixOS-nixops-hetzner": { 102 | "url": "https://github.com/NixOS/nixops-hetzner.git", 103 | "vcs-config": { 104 | "ref": "master" 105 | } 106 | }, 107 | "NixOS-nixos-artwork": { 108 | "url": "https://github.com/NixOS/nixos-artwork.git", 109 | "vcs-config": { 110 | "ref": "master" 111 | } 112 | }, 113 | "NixOS-nixos-channel-scripts": { 114 | "url": "https://github.com/NixOS/nixos-channel-scripts.git", 115 | "vcs-config": { 116 | "ref": "master" 117 | } 118 | }, 119 | "NixOS-nixos-hardware": { 120 | "url": "https://github.com/NixOS/nixos-hardware.git", 121 | "vcs-config": { 122 | "ref": "master" 123 | } 124 | }, 125 | "NixOS-nixos-homepage": { 126 | "url": "https://github.com/NixOS/nixos-homepage.git", 127 | "vcs-config": { 128 | "ref": "master" 129 | } 130 | }, 131 | "NixOS-nixos-org-configurations": { 132 | "url": "https://github.com/NixOS/nixos-org-configurations.git", 133 | "vcs-config": { 134 | "ref": "master" 135 | } 136 | }, 137 | "NixOS-nixos-planet": { 138 | "url": "https://github.com/NixOS/nixos-planet.git", 139 | "vcs-config": { 140 | "ref": "master" 141 | } 142 | }, 143 | "NixOS-nixos-search": { 144 | "url": "https://github.com/NixOS/nixos-search.git", 145 | "vcs-config": { 146 | "ref": "master" 147 | } 148 | }, 149 | "NixOS-nixos-weekly": { 150 | "url": "https://github.com/NixOS/nixos-weekly.git", 151 | "vcs-config": { 152 | "ref": "master" 153 | } 154 | }, 155 | "NixOS-nixpart": { 156 | "url": "https://github.com/NixOS/nixpart.git", 157 | "vcs-config": { 158 | "ref": "master" 159 | } 160 | }, 161 | "NixOS-nixpkgs": { 162 | "url": "https://github.com/NixOS/nixpkgs.git", 163 | "vcs-config": { 164 | "ref": "master" 165 | } 166 | }, 167 | "NixOS-npm2nix": { 168 | "url": "https://github.com/NixOS/npm2nix.git", 169 | "vcs-config": { 170 | "ref": "master" 171 | } 172 | }, 173 | "NixOS-ofborg": { 174 | "url": "https://github.com/NixOS/ofborg.git", 175 | "vcs-config": { 176 | "ref": "released" 177 | } 178 | }, 179 | "NixOS-patchelf": { 180 | "url": "https://github.com/NixOS/patchelf.git", 181 | "vcs-config": { 182 | "ref": "master" 183 | } 184 | }, 185 | "NixOS-rfc-steering-committee": { 186 | "url": "https://github.com/NixOS/rfc-steering-committee.git", 187 | "vcs-config": { 188 | "ref": "master" 189 | } 190 | }, 191 | "NixOS-rfc39": { 192 | "url": "https://github.com/NixOS/rfc39.git", 193 | "vcs-config": { 194 | "ref": "master" 195 | } 196 | }, 197 | "NixOS-rfcs": { 198 | "url": "https://github.com/NixOS/rfcs.git", 199 | "vcs-config": { 200 | "ref": "master" 201 | } 202 | }, 203 | "NixOS-security": { 204 | "url": "https://github.com/NixOS/security.git", 205 | "vcs-config": { 206 | "ref": "master" 207 | } 208 | }, 209 | "NixOS-snapd-nix-base": { 210 | "url": "https://github.com/NixOS/snapd-nix-base.git", 211 | "vcs-config": { 212 | "ref": "master" 213 | } 214 | }, 215 | "NixOS-templates": { 216 | "url": "https://github.com/NixOS/templates.git", 217 | "vcs-config": { 218 | "ref": "master" 219 | } 220 | }, 221 | "nixos-users-wiki-wiki": { 222 | "url": "https://github.com/nixos-users/wiki.wiki.git", 223 | "url-pattern": { 224 | "base-url": "{url}/{path}" 225 | } 226 | } 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /flexo/hound/open-search.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Hound 5 | Search code with Hound 6 | Hound 7 | 10 | 11 | -------------------------------------------------------------------------------- /flexo/hound/update-hound.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env nix-shell 2 | #!nix-shell -i python3 -p python3Packages.python -p python3Packages.requests 3 | 4 | import requests 5 | import json 6 | from pprint import pprint 7 | 8 | blacklist = [ 9 | 'https://github.com/NixOS/nixos.git', 10 | 'https://github.com/NixOS/systemd.git', 11 | 'https://github.com/NixOS/docker.git', 12 | 'https://github.com/NixOS/nixpkgs-channels.git', 13 | 'https://github.com/NixOS/nixops-dashboard.git', 14 | 'https://github.com/NixOS/nixos-foundation.git', 15 | ]; 16 | 17 | def all_for_org(org, blacklist): 18 | 19 | resp = {} 20 | 21 | next_url = 'https://api.github.com/orgs/{}/repos'.format(org) 22 | while next_url is not None: 23 | repo_resp = requests.get(next_url) 24 | 25 | if 'next' in repo_resp.links: 26 | next_url = repo_resp.links['next']['url'] 27 | else: 28 | next_url = None 29 | 30 | repos = repo_resp.json() 31 | 32 | resp.update({ 33 | "{}-{}".format(org, repo['name']): { 34 | 'url': repo['clone_url'], 35 | 'vcs-config': { 36 | 'ref': repo['default_branch'] 37 | } 38 | } 39 | for repo in repos 40 | if repo['clone_url'] not in blacklist 41 | }) 42 | 43 | return resp 44 | 45 | repos = all_for_org('NixOS', blacklist) 46 | repos['nixos-users-wiki-wiki'] = { 47 | "url" : "https://github.com/nixos-users/wiki.wiki.git", 48 | "url-pattern" : { 49 | "base-url" : "{url}/{path}" 50 | } 51 | } 52 | 53 | print(json.dumps( 54 | { 55 | "max-concurrent-indexers" : 1, 56 | "dbpath" : "/var/lib/hound/data", 57 | "repos": repos 58 | }, 59 | indent=4, 60 | sort_keys=True 61 | )) 62 | -------------------------------------------------------------------------------- /flexo/irc.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | let 3 | scripts_repo = pkgs.fetchFromGitHub { 4 | owner = "weechat"; 5 | repo = "scripts"; 6 | rev = "f6f0fd4647024236dd4856a2646c05362078bca4"; 7 | sha256 = "10j0mp5cb0b9cgxhp2wdaidzgc7ir181s3ym74z1pdxkcznha5q3"; 8 | }; 9 | 10 | gcautoconnect = pkgs.stdenv.mkDerivation { 11 | pname = "autoconnect"; 12 | version ="20190524"; 13 | src = scripts_repo; 14 | 15 | passthru.scripts = [ "autoconnect.py" ]; 16 | installPhase = '' 17 | install -D ./python/autoconnect.py $out/share/autoconnect.py 18 | ''; 19 | }; 20 | in { 21 | environment.systemPackages = with pkgs; [ 22 | (weechat.override { 23 | configure = { availablePlugins, ... }: { 24 | scripts = [ gcautoconnect ] ++ (with pkgs.weechatScripts; [ 25 | weechat-autosort 26 | ]); 27 | plugins = [ 28 | (availablePlugins.python.withPackages (ps: [ 29 | (ps.potr.overridePythonAttrs (oldAttrs: 30 | { 31 | propagatedBuildInputs = [ 32 | (ps.buildPythonPackage rec { 33 | name = "pycrypto-${version}"; 34 | version = "2.6.1"; 35 | 36 | src = pkgs.fetchurl { 37 | url = "mirror://pypi/p/pycrypto/${name}.tar.gz"; 38 | sha256 = "0g0ayql5b9mkjam8hym6zyg6bv77lbh66rv1fyvgqb17kfc1xkpj"; 39 | }; 40 | 41 | patches = pkgs.stdenv.lib.singleton (pkgs.fetchpatch { 42 | name = "CVE-2013-7459.patch"; 43 | url = "https://anonscm.debian.org/cgit/collab-maint/python-crypto.git" 44 | + "/plain/debian/patches/CVE-2013-7459.patch?h=debian/2.6.1-7"; 45 | sha256 = "01r7aghnchc1bpxgdv58qyi2085gh34bxini973xhy3ks7fq3ir9"; 46 | }); 47 | 48 | buildInputs = [ pkgs.gmp ]; 49 | 50 | preConfigure = '' 51 | sed -i 's,/usr/include,/no-such-dir,' configure 52 | sed -i "s!,'/usr/include/'!!" setup.py ''; 53 | }) 54 | ]; 55 | } 56 | )) 57 | ])) 58 | ]; 59 | }; 60 | }) 61 | screen 62 | aspell 63 | aspellDicts.en 64 | mosh 65 | ]; 66 | networking.firewall.allowedUDPPorts = [ 60001 ]; 67 | } 68 | -------------------------------------------------------------------------------- /flexo/ircbot.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | let 3 | src = import ./../../ircbot/ofborg {}; 4 | githubgateway = import ./../../../samueldr/github-to-irc/default.nix { inherit pkgs; }; 5 | # openssl 1.0 -> 1.1 in 19.03 -> 19.09 busted this service 6 | # pijulnestgateway = pkgs.callPackage ../../../../nest.pijul.com/grahamc/nest-to-irc { }; 7 | 8 | ircservice = name: bin: cfg: { 9 | "ircbot-${name}" = { 10 | enable = true; 11 | after = [ "network.target" "network-online.target" "rabbitmq.service" ]; 12 | wants = [ "network-online.target" ]; 13 | wantedBy = [ "multi-user.target" ]; 14 | 15 | serviceConfig = { 16 | User = "ofborg-irc"; 17 | Group = "ofborg-irc"; 18 | PrivateTmp = true; 19 | Restart = "always"; 20 | }; 21 | 22 | script = '' 23 | export RUST_BACKTRACE=1 24 | ${bin} ${cfg} 25 | ''; 26 | }; 27 | }; 28 | 29 | in { 30 | users.users.ofborg-irc = { 31 | description = "GC Of Borg IRC"; 32 | home = "/var/empty"; 33 | group = "ofborg-irc"; 34 | uid = 403; 35 | }; 36 | users.groups.ofborg-irc.gid = 403; 37 | 38 | 39 | systemd = { 40 | services = 41 | (ircservice "gateway" 42 | "${src.ircbot}/bin/gateway" 43 | ./../../ircbot/ofborg/config.irc.json) // 44 | (ircservice "github-to-irc" 45 | "${githubgateway}/bin/github-to-irc" 46 | "${./../../ircbot/ofborg/config.irc.json}") // 47 | #(ircservice "pijul-nest-to-irc" 48 | # "${pijulnestgateway}/bin/nest-to-irc" 49 | # "${../../../../nest.pijul.com/grahamc/nest-to-irc/url}") // 50 | {}; 51 | 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /flexo/nix-channel-monitor/changes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eu 4 | 5 | dbg() { 6 | : 7 | echo "$@" >&2 8 | } 9 | 10 | publish() { 11 | msg=$(cat | head -n1) 12 | curl -v \ 13 | -H "content-type:application/json" \ 14 | -XPOST \ 15 | AMQPAPI/api/exchanges/ircbot/amq.default/publish \ 16 | --data @- <<-EOF 17 | { 18 | "properties": {}, 19 | "routing_key": "queue-publish", 20 | "payload_encoding": "string", 21 | "payload": "{\"target\": \"#nixos\", \"body\": \"$msg\", \"message_type\": \"notice\"}" 22 | } 23 | EOF 24 | curl -v \ 25 | -H "content-type:application/json" \ 26 | -XPOST \ 27 | AMQPAPI/api/exchanges/ircbot/amq.default/publish \ 28 | --data @- <<-EOF 29 | { 30 | "properties": {}, 31 | "routing_key": "queue-publish", 32 | "payload_encoding": "string", 33 | "payload": "{\"target\": \"#nixos-bots\", \"body\": \"$msg\", \"message_type\": \"notice\"}" 34 | } 35 | EOF 36 | 37 | 38 | } 39 | 40 | filter_recent_timestamps() { 41 | since=$(date -d "3 months ago" "+%s") 42 | while read commit committed timestamp; do 43 | if [ "$timestamp" -ge "$since" ]; then 44 | echo "$commit $timestamp"; 45 | fi; 46 | done 47 | } 48 | 49 | datapoints() { 50 | cat | filter_recent_timestamps \ 51 | | awk '{ print " [\""$1"\", "$2"000, '$i'],"; }' 52 | } 53 | 54 | make_graph() { 55 | cat < 57 | 58 | 59 | 99 | 100 | EOF 101 | } 102 | 103 | readonly CHAN="#nixos" 104 | readonly URL="https://channels.nix.gsc.io" 105 | readonly remote="origin" 106 | readonly gitrepo="$1" 107 | readonly dest="$2" 108 | 109 | ( 110 | cd "$gitrepo" >&2 111 | git fetch "$remote" >&2 112 | git for-each-ref --format '%(refname:strip=3)' \ 113 | "refs/remotes/$remote/nixos-*" "refs/remotes/$remote/nixpkgs-*" 114 | ) | grep -v HEAD | 115 | ( 116 | cd "$dest" 117 | while read -r branch; do 118 | name=$(echo "$branch" | sed -e "s#/#_#g" -e "s#\.\.#_#g") 119 | mkdir -p "$name" 120 | ( 121 | cd "$name" 122 | touch latest 123 | ( 124 | cd "$gitrepo" >&2 125 | git show -s --format="%H %at" "$remote/$branch" 126 | ) > latest.next 127 | touch latest-v2 128 | ( 129 | cd "$gitrepo" >&2 130 | git show -s --format="%H %at $(date '+%s')" "$remote/$branch" 131 | ) > latest-v2.next 132 | touch latest-url 133 | ( 134 | url=$(curl -w '%{redirect_url}' "https://nixos.org/channels/$branch" -o /dev/null) 135 | echo "$url $(date '+%s')" 136 | ) > latest-url.next 137 | 138 | if [ "$(cut -d' ' -f1 latest-url.next | md5sum)" != "$(cut -d' ' -f1 latest-url | md5sum)" ]; then 139 | dbg "Change in ${branch} URL" 140 | mv latest-url.next latest-url 141 | chmod a+r latest-url 142 | touch history-url 143 | ( 144 | cat history-url 145 | cat latest-url 146 | ) | tail -n100000 > history-url.next 147 | mv history-url.next history-url 148 | chmod a+r history-url 149 | fi 150 | 151 | if [ "$(md5sum < latest.next)" != "$(md5sum < latest)" ]; then 152 | dbg "Change in ${branch}" 153 | ( 154 | cd "$gitrepo" >&2 155 | echo -n "Channel $branch advanced to " 156 | git show -s --format="https://github.com/NixOS/nixpkgs/commit/%h (from %cr, history: $URL/$name)" "$remote/$branch" 157 | ) | publish 158 | mv latest.next latest 159 | chmod a+r latest 160 | touch history 161 | ( 162 | cat history 163 | cat latest 164 | ) | tail -n100000 > history.next 165 | mv history.next history 166 | chmod a+r history 167 | 168 | 169 | # Note: latest-v2 doesn't do a hash check b/c 170 | # its hash always changes due to the date. 171 | mv latest-v2.next latest-v2 172 | chmod a+r latest-v2 173 | touch history-v2 174 | ( 175 | cat history-v2 176 | cat latest-v2 177 | ) | tail -n100000 > history-v2.next 178 | mv history-v2.next history-v2 179 | chmod a+r history-v2 180 | fi 181 | 182 | rm -f latest.next latest-v2.next latest-url.next 183 | 184 | cat < README.txt 185 | This service is provided for free. 186 | 187 | If you use this service automatically please be 188 | polite and follow some rules: 189 | 190 | - please don't poll any more often than every 15 191 | minutes, to reduce the traffic to my server. 192 | 193 | - please don't poll exactly on a 5 minute 194 | increment, to avoid the "thundering herd" 195 | problem. 196 | 197 | - please add a delay on your scheduled polling 198 | script for a random delay between 1 and 59 199 | seconds, to further spread out the load. 200 | 201 | - please consider using my webhooks instead: 202 | email me at graham at grahamc dot com or 203 | message gchristensen on #nixos on Freenode. 204 | 205 | 206 | FILE NOTES 207 | 208 | Each format comes with two files, a "latest" file 209 | and a "history" file. 210 | 211 | The history files will retain 100000 lines of history. 212 | 213 | 214 | 215 | FORMAT NOTES 216 | 217 | latest, history: 218 | commit-hash date-of-commit 219 | 220 | latest-v2, history-v2: 221 | commit-hash date-of-commit date-of-advancement 222 | 223 | latest-url, history-url: 224 | channel-url date-of-advancement 225 | 226 | Note: "date-of-advancement" is actually the date 227 | the advancement was _detected_, and can be 228 | wildly wrong for no longer updated channels. For 229 | example, the nixos-13.10 channel's 230 | date-of-advancement is quite recent despite the 231 | channel not having updated in many years. 232 | 233 | All three formats will continue to be updated. 234 | 235 | Thank you, good luck, have fun 236 | Graham 237 | EOF 238 | ) 239 | done 240 | 241 | make_graph > graph.html 242 | ) 243 | # gnuplot <<< "set term svg; set output 'channel.svg'; set title 'Channel updates'; set timefmt '%s'; set format x '%m-%d'; set xdata time; plot 0, 'channel' using 1:(\$2-\$1)/3600 with linespoints title 'nixos-whatever';" 244 | -------------------------------------------------------------------------------- /flexo/nix-channel-monitor/default.nix: -------------------------------------------------------------------------------- 1 | { secrets }: 2 | { pkgs, config, ... }: 3 | { 4 | systemd.tmpfiles.rules = [ 5 | "d ${config.services.nginx.virtualHosts."channels.nix.gsc.io".root} 0755 nix-channel-monitor nginx" 6 | ]; 7 | services.nginx.virtualHosts."channels.nix.gsc.io" = { 8 | root = "/var/lib/nginx/nix-channel-monitor/monitor/public"; 9 | enableACME = true; 10 | forceSSL = true; 11 | locations."/".extraConfig = '' 12 | autoindex on; 13 | ''; 14 | }; 15 | 16 | users.users.nix-channel-monitor = { 17 | description = "Nix Channel Monitor"; 18 | home = "/var/lib/nix-channel-monitor"; 19 | createHome = true; 20 | group = "nix-channel-monitor"; 21 | uid = 400; 22 | }; 23 | users.groups.nix-channel-monitor.gid = 400; 24 | 25 | systemd.services = { 26 | nix-channel-monitor = { 27 | after = [ "network.target" "network-online.target" ]; 28 | wants = [ "network-online.target" ]; 29 | path = with pkgs; [ 30 | telnet 31 | git 32 | gawk 33 | curl 34 | ]; 35 | 36 | serviceConfig = { 37 | User = "nix-channel-monitor"; 38 | Group = "nix-channel-monitor"; 39 | Type = "oneshot"; 40 | PrivateTmp = true; 41 | WorkingDirectory = "/var/lib/nix-channel-monitor"; 42 | }; 43 | 44 | preStart = '' 45 | set -eux 46 | remote=https://github.com/nixos/nixpkgs.git 47 | if [ ! -d /var/lib/nix-channel-monitor/git ]; then 48 | git clone "$remote" git 49 | fi 50 | git -C /var/lib/nix-channel-monitor/git remote set-url origin "$remote" 51 | ''; 52 | 53 | script = let 54 | src = pkgs.runCommand "queue-monitor-src" {} 55 | '' 56 | mkdir queue-monitor 57 | cp ${./changes.sh} ./changes.sh 58 | sed -i 's#AMQPAPI#${secrets.rabbitmq.nixchannelmonitor}#' ./changes.sh 59 | cp -r ./changes.sh $out 60 | ''; 61 | in '' 62 | ${src} /var/lib/nix-channel-monitor/git ${config.services.nginx.virtualHosts."channels.nix.gsc.io".root} 63 | ''; 64 | }; 65 | }; 66 | 67 | systemd.timers = { 68 | run-nix-channel-monitor = { 69 | description = "Rerun the nix channel monitor"; 70 | wantedBy = [ "timers.target" ]; 71 | partOf = [ "nix-channel-monitor.service" ]; 72 | enable = true; 73 | timerConfig = { 74 | OnCalendar = "*:0/5"; 75 | Unit = "nix-channel-monitor.service"; 76 | Persistent = "yes"; 77 | AccuracySec = "1m"; 78 | RandomizedDelaySec = "30s"; 79 | }; 80 | }; 81 | }; 82 | } 83 | -------------------------------------------------------------------------------- /flexo/periodic-reminders.nix: -------------------------------------------------------------------------------- 1 | { pkgs, config, ... }: 2 | { 3 | systemd = { 4 | services = { 5 | periodicreminder = { 6 | after = [ "network.target" "network-online.target" ]; 7 | wants = [ "network-online.target" ]; 8 | path = [ 9 | (pkgs.python3.withPackages (ps: [ ps.tweepy ])) 10 | ]; 11 | 12 | serviceConfig = { 13 | User = "grahamc"; 14 | Group = "users"; 15 | Type = "oneshot"; 16 | PrivateTmp = true; 17 | WorkingDirectory = "/home/grahamc/yrperiodicreminder"; 18 | }; 19 | 20 | script = '' 21 | ./tweet.sh 22 | ''; 23 | }; 24 | }; 25 | 26 | timers = { 27 | periodicreminder = { 28 | description = "Remind people of important things of the past."; 29 | wantedBy = [ "timers.target" ]; 30 | partOf = [ "periodicreminder.service" ]; 31 | enable = true; 32 | timerConfig = { 33 | OnCalendar = "16,18,20,22:30"; 34 | Unit = "periodicreminder.service"; 35 | Persistent = "yes"; 36 | RandomizedDelaySec = "30m"; 37 | }; 38 | }; 39 | }; 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /flexo/periodic-reminders.py: -------------------------------------------------------------------------------- 1 | { pkgs, config, ... }: 2 | { 3 | systemd = { 4 | services = { 5 | periodicreminder = { 6 | after = [ "network.target" "network-online.target" ]; 7 | wants = [ "network-online.target" ]; 8 | path = [ 9 | (pkgs.python3.withPackages (ps: [ ps.tweepy ])) 10 | ]; 11 | 12 | serviceConfig = { 13 | User = "grahamc"; 14 | Group = "users"; 15 | Type = "oneshot"; 16 | PrivateTmp = true; 17 | WorkingDirectory = "/home/grahamc/yrperiodicreminder"; 18 | }; 19 | 20 | script = '' 21 | ./tweet.sh 22 | ''; 23 | }; 24 | }; 25 | 26 | timers = { 27 | periodicreminder = { 28 | description = "Remind people of important things of the past."; 29 | wantedBy = [ "timers.target" ]; 30 | partOf = [ "periodicreminder.service" ]; 31 | enable = true; 32 | timerConfig = { 33 | OnCalendar = "16,18,20,22:30"; 34 | Unit = "periodicreminder.service"; 35 | Persistent = "yes"; 36 | RandomizedDelaySec = "30m"; 37 | }; 38 | }; 39 | }; 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /flexo/r13y.nix: -------------------------------------------------------------------------------- 1 | { secrets }: 2 | { config, pkgs, ... }: 3 | { 4 | systemd.tmpfiles.rules = [ 5 | "d ${config.services.nginx.virtualHosts."r13y.com".root} 0755 r13y nginx" 6 | ]; 7 | 8 | users.users.r13y = { 9 | description = "Reproducibility"; 10 | group = "r13y"; 11 | uid = 404; 12 | openssh.authorizedKeys.keyFiles = [ secrets.r13y.public ]; 13 | shell = pkgs.bash; 14 | }; 15 | users.groups.r13y.gid = 404; 16 | 17 | services.nginx.virtualHosts = { 18 | "r13y.com" = { 19 | root = "/var/lib/nginx/r13y/r13y.com"; 20 | enableACME = true; 21 | forceSSL = true; 22 | }; 23 | 24 | "www.r13y.com" = { 25 | enableACME = true; 26 | forceSSL = true; 27 | globalRedirect = "r13y.com"; 28 | }; 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /flexo/rabbitmq.nix: -------------------------------------------------------------------------------- 1 | { secrets }: 2 | { pkgs, config, ... }: 3 | let 4 | rabbit_tls_port = 5671; 5 | cert_dir = "/var/lib/acme/flexo.gsc.io"; 6 | in { 7 | networking = { 8 | firewall = { 9 | allowedTCPPorts = [ 80 443 5671 15671 ]; 10 | extraCommands = '' 11 | # epmd 12 | ip46tables -A nixos-fw -i wg0 -p tcp \ 13 | --dport 4369 -j nixos-fw-accept 14 | 15 | # inter-node chat 16 | ip46tables -A nixos-fw -i wg0 -p tcp \ 17 | --dport 25672 -j nixos-fw-accept 18 | 19 | # CLI chat 20 | ip46tables -A nixos-fw -i wg0 -p tcp \ 21 | --dport 35672:35682 -j nixos-fw-accept 22 | 23 | # web interface 24 | ip46tables -A nixos-fw -i wg0 -p tcp \ 25 | --dport 15672 -j nixos-fw-accept 26 | ''; 27 | }; 28 | }; 29 | 30 | security.acme.certs."flexo.gsc.io" = { 31 | extraDomains = { 32 | "events.nix.gsc.io" = null; 33 | }; 34 | plugins = [ "cert.pem" "fullchain.pem" "full.pem" "key.pem" "account_key.json" "account_reg.json" ]; 35 | group = "rabbitmq"; 36 | allowKeysForGroup = true; 37 | }; 38 | 39 | services = { 40 | nginx = { 41 | virtualHosts = { 42 | "flexo.gsc.io" = { 43 | enableACME = true; 44 | forceSSL = true; 45 | }; 46 | }; 47 | }; 48 | 49 | rabbitmq = { 50 | enable = true; 51 | cookie = secrets.rabbitmq.cookie; 52 | plugins = [ "rabbitmq_management" "rabbitmq_web_stomp" "rabbitmq_shovel" "rabbitmq_shovel_management" ]; 53 | config = '' 54 | [ 55 | {rabbit, [ 56 | {tcp_listen_options, [ 57 | {keepalive, true}]}, 58 | {heartbeat, 10}, 59 | {ssl_listeners, [{"::", 5671}]}, 60 | {ssl_options, [ 61 | {cacertfile,"${cert_dir}/fullchain.pem"}, 62 | {certfile,"${cert_dir}/cert.pem"}, 63 | {keyfile,"${cert_dir}/key.pem"}, 64 | {verify,verify_none}, 65 | {fail_if_no_peer_cert,false}]}, 66 | {log_levels, [{connection, debug}]} 67 | ]}, 68 | {rabbitmq_management, [{listener, [{port, 15672}]}]}, 69 | {rabbitmq_web_stomp, 70 | [{ssl_config, [{port, 15671}, 71 | {backlog, 1024}, 72 | {cacertfile,"${cert_dir}/fullchain.pem"}, 73 | {certfile,"${cert_dir}/cert.pem"}, 74 | {keyfile,"${cert_dir}/key.pem"} 75 | ]}]} 76 | ]. 77 | ''; 78 | }; 79 | }; 80 | } 81 | -------------------------------------------------------------------------------- /flexo/wireguard.nix: -------------------------------------------------------------------------------- 1 | { pkgs, config, ... }: 2 | let 3 | privatekey = config.networking.wireguard.interfaces.wg0.privateKeyFile; 4 | publickey = "${dirOf privatekey}/public"; 5 | in { 6 | networking.extraHosts = '' 7 | 10.10.2.15 ogden 8 | 10.10.2.10 petunia 9 | 10.10.2.5 zoidberg 10 | ''; 11 | 12 | programs.ssh.knownHosts.ogden.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHICIvUL8AAPDjwP0wUdYADwWSWBieS8iTgNPVa+fynN"; 13 | 14 | networking.firewall.allowedUDPPorts = [ 51820 ]; 15 | 16 | boot.kernel.sysctl."net.ipv4.ip_forward" = "1"; 17 | networking.nat = { 18 | enable = true; 19 | externalIP = "147.75.105.137"; 20 | externalInterface = "bond0"; 21 | internalIPs = [ "10.10.2.0/24" ]; 22 | internalInterfaces = [ "wg0" ]; 23 | }; 24 | 25 | networking.wireguard.interfaces.wg0 = { 26 | ips = [ "10.10.2.25/24" ]; 27 | privateKeyFile = "/etc/wireguard/private"; 28 | listenPort = 51820; 29 | 30 | peers = [ 31 | { 32 | # petunia 33 | publicKey = "iRqkVDUccM1duRrG02a9IraBgR9zew6SqAclqUaLoyI="; 34 | allowedIPs = [ "10.10.2.10/32" ]; 35 | } 36 | { 37 | # zoidberg 38 | publicKey = "BQ7+bGuKVat/I8b1s75eKlRAE3PwD9DTTbOJ4yUEAzo="; 39 | allowedIPs = [ "10.10.2.5/32" ]; 40 | endpoint = "zoidberg.gsc.io:5820"; 41 | persistentKeepalive = 25; 42 | } 43 | { 44 | # ogden 45 | publicKey = config.about.ogden.wireguard_public_keys.wg0; 46 | allowedIPs = [ "10.10.2.15/32" ]; 47 | persistentKeepalive = 25; 48 | } 49 | { 50 | # elzar 51 | publicKey = "/5HDPKbtNvC/KxqBrUCnPfLVme8scxLJn9Zs2quXLl0="; 52 | allowedIPs = [ "10.10.2.50/32" ]; 53 | persistentKeepalive = 25; 54 | } 55 | ]; 56 | }; 57 | 58 | deployment.keys.sphalerite-wg.text = "23f8zfwdCKtWc/w9gS2qGpqePDfo9YJC9gIT2Me9TFc="; 59 | 60 | systemd.services.wireguard-wg0-key = { 61 | enable = true; 62 | wantedBy = [ "wireguard-wg0.service" ]; 63 | path = with pkgs; [ wireguard ]; 64 | serviceConfig = { 65 | Type = "oneshot"; 66 | RemainAfterExit = true; 67 | }; 68 | 69 | script = '' 70 | mkdir --mode 0644 -p "${dirOf privatekey}" 71 | if [ ! -f "${privatekey}" ]; then 72 | touch "${privatekey}" 73 | chmod 0600 "${privatekey}" 74 | wg genkey > "${privatekey}" 75 | chmod 0400 "${privatekey}" 76 | 77 | touch "${publickey}" 78 | chmod 0600 "${publickey}" 79 | wg pubkey < "${privatekey}" > "${publickey}" 80 | chmod 0444 "${publickey}" 81 | fi 82 | ''; 83 | }; 84 | } 85 | -------------------------------------------------------------------------------- /intake.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -f ./intake/*.json 4 | mkdir -p ./intake 5 | 6 | echo '{' > ./intake/default.nix.next 7 | 8 | nixops info --plain | awk '{ print $1; }' \ 9 | | while read -r node; do 10 | echo "~> $node" 11 | nixops scp --from "$node" /run/about.json "./intake/$node.json" 12 | echo "\"${node}\" = builtins.fromJSON (builtins.readFile ./$node.json);" >> ./intake/default.nix.next 13 | done 14 | echo '}' >> ./intake/default.nix.next 15 | mv ./intake/default.nix.next ./intake/default.nix 16 | -------------------------------------------------------------------------------- /intake/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | "flexo" = builtins.fromJSON (builtins.readFile ./flexo.json); 3 | "lord-nibbler" = builtins.fromJSON (builtins.readFile ./lord-nibbler.json); 4 | "ogden" = builtins.fromJSON (builtins.readFile ./ogden.json); 5 | } 6 | -------------------------------------------------------------------------------- /intake/flexo.json: -------------------------------------------------------------------------------- 1 | { 2 | "ssh_host_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINZJC4F2C2DMZi1fonqMuWB1GdQR0bn3WRaEI3JYnE4d root@Flexo", 3 | "wireguard_public_keys": { 4 | "wg0": "4mxZbkU/uCDSFW0gQQAw05lGDy7CaJRpVs8nERWS+Uk=" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /intake/lord-nibbler.json: -------------------------------------------------------------------------------- 1 | { 2 | "ssh_host_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKTuyQLzbDV8bK6ug+VSEM9AGfLtHxDV8zaiX0tsG2cD root@nixos", 3 | "wireguard_public_keys": {} 4 | } 5 | -------------------------------------------------------------------------------- /intake/ogden.json: -------------------------------------------------------------------------------- 1 | { 2 | "ssh_host_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHICIvUL8AAPDjwP0wUdYADwWSWBieS8iTgNPVa+fynN root@nixos", 3 | "ssh_root_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINsBEaKyvlXvVGjMT7LEhYs87kVNiTpeVtWNtjnElSSg root@ogden", 4 | "wireguard_public_keys": { 5 | "wg0": "gNU592zxr8y+kuaH3+aGuwEhRmwA+FFoBckOATFr7U0=" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /keys/elazar.keys: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCtw6ro3xyQNmbk8G2qiquwYD/jnJnusEJW/DFXZ0gn3Hm/PZVKdWVgwpM2ag+BnqNDHL5U3Nw50C1HIcChU3ZeKW3ouWVdGXeRr8Ezofb52csgfLBphty3Cwm7drWBtvRTYslX0/WMnEpilbTbkzSOqkA/dFe18vbHRwTI6GYKV3IDZIajCotlGE+I6+VRXrJvSXHCCinEUl01eO8fBnNYXhCGct/jXZvjTKf4cwUZZTbI/wZnNXGf0rtxrCGPK1nhJ54Fswkzwnt95ly7QkY3nuD1ZLmD0Mpe3dB/IPwMseug7HqhGSaAPTI0Lv70yxSABzMiOSuIaHa0OzTHwRMdHQKJA3NfL9NUXU2/6UODUb7dEtReWPXVb6GGwDq9drT0lHPp35UbRxeDAxI6svvFls8QddYZ8mYA5PJ8mYGz4HvMUqYD5sqKLuhENkQmmNHFh0hrafkxpVc0r8JmcKulmVR1Tp1PVwRax+RsdcVAGwlqeByKcr4UgsfjAtQAWFx6YvvwXtXW8NFraKXYxx9Uc7TReh8iatD8DcR/viCFBvNa20vVfn/UXziKZ47+RniScZkJlfemkZJd2dfvGTkuH7KjbAvw3Rm6fpwF3M7adtBfZBu24ViqDjXiqV8A0jjYG9/3xV8z9tQcWikTIsbByVxACHAKha8ZJl0BhMZCPQ== 2 | -------------------------------------------------------------------------------- /keys/grahamc.keys: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBwRyfjaeZhuf8iT2iF/ZMyknHMnTpDCIbJCETUuwDqE9osx2d6ClHdJRjjk4R45J3UTJVNwKV0IAjzBenOaIyR+7nyIIolZdjcPzWcBff5e9c+fizYJO/ucVK8JzEIuXZoguSw4Fws4bZF+BW0E/E6f0tnYqJUHm/Ubv8WcUx6+Iq9p5hd4odTrHI+p8Pj/CsiSlOHHRYyGWk+0kdI7+WsOqMAMDx2KGVfNgbaUL7qtXS0PtEyyNHgvdKqT+nPO2f/W8UbZrjwRVMTKS+PvSD2sOrJISGMJPElTfvUg6xgW0XLpJxuZaZRvR5dTJwfOee52WRB1xDqGKn1wLyWn+v 2 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDDTZB6tOYfEmWkYff494DjPpzo45ymhTvEPT4rjPyeTfBB1p+odbaVnYFQPgwk4MYBZyPjzQa9NLC76m2kCDNqnasBFGhTLxSfR9q/4J5G9x0a5NvA/emqNpjtbT25UADjhEETOIYjLYdd7z9rGFr/8ttmJNog6t9NIEw7/ddupzpvNaK80rdPSO7jt4/3TxFiix3yvaTNe4XahCiEDNIXF0hskOTuFtUX4LgiET9lmJa92i/Oh/7oYxDBond6C95HyoppGJu6y3txutAWt12N5rLRzWSPECwrJRNcXIqmIjofl+pt4vd7D4DHCxesKajG4fAs+KXZ3Lxug2dZB0eD 3 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDUDEJaYzYDc87ZutbpWifJ7h2Zk7winbS7/0Qyy/+yGwwJ8qP+Mvje6IwDwsnQuNZ8XZLKdGq7iDaXIa8hfvT8+wbwURxlUvJhhp1eQ6dI1/n6vtVFN0nOnCSPHdgmAQsNoqbt3RG7gGAPsLwAyn2MMfmbsdkz9JF1p7Lja+brNHmXkaVCU4Jq90f5Qv+TrwJNN+VIy4yxU3m7zvQZg0A5cG0bR5SZDMzceL4AsCtxpV+HBiG9tcBETn/Cw60bl7b7cGQFuZRlrZBPyIoyZ3be6Bscv0lOZSMBJzWyYTeOxNbIT2rR5yv8aSW/taQIcJ6LkZYszT6xe+52x/iAXGlL 4 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDY8wRHQtq9uBzdiAYzpSNmF+nmIHmW+AOeBTDNmdva+CFGIBbB56q7w6GCOhfXs8edrPY4qOcQGaOD0ussIvHnqkVfw8e6CbxnpXKeAuIz7+1V72AhLPzOkif4yPrI6tSYF5nvzq6U4Yk1qFnXiLQjkA1s4EcZH6V0KbHMsu7Mtv3Irspdn8KUI3j2UwZcssFu1EuLHhLNussziRQK9tOg9ixb0U1WXuUJn7Noh9odTAsAt6jLFdr5eN/IINgC9WQqvY/W94Tc2/z5TWR7z382pEkMBR/3sf+nYKA82069tagkyrtJ/YXi00CWU4vjpnMvwPEYcmtCddfCPi8ZIUrn grahamc@Morbo -------------------------------------------------------------------------------- /keys/kylechristensen.keys: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDNIsSfWYa8HZZL9UdomWAwsPNRGMI5lXHvt1+ftmCTXhPatXutXGPBBTqdGyfl6cdcvEyGdTphn1s/xhQ8wV/7IJf1BrLLv5eDLZg30CU0zvcpe/Qx8DCur3Zqo9g6se4PGr76TTq1VHvOk+lalHqn7UyJ0b/Jnw31yw6eISo7bb0wQmv9GFCmY8idz+51lAnchvTMU/KUuMTcNNqurlX0GUz+4tOTSnkyVl5dZm2S1h4Fa2dGljFmmTtMWPqvuBFmYMInec3A2I+OhGgQbyO/Mer6ddDMoeia20BS83zvic2yb9rk6gjZgA8jYwXGbNZvpb9p8TkBD5FleZGh6Cs7 kylechristensen@root.local 2 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDS0ANym55tAs5qjHJecWeT/4/alUjCaRSIUg9ERuzKreoi9FvGdqQBWGzk5oogP0jfFH7w55AGbcldE0MQ0ve0jANZkvQxY2ZwB70F3nYDyFT4EHcNB07vThro0IwzDGodPyE5ICRv6qi0MsFjSnwh+i9haptt9/ooGnM/72vguH0KuyA/naUKx2SgqPPC3SpazA9tHP0JDphoRWeX1t+5ijU6dX4L1L1qb53V9inn3mA/fqYUVIkhmNvYDrRtJcd35D1t8FTX+zdUt9mx5Y/WP/sM8w+HLaF8Agv0ofa9RGFnu1CtdxPVpfJSB9t3ojMRXZsZ6HbEl8E0xKVXeGGN christensen.kyle@gmail.com 3 | -------------------------------------------------------------------------------- /keys/update-keys.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | curl https://github.com/grahamc.keys > grahamc.keys 5 | -------------------------------------------------------------------------------- /kif/buildkite.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: { 2 | services.buildkite-agent = { 3 | enable = true; 4 | meta-data = "test=test"; 5 | tokenPath = "/run/keys/buildkite-token"; 6 | openssh.privateKeyPath = "/dev/null"; 7 | openssh.publicKeyPath = "/dev/null"; 8 | runtimePackages = [ pkgs.gzip pkgs.xz pkgs.gnutar pkgs.nix pkgs.bash ]; 9 | 10 | hooks.environment = '' 11 | set -u 12 | echo "--- :nixos:" 13 | echo "--- :key: Authenticating with Vault" 14 | . /etc/vault.sh 15 | get_token() ( 16 | . /run/keys/buildkite-nixops-vault.env 17 | 18 | login_token=$(${pkgs.vault}/bin/vault write -field token auth/approle/login \ 19 | role_id="$VAULT_ROLE_ID" secret_id="$VAULT_SECRET_ID") 20 | VAULT_TOKEN=$login_token ${pkgs.vault}/bin/vault token create \ 21 | -field token 22 | ) 23 | export VAULT_TOKEN=$(get_token) 24 | set +u 25 | ''; 26 | 27 | hooks.pre-exit = '' 28 | echo "--- :closed_lock_with_key: Revoking Vault tokens" 29 | ${pkgs.vault}/bin/vault token revoke -self 30 | ''; 31 | }; 32 | systemd.services = { 33 | buildkite-agent.requires = [ "vault.target" ]; 34 | buildkite-bootstrap = { 35 | requires = [ "vault.target" ]; 36 | wantedBy = [ "buildkite-agent.service" "multi-user.target" ]; 37 | unitConfig.Before = [ "buildkite-agent.service" ]; 38 | path = [ pkgs.vault ]; 39 | serviceConfig = { 40 | Type = "oneshot"; 41 | RemainAfterExit = true; 42 | }; 43 | 44 | # Write out the buildkite token 45 | # Then, allocate an approle / secret ID login token 46 | script = '' 47 | . /etc/vault.sh 48 | export HOME=/root 49 | secwrite() ( 50 | umask 077 51 | rm -f /run/keys/"$1" 52 | touch /run/keys/"$1" 53 | cat > /run/keys/"$1" 54 | chmod 0400 /run/keys/"$1" 55 | chown "$2" /run/keys/"$1" 56 | ) 57 | 58 | vault kv get -field=token secret/buildkite/grahamc/token \ 59 | | secwrite buildkite-token "buildkite-agent:root" 60 | 61 | ( 62 | printf "export VAULT_ROLE_ID=%s\n" "$(vault read -field=role_id auth/approle/role/buildkite-nixops/role-id)" 63 | 64 | # Note: on two lines so we never pass the secret-id as an argument 65 | printf "export VAULT_SECRET_ID=" 66 | vault write -f -field secret_id auth/approle/role/buildkite-nixops/secret-id 67 | ) | secwrite buildkite-nixops-vault.env "buildkite-agent:root" 68 | ''; 69 | }; 70 | }; 71 | } 72 | -------------------------------------------------------------------------------- /kif/default.nix: -------------------------------------------------------------------------------- 1 | { secrets }: 2 | { config, lib, pkgs, ... }: 3 | { 4 | imports = [ 5 | ./hardware.nix 6 | ../modules/wireguard.nix 7 | (import ./vault.nix { inherit secrets; }) 8 | ./buildkite.nix 9 | #(import ./prometheus.nix { inherit secrets; }) 10 | #./sdr.nix 11 | #(import ./dns.nix { inherit secrets; }) 12 | #../../../NixOS/nixos-org-configurations/macs/host 13 | # possibly breaking r13y.com # (import ../../../andir/local-nix-cache/module.nix) 14 | ]; 15 | 16 | services.openssh.enable = true; 17 | systemd.tmpfiles.rules = ['' 18 | e /tmp/nix-build-* - - - 1d - 19 | '']; 20 | 21 | nix = { 22 | gc = { 23 | automatic = true; 24 | dates = "8:06"; 25 | 26 | options = let 27 | freedGb = 300; 28 | in ''--max-freed "$((${toString freedGb} * 1024**3 - 1024 * $(df -P -k /nix/store | tail -n 1 | ${pkgs.gawk}/bin/awk '{ print $4 }')))"''; 29 | }; 30 | }; 31 | 32 | # Select internationalisation properties. 33 | i18n = { 34 | consoleFont = "Lat2-Terminus16"; 35 | consoleKeyMap = "dvorak"; 36 | defaultLocale = "en_US.UTF-8"; 37 | }; 38 | 39 | # List packages installed in system profile. To search by name, run: 40 | # $ nix-env -qaP | grep wget 41 | environment.systemPackages = with pkgs; [ 42 | emacs26-nox 43 | screen 44 | ]; 45 | 46 | services.nginx = { 47 | enable = true; 48 | virtualHosts."plex.gsc.io" = { 49 | enableACME = true; 50 | forceSSL = true; 51 | locations."/".proxyPass = "http://127.0.0.1:32400"; 52 | }; 53 | virtualHosts."plex.wg.gsc.io" = { 54 | # dns-01 validation? 55 | #enableACME = true; 56 | #forceSSL = true; 57 | locations."/".proxyPass = "http://127.0.0.1:32400"; 58 | }; 59 | 60 | }; 61 | 62 | networking.firewall.allowedTCPPorts = [ 63 | 80 # nginx -> plex 64 | 443 # nginx -> plex 65 | 66 | # Plex: Found at https://github.com/NixOS/nixpkgs/blob/release-17.03/nixos/modules/services/misc/plex.nix#L156 67 | 32400 3005 8324 32469 68 | 9100 # Prometheus NodeExporter 69 | ]; 70 | 71 | networking.firewall.allowedUDPPorts = [ 72 | # Plex: Found at https://github.com/NixOS/nixpkgs/blob/release-17.03/nixos/modules/services/misc/plex.nix#L156 73 | 1900 5353 32410 32412 32413 32414 # UDP 74 | ]; 75 | 76 | 77 | users = { 78 | groups.writemedia = {}; 79 | extraUsers = { 80 | root.openssh.authorizedKeys.keys = [ 81 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINT2IAcpePtxrnk9XiXRRkInvvXm6X00mYFd3rpMSBNW root@Petunia" 82 | ]; 83 | 84 | hydraexport = { 85 | isNormalUser = true; # not really, but need to be able to execute commands 86 | uid = 1004; 87 | openssh.authorizedKeys.keys = [ 88 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOyyr/4fMKQ1fwa5DjFVIHQLchr4EKcOWEI++gYBTbWF root@haumea" 89 | ]; 90 | }; 91 | flexoexport = { 92 | isNormalUser = true; # not really, but need to be able to execute commands 93 | uid = 1005; 94 | openssh.authorizedKeys.keys = [ 95 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINsBEaKyvlXvVGjMT7LEhYs87kVNiTpeVtWNtjnElSSg" 96 | ]; 97 | }; 98 | }; 99 | }; 100 | 101 | # Plex 102 | services.plex = { 103 | enable = true; 104 | package = pkgs.plex.overrideAttrs (x: let 105 | # see https://www.plex.tv/media-server-downloads/ for 64bit rpm 106 | version = "1.18.6.2368-97add474d"; 107 | sha1 = "9df823ae360c5c9508c4ebefa417df66796b353c"; 108 | in { 109 | name = "plex-${version}"; 110 | src = pkgs.fetchurl { 111 | url = "https://downloads.plex.tv/plex-media-server-new/${version}/redhat/plexmediaserver-${version}.x86_64.rpm"; 112 | # url = "https://downloads.plex.tv/plex-media-server-new/${version}/plexmediaserver-${version}.x86_64.rpm"; 113 | inherit sha1; 114 | }; 115 | } 116 | ); 117 | }; 118 | } 119 | -------------------------------------------------------------------------------- /kif/dns.nix: -------------------------------------------------------------------------------- 1 | { secrets }: 2 | { pkgs, ... }: 3 | { 4 | deployment.keys.dns-update-secrets = { 5 | text = '' 6 | AWS_ACCESS_KEY_ID="${secrets.awsdns.key}" 7 | AWS_SECRET_ACCESS_KEY="${secrets.awsdns.secret}" 8 | ZONEID="${secrets.awsdns.zone}" 9 | RECORDSET="${secrets.awsdns.record}" 10 | ''; 11 | user = "dns-update"; 12 | group = "keys"; 13 | permissions = "0600"; 14 | }; 15 | 16 | users.users.dns-update.uid = 405; 17 | 18 | systemd.services.dns-update = { 19 | enable = true; 20 | wantedBy = [ "multi-user.target" ]; 21 | after = [ "network.target" ]; 22 | startAt = "*:0/5"; 23 | serviceConfig = { 24 | User = "dns-update"; 25 | Group = "keys"; 26 | EnvironmentFile = "/run/keys/dns-update-secrets"; 27 | ProtectHome = true; 28 | PrivateTmp = true; 29 | }; 30 | path = with pkgs; [ awscli dnsutils bash nettools iproute jq curl ]; 31 | script = "${./dns.sh}"; 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /kif/dns.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eux 4 | 5 | # More advanced options below 6 | # The Time-To-Live of this recordset 7 | 8 | 9 | dns() { 10 | RECORD=$1 11 | TYPE=$2 12 | IP=$3 13 | TTL=300 14 | # Fill a temp file with valid JSON 15 | TMPFILE=$(mktemp /tmp/temporary-file.XXXXXXXX) 16 | cat > ${TMPFILE} << EOF 17 | { 18 | "Changes":[ 19 | { 20 | "Action":"UPSERT", 21 | "ResourceRecordSet":{ 22 | "ResourceRecords":[ 23 | { 24 | "Value":"$IP" 25 | } 26 | ], 27 | "Name":"$RECORD", 28 | "Type":"$TYPE", 29 | "TTL":$TTL 30 | } 31 | } 32 | ] 33 | } 34 | EOF 35 | 36 | # Update the Hosted Zone record 37 | aws route53 change-resource-record-sets \ 38 | --hosted-zone-id $ZONEID \ 39 | --change-batch file://"$TMPFILE" 40 | echo "" 41 | 42 | # Clean up 43 | rm $TMPFILE 44 | } 45 | 46 | dns "$(hostname).wg.gsc.io" "A" "$(ip --json addr show dev wg0 \ 47 | | jq -r '.[] | .addr_info[] | select(.family == "inet") | .local')" 48 | dns "$(hostname).gsc.io" "A" "$(curl https://ipv4.icanhazip.com)" 49 | dns "$(hostname).gsc.io" "AAAA" "$(curl https://ipv6.icanhazip.com)" 50 | -------------------------------------------------------------------------------- /kif/hardware.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | { 3 | boot.initrd.availableKernelModules = [ "mpt3sas" "xhci_pci" "ahci" "nvme" "usb_storage" "usbhid" "sd_mod" "sr_mod" ]; 4 | boot.initrd.kernelModules = [ ]; 5 | boot.kernelModules = [ "kvm-amd" ]; 6 | boot.extraModulePackages = [ ]; 7 | boot.kernelParams = [ "console=tty1,115200n8" ]; 8 | boot.kernelPackages = pkgs.linuxPackages_latest; 9 | 10 | fileSystems."/" = 11 | { device = "hpool/local/root"; 12 | fsType = "zfs"; 13 | }; 14 | fileSystems."/mnt" = 15 | { device = "/dev/sdh1"; 16 | fsType = "ext4"; 17 | }; 18 | fileSystems."/nix" = 19 | { device = "hpool/local/nix"; 20 | fsType = "zfs"; 21 | }; 22 | fileSystems."/boot" = 23 | { device = "/dev/disk/by-label/BOOT"; 24 | fsType = "vfat"; 25 | }; 26 | 27 | fileSystems."/home/emilyc/timemachine" = 28 | { device = "rpool/time-machine/emily"; 29 | fsType = "zfs"; 30 | options = [ "nofail" ]; 31 | }; 32 | fileSystems."/home/grahamc/timemachine" = 33 | { device = "rpool/time-machine/graham"; 34 | fsType = "zfs"; 35 | options = [ "nofail" ]; 36 | }; 37 | fileSystems."/home/kchristensen/storage" = 38 | { device = "rpool/kyle/storage"; 39 | fsType = "zfs"; 40 | options = [ "nofail" ]; 41 | }; 42 | fileSystems."/media" = 43 | { device = "rpool/media/plex"; 44 | fsType = "zfs"; 45 | options = [ "nofail" ]; 46 | }; 47 | 48 | 49 | networking.hostId = "2016154b"; 50 | swapDevices = [ ]; 51 | boot.loader.systemd-boot.enable = true; 52 | nix.maxJobs = 12; 53 | services.zfs.autoScrub.enable = true; 54 | } 55 | -------------------------------------------------------------------------------- /kif/vault.nix: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Bootstrap steps: 4 | 5 | 0. vault operator init -recovery-shares=1 -key-shares=1 -recovery-threshold=1 -key-threshold=1 6 | 1. vault write aws-personal/config/root access_key=... secret_key=... region=us-east-1 7 | 2. vault write -force aws-personal/config/rotate-root 8 | 3. vault kv put packet/config api_token=- 9 | */ 10 | 11 | { secrets }: 12 | { lib, pkgs, config, ... }: 13 | let 14 | address = (if config.services.vault.tlsKeyFile == null 15 | then "http://" 16 | else "https://") + config.services.vault.address; 17 | 18 | plugin_args = (if config.services.vault.tlsKeyFile == null 19 | then "" 20 | else "-ca-cert=/run/vault/certificate.pem"); 21 | 22 | pluginPkgs = pkgs.callPackage ./plugins.nix {}; 23 | 24 | plugins = { 25 | pki = { 26 | type = "secret"; 27 | }; 28 | 29 | aws = { 30 | type = "secret"; 31 | }; 32 | 33 | packet = { 34 | type = "secret"; 35 | package = pluginPkgs.vault-plugin-secrets-packet; 36 | command = "vault-plugin-secrets-packet"; 37 | 38 | # vault kv put packet/config api_token=- 39 | # vault kv put packet/role/nixos-foundation type=project ttl=3600 max_ttl=3600 project_id=86d5d066-b891-4608-af55-a481aa2c0094 read_only=false 40 | }; 41 | #oauthapp = { 42 | # wl-paste | vault write oauth2/github/config -provider=github client_id=theclientid client_secret=- provider=github 43 | 44 | # scopes: https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/ 45 | # vault write oauth2/bitbucket/config/auth_code_url state=foo scopes=bar,baz 46 | 47 | # vault write oauth2/github/config/auth_code_url state=$(uuidgen) scopes=repo,gist 48 | # now it is broken ... https://github.com/puppetlabs/vault-plugin-secrets-oauthapp/issues/4 49 | # type = "secret"; 50 | # package = pkgs.vault-plugin-secrets-oauthapp; 51 | # command = "vault-plugin-secrets-oauthapp"; 52 | #}; 53 | }; 54 | mounts = { 55 | "approle/" = { 56 | type = "auth"; 57 | plugin = "approle"; 58 | }; 59 | "aws-personal/" = { 60 | type = "secrets"; 61 | plugin = "aws"; 62 | }; 63 | "pki_ca/" = { 64 | type = "secrets"; 65 | plugin = "pki"; 66 | }; 67 | "pki_intermediate/" = { 68 | type = "secrets"; 69 | plugin = "pki"; 70 | }; 71 | "secret/" = { 72 | type = "secrets"; 73 | plugin = "kv"; 74 | }; 75 | 76 | "packet/" = { 77 | type = "secrets"; 78 | plugin = "packet"; 79 | }; 80 | #"oauth2/github/" = { 81 | # type = "secrets"; 82 | # plugin = "oauthapp"; 83 | #}; 84 | }; 85 | 86 | writes = [ 87 | { 88 | path = "auth/approle/role/buildkite-nixops"; 89 | args = { 90 | token_policies = "buildkite-nixops"; 91 | token_ttl = "720h"; 92 | token_max_ttl = "720h"; 93 | }; 94 | } 95 | { 96 | path = "packet/role/nixos-foundation"; 97 | args = { 98 | type = "project"; 99 | ttl = "3600"; 100 | max_ttl = "3600"; 101 | project_id = "86d5d066-b891-4608-af55-a481aa2c0094"; 102 | read_only = "false"; 103 | }; 104 | } 105 | { 106 | path = "aws-personal/roles/nixops-deploy"; 107 | args = { 108 | credential_type = "iam_user"; 109 | policy_document = builtins.toJSON { 110 | Version = "2012-10-17"; 111 | Statement = [ 112 | { 113 | Effect = "Allow"; 114 | Action = "s3:ListBucket"; 115 | Resource = "arn:aws:s3:::mybucket"; 116 | } 117 | { 118 | Effect = "Allow"; 119 | Action = [ "s3:GetObject" "s3:PutObject" ]; 120 | Resource = "arn:aws:s3:::grahamc-nixops-state/*.nixops"; 121 | } 122 | { 123 | Effect = "Allow"; 124 | Action = [ 125 | "dynamodb:GetItem" 126 | "dynamodb:PutItem" 127 | "dynamodb:DeleteItem" 128 | ]; 129 | Resource = "arn:aws:dynamodb:*:*:table/grahamc-nixops-lock"; 130 | } 131 | ]; 132 | }; 133 | }; 134 | } 135 | ]; 136 | 137 | 138 | 139 | policies = { 140 | "buildkite-nixops" = { 141 | document = '' 142 | path "auth/token/create" { 143 | capabilities = [ "create", "update" ] 144 | } 145 | 146 | path "auth/token/revoke-self" { 147 | capabilities = [ "update" ] 148 | } 149 | 150 | path "packet/creds/nixos-foundation" { 151 | capabilities = [ "read" ] 152 | } 153 | 154 | path "aws-personal/creds/nixops-deploy" { 155 | capabilities = [ "read" ] 156 | } 157 | ''; 158 | }; 159 | }; 160 | 161 | pluginsBin = pkgs.runCommand "vault-env" {} 162 | '' 163 | mkdir -p $out/bin 164 | 165 | ${builtins.concatStringsSep "\n" (lib.attrsets.mapAttrsToList (name: info: 166 | if info ? package then 167 | '' 168 | ( 169 | echo "#!/bin/sh" 170 | echo 'exec ${info.package}/bin/${info.command} "$@"' 171 | ) > $out/bin/${info.command} 172 | chmod +x $out/bin/${info.command} 173 | '' else "" 174 | ) plugins)} 175 | ''; 176 | 177 | writeCheckedBash = pkgs.writers.makeScriptWriter { 178 | interpreter = "${pkgs.bash}/bin/bash"; 179 | check = "${pkgs.shellcheck}/bin/shellcheck"; 180 | }; 181 | 182 | vault-setup = writeCheckedBash "/bin/vault-setup" '' 183 | PATH="${pkgs.glibc}/bin:${pkgs.curl}/bin:${pkgs.procps}/bin:${pkgs.vault}/bin:${pkgs.jq}/bin:${pkgs.coreutils}/bin" 184 | 185 | set -eux 186 | 187 | scratch=$(mktemp -d -t tmp.XXXXXXXXXX) 188 | function finish { 189 | rm -rf "$scratch" 190 | } 191 | trap finish EXIT 192 | chmod 0700 "$scratch" 193 | 194 | export VAULT_ADDR=${address} 195 | export VAULT_CACERT=/run/vault/certificate.pem 196 | export HOME=/root 197 | 198 | if [ -f /run/keys/vault-unseal-json ]; then 199 | curl \ 200 | --request PUT \ 201 | --data @/run/keys/vault-unseal-json \ 202 | --cacert /run/vault/certificate.pem \ 203 | "${address}/v1/sys/unseal" 204 | fi 205 | 206 | if [ -f /run/keys/vault-login ]; then 207 | vault login - < /run/keys/vault-login > /dev/null 208 | fi 209 | 210 | rm /run/keys/vault* 211 | 212 | vault secrets disable pki_ca || true 213 | vault secrets disable pki_intermediate || true 214 | 215 | ${builtins.concatStringsSep "\n" (lib.attrsets.mapAttrsToList (name: value: 216 | if value ? package then 217 | '' 218 | expected_sha_256="$(sha256sum ${pluginsBin}/bin/${value.command} | cut -d " " -f1)" 219 | 220 | echo "Re-registering ${name}" 221 | vault plugin register -command "${value.command}" -args="${plugin_args}" -sha256 "$expected_sha_256" ${value.type} ${name} 222 | vault write sys/plugins/reload/backend plugin=${name} 223 | '' else "" 224 | ) plugins)} 225 | 226 | ${builtins.concatStringsSep "\n" (lib.attrsets.mapAttrsToList (path: info: 227 | '' 228 | if ! vault ${info.type} list -format json | jq -e '."${path}"?'; then 229 | vault ${info.type} enable -path=${path} ${info.plugin} 230 | fi 231 | '' 232 | ) mounts)} 233 | 234 | ${builtins.concatStringsSep "\n" (lib.attrsets.mapAttrsToList (name: policy: 235 | '' 236 | echo ${lib.escapeShellArg policy.document} | vault policy write ${name} - 237 | '' 238 | ) policies)} 239 | 240 | ${builtins.concatStringsSep "\n" (builtins.map ({ path, args }: '' 241 | vault write ${lib.escapeShellArg path} \ 242 | ${builtins.concatStringsSep " \\\n" (lib.attrsets.mapAttrsToList (name: value: 243 | " ${lib.escapeShellArg name}=${lib.escapeShellArg value}" 244 | ) args)} 245 | '' 246 | ) writes)} 247 | 248 | #vault write auth/approle/role/buildkite-nixops token_policies="buildkite-nixops" \ 249 | # token_ttl=720h token_max_ttl=720h 250 | 251 | # Replace our selfsigned cert with a vault-made key. 252 | # 720h: the laptop can only run for 30 days without a reboot. 253 | # Note: pki backends are obliterated a section or so above. 254 | vault secrets tune -max-lease-ttl=720h pki_ca 255 | sleep 1 256 | 257 | echo "Generating root certificate" 258 | vault write -field=certificate pki_ca/root/generate/internal \ 259 | common_name="localhost" \ 260 | ttl=719h > "$scratch/root-certificate.pem" 261 | 262 | vault write pki_ca/config/urls \ 263 | issuing_certificates="${address}/v1/pki/ca" \ 264 | crl_distribution_points="${address}/v1/pki/crl" 265 | sleep 1 266 | 267 | echo "Generating intermediate certificate" 268 | vault secrets tune -max-lease-ttl=718h pki_intermediate 269 | vault write -format=json pki_intermediate/intermediate/generate/internal \ 270 | common_name="localhost Intermediate Authority" \ 271 | | jq -r '.data.csr' > "$scratch/pki_intermediate.csr" 272 | 273 | vault write -format=json pki_ca/root/sign-intermediate csr=@"$scratch/pki_intermediate.csr" \ 274 | format=pem_bundle ttl="717h" \ 275 | | jq -r '.data.certificate' > "$scratch/intermediate.cert.pem" 276 | vault write pki_intermediate/intermediate/set-signed certificate=@"$scratch/intermediate.cert.pem" 277 | sleep 1 278 | 279 | echo "Generating Vault's certificate" 280 | vault write pki_intermediate/roles/localhost \ 281 | allowed_domains="localhost" \ 282 | allow_subdomains=false \ 283 | max_ttl="716h" 284 | 285 | vault write -format json pki_intermediate/issue/localhost \ 286 | common_name="localhost" ttl="715h" > "$scratch/short.pem" 287 | 288 | jq -r '.data.certificate' < "$scratch/short.pem" > "$scratch/certificate.server.pem" 289 | jq -r '.data.ca_chain[]' < "$scratch/short.pem" >> "$scratch/certificate.server.pem" 290 | jq -r '.data.private_key' < "$scratch/short.pem" > "$scratch/vault.key" 291 | 292 | mv "$scratch/root-certificate.pem" /run/vault/certificate.pem 293 | mv "$scratch/vault.key" /run/vault/vault.key 294 | mv "$scratch/certificate.server.pem" /run/vault/certificate.server.pem 295 | 296 | pkill --signal HUP --exact vault 297 | ''; 298 | 299 | 300 | in { 301 | deployment.keys = { 302 | "vault-unseal-json".keyFile = secrets.kif.vault-unseal-json; 303 | "vault-login".keyFile = secrets.kif.vault-login; 304 | }; 305 | 306 | environment = { 307 | systemPackages = [ pkgs.vault vault-setup ]; 308 | variables = { 309 | VAULT_ADDR = address; 310 | VAULT_CACERT = "/run/vault/certificate.pem"; 311 | }; 312 | etc."vault.sh".text = '' 313 | export VAULT_ADDR=${address} 314 | export VAULT_CACERT=/run/vault/certificate.pem 315 | ''; 316 | }; 317 | 318 | services.vault = { 319 | enable = true; 320 | address = "localhost:8200"; 321 | storageBackend = "file"; 322 | storagePath = "/rpool/persist/vault/"; 323 | extraConfig = '' 324 | api_addr = "${address}" 325 | plugin_directory = "${pluginsBin}/bin" 326 | log_level = "trace" 327 | ''; 328 | tlsCertFile = "/run/vault/certificate.server.pem"; 329 | tlsKeyFile = "/run/vault/vault.key"; 330 | }; 331 | 332 | systemd.services.vault = { 333 | wantedBy = [ "vault.target" ]; 334 | restartIfChanged = lib.mkForce true; 335 | postStart = '' 336 | set -x 337 | . /etc/vault.sh 338 | while ${pkgs.vault}/bin/vault status; [ $? -eq 1 ]; do 339 | sleep 1 340 | done 341 | ''; 342 | }; 343 | 344 | systemd.services.vault-tls-bootstrap = { 345 | wantedBy = [ "vault.service" "vault.target" ]; 346 | path = with pkgs; [ openssl ]; 347 | unitConfig.Before = [ "vault.service" ]; 348 | serviceConfig = { 349 | Type = "oneshot"; 350 | RemainAfterExit = true; 351 | }; 352 | 353 | script = '' 354 | set -eux 355 | rm -rf /run/vault 356 | mkdir /run/vault 357 | 358 | touch /run/vault/vault.key 359 | chmod 0600 /run/vault/vault.key 360 | 361 | touch /run/vault/certificate.pem 362 | chmod 0644 /run/vault/certificate.pem 363 | 364 | openssl req -x509 -subj /CN=localhost -nodes -newkey rsa:4096 -days 1 \ 365 | -keyout /run/vault/vault.key \ 366 | -out /run/vault/certificate.pem 367 | 368 | cp /run/vault/certificate.pem /run/vault/certificate.server.pem 369 | 370 | chown ${config.systemd.services.vault.serviceConfig.User}:${config.systemd.services.vault.serviceConfig.Group} /run/vault/{vault.key,certificate.pem} 371 | ${pkgs.procps}/bin/pkill --signal HUP vault || true 372 | sleep 1 373 | ${pkgs.procps}/bin/pkill --signal HUP vault || true 374 | ''; 375 | }; 376 | systemd.services.vault-unlock = { 377 | wantedBy = [ "vault.service" "vault.target" "multi-user.target" ]; 378 | wants = [ "vault-unseal-json-key.service" "vault-login-key.service" ]; 379 | unitConfig.After = [ "vault-tls-bootstrap.service" "vault.service" "vault-unseal-json-key.service" "vault-login-key.service" ]; 380 | serviceConfig = { 381 | Type = "oneshot"; 382 | RemainAfterExit = true; 383 | }; 384 | 385 | script = '' 386 | . /etc/vault.sh 387 | ${vault-setup}/bin/vault-setup 388 | ''; 389 | }; 390 | 391 | systemd.targets.vault = {}; 392 | } 393 | -------------------------------------------------------------------------------- /lord-nibbler/default.nix: -------------------------------------------------------------------------------- 1 | { secrets }: 2 | { ... }: 3 | { 4 | imports = [ 5 | ./hardware.nix 6 | (import ./router.nix { inherit secrets; }) 7 | ./pxe-image.nix 8 | ]; 9 | 10 | services.fail2ban = { 11 | enable = true; 12 | }; 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /lord-nibbler/hardware.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, ... }: 2 | 3 | { 4 | imports = 5 | [ 6 | ]; 7 | boot.kernelParams = [ "console=ttyS0,115200n8" ]; 8 | boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "ehci_pci" "sd_mod" "sdhci_pci" ]; 9 | boot.kernelModules = [ "kvm-amd" ]; 10 | boot.extraModulePackages = [ ]; 11 | boot.loader.grub.device = "/dev/sda"; 12 | 13 | fileSystems."/" = 14 | { device = "/dev/disk/by-label/nixos"; 15 | fsType = "ext4"; 16 | }; 17 | 18 | swapDevices = 19 | [ { device = "/dev/disk/by-label/swap"; } 20 | ]; 21 | 22 | nix.maxJobs = lib.mkDefault 4; 23 | powerManagement.cpuFreqGovernor = "ondemand"; 24 | system.stateVersion = "17.09"; 25 | boot.loader.grub.enable = true; 26 | boot.loader.grub.version = 2; 27 | } 28 | -------------------------------------------------------------------------------- /lord-nibbler/pxe-image.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, lib, ... }: 2 | let 3 | nixpkgs_path = ./../../nixpkgs; # pkgs.path; 4 | 5 | mkNetboot = config: let 6 | config_evaled = import "${nixpkgs_path}/nixos/lib/eval-config.nix" config; 7 | build = config_evaled.config.system.build; 8 | kernelTarget = config_evaled.pkgs.stdenv.platform.kernelTarget; 9 | in 10 | pkgs.symlinkJoin { 11 | name="netboot"; 12 | paths=[ 13 | build.netbootRamdisk 14 | build.kernel 15 | build.netbootIpxeScript 16 | ]; 17 | postBuild = '' 18 | mkdir -p $out/nix-support 19 | echo "file ${kernelTarget} $out/${kernelTarget}" >> $out/nix-support/hydra-build-products 20 | echo "file initrd $out/initrd" >> $out/nix-support/hydra-build-products 21 | echo "file ipxe $out/netboot.ipxe" >> $out/nix-support/hydra-build-products 22 | ''; 23 | }; 24 | in { 25 | services.nginx = { 26 | enable = true; 27 | virtualHosts.default = { 28 | default = true; 29 | root = ./webroot; 30 | }; 31 | 32 | }; 33 | 34 | services.tftpd = let 35 | netboot_x86_64 = mkNetboot { 36 | system = "x86_64-linux"; 37 | modules = [ 38 | "${nixpkgs_path}/nixos/modules/installer/netboot/netboot-minimal.nix" 39 | { boot.kernelParams = [ "console=ttyS0,115200n8" ]; } 40 | ]; 41 | }; 42 | 43 | netboot_aarch64 = mkNetboot { 44 | system = "aarch64-linux"; 45 | modules = [ 46 | "${nixpkgs_path}/nixos/modules/installer/netboot/netboot-minimal.nix" 47 | { boot.kernelParams = [ "console=ttyS0,115200n8" ]; } 48 | ]; 49 | }; 50 | in { 51 | enable = true; 52 | #ln -s ${netboot_x86_64} $out/nixos-x86_64 53 | #ln -s ${netboot_aarch64} $out/nixos-aarch64 54 | 55 | path = pkgs.runCommand "dhcp-pxe-root" {} 56 | '' 57 | mkdir $out 58 | ln -s ${pkgs.ipxe} $out/ipxe 59 | ''; 60 | }; 61 | } 62 | -------------------------------------------------------------------------------- /lord-nibbler/router.nix: -------------------------------------------------------------------------------- 1 | { secrets }: 2 | { config, lib, pkgs, ... }: 3 | let 4 | prometheus-surfboard-exporter = pkgs.callPackage ({ stdenv, buildGoPackage, fetchFromGitHub }: 5 | buildGoPackage rec { 6 | name = "surfboard_exporter-${version}"; 7 | version = "2.0.0"; 8 | 9 | goPackagePath = "github.com/ipstatic/surfboard_exporter"; 10 | 11 | src = fetchFromGitHub { 12 | rev = version; 13 | owner = "ipstatic"; 14 | repo = "surfboard_exporter"; 15 | sha256 = "11qms26648nwlwslnaflinxcr5rnp55s908rm1qpnbz0jnxf5ipw"; 16 | }; 17 | 18 | meta = with stdenv.lib; { 19 | description = "Arris Surfboard signal metrics exporter"; 20 | homepage = https://github.com/ipstatic/surfboard_exporter; 21 | license = licenses.mit; 22 | maintainers = with maintainers; [ disassembler ]; 23 | platforms = platforms.unix; 24 | }; 25 | }) {}; 26 | 27 | externalInterface = "enp1s0"; 28 | 29 | vlans = { 30 | nougat = { 31 | id = 1; # this was commented, and set to 40 32 | name = "enp2s0"; 33 | interface = "enp2s0"; 34 | firstoctets = "10.5.3"; 35 | subnet = 24; 36 | }; 37 | 38 | admin-wifi = { 39 | id = 10; 40 | name = "adminwifi"; 41 | interface = "enp3s0"; 42 | firstoctets = "10.5.5"; # TODO: Validate ends in no dot 43 | subnet = 24; 44 | }; 45 | 46 | nougat-wifi = { 47 | id = 41; 48 | name = "nougatwifi"; 49 | interface = "enp3s0"; 50 | firstoctets = "10.5.4"; # TODO: Validate ends in no dot 51 | subnet = 24; 52 | }; 53 | 54 | ofborg = { 55 | id = 50; 56 | name = "ofborg"; 57 | interface = "enp3s0"; 58 | firstoctets = "10.88.88"; 59 | subnet = 24; 60 | }; 61 | 62 | target = { 63 | id = 54; 64 | name = "target"; 65 | interface = "enp3s0"; 66 | firstoctets = "10.54.54"; 67 | subnet = 24; 68 | }; 69 | 70 | hue = { 71 | id = 80; 72 | name = "hue"; 73 | interface = "enp3s0"; 74 | firstoctets = "10.80.80"; 75 | subnet = 24; 76 | }; 77 | 78 | roku = { 79 | id = 81; 80 | name = "roku"; 81 | interface = "enp3s0"; 82 | firstoctets = "10.80.81"; 83 | subnet = 24; 84 | }; 85 | }; 86 | 87 | in { 88 | boot.kernel.sysctl = { 89 | "net.ipv4.conf.all.forwarding" = 1; 90 | "net.ipv4.conf.default.forwarding" = 1; 91 | 92 | "net.ipv6.conf.all.forwarding" = 1; 93 | "net.ipv6.conf.default.forwarding" = 1; 94 | "net.ipv6.conf.${externalInterface}.accept_ra" = 2; 95 | }; 96 | 97 | # Basically, we want to allow some ports only locally and refuse 98 | # them externally. 99 | # 100 | # We don't make a distinction between udp and tcp, since hopefully 101 | # we won't have that complex of a configuration. 102 | networking.firewall.extraCommands = let 103 | dropPortNoLog = port: 104 | '' 105 | ip46tables -A nixos-fw -p tcp \ 106 | --dport ${toString port} -j nixos-fw-refuse 107 | ip46tables -A nixos-fw -p udp \ 108 | --dport ${toString port} -j nixos-fw-refuse 109 | ''; 110 | 111 | refusePortOnInterface = port: interface: 112 | '' 113 | ip46tables -A nixos-fw -i ${interface} -p tcp \ 114 | --dport ${toString port} -j nixos-fw-log-refuse 115 | ip46tables -A nixos-fw -i ${interface} -p udp \ 116 | --dport ${toString port} -j nixos-fw-log-refuse 117 | ''; 118 | 119 | refusePortOnInterfaceHighPriority = port: interface: 120 | '' 121 | ip46tables -I nixos-fw -i ${interface} -p tcp \ 122 | --dport ${toString port} -j nixos-fw-log-refuse 123 | ip46tables -I nixos-fw -i ${interface} -p udp \ 124 | --dport ${toString port} -j nixos-fw-log-refuse 125 | ''; 126 | 127 | acceptPortOnInterface = port: interface: 128 | '' 129 | ip46tables -A nixos-fw -i ${interface} -p tcp \ 130 | --dport ${toString port} -j nixos-fw-accept 131 | ip46tables -A nixos-fw -i ${interface} -p udp \ 132 | --dport ${toString port} -j nixos-fw-accept 133 | ''; 134 | 135 | privatelyAcceptPort = port: 136 | lib.concatMapStrings 137 | (interface: acceptPortOnInterface port interface) 138 | [vlans.nougat.name vlans.nougat-wifi.name vlans.admin-wifi.name]; 139 | 140 | publiclyRejectPort = port: 141 | refusePortOnInterface port externalInterface; 142 | 143 | allowPortOnlyPrivately = port: 144 | '' 145 | ${privatelyAcceptPort port} 146 | ${publiclyRejectPort port} 147 | ''; 148 | 149 | # IPv6 flat forwarding. For ipv4, see nat.forwardPorts 150 | forwardPortToHost = port: interface: proto: host: 151 | '' 152 | ip6tables -A FORWARD -i ${interface} \ 153 | -p ${proto} -d ${host} \ 154 | --dport ${toString port} -j ACCEPT 155 | ''; 156 | in lib.concatStrings [ 157 | # (refusePortOnInterfaceHighPriority 22 vlans.target.name) 158 | (lib.concatMapStrings allowPortOnlyPrivately 159 | [ 160 | 161 | 53 # knot dns resolver 162 | 80 # nginx for tftp handoff 163 | 67 # DHCP? 164 | 68 # DHCP? 165 | 69 # tftp 166 | config.services.netatalk.port 167 | 5353 # avahi 168 | 169 | 9100 # node exporter 170 | 9130 # unifi exporter 171 | 9239 # surfboard exporter 172 | 173 | # https://help.ubnt.com/hc/en-us/articles/204910084-UniFi-Change-Default-Ports-for-Controller-and-UAPs 174 | # TCP: 175 | 6789 # Port for throughput tests 176 | 8080 # Port for UAP to inform controller. 177 | 8880 # Port for HTTP portal redirect, if guest portal is enabled. 178 | 8843 # Port for HTTPS portal redirect, ditto. 179 | 8443 # Port for HTTPS portal redirect, ditto. 180 | #UDP: 181 | 3478 # UDP port used for STUN. 182 | 10001 # UDP port used for device discovery. 183 | 184 | # Plex: Found at https://github.com/NixOS/nixpkgs/blob/release-17.03/nixos/modules/services/misc/plex.nix#L156 185 | 3005 8324 32469 # TCP, 32400 is allowed on all interfaces 186 | 1900 5353 32410 32412 32413 32414 # UDP 187 | ]) 188 | (lib.concatMapStrings dropPortNoLog 189 | [ 190 | 23 # Common from public internet 191 | 143 # Common from public internet 192 | 139 # From RT AP 193 | 515 # From RT AP 194 | # 9100 # From RT AP 195 | ]) 196 | (let 197 | crossblock = builtins.attrNames vlans; 198 | allowDirectional = [ 199 | ["nougat" "nougat-wifi"] 200 | ["nougat-wifi" "nougat"] 201 | ["nougat-wifi" "ofborg"] 202 | ["roku" "nougat"] 203 | ["nougat" "roku"] 204 | ]; 205 | in lib.concatStrings (lib.flatten (builtins.map 206 | (src: 207 | builtins.map 208 | (dest: 209 | if builtins.elem [src dest] allowDirectional then "" 210 | else if src == dest then "" 211 | else "ip46tables -I FORWARD -i ${vlans."${src}".name} -o ${vlans."${dest}".name} -j DROP\n" 212 | ) 213 | crossblock 214 | ) 215 | crossblock))) 216 | '' 217 | # allow icmp6, because it unbreaks the internet 218 | ip6tables -A FORWARD -p icmpv6 -j ACCEPT 219 | 220 | # allow from trusted interfaces 221 | ip46tables -A FORWARD -m state --state NEW -i ${vlans.admin-wifi.name} -o ${externalInterface} -j ACCEPT 222 | ip46tables -A FORWARD -m state --state NEW -i ${vlans.nougat-wifi.name} -o ${externalInterface} -j ACCEPT 223 | ip46tables -A FORWARD -m state --state NEW -i ${vlans.nougat.name} -o ${externalInterface} -j ACCEPT 224 | ip46tables -A FORWARD -m state --state NEW -i ${vlans.ofborg.name} -o ${externalInterface} -j ACCEPT 225 | ip46tables -A FORWARD -m state --state NEW -i ${vlans.target.name} -o ${externalInterface} -j ACCEPT 226 | ip46tables -A FORWARD -m state --state NEW -i ${vlans.roku.name} -o ${externalInterface} -j ACCEPT 227 | 228 | # allow traffic with existing state 229 | ip46tables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT 230 | # block forwarding from external interface 231 | ip6tables -A FORWARD -i ${externalInterface} -j DROP 232 | '' 233 | ]; 234 | networking.firewall.allowedTCPPorts = [ 235 | 32400 # plex 236 | 2200 # turner's SSH port 237 | ]; 238 | networking.firewall.allowedUDPPorts = [ 41741 ]; # Wireguard on ogden 239 | networking.firewall.allowPing = true; 240 | networking.interfaces."${vlans.nougat.name}" = { 241 | ipv4.addresses = [{ 242 | address = "${vlans.nougat.firstoctets}.1"; 243 | prefixLength = vlans.nougat.subnet; 244 | }]; 245 | }; 246 | 247 | networking.interfaces."${vlans.admin-wifi.name}" = { 248 | ipv4.addresses = [{ 249 | address = "${vlans.admin-wifi.firstoctets}.1"; 250 | prefixLength = vlans.admin-wifi.subnet; 251 | }]; 252 | }; 253 | 254 | networking.interfaces."${vlans.nougat-wifi.name}" = { 255 | ipv4.addresses = [{ 256 | address = "${vlans.nougat-wifi.firstoctets}.1"; 257 | prefixLength = vlans.nougat-wifi.subnet; 258 | }]; 259 | }; 260 | 261 | networking.interfaces."${vlans.ofborg.name}" = { 262 | ipv4.addresses = [{ 263 | address = "${vlans.ofborg.firstoctets}.1"; 264 | prefixLength = vlans.ofborg.subnet; 265 | }]; 266 | }; 267 | 268 | networking.interfaces."${vlans.target.name}" = { 269 | ipv4.addresses = [{ 270 | address = "${vlans.target.firstoctets}.1"; 271 | prefixLength = vlans.target.subnet; 272 | }]; 273 | }; 274 | 275 | networking.interfaces."${vlans.hue.name}" = { 276 | ipv4.addresses = [{ 277 | address = "${vlans.hue.firstoctets}.1"; 278 | prefixLength = vlans.hue.subnet; 279 | }]; 280 | }; 281 | 282 | networking.interfaces."${vlans.roku.name}" = { 283 | ipv4.addresses = [{ 284 | address = "${vlans.roku.firstoctets}.1"; 285 | prefixLength = vlans.roku.subnet; 286 | }]; 287 | }; 288 | 289 | services.kresd = { 290 | enable = true; 291 | interfaces = [ "127.0.0.1" "${vlans.nougat.firstoctets}.1" "${vlans.nougat-wifi.firstoctets}.1" "::" ]; 292 | extraConfig = if true then '' 293 | modules = { 294 | 'policy', -- Block queries to local zones/bad sites 295 | 'stats', -- Track internal statistics 296 | 'predict', -- Prefetch expiring/frequent records 297 | } 298 | 299 | -- Smaller cache size 300 | cache.size = 10 * MB 301 | '' else '' 302 | modules = { 303 | http = { 304 | host = 'localhost', 305 | port = 8053, 306 | -- geoip = 'GeoLite2-City.mmdb' -- Optional, see 307 | -- e.g. https://dev.maxmind.com/geoip/geoip2/geolite2/ 308 | -- and install mmdblua library 309 | } 310 | } 311 | ''; 312 | }; 313 | 314 | networking.vlans = { 315 | # !!! Make nougat actually a vlan 316 | #"${vlans.nougat.name}" = { 317 | # interface = vlans.nougat.interface; 318 | # id = vlans.nougat.id; 319 | #}; 320 | 321 | "${vlans.admin-wifi.name}" = { 322 | interface = vlans.admin-wifi.interface; 323 | id = vlans.admin-wifi.id; 324 | }; 325 | 326 | "${vlans.nougat-wifi.name}" = { 327 | interface = vlans.nougat-wifi.interface; 328 | id = vlans.nougat-wifi.id; 329 | }; 330 | 331 | "${vlans.ofborg.name}" = { 332 | interface = vlans.ofborg.interface; 333 | id = vlans.ofborg.id; 334 | }; 335 | 336 | "${vlans.target.name}" = { 337 | interface = vlans.target.interface; 338 | id = vlans.target.id; 339 | }; 340 | 341 | "${vlans.hue.name}" = { 342 | interface = vlans.hue.interface; 343 | id = vlans.hue.id; 344 | }; 345 | 346 | "${vlans.roku.name}" = { 347 | interface = vlans.roku.interface; 348 | id = vlans.roku.id; 349 | }; 350 | 351 | }; 352 | networking.nat = { 353 | enable = true; 354 | externalInterface = externalInterface; 355 | internalInterfaces = [ 356 | vlans.admin-wifi.name 357 | vlans.nougat.name 358 | vlans.nougat-wifi.name 359 | vlans.ofborg.name 360 | vlans.target.name 361 | vlans.hue.name 362 | vlans.roku.name 363 | ]; 364 | internalIPs = [ 365 | "${vlans.admin-wifi.firstoctets}.0/${toString vlans.admin-wifi.subnet}" 366 | "${vlans.nougat.firstoctets}.0/${toString vlans.nougat.subnet}" 367 | "${vlans.nougat-wifi.firstoctets}.0/${toString vlans.nougat-wifi.subnet}" 368 | "${vlans.ofborg.firstoctets}.0/${toString vlans.ofborg.subnet}" 369 | "${vlans.target.firstoctets}.0/${toString vlans.target.subnet}" 370 | "${vlans.hue.firstoctets}.0/${toString vlans.hue.subnet}" 371 | "${vlans.roku.firstoctets}.0/${toString vlans.roku.subnet}" 372 | ]; 373 | 374 | forwardPorts = [ 375 | { destination = "10.5.3.105:32400"; proto = "tcp"; sourcePort = 32400; } 376 | { destination = "10.5.3.105:22"; proto = "tcp"; sourcePort = 22; } 377 | { destination = "10.5.3.105:41741"; proto = "udp"; sourcePort = 41741; } 378 | { destination = "10.5.4.50:22"; proto = "tcp"; sourcePort = 2200; } # turner 379 | ]; 380 | }; 381 | 382 | #services.bird2 = { 383 | # enable = true; 384 | # router id "10.5.4.1"; 385 | # 386 | # protocol device { 387 | # scan time 10; 388 | # } 389 | # protocol 390 | #} 391 | 392 | services.radvd = { 393 | enable = true; # ipv6 is just ... awesome and the future, man. 394 | config = '' 395 | interface ${vlans.nougat.name} 396 | { 397 | AdvSendAdvert on; 398 | RDNSS 2606:4700:4700::1111 {}; 399 | prefix ::/64 400 | { 401 | AdvRouterAddr on; 402 | }; 403 | }; 404 | 405 | interface ${vlans.nougat-wifi.name} 406 | { 407 | AdvSendAdvert on; 408 | RDNSS 2606:4700:4700::1111 {}; 409 | prefix ::/64 410 | { 411 | AdvRouterAddr on; 412 | }; 413 | }; 414 | interface ${vlans.target.name} 415 | { 416 | AdvSendAdvert on; 417 | RDNSS 2606:4700:4700::1111 {}; 418 | prefix ::/64 419 | { 420 | AdvRouterAddr on; 421 | }; 422 | }; 423 | interface ${vlans.ofborg.name} 424 | { 425 | AdvSendAdvert on; 426 | RDNSS 2606:4700:4700::1111 {}; 427 | prefix ::/64 428 | { 429 | AdvRouterAddr on; 430 | }; 431 | }; 432 | ''; 433 | }; 434 | 435 | networking.dhcpcd.extraConfig = '' 436 | xidhwaddr 437 | debug 438 | interface enp1s0 439 | ia_pd 1/::/56 enp2s0/2 nougatwifi/3 target/4 ${vlans.ofborg.name}/5 440 | ia_na 2 441 | ''; 442 | 443 | services.dhcpd4 = { 444 | enable = true; 445 | interfaces = [ 446 | vlans.admin-wifi.name 447 | vlans.nougat.name 448 | vlans.nougat-wifi.name 449 | vlans.ofborg.name 450 | vlans.target.name 451 | vlans.hue.name 452 | vlans.roku.name 453 | ]; 454 | extraConfig = '' 455 | max-lease-time 604800; 456 | default-lease-time 604800; 457 | 458 | subnet ${vlans.nougat.firstoctets}.0 netmask 255.255.255.0 { 459 | option subnet-mask 255.255.255.0; 460 | option broadcast-address ${vlans.nougat.firstoctets}.255; 461 | option routers ${vlans.nougat.firstoctets}.1; 462 | option domain-name-servers ${vlans.nougat.firstoctets}.1; 463 | # option domain-name "${secrets.router.domainname}"; 464 | if exists user-class and option user-class = "iPXE" { 465 | filename "http://${vlans.nougat.firstoctets}.1/nixos/netboot.ipxe"; 466 | } else { 467 | filename "ipxe/undionly.kpxe"; 468 | } 469 | 470 | next-server ${vlans.nougat.firstoctets}.1; 471 | range ${vlans.nougat.firstoctets}.100 ${vlans.nougat.firstoctets}.200; 472 | } 473 | 474 | subnet ${vlans.admin-wifi.firstoctets}.0 netmask 255.255.255.0 { 475 | option subnet-mask 255.255.255.0; 476 | option broadcast-address ${vlans.admin-wifi.firstoctets}.255; 477 | option routers ${vlans.admin-wifi.firstoctets}.1; 478 | option domain-name-servers ${vlans.admin-wifi.firstoctets}.1; 479 | range ${vlans.admin-wifi.firstoctets}.100 ${vlans.admin-wifi.firstoctets}.200; 480 | } 481 | 482 | 483 | subnet ${vlans.nougat-wifi.firstoctets}.0 netmask 255.255.255.0 { 484 | option subnet-mask 255.255.255.0; 485 | option broadcast-address ${vlans.nougat-wifi.firstoctets}.255; 486 | option routers ${vlans.nougat-wifi.firstoctets}.1; 487 | option domain-name-servers ${vlans.nougat-wifi.firstoctets}.1; 488 | range ${vlans.nougat-wifi.firstoctets}.100 ${vlans.nougat-wifi.firstoctets}.200; 489 | 490 | group { 491 | host turner { # garage door opener lol 492 | hardware ethernet b8:27:eb:9b:4a:23; 493 | fixed-address 10.5.4.50; 494 | } 495 | } 496 | } 497 | 498 | subnet ${vlans.ofborg.firstoctets}.0 netmask 255.255.255.0 { 499 | option subnet-mask 255.255.255.0; 500 | option broadcast-address ${vlans.ofborg.firstoctets}.255; 501 | option routers ${vlans.ofborg.firstoctets}.1; 502 | option domain-name-servers 8.8.8.8; 503 | range ${vlans.ofborg.firstoctets}.100 ${vlans.ofborg.firstoctets}.200; 504 | } 505 | 506 | subnet ${vlans.target.firstoctets}.0 netmask 255.255.255.0 { 507 | option subnet-mask 255.255.255.0; 508 | option broadcast-address ${vlans.target.firstoctets}.255; 509 | option routers ${vlans.target.firstoctets}.1; 510 | option domain-name-servers 8.8.8.8; 511 | range ${vlans.target.firstoctets}.100 ${vlans.target.firstoctets}.200; 512 | } 513 | 514 | subnet ${vlans.hue.firstoctets}.0 netmask 255.255.255.0 { 515 | option subnet-mask 255.255.255.0; 516 | option broadcast-address ${vlans.hue.firstoctets}.255; 517 | option routers ${vlans.hue.firstoctets}.1; 518 | range ${vlans.hue.firstoctets}.100 ${vlans.hue.firstoctets}.200; 519 | } 520 | 521 | subnet ${vlans.roku.firstoctets}.0 netmask 255.255.255.0 { 522 | option subnet-mask 255.255.255.0; 523 | option broadcast-address ${vlans.roku.firstoctets}.255; 524 | option routers ${vlans.roku.firstoctets}.1; 525 | range ${vlans.roku.firstoctets}.100 ${vlans.roku.firstoctets}.200; 526 | } 527 | ''; 528 | }; 529 | 530 | services.unifi = { 531 | enable = true; 532 | openPorts = false; 533 | # unifiPackage = pkgs.unifiStable; 534 | }; 535 | 536 | services.prometheus.exporters.unifi = { 537 | enable = true; 538 | inherit (secrets.unifi_exporter_opts) unifiAddress unifiInsecure 539 | unifiUsername unifiPassword; 540 | }; 541 | 542 | 543 | systemd.services.prometheus-surfboard-exporter = { 544 | wantedBy = [ "multi-user.target" ]; 545 | after = [ "network.target" ]; 546 | serviceConfig = { 547 | Restart = "always"; 548 | PrivateTmp = true; 549 | WorkingDirectory = "/tmp"; 550 | ExecStart = '' 551 | ${prometheus-surfboard-exporter}/bin/surfboard_exporter \ 552 | --web.listen-address 0.0.0.0:9239 \ 553 | --modem-address 192.168.100.1 \ 554 | --timeout 15s 555 | ''; 556 | }; 557 | }; 558 | 559 | services.avahi.enable = true; 560 | 561 | systemd.services.forward-hairpin-2200-to-turner-22 = { 562 | wantedBy = [ "multi-user.target" ]; 563 | script = '' 564 | set -euxo pipefail 565 | exec ${pkgs.socat}/bin/socat TCP-LISTEN:2200,fork TCP:10.5.4.50:22 566 | ''; 567 | }; 568 | 569 | } 570 | -------------------------------------------------------------------------------- /lord-nibbler/webroot/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /modules/default.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | { 3 | imports = [ 4 | ./standard 5 | ./ofborg.nix 6 | ./role.nix 7 | ./learn/service.nix 8 | ]; 9 | } 10 | -------------------------------------------------------------------------------- /modules/learn/default.nix: -------------------------------------------------------------------------------- 1 | { stdenv, python3Packages, makeWrapper }: 2 | stdenv.mkDerivation { 3 | name = "learn"; 4 | src = ./.; 5 | 6 | buildInputs = [ 7 | makeWrapper 8 | ] ++ (with python3Packages; [ python flake8 ]); 9 | 10 | doCheck = true; 11 | checkPhase = '' 12 | flake8 ./learn.py 13 | ''; 14 | 15 | installPhase = '' 16 | mkdir -p $out/bin/ 17 | mv ./learn.py $out/bin/learn 18 | ''; 19 | } 20 | -------------------------------------------------------------------------------- /modules/learn/learn.py: -------------------------------------------------------------------------------- 1 | #!python3 2 | 3 | import os 4 | import sys 5 | import json 6 | import subprocess 7 | 8 | 9 | def eprint(*args, **kwargs): 10 | print(*args, file=sys.stderr, **kwargs) 11 | 12 | 13 | dataset = {} 14 | 15 | # Host key 16 | if os.path.isfile("/etc/ssh/ssh_host_ed25519_key.pub"): 17 | with open("/etc/ssh/ssh_host_ed25519_key.pub") as fp: 18 | dataset["ssh_host_key"] = fp.read().strip() 19 | 20 | # Root SSH Public Key 21 | if os.path.isfile("/root/.ssh/id_ed25519.pub"): 22 | with open("/root/.ssh/id_ed25519.pub") as fp: 23 | dataset["ssh_root_key"] = fp.read().strip() 24 | 25 | # Wireguard public keys 26 | dataset["wireguard_public_keys"] = {} 27 | try: 28 | output = subprocess.run(["wg", "show", "all", "public-key"], 29 | capture_output=True) 30 | if output.stderr != b'': 31 | eprint("wg show all public-key produced output on stderr:") 32 | eprint(output.stderr) 33 | 34 | if output.returncode == 0: 35 | for line in output.stdout.decode('utf8').strip().split("\n"): 36 | parts = line.split("\t") 37 | if len(parts) == 2: 38 | interface = parts[0] 39 | publickey = parts[1] 40 | dataset['wireguard_public_keys'][interface] = publickey 41 | else: 42 | eprint("wg output '{}' doesn't split to two tab-separated" 43 | "components!".format(line)) 44 | else: 45 | eprint("wg show all public-key exited non-zero: {}" 46 | .format(output.returncode)) 47 | except FileNotFoundError as e: 48 | eprint("wg failed to start: {}".format(e)) 49 | print(json.dumps(dataset, indent=4)) 50 | -------------------------------------------------------------------------------- /modules/learn/service.nix: -------------------------------------------------------------------------------- 1 | { lib, pkgs, ... }: 2 | let 3 | learn = pkgs.callPackage ./. {}; 4 | in { 5 | options.about = lib.mkOption { 6 | description = ""; 7 | type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: { 8 | options = { 9 | name = lib.mkOption { 10 | type = lib.types.str; 11 | default = name; 12 | }; 13 | 14 | ssh_host_key = lib.mkOption { 15 | type = lib.types.str; 16 | }; 17 | 18 | ssh_root_key = lib.mkOption { 19 | type = lib.types.str; 20 | }; 21 | 22 | wireguard_public_keys = lib.mkOption { 23 | type = (lib.types.attrsOf lib.types.str); 24 | default = {}; 25 | }; 26 | }; 27 | })); 28 | }; 29 | 30 | config = { 31 | systemd.services.learn = { 32 | description = "Intake learning data collection"; 33 | wantedBy = [ "multi-user.target" ]; 34 | 35 | path = with pkgs; [ wireguard ]; 36 | 37 | script = '' 38 | ${learn}/bin/learn | ${pkgs.moreutils}/bin/sponge /run/about.json 39 | ''; 40 | 41 | serviceConfig = { 42 | Type = "oneshot"; 43 | }; 44 | }; 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /modules/ofborg.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, ... }: 2 | let 3 | inherit (lib) mkIf mkOption types; 4 | cfg = config.services.ofborg; 5 | 6 | config_json = let 7 | orig = builtins.fromJSON (builtins.readFile ./../../ofborg/config.prod.json); 8 | runnercfg = orig.runner // { 9 | identity = config.networking.hostName; 10 | }; 11 | in builtins.toFile "config.json" (builtins.toJSON (orig // { runner = runnercfg; })); 12 | 13 | src = import ./../../ofborg {}; 14 | 15 | rustborgservice = name: bin: conf: { 16 | "grahamcofborg-${name}" = { 17 | enable = true; 18 | after = [ "network.target" "network-online.target" "rabbitmq.service" ]; 19 | wants = [ "network-online.target" ]; 20 | wantedBy = [ "multi-user.target" ]; 21 | 22 | path = with pkgs; [ 23 | nix 24 | git 25 | curl 26 | bash 27 | ]; 28 | 29 | serviceConfig = { 30 | User = "gc-of-borg"; 31 | Group = "gc-of-borg"; 32 | PrivateTmp = true; 33 | WorkingDirectory = "/var/lib/gc-of-borg"; 34 | Restart = "always"; 35 | RestartSec = "10s"; 36 | }; 37 | 38 | preStart = '' 39 | mkdir -p ./.nix-test 40 | ''; 41 | 42 | script = '' 43 | export HOME=/var/lib/gc-of-borg; 44 | export NIX_REMOTE=daemon; 45 | export NIX_PATH=nixpkgs=/run/current-system/nixpkgs; 46 | git config --global user.email "graham+cofborg@grahamc.com" 47 | git config --global user.name "GrahamCOfBorg" 48 | export RUST_BACKTRACE=1 49 | ${bin} ${conf} 50 | ''; 51 | }; 52 | }; 53 | 54 | ifEvaluator = service: if cfg.enable_evaluator 55 | then service 56 | else {}; 57 | 58 | ifBuilder = service: if cfg.enable_builder 59 | then service 60 | else {}; 61 | 62 | in { 63 | options = { 64 | services.ofborg = { 65 | enable = mkOption { 66 | type = types.bool; 67 | default = false; 68 | }; 69 | 70 | enable_evaluator = mkOption { 71 | type = types.bool; 72 | default = false; 73 | }; 74 | 75 | enable_builder = mkOption { 76 | type = types.bool; 77 | default = false; 78 | }; 79 | }; 80 | }; 81 | 82 | config = mkIf cfg.enable rec { 83 | users.users.gc-of-borg = { 84 | description = "GC Of Borg Workers"; 85 | home = "/var/lib/gc-of-borg"; 86 | createHome = true; 87 | group = "gc-of-borg"; 88 | uid = 402; 89 | }; 90 | users.groups.gc-of-borg.gid = 402; 91 | 92 | 93 | systemd = { 94 | services = 95 | (ifBuilder (rustborgservice "builder" 96 | "${src.ofborg.rs}/bin/builder" 97 | config_json)) // 98 | 99 | (ifEvaluator (rustborgservice "mass-rebuilder" 100 | "${src.ofborg.rs}/bin/mass_rebuilder" 101 | config_json)) // 102 | {}; 103 | }; 104 | }; 105 | } 106 | -------------------------------------------------------------------------------- /modules/role.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, ... }: 2 | let 3 | inherit (lib) mkIf mkOption types; 4 | cfg = config.terraform; 5 | 6 | isRole = role: builtins.elem role cfg.roles.enabled; 7 | in { 8 | options = { 9 | terraform = { 10 | roles.enabled = mkOption { 11 | type = types.listOf types.string; 12 | default = []; 13 | }; 14 | 15 | name = mkOption { 16 | type = types.string; 17 | }; 18 | 19 | idx = mkOption { 20 | type = types.int; 21 | }; 22 | }; 23 | }; 24 | 25 | config = mkIf (isRole "evaluator") rec { 26 | 27 | 28 | boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "virtio_pci" "sd_mod" "sr_mod" ]; 29 | boot.kernelModules = [ ]; 30 | boot.extraModulePackages = [ ]; 31 | 32 | fileSystems."/" = 33 | { device = "/dev/disk/by-uuid/nixos"; 34 | fsType = "ext4"; 35 | }; 36 | 37 | swapDevices = [ ]; 38 | 39 | nix.maxJobs = lib.mkDefault 2; 40 | boot.loader.grub.enable = true; 41 | boot.loader.grub.version = 2; 42 | boot.loader.grub.device = "/dev/sda"; 43 | 44 | networking.firewall.allowedTCPPorts = [ 9100 ]; 45 | 46 | services = { 47 | ofborg = { 48 | enable = true; 49 | enable_evaluator = true; 50 | }; 51 | }; 52 | 53 | nix.gc = { 54 | automatic = true; 55 | dates = "*:0/15"; 56 | 57 | options = let 58 | freedGb = 60; 59 | in ''--max-freed "$((${toString freedGb} * 1024**3 - 1024 * $(df -P -k /nix/store | tail -n 1 | ${pkgs.gawk}/bin/awk '{ print $4 }')))"''; 60 | }; 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /modules/standard/default.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, ... }: 2 | let 3 | inherit (lib) mkIf mkOption types; 4 | cfg = config.services.standard; 5 | secrets = import ../../secrets.nix; 6 | in { 7 | config = { 8 | nixpkgs = { 9 | config = { 10 | allowUnfree = true; 11 | }; 12 | }; 13 | 14 | services.openssh = { 15 | enable = true; 16 | passwordAuthentication = false; 17 | }; 18 | 19 | networking = { 20 | firewall = { 21 | enable = true; 22 | allowedTCPPorts = [ 22 ]; 23 | }; 24 | }; 25 | 26 | users = { 27 | mutableUsers = false; 28 | users = { 29 | root.openssh.authorizedKeys.keyFiles = [ 30 | secrets.root.keys 31 | ]; 32 | 33 | grahamc = { 34 | isNormalUser = true; 35 | uid = 1000; 36 | extraGroups = [ "wheel" ]; 37 | createHome = true; 38 | home = "/home/grahamc"; 39 | hashedPassword = secrets.grahamc.password; 40 | openssh.authorizedKeys.keyFiles = [ 41 | secrets.grahamc.keys 42 | ]; 43 | }; 44 | }; 45 | }; 46 | 47 | nix = { 48 | useSandbox = true; 49 | 50 | nixPath = [ 51 | # Ruin the config so we don't accidentally run 52 | # nixos-rebuild switch on the host 53 | (let 54 | cfg = pkgs.writeText "configuration.nix" 55 | '' 56 | assert builtins.trace "Hey dummy, you're on your server! Use NixOps!" false; 57 | {} 58 | ''; 59 | in "nixos-config=${cfg}") 60 | 61 | # Copy the channel version from the deploy host to the target 62 | "nixpkgs=/run/current-system/nixpkgs" 63 | ]; 64 | }; 65 | 66 | system.extraSystemBuilderCmds = '' 67 | ln -sv ${pkgs.path} $out/nixpkgs 68 | ''; 69 | environment.etc.host-nix-channel.source = pkgs.path; 70 | 71 | services.prometheus.exporters.node = { 72 | enable = true; 73 | enabledCollectors = [ 74 | # "netclass" "exec" "edec" "boottime" 75 | "arp" "bonding" "conntrack" "cpu" "diskstats" 76 | "entropy" # "exec" 77 | "filefd" "filesystem" "hwmon" 78 | "loadavg" "mdadm" "meminfo" 79 | "netdev" "netstat" 80 | "sockstat" "systemd" "textfile" "time" "vmstat" "wifi" "zfs" 81 | ]; 82 | extraFlags = [ 83 | "--collector.textfile.directory=/var/lib/prometheus-node-exporter-text-files" 84 | "" 85 | ]; 86 | }; 87 | 88 | system.activationScripts.node-exporter-system-version = '' 89 | mkdir -pm 0775 /var/lib/prometheus-node-exporter-text-files 90 | ( 91 | cd /var/lib/prometheus-node-exporter-text-files 92 | ( 93 | echo -n "system_version "; 94 | readlink /nix/var/nix/profiles/system | cut -d- -f2 95 | ) > system-version.prom.next 96 | mv system-version.prom.next system-version.prom 97 | ) 98 | 99 | ''; 100 | }; 101 | } 102 | -------------------------------------------------------------------------------- /modules/wireguard.nix: -------------------------------------------------------------------------------- 1 | { pkgs, config, ... }: 2 | let 3 | networks = { 4 | wg0 = { 5 | prefix = 24; 6 | privateKeyFile = "/etc/wireguard/private"; 7 | nodes = { 8 | ogden = { ip = "10.10.2.15"; }; 9 | petunia = { ip = "10.10.2.10"; }; 10 | flexo = { ip = "10.10.2.25"; }; 11 | }; 12 | }; 13 | }; 14 | 15 | privatekey = config.networking.wireguard.interfaces.wg0.privateKeyFile; 16 | publickey = "${dirOf privatekey}/public"; 17 | in { 18 | networking.extraHosts = '' 19 | 10.10.2.15 ogden 20 | 10.10.2.10 petunia 21 | 10.10.2.5 zoidberg 22 | ''; 23 | 24 | programs.ssh.knownHosts.ogden.publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHICIvUL8AAPDjwP0wUdYADwWSWBieS8iTgNPVa+fynN"; 25 | 26 | networking.firewall.allowedUDPPorts = [ 51820 ]; 27 | networking.wireguard.interfaces.wg0 = { 28 | ips = [ "10.10.2.25/24" ]; 29 | privateKeyFile = "/etc/wireguard/private"; 30 | listenPort = 51820; 31 | 32 | peers = [ 33 | { 34 | # petunia 35 | publicKey = "iRqkVDUccM1duRrG02a9IraBgR9zew6SqAclqUaLoyI="; 36 | allowedIPs = [ "10.10.2.10/32" ]; 37 | } 38 | { 39 | # zoidberg 40 | publicKey = "BQ7+bGuKVat/I8b1s75eKlRAE3PwD9DTTbOJ4yUEAzo="; 41 | allowedIPs = [ "10.10.2.5/32" ]; 42 | endpoint = "zoidberg.gsc.io:5820"; 43 | persistentKeepalive = 25; 44 | } 45 | { 46 | # ogden 47 | publicKey = config.about.ogden.wireguard_public_keys.wg0; 48 | allowedIPs = [ "10.10.2.15/32" ]; 49 | persistentKeepalive = 25; 50 | } 51 | 52 | ]; 53 | }; 54 | 55 | systemd.services.wireguard-wg0-key = { 56 | enable = true; 57 | wantedBy = [ "wireguard-wg0.service" ]; 58 | path = with pkgs; [ wireguard ]; 59 | serviceConfig = { 60 | Type = "oneshot"; 61 | RemainAfterExit = true; 62 | }; 63 | 64 | script = '' 65 | mkdir --mode 0644 -p "${dirOf privatekey}" 66 | if [ ! -f "${privatekey}" ]; then 67 | touch "${privatekey}" 68 | chmod 0600 "${privatekey}" 69 | wg genkey > "${privatekey}" 70 | chmod 0400 "${privatekey}" 71 | 72 | touch "${publickey}" 73 | chmod 0600 "${publickey}" 74 | wg pubkey < "${privatekey}" > "${publickey}" 75 | chmod 0444 "${publickey}" 76 | fi 77 | ''; 78 | }; 79 | systemd.paths."wireguard-wg0" = { 80 | pathConfig = { 81 | PathExists = privatekey; 82 | }; 83 | }; 84 | } 85 | -------------------------------------------------------------------------------- /network.nix: -------------------------------------------------------------------------------- 1 | let 2 | secrets = import ./secrets.nix; 3 | in { 4 | defaults = { 5 | about = import ./intake; 6 | }; 7 | network.enableRollback = true; 8 | 9 | #zoidberg = { ... }: { 10 | # imports = [ 11 | # (import ./zoidberg { inherit secrets; }) 12 | # ]; 13 | # boot.loader.grub.memtest86.enable = true; 14 | #}; 15 | 16 | flexo = { ... }: { 17 | deployment = { 18 | targetHost = "147.75.105.137"; 19 | }; 20 | 21 | imports = [ 22 | (import ./flexo { inherit secrets; }) 23 | ]; 24 | boot.loader.grub.memtest86.enable = true; 25 | }; 26 | 27 | lord-nibbler = { ... }: { 28 | deployment = { 29 | targetHost = "10.5.3.1"; 30 | }; 31 | 32 | imports = [ 33 | (import ./lord-nibbler { inherit secrets; }) 34 | ]; 35 | }; 36 | 37 | ogden = { ... }: { 38 | deployment = { 39 | targetHost = "10.10.2.15"; # wireguard! 40 | }; 41 | boot.loader.grub.memtest86.enable = true; 42 | 43 | imports = [ 44 | (import ./ogden { inherit secrets; }) 45 | ]; 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /ogden/default.nix: -------------------------------------------------------------------------------- 1 | { secrets }: 2 | { config, lib, pkgs, ... }: 3 | let 4 | internalInterfaces = [ "enp4s0" ]; 5 | hostConfig = config; 6 | in 7 | { 8 | imports = [ 9 | ./hardware.nix 10 | ./wireguard.nix 11 | (import ./prometheus.nix { inherit secrets; }) 12 | ./sdr.nix 13 | (import ./dns.nix { inherit secrets; }) 14 | ../../../NixOS/nixos-org-configurations/macs/host 15 | # possibly breaking r13y.com # (import ../../../andir/local-nix-cache/module.nix) 16 | ]; 17 | 18 | macosGuest = { 19 | enable = true; 20 | network = { 21 | externalInterface = "enp4s0"; 22 | sshInterface = "lo"; 23 | interiorNetworkPrefix = "192.168.1"; 24 | }; 25 | guest = { 26 | sockets = 1; 27 | cores = 2; 28 | threads = 2; 29 | memoryInMegs = 4 * 1024; 30 | zvolName = "rpool/macos-v1"; 31 | snapshotName = "import"; 32 | guestConfigDir = ./mac-guest; 33 | ovmfCodeFile = ../../../NixOS/nixos-org-configurations/macs/dist/OVMF_CODE.fd; 34 | ovmfVarsFile = ../../../NixOS/nixos-org-configurations/macs/dist/OVMF_VARS-1024x768.fd; 35 | }; 36 | }; 37 | 38 | #local-nix-cache.server = { 39 | # enable = true; 40 | #}; 41 | 42 | systemd.tmpfiles.rules = ['' 43 | e /tmp/nix-build-* - - - 1d - 44 | '']; 45 | 46 | nix = { 47 | gc = { 48 | automatic = true; 49 | dates = "8:06"; 50 | 51 | options = let 52 | freedGb = 300; 53 | in ''--max-freed "$((${toString freedGb} * 1024**3 - 1024 * $(df -P -k /nix/store | tail -n 1 | ${pkgs.gawk}/bin/awk '{ print $4 }')))"''; 54 | }; 55 | 56 | }; 57 | 58 | # Select internationalisation properties. 59 | i18n = { 60 | consoleFont = "Lat2-Terminus16"; 61 | consoleKeyMap = "dvorak"; 62 | defaultLocale = "en_US.UTF-8"; 63 | }; 64 | 65 | # Set your time zone. 66 | time.timeZone = "America/New_York"; 67 | 68 | # List packages installed in system profile. To search by name, run: 69 | # $ nix-env -qaP | grep wget 70 | environment.systemPackages = with pkgs; [ 71 | emacs 72 | screen 73 | git # for kyle's git hosting 74 | borgbackup # for my backups from morbo 75 | ]; 76 | 77 | services.ofborg = { 78 | enable = false; 79 | enable_evaluator = true; 80 | enable_builder = true; 81 | }; 82 | 83 | 84 | services.netatalk = { 85 | enable = true; 86 | extraConfig = '' 87 | afp interfaces = ${lib.concatStringsSep " " internalInterfaces} 88 | afp listen 10.5.3.105 89 | log level = default:debug 90 | ''; 91 | 92 | volumes = { 93 | "emilys-time-machine" = { 94 | "time machine" = "yes"; 95 | path = "/home/emilyc/timemachine/time-machine-root"; 96 | "valid users" = "emilyc"; 97 | }; 98 | 99 | "grahams-time-machine" = { 100 | "time machine" = "yes"; 101 | path = "/home/grahamc/timemachine/time-machine-root"; 102 | "valid users" = "grahamc"; 103 | }; 104 | }; 105 | }; 106 | 107 | services.avahi = { 108 | enable = true; 109 | interfaces = internalInterfaces; 110 | nssmdns = true; 111 | 112 | publish = { 113 | enable = true; 114 | userServices = true; 115 | }; 116 | }; 117 | 118 | services.fail2ban = { 119 | enable = true; 120 | }; 121 | 122 | networking.firewall.extraCommands = let 123 | allowPortMonitoring = port: 124 | '' 125 | iptables -A nixos-fw -p tcp -s 147.75.97.237 \ 126 | --dport ${toString port} -j nixos-fw-accept 127 | 128 | ip6tables -A nixos-fw -p tcp -s 2604:1380:0:d00::1 \ 129 | --dport ${toString port} -j nixos-fw-accept 130 | ''; 131 | in lib.concatStrings [ 132 | (lib.concatMapStrings allowPortMonitoring 133 | [ 134 | # 9100 # Prometheus NodeExporter 135 | ]) 136 | ]; 137 | 138 | networking.firewall.allowedTCPPorts = [ 139 | 8083 # andir/local-nix-cache 140 | 141 | config.services.netatalk.port 142 | 5353 # avahi 143 | 144 | # Plex: Found at https://github.com/NixOS/nixpkgs/blob/release-17.03/nixos/modules/services/misc/plex.nix#L156 145 | 3005 8324 32469 # TCP, 32400 is allowed on all interfaces 146 | 1900 5353 32410 32412 32413 32414 # UDP 147 | 148 | 3000 # grafana 149 | 9090 # prometheus 150 | 151 | # Plex: Found at https://github.com/NixOS/nixpkgs/blob/release-17.03/nixos/modules/services/misc/plex.nix#L156 152 | 32400 3005 8324 32469 153 | 9100 # Prometheus NodeExporter 154 | ]; 155 | 156 | networking.firewall.allowedUDPPorts = [ 157 | # Plex: Found at https://github.com/NixOS/nixpkgs/blob/release-17.03/nixos/modules/services/misc/plex.nix#L156 158 | 1900 5353 32410 32412 32413 32414 # UDP 159 | ]; 160 | 161 | 162 | users = { 163 | groups.writemedia = {}; 164 | extraUsers = { 165 | root.openssh.authorizedKeys.keys = [ 166 | "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINT2IAcpePtxrnk9XiXRRkInvvXm6X00mYFd3rpMSBNW root@Petunia" 167 | ]; 168 | 169 | emilyc = { 170 | isNormalUser = true; 171 | uid = 1002; 172 | createHome = true; 173 | home = "/home/emilyc"; 174 | hashedPassword = secrets.emilyc.password; 175 | }; 176 | 177 | kchristensen = { 178 | isNormalUser = true; 179 | uid = 1003; 180 | extraGroups = [ "writemedia" ]; 181 | createHome = true; 182 | home = "/home/kchristensen"; 183 | openssh.authorizedKeys.keyFiles = [ 184 | secrets.kchristensen.keys 185 | ]; 186 | hashedPassword = secrets.kchristensen.password; 187 | }; 188 | }; 189 | }; 190 | 191 | # Plex 192 | services.plex = { 193 | enable = true; 194 | /* package = pkgs.plex.overrideAttrs (x: let 195 | # see https://www.plex.tv/media-server-downloads/ for 64bit rpm 196 | version = "1.13.6.5339-115f087d6"; 197 | sha1 = "7f425470387b7d6b4f31c799dc37f967cef2aae2"; 198 | in { 199 | name = "plex-${version}"; 200 | src = pkgs.fetchurl { 201 | url = "https://downloads.plex.tv/plex-media-server/${version}/plexmediaserver-${version}.x86_64.rpm"; 202 | inherit sha1; 203 | }; 204 | } 205 | );*/ 206 | }; 207 | 208 | containers = lib.foldr (n: c: c // { "buildkite-builder-grahamc-${toString n}" = { 209 | autoStart = true; 210 | bindMounts.foo = { 211 | hostPath = "/run/keys/buildkite-token-packet"; 212 | mountPoint = "/etc/buildkite-token-packet-bar"; 213 | }; 214 | 215 | bindMounts.agent-token = { 216 | hostPath = "/run/keys/buildkite-token-packet"; 217 | mountPoint = "/etc/buildkite-token-packet"; 218 | }; 219 | bindMounts.ssh-public = { 220 | hostPath = "/run/keys/buildkite-ssh-public-key"; 221 | mountPoint = "/etc/buildkite-ssh-public"; 222 | }; 223 | bindMounts.ssh-private = { 224 | hostPath = "/run/keys/buildkite-ssh-private-key"; 225 | mountPoint = "/etc/buildkite-ssh-private"; 226 | }; 227 | bindMounts.r13y-ssh-private = { 228 | hostPath = "/run/keys/r13y-ssh-private-key"; 229 | mountPoint = "/etc/r13y-ssh-private"; 230 | }; 231 | bindMounts.packet-config = { 232 | hostPath = "/run/keys/packet-nixos-config"; 233 | mountPoint = "/etc/packet-nixos-config"; 234 | }; 235 | bindMounts.aarch64-ssh-private = { 236 | hostPath = "/run/keys/aarch64-ssh-private-key"; 237 | mountPoint = "/etc/aarch64-ssh-private"; 238 | }; 239 | bindMounts.aarch64-build-cfg = { 240 | hostPath = "/run/keys/aarch64-build-cfg"; 241 | mountPoint = "/etc/aarch64-build-cfg"; 242 | }; 243 | 244 | config = { pkgs, lib, ... }: { 245 | programs.ssh.knownHosts = [ 246 | { 247 | hostNames = [ "flexo.gsc.io" "r13y.com" "147.75.105.137" ]; 248 | publicKey = hostConfig.about.flexo.ssh_host_key; 249 | } 250 | { 251 | hostNames = [ "147.75.79.198" ]; 252 | publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDCo+z5d8C6SpCyvC8KAPMAcMEtd5J74tRsk+7sm2KgD"; 253 | } 254 | ]; 255 | services.openssh.enable = lib.mkForce false; # override standard module 256 | services.prometheus.exporters.node.enable = lib.mkForce false; # override standard module 257 | 258 | services.buildkite-agent = { 259 | meta-data = "r13y=true"; 260 | enable = true; 261 | tokenPath = "/etc/buildkite-token-packet"; 262 | openssh.privateKeyPath = "/etc/buildkite-ssh-private"; 263 | openssh.publicKeyPath = "/etc/buildkite-ssh-public"; 264 | runtimePackages = [ pkgs.xz pkgs.gzip pkgs.gnutar pkgs.gitAndTools.git-crypt pkgs.nix pkgs.bash ]; 265 | hooks.environment = '' 266 | export PATH=$PATH:/run/wrappers/bin/ 267 | export NIX_PATH=nixpkgs=${pkgs.path} 268 | ''; 269 | #hooks.pre-command = '' 270 | # sleep ${builtins.toString n} # janky packet race condition 271 | #''; 272 | extraConfig = ''git-clean-flags=-n''; 273 | }; 274 | }; 275 | };}) {} (lib.range 1 10); 276 | 277 | services.buildkite-agent = { 278 | enable = true; 279 | tokenPath = "/run/keys/buildkite-token"; 280 | openssh.privateKeyPath = "/run/keys/buildkite-ssh-private-key"; 281 | openssh.publicKeyPath = "/run/keys/buildkite-ssh-public-key"; 282 | runtimePackages = [ pkgs.gzip pkgs.gnutar pkgs.gitAndTools.git-crypt pkgs.nix pkgs.bash ]; 283 | }; 284 | 285 | deployment.keys.packet-nixos-config = { 286 | text = builtins.readFile secrets.buildkite.packet-config; 287 | user = config.users.extraUsers.buildkite-agent.name; 288 | group = "keys"; 289 | permissions = "0600"; 290 | }; 291 | deployment.keys.buildkite-token-packet = { 292 | text = builtins.readFile secrets.buildkite-packet.token; 293 | user = config.users.extraUsers.buildkite-agent.name; 294 | group = "keys"; 295 | permissions = "0600"; 296 | }; 297 | 298 | deployment.keys.buildkite-token = { 299 | text = builtins.readFile secrets.buildkite.token; 300 | user = config.users.extraUsers.buildkite-agent.name; 301 | group = "keys"; 302 | permissions = "0600"; 303 | }; 304 | deployment.keys.buildkite-ssh-public-key = { 305 | text = builtins.readFile secrets.buildkite.openssh-public-key; 306 | user = config.users.extraUsers.buildkite-agent.name; 307 | group = "keys"; 308 | permissions = "0600"; 309 | }; 310 | deployment.keys.buildkite-ssh-private-key = { 311 | text = builtins.readFile secrets.buildkite.openssh-private-key; 312 | user = config.users.extraUsers.buildkite-agent.name; 313 | group = "keys"; 314 | permissions = "0600"; 315 | }; 316 | 317 | 318 | deployment.keys.aarch64-build-cfg = { 319 | text = builtins.readFile secrets.buildkite.aarch64-build-cfg; 320 | user = config.users.extraUsers.buildkite-agent.name; 321 | group = "keys"; 322 | permissions = "0600"; 323 | }; 324 | deployment.keys.aarch64-ssh-private-key = { 325 | text = builtins.readFile secrets.aarch64.private; 326 | user = config.users.extraUsers.buildkite-agent.name; 327 | group = "keys"; 328 | permissions = "0600"; 329 | }; 330 | deployment.keys.r13y-ssh-private-key = { 331 | text = builtins.readFile secrets.r13y.private; 332 | user = config.users.extraUsers.buildkite-agent.name; 333 | group = "keys"; 334 | permissions = "0600"; 335 | }; 336 | } 337 | -------------------------------------------------------------------------------- /ogden/dns.nix: -------------------------------------------------------------------------------- 1 | { secrets }: 2 | { pkgs, ... }: 3 | { 4 | deployment.keys.dns-update-secrets = { 5 | text = '' 6 | AWS_ACCESS_KEY_ID="${secrets.awsdns.key}" 7 | AWS_SECRET_ACCESS_KEY="${secrets.awsdns.secret}" 8 | ZONEID="${secrets.awsdns.zone}" 9 | RECORDSET="${secrets.awsdns.record}" 10 | ''; 11 | user = "dns-update"; 12 | group = "keys"; 13 | permissions = "0600"; 14 | }; 15 | 16 | users.users.dns-update.uid = 405; 17 | 18 | systemd.services.dns-update = { 19 | enable = true; 20 | wantedBy = [ "multi-user.target" ]; 21 | after = [ "network.target" ]; 22 | startAt = "*:0/5"; 23 | serviceConfig = { 24 | User = "dns-update"; 25 | Group = "keys"; 26 | EnvironmentFile = "/run/keys/dns-update-secrets"; 27 | ProtectHome = true; 28 | PrivateTmp = true; 29 | }; 30 | path = with pkgs; [ awscli dnsutils bash ]; 31 | script = "${./dns.sh}"; 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /ogden/dns.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eux 4 | 5 | # More advanced options below 6 | # The Time-To-Live of this recordset 7 | TTL=300 8 | # Change this if you want 9 | COMMENT="Auto updating @ `date`" 10 | # Change to AAAA if using an IPv6 address 11 | TYPE="A" 12 | 13 | # Get the external IP address from OpenDNS (more reliable than other providers) 14 | IP=`dig +short myip.opendns.com @resolver1.opendns.com` 15 | 16 | function valid_ip() 17 | { 18 | local ip=$1 19 | local stat=1 20 | 21 | if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then 22 | OIFS=$IFS 23 | IFS='.' 24 | ip=($ip) 25 | IFS=$OIFS 26 | [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ 27 | && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] 28 | stat=$? 29 | fi 30 | return $stat 31 | } 32 | 33 | 34 | if ! valid_ip $IP; then 35 | echo "Invalid IP address: $IP" 36 | exit 1 37 | fi 38 | 39 | echo "IP has changed to $IP" 40 | # Fill a temp file with valid JSON 41 | TMPFILE=$(mktemp /tmp/temporary-file.XXXXXXXX) 42 | cat > ${TMPFILE} << EOF 43 | { 44 | "Comment":"$COMMENT", 45 | "Changes":[ 46 | { 47 | "Action":"UPSERT", 48 | "ResourceRecordSet":{ 49 | "ResourceRecords":[ 50 | { 51 | "Value":"$IP" 52 | } 53 | ], 54 | "Name":"$RECORDSET", 55 | "Type":"$TYPE", 56 | "TTL":$TTL 57 | } 58 | } 59 | ] 60 | } 61 | EOF 62 | 63 | # Update the Hosted Zone record 64 | aws route53 change-resource-record-sets \ 65 | --hosted-zone-id $ZONEID \ 66 | --change-batch file://"$TMPFILE" 67 | echo "" 68 | 69 | # Clean up 70 | rm $TMPFILE 71 | -------------------------------------------------------------------------------- /ogden/hardware.nix: -------------------------------------------------------------------------------- 1 | { lib, ... }: 2 | { 3 | #hardware.enableAllFirmware = true; 4 | boot.initrd.availableKernelModules = [ "ahci" "ohci_pci" "ehci_pci" "pata_atiixp" "xhci_pci" "pata_jmicron" "usb_storage" "usbhid" "sd_mod" ]; 5 | boot.kernelModules = [ "kvm-amd" ]; 6 | boot.extraModulePackages = [ ]; 7 | 8 | fileSystems."/" = 9 | { device = "/dev/disk/by-label/nixos"; 10 | fsType = "ext4"; 11 | }; 12 | 13 | fileSystems."/home/emilyc/timemachine" = 14 | { device = "rpool/time-machine/emily"; 15 | fsType = "zfs"; 16 | options = [ "nofail" ]; 17 | }; 18 | fileSystems."/home/grahamc/timemachine" = 19 | { device = "rpool/time-machine/graham"; 20 | fsType = "zfs"; 21 | options = [ "nofail" ]; 22 | }; 23 | fileSystems."/home/kchristensen/storage" = 24 | { device = "rpool/kyle/storage"; 25 | fsType = "zfs"; 26 | options = [ "nofail" ]; 27 | }; 28 | 29 | #fileSystems."/media" = 30 | # { device = "rpool/graham/media"; 31 | # fsType = "zfs"; 32 | # options = [ "nofail" ]; 33 | # }; 34 | 35 | fileSystems."/media" = 36 | { device = "mass/plex"; 37 | fsType = "zfs"; 38 | options = [ "nofail" ]; 39 | }; 40 | 41 | 42 | 43 | swapDevices = [ ]; 44 | 45 | nix.maxJobs = lib.mkDefault 6; 46 | 47 | 48 | # Use the GRUB 2 boot loader. 49 | boot.loader.grub.enable = true; 50 | boot.loader.grub.version = 2; 51 | boot.loader.grub.device = "/dev/sda"; # or "nodev" for efi only 52 | 53 | boot.supportedFilesystems = [ "zfs" ]; 54 | networking.hostId = "a207025c"; 55 | services.zfs.autoScrub.enable = true; 56 | } 57 | -------------------------------------------------------------------------------- /ogden/hydra-machine-status.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env nix-shell 2 | #!nix-shell -i python3 -p python3Packages.beautifulsoup4 -p python3Packages.requests -p python3Packages.prometheus_client 3 | 4 | from bs4 import BeautifulSoup 5 | import requests 6 | from prometheus_client import CollectorRegistry, Histogram, write_to_textfile 7 | import re 8 | 9 | def parse_duration(d): 10 | # https://github.com/NixOS/hydra/blob/0bc548ee2d39debe4fcc7ea1cc1203ba8454a811/src/root/common.tt#L62 11 | matches = re.match('^\s*((?P\d+)d\s)?((?P\d+)h\s)?((?P\d+)m\s)?(?P\d+)s\s*$', d) 12 | 13 | if matches is None: 14 | return 0 15 | 16 | matched = matches.groupdict(0) 17 | return ((int(matched['days']) * 24 * 60 * 60) + 18 | (int(matched['hours']) * 60 * 60) + 19 | (int(matched['minutes']) * 60) + 20 | (int(matched['seconds']))) 21 | 22 | registry = CollectorRegistry() 23 | 24 | data = requests.get("https://hydra.nixos.org/machines") 25 | 26 | parsed = BeautifulSoup(data.text, features="html.parser") 27 | 28 | h = Histogram('hydra_machine_build_duration', 29 | 'How long builds are taking per server', 30 | ['machine'], 31 | buckets=[ 32 | 60, 33 | 600, 34 | 1800, 35 | 3600, 36 | 7200, 37 | 21600, 38 | 43200, 39 | 86400, 40 | 172800, 41 | 259200, 42 | 345600, 43 | 518400, 44 | 604800, 45 | 691200], 46 | registry=registry 47 | ) 48 | 49 | 50 | for duration in parsed.select('tr > td:nth-of-type(6)'): 51 | machinename = duration.findPrevious('thead').tt.text 52 | h.labels(machine=machinename).observe(parse_duration(duration.text)) 53 | 54 | write_to_textfile('./hydra-machines.prom', registry) 55 | -------------------------------------------------------------------------------- /ogden/hydra-queue-status.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env nix-shell 2 | #!nix-shell -i python3 -p python3Packages.beautifulsoup4 -p python3Packages.requests 3 | from bs4 import BeautifulSoup 4 | import requests 5 | 6 | project_q_summary = {} 7 | system_q_summary = {} 8 | 9 | 10 | data = requests.get("https://hydra.nixos.org/queue_summary") 11 | 12 | parsed = BeautifulSoup(data.text, features="html.parser") 13 | 14 | tables = parsed.find_all('table') 15 | project_qs = tables[0] 16 | for row in project_qs.tdata.find_all('tr'): 17 | cols = row.find_all('td') 18 | project_q_summary[cols[0].text] = int(cols[1].text) 19 | 20 | system_qs = tables[1] 21 | for row in system_qs.tdata.find_all('tr'): 22 | cols = row.find_all('td') 23 | system_q_summary[cols[0].text] = int(cols[1].text) 24 | 25 | 26 | print(""" 27 | # HELP hydra_project_jobs_total Number of jobs per project. 28 | # TYPE hydra_project_jobs_total gauge 29 | """) 30 | for project, total in project_q_summary.items(): 31 | print("hydra_project_jobs_total{{project=\"{}\"}} {}".format(project, total)) 32 | 33 | print(""" 34 | # HELP hydra_architecture_jobs_total Number of jobs per architecture. 35 | # TYPE hydra_architecture_jobs_total gauge 36 | """) 37 | for arch, total in system_q_summary.items(): 38 | print("hydra_architecture_jobs_total{{architecture=\"{}\"}} {}".format(arch, total)) 39 | -------------------------------------------------------------------------------- /ogden/prometheus.nix: -------------------------------------------------------------------------------- 1 | { secrets }: 2 | { nodes, lib, pkgs, ... }: 3 | let 4 | prometheus-hue-exporter = pkgs.callPackage ({ stdenv, buildGoPackage, fetchFromGitHub }: 5 | buildGoPackage rec { 6 | name = "surfboard_hue-${version}"; 7 | version = "v0.2.1"; 8 | 9 | goPackagePath = "github.com/mitchellrj/hue_exporter"; 10 | 11 | src = fetchFromGitHub { 12 | rev = version; 13 | owner = "mitchellrj"; 14 | repo = "hue_exporter"; 15 | sha256 = "03kxmf6h8aa0wyns5q4bm0zi61k6jlhpyim5vn1nj2f9sjyvhs75"; 16 | }; 17 | }) {}; 18 | 19 | hueConfig = pkgs.writeText "config.yml" (builtins.toJSON 20 | { 21 | inherit (secrets.hue_exporter_opts) ip_address api_key; 22 | sensors = { 23 | match_names = true; 24 | ignore_types = [ 25 | "CLIPGenericStatus" 26 | ]; 27 | }; 28 | }); 29 | in { 30 | imports = [ ../../prometheus-packet-spot-market-price-exporter/module.nix ]; 31 | networking.extraHosts = '' 32 | 10.10.2.50 elzar 33 | 12.0.0.1 ogden 34 | 10.5.4.50 turner 35 | '' + (let 36 | nums = lib.lists.range 1 9; 37 | name = num: '' 38 | 37.153.215.191 mac${toString num}-host 39 | 37.153.215.191 mac${toString num}-guest 40 | ''; 41 | in lib.strings.concatMapStrings name nums); 42 | 43 | services.grafana = { 44 | enable = true; 45 | addr = "0.0.0.0"; 46 | auth.anonymous.enable = true; 47 | }; 48 | 49 | services.prometheus = { 50 | enable = true; 51 | extraFlags = [ 52 | #"--storage.local.retention=${toString (120 * 24)}h" 53 | "--storage.tsdb.retention.time=120d" 54 | ]; 55 | globalConfig = { 56 | scrape_interval = "30s"; 57 | }; 58 | scrapeConfigs = [ 59 | { 60 | job_name = "prometheus"; 61 | static_configs = [ 62 | { targets = [ "ogden-encrypted:9090" ]; } 63 | ]; 64 | } 65 | 66 | { 67 | job_name = "surfboard"; 68 | static_configs = [ 69 | { targets = [ "10.5.3.1:9239" ]; } 70 | ]; 71 | } 72 | 73 | { 74 | job_name = "unifi"; 75 | static_configs = [ 76 | { targets = [ "10.5.3.1:9130" ]; } 77 | ]; 78 | } 79 | 80 | { 81 | job_name = "hue"; 82 | static_configs = [ 83 | { targets = [ "ogden-encrypted:9366" ]; } 84 | ]; 85 | } 86 | 87 | { 88 | job_name = "packet-spot-market-price"; 89 | static_configs = [ 90 | { targets = [ "127.0.0.1:9400" ]; } 91 | ]; 92 | } 93 | 94 | { 95 | job_name = "weather-berkshires"; 96 | scheme = "https"; 97 | metrics_path = "/weather"; 98 | params = { 99 | latitude = [ "42.45" ]; 100 | longitude = [ "-73.25" ]; 101 | }; 102 | static_configs = [ 103 | { targets = [ "weather.gsc.io" ]; } 104 | ]; 105 | } 106 | 107 | { 108 | job_name = "weather-status"; 109 | scheme = "https"; 110 | static_configs = [ 111 | { targets = [ "weather.gsc.io" ]; } 112 | ]; 113 | } 114 | 115 | { 116 | job_name = "node"; 117 | static_configs = [ 118 | { targets = [ 119 | "10.5.3.1:9100" 120 | "elzar:9100" 121 | "turner:9100" 122 | ] ++ (builtins.map 123 | (nodename: "${nodename}-encrypted:9100") 124 | (pkgs.lib.filter (name: name != "lord-nibbler" && (!(lib.strings.hasPrefix "mac" name))) 125 | (builtins.attrNames nodes))) 126 | ++ (builtins.map (n: "mac${toString n}-host:6010") (lib.lists.range 1 9)) 127 | ++ (builtins.map (n: "mac${toString n}-guest:6010") (lib.lists.range 1 9)); 128 | } 129 | ]; 130 | } 131 | ]; 132 | }; 133 | 134 | 135 | systemd.services.prometheus-hue-exporter = { 136 | wantedBy = [ "multi-user.target" ]; 137 | after = [ "network.target" ]; 138 | serviceConfig = { 139 | Restart = "always"; 140 | RestartSec = "60s"; 141 | PrivateTmp = true; 142 | WorkingDirectory = "/tmp"; 143 | ExecStart = '' 144 | ${prometheus-hue-exporter}/bin/hue_exporter \ 145 | --listen.address 0.0.0.0:9366 \ 146 | --config.file=${hueConfig} 147 | ''; 148 | }; 149 | }; 150 | 151 | systemd.timers.prometheus-smartmon-exporter = { 152 | description = "Captures smartmon data"; 153 | wantedBy = [ "timers.target" ]; 154 | partOf = [ "prometheus-smartmon-exporter.service" ]; 155 | enable = true; 156 | timerConfig = { 157 | OnCalendar = "*:*"; 158 | Unit = "prometheus-smartmon-exporter.service"; 159 | Persistent = "yes"; 160 | }; 161 | }; 162 | systemd.services.prometheus-smartmon-exporter = { 163 | path = [ pkgs.bash pkgs.gawk pkgs.smartmontools ]; 164 | serviceConfig = { 165 | Type = "oneshot"; 166 | PrivateTmp = true; 167 | WorkingDirectory = "/tmp"; 168 | }; 169 | script = '' 170 | mkdir -pm 0775 /var/lib/prometheus-node-exporter-text-files 171 | cd /var/lib/prometheus-node-exporter-text-files 172 | set -euxo pipefail 173 | ${./smartmon.sh} | ${pkgs.moreutils}/bin/sponge smartmon.prom 174 | ''; 175 | 176 | }; 177 | 178 | systemd.timers.prometheus-zfs-snapshot-exporter = { 179 | description = "Captures snapshot data"; 180 | wantedBy = [ "timers.target" ]; 181 | partOf = [ "prometheus-zfs-snapshot-exporter.service" ]; 182 | enable = true; 183 | timerConfig = { 184 | OnCalendar = "*:0/3"; 185 | Unit = "prometheus-zfs-snapshot-exporter.service"; 186 | Persistent = "yes"; 187 | }; 188 | }; 189 | systemd.services.prometheus-zfs-snapshot-exporter = { 190 | path = with pkgs; [ bash gawk gnused moreutils zfs ]; 191 | serviceConfig = { 192 | Type = "oneshot"; 193 | PrivateTmp = true; 194 | WorkingDirectory = "/tmp"; 195 | }; 196 | script = '' 197 | mkdir -pm 0775 /var/lib/prometheus-node-exporter-text-files 198 | cd /var/lib/prometheus-node-exporter-text-files 199 | set -euxo pipefail 200 | zfs list -Hp -t snapshot -o name,creation \ 201 | | sed -e 's#@.*\s# #' \ 202 | | awk ' 203 | { 204 | if (last[$1] < $2) { 205 | last[$1]=$2 206 | } 207 | } 208 | END { 209 | for (m in last) { 210 | printf "zfs_snapshot_age_seconds{dataset=\"%s\"} %s\n", m, last[m]; 211 | } 212 | } 213 | ' \ 214 | | sponge znapzend-snaps.prom 215 | ''; 216 | }; 217 | 218 | systemd.timers.prometheus-hydra-jobs-exporter = { 219 | description = "Captures hydra job data"; 220 | wantedBy = [ "timers.target" ]; 221 | partOf = [ "prometheus-hydra-jobs-exporter.service" ]; 222 | enable = true; 223 | timerConfig = { 224 | OnCalendar = "*:*"; 225 | Unit = "prometheus-hydra-jobs-exporter.service"; 226 | Persistent = "yes"; 227 | }; 228 | }; 229 | systemd.services.prometheus-hydra-jobs-exporter = { 230 | path = [ 231 | (pkgs.python3.withPackages (p: [ p.beautifulsoup4 p.requests ])) 232 | ]; 233 | 234 | serviceConfig = { 235 | Type = "oneshot"; 236 | PrivateTmp = true; 237 | WorkingDirectory = "/tmp"; 238 | }; 239 | script = '' 240 | mkdir -pm 0775 /var/lib/prometheus-node-exporter-text-files 241 | cd /var/lib/prometheus-node-exporter-text-files 242 | set -euxo pipefail 243 | python3 ${./hydra-queue-status.py} | ${pkgs.moreutils}/bin/sponge hydra-queue.prom 244 | ''; 245 | 246 | }; 247 | 248 | 249 | systemd.timers.prometheus-hydra-machines-exporter = { 250 | description = "Captures hydra machines data"; 251 | wantedBy = [ "timers.target" ]; 252 | partOf = [ "prometheus-hydra-machines-exporter.service" ]; 253 | enable = true; 254 | timerConfig = { 255 | OnCalendar = "*:*"; 256 | Unit = "prometheus-hydra-machines-exporter.service"; 257 | Persistent = "yes"; 258 | }; 259 | }; 260 | systemd.services.prometheus-hydra-machines-exporter = { 261 | path = [ 262 | (pkgs.python3.withPackages (p: [ p.beautifulsoup4 p.requests p.prometheus_client ])) 263 | ]; 264 | 265 | serviceConfig = { 266 | Type = "oneshot"; 267 | PrivateTmp = true; 268 | WorkingDirectory = "/tmp"; 269 | }; 270 | script = '' 271 | mkdir -pm 0775 /var/lib/prometheus-node-exporter-text-files 272 | cd /var/lib/prometheus-node-exporter-text-files 273 | set -euxo pipefail 274 | python3 ${./hydra-machine-status.py} 275 | ''; 276 | 277 | }; 278 | 279 | } 280 | -------------------------------------------------------------------------------- /ogden/rtl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -euxo pipefail 4 | 5 | modprobe -r dvb_usb_rtl28xxu || true 6 | 7 | function finish { 8 | set +eu 9 | kill "$COPROC_PID" >&2 10 | sleep 5 11 | while pgrep rtl_tcp >&2; do 12 | echo "rtl_tcp still running?" >&2 13 | pkill -9 rtl_tcp >&2 14 | sleep 1 15 | done 16 | } 17 | trap finish EXIT 18 | 19 | coproc rtl_tcp >&2 20 | 21 | i=0 22 | while ! nc -z 127.0.0.1 1234 >&2; do 23 | sleep 1 24 | i=$((i+1)) 25 | if [ $i -gt 30 ]; then 26 | exit 1 27 | fi 28 | done 29 | 30 | 31 | 32 | echo "# HELP consumption_meter meter consumption" 33 | echo "# TYPE consumption_meter counter" 34 | rtlamr -format=csv -duration=30s \ 35 | | awk -F, '{ $1=""; print "consumption_meter{id=\""$4"\", type=\""$5"\"} "$8"" }' \ 36 | | sort | rev | uniq -f2 | rev # one report per meter 37 | -------------------------------------------------------------------------------- /ogden/sdr.nix: -------------------------------------------------------------------------------- 1 | { nodes, pkgs, ... }: 2 | let 3 | rtlamr = pkgs.callPackage ({ stdenv, buildGoPackage, fetchFromGitHub }: 4 | 5 | buildGoPackage rec { 6 | name = "rtlamr-${version}"; 7 | version = "20180824"; 8 | 9 | goPackagePath = "github.com/bemasher/rtlamr"; 10 | 11 | src = fetchFromGitHub { 12 | rev = "d4b98558f8f095bdba509fdc70fa2cc21ab1c4e9"; 13 | owner = "bemasher"; 14 | repo = "rtlamr"; 15 | sha256 = "0csxv27y5gb49mql19n431iwwmfkm9bhcrk3l5kkx53rq5338ggr"; 16 | }; 17 | 18 | preFixup = '' 19 | ${pkgs.tree}/bin/tree . 20 | ''; 21 | }) {}; 22 | in { 23 | boot.blacklistedKernelModules = [ "dvb_usb_rtl28xxu" ]; 24 | 25 | services.prometheus = { 26 | scrapeConfigs = [ 27 | { 28 | job_name = "consumption"; 29 | static_configs = [ 30 | { targets = [ "ogden-encrypted:9111" ]; } 31 | ]; 32 | } 33 | ]; 34 | }; 35 | 36 | systemd.timers = { 37 | prometheus-meters-exporter = { 38 | description = "Captures meters data"; 39 | wantedBy = [ "timers.target" ]; 40 | partOf = [ "prometheus-meters-exporter.service" ]; 41 | enable = true; 42 | timerConfig = { 43 | OnCalendar = "*:*"; 44 | RandomizedDelaySec = 15; 45 | Unit = "prometheus-meters-exporter.service"; 46 | Persistent = "yes"; 47 | }; 48 | }; 49 | }; 50 | systemd.services.prometheus-meters-exporter = { 51 | path = [ pkgs.rtl-sdr pkgs.netcat rtlamr pkgs.kmod pkgs.gawk pkgs.coreutils pkgs.procps pkgs.utillinux ]; 52 | serviceConfig = { 53 | Type = "oneshot"; 54 | PrivateTmp = true; 55 | WorkingDirectory = "/tmp"; 56 | }; 57 | script = '' 58 | mkdir -pm 0775 /var/lib/prometheus-node-exporter-text-files 59 | cd /var/lib/prometheus-node-exporter-text-files 60 | set -euxo pipefail 61 | 62 | ${./rtl.sh} | ${pkgs.moreutils}/bin/sponge meters.prom 63 | cat meters.prom 64 | ''; 65 | 66 | }; 67 | 68 | } 69 | -------------------------------------------------------------------------------- /ogden/smartmon.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Script informed by the collectd monitoring script for smartmontools (using smartctl) 3 | # by Samuel B. (c) 2012 4 | # source at: http://devel.dob.sk/collectd-scripts/ 5 | 6 | # TODO: This probably needs to be a little more complex. The raw numbers can have more 7 | # data in them than you'd think. 8 | # http://arstechnica.com/civis/viewtopic.php?p=22062211 9 | 10 | # Source: https://github.com/prometheus/node_exporter/pull/1050/files 11 | 12 | PATH="/usr/sbin:$PATH" 13 | 14 | set -uo pipefail 15 | 16 | parse_smartctl_attributes_awk="$(cat << 'SMARTCTLAWK' 17 | $1 ~ /^ *[0-9]+$/ && $2 ~ /^[a-zA-Z0-9_-]+$/ { 18 | gsub(/-/, "_"); 19 | printf "%s_value{%s,smart_id=\"%s\"} %d\n", $2, labels, $1, $4 20 | printf "%s_worst{%s,smart_id=\"%s\"} %d\n", $2, labels, $1, $5 21 | printf "%s_threshold{%s,smart_id=\"%s\"} %d\n", $2, labels, $1, $6 22 | printf "%s_raw_value{%s,smart_id=\"%s\"} %e\n", $2, labels, $1, $10 23 | } 24 | SMARTCTLAWK 25 | )" 26 | 27 | smartmon_attrs="$(cat << 'SMARTMONATTRS' | tr '\n' '|' 28 | airflow_temperature_cel 29 | command_timeout 30 | current_pending_sector 31 | end_to_end_error 32 | erase_fail_count 33 | g_sense_error_rate 34 | hardware_ecc_recovered 35 | host_reads_mib 36 | host_reads_32mib 37 | host_writes_mib 38 | host_writes_32mib 39 | load_cycle_count 40 | media_wearout_indicator 41 | wear_leveling_count 42 | nand_writes_1gib 43 | offline_uncorrectable 44 | power_cycle_count 45 | power_on_hours 46 | program_fail_count 47 | raw_read_error_rate 48 | reallocated_sector_ct 49 | reported_uncorrect 50 | sata_downshift_count 51 | spin_retry_count 52 | spin_up_time 53 | start_stop_count 54 | temperature_case 55 | temperature_celsius 56 | temperature_internal 57 | total_lbas_read 58 | total_lbas_written 59 | udma_crc_error_count 60 | unsafe_shutdown_count 61 | workld_host_reads_perc 62 | workld_media_wear_indic 63 | workload_minutes 64 | SMARTMONATTRS 65 | )" 66 | 67 | parse_smartctl_attributes() { 68 | local disk="$1" 69 | local disk_type="$2" 70 | local labels="disk=\"${disk}\",type=\"${disk_type}\"" 71 | 72 | sed 's/^ \+//g' \ 73 | | awk -v labels="${labels}" "${parse_smartctl_attributes_awk}" 2>/dev/null \ 74 | | tr '[:upper:]' '[:lower:]' \ 75 | | grep -E "(${smartmon_attrs})" 76 | } 77 | 78 | parse_smartctl_scsi_attributes() { 79 | local disk="$1" 80 | local disk_type="$2" 81 | local labels="disk=\"${disk}\",type=\"${disk_type}\"" 82 | while read -r line ; do 83 | attr_type="$(echo "${line}" | tr '=' ':' | cut -f1 -d: | sed 's/^ \+//g' | tr ' ' '_')" 84 | attr_value="$(echo "${line}" | tr '=' ':' | cut -f2 -d: | sed 's/^ \+//g')" 85 | case "${attr_type}" in 86 | number_of_hours_powered_up_) power_on="$( echo "${attr_value}" | awk '{ printf "%e\n", $1 }')" ;; 87 | Current_Drive_Temperature) temp_cel="$(echo "${attr_value}" | cut -f1 -d' ' | awk '{ printf "%e\n", $1 }')" ;; 88 | Blocks_read_from_cache_and_sent_to_initiator_) lbas_read="$(echo "${attr_value}" | awk '{ printf "%e\n", $1 }')" ;; 89 | Accumulated_start-stop_cycles) power_cycle="$(echo "${attr_value}" | awk '{ printf "%e\n", $1 }')" ;; 90 | esac 91 | done 92 | echo "power_on_hours_raw_value{${labels},smart_id=\"9\"} ${power_on}" 93 | echo "temperature_celsius_raw_value{${labels},smart_id=\"194\"} ${temp_cel}" 94 | echo "total_lbas_read_raw_value{${labels},smart_id=\"242\"} ${lbas_read}" 95 | echo "power_cycle_count_raw_value{${labels},smart_id=\"12\"} ${power_cycle}" 96 | } 97 | 98 | parse_smartctl_info() { 99 | local -i smart_available=0 smart_enabled=0 smart_healthy=0 100 | local disk="$1" disk_type="$2" 101 | local model_family='' device_model='' serial_number='' fw_version='' vendor='' product='' revision='' lun_id='' 102 | while read -r line ; do 103 | info_type="$(echo "${line}" | cut -f1 -d: | tr ' ' '_')" 104 | info_value="$(echo "${line}" | cut -f2- -d: | sed 's/^ \+//g' | sed 's/"/\\"/')" 105 | case "${info_type}" in 106 | Model_Family) model_family="${info_value}" ;; 107 | Device_Model) device_model="${info_value}" ;; 108 | Serial_Number) serial_number="${info_value}" ;; 109 | Firmware_Version) fw_version="${info_value}" ;; 110 | Vendor) vendor="${info_value}" ;; 111 | Product) product="${info_value}" ;; 112 | Revision) revision="${info_value}" ;; 113 | Logical_Unit_id) lun_id="${info_value}" ;; 114 | esac 115 | if [[ "${info_type}" == 'SMART_support_is' ]] ; then 116 | case "${info_value:0:7}" in 117 | Enabled) smart_enabled=1 ;; 118 | Availab) smart_available=1 ;; 119 | Unavail) smart_available=0 ;; 120 | esac 121 | fi 122 | if [[ "${info_type}" == 'SMART_overall-health_self-assessment_test_result' ]] ; then 123 | case "${info_value:0:6}" in 124 | PASSED) smart_healthy=1 ;; 125 | esac 126 | elif [[ "${info_type}" == 'SMART_Health_Status' ]] ; then 127 | case "${info_value:0:2}" in 128 | OK) smart_healthy=1 ;; 129 | esac 130 | fi 131 | done 132 | echo "device_info{disk=\"${disk}\",type=\"${disk_type}\",vendor=\"${vendor}\",product=\"${product}\",revision=\"${revision}\",lun_id=\"${lun_id}\",model_family=\"${model_family}\",device_model=\"${device_model}\",serial_number=\"${serial_number}\",firmware_version=\"${fw_version}\"} 1" 133 | echo "device_smart_available{disk=\"${disk}\",type=\"${disk_type}\"} ${smart_available}" 134 | echo "device_smart_enabled{disk=\"${disk}\",type=\"${disk_type}\"} ${smart_enabled}" 135 | echo "device_smart_healthy{disk=\"${disk}\",type=\"${disk_type}\"} ${smart_healthy}" 136 | } 137 | 138 | output_format_awk="$(cat << 'OUTPUTAWK' 139 | BEGIN { v = "" } 140 | v != $1 { 141 | print "# HELP smartmon_" $1 " SMART metric " $1; 142 | print "# TYPE smartmon_" $1 " gauge"; 143 | v = $1 144 | } 145 | {print "smartmon_" $0} 146 | OUTPUTAWK 147 | )" 148 | 149 | format_output() { 150 | sort \ 151 | | awk -F'{' "${output_format_awk}" 152 | } 153 | 154 | smartctl_version="$(smartctl -V | head -n1 | awk '$1 == "smartctl" {print $2}')" 155 | 156 | echo "smartctl_version{version=\"${smartctl_version}\"} 1" | format_output 157 | 158 | if [[ "$(expr "${smartctl_version}" : '\([0-9]*\)\..*')" -lt 6 ]] ; then 159 | exit 160 | fi 161 | 162 | device_list="$(smartctl --scan-open | awk '/^\/dev/{print $1 "|" $3}')" 163 | 164 | for device in ${device_list}; do 165 | disk="$(echo "${device}" | cut -f1 -d'|')" 166 | type="$(echo "${device}" | cut -f2 -d'|')" 167 | echo "smartctl_run{disk=\"${disk}\",type=\"${type}\"} $(TZ=UTC date '+%s')" 168 | # Get the SMART information and health 169 | smartctl -i -H -d "${type}" "${disk}" | parse_smartctl_info "${disk}" "${type}" 170 | # Get the SMART attributes 171 | case ${type} in 172 | sat) smartctl -A -d "${type}" "${disk}" | parse_smartctl_attributes "${disk}" "${type}" ;; 173 | scsi) smartctl -A -d "${type}" "${disk}" | parse_smartctl_scsi_attributes "${disk}" "${type}" ;; 174 | *) echo "disk type is not sat or scsi, ${type}"; exit ;; 175 | esac 176 | done | format_output 177 | -------------------------------------------------------------------------------- /ogden/wireguard.nix: -------------------------------------------------------------------------------- 1 | { pkgs, config, ... }: 2 | let 3 | privatekey = config.networking.wireguard.interfaces.wg0.privateKeyFile; 4 | publickey = "${dirOf privatekey}/public"; 5 | in { 6 | networking.firewall.allowedUDPPorts = [ 41741 ]; 7 | networking.wireguard.interfaces.wg0 = { 8 | ips = [ "10.10.2.15/24" ]; 9 | privateKeyFile = "/etc/wireguard/private"; 10 | listenPort = 41741; 11 | 12 | peers = [ 13 | { 14 | # petunia 15 | publicKey = "iRqkVDUccM1duRrG02a9IraBgR9zew6SqAclqUaLoyI="; 16 | allowedIPs = [ "10.10.2.10/32" ]; 17 | } 18 | { 19 | # zoidberg 20 | publicKey = "BQ7+bGuKVat/I8b1s75eKlRAE3PwD9DTTbOJ4yUEAzo="; 21 | allowedIPs = [ "10.10.2.5/32" ]; 22 | endpoint = "gsc.io:51820"; 23 | persistentKeepalive = 25; 24 | } 25 | { 26 | # flexo 27 | publicKey = config.about.flexo.wireguard_public_keys.wg0; 28 | allowedIPs = [ "10.10.2.25/32" ]; 29 | endpoint = "flexo.gsc.io:51820"; 30 | persistentKeepalive = 25; 31 | } 32 | { 33 | # elzar 34 | publicKey = "/5HDPKbtNvC/KxqBrUCnPfLVme8scxLJn9Zs2quXLl0="; 35 | allowedIPs = ["10.10.2.50/32" ]; 36 | persistentKeepalive = 25; 37 | } 38 | ]; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | let 2 | pkgs = import {}; 3 | 4 | inherit (pkgs) stdenv; 5 | 6 | in stdenv.mkDerivation rec { 7 | name = "nixops-personal"; 8 | version = "0.1"; 9 | 10 | src = builtins.toString ./.; 11 | 12 | buildInputs = [ 13 | pkgs.packet 14 | (if true then pkgs.nixops else (import ./nixops/release.nix {}).build.x86_64-linux) 15 | pkgs.jq 16 | ]; 17 | 18 | phases = [ "donotbuild" ]; 19 | donotbuild = '' 20 | printf "\n\n\nDon't nix-build ${src}! It is a dev environment\n\n\n\n" 21 | exit 1 22 | ''; 23 | 24 | SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; 25 | NIXOS_EXTRA_MODULE_PATH = "${src}/modules/default.nix"; 26 | NIXOPS_DEPLOYMENT = "personal"; 27 | HISTFILE = "${src}/.bash_hist"; 28 | NIX_PATH="nixpkgs=https://nixos.org/channels/nixos-19.09/nixexprs.tar.xz"; 29 | } 30 | -------------------------------------------------------------------------------- /zoidberg/default-vhost-config.nix: -------------------------------------------------------------------------------- 1 | { 2 | enableACME = false; 3 | forceSSL = false; 4 | 5 | extraConfig = '' 6 | error_log syslog:server=unix:/dev/log; 7 | access_log syslog:server=unix:/dev/log combined_host; 8 | ''; 9 | } 10 | -------------------------------------------------------------------------------- /zoidberg/default.nix: -------------------------------------------------------------------------------- 1 | { secrets }: 2 | let 3 | defaultVhostCfg = import ./default-vhost-config.nix; 4 | vhostPHPLocations = pkgs: root: { 5 | "/" = { 6 | index = "index.php index.html"; 7 | 8 | extraConfig = '' 9 | try_files $uri $uri/ /index.php$is_args$args; 10 | ''; 11 | }; 12 | 13 | "~ \.php$" = { 14 | extraConfig = '' 15 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 16 | fastcgi_pass unix:/run/php-fpm.sock; 17 | fastcgi_index index.php; 18 | fastcgi_param SCRIPT_FILENAME ${root}/$fastcgi_script_name; 19 | include ${pkgs.nginx}/conf/fastcgi_params; 20 | ''; 21 | }; 22 | }; 23 | in { pkgs, ... }: { 24 | imports = [ 25 | ./wireguard.nix 26 | ./everyaws.nix 27 | (import ./packet-type-0.nix { inherit secrets; }) 28 | ]; 29 | 30 | networking = { 31 | firewall = { 32 | allowedTCPPorts = [ 80 443 587 ]; 33 | }; 34 | }; 35 | 36 | environment = { 37 | systemPackages = with pkgs; [ 38 | git # for kylechristensen 39 | (weechat.override { 40 | configure = { availablePlugins, ... }: { 41 | plugins = [ 42 | (availablePlugins.python.withPackages (ps: [ 43 | (ps.potr.overridePythonAttrs (oldAttrs: 44 | { 45 | propagatedBuildInputs = [ 46 | (ps.buildPythonPackage rec { 47 | name = "pycrypto-${version}"; 48 | version = "2.6.1"; 49 | 50 | src = pkgs.fetchurl { 51 | url = "mirror://pypi/p/pycrypto/${name}.tar.gz"; 52 | sha256 = "0g0ayql5b9mkjam8hym6zyg6bv77lbh66rv1fyvgqb17kfc1xkpj"; 53 | }; 54 | 55 | patches = pkgs.stdenv.lib.singleton (pkgs.fetchpatch { 56 | name = "CVE-2013-7459.patch"; 57 | url = "https://anonscm.debian.org/cgit/collab-maint/python-crypto.git" 58 | + "/plain/debian/patches/CVE-2013-7459.patch?h=debian/2.6.1-7"; 59 | sha256 = "01r7aghnchc1bpxgdv58qyi2085gh34bxini973xhy3ks7fq3ir9"; 60 | }); 61 | 62 | buildInputs = [ pkgs.gmp ]; 63 | 64 | preConfigure = '' 65 | sed -i 's,/usr/include,/no-such-dir,' configure 66 | sed -i "s!,'/usr/include/'!!" setup.py 67 | ''; 68 | }) 69 | ]; 70 | } 71 | )) 72 | ])) 73 | ]; 74 | }; 75 | }) 76 | screen 77 | tmux 78 | aspell 79 | aspellDicts.en 80 | emacs 81 | vim 82 | ]; 83 | }; 84 | 85 | services = { 86 | fail2ban = { 87 | enable = true; 88 | }; 89 | 90 | mysql = { 91 | enable = true; 92 | package = pkgs.mysql55; 93 | }; 94 | 95 | nginx = { 96 | enable = true; 97 | recommendedGzipSettings = true; 98 | recommendedOptimisation = true; 99 | recommendedProxySettings = true; 100 | recommendedTlsSettings = true; 101 | commonHttpConfig = '' 102 | log_format combined_host '$host $remote_addr - $remote_user [$time_local] ' 103 | '"$request" $status $bytes_sent ' 104 | '"$http_referer" "$http_user_agent" "$gzip_ratio"'; 105 | ''; 106 | 107 | virtualHosts = { 108 | "zoidberg.gsc.io" = defaultVhostCfg // { 109 | default = true; 110 | }; 111 | 112 | #"zoidberg-ssl.gsc.io" = defaultVhostCfg // { 113 | # default = true; 114 | # enableACME = true; 115 | # enableSSL = true; 116 | #}; 117 | 118 | "nix.gsc.io" = defaultVhostCfg // { 119 | root = ./nix/webroot; 120 | enableACME = true; 121 | forceSSL = true; 122 | locations."/".extraConfig = '' 123 | autoindex on; 124 | ''; 125 | }; 126 | 127 | "gsc.io" = defaultVhostCfg // { 128 | #enableACME = true; 129 | #forceSSL = true; 130 | root = "/var/lib/nginx/grahamc/gsc.io/public"; 131 | }; 132 | 133 | #"ihavenoideawhatimdoing.dog" = defaultVhostCfg // rec { 134 | # enableACME = true; 135 | # forceSSL = true; 136 | # root = pkgs.callPackage ../../ihavenoideawhatimdoing.dog {}; 137 | # locations = (vhostPHPLocations pkgs root); 138 | #}; 139 | 140 | "www.gsc.io" = defaultVhostCfg // { 141 | enableACME = true; 142 | forceSSL = true; 143 | globalRedirect = "gsc.io"; 144 | }; 145 | 146 | "u.gsc.io" = defaultVhostCfg // { 147 | root = ./url-shortener-root; 148 | enableACME = true; 149 | forceSSL = true; 150 | 151 | extraConfig = '' 152 | rewrite ^/(\d+)$ index.php?n=$1 last; 153 | ''; 154 | 155 | locations = (vhostPHPLocations pkgs ./url-shortener-root); 156 | }; 157 | }; 158 | }; 159 | 160 | phpfpm.pools.main = { 161 | listen = "/run/php-fpm.sock"; 162 | extraConfig = '' 163 | 164 | listen.owner = nginx 165 | listen.group = nginx 166 | listen.mode = 0600 167 | user = nginx 168 | pm = dynamic 169 | pm.max_children = 75 170 | pm.start_servers = 10 171 | pm.min_spare_servers = 5 172 | pm.max_spare_servers = 20 173 | pm.max_requests = 500 174 | catch_workers_output = yes 175 | ''; 176 | }; 177 | }; 178 | 179 | systemd = { 180 | services = { 181 | urlsdir = { 182 | wantedBy = [ "multi-user.target" ]; 183 | before = [ "nginx.service" ]; 184 | serviceConfig = { 185 | Type = "oneshot"; 186 | RemainAfterExit = true; 187 | }; 188 | 189 | script = '' 190 | mkdir -p /var/lib/url-shortener 191 | chown -R nginx:nginx /var/lib/url-shortener 192 | 193 | mkdir -p /var/lib/nginx 194 | chown nginx:nginx /var/lib/nginx 195 | 196 | mkdir -p /var/lib/nginx/grahamc/gsc.io/public 197 | chown nginx:nginx /var/lib/nginx/grahamc/ 198 | chown grahamc:users /var/lib/nginx/grahamc/gsc.io 199 | chown grahamc:users /var/lib/nginx/grahamc/gsc.io/public 200 | if ! test -L /home/grahamc/gsc.io; then 201 | ln -s /var/lib/nginx/grahamc/gsc.io /home/grahamc/gsc.io 202 | fi 203 | ''; 204 | }; 205 | }; 206 | }; 207 | 208 | users = { 209 | extraUsers = { 210 | }; 211 | }; 212 | } 213 | -------------------------------------------------------------------------------- /zoidberg/gcofborgpkg.nix: -------------------------------------------------------------------------------- 1 | (import ./../../ircbot/ofborg {}) 2 | -------------------------------------------------------------------------------- /zoidberg/micro-ci.nix: -------------------------------------------------------------------------------- 1 | { secrets }: 2 | { pkgs, config, ... }: 3 | let 4 | defaultVhostCfg = import ./default-vhost-config.nix; 5 | in { 6 | services.nginx.virtualHosts = { 7 | "ci.nix.gsc.io" = defaultVhostCfg // { 8 | enableACME = true; 9 | forceSSL = true; 10 | locations = { 11 | "/".proxyPass = "http://127.0.0.1:8080/"; 12 | }; 13 | }; 14 | }; 15 | 16 | users = { 17 | users.microci = { 18 | description = "MicroCI"; 19 | home = "/var/lib/microci"; 20 | createHome = true; 21 | group = "microci"; 22 | uid = 401; 23 | }; 24 | 25 | groups.microci.gid = 401; 26 | }; 27 | 28 | 29 | systemd.services.microci = { 30 | enable = false; 31 | after = [ "network.target" "network-online.target" ]; 32 | wants = [ "network-online.target" ]; 33 | before = [ "nginx.service" ]; 34 | wantedBy = [ "multi-user.target" ]; 35 | 36 | path = with pkgs; [ git nix ]; 37 | 38 | serviceConfig = { 39 | User = "microci"; 40 | Group = "microci"; 41 | PrivateTmp = true; 42 | WorkingDirectory = "/var/lib/microci"; 43 | }; 44 | 45 | preStart = '' 46 | rm -f config.dhall 47 | cp ${./config.dhall} ./config.dhall 48 | ''; 49 | 50 | script = '' 51 | . /etc/profile 52 | ${(import ./micro-ci/ci.nix).micro-ci}/bin/micro-ci 53 | ''; 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /zoidberg/nix/webroot/index.html: -------------------------------------------------------------------------------- 1 |

nix.gsc.io

2 |

Unofficial Services for the Nix Project

3 |

Here are some services I offer for the Nix project. I set them up 4 | because I find them useful. Maybe you will too.

5 |

Have fun :)

6 |

- Graham

7 | 12 | 13 |
14 |

email me if you want, graham-at-grahamc-dot-com, or gchristensen on Freenode

15 |
16 | -------------------------------------------------------------------------------- /zoidberg/packet-type-0.nix: -------------------------------------------------------------------------------- 1 | { secrets }: 2 | { 3 | boot = { 4 | kernel.sysctl = { 5 | "net.ipv4.forwarding" = 1; # BGP ^.^ 6 | }; 7 | 8 | initrd = { 9 | availableKernelModules = [ 10 | "ehci_pci" "ahci" "usbhid" "sd_mod" 11 | ]; 12 | }; 13 | kernelModules = [ "kvm-intel" ]; 14 | kernelParams = [ "console=ttyS1,115200n8" ]; 15 | extraModulePackages = [ ]; 16 | loader = { 17 | grub = { 18 | devices = [ "/dev/sda" ]; 19 | enable = true; 20 | version = 2; 21 | extraConfig = '' 22 | serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1 23 | terminal_output serial console 24 | terminal_input serial console 25 | ''; 26 | }; 27 | }; 28 | }; 29 | 30 | deployment = { 31 | targetHost = "147.75.97.237"; # "2604:1380:0:d00::1"; 32 | # targetPort = 443; 33 | }; 34 | 35 | fileSystems = { 36 | "/" = { 37 | device = "/dev/disk/by-label/nixos"; 38 | fsType = "ext4"; 39 | }; 40 | }; 41 | 42 | hardware = { 43 | enableAllFirmware = true; 44 | }; 45 | 46 | nix = { 47 | maxJobs = 4; 48 | }; 49 | 50 | services.openssh.enable = true; 51 | services.bird = { 52 | enable = true; 53 | config = '' 54 | filter packetdns { 55 | # IPs to announce (the elastic ip in our case) 56 | # Doesn't have to be /32. Can be lower 57 | if net = 147.75.96.102/32 then accept; 58 | } 59 | 60 | # your (Private) bond0 IP below here 61 | router id 10.100.5.1; 62 | protocol direct { 63 | interface "lo"; # Restrict network interfaces it works with 64 | } 65 | protocol kernel { 66 | # learn; # Learn all alien routes from the kernel 67 | persist; # Don't remove routes on bird shutdown 68 | scan time 20; # Scan kernel routing table every 20 seconds 69 | import all; # Default is import all 70 | export all; # Default is export none 71 | # kernel table 5; # Kernel table to synchronize with (default: main) 72 | } 73 | 74 | # This pseudo-protocol watches all interface up/down events. 75 | protocol device { 76 | scan time 10; # Scan interfaces every 10 seconds 77 | } 78 | 79 | # your default gateway IP below here 80 | protocol bgp { 81 | export filter packetdns; 82 | local as 65000; 83 | neighbor 10.100.5.0 as 65530; 84 | password "${secrets.zoidberg_bgp_password}"; 85 | } 86 | ''; 87 | }; 88 | 89 | networking = { 90 | hostId = "7a13df42"; 91 | hostName = "zoidberg"; 92 | dhcpcd.enable = false; 93 | 94 | nameservers = [ "4.2.2.1" "4.2.2.2" "2001:4860:4860::8888" ]; 95 | 96 | bonds = { 97 | bond0 = { 98 | driverOptions.mode = "balance-tlb"; 99 | interfaces = [ 100 | "enp0s20f0" "enp0s20f1" 101 | ]; 102 | }; 103 | }; 104 | 105 | defaultGateway = { 106 | address = "147.75.97.236"; 107 | interface = "bond0"; 108 | }; 109 | 110 | defaultGateway6 = { 111 | address = "2604:1380:0:d00::"; 112 | interface = "bond0"; 113 | }; 114 | 115 | interfaces = { 116 | lo = { 117 | useDHCP = false; 118 | 119 | ipv4.addresses = [ 120 | { 121 | # BGP ^.^ 122 | address = "147.75.96.102"; 123 | prefixLength = 32; 124 | } 125 | ]; 126 | }; 127 | bond0 = { 128 | useDHCP = true; 129 | 130 | ipv4 = { 131 | routes = [ 132 | { 133 | address = "10.0.0.0"; 134 | prefixLength = 8; 135 | via = "10.100.5.0"; 136 | } 137 | ]; 138 | addresses = [ 139 | { 140 | address = "147.75.97.237"; 141 | prefixLength = 31; 142 | } 143 | { 144 | address = "10.100.5.1"; 145 | prefixLength = 31; 146 | } 147 | ]; 148 | }; 149 | 150 | ipv6 = { 151 | addresses = [ 152 | { 153 | address = "2604:1380:0:d00::1"; 154 | prefixLength = 127; 155 | } 156 | ]; 157 | }; 158 | }; 159 | }; 160 | }; 161 | } 162 | -------------------------------------------------------------------------------- /zoidberg/queue-monitor/index.html: -------------------------------------------------------------------------------- 1 | email me for creds: graham-at-grahamc-dot-com, gchristensen on irc 2 | -------------------------------------------------------------------------------- /zoidberg/queue-monitor/prometheus.php: -------------------------------------------------------------------------------- 1 | $queue['name'], 12 | 'consumers' => $queue['consumers'], 13 | 'messages' => [ 14 | 'waiting' => $todo, 15 | 'in_progress' => $total_msgs - $todo, 16 | ], 17 | ]; 18 | }, 19 | $queues 20 | ); 21 | 22 | $filtered_stats = array_filter($stats, 23 | function($queue) { 24 | return (strpos($queue['name'], 'build-inputs-') === 0) 25 | || ($queue['name'] === 'mass-rebuild-check-jobs'); 26 | } 27 | ); 28 | 29 | 30 | $stats = array_reduce( 31 | $filtered_stats, 32 | function($collector, $arch) { 33 | $name = $arch['name']; 34 | unset($arch['name']); 35 | 36 | if ($name === 'mass-rebuild-check-jobs') { 37 | $collector['evaluator'] = $arch; 38 | } elseif (strpos($name, 'build-inputs-') === 0) { 39 | if (!isset($collector['build-queues'])) { 40 | $collector['build-queues'] = []; 41 | } 42 | 43 | $collector['build-queues'][$name] = $arch; 44 | } 45 | 46 | return $collector; 47 | }, 48 | [] 49 | ); 50 | 51 | echo "ofborg_queue_evaluator_consumers " . $stats['evaluator']['consumers'] . "\n"; 52 | echo "ofborg_queue_evaluator_waiting " . $stats['evaluator']['messages']['waiting'] . "\n"; 53 | echo "ofborg_queue_evaluator_in_progress " . $stats['evaluator']['messages']['in_progress'] . "\n"; 54 | 55 | foreach ($stats['build-queues'] as $archstr => $stats) { 56 | $arch = str_replace("build-inputs-", "", $archstr); 57 | 58 | echo 'ofborg_queue_builder_consumers{arch="' . $arch .'"} '. $stats['consumers'] . "\n"; 59 | echo 'ofborg_queue_builder_waiting{arch="' . $arch .'"} '. $stats['messages']['waiting'] . "\n"; 60 | echo 'ofborg_queue_builder_in_progress{arch="' . $arch .'"} '. $stats['messages']['in_progress'] . "\n"; 61 | } 62 | -------------------------------------------------------------------------------- /zoidberg/queue-monitor/stats.php: -------------------------------------------------------------------------------- 1 | $queue['name'], 12 | 'consumers' => $queue['consumers'], 13 | 'messages' => [ 14 | 'waiting' => $todo, 15 | 'in_progress' => $total_msgs - $todo, 16 | ], 17 | ]; 18 | }, 19 | $queues 20 | ); 21 | 22 | $filtered_stats = array_filter($stats, 23 | function($queue) { 24 | return (strpos($queue['name'], 'build-inputs-') === 0) 25 | || ($queue['name'] === 'mass-rebuild-check-jobs'); 26 | } 27 | ); 28 | 29 | 30 | $categorized_stats = array_reduce( 31 | $filtered_stats, 32 | function($collector, $arch) { 33 | $name = $arch['name']; 34 | unset($arch['name']); 35 | 36 | if ($name === 'mass-rebuild-check-jobs') { 37 | $collector['evaluator'] = $arch; 38 | } elseif (strpos($name, 'build-inputs-') === 0) { 39 | if (!isset($collector['build-queues'])) { 40 | $collector['build-queues'] = []; 41 | } 42 | 43 | $collector['build-queues'][$name] = $arch; 44 | } 45 | 46 | return $collector; 47 | }, 48 | [] 49 | ); 50 | 51 | 52 | header('Content-Type: application/json'); 53 | echo json_encode($categorized_stats, JSON_PRETTY_PRINT); 54 | -------------------------------------------------------------------------------- /zoidberg/url-shortener-root/index.php: -------------------------------------------------------------------------------- 1 | "${privatekey}" 58 | chmod 0400 "${privatekey}" 59 | 60 | touch "${publickey}" 61 | chmod 0600 "${publickey}" 62 | wg pubkey < "${privatekey}" > "${publickey}" 63 | chmod 0444 "${publickey}" 64 | fi 65 | ''; 66 | }; 67 | systemd.paths."wireguard-wg0" = { 68 | pathConfig = { 69 | PathExists = privatekey; 70 | }; 71 | }; 72 | } 73 | --------------------------------------------------------------------------------