fix: der Tunnel laeuft jetzt OHNE Fenster -- S4U statt Interactive

Gemeldet: "durch dein ssh script habe ich trotzdem ein powershell fenster
offen, gemerkt habe ich das weil ich es gerade geschlossen habe und die
verbindung weg war."

Genau so ist es. -LogonType Interactive laesst die Aufgabe IN der
Anmeldesitzung laufen; ein Konsolenprogramm bekommt dort ein Fenster, und
"-WindowStyle Hidden" haelt es nur halb zurueck. Wer es zumacht, kappt den
Tunnel, ohne zu wissen was er tut -- und das ist keine Randerscheinung, sondern
passiert genau einmal und dann jedes Mal wieder.

S4U ("Service for User") laeuft AUSSERHALB der Sitzung: es gibt gar keinen
Bildschirm, auf dem etwas erscheinen koennte. Auf lokale Dateien darf es
weiterhin zugreifen, und mehr braucht der Schluessel unter %USERPROFILE% nicht.
Ein Passwort muss dafuer nirgends gespeichert werden.

DAZU GEHOERT EIN PROTOKOLL, sonst tauscht man ein stoerendes Fenster gegen eine
stille Fehlfunktion: ohne Sitzung sieht niemand mehr, was ssh sagt. Ein
abgelehnter Schluessel oder ein besetzter Port waere unsichtbar -- der Tunnel
stuende einfach nicht, und niemand wuesste warum. tunnel.log haelt jeden
Verbindungsversuch fest und wird bei 1 MB verworfen.

Das Skript sagt am Ende auch, wie man ohne Fenster nachsieht:
Get-NetTCPConnection -LocalPort 13389 -State Listen, und die letzten Zeilen des
Protokolls.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-13 01:27:06 +02:00
co-authored by Claude Opus 5
parent 8e3a13b374
commit a494f472d1
+29 -4
View File
@@ -114,7 +114,16 @@ $skript = Join-Path $ordner 'tunnel.ps1'
@"
# Erzeugt von tunnel-einrichten.ps1 -- von Hand ändern lohnt nicht,
# lieber das Einrichtungsskript neu laufen lassen.
#
# PROTOKOLL, weil es kein Fenster mehr gibt: die Aufgabe laeuft ausserhalb der
# Sitzung, also sieht niemand mehr, was ssh sagt. Ohne diese Datei waere ein
# abgelehnter Schluessel oder ein besetzter Port unsichtbar -- der Tunnel
# stuende einfach nicht, und niemand wuesste warum.
`$log = Join-Path `$PSScriptRoot 'tunnel.log'
if ((Test-Path `$log) -and (Get-Item `$log).Length -gt 1MB) { Remove-Item `$log -Force }
while (`$true) {
("[" + (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') + "] verbinde ...") | Add-Content `$log
# -N: nur weiterleiten, keine Shell.
# ExitOnForwardFailure: lieber sofort scheitern und neu versuchen, als
# scheinbar zu laufen, während die Weiterleitung gar nicht steht.
@@ -124,7 +133,8 @@ while (`$true) {
-o ServerAliveInterval=30 -o ServerAliveCountMax=3 ``
-o StrictHostKeyChecking=accept-new ``
-L ${LokalerPort}:localhost:${ZielPort} ``
$Benutzer@$Server
$Benutzer@$Server 2>&1 | Add-Content `$log
("[" + (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') + "] beendet, neuer Versuch in 5 s") | Add-Content `$log
Start-Sleep -Seconds 5
}
"@ | Set-Content -Path $skript -Encoding UTF8
@@ -142,9 +152,20 @@ if (-not $pwsh) { $pwsh = (Get-Command powershell.exe).Source }
$aktion = New-ScheduledTaskAction -Execute $pwsh `
-Argument "-NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$skript`""
$ausloeser = New-ScheduledTaskTrigger -AtLogOn -User "$env:USERDOMAIN\$env:USERNAME"
# Interactive, nicht S4U: der Schlüssel liegt im Benutzerprofil, und die
# Aufgabe muss ihn lesen können.
$prinzipal = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" -LogonType Interactive -RunLevel Limited
# S4U statt Interactive -- DAS ist der Unterschied zwischen "kein Fenster" und
# "ein Fenster, das man versehentlich zumacht".
#
# Bei -LogonType Interactive laeuft die Aufgabe IN der Anmeldesitzung. Ein
# Konsolenprogramm bekommt dort ein Fenster, und "-WindowStyle Hidden" hilft
# nur halb: es blieb sichtbar, und wer es schliesst, kappt den Tunnel, ohne zu
# wissen was er tut. Genau so ist es passiert.
#
# S4U ("Service for User") laeuft ausserhalb der Sitzung -- es gibt gar keinen
# Bildschirm, auf dem etwas erscheinen koennte. Auf lokale Dateien darf es
# weiterhin zugreifen, und mehr braucht der Schluessel unter %USERPROFILE%
# nicht. Ein Passwort muss dafuer nirgends gespeichert werden.
$prinzipal = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" -LogonType S4U -RunLevel Limited
$optionen = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries `
-ExecutionTimeLimit ([TimeSpan]::Zero) -RestartCount 999 -RestartInterval (New-TimeSpan -Minutes 1)
@@ -172,6 +193,10 @@ if ($steht) {
Write-Host " Port : $LokalerPort" -ForegroundColor White
Write-Host ''
Write-Host " Nicht mehr auf $Server verbinden -- das ist gesperrt." -ForegroundColor DarkGray
Write-Host ''
Write-Host ' Es gibt jetzt KEIN Fenster mehr. Nachsehen kann man so:' -ForegroundColor DarkGray
Write-Host " Get-NetTCPConnection -LocalPort $LokalerPort -State Listen" -ForegroundColor DarkGray
Write-Host " Get-Content '$ordner unnel.log' -Tail 20" -ForegroundColor DarkGray
} else {
Warnung "Der Tunnel steht noch nicht auf Port $LokalerPort."
Write-Host ''