sshconf

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 24, 2026 License: MIT Imports: 12 Imported by: 2

README

sshconf

Go Reference Go Report Card

A complete Go implementation of OpenSSH client configuration parsing, following the ssh_config(5) specification.

Why sshconf?

  • Complete Spec Compliance - Supports all standard SSH config options and directives
  • Match Directive - Full support for conditional configuration with Match exec, Match host, Match user, etc.
  • Proper Semantics - Correctly implements "first obtained value wins" for single-value options
  • Token Expansion - All tokens supported: %h, %p, %r, %u, %d, %L, %l, %n, %C
  • Include Support - Conditional includes inside Host/Match blocks
  • Zero Dependencies - Pure Go, only standard library for core parsing

Installation

go get github.com/cocotyty/sshconf

Quick Start

package main

import (
    "fmt"
    "github.com/cocotyty/sshconf"
)

func main() {
    // Parse SSH config file
    config, err := sshconf.ParseFile("~/.ssh/config")
    if err != nil {
        panic(err)
    }

    // Get fully resolved configuration for a host
    opts := config.GetHostConfig("myserver.example.com")

    fmt.Printf("Hostname: %s\n", opts.Hostname)
    fmt.Printf("User: %s\n", opts.User)
    fmt.Printf("Port: %d\n", opts.Port)
    fmt.Printf("Identity Files: %v\n", opts.IdentityFile)
}

Features

Core Parsing
  • ✅ All standard SSH configuration options
  • ✅ Host pattern matching with wildcards (*, ?)
  • ✅ Pattern negation (!pattern)
  • ✅ Case-insensitive keywords
  • ✅ Quoted values with spaces
  • ✅ Key=Value and Key Value syntax
Directives
Directive Status Notes
Host Pattern matching with wildcards
Match All criteria: all, canonical, final, exec, localnetwork, host, originalhost, tagged, user, localuser
Include Supports globs, conditional in Host/Match blocks
IgnoreUnknown Suppress warnings for unknown options
Tag For use with Match tagged
Token Expansion
Token Description
%% Literal %
%d Local user's home directory
%h Target hostname
%i Local user ID (UID)
%L Local hostname (first component)
%l Local hostname (full)
%n Original hostname
%p Port number
%r Remote username
%u Local username
%C Connection hash
Match Conditions
// Create match context with tags
ctx := sshconf.NewMatchContext("server.example.com", "server", "admin")
ctx = ctx.WithTags([]string{"production"})
ctx = ctx.WithCanonical(true)

// Get config with context
opts := config.GetConfigForHostWithCtx(ctx)

Supported Match criteria:

  • all - Always matches
  • canonical - Matches during canonicalized pass
  • final - Matches during final pass
  • exec command - Execute command, match if exit code is 0
  • localnetwork cidr,... - Match local network interfaces
  • host pattern,... - Match target hostname
  • originalhost pattern,... - Match original hostname
  • tagged tag,... - Match tags
  • user pattern,... - Match remote username
  • localuser pattern,... - Match local username

SSH Client Package

The ssh subpackage provides a ready-to-use SSH client:

import sshpkg "github.com/cocotyty/sshconf/ssh"

func main() {
    // Create config
    config, err := sshpkg.NewConfigFromFile("~/.ssh/config")
    if err != nil {
        panic(err)
    }

    // Connect (supports ProxyCommand and ProxyJump)
    client, err := config.Dial("myserver")
    if err != nil {
        panic(err)
    }
    defer client.Close()

    // Execute command
    session, err := client.Session()
    if err != nil {
        panic(err)
    }
    defer session.Close()

    output, err := session.Output("uptime")
    fmt.Println(string(output))
}

Configuration Semantics

SSH uses "for each parameter, the first obtained value will be used":

# ❌ Wrong - defaults won't apply
Host *
    User defaultuser
    Port 22

Host *.work.com
    Port 2222      # Ignored! Port already set by Host *

# ✅ Correct - put defaults last
Host *.work.com
    Port 2222
    User workuser

Host *
    User defaultuser
    Port 22
Option Types
Type Behavior Examples
Single-value First match wins Hostname, User, Port
List Accumulate from all matches IdentityFile, LocalForward
Boolean First explicit setting wins Compression, ForwardX11

