func TestMapPublicKeyFromUserfile(t *testing.T) { user := "******" buildWorkingDir([]string{user}, t) defer cleanupWorkdir(t) privateKey, _ := ssh.ParsePrivateKey(testdata.PEMBytes["rsa"]) publicKey := privateKey.PublicKey() privateKey2, _ := ssh.ParsePrivateKey(testdata.PEMBytes["dsa"]) _ = privateKey2 err := ioutil.WriteFile(UserKeyFile.realPath(user), testdata.PEMBytes["rsa"], 0777) if err != nil { t.Fatalf("cant create file: %v", err) } authKeys := ssh.MarshalAuthorizedKey(publicKey) err = ioutil.WriteFile(UserAuthorizedKeysFile.realPath(user), authKeys, 0777) if err != nil { t.Fatalf("cant create file: %v", err) } t.Logf("testing file too open") // UserAuthorizedKeysFile _, err = mapPublicKeyFromUserfile(stubConnMetadata{user}, publicKey) if err == nil { t.Fatalf("should return err when file too open") } err = os.Chmod(UserAuthorizedKeysFile.realPath(user), 0600) if err != nil { t.Fatalf("cant change file mode %v", err) } // UserKeyFile _, err = mapPublicKeyFromUserfile(stubConnMetadata{user}, publicKey) if err == nil { t.Fatalf("should return err when file too open") } err = os.Chmod(UserKeyFile.realPath(user), 0600) if err != nil { t.Fatalf("cant change file mode %v", err) } t.Logf("testing user not found") _, err = mapPublicKeyFromUserfile(stubConnMetadata{"nosuchuser"}, publicKey) if err == nil { t.Fatalf("should return err when mapping from nosuchuser") } t.Logf("testing mapping signer") signer, err := mapPublicKeyFromUserfile(stubConnMetadata{user}, privateKey.PublicKey()) if err != nil { t.Fatalf("error mapping key %v", err) } if !bytes.Equal(signer.PublicKey().Marshal(), privateKey.PublicKey().Marshal()) { t.Fatalf("id_rsa not the same") } t.Logf("testing not in UserAuthorizedKeysFile") authKeys = ssh.MarshalAuthorizedKey(privateKey2.PublicKey()) err = ioutil.WriteFile(UserAuthorizedKeysFile.realPath(user), authKeys, 0600) if err != nil { t.Fatalf("cant create file: %v", err) } signer, err = mapPublicKeyFromUserfile(stubConnMetadata{user}, privateKey.PublicKey()) if signer != nil { t.Fatalf("should not map private key when public key not in UserAuthorizedKeysFile") } }
func main() { initConfig() initTemplate() initLogger() showVersion() showHelpOrVersion() showConfig() // TODO make this pluggable piper := &ssh.SSHPiperConfig{ FindUpstream: findUpstreamFromUserfile, MapPublicKey: mapPublicKeyFromUserfile, } if config.Challenger != "" { ac, err := challenger.GetChallenger(config.Challenger) if err != nil { logger.Fatalln("failed to load challenger", err) } logger.Printf("using additional challenger %s", config.Challenger) piper.AdditionalChallenge = ac } privateBytes, err := ioutil.ReadFile(config.PiperKeyFile) if err != nil { logger.Fatalln(err) } private, err := ssh.ParsePrivateKey(privateBytes) if err != nil { logger.Fatalln(err) } piper.AddHostKey(private) listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.ListenAddr, config.Port)) if err != nil { logger.Fatalln("failed to listen for connection: %v", err) } defer listener.Close() logger.Printf("SSHPiperd started") for { c, err := listener.Accept() if err != nil { logger.Printf("failed to accept connection: %v", err) continue } logger.Printf("connection accepted: %v", c.RemoteAddr()) go func() { p, err := ssh.NewSSHPiperConn(c, piper) if err != nil { logger.Printf("connection from %v establishing failed reason: %v", c.RemoteAddr(), err) return } err = p.Wait() logger.Printf("connection from %v closed reason: %v", c.RemoteAddr(), err) }() } }
func mapPublicKeyFromUserfile(conn ssh.ConnMetadata, key ssh.PublicKey) (signer ssh.Signer, err error) { user := conn.User() if !checkUsername(user) { return nil, fmt.Errorf("downstream is not using a valid username") } defer func() { // print error when func exit if err != nil { logger.Printf("mapping private key error: %v, public key auth denied for [%v] from [%v]", err, user, conn.RemoteAddr()) } }() err = UserAuthorizedKeysFile.checkPerm(user) if err != nil { return nil, err } keydata := key.Marshal() var rest []byte rest, err = UserAuthorizedKeysFile.read(user) if err != nil { return nil, err } var authedPubkey ssh.PublicKey for len(rest) > 0 { authedPubkey, _, _, rest, err = ssh.ParseAuthorizedKey(rest) if err != nil { return nil, err } if bytes.Equal(authedPubkey.Marshal(), keydata) { err = UserKeyFile.checkPerm(user) if err != nil { return nil, err } var privateBytes []byte privateBytes, err = UserKeyFile.read(user) if err != nil { return nil, err } var private ssh.Signer private, err = ssh.ParsePrivateKey(privateBytes) if err != nil { return nil, err } // in log may see this twice, one is for query the other is real sign again logger.Printf("auth succ, using mapped private key [%v] for user [%v] from [%v]", UserKeyFile.realPath(user), user, conn.RemoteAddr()) return private, nil } } logger.Printf("public key auth failed user [%v] from [%v]", conn.User(), conn.RemoteAddr()) return nil, nil }