mirror of
https://github.com/tailscale/tailscale.git
synced 2024-12-12 03:04:40 +00:00
cmd/ssh-auth-none-demo: add SSH demo server w/ auth type none
Change-Id: Ief8b1d904a4304ddb72e76d52fa8c50871c704f6
This commit is contained in:
parent
393a229de9
commit
a3777f0dc8
163
cmd/ssh-auth-none-demo/ssh-auth-none-demo.go
Normal file
163
cmd/ssh-auth-none-demo/ssh-auth-none-demo.go
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
gossh "github.com/tailscale/golang-x-crypto/ssh"
|
||||||
|
"tailscale.com/tempfork/gliderlabs/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
// keyTypes are the SSH key types that we either try to read from the
|
||||||
|
// system's OpenSSH keys.
|
||||||
|
var keyTypes = []string{"rsa", "ecdsa", "ed25519"}
|
||||||
|
|
||||||
|
var (
|
||||||
|
addr = flag.String("addr", ":2222", "address to listen on")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
cacheDir, err := os.UserCacheDir()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
dir := filepath.Join(cacheDir, "ssh-auth-none-demo")
|
||||||
|
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
keys, err := getHostKeys(dir)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(keys) == 0 {
|
||||||
|
log.Fatal("no host keys")
|
||||||
|
}
|
||||||
|
|
||||||
|
srv := &ssh.Server{
|
||||||
|
Addr: *addr,
|
||||||
|
Version: "Tailscale",
|
||||||
|
Handler: handleSessionPostSSHAuth,
|
||||||
|
ServerConfigCallback: func(ctx ssh.Context) *gossh.ServerConfig {
|
||||||
|
return &gossh.ServerConfig{
|
||||||
|
ImplictAuthMethod: "tailscale",
|
||||||
|
NoClientAuth: true, // required for the NoClientAuthCallback to run
|
||||||
|
NoClientAuthCallback: func(gossh.ConnMetadata) (*gossh.Permissions, error) {
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
BannerCallback: func(cm gossh.ConnMetadata) string {
|
||||||
|
log.Printf("Got connection from user %q, %q from %v", cm.User(), cm.ClientVersion(), cm.RemoteAddr())
|
||||||
|
return fmt.Sprintf("# Banner for user %q, %q\n", cm.User(), cm.ClientVersion())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, signer := range keys {
|
||||||
|
srv.AddHostKey(signer)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Running on %s ...", srv.Addr)
|
||||||
|
if err := srv.ListenAndServe(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Printf("done")
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleSessionPostSSHAuth(s ssh.Session) {
|
||||||
|
log.Printf("Started session from user %q", s.User())
|
||||||
|
fmt.Fprintf(s, "Hello user %q, it worked.\n", s.User())
|
||||||
|
|
||||||
|
// Abort the session on Control-C or Control-D.
|
||||||
|
go func() {
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
for {
|
||||||
|
n, err := s.Read(buf)
|
||||||
|
for _, b := range buf[:n] {
|
||||||
|
if b <= 4 { // abort on Control-C (3) or Control-D (4)
|
||||||
|
io.WriteString(s, "bye\n")
|
||||||
|
s.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for i := 10; i > 0; i-- {
|
||||||
|
fmt.Fprintf(s, "%v ...\n", i)
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
s.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHostKeys(dir string) (ret []ssh.Signer, err error) {
|
||||||
|
for _, typ := range keyTypes {
|
||||||
|
hostKey, err := hostKeyFileOrCreate(dir, typ)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
signer, err := gossh.ParsePrivateKey(hostKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ret = append(ret, signer)
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func hostKeyFileOrCreate(keyDir, typ string) ([]byte, error) {
|
||||||
|
path := filepath.Join(keyDir, "ssh_host_"+typ+"_key")
|
||||||
|
v, err := ioutil.ReadFile(path)
|
||||||
|
if err == nil {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var priv any
|
||||||
|
switch typ {
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported key type %q", typ)
|
||||||
|
case "ed25519":
|
||||||
|
_, priv, err = ed25519.GenerateKey(rand.Reader)
|
||||||
|
case "ecdsa":
|
||||||
|
// curve is arbitrary. We pick whatever will at
|
||||||
|
// least pacify clients as the actual encryption
|
||||||
|
// doesn't matter: it's all over WireGuard anyway.
|
||||||
|
curve := elliptic.P256()
|
||||||
|
priv, err = ecdsa.GenerateKey(curve, rand.Reader)
|
||||||
|
case "rsa":
|
||||||
|
// keySize is arbitrary. We pick whatever will at
|
||||||
|
// least pacify clients as the actual encryption
|
||||||
|
// doesn't matter: it's all over WireGuard anyway.
|
||||||
|
const keySize = 2048
|
||||||
|
priv, err = rsa.GenerateKey(rand.Reader, keySize)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mk, err := x509.MarshalPKCS8PrivateKey(priv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pemGen := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: mk})
|
||||||
|
err = os.WriteFile(path, pemGen, 0700)
|
||||||
|
return pemGen, err
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user