API Reference

Config Methods
// Parse from different sources
config, err := sshconf.ParseFile("~/.ssh/config")
config, err := sshconf.ParseString(configString)
config, err := sshconf.Parse(reader)

// Get configuration for a host
opts := config.GetHostConfig("hostname")

// Get with context (for Match conditions)
ctx := sshconf.NewMatchContext("hostname", "originalhost", "user")
opts := config.GetConfigForHostWithCtx(ctx)

// Get with canonicalization
opts := config.GetHostConfigCanonicalized("hostname", "user", []string{"tags"})
SSHOptions Fields
type SSHOptions struct {
    Hostname           string
    Port               int
    User               string
    IdentityFile       []string
    ProxyCommand       string
    ProxyJump          []string
    ForwardAgent       string
    Compression        Tristate  // Unset, Yes, No
    // ... and 100+ more options
}

Project Structure

sshconf/
├── types.go           # Type definitions (Tristate, SSHOptions, etc.)
├── parser.go          # Configuration parsing
├── matcher.go         # Host matching and option merging
├── expand.go          # Token and path expansion
├── canonicalize.go    # Hostname canonicalization
├── ssh/               # SSH client package
│   └── client.go      # Dial, Session, ProxyCommand support
└── test_cases/        # Test configurations
    ├── basic/
    ├── advanced/
    └── real_world/

Testing

# Run all tests
go test ./...

# Run with coverage
go test -cover ./...

# Run specific test
go test -v -run TestMatchEvaluate ./...

Compatibility

  • Go 1.21+
  • Tested against OpenSSH 8.0+ configuration format
  • Compatible with Linux, macOS, and BSD

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing)
  3. Add tests for new functionality
  4. Ensure all tests pass (go test ./...)
  5. Commit changes (git commit -m 'Add amazing feature')
  6. Push to branch (git push origin feature/amazing)
  7. Open a Pull Request

License

MIT License

Acknowledgments

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	Hosts         []HostConfig
	GlobalOptions SSHOptions
}

func Parse

func Parse(r io.Reader) (*Config, error)

func ParseFile

func ParseFile(filename string) (*Config, error)

func ParseString

func ParseString(s string) (*Config, error)

func (*Config) GetConfigForHost

func (c *Config) GetConfigForHost(hostname string) SSHOptions

func (*Config) GetConfigForHostWithContext

func (c *Config) GetConfigForHostWithContext(hostname, originalHost, remoteUser, localUser string, isCanonical, isFinal bool) SSHOptions

func (*Config) GetConfigForHostWithCtx

func (c *Config) GetConfigForHostWithCtx(ctx *MatchContext) SSHOptions

func (*Config) GetHostConfig

func (c *Config) GetHostConfig(hostname string) SSHOptions

func (*Config) GetHostConfigCanonicalized

func (c *Config) GetHostConfigCanonicalized(hostname, remoteUser string, tags []string) SSHOptions

func (*Config) GetHostConfigWithCtx

func (c *Config) GetHostConfigWithCtx(ctx *MatchContext) SSHOptions

type HostConfig

type HostConfig struct {
	Pattern  string
	Patterns []Pattern
	Config   SSHOptions
}

func (*HostConfig) Matches

func (hc *HostConfig) Matches(hostname string) bool

func (*HostConfig) MatchesWithContext

func (hc *HostConfig) MatchesWithContext(hostname, originalHost, remoteUser, localUser string, isCanonical, isFinal bool) bool

func (*HostConfig) MatchesWithCtx

func (hc *HostConfig) MatchesWithCtx(ctx *MatchContext) bool

type MatchBlock

type MatchBlock struct {
	Conditions []MatchCondition
	Config     SSHOptions
}

type MatchCondition

type MatchCondition struct {
	Type          string
	Value         string
	Negated       bool
	Values        []string
	SubConditions []MatchCondition
}

type MatchContext

type MatchContext struct {
	Hostname     string
	OriginalHost string
	RemoteUser   string
	LocalUser    string
	IsCanonical  bool
	IsFinal      bool
	Tags         []string
	LocalAddrs   []net.Addr
}

func NewMatchContext

func NewMatchContext(hostname, originalHost, remoteUser string) *MatchContext

