Files

723 lines
24 KiB
Go

// Copyright 2025.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ui
// FieldHelp contains help information for SSH config fields
type FieldHelp struct {
Field string // Field name
Description string // Brief description
Syntax string // Syntax format
Examples []string // Usage examples
Default string // Default value
Since string // OpenSSH version when introduced
Category string // Category for grouping
}
// HelpDisplayMode defines how help is displayed
type HelpDisplayMode int
const (
HelpModeOff HelpDisplayMode = iota // No help shown
HelpModeCompact // Single line help
HelpModeNormal // Standard help panel
HelpModeFull // Detailed help with all info
)
// GetFieldHelp returns help information for a specific field
func GetFieldHelp(fieldName string) *FieldHelp {
if help, exists := fieldHelpData[fieldName]; exists {
// Update default value from centralized source
defaultValue := GetSSHFieldDefault(fieldName)
if defaultValue != "" {
help.Default = formatDefaultValue(fieldName, defaultValue)
}
return &help
}
return nil
}
// formatDefaultValue formats the default value for display in help
func formatDefaultValue(fieldName, value string) string {
// Special formatting for certain fields
switch fieldName {
case "ConnectTimeout":
if value == "" {
return "none (system default)"
}
return value + " seconds"
case "ServerAliveInterval":
if value == "0" {
return "0 (disabled)"
}
return value + " seconds"
case "ControlPath", "ProxyJump", "ProxyCommand", "RemoteCommand",
"LocalForward", "RemoteForward", "DynamicForward",
"LocalCommand", "SendEnv", "SetEnv", "BindAddress", "BindInterface",
"CanonicalDomains", "CanonicalizePermittedCNAMEs",
"PubkeyAcceptedAlgorithms", "HostbasedAcceptedAlgorithms",
"HostKeyAlgorithms", "Ciphers", "MACs", "KexAlgorithms":
if value == "" {
return "none" //nolint:goconst // "none" here means empty/not configured, different from sessionTypeNone
}
return value
case "PreferredAuthentications":
if value == "gssapi-with-mic,hostbased,publickey,keyboard-interactive,password" {
return "gssapi-with-mic,hostbased,publickey,keyboard-interactive,password"
}
return value
case "IdentityAgent":
if value == "SSH_AUTH_SOCK" {
return "SSH_AUTH_SOCK"
}
return value
case "User":
if value == "" {
return "current username"
}
return value
default:
return value
}
}
// fieldHelpData contains help information for all SSH config fields
var fieldHelpData = map[string]FieldHelp{
// Basic fields
"Alias": {
Field: "Alias",
Description: "A nickname or abbreviation for the host. This is what you type after 'ssh' command.",
Syntax: "any_string_without_spaces",
Examples: []string{"myserver", "prod-db", "dev-web-01"},
Default: "(required)",
Category: "Basic",
},
"Host": {
Field: "Host",
Description: "The real hostname or IP address to connect to. Can be a domain name or IP address.",
Syntax: "hostname | ip_address",
Examples: []string{"example.com", "192.168.1.100", "2001:db8::1"},
Default: "(required)",
Category: "Basic",
},
"Port": {
Field: "Port",
Description: "The port number to connect to on the remote host. Standard SSH port is 22.",
Syntax: "port_number (1-65535)",
Examples: []string{"22", "2222", "8022"},
Default: "22",
Category: "Basic",
},
"User": {
Field: "User",
Description: "Username for logging into the remote machine. If not specified, uses current username.",
Syntax: "username",
Examples: []string{"root", "ubuntu", "admin", "deploy"},
Default: "current username",
Category: "Basic",
},
"Keys": {
Field: "Keys",
Description: "Path to SSH private key files for authentication. Multiple keys can be specified.",
Syntax: "path[,path,...]",
Examples: []string{"~/.ssh/id_ed25519", "~/.ssh/id_rsa,~/.ssh/id_ed25519"},
Default: "~/.ssh/id_rsa, ~/.ssh/id_ed25519, etc.",
Category: "Basic",
},
// Connection fields
"ProxyJump": {
Field: "ProxyJump",
Description: "Specifies one or more jump hosts (bastion hosts) to reach the destination. Useful for accessing servers behind firewalls.",
Syntax: "[user@]host[:port][,[user@]host[:port]]",
Examples: []string{"bastion.example.com", "jump1.com,jump2.com", "user@proxy:2222"},
Default: "none",
Since: "OpenSSH 7.3+",
Category: "Connection",
},
"ProxyCommand": {
Field: "ProxyCommand",
Description: "Command to use to connect to the server. Useful for connecting through proxies or using custom connection methods.",
Syntax: "command",
Examples: []string{"ssh -W %h:%p jump.example.com", "nc -X 5 -x proxy:1080 %h %p"},
Default: "none",
Category: "Connection",
},
"RemoteCommand": {
Field: "RemoteCommand",
Description: "Specifies a command to execute on the remote machine after successfully connecting.",
Syntax: "command | none",
Examples: []string{"tmux attach || tmux new", "screen -r", "none"},
Default: "none",
Since: "OpenSSH 7.6+ (for 'none' value)",
Category: "Connection",
},
"ConnectTimeout": {
Field: "ConnectTimeout",
Description: "Timeout in seconds for establishing the connection. Useful for slow or unreliable networks.",
Syntax: "seconds | none",
Examples: []string{"10", "30", "none"},
Default: "none (system default)",
Category: "Connection",
},
"ConnectionAttempts": {
Field: "ConnectionAttempts",
Description: "Number of attempts to make before giving up on connecting.",
Syntax: "number",
Examples: []string{"1", "3", "5"},
Default: "1",
Category: "Connection",
},
"SessionType": {
Field: "SessionType",
Description: "Type of session to request. 'none' (-N flag) is useful for port forwarding without shell.",
Syntax: "none | subsystem | default",
Examples: []string{"none", "subsystem", "default"},
Default: "default",
Since: "OpenSSH 8.7+",
Category: "Connection",
},
"RequestTTY": {
Field: "RequestTTY",
Description: "Request a pseudo-terminal for the session. Required for interactive programs.",
Syntax: "yes | no | force | auto",
Examples: []string{"yes", "force", "auto"},
Default: "auto",
Category: "Connection",
},
// Port forwarding fields
"LocalForward": {
Field: "LocalForward",
Description: "Forward a local port to a remote address. Useful for accessing remote services through SSH tunnel.",
Syntax: "[bind_address:]port:host:hostport (CLI format, auto-converted for config file)",
Examples: []string{"8080:localhost:80", "3306:db.internal:3306", "*:8080:localhost:80"},
Default: "none",
Category: "Forwarding",
},
"RemoteForward": {
Field: "RemoteForward",
Description: "Forward a remote port to a local address. Allows remote users to access local services.",
Syntax: "[bind_address:]port:host:hostport (CLI format, auto-converted for config file)",
Examples: []string{"8080:localhost:3000", "*:80:localhost:8080"},
Default: "none",
Category: "Forwarding",
},
"DynamicForward": {
Field: "DynamicForward",
Description: "Create a SOCKS proxy on the specified port. Useful for routing traffic through SSH.",
Syntax: "[bind_address:]port",
Examples: []string{"1080", "localhost:1080", "*:1080"},
Default: "none",
Category: "Forwarding",
},
"ForwardAgent": {
Field: "ForwardAgent",
Description: "Forward SSH agent connection to remote host. Allows using local SSH keys on remote servers.",
Syntax: "yes | no",
Examples: []string{"yes", "no"},
Default: "no",
Category: "Forwarding",
},
"ForwardX11": {
Field: "ForwardX11",
Description: "Enable X11 forwarding for GUI applications over SSH.",
Syntax: "yes | no",
Examples: []string{"yes", "no"},
Default: "no",
Category: "Forwarding",
},
// Authentication fields
"PubkeyAuthentication": {
Field: "PubkeyAuthentication",
Description: "Enable or disable public key authentication.",
Syntax: "yes | no",
Examples: []string{"yes", "no"},
Default: "yes",
Category: "Authentication",
},
"PasswordAuthentication": {
Field: "PasswordAuthentication",
Description: "Enable or disable password authentication.",
Syntax: "yes | no",
Examples: []string{"yes", "no"},
Default: "yes",
Category: "Authentication",
},
"PreferredAuthentications": {
Field: "PreferredAuthentications",
Description: "Order of authentication methods to try.",
Syntax: "method[,method,...]",
Examples: []string{"publickey,password", "publickey,keyboard-interactive,password"},
Default: "gssapi-with-mic,hostbased,publickey,keyboard-interactive,password",
Category: "Authentication",
},
"IdentitiesOnly": {
Field: "IdentitiesOnly",
Description: "Only use authentication identity files configured in ssh_config, ignore ssh-agent.",
Syntax: "yes | no",
Examples: []string{"yes", "no"},
Default: "no",
Category: "Authentication",
},
"AddKeysToAgent": {
Field: "AddKeysToAgent",
Description: "Add keys to ssh-agent automatically when used.",
Syntax: "yes | no | ask | confirm",
Examples: []string{"yes", "ask", "confirm"},
Default: "no",
Since: "OpenSSH 7.2+",
Category: "Authentication",
},
// Multiplexing fields
"ControlMaster": {
Field: "ControlMaster",
Description: "Enable connection multiplexing. Reuse existing connections for speed.",
Syntax: "yes | no | ask | auto | autoask",
Examples: []string{"auto", "yes", "no"},
Default: "no",
Category: "Multiplexing",
},
"ControlPath": {
Field: "ControlPath",
Description: "Path to control socket for connection multiplexing.",
Syntax: "path",
Examples: []string{"~/.ssh/master-%r@%h:%p", "/tmp/ssh-%r@%h:%p"},
Default: "none",
Category: "Multiplexing",
},
"ControlPersist": {
Field: "ControlPersist",
Description: "Keep master connection open in background after initial client exits.",
Syntax: "yes | no | time",
Examples: []string{"yes", "10m", "4h", "no"},
Default: "no",
Category: "Multiplexing",
},
// Keep-alive fields
"ServerAliveInterval": {
Field: "ServerAliveInterval",
Description: "Seconds between keepalive messages. Prevents connection drops on idle connections.",
Syntax: "seconds",
Examples: []string{"60", "120", "300"},
Default: "0 (disabled)",
Category: "Keep-Alive",
},
"ServerAliveCountMax": {
Field: "ServerAliveCountMax",
Description: "Number of keepalive messages before disconnecting.",
Syntax: "count",
Examples: []string{"3", "5", "10"},
Default: "3",
Category: "Keep-Alive",
},
"TCPKeepAlive": {
Field: "TCPKeepAlive",
Description: "Send TCP keepalive messages to detect broken connections.",
Syntax: "yes | no",
Examples: []string{"yes", "no"},
Default: "yes",
Category: "Keep-Alive",
},
// Security fields
"StrictHostKeyChecking": {
Field: "StrictHostKeyChecking",
Description: "How to handle unknown host keys. 'ask' prompts user, 'no' auto-adds, 'yes' requires pre-existing key.",
Syntax: "yes | no | ask | accept-new",
Examples: []string{"ask", "accept-new", "yes"},
Default: "ask",
Category: "Security",
},
"UserKnownHostsFile": {
Field: "UserKnownHostsFile",
Description: "File to store host keys. Can specify multiple files.",
Syntax: "path [path ...]",
Examples: []string{"~/.ssh/known_hosts", "~/.ssh/known_hosts ~/.ssh/known_hosts2"},
Default: "~/.ssh/known_hosts",
Category: "Security",
},
"HostKeyAlgorithms": {
Field: "HostKeyAlgorithms",
Description: "Host key algorithms in order of preference. Use +/- to add/remove from defaults.",
Syntax: "algorithm[,algorithm,...] | +algo | -algo",
Examples: []string{"ssh-ed25519,ssh-rsa", "+ssh-rsa", "-ssh-dss"},
Default: "ssh-ed25519,ecdsa-sha2-nistp256,ssh-rsa,...",
Category: "Security",
},
"Ciphers": {
Field: "Ciphers",
Description: "Encryption algorithms in order of preference.",
Syntax: "cipher[,cipher,...] | +cipher | -cipher",
Examples: []string{"aes256-gcm@openssh.com,aes256-ctr", "+aes256-cbc", "-3des-cbc"},
Default: "chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,...",
Category: "Security",
},
"MACs": {
Field: "MACs",
Description: "Message authentication code algorithms in order of preference.",
Syntax: "mac[,mac,...] | +mac | -mac",
Examples: []string{"hmac-sha2-256,hmac-sha2-512", "+hmac-md5", "-hmac-sha1"},
Default: "umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,...",
Category: "Security",
},
// Other useful fields
"LogLevel": {
Field: "LogLevel",
Description: "Verbosity level for logging. Higher levels show more detail for debugging.",
Syntax: "QUIET | FATAL | ERROR | INFO | VERBOSE | DEBUG | DEBUG1 | DEBUG2 | DEBUG3",
Examples: []string{"INFO", "DEBUG", "ERROR"},
Default: "INFO",
Category: "Debugging",
},
"Compression": {
Field: "Compression",
Description: "Enable compression to reduce bandwidth usage. Useful for slow connections.",
Syntax: "yes | no",
Examples: []string{"yes", "no"},
Default: "no",
Category: "Connection",
},
"BatchMode": {
Field: "BatchMode",
Description: "Disable all interactive prompts. Useful for scripts and automation.",
Syntax: "yes | no",
Examples: []string{"yes", "no"},
Default: "no",
Category: "Connection",
},
// Missing fields - Tags
"Tags": {
Field: "Tags",
Description: "Custom tags for organizing and filtering servers. Comma-separated list.",
Syntax: "tag1[,tag2,...] ",
Examples: []string{"production", "development,staging", "web,frontend"},
Default: "none",
Category: "Basic",
},
// Connection - IP and Address fields
"IPQoS": {
Field: "IPQoS",
Description: "Quality of Service (QoS) / DSCP / TOS for SSH connections. Can specify different values for interactive and bulk traffic.",
Syntax: "dscp_value | lowdelay | throughput | reliability | af11-af43 | cs0-cs7 | ef | le",
Examples: []string{"af21 cs1", "lowdelay throughput", "cs2"},
Default: "af21 cs1",
Category: "Connection",
},
"BindAddress": {
Field: "BindAddress",
Description: "Use specific source address for the connection. Useful for multi-homed hosts.",
Syntax: "address | hostname",
Examples: []string{"192.168.1.100", "localhost", "*"},
Default: "none",
Category: "Connection",
},
"BindInterface": {
Field: "BindInterface",
Description: "Use specific network interface for the connection. Useful for routing through specific NICs.",
Syntax: "interface_name",
Examples: []string{"eth0", "en0", "wlan0"},
Default: "none",
Since: "OpenSSH 7.7+",
Category: "Connection",
},
"AddressFamily": {
Field: "AddressFamily",
Description: "Limit connections to IPv4 or IPv6 addresses.",
Syntax: "any | inet | inet6",
Examples: []string{"any", "inet", "inet6"},
Default: "any",
Category: "Connection",
},
// Hostname Canonicalization
"CanonicalizeHostname": {
Field: "CanonicalizeHostname",
Description: "Controls whether to perform hostname canonicalization. Useful for shortening hostnames.",
Syntax: "yes | no | always",
Examples: []string{"yes", "no", "always"},
Default: "no",
Since: "OpenSSH 6.5+",
Category: "Connection",
},
"CanonicalDomains": {
Field: "CanonicalDomains",
Description: "Search domains for hostname canonicalization. SSH will try appending these domains.",
Syntax: "domain1[,domain2,...] ",
Examples: []string{"example.com", "internal.net,example.org"},
Default: "none",
Since: "OpenSSH 6.5+",
Category: "Connection",
},
"CanonicalizeFallbackLocal": {
Field: "CanonicalizeFallbackLocal",
Description: "Whether to fail if canonicalization fails. If yes, uses the original hostname.",
Syntax: "yes | no",
Examples: []string{"yes", "no"},
Default: "yes",
Since: "OpenSSH 6.5+",
Category: "Connection",
},
"CanonicalizeMaxDots": {
Field: "CanonicalizeMaxDots",
Description: "Maximum dots in hostname before disabling canonicalization.",
Syntax: "number",
Examples: []string{"1", "2", "0"},
Default: "1",
Since: "OpenSSH 6.5+",
Category: "Connection",
},
"CanonicalizePermittedCNAMEs": {
Field: "CanonicalizePermittedCNAMEs",
Description: "Rules for CNAME following during canonicalization.",
Syntax: "source:target[,source:target,...] ",
Examples: []string{"*.example.com:example.net", "*.internal:*.example.com"},
Default: "none",
Since: "OpenSSH 6.5+",
Category: "Connection",
},
"ForwardX11Trusted": {
Field: "ForwardX11Trusted",
Description: "Enable trusted X11 forwarding. Less secure but more compatible.",
Syntax: "yes | no",
Examples: []string{"yes", "no"},
Default: "no",
Category: "Forwarding",
},
"ClearAllForwardings": {
Field: "ClearAllForwardings",
Description: "Clear all port forwardings set in configuration files.",
Syntax: "yes | no",
Examples: []string{"yes", "no"},
Default: "no",
Category: "Forwarding",
},
"ExitOnForwardFailure": {
Field: "ExitOnForwardFailure",
Description: "Terminate connection if port forwarding fails.",
Syntax: "yes | no",
Examples: []string{"yes", "no"},
Default: "no",
Category: "Forwarding",
},
"GatewayPorts": {
Field: "GatewayPorts",
Description: "Allow remote hosts to connect to forwarded ports.",
Syntax: "yes | no | clientspecified",
Examples: []string{"no", "yes", "clientspecified"},
Default: "no",
Category: "Forwarding",
},
// Authentication fields
"KbdInteractiveAuthentication": {
Field: "KbdInteractiveAuthentication",
Description: "Enable keyboard-interactive authentication (e.g., for 2FA).",
Syntax: "yes | no",
Examples: []string{"yes", "no"},
Default: "yes",
Category: "Authentication",
},
"NumberOfPasswordPrompts": {
Field: "NumberOfPasswordPrompts",
Description: "Number of password prompts before giving up.",
Syntax: "number",
Examples: []string{"3", "1", "5"},
Default: "3",
Category: "Authentication",
},
"IdentityAgent": {
Field: "IdentityAgent",
Description: "Location of the authentication agent socket.",
Syntax: "path | SSH_AUTH_SOCK | none",
Examples: []string{"SSH_AUTH_SOCK", "~/.ssh/agent.sock", "none"},
Default: "SSH_AUTH_SOCK",
Since: "OpenSSH 7.3+",
Category: "Authentication",
},
"PubkeyAcceptedAlgorithms": {
Field: "PubkeyAcceptedAlgorithms",
Description: "Signature algorithms accepted for public key authentication.",
Syntax: "algorithm[,algorithm,...] ",
Examples: []string{"ssh-ed25519,ssh-rsa", "+ssh-rsa", "-ssh-dss"},
Default: "(all supported)",
Since: "OpenSSH 8.5+ (PubkeyAcceptedKeyTypes before)",
Category: "Authentication",
},
"HostbasedAcceptedAlgorithms": {
Field: "HostbasedAcceptedAlgorithms",
Description: "Signature algorithms accepted for host-based authentication.",
Syntax: "algorithm[,algorithm,...] ",
Examples: []string{"ssh-ed25519,ssh-rsa", "+ssh-rsa", "-ssh-dss"},
Default: "(all supported)",
Since: "OpenSSH 8.5+",
Category: "Authentication",
},
// Security fields
"CheckHostIP": {
Field: "CheckHostIP",
Description: "Check the host IP address in known_hosts file.",
Syntax: "yes | no",
Examples: []string{"yes", "no"},
Default: "no",
Category: "Security",
},
"FingerprintHash": {
Field: "FingerprintHash",
Description: "Hash algorithm for displaying key fingerprints.",
Syntax: "md5 | sha256",
Examples: []string{"sha256", "md5"},
Default: "sha256",
Since: "OpenSSH 6.8+",
Category: "Security",
},
"VerifyHostKeyDNS": {
Field: "VerifyHostKeyDNS",
Description: "Verify host keys using DNS SSHFP records.",
Syntax: "yes | no | ask",
Examples: []string{"no", "ask", "yes"},
Default: "no",
Category: "Security",
},
"UpdateHostKeys": {
Field: "UpdateHostKeys",
Description: "Update known_hosts automatically with new host keys.",
Syntax: "yes | no | ask",
Examples: []string{"no", "ask", "yes"},
Default: "no",
Since: "OpenSSH 6.8+",
Category: "Security",
},
"HashKnownHosts": {
Field: "HashKnownHosts",
Description: "Hash host names and addresses in known_hosts file.",
Syntax: "yes | no",
Examples: []string{"no", "yes"},
Default: "no",
Category: "Security",
},
"VisualHostKey": {
Field: "VisualHostKey",
Description: "Display ASCII art representation of the host key.",
Syntax: "yes | no",
Examples: []string{"no", "yes"},
Default: "no",
Category: "Security",
},
// Cryptography
"KexAlgorithms": {
Field: "KexAlgorithms",
Description: "Key exchange algorithms to use.",
Syntax: "algorithm[,algorithm,...] ",
Examples: []string{"curve25519-sha256", "+diffie-hellman-group14-sha256", "-ecdh-sha2-nistp256"},
Default: "(all supported)",
Category: "Cryptography",
},
// Command execution
"LocalCommand": {
Field: "LocalCommand",
Description: "Command to execute on local machine after connecting.",
Syntax: "command",
Examples: []string{"echo 'Connected to %h'", "notify-send 'SSH Connected'"},
Default: "none",
Category: "Command",
},
"PermitLocalCommand": {
Field: "PermitLocalCommand",
Description: "Allow LocalCommand execution.",
Syntax: "yes | no",
Examples: []string{"no", "yes"},
Default: "no",
Category: "Command",
},
"EscapeChar": {
Field: "EscapeChar",
Description: "Escape character for SSH session (~ by default). Set to 'none' to disable.",
Syntax: "char | none | ^char",
Examples: []string{"~", "^", "none"},
Default: "~",
Category: "Command",
},
// Environment
"SendEnv": {
Field: "SendEnv",
Description: "Environment variables to send to the server.",
Syntax: "variable[,variable,...] ",
Examples: []string{"LANG", "LC_*", "TERM", "LANG LC_* EDITOR"},
Default: "none",
Category: "Environment",
},
"SetEnv": {
Field: "SetEnv",
Description: "Set environment variables for the SSH session.",
Syntax: "VAR=value[,VAR=value,...] ",
Examples: []string{"FOO=bar", "DEBUG=1", "PATH=/custom/path:$PATH"},
Default: "none",
Since: "OpenSSH 7.8+",
Category: "Environment",
},
}
// GetFieldsByCategory returns all fields in a specific category
func GetFieldsByCategory(category string) []string {
// Pre-count to allocate correct capacity
count := 0
for _, help := range fieldHelpData {
if help.Category == category {
count++
}
}
fields := make([]string, 0, count)
for name, help := range fieldHelpData {
if help.Category == category {
fields = append(fields, name)
}
}
return fields
}
// GetAllCategories returns all available help categories
func GetAllCategories() []string {
categories := make(map[string]bool)
for _, help := range fieldHelpData {
categories[help.Category] = true
}
// Convert to slice with defined order
orderedCategories := []string{
"Basic", "Connection", "Forwarding", "Authentication",
"Multiplexing", "Keep-Alive", "Security", "Debugging",
}
var result []string
for _, cat := range orderedCategories {
if categories[cat] {
result = append(result, cat)
}
}
return result
}