Files
ssh-access-tui/internal/keymgr/instructions.go
T
2026-07-22 14:14:53 +03:00

83 lines
3.7 KiB
Go

package keymgr
import (
"fmt"
"strings"
"time"
"ssh-access-tui/internal/model"
)
func genTimestamp() string {
return time.Now().UTC().Format("2006-01-02 15:04 UTC")
}
// RenderInstructions produces a plain-text README aimed at a Windows user
// whose only guaranteed tool is the OpenSSH client bundled with Windows
// 10/11 ("Optional Features" -> OpenSSH Client, or already present).
func RenderInstructions(username, jumpHost, keyFileName string, hosts []model.Host) string {
var b strings.Builder
b.WriteString("SSH ACCESS — SETUP INSTRUCTIONS (Windows)\n")
b.WriteString("==========================================\n\n")
fmt.Fprintf(&b, "Account: %s\n", username)
fmt.Fprintf(&b, "Jump host: %s\n\n", jumpHost)
b.WriteString("This archive contains:\n")
fmt.Fprintf(&b, " - %s your PRIVATE key (keep secret, do not share or send onward)\n", keyFileName)
fmt.Fprintf(&b, " - %s.pub the matching public key (for reference only)\n", keyFileName)
b.WriteString(" - config SSH client config with ready-made host shortcuts\n")
b.WriteString(" - INSTRUCTIONS.txt this file\n\n")
b.WriteString("1. Check you have the SSH client\n")
b.WriteString(" Open PowerShell and run:\n")
b.WriteString(" ssh -V\n")
b.WriteString(" If this fails: Settings -> Apps -> Optional Features -> Add a feature ->\n")
b.WriteString(" \"OpenSSH Client\" -> Install. (Windows 10 1809+/11 ship this by default.)\n\n")
b.WriteString("2. Put the files in place\n")
b.WriteString(" Create the folder (if it doesn't exist) and copy the 3 files into it:\n")
b.WriteString(" %USERPROFILE%\\.ssh\\\n")
b.WriteString(" i.e. C:\\Users\\<you>\\.ssh\\\n")
b.WriteString(" The \"config\" file should be merged with (or placed as) that exact filename,\n")
b.WriteString(" with no file extension: C:\\Users\\<you>\\.ssh\\config\n\n")
b.WriteString("3. Fix the private key permissions (Windows OpenSSH refuses keys that are\n")
b.WriteString(" readable by other users). In PowerShell:\n")
fmt.Fprintf(&b, " icacls $env:USERPROFILE\\.ssh\\%s /inheritance:r\n", keyFileName)
fmt.Fprintf(&b, " icacls $env:USERPROFILE\\.ssh\\%s /grant:r \"$env:USERNAME:(R)\"\n\n", keyFileName)
b.WriteString("4. Connect\n")
b.WriteString(" You have shortcuts for each host you're allowed to reach. To open a local\n")
b.WriteString(" port forward to one of them (example: a web service on port 8443):\n\n")
if len(hosts) > 0 {
h := hosts[0]
fmt.Fprintf(&b, " ssh -N -L 8443:localhost:%d %s\n", h.Port, h.Name)
b.WriteString(" Then browse to https://localhost:8443 (or whatever local port you chose)\n")
b.WriteString(" while that command keeps running. Ctrl+C to stop.\n\n")
} else {
b.WriteString(" ssh -N -L <local-port>:localhost:<remote-port> <host-alias>\n\n")
}
b.WriteString(" Your allowed hosts (aliases from the config file):\n")
for _, h := range hosts {
fmt.Fprintf(&b, " - %-20s -> %s:%d", h.Name, h.Address, h.Port)
if h.Description != "" {
fmt.Fprintf(&b, " (%s)", h.Description)
}
b.WriteString("\n")
}
b.WriteString("\n")
b.WriteString("5. You will NOT get an interactive shell\n")
b.WriteString(" Running plain \"ssh jump\" will connect and immediately close — this account\n")
b.WriteString(" is jump/port-forwarding only, by design. Use the -L/-D forwarding commands\n")
b.WriteString(" above (via the host aliases, which already route through the jump host).\n\n")
b.WriteString("If access stops working, contact your administrator — your account may have\n")
b.WriteString("been locked, or the allowed host list may have changed (ask for an updated\n")
b.WriteString("config in that case).\n")
return b.String()
}