func (*MatchContext) WithCanonical

func (ctx *MatchContext) WithCanonical(isCanonical bool) *MatchContext

func (*MatchContext) WithFinal

func (ctx *MatchContext) WithFinal(isFinal bool) *MatchContext

func (*MatchContext) WithTags

func (ctx *MatchContext) WithTags(tags []string) *MatchContext

type MatchCriteria

type MatchCriteria string
const (
	MatchCanonical    MatchCriteria = "canonical"
	MatchFinal        MatchCriteria = "final"
	MatchExec         MatchCriteria = "exec"
	MatchLocalNetwork MatchCriteria = "localnetwork"
	MatchHost         MatchCriteria = "host"
	MatchOriginalHost MatchCriteria = "originalhost"
	MatchTagged       MatchCriteria = "tagged"
	MatchUser         MatchCriteria = "user"
	MatchLocalUser    MatchCriteria = "localuser"
	MatchAll          MatchCriteria = "all"
)

type Pattern

type Pattern struct {
	Value   string
	Negated bool
}

type SSHOptions

type SSHOptions struct {
	Hostname           string
	Port               int
	User               string
	AddressFamily      string
	BindAddress        string
	BindInterface      string
	ConnectTimeout     int
	ConnectionAttempts int

	PasswordAuthentication          Tristate
	PubkeyAuthentication            Tristate
	HostbasedAuthentication         Tristate
	GSSAPIAuthentication            Tristate
	KbdInteractiveAuthentication    Tristate
	ChallengeResponseAuthentication Tristate
	PreferredAuthentications        string
	IdentitiesOnly                  Tristate
	IdentityFile                    []string
	CertificateFile                 []string
	AddKeysToAgent                  string
	IdentityAgent                   string

	HostKeyAlgorithms     string
	HostKeyAlias          string
	StrictHostKeyChecking string
	UserKnownHostsFile    []string
	GlobalKnownHostsFile  []string
	CheckHostIP           Tristate
	HashKnownHosts        Tristate
	VerifyHostKeyDNS      string
	KnownHostsCommand     string

	ForwardAgent         string
	ForwardX11           Tristate
	ForwardX11Trusted    Tristate
	ForwardX11Timeout    string
	LocalForward         []string
	RemoteForward        []string
	DynamicForward       []string
	GatewayPorts         Tristate
	ExitOnForwardFailure Tristate
	ClearAllForwardings  Tristate

	ControlMaster  string
	ControlPath    string
	ControlPersist string

	ServerAliveInterval int
	ServerAliveCountMax int
	TCPKeepAlive        Tristate
	LogLevel            string
	LogVerbose          string
	Compression         Tristate
	BatchMode           Tristate
	RequestTTY          string
	RemoteCommand       string
	LocalCommand        string
	PermitLocalCommand  Tristate
	SendEnv             []string
	SetEnv              map[string]string
	SendEnvPatterns     []string

	ProxyCommand string
	ProxyJump    []string

	Ciphers                     string
	MACs                        string
	KexAlgorithms               string
	CASignatureAlgorithms       string
	HostbasedAcceptedAlgorithms string
	PubkeyAcceptedAlgorithms    string

	RekeyLimit string

	IdentityAgentEnv string

	UpdateHostKeys string

	Tunnel       string
	TunnelDevice string

	EscapeChar              string
	EnableEscapeCommandline Tristate

	CanonicalizeHostname        string
	CanonicalDomains            []string
	CanonicalizeMaxDots         int
	CanonicalizeFallbackLocal   Tristate
	CanonicalizePermittedCNAMEs []string

	Include []string

	IgnoreUnknown []string

	MatchConditions []MatchCondition
	IsMatchBlock    bool
	Tag             string

	UnknownOptions map[string][]string
	// contains filtered or unexported fields
}

type Tristate

type Tristate int
const (
	Unset Tristate = iota
	No
	Yes
)

func ParseTristate

func ParseTristate(s string) Tristate

func (Tristate) Bool

func (t Tristate) Bool() bool

func (Tristate) IsSet

func (t Tristate) IsSet() bool

func (Tristate) String

func (t Tristate) String() string

Directories

Path Synopsis
ssh module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL