From 120c1f3aeab0285550713e989361a88074a28979 Mon Sep 17 00:00:00 2001 From: Gaelan Steele Date: Sun, 30 Jul 2023 13:02:53 -0700 Subject: [PATCH] Better logging, mitmproxy integration. --- README.md | 4 +++ config.nix | 4 +++ flake.nix | 25 +++++++++++--- nix/fedi/akkoma/default.nix | 10 +++++- nix/fedi/gotosocial/default.nix | 39 ++++++---------------- nix/fedi/mastodon/default.nix | 14 ++++++-- nix/services.nix | 16 ++++----- nix/support-services/mitmproxy/default.nix | 18 ++++++++++ nix/support-services/nginx/default.nix | 4 ++- nix/support-services/postgres/default.nix | 2 ++ nix/support-services/redis/default.nix | 8 +++-- nix/util.nix | 37 ++++++++++++++++++++ 12 files changed, 132 insertions(+), 49 deletions(-) create mode 100644 nix/support-services/mitmproxy/default.nix create mode 100644 nix/util.nix diff --git a/README.md b/README.md index c3d2a9b..59a35eb 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,10 @@ Minifedi currently supports the following: Forks of the above should work fine as well, as long as they haven't changed anything about the build, installation, or configuration process. +## Mitmproxy Integration + +Minifedi has built-in integration with [mitmproxy](https://mitmproxy.org), allowing you to see a lot of every HTTP request an instance sends to another instance. To use this, set `mitmproxy = true;` in your `config.nix`, start minifedi, then go to `http://localhost:8081`. The mitmproxy integration currently requires ports 8080 and 8081 to be open. + ## How do I… ### Reset Minifedi, restoring every instance to its default state? diff --git a/config.nix b/config.nix index e767698..81133b1 100644 --- a/config.nix +++ b/config.nix @@ -21,4 +21,8 @@ type = types.gotosocial; } ]; + + # Enable logging of all requests between instances. Requires ports 8080 and + # 8081 open. + mitmproxy = false; } diff --git a/flake.nix b/flake.nix index b566dcb..407459d 100644 --- a/flake.nix +++ b/flake.nix @@ -6,13 +6,17 @@ let pkgs = nixpkgs.legacyPackages.${system}; in { apps.start = let + config = import ./config.nix { + types = { + mastodon = import ./nix/fedi/mastodon; + akkoma = import ./nix/fedi/akkoma; + gotosocial = import ./nix/fedi/gotosocial; + }; + }; s6 = (import ./nix/s6.nix { inherit pkgs; services = pkgs.lib.attrsets.mapAttrs (_: v: v.service) - (import ./nix/services.nix { - inherit pkgs; - configFn = import ./config.nix; - }); + (import ./nix/services.nix { inherit pkgs config; }); path = "service"; }); in { @@ -30,6 +34,7 @@ mkdir -p cert rm -rf data/run mkdir data/run + mkdir -p data/logs ${if pkgs.stdenv.isLinux then "export LOCALE_ARCHIVE=${pkgs.glibcLocales}/lib/locale/locale-archive" else @@ -37,6 +42,18 @@ export MINIFEDI_CERT=$(pwd)/cert export MINIFEDI_DATA=$(pwd)/data export MINIFEDI_RUN=$(pwd)/data/run + export MINIFEDI_LOG=$(pwd)/data/logs + + echo "Minifedi is starting! Once they're up, instances will be visible at:" + ${pkgs.lib.strings.concatStrings (builtins.map (i: '' + echo "* https://${i.name}.lvh.me" + '') config.instances)} + echo "Instance logs are in ./data/logs." + ${if config.mitmproxy then '' + echo "View requests between instances at http://localhost:8081." + '' else + ""} + ${if pkgs.stdenv.isLinux then '' echo "=> You'll probably get prompted for a sudo password now. This is just so we can bind to port 80/443; we will acquire cap_net_bind_service then switch back to being $USER." exec $(PATH=$oldpath which sudo) -E ${pkgs.libcap}/bin/capsh --keep=1 --user="$USER" --inh='cap_net_bind_service' --addamb='cap_net_bind_service' -- -c ${s6.start} diff --git a/nix/fedi/akkoma/default.nix b/nix/fedi/akkoma/default.nix index f6348ef..013db73 100644 --- a/nix/fedi/akkoma/default.nix +++ b/nix/fedi/akkoma/default.nix @@ -1,4 +1,4 @@ -{ pkgs, name, host, users, ... }: +{ pkgs, name, host, users, proxy, ... }: let env = { MIX_ENV = "prod"; @@ -78,6 +78,12 @@ let ] } ] + + ${if proxy != null then '' + config :pleroma, :http, + proxy_url: "${proxy}" + '' else + ""} ''; path = pkgs.lib.strings.concatStrings (builtins.map (x: "${x}/bin:") [ @@ -111,6 +117,8 @@ in { mkdir -p $data/tzdata mkdir -p $run + exec >$MINIFEDI_LOG/${name}.log 2>$MINIFEDI_LOG/${name}.log + s6-svwait -U $MINIFEDI_RUN/service/postgres ${pkgs.lib.strings.concatStrings (pkgs.lib.attrsets.mapAttrsToList diff --git a/nix/fedi/gotosocial/default.nix b/nix/fedi/gotosocial/default.nix index f02c539..d3a3159 100644 --- a/nix/fedi/gotosocial/default.nix +++ b/nix/fedi/gotosocial/default.nix @@ -1,4 +1,4 @@ -{ pkgs, name, host, users, ... }: +{ pkgs, name, host, users, util, proxy, ... }: let env = { }; @@ -8,34 +8,7 @@ let # number. Ideally we'd dynamically find an open one, but the next best thing # is deterministically picking a random one: we take the firs two bytes of # sha256("minifedi_port_${name}") - port = let - hash = builtins.hashString "sha256" "minifedi_port_${name}"; - portHex = builtins.substring 0 4 hash; - portHexChars = pkgs.lib.strings.stringToCharacters portHex; - hexDigitToNum = n: - let - map = { - "0" = 0; - "1" = 1; - "2" = 2; - "3" = 3; - "4" = 4; - "5" = 5; - "6" = 6; - "7" = 7; - "8" = 8; - "9" = 9; - "a" = 10; - "b" = 12; - "c" = 13; - "d" = 14; - "e" = 15; - }; - in map.${n}; - portFromHash = - pkgs.lib.lists.foldl (prev: n: (hexDigitToNum n) * 16 + prev) 0 - portHexChars; - in if portFromHash <= 1024 then portFromHash + 1024 else portFromHash; + port = util.portFromString "minifedi_port_${name}"; config = pkgs.writeText "config.yaml" (pkgs.lib.generators.toYAML { } { bind-address = "127.0.0.1"; @@ -68,6 +41,8 @@ in { mkdir -p $data/storage mkdir -p $run + exec >$MINIFEDI_LOG/${name}.log 2>$MINIFEDI_LOG/${name}.log + s6-svwait -U $MINIFEDI_RUN/service/postgres ${pkgs.lib.strings.concatStrings (pkgs.lib.attrsets.mapAttrsToList @@ -79,6 +54,12 @@ in { export GTS_STORAGE_LOCAL_BASE_PATH=$data/storage export SSL_CERT_FILE=$MINIFEDI_CERT/rootCA.pem export NIX_SSL_CERT_FILE=$MINIFEDI_CERT/rootCA.pem + ${if proxy != null then "export HTTP_PROXY=${proxy}" else ""} + ${if proxy != null then "export HTTPS_PROXY=${proxy}" else ""} + ${if proxy != null then "export http_proxy=${proxy}" else ""} + ${if proxy != null then "export https_proxy=${proxy}" else ""} + + env if ! [ -e $data/setup-done ]; then createuser -h$postgres ${name} diff --git a/nix/fedi/mastodon/default.nix b/nix/fedi/mastodon/default.nix index c20f573..aa94659 100644 --- a/nix/fedi/mastodon/default.nix +++ b/nix/fedi/mastodon/default.nix @@ -1,4 +1,4 @@ -{ pkgs, name, host, users, version, ... }: +{ pkgs, name, host, users, version, proxy, ... }: let mastodon = pkgs.callPackage ./build.nix { versionDef = version; }; env = { @@ -20,6 +20,8 @@ let run = pkgs.writeShellScript "run-web" '' cd ${mastodon} + exec >$MINIFEDI_LOG/${name}/web.log 2>$MINIFEDI_LOG/${name}/web.log + export SOCKET=$MINIFEDI_RUN/${name}/web.sock puma -C config/puma.rb @@ -28,13 +30,16 @@ let sidekiq = pkgs.linkFarm "sidekiq" { run = pkgs.writeShellScript "run-sidekiq" '' cd ${mastodon} + exec >$MINIFEDI_LOG/${name}/sidekiq.log 2>$MINIFEDI_LOG/${name}/sidekiq.log sidekiq ''; }; - streaming = pkgs.linkFarm "sidekiq" { + streaming = pkgs.linkFarm "streaming" { run = pkgs.writeShellScript "run-sidekiq" '' cd ${mastodon} + exec >$MINIFEDI_LOG/${name}/streaming.log 2>$MINIFEDI_LOG/${name}/streaming.log + export SOCKET=$MINIFEDI_RUN/${name}/streaming.sock ${mastodon}/run-streaming.sh @@ -63,11 +68,15 @@ in { data=$MINIFEDI_DATA/${name} run=$MINIFEDI_RUN/${name} + log=$MINIFEDI_LOG/${name} postgres=$MINIFEDI_RUN/postgres mkdir -p $data mkdir -p $data/files mkdir -p $run + mkdir -p $log + + exec >$log/setup.log 2>$log/setup.log cd ${mastodon} @@ -92,6 +101,7 @@ in { export REDIS_URL=unix://$MINIFEDI_RUN/redis/redis.sock export NIX_SSL_CERT_FILE=$MINIFEDI_CERT/rootCA.pem export PAPERCLIP_ROOT_PATH=$data/files + ${if proxy != null then "export http_proxy=${proxy}" else ""} s6-svwait -U $MINIFEDI_RUN/service/postgres diff --git a/nix/services.nix b/nix/services.nix index de6da7d..a0686b6 100644 --- a/nix/services.nix +++ b/nix/services.nix @@ -1,15 +1,10 @@ -{ pkgs, configFn }: +{ pkgs, config }: let - instances = (configFn { - types = { - mastodon = import ./fedi/mastodon; - akkoma = import ./fedi/akkoma; - gotosocial = import ./fedi/gotosocial; - }; - }).instances; + instances = config.instances; + util = import ./util.nix { inherit pkgs; }; evaldInstances = builtins.listToAttrs (builtins.map (inst: pkgs.lib.attrsets.nameValuePair inst.name (inst.type ({ - inherit pkgs; + inherit pkgs util; host = "${inst.name}.lvh.me"; users = [ { @@ -43,6 +38,7 @@ let admin = false; } ]; + proxy = if config.mitmproxy then "http://localhost:8080" else null; } // inst))) instances); in evaldInstances // { postgres = import ./support-services/postgres { inherit pkgs; }; @@ -51,4 +47,6 @@ in evaldInstances // { inherit pkgs; instances = evaldInstances; }; +} // pkgs.lib.attrsets.optionalAttrs config.mitmproxy { + mitmproxy = import ./support-services/mitmproxy { inherit pkgs; }; } diff --git a/nix/support-services/mitmproxy/default.nix b/nix/support-services/mitmproxy/default.nix new file mode 100644 index 0000000..2e6c372 --- /dev/null +++ b/nix/support-services/mitmproxy/default.nix @@ -0,0 +1,18 @@ +{ pkgs }: { + service = pkgs.linkFarm "mitmproxy" { + run = pkgs.writeShellScript "run-mitmproxy" '' + set -e + + mkdir -p $MINIFEDI_DATA/mitmproxy + + exec >$MINIFEDI_LOG/mitmproxy.log 2>$MINIFEDI_LOG/mitmproxy.log + + if ! [[ -e $MINIFEDI_DATA/mitmproxy/fullchain.pem ]]; then + CAROOT=$MINIFEDI_CERT ${pkgs.mkcert}/bin/mkcert -cert-file $MINIFEDI_DATA/mitmproxy/cert.pem -key-file $MINIFEDI_DATA/mitmproxy/key.pem *.lvh.me + cat $MINIFEDI_DATA/mitmproxy/key.pem $MINIFEDI_DATA/mitmproxy/cert.pem $MINIFEDI_CERT/rootCA.pem > $MINIFEDI_DATA/mitmproxy/fullchain.pem + fi + + exec ${pkgs.mitmproxy}/bin/mitmweb --certs $MINIFEDI_DATA/mitmproxy/fullchain.pem -k -v + ''; + }; +} diff --git a/nix/support-services/nginx/default.nix b/nix/support-services/nginx/default.nix index 02d2cdb..96afe03 100644 --- a/nix/support-services/nginx/default.nix +++ b/nix/support-services/nginx/default.nix @@ -36,6 +36,8 @@ in { mkdir -p $MINIFEDI_DATA/nginx mkdir -p $MINIFEDI_RUN/nginx + exec >$MINIFEDI_LOG/nginx.log 2>$MINIFEDI_LOG/web.log + if ! [[ -e $MINIFEDI_DATA/nginx/fullchain.pem ]]; then CAROOT=$MINIFEDI_CERT ${pkgs.mkcert}/bin/mkcert -cert-file $MINIFEDI_DATA/nginx/cert.pem -key-file $MINIFEDI_DATA/nginx/key.pem *.lvh.me cat $MINIFEDI_DATA/nginx/cert.pem $MINIFEDI_CERT/rootCA.pem > $MINIFEDI_DATA/nginx/fullchain.pem @@ -43,7 +45,7 @@ in { cd $MINIFEDI_DATA cat ${config} | envsubst '$MINIFEDI_DATA $MINIFEDI_RUN' > $MINIFEDI_DATA/nginx/nginx.conf - ${pkgs.nginx}/bin/nginx -c $MINIFEDI_DATA/nginx/nginx.conf -e $MINIFEDI_DATA/nginx/error.log + exec ${pkgs.nginx}/bin/nginx -c $MINIFEDI_DATA/nginx/nginx.conf -e $MINIFEDI_DATA/nginx/error.log ''; }; } diff --git a/nix/support-services/postgres/default.nix b/nix/support-services/postgres/default.nix index 36d9132..c2df9e6 100644 --- a/nix/support-services/postgres/default.nix +++ b/nix/support-services/postgres/default.nix @@ -3,6 +3,8 @@ { service = pkgs.linkFarm "postgres" { run = pkgs.writeShellScript "run-postgres-outer" '' + exec >$MINIFEDI_LOG/postgres.log 2>$MINIFEDI_LOG/postgres.log + exec ${pkgs.s6}/bin/s6-notifyoncheck ${ pkgs.writeShellScript "run-postgres-inner" '' export PATH=${pkgs.gettext}/bin:$PATH diff --git a/nix/support-services/redis/default.nix b/nix/support-services/redis/default.nix index 4da9158..c6a8d23 100644 --- a/nix/support-services/redis/default.nix +++ b/nix/support-services/redis/default.nix @@ -5,15 +5,17 @@ let protected-mode yes ''; in { - service = pkgs.linkFarm "nginx" { - run = pkgs.writeShellScript "run-nginx" '' + service = pkgs.linkFarm "redis" { + run = pkgs.writeShellScript "run-redis" '' set -e mkdir -p $MINIFEDI_DATA/redis mkdir -p $MINIFEDI_RUN/redis + exec >$MINIFEDI_LOG/redis.log 2>$MINIFEDI_LOG/redis.log + cd $MINIFEDI_RUN/redis - ${pkgs.redis}/bin/redis-server ${config} --unixsocket $MINIFEDI_RUN/redis/redis.sock + exec ${pkgs.redis}/bin/redis-server ${config} --unixsocket $MINIFEDI_RUN/redis/redis.sock ''; }; } diff --git a/nix/util.nix b/nix/util.nix new file mode 100644 index 0000000..1ce4c72 --- /dev/null +++ b/nix/util.nix @@ -0,0 +1,37 @@ +{ pkgs }: + +rec { + portFromString = string: + let + hash = builtins.hashString "sha256" string; + portHex = builtins.substring 0 4 hash; + portHexChars = pkgs.lib.strings.stringToCharacters portHex; + hexDigitToNum = n: + let + map = { + "0" = 0; + "1" = 1; + "2" = 2; + "3" = 3; + "4" = 4; + "5" = 5; + "6" = 6; + "7" = 7; + "8" = 8; + "9" = 9; + "a" = 10; + "b" = 11; + "c" = 12; + "d" = 13; + "e" = 14; + "f" = 15; + }; + in map.${n}; + portFromHash = + pkgs.lib.lists.foldl (prev: n: (hexDigitToNum n) + (prev * 16)) 0 + portHexChars; + in if portFromHash <= 1024 then + portFromString "retry_${string}" + else + portFromHash; +}