Compare commits
73 Commits
staging/li
...
staging/dn
Author | SHA1 | Date | |
---|---|---|---|
20bdad9132 | |||
7aaab9eb30 | |||
481110fefb | |||
c44f69a01f | |||
adbc2a76c3 | |||
34ed201aff | |||
4d63b81b05 | |||
e1a18cdae1 | |||
2a1d87650b | |||
4a18dfeef3 | |||
ff1aece1ed | |||
05cf5e376a | |||
855a66499f | |||
b9cc581736 | |||
0a8eee8af0 | |||
a40fc7e112 | |||
6bbb5669a6 | |||
c8d5411462 | |||
af4cfc29b1 | |||
9942025a2f | |||
04f7287781 | |||
14ae501433 | |||
46edc56a32 | |||
7907623887 | |||
c542e120ef | |||
7fcff0b6a2 | |||
32671201a4 | |||
4d2268b5f1 | |||
e5fe7c093a | |||
162f3a291c | |||
31740befbf | |||
0c610c8f1c | |||
e9dc22c1f2 | |||
75e6393680 | |||
9ca6857f4d | |||
8c30b87a94 | |||
6ffd6693cb | |||
e11fe929f4 | |||
3dcd5629a7 | |||
4cf4c38da3 | |||
e0e3c36d1b | |||
108c1d9d60 | |||
c6e16ebc13 | |||
aa60838551 | |||
d6bde02dfe | |||
d07bb03936 | |||
1ab2f42ff4 | |||
e0d20cb62a | |||
f8944c8379 | |||
ca38bb4aec | |||
287817056f | |||
5cc7ced859 | |||
4dc5378b3e | |||
fe7e440997 | |||
e4262cb0bc | |||
35c9f2bf60 | |||
13794e9eaa | |||
a33950da62 | |||
37995e23c2 | |||
66156829d9 | |||
3c40fa6982 | |||
c1ddddddc0 | |||
aae118b476 | |||
7e402ce974 | |||
5b80308074 | |||
e5c94b410f | |||
209c18cb38 | |||
616a2dd19f | |||
5b0f898c62 | |||
a541e866a1 | |||
d3eb0bee26 | |||
2ca0f6ea62 | |||
66be38bfbf |
7
TODO.md
7
TODO.md
@@ -1,3 +1,7 @@
|
||||
## BUGS
|
||||
- why i need to manually restart `wireguard-wg-ovpns` on servo periodically
|
||||
- else DNS fails
|
||||
|
||||
## REFACTORING:
|
||||
### sops/secrets
|
||||
- attach secrets to the thing they're used by (sane.programs)
|
||||
@@ -9,6 +13,9 @@
|
||||
- will make it easier to test new services?
|
||||
|
||||
### upstreaming
|
||||
- split out a trust-dns module
|
||||
- see: <https://github.com/NixOS/nixpkgs/pull/205866#issuecomment-1575753054>
|
||||
- bump nodejs version in lemmy-ui
|
||||
- add updateScripts to all my packages in nixpkgs
|
||||
- fix lightdm-mobile-greeter for newer libhandy
|
||||
- port zecwallet-lite to a from-source build
|
||||
|
18
flake.lock
generated
18
flake.lock
generated
@@ -66,11 +66,11 @@
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1684025543,
|
||||
"narHash": "sha256-hGe7S+i5je+8E/b2mOXVI9nmr038Dw+bV8e1P8xHSe0=",
|
||||
"lastModified": 1684632198,
|
||||
"narHash": "sha256-SdxMPd0WmU9MnDBuuy7ouR++GftrThmSGL7PCQj/uVI=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c6d2f3dc0d3efd4285eebe4f8a36a47ba438138e",
|
||||
"rev": "d0dade110dc7072d67ce27826cfe9ab2ab0cf247",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -82,11 +82,11 @@
|
||||
},
|
||||
"nixpkgs-unpatched": {
|
||||
"locked": {
|
||||
"lastModified": 1684385584,
|
||||
"narHash": "sha256-O7y0gK8OLIDqz+LaHJJyeu09IGiXlZIS3+JgEzGmmJA=",
|
||||
"lastModified": 1684935479,
|
||||
"narHash": "sha256-6QMMsXMr2nhmOPHdti2j3KRHt+bai2zw+LJfdCl97Mk=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "48a0fb7aab511df92a17cf239c37f2bd2ec9ae3a",
|
||||
"rev": "f91ee3065de91a3531329a674a45ddcb3467a650",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -113,11 +113,11 @@
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1684032930,
|
||||
"narHash": "sha256-ueeSYDii2e5bkKrsSdP12JhkW9sqgYrUghLC8aDfYGQ=",
|
||||
"lastModified": 1684637723,
|
||||
"narHash": "sha256-0vAxL7MVMhGbTkAyvzLvleELHjVsaS43p+PR1h9gzNQ=",
|
||||
"owner": "Mic92",
|
||||
"repo": "sops-nix",
|
||||
"rev": "a376127bb5277cd2c337a9458744f370aaf2e08d",
|
||||
"rev": "4ccdfb573f323a108a44c13bb7730e42baf962a9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@@ -77,14 +77,30 @@
|
||||
# enable rotation sensor
|
||||
hardware.sensor.iio.enable = true;
|
||||
|
||||
# from https://gitlab.manjaro.org/manjaro-arm/packages/community/phosh/alsa-ucm-pinephone
|
||||
# mobile-nixos does this same thing, with *slightly different settings*.
|
||||
# i trust manjaro more because the guy maintaining that is actively trying to upstream into alsa-ucm-conf.
|
||||
# an alternative may be to build a custom alsa with the PinePhone config patch applied:
|
||||
# - <https://github.com/alsa-project/alsa-ucm-conf/pull/134>
|
||||
# that would make this be not device-specific
|
||||
environment.variables.ALSA_CONFIG_UCM2 = "${./ucm2}";
|
||||
systemd.services.pulseaudio.environment.ALSA_CONFIG_UCM2 = "${./ucm2}";
|
||||
# inject specialized alsa configs via the environment.
|
||||
# specifically, this gets the pinephone headphones & internal earpiece working.
|
||||
# see pkgs/patched/alsa-ucm-conf for more info.
|
||||
environment.variables.ALSA_CONFIG_UCM2 = "/run/current-system/sw/share/alsa/ucm2";
|
||||
environment.pathsToLink = [ "/share/alsa/ucm2" ];
|
||||
environment.systemPackages = [ pkgs.alsa-ucm-conf-sane ];
|
||||
systemd =
|
||||
let ucm-env = config.environment.variables.ALSA_CONFIG_UCM2;
|
||||
in {
|
||||
# cribbed from <repo:nixos/mobile-nixos:modules/quirks/audio.nix>
|
||||
|
||||
# pulseaudio
|
||||
user.services.pulseaudio.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
services.pulseaudio.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
|
||||
# pipewire
|
||||
user.services.pipewire.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
user.services.pipewire-pulse.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
user.services.wireplumber.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
services.pipewire.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
services.pipewire-pulse.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
services.wireplumber.environment.ALSA_CONFIG_UCM2 = ucm-env;
|
||||
};
|
||||
|
||||
|
||||
hardware.opengl.driSupport = true;
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@
|
||||
sane.zsh.showDeadlines = false; # ~/knowledge doesn't always exist
|
||||
sane.services.dyn-dns.enable = true;
|
||||
sane.services.wg-home.enable = true;
|
||||
sane.services.wg-home.enableWan = true;
|
||||
sane.services.wg-home.ip = config.sane.hosts.by-name."servo".wg-home.ip;
|
||||
# sane.services.duplicity.enable = true; # TODO: re-enable after HW upgrade
|
||||
|
||||
|
@@ -3,6 +3,12 @@
|
||||
{
|
||||
networking.domain = "uninsane.org";
|
||||
|
||||
sane.ports.openFirewall = true;
|
||||
sane.ports.openUpnp = true;
|
||||
|
||||
# view refused packets with: `sudo journalctl -k`
|
||||
# networking.firewall.logRefusedPackets = true;
|
||||
|
||||
# The global useDHCP flag is deprecated, therefore explicitly set to false here.
|
||||
# Per-interface useDHCP will be mandatory in the future, so this generated config
|
||||
# replicates the default behaviour.
|
||||
@@ -11,9 +17,6 @@
|
||||
# XXX colin: probably don't need this. wlan0 won't be populated unless i touch a value in networking.interfaces.wlan0
|
||||
networking.wireless.enable = false;
|
||||
|
||||
# networking.firewall.enable = false;
|
||||
networking.firewall.enable = true;
|
||||
|
||||
# this is needed to forward packets from the VPN to the host
|
||||
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
|
||||
|
||||
@@ -153,9 +156,9 @@
|
||||
|
||||
# we also bridge DNS traffic
|
||||
${in-ns} ${iptables} -A PREROUTING -t nat -p udp --dport 53 -m iprange --dst-range ${vpn-ip} \
|
||||
-j DNAT --to-destination ${veth-host-ip}:53
|
||||
-j DNAT --to-destination ${veth-host-ip}
|
||||
${in-ns} ${iptables} -A PREROUTING -t nat -p tcp --dport 53 -m iprange --dst-range ${vpn-ip} \
|
||||
-j DNAT --to-destination ${veth-host-ip}:53
|
||||
-j DNAT --to-destination ${veth-host-ip}
|
||||
|
||||
# in order to access DNS in this netns, we need to route it to the VPN's nameservers
|
||||
# - alternatively, we could fix DNS servers like 1.1.1.1.
|
||||
|
@@ -22,20 +22,60 @@
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "ejabberd"; group = "ejabberd"; directory = "/var/lib/ejabberd"; }
|
||||
];
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
3478 # STUN/TURN
|
||||
5222 # XMPP client -> server
|
||||
5223 # XMPPS client -> server (XMPP over TLS)
|
||||
5269 # XMPP server -> server
|
||||
5270 # XMPPS server -> server (XMPP over TLS)
|
||||
5280 # bosh
|
||||
5281 # bosh (https) ??
|
||||
5349 # STUN/TURN (TLS)
|
||||
5443 # web services (file uploads, websockets, admin)
|
||||
];
|
||||
networking.firewall.allowedUDPPorts = [
|
||||
3478 # STUN/TURN
|
||||
];
|
||||
sane.ports.ports."3478" = {
|
||||
protocol = [ "tcp" "udp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-stun-turn";
|
||||
};
|
||||
sane.ports.ports."5222" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-client-to-server";
|
||||
};
|
||||
sane.ports.ports."5223" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpps-client-to-server"; # XMPP over TLS
|
||||
};
|
||||
sane.ports.ports."5269" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-server-to-server";
|
||||
};
|
||||
sane.ports.ports."5270" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpps-server-to-server"; # XMPP over TLS
|
||||
};
|
||||
sane.ports.ports."5280" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-bosh";
|
||||
};
|
||||
sane.ports.ports."5281" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-bosh-https";
|
||||
};
|
||||
sane.ports.ports."5349" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-stun-turn-over-tls";
|
||||
};
|
||||
sane.ports.ports."5443" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-web-services"; # file uploads, websockets, admin
|
||||
};
|
||||
|
||||
# TODO: forward these TURN ports!
|
||||
networking.firewall.allowedTCPPortRanges = [{
|
||||
from = 49152; # TURN
|
||||
to = 49408;
|
||||
@@ -77,7 +117,7 @@
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet = {
|
||||
# XXX: SRV records have to point to something with a A/AAAA record; no CNAMEs
|
||||
A."xmpp" = "%NATIVE%";
|
||||
A."xmpp" = "%ANATIVE%";
|
||||
CNAME."muc.xmpp" = "xmpp";
|
||||
CNAME."pubsub.xmpp" = "xmpp";
|
||||
CNAME."upload.xmpp" = "xmpp";
|
||||
@@ -234,7 +274,7 @@
|
||||
use_turn: true
|
||||
turn_min_port: 49152
|
||||
turn_max_port: 65535
|
||||
turn_ipv4_address: %NATIVE%
|
||||
turn_ipv4_address: %ANATIVE%
|
||||
-
|
||||
# STUN+TURN UDP
|
||||
port: 3478
|
||||
@@ -243,7 +283,7 @@
|
||||
use_turn: true
|
||||
turn_min_port: 49152
|
||||
turn_max_port: 65535
|
||||
turn_ipv4_address: %NATIVE%
|
||||
turn_ipv4_address: %ANATIVE%
|
||||
-
|
||||
# STUN+TURN TLS over TCP
|
||||
port: 5349
|
||||
@@ -254,7 +294,7 @@
|
||||
use_turn: true
|
||||
turn_min_port: 49152
|
||||
turn_max_port: 65535
|
||||
turn_ipv4_address: %NATIVE%
|
||||
turn_ipv4_address: %ANATIVE%
|
||||
|
||||
# TODO: enable mod_fail2ban
|
||||
# TODO(low): look into mod_http_fileserver for serving macros?
|
||||
@@ -387,7 +427,7 @@
|
||||
# config is 444 (not 644), so we want to write out-of-place and then atomically move
|
||||
# TODO: factor this out into `sane-woop` helper?
|
||||
rm -f /var/lib/ejabberd/ejabberd.yaml.new
|
||||
${sed} "s/%NATIVE%/$ip/" ${config-in} > /var/lib/ejabberd/ejabberd.yaml.new
|
||||
${sed} "s/%ANATIVE%/$ip/" ${config-in} > /var/lib/ejabberd/ejabberd.yaml.new
|
||||
mv /var/lib/ejabberd/ejabberd.yaml{.new,}
|
||||
'';
|
||||
|
||||
|
@@ -6,11 +6,18 @@
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
# exposed over non-vpn imap.uninsane.org
|
||||
143 # IMAP
|
||||
993 # IMAPS
|
||||
];
|
||||
sane.ports.ports."143" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-imap-imap.uninsane.org";
|
||||
};
|
||||
sane.ports.ports."993" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-imaps-imap.uninsane.org";
|
||||
};
|
||||
|
||||
# exists only to manage certs for dovecot
|
||||
services.nginx.virtualHosts."imap.uninsane.org" = {
|
||||
|
@@ -28,12 +28,21 @@ in
|
||||
# "/var/lib/dovecot"
|
||||
];
|
||||
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
# exposed over vpn mx.uninsane.org
|
||||
25 # SMTP
|
||||
465 # SMTPS
|
||||
587 # SMTPS/submission
|
||||
];
|
||||
sane.ports.ports."25" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.ovpn = true;
|
||||
description = "colin-smtp-mx.uninsane.org";
|
||||
};
|
||||
sane.ports.ports."465" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.ovpn = true;
|
||||
description = "colin-smtps-mx.uninsane.org";
|
||||
};
|
||||
sane.ports.ports."587" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.ovpn = true;
|
||||
description = "colin-smtps-submission-mx.uninsane.org";
|
||||
};
|
||||
|
||||
# exists only to manage certs for Postfix
|
||||
services.nginx.virtualHosts."mx.uninsane.org" = {
|
||||
|
@@ -99,4 +99,11 @@
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."git" = "native";
|
||||
|
||||
sane.ports.ports."22" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-git@git.uninsane.org";
|
||||
};
|
||||
}
|
||||
|
@@ -16,17 +16,30 @@
|
||||
{ config, lib, ... }:
|
||||
|
||||
{
|
||||
# identical to:
|
||||
# services.jellyfin.openFirewall = true;
|
||||
networking.firewall.allowedUDPPorts = [
|
||||
# https://jellyfin.org/docs/general/networking/index.html
|
||||
1900 # UPnP service discovery
|
||||
7359 # Jellyfin-specific (?) client discovery
|
||||
];
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
8096 # HTTP (for the LAN)
|
||||
8920 # HTTPS (for the LAN)
|
||||
];
|
||||
# https://jellyfin.org/docs/general/networking/index.html
|
||||
sane.ports.ports."1900" = {
|
||||
protocol = [ "udp" ];
|
||||
visibleTo.lan = true;
|
||||
description = "colin-upnp-for-jellyfin";
|
||||
};
|
||||
sane.ports.ports."7359" = {
|
||||
protocol = [ "udp" ];
|
||||
visibleTo.lan = true;
|
||||
description = "colin-jellyfin-specific-client-discovery";
|
||||
# ^ not sure if this is necessary: copied this port from nixos jellyfin.openFirewall
|
||||
};
|
||||
# not sure if 8096/8920 get used either:
|
||||
sane.ports.ports."8096" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
description = "colin-jellyfin-http-lan";
|
||||
};
|
||||
sane.ports.ports."8920" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
description = "colin-jellyfin-https-lan";
|
||||
};
|
||||
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "jellyfin"; group = "jellyfin"; mode = "0700"; directory = "/var/lib/jellyfin"; }
|
||||
];
|
||||
|
@@ -1,4 +1,9 @@
|
||||
{ lib, ... }:
|
||||
|
||||
# XXX mx-discord-puppet uses nodejs_14 which is EOL
|
||||
# - mx-discord-puppet is abandoned upstream _and_ in nixpkgs
|
||||
# - recommended to use mautrix-discord: <https://github.com/NixOS/nixpkgs/pull/200462>
|
||||
lib.mkIf false
|
||||
{
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/mx-puppet-discord"; }
|
||||
|
@@ -13,7 +13,19 @@ let
|
||||
in
|
||||
{
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||
sane.ports.ports."80" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
visibleTo.ovpn = true; # so that letsencrypt can procure a cert for the mx record
|
||||
description = "colin-http-uninsane.org";
|
||||
};
|
||||
sane.ports.ports."443" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-https-uninsane.org";
|
||||
};
|
||||
|
||||
services.nginx.enable = true;
|
||||
services.nginx.appendConfig = ''
|
||||
|
@@ -12,12 +12,29 @@ lib.mkIf false
|
||||
sane.persist.sys.plaintext = [
|
||||
{ user = "prosody"; group = "prosody"; directory = "/var/lib/prosody"; }
|
||||
];
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
5222 # XMPP client -> server
|
||||
5269 # XMPP server -> server
|
||||
5280 # bosh
|
||||
5281 # Prosody HTTPS port (necessary?)
|
||||
];
|
||||
sane.ports.ports."5222" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-client-to-server";
|
||||
};
|
||||
sane.ports.ports."5269" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-server-to-server";
|
||||
};
|
||||
sane.ports.ports."5280" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-bosh";
|
||||
};
|
||||
sane.ports.ports."5281" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-xmpp-prosody-https"; # necessary?
|
||||
};
|
||||
|
||||
# provide access to certs
|
||||
users.users.prosody.extraGroups = [ "nginx" ];
|
||||
|
@@ -30,17 +30,20 @@
|
||||
7d ; Expire
|
||||
5m) ; Negative response TTL
|
||||
'';
|
||||
TXT."rev" = "2022122101";
|
||||
TXT."rev" = "2023052901";
|
||||
|
||||
CNAME."native" = "%CNAMENATIVE%";
|
||||
A."@" = "%ANATIVE%";
|
||||
A."wan" = "%AWAN%";
|
||||
A."servo.lan" = config.sane.hosts.by-name."servo".lan-ip;
|
||||
|
||||
# XXX NS records must also not be CNAME
|
||||
# it's best that we keep this identical, or a superset of, what org. lists as our NS.
|
||||
# so, org. can specify ns2/ns3 as being to the VPN, with no mention of ns1. we provide ns1 here.
|
||||
A."ns1" = "%NATIVE%";
|
||||
A."ns1" = "%ANATIVE%";
|
||||
A."ns2" = "185.157.162.178";
|
||||
A."ns3" = "185.157.162.178";
|
||||
A."ovpns" = "185.157.162.178";
|
||||
A."native" = "%NATIVE%";
|
||||
A."@" = "%NATIVE%";
|
||||
NS."@" = [
|
||||
"ns1.uninsane.org."
|
||||
"ns2.uninsane.org."
|
||||
@@ -48,20 +51,68 @@
|
||||
];
|
||||
};
|
||||
|
||||
sane.services.trust-dns.zones."uninsane.org".file =
|
||||
"/var/lib/trust-dns/uninsane.org.zone";
|
||||
sane.services.trust-dns.zones."uninsane.org".file = "uninsane.org.zone";
|
||||
sane.services.trust-dns.zonedir = null;
|
||||
|
||||
systemd.services.trust-dns.preStart = let
|
||||
sed = "${pkgs.gnused}/bin/sed";
|
||||
zone-dir = "/var/lib/trust-dns";
|
||||
zone-out = "${zone-dir}/uninsane.org.zone";
|
||||
zone-template = pkgs.writeText "uninsane.org.zone.in" config.sane.services.trust-dns.generatedZones."uninsane.org";
|
||||
in ''
|
||||
# make WAN records available to trust-dns
|
||||
mkdir -p ${zone-dir}
|
||||
ip=$(cat '${config.sane.services.dyn-dns.ipPath}')
|
||||
${sed} s/%NATIVE%/$ip/ ${zone-template} > ${zone-out}
|
||||
'';
|
||||
sane.services.trust-dns.package =
|
||||
let
|
||||
sed = "${pkgs.gnused}/bin/sed";
|
||||
zone-dir = "/var/lib/trust-dns";
|
||||
zone-wan = "${zone-dir}/wan/uninsane.org.zone";
|
||||
zone-lan = "${zone-dir}/lan/uninsane.org.zone";
|
||||
zone-template = config.sane.services.trust-dns.zones."uninsane.org".file;
|
||||
in pkgs.writeShellScriptBin "named" ''
|
||||
# compute wan/lan values
|
||||
mkdir -p ${zone-dir}/{ovpn,wan,lan}
|
||||
wan=$(cat '${config.sane.services.dyn-dns.ipPath}')
|
||||
lan=${config.sane.hosts.by-name."servo".lan-ip}
|
||||
|
||||
# create specializations that resolve native.uninsane.org to different CNAMEs
|
||||
${sed} s/%AWAN%/$wan/ ${zone-template} \
|
||||
| ${sed} s/%CNAMENATIVE%/wan/ \
|
||||
| ${sed} s/%ANATIVE%/$wan/ \
|
||||
> ${zone-wan}
|
||||
${sed} s/%AWAN%/$wan/ ${zone-template} \
|
||||
| ${sed} s/%CNAMENATIVE%/servo.lan/ \
|
||||
| ${sed} s/%ANATIVE%/$lan/ \
|
||||
> ${zone-lan}
|
||||
|
||||
# launch the different interfaces, separately
|
||||
${pkgs.trust-dns}/bin/named --port 53 --zonedir ${zone-dir}/wan/ $@ &
|
||||
WANPID=$!
|
||||
${pkgs.trust-dns}/bin/named --port 1053 --zonedir ${zone-dir}/lan/ $@ &
|
||||
LANPID=$!
|
||||
|
||||
# wait until any of the processes exits, then kill them all and exit error
|
||||
while kill -0 $WANPID $LANPID ; do
|
||||
sleep 5
|
||||
done
|
||||
kill $WANPID $LANPID
|
||||
exit 1
|
||||
'';
|
||||
|
||||
sane.services.dyn-dns.restartOnChange = [ "trust-dns.service" ];
|
||||
|
||||
networking.nat.enable = true;
|
||||
networking.nat.extraCommands = ''
|
||||
# redirect incoming DNS requests from LAN addresses
|
||||
# to the LAN-specialized DNS service
|
||||
# N.B.: use the `nixos-*` chains instead of e.g. PREROUTING
|
||||
# because they get cleanly reset across activations or `systemctl restart firewall`
|
||||
# instead of accumulating cruft
|
||||
iptables -t nat -A nixos-nat-pre -p udp --dport 53 \
|
||||
-m iprange --src-range 10.78.76.0-10.78.79.255 \
|
||||
-j DNAT --to-destination :1053
|
||||
iptables -t nat -A nixos-nat-pre -p tcp --dport 53 \
|
||||
-m iprange --src-range 10.78.76.0-10.78.79.255 \
|
||||
-j DNAT --to-destination :1053
|
||||
'';
|
||||
|
||||
sane.ports.ports."1053" = {
|
||||
# because the NAT above redirects in nixos-nat-pre, LAN requests behave as though they arrived on the external interface at the redirected port.
|
||||
# TODO: try nixos-nat-post instead?
|
||||
protocol = [ "udp" "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
description = "colin-redirected-dns-for-lan-namespace";
|
||||
};
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@
|
||||
./ids.nix
|
||||
./machine-id.nix
|
||||
./net.nix
|
||||
./nix-path
|
||||
./persist.nix
|
||||
./programs
|
||||
./secrets.nix
|
||||
@@ -36,11 +37,6 @@
|
||||
nix.extraOptions = ''
|
||||
experimental-features = nix-command flakes
|
||||
'';
|
||||
# allow `nix-shell` (and probably nix-index?) to locate our patched and custom packages
|
||||
nix.nixPath = [
|
||||
"nixpkgs=${pkgs.path}"
|
||||
"nixpkgs-overlays=${../..}/overlays"
|
||||
];
|
||||
# hardlinks identical files in the nix store to save 25-35% disk space.
|
||||
# unclear _when_ this occurs. it's not a service.
|
||||
# does the daemon continually scan the nix store?
|
||||
|
@@ -76,7 +76,7 @@ let
|
||||
## Multidisciplinary Association for Psychedelic Studies
|
||||
(fromDb "mapspodcast.libsyn.com" // uncat)
|
||||
(fromDb "allinchamathjason.libsyn.com" // pol)
|
||||
(fromDb "acquired.libsyn.com" // tech)
|
||||
(fromDb "feeds.transistor.fm/acquired" // tech)
|
||||
## ACQ2 - more "Acquired" episodes
|
||||
(fromDb "acquiredlpbonussecretsecret.libsyn.com" // tech)
|
||||
# The Intercept - Deconstructed; also available: <rss.acast.com/deconstructed>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
{ lib, ... }:
|
||||
|
||||
{
|
||||
# the default backend is "wpa_supplicant".
|
||||
@@ -20,4 +20,8 @@
|
||||
General.RoamThreshold = "-52"; # default -70
|
||||
General.RoamThreshold5G = "-52"; # default -76
|
||||
};
|
||||
|
||||
networking.firewall.allowedUDPPorts = [
|
||||
1900 # to received UPnP advertisements. required by sane-ip-check-upnp
|
||||
];
|
||||
}
|
||||
|
10
hosts/common/nix-path/default.nix
Normal file
10
hosts/common/nix-path/default.nix
Normal file
@@ -0,0 +1,10 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
# allow `nix-shell` (and probably nix-index?) to locate our patched and custom packages
|
||||
nix.nixPath = [
|
||||
"nixpkgs=${pkgs.path}"
|
||||
# note the import starts at repo root: this allows `./overlay/default.nix` to access the stuff at the root
|
||||
"nixpkgs-overlays=${../../..}/hosts/common/nix-path/overlay"
|
||||
];
|
||||
}
|
4
hosts/common/nix-path/overlay/default.nix
Normal file
4
hosts/common/nix-path/overlay/default.nix
Normal file
@@ -0,0 +1,4 @@
|
||||
# XXX: NIX_PATH=...:nixpkgs-overlays=... will import every overlay in the directory
|
||||
# so we prefer to give it a directory with just this *one* overlay, otherwise it imports conflicting overlays
|
||||
# and gets stuck in a loop until it OOMs
|
||||
import ../../../../overlays/all.nix
|
@@ -42,6 +42,7 @@ let
|
||||
jq
|
||||
killall
|
||||
lsof
|
||||
miniupnpc
|
||||
nano
|
||||
netcat
|
||||
nethogs
|
||||
@@ -61,6 +62,7 @@ let
|
||||
tree
|
||||
usbutils
|
||||
wget
|
||||
wirelesstools # iwlist
|
||||
;
|
||||
};
|
||||
sysadminExtraPkgs = {
|
||||
@@ -93,6 +95,7 @@ let
|
||||
# - transcoders (ffmpeg, imagemagick) only wanted on desko/lappy ("powerutils"?)
|
||||
consolePkgs = {
|
||||
inherit (pkgs)
|
||||
alsaUtils # for aplay, speaker-test
|
||||
cdrtools
|
||||
dmidecode
|
||||
efivar
|
||||
@@ -110,7 +113,7 @@ let
|
||||
lm_sensors # for sensors-detect
|
||||
lshw
|
||||
ffmpeg
|
||||
memtester
|
||||
# memtester
|
||||
neovim
|
||||
# nettools
|
||||
# networkmanager
|
||||
@@ -261,6 +264,7 @@ in
|
||||
./git.nix
|
||||
./gnome-feeds.nix
|
||||
./gpodder.nix
|
||||
./jellyfin-media-player.nix
|
||||
./kitty
|
||||
./libreoffice.nix
|
||||
./mpv.nix
|
||||
@@ -355,11 +359,6 @@ in
|
||||
suggestedPrograms = [ "ghostscript" ];
|
||||
};
|
||||
|
||||
# jellyfin stores things in a bunch of directories: this one persists auth info.
|
||||
# it *might* be possible to populate this externally (it's Qt stuff), but likely to
|
||||
# be fragile and take an hour+ to figure out.
|
||||
jellyfin-media-player.persist.plaintext = [ ".local/share/Jellyfin Media Player" ];
|
||||
|
||||
# actual monero blockchain (not wallet/etc; safe to delete, just slow to regenerate)
|
||||
# XXX: is it really safe to persist this? it doesn't have info that could de-anonymize if captured?
|
||||
monero-gui.persist.plaintext = [ ".bitmonero" ];
|
||||
|
13
hosts/common/programs/jellyfin-media-player.nix
Normal file
13
hosts/common/programs/jellyfin-media-player.nix
Normal file
@@ -0,0 +1,13 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
{
|
||||
sane.programs.jellyfin-media-player = {
|
||||
package = pkgs.jellyfin-media-player;
|
||||
# package = pkgs.jellyfin-media-player-qt6;
|
||||
|
||||
# jellyfin stores things in a bunch of directories: this one persists auth info.
|
||||
# it *might* be possible to populate this externally (it's Qt stuff), but likely to
|
||||
# be fragile and take an hour+ to figure out.
|
||||
persist.plaintext = [ ".local/share/Jellyfin Media Player" ];
|
||||
};
|
||||
}
|
@@ -56,10 +56,26 @@ let
|
||||
nixExtensions = concatMap (ext: optional ext.enable ext.package) (attrValues cfg.addons);
|
||||
|
||||
extraPolicies = {
|
||||
FirefoxHome = {
|
||||
Search = true;
|
||||
Pocket = false;
|
||||
Snippets = false;
|
||||
TopSites = false;
|
||||
Highlights = false;
|
||||
};
|
||||
NoDefaultBookmarks = true;
|
||||
OfferToSaveLogins = false;
|
||||
OfferToSaveLoginsDefault = false;
|
||||
PasswordManagerEnabled = false;
|
||||
SearchEngines = {
|
||||
Default = "DuckDuckGo";
|
||||
};
|
||||
UserMessaging = {
|
||||
ExtensionRecommendations = false;
|
||||
SkipOnboarding = true;
|
||||
};
|
||||
|
||||
# these were taken from Librewolf
|
||||
AppUpdateURL = "https://localhost";
|
||||
DisableAppUpdate = true;
|
||||
OverrideFirstRunPage = "";
|
||||
@@ -88,6 +104,7 @@ let
|
||||
# };
|
||||
# NewTabPage = true;
|
||||
};
|
||||
# extraPrefs = ...
|
||||
};
|
||||
|
||||
addonOpts = types.submodule {
|
||||
|
@@ -138,7 +138,7 @@ in
|
||||
}
|
||||
''
|
||||
+ lib.optionalString cfg.showDeadlines ''
|
||||
${pkgs.sane-scripts}/bin/sane-deadlines
|
||||
${pkgs.sane-scripts.deadlines}/bin/sane-deadlines
|
||||
''
|
||||
+ ''
|
||||
# auto-cd into any of these dirs by typing them and pressing 'enter':
|
||||
|
@@ -30,4 +30,15 @@ in
|
||||
})
|
||||
(globalKeys ++ domainKeys)
|
||||
);
|
||||
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings.PermitRootLogin = "no";
|
||||
settings.PasswordAuthentication = false;
|
||||
};
|
||||
sane.ports.ports."22" = {
|
||||
protocol = [ "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
description = lib.mkDefault "colin-ssh";
|
||||
};
|
||||
}
|
||||
|
@@ -129,11 +129,5 @@ in
|
||||
enable = true;
|
||||
wheelNeedsPassword = false;
|
||||
};
|
||||
|
||||
services.openssh = {
|
||||
enable = true;
|
||||
settings.PermitRootLogin = "no";
|
||||
settings.PasswordAuthentication = false;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@@ -90,7 +90,7 @@ in
|
||||
};
|
||||
sane.gui.sxmo.terminal = mkOption {
|
||||
# type = types.nullOr (types.enum [ "foot" "st" "vte" ]);
|
||||
type = types.nullOr types.string;
|
||||
type = types.nullOr types.str;
|
||||
default = "foot";
|
||||
description = ''
|
||||
name of terminal to use for sxmo_terminal.sh.
|
||||
@@ -99,7 +99,7 @@ in
|
||||
};
|
||||
sane.gui.sxmo.keyboard = mkOption {
|
||||
# type = types.nullOr (types.enum ["wvkbd"])
|
||||
type = types.nullOr types.string;
|
||||
type = types.nullOr types.str;
|
||||
default = "wvkbd";
|
||||
description = ''
|
||||
name of on-screen-keyboard to use for sxmo_keyboard.sh.
|
||||
@@ -108,7 +108,7 @@ in
|
||||
'';
|
||||
};
|
||||
sane.gui.sxmo.settings = mkOption {
|
||||
type = types.attrsOf types.string;
|
||||
type = types.attrsOf types.str;
|
||||
default = {};
|
||||
description = ''
|
||||
environment variables used to configure sxmo.
|
||||
|
@@ -26,7 +26,7 @@ in
|
||||
type = types.bool;
|
||||
};
|
||||
sane.nixcache.substituters = mkOption {
|
||||
type = types.listOf types.string;
|
||||
type = types.listOf types.str;
|
||||
default =
|
||||
# TODO: make these blacklisted entries injectable
|
||||
(lib.optional (hostName != "servo") "https://nixcache.uninsane.org")
|
||||
|
@@ -10,7 +10,7 @@
|
||||
};
|
||||
|
||||
config = lib.mkIf config.sane.roles.ac {
|
||||
sane.yggdrasil.enable = true;
|
||||
services.i2p.enable = true;
|
||||
# sane.yggdrasil.enable = true;
|
||||
# services.i2p.enable = true;
|
||||
};
|
||||
}
|
||||
|
@@ -33,6 +33,11 @@ in
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
sane.services.wg-home.enableWan = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "whether to make this port visible on the WAN";
|
||||
};
|
||||
sane.services.wg-home.ip = mkOption {
|
||||
type = types.str;
|
||||
};
|
||||
@@ -50,7 +55,12 @@ in
|
||||
# this config defines both the endpoint (server) and client configs
|
||||
|
||||
# for convenience, have both the server and client use the same port for their wireguard connections.
|
||||
networking.firewall.allowedUDPPorts = [ 51820 ];
|
||||
sane.ports.ports."51820" = {
|
||||
protocol = [ "udp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = cfg.enableWan;
|
||||
description = "colin-wireguard";
|
||||
};
|
||||
networking.wireguard.interfaces.wg-home = {
|
||||
listenPort = 51820;
|
||||
privateKeyFile = "/run/wg-home.priv";
|
||||
|
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 1369733,
|
||||
"content_type": "application/rss+xml; charset=utf-8",
|
||||
"description": "Every company has a story. Learn the playbooks that built the world’s greatest companies — and how you can apply them as a founder, operator, or investor.",
|
||||
"favicon": null,
|
||||
"hubs": [],
|
||||
"is_podcast": true,
|
||||
"is_push": false,
|
||||
"item_count": 173,
|
||||
"last_seen": "2023-01-11T15:26:37.515527+00:00",
|
||||
"last_updated": "2022-12-19T07:22:28+00:00",
|
||||
"score": 18,
|
||||
"self_url": "https://acquired.libsyn.com/rss",
|
||||
"site_name": null,
|
||||
"site_url": null,
|
||||
"title": "Acquired",
|
||||
"url": "https://acquired.libsyn.com/rss",
|
||||
"velocity": 0.066,
|
||||
"version": "rss20"
|
||||
}
|
@@ -1,21 +1,23 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 443732,
|
||||
"content_type": "application/rss+xml; charset=utf-8",
|
||||
"description": "Ben and David are joined by expert founders and investors \u2014 writing the next generation of great company stories in real-time.\n\nWe go behind the scenes on their journeys and bring back emerging insights and lessons that are useful for anyone in the tech and investing ecosystems.\n\nAcquired covers yesterday. ACQ2 covers tomorrow.",
|
||||
"content_length": 567579,
|
||||
"content_type": "text/xml; charset=utf-8",
|
||||
"description": "ACQ2 is Ben and David's conversations with expert founders and investors.",
|
||||
"favicon": "",
|
||||
"favicon_data_uri": "",
|
||||
"hubs": [],
|
||||
"hubs": [
|
||||
"https://pubsubhubbub.appspot.com/"
|
||||
],
|
||||
"is_podcast": true,
|
||||
"is_push": false,
|
||||
"item_count": 92,
|
||||
"last_updated": "2023-03-02T17:03:15+00:00",
|
||||
"score": 10,
|
||||
"self_url": "https://acquiredlpbonussecretsecret.libsyn.com/",
|
||||
"site_name": "ACQ2 by Acquired",
|
||||
"site_url": "https://acquiredlpbonussecretsecret.libsyn.com",
|
||||
"title": "ACQ2 by Acquired",
|
||||
"url": "https://acquiredlpbonussecretsecret.libsyn.com",
|
||||
"velocity": 0.057,
|
||||
"is_push": true,
|
||||
"item_count": 91,
|
||||
"last_updated": "2023-05-09T06:51:48+00:00",
|
||||
"score": 24,
|
||||
"self_url": "https://feeds.transistor.fm/acq2",
|
||||
"site_name": "ACQ2: The Acquired Interviews",
|
||||
"site_url": "https://feeds.transistor.fm",
|
||||
"title": "ACQ2: The Acquired Interviews",
|
||||
"url": "https://feeds.transistor.fm/acq2",
|
||||
"velocity": 0.054,
|
||||
"version": "rss20"
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 1579416,
|
||||
"content_type": "text/xml; charset=utf-8",
|
||||
"description": "Every company has a story.",
|
||||
"favicon": "",
|
||||
"favicon_data_uri": "",
|
||||
"hubs": [
|
||||
"https://pubsubhubbub.appspot.com/"
|
||||
],
|
||||
"is_podcast": true,
|
||||
"is_push": true,
|
||||
"item_count": 178,
|
||||
"last_updated": "2023-05-30T05:02:40+00:00",
|
||||
"score": 24,
|
||||
"self_url": "https://feeds.transistor.fm/acquired",
|
||||
"site_name": "",
|
||||
"site_url": "https://feeds.transistor.fm",
|
||||
"title": "Acquired",
|
||||
"url": "https://feeds.transistor.fm/acquired",
|
||||
"velocity": 0.064,
|
||||
"version": "rss20"
|
||||
}
|
@@ -1,21 +1,21 @@
|
||||
{
|
||||
"bozo": 0,
|
||||
"content_length": 235911,
|
||||
"content_length": 145311,
|
||||
"content_type": "application/xml; charset=utf-8",
|
||||
"description": "<p>The Portal is an exploration into discovery, including conversations with thought leaders. Host Eric Weinstein, Managing Director of Thiel Capital, brings his unique expertise and diverse roster of guests for a wide range of discussions, including science, culture, business, and capitalism. The show will feature people whose lives demonstrate that portals into what we would normally consider impossible, are indeed possible. Guests include presidential candidate Andrew Yang, NY Times bestselling author Sam Harris, and retired Navy Seal and creator of the hit business podcast Jocko Willink.</p>",
|
||||
"favicon": null,
|
||||
"favicon": "",
|
||||
"favicon_data_uri": "",
|
||||
"hubs": [],
|
||||
"is_podcast": true,
|
||||
"is_push": false,
|
||||
"item_count": 44,
|
||||
"last_seen": "2023-01-11T14:47:44.995855+00:00",
|
||||
"last_updated": "2020-12-02T07:50:55+00:00",
|
||||
"score": -12,
|
||||
"self_url": "https://www.omnycontent.com/d/playlist/9b7dacdf-a925-4f95-84dc-ac46003451ff/1713c520-edb6-43a3-b1b9-acb8002fdae7/58e33a0c-f86b-41c5-a11c-acb8002fdaf5/podcast.rss",
|
||||
"site_name": null,
|
||||
"site_url": null,
|
||||
"score": 8,
|
||||
"self_url": "",
|
||||
"site_name": "",
|
||||
"site_url": "",
|
||||
"title": "The Portal",
|
||||
"url": "https://www.omnycontent.com/d/playlist/9b7dacdf-a925-4f95-84dc-ac46003451ff/1713c520-edb6-43a3-b1b9-acb8002fdae7/58e33a0c-f86b-41c5-a11c-acb8002fdaf5/podcast.rss",
|
||||
"url": "https://feed.cdnstream1.com/zjb/feed/download/d9/8a/71/d98a71ac-d1a3-4d92-ab64-64b4ff3192d1.xml",
|
||||
"velocity": 0.082,
|
||||
"version": "rss20"
|
||||
}
|
||||
}
|
@@ -2,12 +2,14 @@
|
||||
|
||||
{
|
||||
imports = [
|
||||
./dns.nix
|
||||
./feeds.nix
|
||||
./fs
|
||||
./ids.nix
|
||||
./programs.nix
|
||||
./image.nix
|
||||
./persist
|
||||
./ports.nix
|
||||
./services
|
||||
./sops.nix
|
||||
./ssh.nix
|
||||
|
145
modules/dns.nix
Normal file
145
modules/dns.nix
Normal file
@@ -0,0 +1,145 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
let
|
||||
cfg = config.sane.dns;
|
||||
toml = pkgs.formats.toml { };
|
||||
recordFormatters = {
|
||||
# quote rules for zone files:
|
||||
# - any character may be encoded by `\DDD`, where `DDD` represents its ascii value in base 8.
|
||||
# - any non-digit `X` may be encoded by `\X`.
|
||||
# - stated in: <https://www.ietf.org/rfc/rfc1035.txt>: 5.1 Format
|
||||
# - visible in <trust-dns:crates/proto/src/serialize/txt/zone_lex.rs:escape_seq>
|
||||
# for us, we can just replace `\` => `\\ and `"` -> `\"`
|
||||
TXT = value: "\"" + (lib.escape [ "\\" "\"" ] value) + "\"";
|
||||
};
|
||||
# proto: "INET", etc
|
||||
# rrtype: "TXT", "A", "CNAME", etc
|
||||
fmtRecord = proto: rrtype: name: value:
|
||||
let
|
||||
formatter = recordFormatters."${rrtype}" or lib.id;
|
||||
in
|
||||
"${name}\t${proto}\t${rrtype}\t${formatter value}";
|
||||
fmtRecordList = proto: rrtype: name: values: concatStringsSep
|
||||
"\n"
|
||||
(map (fmtRecord proto rrtype name) values)
|
||||
;
|
||||
fmtRecordAttrs = proto: rrtype: rrAttrs:
|
||||
concatStringsSep
|
||||
"\n"
|
||||
(
|
||||
attrValues (
|
||||
mapAttrs
|
||||
(name: fmtRecordList proto rrtype name)
|
||||
rrAttrs
|
||||
)
|
||||
);
|
||||
# format other .zone files to include into this one
|
||||
fmtIncludes = paths: concatStringsSep
|
||||
"\n"
|
||||
(map (path: "$INCLUDE ${path}") paths);
|
||||
|
||||
genZone = zcfg: ''
|
||||
$TTL ${toString zcfg.TTL}
|
||||
${fmtRecordAttrs "IN" "SOA" zcfg.inet.SOA}
|
||||
${fmtRecordAttrs "IN" "A" zcfg.inet.A}
|
||||
${fmtRecordAttrs "IN" "CNAME" zcfg.inet.CNAME}
|
||||
${fmtRecordAttrs "IN" "MX" zcfg.inet.MX}
|
||||
${fmtRecordAttrs "IN" "NS" zcfg.inet.NS}
|
||||
${fmtRecordAttrs "IN" "SRV" zcfg.inet.SRV}
|
||||
${fmtRecordAttrs "IN" "TXT" zcfg.inet.TXT}
|
||||
${fmtIncludes zcfg.include}
|
||||
${zcfg.extraConfig}
|
||||
'';
|
||||
|
||||
# (listOf ty) type which also accepts single-assignment of `ty`.
|
||||
# it's used to allow the user to write:
|
||||
# CNAME."foo" = "bar";
|
||||
# as shorthand for
|
||||
# CNAME."foo" = [ "bar" ];
|
||||
listOrUnit = ty: types.coercedTo ty (elem: [ elem ]) (types.listOf ty);
|
||||
in
|
||||
{
|
||||
options = {
|
||||
sane.dns = {
|
||||
zones = mkOption {
|
||||
type = types.attrsOf (types.submodule {
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
description = "zone name. defaults to the attribute name in zones";
|
||||
default = null;
|
||||
};
|
||||
TTL = mkOption {
|
||||
type = types.int;
|
||||
description = "default TTL";
|
||||
default = 3600;
|
||||
};
|
||||
include = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = "paths of other zone files to $INCLUDE into this one";
|
||||
default = [];
|
||||
};
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
description = "extra lines to append to the zone file";
|
||||
default = "";
|
||||
};
|
||||
inet = {
|
||||
SOA = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "Start of Authority record(s)";
|
||||
default = {};
|
||||
};
|
||||
A = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "IPv4 address record(s)";
|
||||
default = {};
|
||||
};
|
||||
CNAME = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "canonical name record(s)";
|
||||
default = {};
|
||||
};
|
||||
MX = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "mail exchanger record(s)";
|
||||
default = {};
|
||||
};
|
||||
NS = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "name server record(s)";
|
||||
default = {};
|
||||
};
|
||||
SRV = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "service record(s)";
|
||||
default = {};
|
||||
};
|
||||
TXT = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "text record(s)";
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
|
||||
file = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
instead of using the generated zone file, use the specified path (user should populate the file specified here).
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
default = {};
|
||||
description = "Declarative zone config";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
sane.services.trust-dns.zones = builtins.mapAttrs (_name: zcfg: {
|
||||
text = genZone zcfg;
|
||||
}) cfg.zones;
|
||||
};
|
||||
}
|
113
modules/ports.nix
Normal file
113
modules/ports.nix
Normal file
@@ -0,0 +1,113 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.sane.ports;
|
||||
|
||||
portOpts = with lib; types.submodule {
|
||||
options = {
|
||||
protocol = mkOption {
|
||||
type = types.listOf (types.enum [ "udp" "tcp" ]);
|
||||
};
|
||||
visibleTo.lan = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
# XXX: if a service is visible to the WAN, it ends up visible to the LAN as well.
|
||||
# technically solvable (explicitly drop packets delivered from LAN IPs) but doesn't make much sense.
|
||||
};
|
||||
visibleTo.wan = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
visibleTo.ovpn = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
# XXX: behaves more or less the same as `lan` visibility.
|
||||
# OVPN passes everything by default.
|
||||
# TODO: have *this* drive what we forward from wireguard namespace to main namespace
|
||||
};
|
||||
description = mkOption {
|
||||
type = types.str;
|
||||
default = "colin-${config.net.hostName}";
|
||||
description = ''
|
||||
short description of why this port is open.
|
||||
this is shown, for example, in an upstream's UPnP status page.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# gives networking.firewall value for a given "${port}" = portCfg.
|
||||
firewallConfigForPort = port: portCfg:
|
||||
# any form of visibility means we need to open the firewall
|
||||
lib.mkIf (portCfg.visibleTo.lan || portCfg.visibleTo.wan || portCfg.visibleTo.ovpn) {
|
||||
allowedTCPPorts = lib.optional (lib.elem "tcp" portCfg.protocol) (lib.toInt port);
|
||||
allowedUDPPorts = lib.optional (lib.elem "udp" portCfg.protocol) (lib.toInt port);
|
||||
};
|
||||
in
|
||||
{
|
||||
options = with lib; {
|
||||
sane.ports = {
|
||||
openFirewall = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
};
|
||||
openUpnp = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
};
|
||||
upnpRenewInterval = mkOption {
|
||||
default = "1hr";
|
||||
type = types.str;
|
||||
description = "how frequently to renew UPnP leases";
|
||||
};
|
||||
upnpLeaseDuration = mkOption {
|
||||
default = 86400;
|
||||
type = types.int;
|
||||
description = "how long to lease UPnP ports for";
|
||||
};
|
||||
|
||||
ports = mkOption {
|
||||
type = types.attrsOf portOpts;
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkMerge [
|
||||
(lib.mkIf cfg.openFirewall {
|
||||
networking.firewall = lib.mkMerge (lib.mapAttrsToList firewallConfigForPort cfg.ports);
|
||||
})
|
||||
(lib.mkIf cfg.openUpnp {
|
||||
systemd.services.upnp-forwards = {
|
||||
description = "forward ports from upstream gateway to this host";
|
||||
serviceConfig.Type = "oneshot";
|
||||
restartTriggers = [(builtins.toJSON cfg)];
|
||||
|
||||
after = [ "network.target" ];
|
||||
script =
|
||||
let
|
||||
portFwd = "${pkgs.sane-scripts.ip-port-forward}/bin/sane-ip-port-forward";
|
||||
forwardsPerCfg = lib.mapAttrsToList
|
||||
(port: portCfg: lib.optionals portCfg.visibleTo.wan
|
||||
(
|
||||
lib.optional (lib.elem "udp" portCfg.protocol) "udp:${port}:${portCfg.description}"
|
||||
++ lib.optional (lib.elem "tcp" portCfg.protocol) "tcp:${port}:${portCfg.description}"
|
||||
)
|
||||
)
|
||||
cfg.ports;
|
||||
forwards = lib.flatten forwardsPerCfg;
|
||||
in ''
|
||||
${portFwd} -v -d ${builtins.toString cfg.upnpLeaseDuration} \
|
||||
${lib.escapeShellArgs forwards}
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.timers.upnp-forwards = {
|
||||
wantedBy = [ "network.target" ];
|
||||
timerConfig = {
|
||||
OnStartupSec = "1min";
|
||||
OnUnitActiveSec = cfg.upnpRenewInterval;
|
||||
};
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
@@ -5,8 +5,9 @@ let
|
||||
cfg = config.sane.services.dyn-dns;
|
||||
getIp = pkgs.writeShellScript "dyn-dns-query-wan" ''
|
||||
# preferred method and fallback
|
||||
${pkgs.sane-scripts}/bin/sane-ip-check-router-wan || \
|
||||
${pkgs.sane-scripts}/bin/sane-ip-check
|
||||
# OPNsense router broadcasts its UPnP endpoint every 30s
|
||||
timeout 60 ${pkgs.sane-scripts.ip-check-upnp}/bin/sane-ip-check-upnp || \
|
||||
${pkgs.sane-scripts.ip-check}/bin/sane-ip-check
|
||||
'';
|
||||
in
|
||||
{
|
||||
|
@@ -7,50 +7,6 @@ with lib;
|
||||
let
|
||||
cfg = config.sane.services.trust-dns;
|
||||
toml = pkgs.formats.toml { };
|
||||
recordFormatters = {
|
||||
# quote rules for zone files:
|
||||
# - any character may be encoded by `\DDD`, where `DDD` represents its ascii value in base 8.
|
||||
# - any non-digit `X` may be encoded by `\X`.
|
||||
# - stated in: <https://www.ietf.org/rfc/rfc1035.txt>: 5.1 Format
|
||||
# - visible in <trust-dns:crates/proto/src/serialize/txt/zone_lex.rs:escape_seq>
|
||||
# for us, we can just replace `\` => `\\ and `"` -> `\"`
|
||||
TXT = value: "\"" + (lib.escape [ "\\" "\"" ] value) + "\"";
|
||||
};
|
||||
fmtRecord = proto: rrtype: name: value:
|
||||
let
|
||||
formatter = recordFormatters."${rrtype}" or lib.id;
|
||||
in
|
||||
"${name}\t${proto}\t${rrtype}\t${formatter value}";
|
||||
fmtRecordList = proto: rrtype: name: values: concatStringsSep
|
||||
"\n"
|
||||
(map (fmtRecord proto rrtype name) values)
|
||||
;
|
||||
fmtRecordAttrs = proto: rrtype: rrAttrs:
|
||||
concatStringsSep
|
||||
"\n"
|
||||
(
|
||||
attrValues (
|
||||
mapAttrs
|
||||
(name: fmtRecordList proto rrtype name)
|
||||
rrAttrs
|
||||
)
|
||||
);
|
||||
fmtIncludes = paths: concatStringsSep
|
||||
"\n"
|
||||
(map (path: "$INCLUDE ${path}") paths);
|
||||
|
||||
genZone = zcfg: ''
|
||||
$TTL ${toString zcfg.TTL}
|
||||
${fmtRecordAttrs "IN" "SOA" zcfg.inet.SOA}
|
||||
${fmtRecordAttrs "IN" "A" zcfg.inet.A}
|
||||
${fmtRecordAttrs "IN" "CNAME" zcfg.inet.CNAME}
|
||||
${fmtRecordAttrs "IN" "MX" zcfg.inet.MX}
|
||||
${fmtRecordAttrs "IN" "NS" zcfg.inet.NS}
|
||||
${fmtRecordAttrs "IN" "SRV" zcfg.inet.SRV}
|
||||
${fmtRecordAttrs "IN" "TXT" zcfg.inet.TXT}
|
||||
${fmtIncludes zcfg.include}
|
||||
${zcfg.extraConfig}
|
||||
'';
|
||||
|
||||
configFile = toml.generate "trust-dns.toml" {
|
||||
listen_addrs_ipv4 = cfg.listenAddrsIPv4;
|
||||
@@ -58,20 +14,10 @@ let
|
||||
mapAttrs (zname: zcfg: rec {
|
||||
zone = if zcfg.name == null then zname else zcfg.name;
|
||||
zone_type = "Primary";
|
||||
file = if zcfg.file == null then
|
||||
pkgs.writeText "${zone}.zone" (genZone zcfg)
|
||||
else
|
||||
zcfg.file;
|
||||
file = zcfg.file;
|
||||
}) cfg.zones
|
||||
);
|
||||
};
|
||||
|
||||
# (listOf ty) type which also accepts single-assignment of `ty`.
|
||||
# it's used to allow the user to write:
|
||||
# CNAME."foo" = "bar";
|
||||
# as shorthand for
|
||||
# CNAME."foo" = [ "bar" ];
|
||||
listOrUnit = ty: types.coercedTo ty (elem: [ elem ]) (types.listOf ty);
|
||||
in
|
||||
{
|
||||
options = {
|
||||
@@ -80,6 +26,14 @@ in
|
||||
default = false;
|
||||
type = types.bool;
|
||||
};
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.trust-dns;
|
||||
description = ''
|
||||
trust-dns package to use.
|
||||
should provide bin/named, which will be invoked with --config x and --zonedir d and maybe -q.
|
||||
'';
|
||||
};
|
||||
listenAddrsIPv4 = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
@@ -89,101 +43,65 @@ in
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
zonedir = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = "/";
|
||||
description = ''
|
||||
where the `file` option in zones.* is relative to.
|
||||
'';
|
||||
};
|
||||
# reference <nixpkgs:nixos/modules/services/web-servers/nginx/vhost-options.nix>
|
||||
zones = mkOption {
|
||||
type = types.attrsOf (types.submodule {
|
||||
type = types.attrsOf (types.submodule ({ config, name, ... }: {
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
description = "zone name. defaults to the attribute name in zones";
|
||||
default = name;
|
||||
};
|
||||
text = mkOption {
|
||||
type = types.nullOr types.str; # TODO: types.lines?
|
||||
default = null;
|
||||
};
|
||||
TTL = mkOption {
|
||||
type = types.int;
|
||||
description = "default TTL";
|
||||
default = 3600;
|
||||
};
|
||||
include = mkOption {
|
||||
type = types.listOf types.str;
|
||||
description = "paths of other zone files to $INCLUDE into this one";
|
||||
default = [];
|
||||
};
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
description = "extra lines to append to the zone file";
|
||||
default = "";
|
||||
};
|
||||
inet = {
|
||||
SOA = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "Start of Authority record(s)";
|
||||
default = {};
|
||||
};
|
||||
A = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "IPv4 address record(s)";
|
||||
default = {};
|
||||
};
|
||||
CNAME = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "canonical name record(s)";
|
||||
default = {};
|
||||
};
|
||||
MX = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "mail exchanger record(s)";
|
||||
default = {};
|
||||
};
|
||||
NS = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "name server record(s)";
|
||||
default = {};
|
||||
};
|
||||
SRV = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "service record(s)";
|
||||
default = {};
|
||||
};
|
||||
TXT = mkOption {
|
||||
type = types.attrsOf (listOrUnit types.str);
|
||||
description = "text record(s)";
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
|
||||
file = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "instead of using the generated zone file, use the specified path";
|
||||
type = types.nullOr types.str; # TODO: types.path?
|
||||
description = ''
|
||||
runtime path to a .zone file.
|
||||
if omitted, will be generated from the `text` option.
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
config = {
|
||||
file = lib.mkIf (config.text != null) (pkgs.writeText "${config.name}.zone" config.text);
|
||||
};
|
||||
}));
|
||||
default = {};
|
||||
description = "Declarative zone config";
|
||||
};
|
||||
|
||||
generatedZones = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
description = "generated zone text for each zone";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
sane.services.trust-dns.generatedZones = mapAttrs (zone: zcfg: genZone zcfg) cfg.zones;
|
||||
networking.firewall.allowedTCPPorts = [ 53 ];
|
||||
networking.firewall.allowedUDPPorts = [ 53 ];
|
||||
sane.ports.ports."53" = {
|
||||
protocol = [ "udp" "tcp" ];
|
||||
visibleTo.lan = true;
|
||||
visibleTo.wan = true;
|
||||
description = "colin-dns-hosting";
|
||||
};
|
||||
|
||||
systemd.services.trust-dns = {
|
||||
description = "trust-dns DNS server";
|
||||
serviceConfig = {
|
||||
ExecStart =
|
||||
let
|
||||
flags = lib.optionalString cfg.quiet "-q";
|
||||
flags = lib.optional cfg.quiet "-q" ++
|
||||
lib.optionals (cfg.zonedir != null) [ "--zonedir" cfg.zonedir ];
|
||||
flagsStr = builtins.concatStringsSep " " flags;
|
||||
in ''
|
||||
${pkgs.trust-dns}/bin/named \
|
||||
${cfg.package}/bin/named \
|
||||
--config ${configFile} \
|
||||
--zonedir / ${flags}
|
||||
${flagsStr}
|
||||
'';
|
||||
Type = "simple";
|
||||
Restart = "on-failure";
|
||||
|
14
nixpatches/2023-05-31-toplevel-alsa.patch
Normal file
14
nixpatches/2023-05-31-toplevel-alsa.patch
Normal file
@@ -0,0 +1,14 @@
|
||||
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
|
||||
index d188ecdda55..69174ba7dc7 100644
|
||||
--- a/pkgs/top-level/all-packages.nix
|
||||
+++ b/pkgs/top-level/all-packages.nix
|
||||
@@ -26607,7 +26607,8 @@ with pkgs;
|
||||
|
||||
tinyalsa = callPackage ../os-specific/linux/tinyalsa { };
|
||||
|
||||
- inherit (callPackage ../os-specific/linux/alsa-project { })
|
||||
+ alsa-project = callPackage ../os-specific/linux/alsa-project { };
|
||||
+ inherit (alsa-project)
|
||||
alsa-firmware
|
||||
alsa-lib
|
||||
alsa-oss
|
@@ -129,14 +129,6 @@ in [
|
||||
hash = "sha256-+g3XhmBt/udhbBDiVyfWnfXKvZTvDurlvPblQ9HYp3s=";
|
||||
})
|
||||
|
||||
(fetchpatch' {
|
||||
# 2023/05/24: merged upstream
|
||||
# hare: unstable-2023-03-15 -> unstable-2023-04-23
|
||||
# + harec: unstable-2023-02-18 -> unstable-2023-04-25
|
||||
prUrl = "https://github.com/NixOS/nixpkgs/pull/233732";
|
||||
hash = "sha256-SGDKvsMiK3Pq57JEj/MamDBX5jBXwV/E5jclKO2NAUs=";
|
||||
})
|
||||
|
||||
# (fetchpatch' {
|
||||
# title = "hare-json: init at unstable-2023-01-31";
|
||||
# saneCommit = "260f9c6ac4e3564acbceb46aa4b65fbb652f8e23";
|
||||
@@ -158,6 +150,9 @@ in [
|
||||
hash = "sha256-9XKPNg7TewicfbMgiASpYysTs5aduIVP+4onz+noc/0=";
|
||||
})
|
||||
|
||||
# make alsa-project members overridable
|
||||
./2023-05-31-toplevel-alsa.patch
|
||||
|
||||
# for raspberry pi: allow building u-boot for rpi 4{,00}
|
||||
# TODO: remove after upstreamed: https://github.com/NixOS/nixpkgs/pull/176018
|
||||
# (it's a dupe of https://github.com/NixOS/nixpkgs/pull/112677 )
|
||||
|
@@ -378,8 +378,17 @@ in {
|
||||
# };
|
||||
# fixes: "src/meson.build:106:0: ERROR: Program 'glib-compile-resources' not found or not executable"
|
||||
file-roller = mvToNativeInputs [ final.glib ] super.file-roller;
|
||||
gnome-bluetooth = super.gnome-bluetooth.override {
|
||||
# fixes -msse2, -mfpmath=sse flags
|
||||
wrapGAppsHook4 = final.wrapGAppsHook;
|
||||
};
|
||||
# fixes: "meson.build:75:6: ERROR: Program 'gtk-update-icon-cache' not found or not executable"
|
||||
gnome-clocks = addNativeInputs [ final.gtk4 ] super.gnome-clocks;
|
||||
gnome-clocks = (
|
||||
addNativeInputs [ final.gtk4 ] super.gnome-clocks
|
||||
).override {
|
||||
# fixes -msse2, -mfpmath=sse flags
|
||||
wrapGAppsHook4 = final.wrapGAppsHook;
|
||||
};
|
||||
# fixes: "src/meson.build:3:0: ERROR: Program 'glib-compile-resources' not found or not executable"
|
||||
gnome-color-manager = mvToNativeInputs [ final.glib ] super.gnome-color-manager;
|
||||
# fixes "subprojects/gvc/meson.build:30:0: ERROR: Program 'glib-mkenums mkenums' not found or not executable"
|
||||
@@ -470,7 +479,7 @@ in {
|
||||
gnome-user-share = addNativeInputs [ final.glib ] super.gnome-user-share;
|
||||
# fixes: "FileNotFoundError: [Errno 2] No such file or directory: 'gtk4-update-icon-cache'"
|
||||
gnome-weather = addNativeInputs [ final.gtk4 ] super.gnome-weather;
|
||||
mutter = super.mutter.overrideAttrs (orig: {
|
||||
mutter = (super.mutter.overrideAttrs (orig: {
|
||||
nativeBuildInputs = orig.nativeBuildInputs ++ [
|
||||
final.glib # fixes "clutter/clutter/meson.build:281:0: ERROR: Program 'glib-mkenums mkenums' not found or not executable"
|
||||
final.buildPackages.gobject-introspection # allows to build without forcing `introspection=false` (which would break gnome-shell)
|
||||
@@ -481,7 +490,10 @@ in {
|
||||
];
|
||||
mesonFlags = lib.remove "-Ddocs=true" orig.mesonFlags;
|
||||
outputs = lib.remove "devdoc" orig.outputs;
|
||||
});
|
||||
})).override {
|
||||
# fixes -msse2, -mfpmath=sse flags
|
||||
wrapGAppsHook4 = final.wrapGAppsHook;
|
||||
};
|
||||
# nautilus = super.nautilus.override {
|
||||
# # fixes: "meson.build:123:0: ERROR: Dependency "libxml-2.0" not found, tried pkgconfig"
|
||||
# # new failure mode: "/nix/store/grqh2wygy9f9wp5bgvqn4im76v82zmcx-binutils-2.39/bin/ld: /nix/store/f7yr5z123d162p5457jh3wzkqm7x8yah-glib-2.74.3/lib/libglib-2.0.so: error adding symbols: file in wrong format"
|
||||
@@ -1134,7 +1146,14 @@ in {
|
||||
addNativeInputs [ final.wayland-scanner ] (
|
||||
mvToNativeInputs [ final.gettext final.glib ] prev.xdg-desktop-portal-gnome
|
||||
)
|
||||
);
|
||||
).override {
|
||||
# fixes -msse2, -mfpmath=sse flags
|
||||
wrapGAppsHook4 = final.wrapGAppsHook;
|
||||
};
|
||||
# "fatal error: urcu.h: No such file or directory"
|
||||
# xfsprogs wants to compile things for the build target (BUILD_CC)
|
||||
# xfsprogs = useEmulatedStdenv prev.xfsprogs;
|
||||
xfsprogs = addNativeInputs [ final.liburcu ] prev.xfsprogs;
|
||||
# webkitgtk = prev.webkitgtk.override { stdenv = final.ccacheStdenv; };
|
||||
# webp-pixbuf-loader = prev.webp-pixbuf-loader.override {
|
||||
# # fixes "Builder called die: Cannot wrap '/nix/store/kpp8qhzdjqgvw73llka5gpnsj0l4jlg8-gdk-pixbuf-aarch64-unknown-linux-gnu-2.42.10/bin/gdk-pixbuf-thumbnailer' because it is not an executable file"
|
||||
@@ -1150,6 +1169,10 @@ in {
|
||||
});
|
||||
# XXX: aarch64 webp-pixbuf-loader wanted by gdk-pixbuf-loaders.cache.drv, wanted by aarch64 gnome-control-center
|
||||
|
||||
# "extract-binary-wrapper-cmd: line 2: strings: command not found"
|
||||
# XXX: technically this belongs in pkgs/build-support/setup-hooks/make-binary-wrapper/default.nix ?
|
||||
wrapFirefox = browser: args: addNativeInputs [ final.bintools-unwrapped ] (prev.wrapFirefox browser args);
|
||||
|
||||
wvkbd = (
|
||||
# "wayland-scanner: no such program"
|
||||
mvToNativeInputs [ final.wayland-scanner ] prev.wvkbd
|
||||
|
24
pkgs/additional/alsa-ucm-conf-sane/default.nix
Normal file
24
pkgs/additional/alsa-ucm-conf-sane/default.nix
Normal file
@@ -0,0 +1,24 @@
|
||||
{ alsa-ucm-conf }:
|
||||
alsa-ucm-conf.overrideAttrs (upstream: {
|
||||
# upstream alsa ships with PinePhone audio configs, but they don't actually produce sound.
|
||||
# see: <https://github.com/alsa-project/alsa-ucm-conf/pull/134>
|
||||
# these audio files come from some revision of:
|
||||
# - <https://gitlab.manjaro.org/manjaro-arm/packages/community/phosh/alsa-ucm-pinephone>
|
||||
#
|
||||
# alternative to patching is to plumb `ALSA_CONFIG_UCM2 = "${./ucm2}"` environment variable into the relevant places
|
||||
# e.g. `systemd.services.pulseaudio.environment`.
|
||||
# that leaves more opportunity for gaps (i.e. missing a service),
|
||||
# on the other hand this method causes about 500 packages to be rebuilt (including qt5 and webkitgtk).
|
||||
#
|
||||
# note that with these files, the following audio device support:
|
||||
# - headphones work.
|
||||
# - "internal earpiece" works.
|
||||
# - "internal speaker" doesn't work.
|
||||
# - "analog output" doesn't work.
|
||||
postPatch = upstream.postPatch or "" + ''
|
||||
cp ${./ucm2/PinePhone}/* ucm2/Allwinner/A64/PinePhone/
|
||||
# fix the self-contained ucm files i source from to have correct path within the alsa-ucm-conf source tree
|
||||
sed -i 's:"HiFi.conf":"/Allwinner/A64/PinePhone/HiFi.conf":' ucm2/Allwinner/A64/PinePhone/PinePhone.conf
|
||||
sed -i 's:"VoiceCall.conf":"/Allwinner/A64/PinePhone/VoiceCall.conf":' ucm2/Allwinner/A64/PinePhone/PinePhone.conf
|
||||
'';
|
||||
})
|
@@ -31,8 +31,8 @@ in
|
||||
# repeat imports are deduplicated by url, even when offline.
|
||||
postBuild = ''
|
||||
makeWrapper $out/bin/gpodder $out/bin/gpodder-configured \
|
||||
--run "$out/bin/gpodder-remove-extra ~/.config/gpodderFeeds.opml" \
|
||||
--run "$out/bin/gpo import ~/.config/gpodderFeeds.opml" \
|
||||
--run "$out/bin/gpodder-remove-extra ~/.config/gpodderFeeds.opml || true" \
|
||||
--run "$out/bin/gpo import ~/.config/gpodderFeeds.opml || true" \
|
||||
|
||||
# fix up the .desktop file to invoke our wrapped application
|
||||
orig_desktop=$(readlink $out/share/applications/gpodder.desktop)
|
||||
|
@@ -0,0 +1,24 @@
|
||||
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
|
||||
index bcebe43..a15b0ef 100644
|
||||
--- a/src/CMakeLists.txt
|
||||
+++ b/src/CMakeLists.txt
|
||||
@@ -107,8 +107,8 @@ endif()
|
||||
set(RESOURCE_ROOT .)
|
||||
if(APPLE)
|
||||
set(RESOURCE_ROOT Resources)
|
||||
- add_resources(TARGET ${MAIN_TARGET} SOURCES ${CMAKE_CURRENT_BINARY_DIR}/../dist/ DEST ${RESOURCE_ROOT}/web-client/desktop)
|
||||
- add_resources(TARGET ${MAIN_TARGET} SOURCES ${CMAKE_SOURCE_DIR}/native/ DEST ${RESOURCE_ROOT}/web-client/extension)
|
||||
+ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../jellyfin-web/ DESTINATION ${RESOURCE_ROOT}/web-client/desktop)
|
||||
+ install(DIRECTORY ${CMAKE_SOURCE_DIR}/native/ DESTINATION ${RESOURCE_ROOT}/web-client/extension)
|
||||
endif()
|
||||
|
||||
if(NOT APPLE)
|
||||
@@ -121,7 +121,7 @@ if(NOT APPLE)
|
||||
install(FILES ${loc}/qtwebengine_devtools_resources.pak DESTINATION resources)
|
||||
endif()
|
||||
endforeach()
|
||||
- install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../dist/ DESTINATION ${INSTALL_RESOURCE_DIR}/web-client/desktop)
|
||||
+ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../jellyfin-web/ DESTINATION ${INSTALL_RESOURCE_DIR}/web-client/desktop)
|
||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/native/ DESTINATION ${INSTALL_RESOURCE_DIR}/web-client/extension)
|
||||
endif()
|
||||
|
@@ -0,0 +1,13 @@
|
||||
diff --git a/src/input/InputComponent.cpp b/src/input/InputComponent.cpp
|
||||
index 0f5f129..94596b6 100644
|
||||
--- a/src/input/InputComponent.cpp
|
||||
+++ b/src/input/InputComponent.cpp
|
||||
@@ -132,7 +132,7 @@ void InputComponent::handleAction(const QString& action)
|
||||
else
|
||||
{
|
||||
qDebug() << "Invoking slot" << qPrintable(recvSlot->m_slot.data());
|
||||
- QGenericArgument arg0 = QGenericArgument();
|
||||
+ QMetaMethodArgument arg0;
|
||||
|
||||
if (recvSlot->m_hasArguments)
|
||||
arg0 = Q_ARG(const QString&, hostArguments);
|
66
pkgs/additional/jellyfin-media-player-qt6/default.nix
Normal file
66
pkgs/additional/jellyfin-media-player-qt6/default.nix
Normal file
@@ -0,0 +1,66 @@
|
||||
{ lib
|
||||
, cmake
|
||||
, fetchFromGitHub
|
||||
, jellyfin-media-player
|
||||
, libGL
|
||||
, libX11
|
||||
, libXrandr
|
||||
, libvdpau
|
||||
, mpv
|
||||
, ninja
|
||||
, pkg-config
|
||||
, python3
|
||||
, qt6
|
||||
, SDL2
|
||||
, stdenv
|
||||
}:
|
||||
(jellyfin-media-player.overrideAttrs (upstream: {
|
||||
src = fetchFromGitHub {
|
||||
owner = "jellyfin";
|
||||
repo = "jellyfin-media-player";
|
||||
rev = "qt6";
|
||||
hash = "sha256-saR/P2daqjF0G8N7BX6Rtsb1dWGjdf5MPDx1lhoioEw=";
|
||||
};
|
||||
# nixos ships two patches:
|
||||
# - the first fixes "web paths" and has *mostly* been upstreamed (so skip and manually tweak a bit)
|
||||
# - the second disables auto-update notifications (keep)
|
||||
patches = (builtins.tail upstream.patches) ++ [
|
||||
./0001-fix-web-path.patch
|
||||
./0002-qt6-build-fixes.patch
|
||||
];
|
||||
buildInputs = [
|
||||
SDL2
|
||||
libGL
|
||||
libX11
|
||||
libXrandr
|
||||
libvdpau
|
||||
mpv
|
||||
qt6.qtbase
|
||||
qt6.qtwebchannel
|
||||
qt6.qtwebengine
|
||||
# qtx11extras
|
||||
] ++ lib.optionals stdenv.isLinux [
|
||||
qt6.qtwayland
|
||||
];
|
||||
|
||||
nativeBuildInputs = [
|
||||
cmake
|
||||
ninja
|
||||
pkg-config
|
||||
python3
|
||||
|
||||
# new packages which weren't needed before
|
||||
qt6.wrapQtAppsHook # replaces the implicit qt5 version
|
||||
qt6.qt5compat
|
||||
];
|
||||
|
||||
cmakeFlags = [
|
||||
"-DCMAKE_BUILD_TYPE=Release"
|
||||
"-DQTROOT=${qt6.qtbase}"
|
||||
"-GNinja"
|
||||
];
|
||||
|
||||
meta = upstream.meta // {
|
||||
platforms = upstream.meta.platforms ++ [ "aarch64-linux" ];
|
||||
};
|
||||
}))
|
@@ -1,109 +1,13 @@
|
||||
{ lib
|
||||
, pkgs
|
||||
, resholve
|
||||
, static-nix-shell
|
||||
, symlinkJoin
|
||||
}:
|
||||
|
||||
let
|
||||
shell-scripts = resholve.mkDerivation {
|
||||
# resholve documentation:
|
||||
# - nix: https://github.com/nixos/nixpkgs/blob/master/pkgs/development/misc/resholve/README.md
|
||||
# - generic: https://github.com/abathur/resholve
|
||||
pname = "sane-scripts";
|
||||
version = "0.1.0";
|
||||
|
||||
src = ./src;
|
||||
|
||||
solutions = {
|
||||
default = {
|
||||
# note: `scripts` refers to the store path here
|
||||
scripts = [ "bin/*" ];
|
||||
interpreter = "${pkgs.bash}/bin/bash";
|
||||
inputs = with pkgs; [
|
||||
# string is interpreted as relative path from @OUT@.
|
||||
# this lets our scripts reference eachother.
|
||||
# see: <https://github.com/abathur/resholve/issues/26>
|
||||
"bin"
|
||||
coreutils-full
|
||||
curl
|
||||
file
|
||||
findutils
|
||||
git
|
||||
gnugrep
|
||||
gnused
|
||||
gocryptfs
|
||||
ifuse
|
||||
inetutils
|
||||
inotify-tools
|
||||
iwd
|
||||
jq
|
||||
ncurses
|
||||
oath-toolkit
|
||||
openssh
|
||||
openssl
|
||||
rmlint
|
||||
rsync
|
||||
ssh-to-age
|
||||
sops
|
||||
sudo
|
||||
systemd
|
||||
util-linux
|
||||
which
|
||||
];
|
||||
keep = {
|
||||
# we write here: keep it
|
||||
"/tmp/rmlint.sh" = true;
|
||||
# intentionally escapes (into user code)
|
||||
"$external_cmd" = true;
|
||||
"$maybe_sudo" = true;
|
||||
};
|
||||
fake = {
|
||||
external = [
|
||||
# https://github.com/abathur/resholve/issues/29
|
||||
# "umount"
|
||||
# "/run/wrappers/bin/sudo"
|
||||
"sudo"
|
||||
];
|
||||
};
|
||||
fix = {
|
||||
# this replaces umount with the non-setuid-wrapper umount.
|
||||
# not sure if/where that lack of suid causes problems.
|
||||
umount = true;
|
||||
};
|
||||
prologue = "${./resholve-prologue}";
|
||||
|
||||
# list of programs which *can* or *cannot* exec their arguments
|
||||
execer = with pkgs; [
|
||||
"cannot:${git}/bin/git"
|
||||
"cannot:${gocryptfs}/bin/gocryptfs"
|
||||
"cannot:${ifuse}/bin/ifuse"
|
||||
"cannot:${iwd}/bin/iwctl"
|
||||
"cannot:${oath-toolkit}/bin/oathtool"
|
||||
"cannot:${openssh}/bin/ssh-keygen"
|
||||
"cannot:${rmlint}/bin/rmlint"
|
||||
"cannot:${rsync}/bin/rsync"
|
||||
"cannot:${sops}/bin/sops"
|
||||
"cannot:${ssh-to-age}/bin/ssh-to-age"
|
||||
"cannot:${systemd}/bin/systemctl"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
# remove python scripts (we package them further below)
|
||||
patchPhase = builtins.concatStringsSep
|
||||
"\n"
|
||||
(lib.mapAttrsToList (name: pkg: "rm ${pkg.pname}") py-scripts)
|
||||
;
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
cp -R * $out/bin/
|
||||
'';
|
||||
};
|
||||
|
||||
py-scripts = {
|
||||
nix-shell-scripts = {
|
||||
# anything added to this attrset gets symlink-joined into `sane-scripts`
|
||||
# and is made available through `sane-scripts.passthru`
|
||||
backup-ls = static-nix-shell.mkBash {
|
||||
pname = "sane-backup-ls";
|
||||
src = ./src;
|
||||
@@ -134,25 +38,178 @@ let
|
||||
src = ./src;
|
||||
pkgs = [ "transmission" ];
|
||||
};
|
||||
date-math = static-nix-shell.mkPython3Bin {
|
||||
pname = "sane-date-math";
|
||||
deadlines = static-nix-shell.mkBash {
|
||||
pname = "sane-deadlines";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" ];
|
||||
};
|
||||
dev-cargo-loop = static-nix-shell.mkBash {
|
||||
pname = "sane-dev-cargo-loop";
|
||||
src = ./src;
|
||||
pkgs = [ "inotify-tools" "ncurses" ];
|
||||
};
|
||||
find-dotfiles = static-nix-shell.mkBash {
|
||||
pname = "sane-find-dotfiles";
|
||||
src = ./src;
|
||||
pkgs = [ "findutils" ];
|
||||
};
|
||||
git-init = static-nix-shell.mkBash {
|
||||
pname = "sane-git-init";
|
||||
src = ./src;
|
||||
pkgs = [ "git" ];
|
||||
};
|
||||
ip-check = static-nix-shell.mkBash {
|
||||
pname = "sane-ip-check";
|
||||
src = ./src;
|
||||
pkgs = [ "curl" "gnugrep" ];
|
||||
};
|
||||
ip-check-upnp = static-nix-shell.mkPython3Bin {
|
||||
pname = "sane-ip-check-upnp";
|
||||
src = ./src;
|
||||
pkgs = [ "miniupnpc" ];
|
||||
postInstall = ''
|
||||
mkdir -p $out/bin/lib
|
||||
cp -R lib/* $out/bin/lib/
|
||||
'';
|
||||
};
|
||||
ip-port-forward = static-nix-shell.mkPython3Bin {
|
||||
pname = "sane-ip-port-forward";
|
||||
src = ./src;
|
||||
pkgs = [ "inetutils" "miniupnpc" ];
|
||||
postInstall = ''
|
||||
mkdir -p $out/bin/lib
|
||||
cp -R lib/* $out/bin/lib/
|
||||
'';
|
||||
};
|
||||
ip-reconnect = static-nix-shell.mkPython3Bin {
|
||||
pname = "sane-ip-reconnect";
|
||||
src = ./src;
|
||||
};
|
||||
mount-servo = static-nix-shell.mkBash {
|
||||
pname = "sane-mount-servo";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" ];
|
||||
};
|
||||
mount-servo-root = static-nix-shell.mkBash {
|
||||
pname = "sane-mount-servo-root";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" ];
|
||||
};
|
||||
private-change-passwd = static-nix-shell.mkBash {
|
||||
pname = "sane-private-change-passwd";
|
||||
src = ./src;
|
||||
pkgs = [ "gocryptfs" "rsync" "sane-scripts.private-unlock" ];
|
||||
};
|
||||
private-do = static-nix-shell.mkBash {
|
||||
pname = "sane-private-do";
|
||||
src = ./src;
|
||||
pkgs = [ "sane-scripts.private-unlock" ];
|
||||
};
|
||||
private-init = static-nix-shell.mkBash {
|
||||
pname = "sane-private-init";
|
||||
src = ./src;
|
||||
pkgs = [ "gocryptfs" ];
|
||||
};
|
||||
private-lock = static-nix-shell.mkBash {
|
||||
pname = "sane-private-lock";
|
||||
src = ./src;
|
||||
};
|
||||
private-unlock = static-nix-shell.mkBash {
|
||||
pname = "sane-private-unlock";
|
||||
src = ./src;
|
||||
pkgs = [ "gocryptfs" ];
|
||||
};
|
||||
rcp = static-nix-shell.mkBash {
|
||||
pname = "sane-rcp";
|
||||
src = ./src;
|
||||
pkgs = [ "rsync" ];
|
||||
};
|
||||
reboot = static-nix-shell.mkBash {
|
||||
pname = "sane-reboot";
|
||||
src = ./src;
|
||||
pkgs = [ "systemd" ];
|
||||
};
|
||||
reclaim-boot-space = static-nix-shell.mkPython3Bin {
|
||||
pname = "sane-reclaim-boot-space";
|
||||
src = ./src;
|
||||
};
|
||||
ip-reconnect = static-nix-shell.mkPython3Bin {
|
||||
pname = "sane-ip-reconnect";
|
||||
reclaim-disk-space = static-nix-shell.mkBash {
|
||||
pname = "sane-reclaim-disk-space";
|
||||
src = ./src;
|
||||
pkgs = [ "nix" "rmlint" "util-linux" ];
|
||||
};
|
||||
secrets-dump = static-nix-shell.mkBash {
|
||||
pname = "sane-secrets-dump";
|
||||
src = ./src;
|
||||
pkgs = [ "gnugrep" "sops" "oath-toolkit" ];
|
||||
};
|
||||
secrets-unlock = static-nix-shell.mkBash {
|
||||
pname = "sane-secrets-unlock";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" "openssh" "ssh-to-age" ];
|
||||
};
|
||||
secrets-update-keys = static-nix-shell.mkBash {
|
||||
pname = "sane-secrets-update-keys";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" "findutils" "sops" ];
|
||||
};
|
||||
shutdown = static-nix-shell.mkBash {
|
||||
pname = "sane-shutdown";
|
||||
src = ./src;
|
||||
pkgs = [ "inetutils" "systemd" ];
|
||||
};
|
||||
ssl-dump = static-nix-shell.mkBash {
|
||||
pname = "sane-ssl-dump";
|
||||
src = ./src;
|
||||
pkgs = [ "openssl" ];
|
||||
};
|
||||
stop-all-servo = static-nix-shell.mkBash {
|
||||
pname = "sane-stop-all-servo";
|
||||
src = ./src;
|
||||
pkgs = [ "systemd" ];
|
||||
};
|
||||
sudo-redirect = static-nix-shell.mkBash {
|
||||
pname = "sane-sudo-redirect";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" ];
|
||||
};
|
||||
sync-from-iphone = static-nix-shell.mkZsh {
|
||||
pname = "sane-sync-from-iphone";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" "ifuse" "rsync" ];
|
||||
};
|
||||
sync-from-servo = static-nix-shell.mkBash {
|
||||
pname = "sane-sync-from-servo";
|
||||
src = ./src;
|
||||
pkgs = [ "rsync" "sane-scripts.mount-servo" ];
|
||||
};
|
||||
vpn-down = static-nix-shell.mkBash {
|
||||
pname = "sane-vpn-down";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" "gnugrep" "gnused" "sane-scripts.ip-check" "systemd" ];
|
||||
};
|
||||
vpn-up = static-nix-shell.mkBash {
|
||||
pname = "sane-vpn-up";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" "gnugrep" "gnused" "sane-scripts.ip-check" "systemd" ];
|
||||
};
|
||||
which = static-nix-shell.mkBash {
|
||||
pname = "sane-which";
|
||||
src = ./src;
|
||||
pkgs = [ "coreutils-full" "file" ];
|
||||
};
|
||||
wipe-browser = static-nix-shell.mkBash {
|
||||
pname = "sane-wipe-browser";
|
||||
src = ./src;
|
||||
};
|
||||
};
|
||||
in
|
||||
symlinkJoin {
|
||||
name = "sane-scripts";
|
||||
paths = [ shell-scripts ] ++ lib.attrValues py-scripts;
|
||||
paths = lib.attrValues nix-shell-scripts;
|
||||
passthru = nix-shell-scripts;
|
||||
meta = {
|
||||
description = "collection of scripts associated with uninsane systems";
|
||||
description = "collection of scripts associated with sane systems";
|
||||
homepage = "https://git.uninsane.org";
|
||||
platforms = lib.platforms.all;
|
||||
};
|
||||
|
116
pkgs/additional/sane-scripts/src/lib/sane_ssdp.py
Normal file
116
pkgs/additional/sane-scripts/src/lib/sane_ssdp.py
Normal file
@@ -0,0 +1,116 @@
|
||||
# based on this minimal SSDP client: <https://gist.github.com/schlamar/2428250>
|
||||
|
||||
import logging
|
||||
import socket
|
||||
import struct
|
||||
import subprocess
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
MCAST_GRP = "239.255.255.250"
|
||||
|
||||
class SsdpResponse:
|
||||
def __init__(self, headers: "Dict[str, str]"):
|
||||
self.headers = headers
|
||||
|
||||
@staticmethod
|
||||
def parse(msg: str) -> "Self":
|
||||
headers = {}
|
||||
for line in [m.strip() for m in msg.split("\r\n") if m.strip()]:
|
||||
if ":" not in line: continue
|
||||
sep_idx = line.find(":")
|
||||
header, content = line[:sep_idx].strip(), line[sep_idx+1:].strip()
|
||||
headers[header.upper()] = content
|
||||
if headers:
|
||||
return SsdpResponse(headers)
|
||||
|
||||
def is_rootdevice(self) -> bool:
|
||||
return self.headers.get("NT", "").lower() == "upnp:rootdevice"
|
||||
|
||||
def location(self) -> str:
|
||||
return self.headers.get("LOCATION")
|
||||
|
||||
|
||||
def get_root_devices():
|
||||
listener = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
|
||||
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
listener.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
|
||||
|
||||
listener.bind(("", 1900))
|
||||
logger.info("bound")
|
||||
|
||||
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
|
||||
listener.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
|
||||
|
||||
root_descs = set()
|
||||
while True:
|
||||
packet, (host, src_port) = listener.recvfrom(2048)
|
||||
logger.info(f"message from {host}")
|
||||
# if host.endswith(".1"): # router
|
||||
try:
|
||||
msg = packet.decode("utf-8")
|
||||
except:
|
||||
logger.debug("failed to decode packet to string")
|
||||
else:
|
||||
logger.debug(msg)
|
||||
resp = SsdpResponse.parse(msg)
|
||||
if resp and resp.is_rootdevice():
|
||||
root_desc = resp.location()
|
||||
if root_desc and root_desc not in root_descs:
|
||||
root_descs.add(root_desc)
|
||||
logger.info(f"root desc: {root_desc}")
|
||||
yield root_desc
|
||||
|
||||
def get_ips_from_location(location: str):
|
||||
"""
|
||||
location = URI from the Location header, e.g. http://10.78.79.1:2189/rootDesc.xml
|
||||
returns (lan, wan)
|
||||
"""
|
||||
|
||||
# get connection [s]tatus
|
||||
cmd = ["upnpc", "-u", location, "-s"]
|
||||
res = subprocess.run(cmd, capture_output=True)
|
||||
if res.returncode != 0:
|
||||
logger.info(f"get_wan_from_location failed: {cmd!r}\n{res.stderr}")
|
||||
return None
|
||||
|
||||
status = res.stdout.decode("utf-8")
|
||||
logger.debug(f"got status: {status}")
|
||||
|
||||
lan = None
|
||||
wan = None
|
||||
for line in [l.strip() for l in status.split("\n")]:
|
||||
wan_sentinel = "ExternalIPAddress ="
|
||||
lan_sentinel = "Local LAN ip address :"
|
||||
if line.startswith(wan_sentinel):
|
||||
wan = line[len(wan_sentinel):].strip()
|
||||
logger.info(f"got WAN = {wan} from {location}")
|
||||
if line.startswith(lan_sentinel):
|
||||
lan = line[len(lan_sentinel):].strip()
|
||||
logger.info(f"got LAN = {lan} from {location}")
|
||||
return lan, wan
|
||||
|
||||
def get_any_wan():
|
||||
""" return (location, LAN IP, WAN IP) for the first device seen which has a WAN IP """
|
||||
for location in get_root_devices():
|
||||
lan, wan = get_ips_from_location(location)
|
||||
if lan and wan:
|
||||
return location, lan, wan
|
||||
|
||||
def forward_port(root_device: str, proto: str, port: int, lan_ip: str, reason: str = "", duration: int = 86400):
|
||||
args = [
|
||||
"upnpc",
|
||||
"-u", root_device,
|
||||
"-e", reason,
|
||||
"-a", lan_ip,
|
||||
str(port),
|
||||
str(port),
|
||||
proto,
|
||||
str(duration),
|
||||
]
|
||||
|
||||
logger.debug(f"running: {args!r}")
|
||||
stdout = subprocess.check_output(args).decode("utf-8")
|
||||
|
||||
logger.info(stdout)
|
@@ -1,348 +0,0 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])"
|
||||
|
||||
# i just went overboard playing around with parsers, is all.
|
||||
# use this like `./sane-date-math 'today - 5d'`
|
||||
# of course, it handles parentheses and operator precedence/associativity, so you can do sillier things like
|
||||
# `./sane-date-math ' today - (1+3 *4 - ((0)) ) *7d '`
|
||||
|
||||
|
||||
import abc
|
||||
from datetime import datetime, timedelta
|
||||
import sys
|
||||
|
||||
class Token:
|
||||
def __init__(self, c: str):
|
||||
self.c = c
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.c!r}"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.c
|
||||
|
||||
def __eq__(self, other: 'Token') -> bool:
|
||||
return self.c == other.c
|
||||
|
||||
PLUS = Token('+')
|
||||
MINUS = Token('-')
|
||||
ASTERISK = Token('*')
|
||||
SPACE = Token(' ')
|
||||
OPEN_PAREN = Token('(')
|
||||
CLOSE_PAREN = Token(')')
|
||||
UNDERSCORE = Token('_')
|
||||
DIGITS = [Token(c) for c in '0123456789']
|
||||
ALPHA_LOWER = [Token(c) for c in 'abcdefghijklmnopqrstuvwxyz']
|
||||
ALPHA_UPPER = [Token(t.c.upper()) for t in ALPHA_LOWER]
|
||||
ALPHA = ALPHA_LOWER + ALPHA_UPPER
|
||||
ALPHA_UNDER = ALPHA + [UNDERSCORE]
|
||||
ALPHA_NUM_UNDER = ALPHA_UNDER + DIGITS
|
||||
|
||||
class ParserContext:
|
||||
def feed(self, token: Token) -> 'ParserContext':
|
||||
return None # can't ingest the token
|
||||
|
||||
def upgrade(self) -> 'ParserContext':
|
||||
return None # no upgrade path
|
||||
|
||||
class Parser:
|
||||
"""
|
||||
LR parser.
|
||||
keeps exactly one root item, and for each input token
|
||||
feeds it to the root, possibly "upgrading" the root N times
|
||||
before it's able to be fed.
|
||||
"""
|
||||
def __init__(self, root: ParserContext):
|
||||
self.root = root
|
||||
|
||||
def feed(self, token: Token) -> bool:
|
||||
new_root = self.root.feed(token)
|
||||
if new_root is not None:
|
||||
self.root = new_root
|
||||
return True
|
||||
else:
|
||||
# root can't directly accept this item.
|
||||
# "upgrade" it and try again.
|
||||
new_root = self.root.upgrade()
|
||||
if new_root is None: return False
|
||||
self.root = new_root
|
||||
return self.feed(token)
|
||||
|
||||
def complete(self) -> ParserContext:
|
||||
# upgrade the root as far as possible before returning
|
||||
root = None
|
||||
new_root = self.root
|
||||
while new_root is not None:
|
||||
root = new_root
|
||||
new_root = root.upgrade()
|
||||
|
||||
return root
|
||||
|
||||
class ReprParserContext(ParserContext):
|
||||
""" helper that gives a good default repr to most contexts """
|
||||
def __init__(self, items: list = None):
|
||||
self.items = items if items is not None else []
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'{self.__class__.__name__}({self.items!r})'
|
||||
|
||||
|
||||
class BaseContext(ReprParserContext):
|
||||
""" empty context; initial state of the parser """
|
||||
def feed(self, token: Token) -> ParserContext:
|
||||
if token == SPACE:
|
||||
return self
|
||||
if token == OPEN_PAREN:
|
||||
return ParenContext(BaseContext())
|
||||
if token in DIGITS:
|
||||
return IntegerContext([token])
|
||||
if token in ALPHA_UNDER:
|
||||
return IdentifierContext([token])
|
||||
|
||||
class IdentifierContext(ReprParserContext):
|
||||
""" context is an identifier like `today` """
|
||||
def __init__(self, tokens: list):
|
||||
super().__init__(tokens)
|
||||
self.tokens = tokens
|
||||
|
||||
def feed(self, token: Token) -> ParserContext:
|
||||
if token in ALPHA_NUM_UNDER:
|
||||
return IdentifierContext(self.tokens + [token])
|
||||
|
||||
def upgrade(self) -> ParserContext:
|
||||
return StrongValueContext(self)
|
||||
|
||||
class IntegerContext(ReprParserContext):
|
||||
""" context is an integer like `45` """
|
||||
def __init__(self, tokens: list):
|
||||
super().__init__(tokens)
|
||||
self.tokens = tokens
|
||||
|
||||
def feed(self, token: Token) -> ParserContext:
|
||||
if token in DIGITS:
|
||||
return IntegerContext(self.tokens + [token])
|
||||
if token == Token('d'):
|
||||
return DurationContext(self)
|
||||
|
||||
def upgrade(self) -> ParserContext:
|
||||
# can't continue the integer; it becomes a value
|
||||
return StrongValueContext(self)
|
||||
|
||||
class DurationContext(ReprParserContext):
|
||||
""" context is a duration like `14d` """
|
||||
def __init__(self, value: IntegerContext):
|
||||
super().__init__([value])
|
||||
self.value = value
|
||||
|
||||
def upgrade(self) -> ParserContext:
|
||||
return StrongValueContext(self)
|
||||
|
||||
class BaseValueContext(ReprParserContext):
|
||||
""" abstract base for types that can be used in compound expressions """
|
||||
def __init__(self, value: ParserContext):
|
||||
super().__init__([value])
|
||||
self.value = value
|
||||
|
||||
def feed(self, token: Token) -> ParserContext:
|
||||
if token == SPACE:
|
||||
return self
|
||||
|
||||
class StrongValueContext(BaseValueContext):
|
||||
"""
|
||||
in the context of operators, a strong value is something which prefers
|
||||
to not be grabbed by a lhs value.
|
||||
|
||||
so for example, strong values have the opportunity to initiate a multiply operation before the lhs closes an addition operation that this strong value is a part of
|
||||
"""
|
||||
def feed(self, token: Token) -> ParserContext:
|
||||
if token == ASTERISK:
|
||||
return BinaryOpContext(self, token, BaseContext())
|
||||
return super().feed(token)
|
||||
|
||||
def upgrade(self) -> ParserContext:
|
||||
return WeakValueContext(self.value)
|
||||
|
||||
class WeakValueContext(BaseValueContext):
|
||||
def feed(self, token: Token) -> ParserContext:
|
||||
if token == PLUS:
|
||||
return BinaryOpContext(self, token, BaseContext())
|
||||
if token == MINUS:
|
||||
return BinaryOpContext(self, token, BaseContext())
|
||||
|
||||
return super().feed(token)
|
||||
|
||||
class BinaryOpContext(ReprParserContext):
|
||||
""" context for a binary operation. the LHS and operator are parsed, but the rhs may not yet contain a value """
|
||||
def __init__(self, lhs: BaseValueContext, oper: Token, rhs: ParserContext):
|
||||
super().__init__([lhs, oper, rhs])
|
||||
self.lhs = lhs
|
||||
self.oper = oper
|
||||
self.rhs = rhs
|
||||
|
||||
@property
|
||||
def precedence_class(self) -> type:
|
||||
if self.oper in [PLUS, MINUS]:
|
||||
return WeakValueContext
|
||||
if self.oper == ASTERISK:
|
||||
return StrongValueContext
|
||||
|
||||
def feed(self, token: Token) -> ParserContext:
|
||||
new_rhs = self.rhs.feed(token)
|
||||
if new_rhs is not None:
|
||||
return BinaryOpContext(self.lhs, self.oper, new_rhs)
|
||||
|
||||
def upgrade(self) -> ParserContext:
|
||||
new_rhs = self.rhs.upgrade()
|
||||
if new_rhs is None: return None
|
||||
|
||||
# upgrade self once the rhs has reach the required precedence compatible with this operator
|
||||
new_self = BinaryOpContext(self.lhs, self.oper, new_rhs)
|
||||
if isinstance(new_rhs, self.precedence_class):
|
||||
return StrongValueContext(self) # close the operation
|
||||
|
||||
return new_self
|
||||
|
||||
class ParenContext(ReprParserContext):
|
||||
""" context for a value contained within parentheses """
|
||||
def __init__(self, inner: ParserContext):
|
||||
super().__init__([inner])
|
||||
self.inner = inner
|
||||
|
||||
def feed(self, token: Token) -> ParserContext:
|
||||
new_inner = self.inner.feed(token)
|
||||
if new_inner is not None:
|
||||
return ParenContext(new_inner)
|
||||
|
||||
if token == CLOSE_PAREN and isinstance(self.inner, WeakValueContext):
|
||||
return StrongValueContext(self)
|
||||
|
||||
def upgrade(self) -> ParserContext:
|
||||
new_inner = self.inner.upgrade()
|
||||
if new_inner is not None:
|
||||
return ParenContext(new_inner)
|
||||
|
||||
|
||||
## AstItems are produced from a ParserContext input
|
||||
## ParserContext parse outputs are translated into `AstItem`s before evaluation
|
||||
## so that we can operate on a higher-level tree that directly encodes native values like integers
|
||||
|
||||
class AstItem(metaclass=abc.ABCMeta):
|
||||
@abc.abstractmethod
|
||||
def eval(self, context: dict):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def decode_item(p: ParserContext) -> 'AstItem':
|
||||
if isinstance(p, IntegerContext):
|
||||
return Literal(AstItem.decode_integer(p))
|
||||
if isinstance(p, DurationContext):
|
||||
return Literal(timedelta(AstItem.decode_integer(p.value)))
|
||||
if isinstance(p, IdentifierContext):
|
||||
return Variable(AstItem.decode_identifier(p))
|
||||
if isinstance(p, BaseValueContext):
|
||||
return AstItem.decode_item(p.value)
|
||||
if isinstance(p, BinaryOpContext):
|
||||
return AstItem.decode_bin_op(
|
||||
p.oper.c,
|
||||
AstItem.decode_item(p.lhs),
|
||||
AstItem.decode_item(p.rhs)
|
||||
)
|
||||
if isinstance(p, ParenContext):
|
||||
return AstItem.decode_item(p.inner)
|
||||
|
||||
@staticmethod
|
||||
def decode_integer(p: IntegerContext) -> int:
|
||||
return int(''.join(t.c for t in p.tokens))
|
||||
|
||||
@staticmethod
|
||||
def decode_identifier(p: IdentifierContext) -> str:
|
||||
return ''.join(t.c for t in p.tokens)
|
||||
|
||||
@staticmethod
|
||||
def decode_bin_op(ty: str, lhs: 'AstItem', rhs: 'AstItem') -> 'BinaryOp':
|
||||
if ty == '+':
|
||||
return AddOp(lhs, rhs)
|
||||
if ty == '-':
|
||||
return SubOp(lhs, rhs)
|
||||
if ty == '*':
|
||||
return MulOp(lhs, rhs)
|
||||
|
||||
class Literal(AstItem):
|
||||
def __init__(self, v):
|
||||
self.v = v
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.v)
|
||||
|
||||
def eval(self, context: dict):
|
||||
return self.v
|
||||
|
||||
class Variable(AstItem):
|
||||
def __init__(self, name: str):
|
||||
self.name = name
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def eval(self, context: dict):
|
||||
return context[self.name]
|
||||
|
||||
class BinaryOp(AstItem):
|
||||
def __init__(self, lhs, rhs):
|
||||
self.lhs = lhs
|
||||
self.rhs = rhs
|
||||
|
||||
class AddOp(BinaryOp):
|
||||
def __str__(self):
|
||||
return f"({self.lhs} + {self.rhs})"
|
||||
|
||||
def eval(self, context: dict):
|
||||
return self.lhs.eval(context) + self.rhs.eval(context)
|
||||
|
||||
class SubOp(BinaryOp):
|
||||
def __str__(self):
|
||||
return f"({self.lhs} - {self.rhs})"
|
||||
|
||||
def eval(self, context: dict):
|
||||
return self.lhs.eval(context) - self.rhs.eval(context)
|
||||
|
||||
class MulOp(BinaryOp):
|
||||
def __str__(self):
|
||||
return f"({self.lhs} * {self.rhs})"
|
||||
|
||||
def eval(self, context: dict):
|
||||
return self.lhs.eval(context) * self.rhs.eval(context)
|
||||
|
||||
|
||||
## toplevel routine. tokenize -> parse -> decode to AST -> evaluate
|
||||
|
||||
def tokenize(stream: str) -> list:
|
||||
return [Token(char) for char in stream]
|
||||
|
||||
def parse(tokens: list) -> ParserContext:
|
||||
parser = Parser(BaseContext())
|
||||
for i, t in enumerate(tokens):
|
||||
result = parser.feed(t)
|
||||
# print(f"i={i}; t={t}; state: {ctx!r}")
|
||||
assert result, f"unexpected token '{t}' at {i}; state: {parser.complete()!r}"
|
||||
|
||||
return parser.complete()
|
||||
|
||||
|
||||
def evaluate(expr: str) -> object:
|
||||
tok = tokenize(expr)
|
||||
parse_tree = parse(tok)
|
||||
print(parse_tree)
|
||||
ast = AstItem.decode_item(parse_tree)
|
||||
print(ast)
|
||||
|
||||
env = dict(
|
||||
today=datetime.now()
|
||||
)
|
||||
return ast.eval(env)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
expr = " ".join(sys.argv[1:])
|
||||
print(evaluate(expr))
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p coreutils-full
|
||||
|
||||
# processes a tab-separated "deadlines" file and alerts for any upcoming events.
|
||||
#
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p inotify-tools -p ncurses
|
||||
# watches PWD for any changes underneath it and re-runs `cargo build --a>
|
||||
# optionally, provide your own build command as the first argument
|
||||
|
||||
|
@@ -1,9 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p findutils
|
||||
# find where a package stores its dotfiles/dotdir
|
||||
# e.g. `sane-find-dotfiles foo` might print `/home/colin/.foo`, `/home/colin/.local/share/foo`, etc.
|
||||
|
||||
find ~/ -maxdepth 1 -iname "*$1*" -print
|
||||
find ~/.local/share/*/ -maxdepth 1 -iname "*$1*" -print
|
||||
find ~/.local/state/*/ -maxdepth 1 -iname "*$1*" -print
|
||||
find ~/.config/*/ -maxdepth 1 -iname "*$1*" -print
|
||||
find ~/.cache/*/ -maxdepth 1 -iname "*$1*" -print
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p git
|
||||
|
||||
set -x
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p curl -p gnugrep
|
||||
ip=$(curl --silent https://ipinfo.io/ip)
|
||||
echo "$ip" | grep -P " *^\d+\.\d+\.\d+\.\d+ *$"
|
||||
exit $?
|
||||
|
@@ -1,18 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# query the WAN IP address OF MY ROUTER
|
||||
# requires creds
|
||||
passwd=$(sudo cat /run/secrets/router_passwd)
|
||||
cookie=$(mktemp)
|
||||
curlflags="curl --silent --insecure --cookie-jar $cookie --connect-timeout 5"
|
||||
|
||||
# authenticate
|
||||
curl $curlflags \
|
||||
--data "username=admin&password=$passwd" \
|
||||
https://192.168.0.1
|
||||
# query the WAN IP
|
||||
ip=$(curl $curlflags \
|
||||
-H "X-Requested-With: XMLHttpRequest" \
|
||||
"https://192.168.0.1/cgi/cgi_action?Action=GetConnectionStatus" \
|
||||
| jq -r .wan_status.ipaddr)
|
||||
echo "$ip" | grep -P " *^\d+\.\d+\.\d+\.\d+ *$"
|
||||
exit $?
|
27
pkgs/additional/sane-scripts/src/sane-ip-check-upnp
Executable file
27
pkgs/additional/sane-scripts/src/sane-ip-check-upnp
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" -p miniupnpc
|
||||
|
||||
# best to run this with an external timeout. e.g.
|
||||
# - `timeout 60 sane-ip-check-upnp`
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
d = os.path.dirname(__file__)
|
||||
sys.path.insert(0, d)
|
||||
|
||||
from lib.sane_ssdp import get_any_wan
|
||||
|
||||
if __name__ == '__main__':
|
||||
logging.basicConfig()
|
||||
|
||||
for arg in sys.argv[1:]:
|
||||
if arg == "-v":
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
elif arg == "-vv":
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
else:
|
||||
raise RuntimeError(f"invalid CLI argument {arg!r}")
|
||||
_rootdev, _lan_ip, wan_ip = get_any_wan()
|
||||
print(wan_ip)
|
106
pkgs/additional/sane-scripts/src/sane-ip-port-forward
Executable file
106
pkgs/additional/sane-scripts/src/sane-ip-port-forward
Executable file
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" -p inetutils -p miniupnpc
|
||||
|
||||
'''
|
||||
USAGE: sane-ip-port-forward [options] [proto:port[:desc]]*
|
||||
|
||||
options:
|
||||
-v: verbose (show info messages)
|
||||
-vv: more verbose (show debug messages)
|
||||
-h: show this help messages
|
||||
-d <int>: lease for the given duration in seconds (default: {DEFAULT_LEASE_SEC})
|
||||
|
||||
proto:port[:desc]:
|
||||
proto is `udp` or `tcp` (case insensitive)
|
||||
port is any integer 1-65535 inclusive
|
||||
desc is some public description of the port forward (for humans trying to understand a network config)
|
||||
'''
|
||||
|
||||
DEFAULT_LEASE_SEC = 86400
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, ".")
|
||||
|
||||
from lib.sane_ssdp import get_any_wan, forward_port
|
||||
|
||||
class BadCliArgs(Exception):
|
||||
def __init__(self, msg: str = None):
|
||||
helpstr = __doc__.format(DEFAULT_LEASE_SEC=DEFAULT_LEASE_SEC).strip()
|
||||
if msg:
|
||||
super().__init__(f"{msg}\n\n{helpstr}")
|
||||
else:
|
||||
super().__init__(helpstr)
|
||||
|
||||
def try_parse_port(s: str):
|
||||
"""
|
||||
`udp:53` -> ["udp", 53, ""]
|
||||
`tcp:65535:my description` -> ["tcp", 65535, "my description"]
|
||||
"""
|
||||
s = s.strip()
|
||||
|
||||
def split_at_colon(s_: str):
|
||||
next_colon = s_.find(":")
|
||||
return s_[:next_colon], s_[next_colon+1:]
|
||||
|
||||
if ":" not in s: return # invalid format
|
||||
proto, rest = split_at_colon(s)
|
||||
|
||||
if ":" in rest:
|
||||
portstr, desc = split_at_colon(rest)
|
||||
else:
|
||||
portstr = rest
|
||||
desc = ""
|
||||
|
||||
try:
|
||||
proto, port = proto.lower(), int(portstr)
|
||||
assert proto in ["tcp", "udp"]
|
||||
assert 0 < port < 65536
|
||||
return proto, port, desc
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def parse_args(argv: "List[str]") -> "List[('udp'|'tcp', port: int, description: str)]":
|
||||
"""
|
||||
returns (list of forwards, lease duration)
|
||||
where:
|
||||
- list of forwards is [('udp'|'tcp', port: int, description: str)]
|
||||
- lease duration is seconds: int
|
||||
"""
|
||||
forwards = []
|
||||
duration = DEFAULT_LEASE_SEC
|
||||
unparsed = sys.argv[1:][::-1]
|
||||
while unparsed:
|
||||
arg = unparsed.pop()
|
||||
if arg == "-h":
|
||||
raise BadCliArgs()
|
||||
if arg == "-v":
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
elif arg == "-vv":
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
elif arg == "-d" and unparsed:
|
||||
d = unparsed.pop()
|
||||
try:
|
||||
duration = int(d)
|
||||
except Exception:
|
||||
raise BadCliArgs(f"invalid CLI argument: -d {d!r}")
|
||||
elif try_parse_port(arg):
|
||||
forwards.append(try_parse_port(arg))
|
||||
else:
|
||||
raise BadCliArgs(f"invalid CLI argument: {arg!r}")
|
||||
return forwards, duration
|
||||
|
||||
if __name__ == '__main__':
|
||||
logging.basicConfig()
|
||||
|
||||
try:
|
||||
forwards, duration = parse_args(sys.argv)
|
||||
except BadCliArgs as e:
|
||||
print(e)
|
||||
sys.exit(1)
|
||||
|
||||
root_device, lan, _wan = get_any_wan()
|
||||
for (proto, port, reason) in forwards:
|
||||
forward_port(root_device, proto, port, lan_ip=lan, reason=reason, duration=duration)
|
@@ -1,4 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p coreutils-full
|
||||
#^ requires `sudo` and `mount` to be installed system-wide (suid)
|
||||
|
||||
set -ex
|
||||
|
||||
# if lan not mounted, then try to mount it
|
||||
|
@@ -1,4 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p coreutils-full
|
||||
#^ requires `sudo` and `mount` to be installed system-wide (suid)
|
||||
#
|
||||
set -ex
|
||||
|
||||
# if lan not mounted, then try to mount it
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p gocryptfs -p rsync -p sane-scripts.private-unlock
|
||||
|
||||
set -ex
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p sane-scripts.private-unlock
|
||||
|
||||
# unlock the ~/private store, run some command, and then re-lock the store
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p gocryptfs
|
||||
|
||||
set -ex
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash
|
||||
|
||||
sudo umount /home/colin/private
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p gocryptfs
|
||||
|
||||
set -ex
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p rsync
|
||||
# rsync, with sane defaults
|
||||
# + verbosity
|
||||
# + default to cwd as destination if none is provided
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p systemd
|
||||
|
||||
target="$1"
|
||||
host="$(hostname)"
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p nix -p rmlint -p util-linux
|
||||
# script to reclaim some hard drive space
|
||||
# some of this is documented here:
|
||||
# - <https://nixos.wiki/wiki/Storage_optimization>
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p gnugrep -p oath-toolkit -p sops
|
||||
# use: `sane-dump-secret /path/to/accounts/website.yaml`
|
||||
# dumps relevant information about the account, include a OTP code if present
|
||||
secrets=$(sops -d --output-type dotenv $1)
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p coreutils-full -p openssh -p ssh-to-age
|
||||
# unlocks the SOPS store (i.e. populate a SOPS key from the user's SSH key)
|
||||
set -ex
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p coreutils-full -p findutils -p sops
|
||||
# after modifying .sops.yaml, run this to re-encode all secrets to the new keys
|
||||
# pass the base directory (under which *everything* is a secret) as argument
|
||||
for i in $(find "$1" -print)
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p inetutils -p systemd
|
||||
|
||||
target="$1"
|
||||
host="$(hostname)"
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p openssl
|
||||
|
||||
# dump info about the provided SSL certificate
|
||||
cert="$1"
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p systemd
|
||||
sudo systemctl stop matrix-appservice-irc mx-puppet-discord
|
||||
sudo systemctl stop pleroma gitea matrix-synapse jellyfin transmission jackett
|
||||
sudo systemctl stop ejabberd goaccess i2p kiwix-serve navidrome
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p coreutils-full
|
||||
|
||||
# redirects to $1, when writing to $1 requires sudo permissions.
|
||||
# i.e. convert a failing command:
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env zsh
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i zsh -p coreutils-full -p ifuse -p rsync
|
||||
# this really does need zsh because bash `test -e` behaves differently
|
||||
set -ex
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p rsync -p sane-scripts.mount-servo
|
||||
set -ex
|
||||
sane-mount-servo
|
||||
|
||||
|
@@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
echo success
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p coreutils-full -p gnugrep -p gnused -p sane-scripts.ip-check -p systemd
|
||||
|
||||
# first arg should be the region, e.g. `us` or `ukr`
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p coreutils-full -p gnugrep -p gnused -p sane-scripts.ip-check -p systemd
|
||||
|
||||
# first arg should be the region, e.g. `us` or `ukr`
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash -p coreutils-full -p file
|
||||
# traces a PATH lookup by printing the source, resolution, and any symlinks traversed
|
||||
# finally, prints the content of the file
|
||||
echo $1
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -i bash
|
||||
# remove firefox/librewolf/chromium artifacts
|
||||
rm -rf \
|
||||
~/.librewolf/default/* \
|
||||
|
@@ -13,9 +13,10 @@ let
|
||||
# <name> = expected string in the nix-shell invocation
|
||||
# <value> = package to provide
|
||||
pkgsToAttrs = prefix: pkgSet: expr: ({
|
||||
# branch based on the type of `expr`
|
||||
"lambda" = expr: pkgsToAttrs prefix pkgSet (expr pkgSet);
|
||||
"list" = expr: foldl' (acc: pname: acc // {
|
||||
"${prefix + pname}" = pkgSet."${pname}";
|
||||
"${prefix + pname}" = lib.getAttrFromPath (lib.splitString "." pname) pkgSet;
|
||||
}) {} expr;
|
||||
"set" = expr: expr;
|
||||
})."${typeOf expr}" expr;
|
||||
@@ -53,6 +54,7 @@ in rec {
|
||||
'';
|
||||
nativeBuildInputs = [ makeWrapper ];
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p $out/bin
|
||||
mv ${srcPath} $out/bin/${srcPath}
|
||||
|
||||
@@ -62,6 +64,8 @@ in rec {
|
||||
# add runtime dependencies to PATH
|
||||
wrapProgram $out/bin/${srcPath} \
|
||||
--suffix PATH : ${lib.makeBinPath pkgsEnv }
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
} // (removeAttrs attrs [ "interpreter" "interpreterName" "pkgsEnv" "pkgExprs" "srcPath" ])
|
||||
);
|
||||
@@ -78,6 +82,18 @@ in rec {
|
||||
} // (removeAttrs attrs [ "pkgs" ])
|
||||
);
|
||||
|
||||
# `mkShell` specialization for `nix-shell -i zsh` scripts.
|
||||
mkZsh = { pname, pkgs ? {}, srcPath ? pname, ...}@attrs:
|
||||
let
|
||||
pkgsAsAttrs = pkgsToAttrs "" pkgs' pkgs;
|
||||
pkgsEnv = attrValues pkgsAsAttrs;
|
||||
pkgExprs = attrNames pkgsAsAttrs;
|
||||
in mkShell ({
|
||||
inherit pkgsEnv pkgExprs;
|
||||
interpreter = "${pkgs'.zsh}/bin/zsh";
|
||||
} // (removeAttrs attrs [ "pkgs" ])
|
||||
);
|
||||
|
||||
# `mkShell` specialization for invocations of `nix-shell -p "python3.withPackages (...)"`
|
||||
# pyPkgs argument is parsed the same as pkgs, except that names are assumed to be relative to `"ps"` if specified in list form.
|
||||
mkPython3Bin = { pname, pkgs ? {}, pyPkgs ? {}, srcPath ? pname, ... }@attrs:
|
||||
|
24
pkgs/additional/sxmo-utils/0004-no-busybox.patch
Normal file
24
pkgs/additional/sxmo-utils/0004-no-busybox.patch
Normal file
@@ -0,0 +1,24 @@
|
||||
diff --git a/setup_config_version.sh b/setup_config_version.sh
|
||||
index fde40c1..6386014 100755
|
||||
--- a/setup_config_version.sh
|
||||
+++ b/setup_config_version.sh
|
||||
@@ -8,7 +8,7 @@ case "$1" in
|
||||
exit
|
||||
esac
|
||||
|
||||
-case "$(busybox head -n1 "$1")" in
|
||||
+case "$(head -n1 "$1")" in
|
||||
"#"*)
|
||||
comment="#"
|
||||
;;
|
||||
@@ -23,7 +23,7 @@ case "$(busybox head -n1 "$1")" in
|
||||
;;
|
||||
esac
|
||||
|
||||
-busybox md5sum "$1" | \
|
||||
- busybox cut -d" " -f1 | \
|
||||
- busybox xargs -I{} busybox sed -i "2i$comment configversion: {}" \
|
||||
+md5sum "$1" | \
|
||||
+ cut -d" " -f1 | \
|
||||
+ xargs -I{} sed -i "2i$comment configversion: {}" \
|
||||
"$1"
|
@@ -0,0 +1,54 @@
|
||||
#!/bin/sh
|
||||
|
||||
# include common definitions
|
||||
# shellcheck source=scripts/core/sxmo_common.sh
|
||||
. sxmo_common.sh
|
||||
|
||||
# Create xdg user directories, such as ~/Pictures
|
||||
xdg-user-dirs-update
|
||||
|
||||
sxmo_daemons.sh start daemon_manager superd -v
|
||||
|
||||
# let time to superd to start correctly
|
||||
while ! superctl status > /dev/null 2>&1; do
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
# Periodically update some status bar components
|
||||
sxmo_hook_statusbar.sh all
|
||||
sxmo_daemons.sh start statusbar_periodics sxmo_run_aligned.sh 60 \
|
||||
sxmo_hook_statusbar.sh periodics
|
||||
|
||||
# mako/dunst are required for warnings.
|
||||
# load some other little things here too.
|
||||
superctl start mako
|
||||
superctl start sxmo_wob
|
||||
superctl start sxmo_menumode_toggler
|
||||
superctl start bonsaid
|
||||
swaymsg output '*' bg "$SXMO_BG_IMG" fill
|
||||
|
||||
# To setup initial lock state
|
||||
sxmo_hook_unlock.sh
|
||||
|
||||
# Turn on auto-suspend
|
||||
if [ -w "/sys/power/wakeup_count" ] && [ -f "/sys/power/wake_lock" ]; then
|
||||
superctl start sxmo_autosuspend
|
||||
fi
|
||||
|
||||
# Turn on lisgd
|
||||
superctl start sxmo_hook_lisgd
|
||||
|
||||
# Start the desktop widget (e.g. clock)
|
||||
superctl start sxmo_conky
|
||||
|
||||
# Monitor the battery
|
||||
superctl start sxmo_battery_monitor
|
||||
|
||||
# It watch network changes and update the status bar icon by example
|
||||
superctl start sxmo_networkmonitor
|
||||
|
||||
# The daemon that display notifications popup messages
|
||||
superctl start sxmo_notificationmonitor
|
||||
|
||||
# monitor for headphone for statusbar
|
||||
superctl start sxmo_soundmonitor
|
@@ -2,6 +2,7 @@
|
||||
, fetchgit
|
||||
, gitUpdater
|
||||
, lib
|
||||
, rsync
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation rec {
|
||||
@@ -19,9 +20,10 @@ stdenv.mkDerivation rec {
|
||||
./0001-group-differs-from-user.patch
|
||||
./0002-ensure-log-dir.patch
|
||||
./0003-fix-xkb-paths.patch
|
||||
./0004-no-busybox.patch
|
||||
|
||||
# personal preferences:
|
||||
./0004-full-auto-rotate.patch
|
||||
./0104-full-auto-rotate.patch
|
||||
];
|
||||
|
||||
postPatch = ''
|
||||
@@ -30,8 +32,11 @@ stdenv.mkDerivation rec {
|
||||
sed -i "s@/usr/bin/@@g" scripts/core/sxmo_version.sh
|
||||
sed -i 's:ExecStart=/usr/bin/:ExecStart=/usr/bin/env :' configs/superd/services/*.service
|
||||
|
||||
# on devices where volume is part of the primary keyboard, we want to avoid overwriting the default map
|
||||
cp ${./en_us_105.xkb} configs/xkb/xkb_mobile_normal_buttons
|
||||
# apply customizations
|
||||
# - xkb_mobile_normal_buttons:
|
||||
# - on devices where volume is part of the primary keyboard (e.g. thinkpad), we want to avoid overwriting the default map
|
||||
# - this provided map is the en_US 105 key map
|
||||
${rsync}/bin/rsync -rlv ${./customization}/ ./
|
||||
'';
|
||||
|
||||
installFlags = [
|
||||
|
@@ -19,11 +19,13 @@ let
|
||||
sane-lib = import ../modules/lib final';
|
||||
|
||||
### ADDITIONAL PACKAGES
|
||||
alsa-ucm-conf-sane = callPackage ./additional/alsa-ucm-conf-sane { };
|
||||
bonsai = unpatched.bonsai or (callPackage ./additional/bonsai { });
|
||||
bootpart-uefi-x86_64 = callPackage ./additional/bootpart-uefi-x86_64 { };
|
||||
browserpass-extension = callPackage ./additional/browserpass-extension { };
|
||||
cargoDocsetHook = callPackage ./additional/cargo-docset/hook.nix { };
|
||||
feeds = lib.recurseIntoAttrs (callPackage ./additional/feeds { });
|
||||
jellyfin-media-player-qt6 = callPackage ./additional/jellyfin-media-player-qt6 { };
|
||||
gopass-native-messaging-host = callPackage ./additional/gopass-native-messaging-host { };
|
||||
gpodder-configured = callPackage ./additional/gpodder-configured { };
|
||||
hare-ev = unpatched.hare-ev or (callPackage ./additional/hare-ev { });
|
||||
@@ -63,6 +65,13 @@ let
|
||||
# - pkgs.callPackage draws from the _final_ package set.
|
||||
# - unpatched.XYZ draws (selectively) from the _unpatched_ package set.
|
||||
# see <overlays/pkgs.nix>
|
||||
|
||||
# XXX patching this is... really costly.
|
||||
# prefer to set ALSA_CONFIG_UCM2 = "${pkgs.alsa-ucm-conf-sane}/share/alsa/ucm2" if possible instead.
|
||||
# alsa-project = unpatched.alsa-project.overrideScope' (sself: ssuper: {
|
||||
# alsa-ucm-conf = sself.callPackage ./additional/alsa-ucm-conf-sane { inherit (ssuper) alsa-ucm-conf; };
|
||||
# });
|
||||
|
||||
browserpass = callPackage ./patched/browserpass { inherit (unpatched) browserpass; };
|
||||
|
||||
# mozilla keeps nerfing itself and removing configuration options
|
||||
@@ -80,6 +89,7 @@ let
|
||||
jackett = callPackage ./patched/jackett { inherit (unpatched) jackett; };
|
||||
|
||||
lemmy-server = callPackage ./patched/lemmy-server { inherit (unpatched) lemmy-server; };
|
||||
lemmy-ui = callPackage ./patched/lemmy-ui { inherit (unpatched) lemmy-ui; };
|
||||
|
||||
phoc = callPackage ./patched/phoc { inherit (unpatched) phoc; };
|
||||
|
||||
|
@@ -16,7 +16,7 @@ let
|
||||
src = ./.;
|
||||
|
||||
inherit bash gnused sops;
|
||||
sane_scripts = sane-scripts;
|
||||
sane_secrets_unlock = sane-scripts.secrets-unlock; # XXX: must be snake_case
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
substituteAll ${./sops-gpg-adapter} $out/bin/gpg
|
||||
@@ -45,4 +45,8 @@ in
|
||||
mkdir -p $out/lib/mozilla/native-messaging-hosts
|
||||
ln -s $out/lib/browserpass/hosts/firefox/*.json $out/lib/mozilla/native-messaging-hosts
|
||||
'';
|
||||
|
||||
passthru = (upstream.passthru or {}) // {
|
||||
inherit sane-browserpass-gpg;
|
||||
};
|
||||
})
|
||||
|
@@ -8,7 +8,7 @@ then
|
||||
fi
|
||||
|
||||
# ensure the secret store is unlocked
|
||||
@sane_scripts@/bin/sane-secrets-unlock
|
||||
@sane_secrets_unlock@/bin/sane-secrets-unlock
|
||||
|
||||
# using exec here forwards our stdin
|
||||
# browserpass parses the response in
|
||||
|
5
pkgs/patched/lemmy-ui/default.nix
Normal file
5
pkgs/patched/lemmy-ui/default.nix
Normal file
@@ -0,0 +1,5 @@
|
||||
{ lemmy-ui, nodejs }:
|
||||
lemmy-ui.override {
|
||||
# build w/ latest nodejs; not 14.x
|
||||
inherit nodejs;
|
||||
}
|
Reference in New Issue
Block a user