Skip to content
/ sshkrb5 Public

Golang library providing GSSAPI middleware for crypto/ssh

License

Notifications You must be signed in to change notification settings

bodgit/sshkrb5

Folders and files

NameName
Last commit message
Last commit date
Dec 12, 2024
Jul 29, 2024
Sep 19, 2023
Dec 12, 2024
Dec 12, 2024
Sep 20, 2023
Sep 20, 2023
Dec 12, 2024
Dec 12, 2024
Sep 27, 2023
Sep 19, 2023
Sep 19, 2023
Dec 14, 2024
Dec 14, 2024
Feb 28, 2024
Sep 27, 2023
Sep 20, 2023
Sep 19, 2023
Sep 20, 2023
Sep 20, 2023
Sep 27, 2023
Sep 27, 2023
Sep 20, 2023
Sep 19, 2023

Repository files navigation

GitHub release Build Status Coverage Status Go Report Card GoDoc Go version Go version

GSSAPI middleware for crypto/ssh

The github.com/bodgit/sshkrb5 package implements the GSSAPIClient & GSSAPIServer interfaces in golang.org/x/crypto/ssh.

On non-Windows platforms GSSAPI is supported through either github.com/jcmturner/gokrb5 or github.com/openshift/gssapi. On Windows, SSPI is supported using github.com/alexbrainman/sspi.

It has been tested successfully against OpenSSH.

Sample client:

package main

import (
	"net"
	"os"
	"os/user"

	"github.com/bodgit/sshkrb5"
	"golang.org/x/crypto/ssh"
)

func main() {
	hostname := os.Args[1]

	u, err := user.Current()
	if err != nil {
		panic(err)
	}

	gssapi, err := sshkrb5.NewClient()
	if err != nil {
		panic(err)
	}
	defer gssapi.Close()

	config := &ssh.ClientConfig{
		User: u.Username,
		Auth: []ssh.AuthMethod{
			ssh.GSSAPIWithMICAuthMethod(gssapi, hostname),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}

	client, err := ssh.Dial("tcp", net.JoinHostPort(hostname, "22"), config)
	if err != nil {
		panic(err)
	}
	defer client.Close()

	session, err := client.NewSession()
	if err != nil {
		panic(err)
	}
	defer session.Close()

	b, err := session.Output("whoami")
	if err != nil {
		panic(err)
	}
	os.Stdout.Write(b)
}

Sample server:

package main

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"net"

	"github.com/bodgit/sshkrb5"
	"golang.org/x/crypto/ssh"
)

func main() {
	key, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		panic(err)
	}

	buf := new(bytes.Buffer)
	if err := pem.Encode(buf, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}); err != nil {
		panic(err)
	}

	private, err := ssh.ParsePrivateKey(buf.Bytes())
	if err != nil {
		panic(err)
	}

	gssapi, err := sshkrb5.NewServer()
	if err != nil {
		panic(err)
	}
	defer gssapi.Close()

	config := &ssh.ServerConfig{
		GSSAPIWithMICConfig: &ssh.GSSAPIWithMICConfig{
			AllowLogin: func(c ssh.ConnMetadata, name string) (*ssh.Permissions, error) {
				return nil, nil
			},
			Server: gssapi,
		},
	}

	config.AddHostKey(private)

	listener, err := net.Listen("tcp", "0.0.0.0:22")
	if err != nil {
		panic(err)
	}
	defer listener.Close()

	go func() {
		for {
			conn, err := listener.Accept()
			if err != nil {
				continue
			}

			_, chans, reqs, err := ssh.NewServerConn(conn, config)
			if err != nil {
				continue
			}

			go ssh.DiscardRequests(reqs)
			go handleChannels(chans)
		}
	}()
}

func handleChannels(chans <-chan ssh.NewChannel) {
	for newChannel := range chans {
		go handleChannel(newChannel)
	}
}

func handleChannel(newChannel ssh.NewChannel) {
	if t := newChannel.ChannelType(); t != "session" {
		_ = newChannel.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t))

		return
	}

	_, requests, err := newChannel.Accept()
	if err != nil {
		return
	}

	go ssh.DiscardRequests(requests)
}