Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cli: add -version switch to display software version #91

Merged
merged 2 commits into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ func Dial(ctx context.Context, options *Options, qconn quic.EarlyConnection,
log.Fatal().Msgf("%s", err)
}
req.Proto = "ssh3"
req.Header.Set("User-Agent", ssh3.GetCurrentVersion())
req.Header.Set("User-Agent", ssh3.GetCurrentVersionString())

var identity ssh3.Identity
for _, method := range options.authMethods {
Expand Down
9 changes: 9 additions & 0 deletions cmd/ssh3-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"os"
"os/exec"
"path"
"path/filepath"
"sync"
"syscall"
"unsafe"
Expand Down Expand Up @@ -671,6 +672,7 @@ func fileExists(path string) bool {
func main() {
bindAddr := flag.String("bind", "[::]:443", "the address:port pair to listen to, e.g. 0.0.0.0:443")
verbose := flag.Bool("v", false, "verbose mode, if set")
displayVersion := flag.Bool("version", false, "if set, displays the software version on standard output and exit")
urlPath := flag.String("url-path", "/ssh3-term", "the secret URL path on which the ssh3 server listens")
generateSelfSignedCert := flag.Bool("generate-selfsigned-cert", false, "if set, generates a self-self-signed cerificate and key "+
"that will be stored at the paths indicated by the -cert and -key args (they must not already exist)")
Expand All @@ -682,6 +684,11 @@ func main() {
}
flag.Parse()

if *displayVersion {
fmt.Fprintln(os.Stdout, filepath.Base(os.Args[0]), "version", ssh3.GetCurrentSoftwareVersion())
os.Exit(0)
}

if !enablePasswordLogin {
fmt.Fprintln(os.Stderr, "password login is disabled")
}
Expand Down Expand Up @@ -749,6 +756,8 @@ func main() {
log.Logger = log.Output(logFile)
}

log.Debug().Msgf("version %s", ssh3.GetCurrentSoftwareVersion())

quicConf := &quic.Config{
Allow0RTT: true,
}
Expand Down
15 changes: 15 additions & 0 deletions cmd/ssh3/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"os"
osuser "os/user"
"path"
"path/filepath"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -266,13 +267,19 @@ func mainWithStatusCode() int {
issuerUrl := flag.String("use-oidc", "", "if set, force the use of OpenID Connect with the specified issuer url as parameter (it opens a browser window)")
oidcConfigFileName := flag.String("oidc-config", "", "OpenID Connect json config file containing the \"client_id\" and \"client_secret\" fields needed for most identity providers")
verbose := flag.Bool("v", false, "if set, enable verbose mode")
displayVersion := flag.Bool("version", false, "if set, displays the software version on standard output and exit")
doPKCE := flag.Bool("do-pkce", false, "if set perform PKCE challenge-response with oidc")
forwardSSHAgent := flag.Bool("forward-agent", false, "if set, forwards ssh agent to be used with sshv2 connections on the remote host")
forwardUDP := flag.String("forward-udp", "", "if set, take a localport/remoteip@remoteport forwarding localhost@localport towards remoteip@remoteport")
forwardTCP := flag.String("forward-tcp", "", "if set, take a localport/remoteip@remoteport forwarding localhost@localport towards remoteip@remoteport")
flag.Parse()
args := flag.Args()

if *displayVersion {
fmt.Fprintln(os.Stdout, filepath.Base(os.Args[0]), "version", ssh3.GetCurrentSoftwareVersion())
return 0
}

useOIDC := *issuerUrl != ""

ssh3Dir := path.Join(homedir(), ".ssh3")
Expand All @@ -285,6 +292,14 @@ func mainWithStatusCode() int {
util.ConfigureLogger(os.Getenv("SSH3_LOG_LEVEL"))
}

if len(args) == 0 {
log.Error().Msgf("no remote host specified, exit")
flag.Usage()
os.Exit(-1)
}

log.Debug().Msgf("version %s", ssh3.GetCurrentSoftwareVersion())

knownHostsPath := path.Join(ssh3Dir, "known_hosts")
knownHosts, skippedLines, err := ssh3.ParseKnownHosts(knownHostsPath)
if len(skippedLines) != 0 {
Expand Down
2 changes: 1 addition & 1 deletion conversation.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func (c *Conversation) EstablishClientConversation(req *http.Request, roundTripp
}

serverVersion := rsp.Header.Get("Server")
major, minor, patch, err := ParseVersion(serverVersion)
major, minor, patch, err := ParseVersionString(serverVersion)
if err != nil {
log.Error().Msgf("Could not parse server version: \"%s\"", serverVersion)
if rsp.StatusCode == 200 {
Expand Down
6 changes: 3 additions & 3 deletions unix_server/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ func HandleAuths(ctx context.Context, enablePasswordLogin bool, defaultMaxPacket
}
return func(w http.ResponseWriter, r *http.Request) {
defer w.(http.Flusher).Flush()
w.Header().Set("Server", ssh3.GetCurrentVersion())
major, minor, patch, err := ssh3.ParseVersion(r.UserAgent())
w.Header().Set("Server", ssh3.GetCurrentVersionString())
major, minor, patch, err := ssh3.ParseVersionString(r.UserAgent())
log.Debug().Msgf("received request from User-Agent %s (major %d, minor %d, patch %d)", r.UserAgent(), major, minor, patch)
// currently apply strict version rules
if err != nil || major != ssh3.MAJOR || minor != ssh3.MINOR {
w.WriteHeader(http.StatusForbidden)
if err == nil {
w.Write([]byte(fmt.Sprintf("Unsupported version: %d.%d.%d not supported by server in version %s", major, minor, patch, ssh3.GetCurrentVersion())))
w.Write([]byte(fmt.Sprintf("Unsupported version: %d.%d.%d not supported by server in version %s", major, minor, patch, ssh3.GetCurrentVersionString())))
} else {
w.Write([]byte("Unsupported user-agent"))
}
Expand Down
21 changes: 17 additions & 4 deletions version.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import (

const MAJOR int = 0
const MINOR int = 1
const PATCH int = 3
const PATCH int = 5
const RC int = 3

type InvalidSSHVersion struct {
versionString string
Expand All @@ -28,13 +29,15 @@ func (e UnsupportedSSHVersion) Error() string {
return fmt.Sprintf("unsupported ssh version: %s", e.versionString)
}

func GetCurrentVersion() string {
// GetCurrentVersionString() returns the version string to be exchanged between two
// endpoints for version negotiation
func GetCurrentVersionString() string {
return fmt.Sprintf("SSH 3.0 francoismichel/ssh3 %d.%d.%d", MAJOR, MINOR, PATCH)
}

func ParseVersion(version string) (major int, minor int, patch int, err error) {
func ParseVersionString(version string) (major int, minor int, patch int, err error) {
fields := strings.Fields(version)
if len(fields) != 4 || fields[0] != "SSH" || fields[1] != "3.0" {
if len(fields) < 4 || fields[0] != "SSH" || fields[1] != "3.0" {
log.Error().Msgf("bad SSH version fields")
return 0, 0, 0, InvalidSSHVersion{versionString: version}
}
Expand All @@ -60,3 +63,13 @@ func ParseVersion(version string) (major int, minor int, patch int, err error) {
}
return major, minor, patch, nil
}

// GetCurrentSoftwareVersion() returns the current software version to be displayed to the user
// For version string to be communicated between endpoints, use GetCurrentVersionString() instead.
func GetCurrentSoftwareVersion() string {
versionStr := fmt.Sprintf("%d.%d.%d", MAJOR, MINOR, PATCH)
if RC > 0 {
versionStr += fmt.Sprintf("-rc%d", RC)
}
return versionStr
}