feat(nix-on-droid): module hermes-access (sshd + tunnel inverse vers rack) ; accès SSH hermes au tel sans port ouvert

This commit is contained in:
Hermes Agent
2026-11-03 00:54:50 -05:00
parent b2878b05dd
commit bdd332897c
2 changed files with 162 additions and 0 deletions
+1
View File
@@ -6,6 +6,7 @@
...
}: {
imports = [
./hermes-access.nix
# ./modules/sshd
# ../../nod-sshd
# ../../zsh
+161
View File
@@ -0,0 +1,161 @@
{ config, lib, pkgs, ... }:
# Accès SSH de hermes vers le téléphone (modèle tmate : le tel sort vers le VPS
# `rack`, aucun port ouvert, aucun tiers, pas d'app VPN en plus).
#
# - sshd local (port 8023, clé hermes@tower autorisée, sftp) ;
# - `phone-tunnel-loop` : boucle `ssh -R 127.0.0.1:2224:127.0.0.1:8023 root@marcpartensky.com` ;
# - `phone-up` : démarrage idempotent (sshd + tunnel + wake-lock), accroché à chaque
# session via ~/.zprofile, ~/.zshrc, ~/.profile et ~/.bashrc ;
# - `phone-status` : diagnostic (pids + logs).
#
# La clé privée du tunnel vit uniquement sur le tel (~/.ssh/relay_ed25519), jamais ici.
# Côté rack, l'entrée restreinte :
# command="false",restrict,port-forwarding,permitlisten="127.0.0.1:2224" ssh-ed25519 AAAA... phone-relay
let
inherit (config.user) home;
sshdDir = "${home}/sshd";
sshdPort = 8023;
relayHost = "marcpartensky.com";
relayPort = 2224;
relayKey = "${home}/.ssh/relay_ed25519";
# Clés autorisées sur le sshd du téléphone.
authorizedKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKF3huPUXHP6P6SBXHHw9k7HGh6Cs8ntoRk2pnqrG2Hc hermes@tower"
];
sshdConfig = pkgs.writeText "phone-sshd_config" ''
Port ${toString sshdPort}
ListenAddress 0.0.0.0
HostKey ${sshdDir}/ssh_host_ed25519_key
PidFile ${sshdDir}/sshd.pid
AuthorizedKeysFile ${home}/.ssh/authorized_keys
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
UsePAM no
AllowUsers ${config.user.userName}
StrictModes no
PrintMotd no
Subsystem sftp internal-sftp
'';
authorizedKeysFile = pkgs.writeText "phone-authorized_keys"
(lib.concatStringsSep "\n" authorizedKeys + "\n");
tunnelLoop = pkgs.writeScriptBin "phone-tunnel-loop" ''
#!${pkgs.runtimeShell}
while true; do
${pkgs.openssh}/bin/ssh -NT \
-i ${relayKey} \
-o IdentitiesOnly=yes -o BatchMode=yes -o ConnectTimeout=15 \
-o ServerAliveInterval=25 -o ServerAliveCountMax=4 \
-o ExitOnForwardFailure=yes -o StrictHostKeyChecking=accept-new \
-o UserKnownHostsFile=${home}/.ssh/known_hosts \
-R 127.0.0.1:${toString relayPort}:127.0.0.1:${toString sshdPort} \
root@${relayHost}
${pkgs.coreutils}/bin/sleep 15
done
'';
phoneUp = pkgs.writeScriptBin "phone-up" ''
#!${pkgs.runtimeShell}
core=${pkgs.coreutils}/bin
flock=${lib.getBin pkgs.util-linux}/bin/flock
$core/mkdir -p ${sshdDir}
lock=${sshdDir}/up.lock
exec 9>"$lock" || exit 0
if [ -x "$flock" ]; then "$flock" -n 9 || exit 0; fi
# 1) sshd
if [ -f ${sshdDir}/sshd.pid ] && kill -0 "$($core/cat ${sshdDir}/sshd.pid)" 2>/dev/null; then
:
else
$core/rm -f ${sshdDir}/sshd.pid
${pkgs.openssh}/bin/sshd -f ${sshdDir}/sshd_config >> ${sshdDir}/up.log 2>&1 || true
fi
# 2) tunnel inverse (nécessite la clé posée au bootstrap)
if [ -f ${sshdDir}/tunnel.pid ] && kill -0 "$($core/cat ${sshdDir}/tunnel.pid)" 2>/dev/null; then
:
elif [ -f ${relayKey} ]; then
$core/rm -f ${sshdDir}/tunnel.pid
$core/nohup ${tunnelLoop}/bin/phone-tunnel-loop >> ${sshdDir}/tunnel.log 2>&1 &
echo $! > ${sshdDir}/tunnel.pid
fi
# 3) wake lock (anti-gel Android ; demande la permission "superposition d'écran")
wl=/nix/var/nix/profiles/nix-on-droid/nix-on-droid-path/bin/termux-wake-lock
if [ -x "$wl" ]; then
"$wl" || true
fi
'';
phoneStatus = pkgs.writeScriptBin "phone-status" ''
#!${pkgs.runtimeShell}
echo "== sshd"
if [ -f ${sshdDir}/sshd.pid ]; then
${pkgs.procps}/bin/ps -p "$(cat ${sshdDir}/sshd.pid)" -o pid,etime,args || true
else
echo "no pidfile"
fi
echo "== tunnel"
if [ -f ${sshdDir}/tunnel.pid ]; then
${pkgs.procps}/bin/ps -p "$(cat ${sshdDir}/tunnel.pid)" -o pid,etime,args || true
else
echo "no pidfile"
fi
echo "== tunnel.log"
${pkgs.coreutils}/bin/tail -n 5 ${sshdDir}/tunnel.log 2>/dev/null || true
echo "== up.log"
${pkgs.coreutils}/bin/tail -n 5 ${sshdDir}/up.log 2>/dev/null || true
'';
in
{
environment.packages = with pkgs; [
openssh
util-linux # flock pour phone-up
gnugrep # pratique en session de debug
tunnelLoop # référencés ici -> installés sur le tel
phoneUp
phoneStatus
];
android-integration = {
termux-wake-lock.enable = true;
termux-wake-unlock.enable = true;
termux-setup-storage.enable = true;
termux-open.enable = true;
termux-open-url.enable = true;
am.enable = true;
};
build.activation = {
sshd = ''
$DRY_RUN_CMD mkdir -p ${sshdDir}
if [ ! -f ${sshdDir}/ssh_host_ed25519_key ]; then
$VERBOSE_ECHO "phone: generating sshd host key"
$DRY_RUN_CMD ${pkgs.openssh}/bin/ssh-keygen -t ed25519 -N "" -f ${sshdDir}/ssh_host_ed25519_key
fi
$VERBOSE_ECHO "phone: installing sshd_config + authorized_keys"
$DRY_RUN_CMD ${pkgs.coreutils}/bin/mkdir -p ${home}/.ssh
$DRY_RUN_CMD ${pkgs.coreutils}/bin/chmod 700 ${home}/.ssh
$DRY_RUN_CMD ${pkgs.coreutils}/bin/install -m 600 ${sshdConfig} ${sshdDir}/sshd_config
$DRY_RUN_CMD ${pkgs.coreutils}/bin/install -m 600 ${authorizedKeysFile} ${home}/.ssh/authorized_keys
'';
autostart = ''
if [ -z "''${DRY_RUN:-}" ]; then
for f in ${home}/.zprofile ${home}/.zshrc ${home}/.profile ${home}/.bashrc; do
if ! grep -q nod-phone-autostart2 "$f" 2>/dev/null; then
printf '\n# nod-phone-autostart2\nif [ -x /nix/var/nix/profiles/nix-on-droid/nix-on-droid-path/bin/phone-up ]; then /nix/var/nix/profiles/nix-on-droid/nix-on-droid-path/bin/phone-up >/dev/null 2>&1 || true; fi\n' >> "$f"
fi
done
fi
'';
};
}