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

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

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)
}