libplugin
This commit is contained in:
parent
b49669bdae
commit
dbef31dd3d
18 changed files with 5250 additions and 4 deletions
3
libplugin/doc.go
Normal file
3
libplugin/doc.go
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative plugin.proto
|
||||
|
||||
package libplugin
|
||||
48
libplugin/ioconn/cmd.go
Normal file
48
libplugin/ioconn/cmd.go
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
package ioconn
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
type cmdconn struct {
|
||||
conn
|
||||
cmd *exec.Cmd
|
||||
}
|
||||
|
||||
func (c *cmdconn) Close() error {
|
||||
err := c.conn.Close()
|
||||
|
||||
if c.cmd.Process != nil {
|
||||
return c.cmd.Process.Kill()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func DialCmd(cmd *exec.Cmd) (net.Conn, io.ReadCloser, error) {
|
||||
in, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
out, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &cmdconn{
|
||||
conn: *dial(in, out),
|
||||
cmd: cmd,
|
||||
}, stderr, nil
|
||||
}
|
||||
113
libplugin/ioconn/conn.go
Normal file
113
libplugin/ioconn/conn.go
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
package ioconn
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type addr string
|
||||
|
||||
func (a addr) Network() string {
|
||||
return "ioconn"
|
||||
}
|
||||
|
||||
func (a addr) String() string {
|
||||
return string(a)
|
||||
}
|
||||
|
||||
type conn struct {
|
||||
in io.ReadCloser
|
||||
out io.WriteCloser
|
||||
}
|
||||
|
||||
func Dial(in io.ReadCloser, out io.WriteCloser) (net.Conn, error) {
|
||||
return dial(in, out), nil
|
||||
}
|
||||
|
||||
func dial(in io.ReadCloser, out io.WriteCloser) *conn {
|
||||
return &conn{in, out}
|
||||
}
|
||||
|
||||
// Read reads data from the connection.
|
||||
// Read can be made to time out and return an error after a fixed
|
||||
// time limit; see SetDeadline and SetReadDeadline.
|
||||
func (c *conn) Read(b []byte) (n int, err error) {
|
||||
return c.in.Read(b)
|
||||
}
|
||||
|
||||
// Write writes data to the connection.
|
||||
// Write can be made to time out and return an error after a fixed
|
||||
// time limit; see SetDeadline and SetWriteDeadline.
|
||||
func (c *conn) Write(b []byte) (n int, err error) {
|
||||
return c.out.Write(b)
|
||||
}
|
||||
|
||||
// Close closes the connection.
|
||||
// Any blocked Read or Write operations will be unblocked and return errors.
|
||||
func (c *conn) Close() error {
|
||||
inerr := c.in.Close()
|
||||
outerr := c.out.Close()
|
||||
|
||||
if inerr == nil {
|
||||
return outerr
|
||||
}
|
||||
|
||||
if outerr == nil {
|
||||
return outerr
|
||||
}
|
||||
|
||||
return fmt.Errorf("io close error in: %v, out: %v", inerr, outerr)
|
||||
}
|
||||
|
||||
// LocalAddr returns the local network address, if known.
|
||||
func (c *conn) LocalAddr() net.Addr {
|
||||
return addr("ioconn:local")
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote network address, if known.
|
||||
func (c *conn) RemoteAddr() net.Addr {
|
||||
return addr("ioconn:remote")
|
||||
}
|
||||
|
||||
// SetDeadline sets the read and write deadlines associated
|
||||
// with the connection. It is equivalent to calling both
|
||||
// SetReadDeadline and SetWriteDeadline.
|
||||
//
|
||||
// A deadline is an absolute time after which I/O operations
|
||||
// fail instead of blocking. The deadline applies to all future
|
||||
// and pending I/O, not just the immediately following call to
|
||||
// Read or Write. After a deadline has been exceeded, the
|
||||
// connection can be refreshed by setting a deadline in the future.
|
||||
//
|
||||
// If the deadline is exceeded a call to Read or Write or to other
|
||||
// I/O methods will return an error that wraps os.ErrDeadlineExceeded.
|
||||
// This can be tested using errors.Is(err, os.ErrDeadlineExceeded).
|
||||
// The error's Timeout method will return true, but note that there
|
||||
// are other possible errors for which the Timeout method will
|
||||
// return true even if the deadline has not been exceeded.
|
||||
//
|
||||
// An idle timeout can be implemented by repeatedly extending
|
||||
// the deadline after successful Read or Write calls.
|
||||
//
|
||||
// A zero value for t means I/O operations will not time out.
|
||||
func (c *conn) SetDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetReadDeadline sets the deadline for future Read calls
|
||||
// and any currently-blocked Read call.
|
||||
// A zero value for t means Read will not time out.
|
||||
func (c *conn) SetReadDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetWriteDeadline sets the deadline for future Write calls
|
||||
// and any currently-blocked Write call.
|
||||
// Even if write times out, it may return n > 0, indicating that
|
||||
// some of the data was successfully written.
|
||||
// A zero value for t means Write will not time out.
|
||||
func (c *conn) SetWriteDeadline(t time.Time) error {
|
||||
return nil
|
||||
}
|
||||
37
libplugin/ioconn/listener.go
Normal file
37
libplugin/ioconn/listener.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package ioconn
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
type singleConnListener struct {
|
||||
conn
|
||||
used chan int
|
||||
}
|
||||
|
||||
// Accept implements net.Listener
|
||||
func (l *singleConnListener) Accept() (net.Conn, error) {
|
||||
<-l.used
|
||||
return &l.conn, nil
|
||||
}
|
||||
|
||||
// Addr implements net.Listener
|
||||
func (l *singleConnListener) Addr() net.Addr {
|
||||
return l.conn.LocalAddr()
|
||||
}
|
||||
|
||||
// Close implements net.Listener
|
||||
func (l *singleConnListener) Close() error {
|
||||
return l.conn.Close()
|
||||
}
|
||||
|
||||
func ListenFromSingleIO(in io.ReadCloser, out io.WriteCloser) (net.Listener, error) {
|
||||
l := &singleConnListener{
|
||||
conn{in, out},
|
||||
make(chan int, 1),
|
||||
}
|
||||
|
||||
l.used <- 1 // ready for accept
|
||||
return l, nil
|
||||
}
|
||||
2746
libplugin/plugin.pb.go
Normal file
2746
libplugin/plugin.pb.go
Normal file
File diff suppressed because it is too large
Load diff
189
libplugin/plugin.proto
Normal file
189
libplugin/plugin.proto
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package libplugin;
|
||||
|
||||
option go_package = "github.com/tg123/sshpiper/libplugin";
|
||||
|
||||
message ConnMeta {
|
||||
string user_name = 1;
|
||||
string from_addr = 2;
|
||||
string uniq_id = 3;
|
||||
}
|
||||
|
||||
message Upstream {
|
||||
string host = 1;
|
||||
int32 port = 2;
|
||||
string user_name = 3;
|
||||
bool ignore_host_key = 4;
|
||||
|
||||
oneof auth {
|
||||
UpstreamNoneAuth none = 100;
|
||||
UpstreamPasswordAuth password = 101;
|
||||
UpstreamPrivateKeyAuth private_key = 102;
|
||||
UpstreamRemoteSignerAuth remote_signer = 103;
|
||||
UpstreamNextPluginAuth next_plugin = 200;
|
||||
}
|
||||
}
|
||||
|
||||
message UpstreamNoneAuth {
|
||||
|
||||
}
|
||||
|
||||
message UpstreamPasswordAuth {
|
||||
string password = 1;
|
||||
}
|
||||
|
||||
message UpstreamPrivateKeyAuth {
|
||||
bytes private_key = 1;
|
||||
}
|
||||
|
||||
message UpstreamRemoteSignerAuth{
|
||||
string meta = 1;
|
||||
}
|
||||
|
||||
message UpstreamNextPluginAuth {
|
||||
map<string, string> meta = 1;
|
||||
}
|
||||
|
||||
service SshPiperPlugin {
|
||||
rpc Logs(StartLogRequest) returns (stream Log) {}
|
||||
rpc ListCallbacks(ListCallbackRequest) returns (ListCallbackResponse) {}
|
||||
|
||||
rpc NewConnection(NewConnectionRequest) returns (NewConnectionResponse) {};
|
||||
rpc NextAuthMethods(NextAuthMethodsRequest) returns (NextAuthMethodsResponse) {};
|
||||
rpc NoneAuth(NoneAuthRequest) returns (NoneAuthResponse) {};
|
||||
rpc PasswordAuth(PasswordAuthRequest) returns (PasswordAuthResponse) {};
|
||||
rpc PublicKeyAuth(PublicKeyAuthRequest) returns (PublicKeyAuthResponse) {};
|
||||
rpc KeyboardInteractiveAuth(stream KeyboardInteractiveAuthMessage) returns (stream KeyboardInteractiveAuthMessage);
|
||||
rpc UpstreamAuthFailureNotice(UpstreamAuthFailureNoticeRequest) returns (UpstreamAuthFailureNoticeResponse) {};
|
||||
rpc Banner(BannerRequest) returns (BannerResponse) {};
|
||||
rpc VerifyHostKey (VerifyHostKeyRequest) returns (VerifyHostKeyReply) {}
|
||||
}
|
||||
|
||||
message StartLogRequest {
|
||||
string uniq_id = 1;
|
||||
string level = 2;
|
||||
|
||||
}
|
||||
|
||||
message Log {
|
||||
string message = 1;
|
||||
}
|
||||
|
||||
message ListCallbackRequest {
|
||||
}
|
||||
|
||||
message ListCallbackResponse {
|
||||
repeated string callbacks = 1;
|
||||
}
|
||||
|
||||
message NewConnectionRequest {
|
||||
ConnMeta meta = 1;
|
||||
}
|
||||
|
||||
message NewConnectionResponse {
|
||||
}
|
||||
|
||||
message NextAuthMethodsRequest {
|
||||
ConnMeta meta = 1;
|
||||
}
|
||||
|
||||
enum AuthMethod {
|
||||
NONE = 0;
|
||||
PASSWORD = 1;
|
||||
PUBLICKEY = 2;
|
||||
KEYBOARD_INTERACTIVE = 3;
|
||||
}
|
||||
|
||||
message NextAuthMethodsResponse {
|
||||
repeated AuthMethod methods = 1;
|
||||
}
|
||||
|
||||
message NoneAuthRequest {
|
||||
ConnMeta meta = 1;
|
||||
}
|
||||
|
||||
message NoneAuthResponse {
|
||||
Upstream upstream = 1;
|
||||
}
|
||||
|
||||
message PasswordAuthRequest {
|
||||
ConnMeta meta = 1;
|
||||
bytes password = 2;
|
||||
}
|
||||
|
||||
message PasswordAuthResponse {
|
||||
Upstream upstream = 1;
|
||||
}
|
||||
|
||||
message PublicKeyAuthRequest {
|
||||
ConnMeta meta = 1;
|
||||
bytes public_key = 2;
|
||||
}
|
||||
|
||||
message PublicKeyAuthResponse {
|
||||
Upstream upstream = 1;
|
||||
}
|
||||
|
||||
message KeyboardInteractiveUserResponse {
|
||||
repeated string answers = 1;
|
||||
}
|
||||
|
||||
message KeyboardInteractivePromptRequest {
|
||||
message Question{
|
||||
string text = 1;
|
||||
bool echo = 2;
|
||||
}
|
||||
|
||||
string name = 1;
|
||||
string instruction = 2;
|
||||
repeated Question questions = 3;
|
||||
}
|
||||
|
||||
message KeyboardInteractiveMetaRequest {
|
||||
}
|
||||
|
||||
message KeyboardInteractiveMetaResponse {
|
||||
ConnMeta meta = 1;
|
||||
}
|
||||
|
||||
message KeyboardInteractiveFinishRequest {
|
||||
Upstream upstream = 1;
|
||||
}
|
||||
|
||||
message KeyboardInteractiveAuthMessage {
|
||||
oneof message {
|
||||
KeyboardInteractivePromptRequest prompt_request = 1;
|
||||
KeyboardInteractiveUserResponse user_response = 2;
|
||||
KeyboardInteractiveMetaRequest meta_request = 3;
|
||||
KeyboardInteractiveMetaResponse meta_response = 4;
|
||||
KeyboardInteractiveFinishRequest finish_request = 5;
|
||||
}
|
||||
}
|
||||
|
||||
message UpstreamAuthFailureNoticeRequest {
|
||||
ConnMeta meta = 1;
|
||||
string method = 2;
|
||||
string error = 3;
|
||||
repeated AuthMethod allowed_methods = 4;
|
||||
}
|
||||
|
||||
message UpstreamAuthFailureNoticeResponse {
|
||||
}
|
||||
|
||||
message BannerRequest {
|
||||
ConnMeta meta = 1;
|
||||
}
|
||||
|
||||
message BannerResponse {
|
||||
string message = 1;
|
||||
}
|
||||
|
||||
message VerifyHostKeyRequest {
|
||||
ConnMeta meta = 1;
|
||||
bytes key = 2;
|
||||
}
|
||||
|
||||
message VerifyHostKeyReply {
|
||||
bool verified = 1;
|
||||
}
|
||||
521
libplugin/plugin_grpc.pb.go
Normal file
521
libplugin/plugin_grpc.pb.go
Normal file
|
|
@ -0,0 +1,521 @@
|
|||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
|
||||
package libplugin
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// SshPiperPluginClient is the client API for SshPiperPlugin service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type SshPiperPluginClient interface {
|
||||
Logs(ctx context.Context, in *StartLogRequest, opts ...grpc.CallOption) (SshPiperPlugin_LogsClient, error)
|
||||
ListCallbacks(ctx context.Context, in *ListCallbackRequest, opts ...grpc.CallOption) (*ListCallbackResponse, error)
|
||||
NewConnection(ctx context.Context, in *NewConnectionRequest, opts ...grpc.CallOption) (*NewConnectionResponse, error)
|
||||
NextAuthMethods(ctx context.Context, in *NextAuthMethodsRequest, opts ...grpc.CallOption) (*NextAuthMethodsResponse, error)
|
||||
NoneAuth(ctx context.Context, in *NoneAuthRequest, opts ...grpc.CallOption) (*NoneAuthResponse, error)
|
||||
PasswordAuth(ctx context.Context, in *PasswordAuthRequest, opts ...grpc.CallOption) (*PasswordAuthResponse, error)
|
||||
PublicKeyAuth(ctx context.Context, in *PublicKeyAuthRequest, opts ...grpc.CallOption) (*PublicKeyAuthResponse, error)
|
||||
KeyboardInteractiveAuth(ctx context.Context, opts ...grpc.CallOption) (SshPiperPlugin_KeyboardInteractiveAuthClient, error)
|
||||
UpstreamAuthFailureNotice(ctx context.Context, in *UpstreamAuthFailureNoticeRequest, opts ...grpc.CallOption) (*UpstreamAuthFailureNoticeResponse, error)
|
||||
Banner(ctx context.Context, in *BannerRequest, opts ...grpc.CallOption) (*BannerResponse, error)
|
||||
VerifyHostKey(ctx context.Context, in *VerifyHostKeyRequest, opts ...grpc.CallOption) (*VerifyHostKeyReply, error)
|
||||
}
|
||||
|
||||
type sshPiperPluginClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewSshPiperPluginClient(cc grpc.ClientConnInterface) SshPiperPluginClient {
|
||||
return &sshPiperPluginClient{cc}
|
||||
}
|
||||
|
||||
func (c *sshPiperPluginClient) Logs(ctx context.Context, in *StartLogRequest, opts ...grpc.CallOption) (SshPiperPlugin_LogsClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &SshPiperPlugin_ServiceDesc.Streams[0], "/libplugin.SshPiperPlugin/Logs", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &sshPiperPluginLogsClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type SshPiperPlugin_LogsClient interface {
|
||||
Recv() (*Log, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type sshPiperPluginLogsClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *sshPiperPluginLogsClient) Recv() (*Log, error) {
|
||||
m := new(Log)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *sshPiperPluginClient) ListCallbacks(ctx context.Context, in *ListCallbackRequest, opts ...grpc.CallOption) (*ListCallbackResponse, error) {
|
||||
out := new(ListCallbackResponse)
|
||||
err := c.cc.Invoke(ctx, "/libplugin.SshPiperPlugin/ListCallbacks", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sshPiperPluginClient) NewConnection(ctx context.Context, in *NewConnectionRequest, opts ...grpc.CallOption) (*NewConnectionResponse, error) {
|
||||
out := new(NewConnectionResponse)
|
||||
err := c.cc.Invoke(ctx, "/libplugin.SshPiperPlugin/NewConnection", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sshPiperPluginClient) NextAuthMethods(ctx context.Context, in *NextAuthMethodsRequest, opts ...grpc.CallOption) (*NextAuthMethodsResponse, error) {
|
||||
out := new(NextAuthMethodsResponse)
|
||||
err := c.cc.Invoke(ctx, "/libplugin.SshPiperPlugin/NextAuthMethods", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sshPiperPluginClient) NoneAuth(ctx context.Context, in *NoneAuthRequest, opts ...grpc.CallOption) (*NoneAuthResponse, error) {
|
||||
out := new(NoneAuthResponse)
|
||||
err := c.cc.Invoke(ctx, "/libplugin.SshPiperPlugin/NoneAuth", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sshPiperPluginClient) PasswordAuth(ctx context.Context, in *PasswordAuthRequest, opts ...grpc.CallOption) (*PasswordAuthResponse, error) {
|
||||
out := new(PasswordAuthResponse)
|
||||
err := c.cc.Invoke(ctx, "/libplugin.SshPiperPlugin/PasswordAuth", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sshPiperPluginClient) PublicKeyAuth(ctx context.Context, in *PublicKeyAuthRequest, opts ...grpc.CallOption) (*PublicKeyAuthResponse, error) {
|
||||
out := new(PublicKeyAuthResponse)
|
||||
err := c.cc.Invoke(ctx, "/libplugin.SshPiperPlugin/PublicKeyAuth", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sshPiperPluginClient) KeyboardInteractiveAuth(ctx context.Context, opts ...grpc.CallOption) (SshPiperPlugin_KeyboardInteractiveAuthClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &SshPiperPlugin_ServiceDesc.Streams[1], "/libplugin.SshPiperPlugin/KeyboardInteractiveAuth", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &sshPiperPluginKeyboardInteractiveAuthClient{stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type SshPiperPlugin_KeyboardInteractiveAuthClient interface {
|
||||
Send(*KeyboardInteractiveAuthMessage) error
|
||||
Recv() (*KeyboardInteractiveAuthMessage, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type sshPiperPluginKeyboardInteractiveAuthClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *sshPiperPluginKeyboardInteractiveAuthClient) Send(m *KeyboardInteractiveAuthMessage) error {
|
||||
return x.ClientStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *sshPiperPluginKeyboardInteractiveAuthClient) Recv() (*KeyboardInteractiveAuthMessage, error) {
|
||||
m := new(KeyboardInteractiveAuthMessage)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *sshPiperPluginClient) UpstreamAuthFailureNotice(ctx context.Context, in *UpstreamAuthFailureNoticeRequest, opts ...grpc.CallOption) (*UpstreamAuthFailureNoticeResponse, error) {
|
||||
out := new(UpstreamAuthFailureNoticeResponse)
|
||||
err := c.cc.Invoke(ctx, "/libplugin.SshPiperPlugin/UpstreamAuthFailureNotice", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sshPiperPluginClient) Banner(ctx context.Context, in *BannerRequest, opts ...grpc.CallOption) (*BannerResponse, error) {
|
||||
out := new(BannerResponse)
|
||||
err := c.cc.Invoke(ctx, "/libplugin.SshPiperPlugin/Banner", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *sshPiperPluginClient) VerifyHostKey(ctx context.Context, in *VerifyHostKeyRequest, opts ...grpc.CallOption) (*VerifyHostKeyReply, error) {
|
||||
out := new(VerifyHostKeyReply)
|
||||
err := c.cc.Invoke(ctx, "/libplugin.SshPiperPlugin/VerifyHostKey", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// SshPiperPluginServer is the server API for SshPiperPlugin service.
|
||||
// All implementations must embed UnimplementedSshPiperPluginServer
|
||||
// for forward compatibility
|
||||
type SshPiperPluginServer interface {
|
||||
Logs(*StartLogRequest, SshPiperPlugin_LogsServer) error
|
||||
ListCallbacks(context.Context, *ListCallbackRequest) (*ListCallbackResponse, error)
|
||||
NewConnection(context.Context, *NewConnectionRequest) (*NewConnectionResponse, error)
|
||||
NextAuthMethods(context.Context, *NextAuthMethodsRequest) (*NextAuthMethodsResponse, error)
|
||||
NoneAuth(context.Context, *NoneAuthRequest) (*NoneAuthResponse, error)
|
||||
PasswordAuth(context.Context, *PasswordAuthRequest) (*PasswordAuthResponse, error)
|
||||
PublicKeyAuth(context.Context, *PublicKeyAuthRequest) (*PublicKeyAuthResponse, error)
|
||||
KeyboardInteractiveAuth(SshPiperPlugin_KeyboardInteractiveAuthServer) error
|
||||
UpstreamAuthFailureNotice(context.Context, *UpstreamAuthFailureNoticeRequest) (*UpstreamAuthFailureNoticeResponse, error)
|
||||
Banner(context.Context, *BannerRequest) (*BannerResponse, error)
|
||||
VerifyHostKey(context.Context, *VerifyHostKeyRequest) (*VerifyHostKeyReply, error)
|
||||
mustEmbedUnimplementedSshPiperPluginServer()
|
||||
}
|
||||
|
||||
// UnimplementedSshPiperPluginServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedSshPiperPluginServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedSshPiperPluginServer) Logs(*StartLogRequest, SshPiperPlugin_LogsServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method Logs not implemented")
|
||||
}
|
||||
func (UnimplementedSshPiperPluginServer) ListCallbacks(context.Context, *ListCallbackRequest) (*ListCallbackResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListCallbacks not implemented")
|
||||
}
|
||||
func (UnimplementedSshPiperPluginServer) NewConnection(context.Context, *NewConnectionRequest) (*NewConnectionResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method NewConnection not implemented")
|
||||
}
|
||||
func (UnimplementedSshPiperPluginServer) NextAuthMethods(context.Context, *NextAuthMethodsRequest) (*NextAuthMethodsResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method NextAuthMethods not implemented")
|
||||
}
|
||||
func (UnimplementedSshPiperPluginServer) NoneAuth(context.Context, *NoneAuthRequest) (*NoneAuthResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method NoneAuth not implemented")
|
||||
}
|
||||
func (UnimplementedSshPiperPluginServer) PasswordAuth(context.Context, *PasswordAuthRequest) (*PasswordAuthResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method PasswordAuth not implemented")
|
||||
}
|
||||
func (UnimplementedSshPiperPluginServer) PublicKeyAuth(context.Context, *PublicKeyAuthRequest) (*PublicKeyAuthResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method PublicKeyAuth not implemented")
|
||||
}
|
||||
func (UnimplementedSshPiperPluginServer) KeyboardInteractiveAuth(SshPiperPlugin_KeyboardInteractiveAuthServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method KeyboardInteractiveAuth not implemented")
|
||||
}
|
||||
func (UnimplementedSshPiperPluginServer) UpstreamAuthFailureNotice(context.Context, *UpstreamAuthFailureNoticeRequest) (*UpstreamAuthFailureNoticeResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method UpstreamAuthFailureNotice not implemented")
|
||||
}
|
||||
func (UnimplementedSshPiperPluginServer) Banner(context.Context, *BannerRequest) (*BannerResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Banner not implemented")
|
||||
}
|
||||
func (UnimplementedSshPiperPluginServer) VerifyHostKey(context.Context, *VerifyHostKeyRequest) (*VerifyHostKeyReply, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method VerifyHostKey not implemented")
|
||||
}
|
||||
func (UnimplementedSshPiperPluginServer) mustEmbedUnimplementedSshPiperPluginServer() {}
|
||||
|
||||
// UnsafeSshPiperPluginServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to SshPiperPluginServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeSshPiperPluginServer interface {
|
||||
mustEmbedUnimplementedSshPiperPluginServer()
|
||||
}
|
||||
|
||||
func RegisterSshPiperPluginServer(s grpc.ServiceRegistrar, srv SshPiperPluginServer) {
|
||||
s.RegisterService(&SshPiperPlugin_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _SshPiperPlugin_Logs_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(StartLogRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(SshPiperPluginServer).Logs(m, &sshPiperPluginLogsServer{stream})
|
||||
}
|
||||
|
||||
type SshPiperPlugin_LogsServer interface {
|
||||
Send(*Log) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type sshPiperPluginLogsServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *sshPiperPluginLogsServer) Send(m *Log) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func _SshPiperPlugin_ListCallbacks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListCallbackRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SshPiperPluginServer).ListCallbacks(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/libplugin.SshPiperPlugin/ListCallbacks",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SshPiperPluginServer).ListCallbacks(ctx, req.(*ListCallbackRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SshPiperPlugin_NewConnection_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(NewConnectionRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SshPiperPluginServer).NewConnection(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/libplugin.SshPiperPlugin/NewConnection",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SshPiperPluginServer).NewConnection(ctx, req.(*NewConnectionRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SshPiperPlugin_NextAuthMethods_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(NextAuthMethodsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SshPiperPluginServer).NextAuthMethods(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/libplugin.SshPiperPlugin/NextAuthMethods",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SshPiperPluginServer).NextAuthMethods(ctx, req.(*NextAuthMethodsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SshPiperPlugin_NoneAuth_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(NoneAuthRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SshPiperPluginServer).NoneAuth(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/libplugin.SshPiperPlugin/NoneAuth",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SshPiperPluginServer).NoneAuth(ctx, req.(*NoneAuthRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SshPiperPlugin_PasswordAuth_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PasswordAuthRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SshPiperPluginServer).PasswordAuth(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/libplugin.SshPiperPlugin/PasswordAuth",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SshPiperPluginServer).PasswordAuth(ctx, req.(*PasswordAuthRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SshPiperPlugin_PublicKeyAuth_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(PublicKeyAuthRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SshPiperPluginServer).PublicKeyAuth(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/libplugin.SshPiperPlugin/PublicKeyAuth",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SshPiperPluginServer).PublicKeyAuth(ctx, req.(*PublicKeyAuthRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SshPiperPlugin_KeyboardInteractiveAuth_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(SshPiperPluginServer).KeyboardInteractiveAuth(&sshPiperPluginKeyboardInteractiveAuthServer{stream})
|
||||
}
|
||||
|
||||
type SshPiperPlugin_KeyboardInteractiveAuthServer interface {
|
||||
Send(*KeyboardInteractiveAuthMessage) error
|
||||
Recv() (*KeyboardInteractiveAuthMessage, error)
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type sshPiperPluginKeyboardInteractiveAuthServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *sshPiperPluginKeyboardInteractiveAuthServer) Send(m *KeyboardInteractiveAuthMessage) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *sshPiperPluginKeyboardInteractiveAuthServer) Recv() (*KeyboardInteractiveAuthMessage, error) {
|
||||
m := new(KeyboardInteractiveAuthMessage)
|
||||
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func _SshPiperPlugin_UpstreamAuthFailureNotice_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpstreamAuthFailureNoticeRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SshPiperPluginServer).UpstreamAuthFailureNotice(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/libplugin.SshPiperPlugin/UpstreamAuthFailureNotice",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SshPiperPluginServer).UpstreamAuthFailureNotice(ctx, req.(*UpstreamAuthFailureNoticeRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SshPiperPlugin_Banner_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(BannerRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SshPiperPluginServer).Banner(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/libplugin.SshPiperPlugin/Banner",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SshPiperPluginServer).Banner(ctx, req.(*BannerRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SshPiperPlugin_VerifyHostKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(VerifyHostKeyRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SshPiperPluginServer).VerifyHostKey(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/libplugin.SshPiperPlugin/VerifyHostKey",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SshPiperPluginServer).VerifyHostKey(ctx, req.(*VerifyHostKeyRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// SshPiperPlugin_ServiceDesc is the grpc.ServiceDesc for SshPiperPlugin service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var SshPiperPlugin_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "libplugin.SshPiperPlugin",
|
||||
HandlerType: (*SshPiperPluginServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "ListCallbacks",
|
||||
Handler: _SshPiperPlugin_ListCallbacks_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "NewConnection",
|
||||
Handler: _SshPiperPlugin_NewConnection_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "NextAuthMethods",
|
||||
Handler: _SshPiperPlugin_NextAuthMethods_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "NoneAuth",
|
||||
Handler: _SshPiperPlugin_NoneAuth_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "PasswordAuth",
|
||||
Handler: _SshPiperPlugin_PasswordAuth_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "PublicKeyAuth",
|
||||
Handler: _SshPiperPlugin_PublicKeyAuth_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpstreamAuthFailureNotice",
|
||||
Handler: _SshPiperPlugin_UpstreamAuthFailureNotice_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Banner",
|
||||
Handler: _SshPiperPlugin_Banner_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "VerifyHostKey",
|
||||
Handler: _SshPiperPlugin_VerifyHostKey_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "Logs",
|
||||
Handler: _SshPiperPlugin_Logs_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
{
|
||||
StreamName: "KeyboardInteractiveAuth",
|
||||
Handler: _SshPiperPlugin_KeyboardInteractiveAuth_Handler,
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "plugin.proto",
|
||||
}
|
||||
374
libplugin/pluginbase.go
Normal file
374
libplugin/pluginbase.go
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
package libplugin
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
context "context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/tg123/sshpiper/libplugin/ioconn"
|
||||
"google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type ConnMetadata interface {
|
||||
User() string
|
||||
|
||||
RemoteAddr() string
|
||||
|
||||
UniqueID() string
|
||||
}
|
||||
|
||||
func (c *ConnMeta) User() string {
|
||||
return c.UserName
|
||||
}
|
||||
|
||||
func (c *ConnMeta) RemoteAddr() string {
|
||||
return c.FromAddr
|
||||
}
|
||||
|
||||
func (c *ConnMeta) UniqueID() string {
|
||||
return c.UniqId
|
||||
}
|
||||
|
||||
type KeyboardInteractiveChallenge func(instruction string, question string, echo bool) (answer string, err error)
|
||||
|
||||
type SshPiperPluginConfig struct {
|
||||
NewConnectionCallback func(conn ConnMetadata) error
|
||||
|
||||
NextAuthMethodsCallback func(conn ConnMetadata) ([]string, error)
|
||||
|
||||
NoneAuthCallback func(conn ConnMetadata) (*Upstream, error)
|
||||
|
||||
PasswordCallback func(conn ConnMetadata, password []byte) (*Upstream, error)
|
||||
|
||||
PublicKeyCallback func(conn ConnMetadata, key []byte) (*Upstream, error)
|
||||
|
||||
KeyboardInteractiveCallback func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Upstream, error)
|
||||
|
||||
UpstreamAuthFailureCallback func(conn ConnMetadata, method string, err error)
|
||||
|
||||
BannerCallback func(conn ConnMetadata) string
|
||||
|
||||
VerifyHostKeyCallback func(conn ConnMetadata, key []byte) (bool, error)
|
||||
}
|
||||
|
||||
type SshPiperPlugin interface {
|
||||
GetLoggerOutput() io.Writer
|
||||
GetGrpcServer() *grpc.Server
|
||||
Serve() error
|
||||
}
|
||||
|
||||
func NewFromStdio(config SshPiperPluginConfig) (SshPiperPlugin, error) {
|
||||
s := grpc.NewServer()
|
||||
l, err := ioconn.ListenFromSingleIO(os.Stdin, os.Stdout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewFromGrpc(config, s, l)
|
||||
}
|
||||
|
||||
func NewFromGrpc(config SshPiperPluginConfig, grpc *grpc.Server, listener net.Listener) (SshPiperPlugin, error) {
|
||||
r, w := io.Pipe()
|
||||
|
||||
s := &server{
|
||||
config: config,
|
||||
grpc: grpc,
|
||||
listener: listener,
|
||||
logwriter: w,
|
||||
logs: make(chan string, 1000),
|
||||
}
|
||||
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(r)
|
||||
|
||||
for scanner.Scan() {
|
||||
s.logs <- scanner.Text()
|
||||
}
|
||||
}()
|
||||
|
||||
RegisterSshPiperPluginServer(s.grpc, s)
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
type server struct {
|
||||
UnimplementedSshPiperPluginServer
|
||||
|
||||
config SshPiperPluginConfig
|
||||
grpc *grpc.Server
|
||||
listener net.Listener
|
||||
|
||||
logs chan string
|
||||
logwriter io.Writer
|
||||
}
|
||||
|
||||
func (s *server) GetGrpcServer() *grpc.Server {
|
||||
return s.grpc
|
||||
}
|
||||
|
||||
func (s *server) GetLoggerOutput() io.Writer {
|
||||
return s.logwriter
|
||||
}
|
||||
|
||||
func (s *server) Serve() error {
|
||||
return s.grpc.Serve(s.listener)
|
||||
}
|
||||
|
||||
func (s *server) Logs(req *StartLogRequest, stream SshPiperPlugin_LogsServer) error {
|
||||
for log := range s.logs {
|
||||
if err := stream.Send(&Log{
|
||||
Message: log,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *server) ListCallbacks(ctx context.Context, req *ListCallbackRequest) (*ListCallbackResponse, error) {
|
||||
|
||||
var cb []string
|
||||
|
||||
if s.config.NewConnectionCallback != nil {
|
||||
cb = append(cb, "NewConnection")
|
||||
}
|
||||
|
||||
if s.config.NextAuthMethodsCallback != nil {
|
||||
cb = append(cb, "NextAuthMethods")
|
||||
}
|
||||
|
||||
if s.config.NoneAuthCallback != nil {
|
||||
cb = append(cb, "NoneAuth")
|
||||
}
|
||||
|
||||
if s.config.PasswordCallback != nil {
|
||||
cb = append(cb, "PasswordAuth")
|
||||
}
|
||||
|
||||
if s.config.PublicKeyCallback != nil {
|
||||
cb = append(cb, "PublicKeyAuth")
|
||||
}
|
||||
|
||||
if s.config.KeyboardInteractiveCallback != nil {
|
||||
cb = append(cb, "KeyboardInteractiveAuth")
|
||||
}
|
||||
|
||||
if s.config.UpstreamAuthFailureCallback != nil {
|
||||
cb = append(cb, "UpstreamAuthFailure")
|
||||
}
|
||||
|
||||
if s.config.BannerCallback != nil {
|
||||
cb = append(cb, "Banner")
|
||||
}
|
||||
|
||||
if s.config.VerifyHostKeyCallback != nil {
|
||||
cb = append(cb, "VerifyHostKey")
|
||||
}
|
||||
|
||||
return &ListCallbackResponse{
|
||||
Callbacks: cb,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *server) NewConnection(ctx context.Context, req *NewConnectionRequest) (*NewConnectionResponse, error) {
|
||||
if s.config.NewConnectionCallback == nil {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method NewConnection not implemented")
|
||||
}
|
||||
|
||||
if err := s.config.NewConnectionCallback(req.Meta); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &NewConnectionResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *server) NextAuthMethods(ctx context.Context, req *NextAuthMethodsRequest) (*NextAuthMethodsResponse, error) {
|
||||
if s.config.NextAuthMethodsCallback == nil {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method NextAuthMethods not implemented")
|
||||
}
|
||||
|
||||
methods, err := s.config.NextAuthMethodsCallback(req.Meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &NextAuthMethodsResponse{}
|
||||
|
||||
for _, method := range methods {
|
||||
m := AuthMethodFromName(method)
|
||||
if m == -1 {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "unknown method %s", method)
|
||||
}
|
||||
resp.Methods = append(resp.Methods, m)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *server) NoneAuth(ctx context.Context, req *NoneAuthRequest) (*NoneAuthResponse, error) {
|
||||
if s.config.NoneAuthCallback == nil {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method NoneAuth not implemented")
|
||||
}
|
||||
|
||||
upstream, err := s.config.NoneAuthCallback(req.Meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &NoneAuthResponse{
|
||||
Upstream: upstream,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *server) PasswordAuth(ctx context.Context, req *PasswordAuthRequest) (*PasswordAuthResponse, error) {
|
||||
if s.config.PasswordCallback == nil {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method PasswordAuth not implemented")
|
||||
}
|
||||
|
||||
upstream, err := s.config.PasswordCallback(req.Meta, req.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &PasswordAuthResponse{
|
||||
Upstream: upstream,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *server) PublicKeyAuth(ctx context.Context, req *PublicKeyAuthRequest) (*PublicKeyAuthResponse, error) {
|
||||
if s.config.PublicKeyCallback == nil {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method PublicKeyAuth not implemented")
|
||||
}
|
||||
|
||||
upstream, err := s.config.PublicKeyCallback(req.Meta, req.PublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &PublicKeyAuthResponse{
|
||||
Upstream: upstream,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *server) KeyboardInteractiveAuth(stream SshPiperPlugin_KeyboardInteractiveAuthServer) error {
|
||||
if s.config.KeyboardInteractiveCallback == nil {
|
||||
return status.Errorf(codes.Unimplemented, "method KeyboardInteractiveAuth not implemented")
|
||||
}
|
||||
|
||||
if err := stream.Send(&KeyboardInteractiveAuthMessage{
|
||||
Message: &KeyboardInteractiveAuthMessage_MetaRequest{},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
metareply, err := stream.Recv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
meta := metareply.GetMetaResponse()
|
||||
if meta == nil {
|
||||
return status.Errorf(codes.InvalidArgument, "missing meta")
|
||||
}
|
||||
|
||||
upstream, err := s.config.KeyboardInteractiveCallback(meta.Meta, func(instruction string, question string, echo bool) (answer string, err error) {
|
||||
var questions []*KeyboardInteractivePromptRequest_Question
|
||||
if question != "" {
|
||||
questions = append(questions, &KeyboardInteractivePromptRequest_Question{
|
||||
Text: question,
|
||||
Echo: echo,
|
||||
})
|
||||
}
|
||||
|
||||
if err := stream.Send(&KeyboardInteractiveAuthMessage{
|
||||
Message: &KeyboardInteractiveAuthMessage_PromptRequest{
|
||||
PromptRequest: &KeyboardInteractivePromptRequest{
|
||||
Name: "", // temporary unused
|
||||
Instruction: instruction,
|
||||
Questions: questions,
|
||||
},
|
||||
},
|
||||
}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if question == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
userInputReply, err := stream.Recv()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
userInput := userInputReply.GetUserResponse()
|
||||
if userInput == nil {
|
||||
return "", status.Errorf(codes.InvalidArgument, "missing user input")
|
||||
}
|
||||
|
||||
if len(userInput.Answers) != 1 {
|
||||
return "", status.Errorf(codes.InvalidArgument, "expected 1 answer, got %d", len(userInput.Answers))
|
||||
}
|
||||
|
||||
return userInput.Answers[0], nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := stream.Send(&KeyboardInteractiveAuthMessage{
|
||||
Message: &KeyboardInteractiveAuthMessage_FinishRequest{
|
||||
FinishRequest: &KeyboardInteractiveFinishRequest{
|
||||
Upstream: upstream,
|
||||
},
|
||||
},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *server) UpstreamAuthFailureNotice(ctx context.Context, req *UpstreamAuthFailureNoticeRequest) (*UpstreamAuthFailureNoticeResponse, error) {
|
||||
if s.config.UpstreamAuthFailureCallback == nil {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method UpstreamAuthFailureNotice not implemented")
|
||||
}
|
||||
|
||||
s.config.UpstreamAuthFailureCallback(req.Meta, req.Method, fmt.Errorf(req.Error))
|
||||
|
||||
return &UpstreamAuthFailureNoticeResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *server) Banner(ctx context.Context, req *BannerRequest) (*BannerResponse, error) {
|
||||
if s.config.BannerCallback == nil {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Banner not implemented")
|
||||
}
|
||||
|
||||
msg := s.config.BannerCallback(req.Meta)
|
||||
|
||||
return &BannerResponse{
|
||||
Message: msg,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *server) VerifyHostKey(ctx context.Context, req *VerifyHostKeyRequest) (*VerifyHostKeyReply, error) {
|
||||
if s.config.VerifyHostKeyCallback == nil {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method VerifyHostKey not implemented")
|
||||
}
|
||||
|
||||
verifed, err := s.config.VerifyHostKeyCallback(req.Meta, req.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &VerifyHostKeyReply{
|
||||
Verified: verifed,
|
||||
}, nil
|
||||
}
|
||||
138
libplugin/util.go
Normal file
138
libplugin/util.go
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
package libplugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func WriterToFile(writer io.Writer) (*os.File, error) {
|
||||
r, w, err := os.Pipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go io.Copy(writer, r)
|
||||
|
||||
return w, nil
|
||||
}
|
||||
|
||||
func AuthMethodTypeToName(a AuthMethod) string {
|
||||
switch a {
|
||||
case AuthMethod_NONE:
|
||||
return "none"
|
||||
case AuthMethod_PASSWORD:
|
||||
return "password"
|
||||
case AuthMethod_PUBLICKEY:
|
||||
return "publickey"
|
||||
case AuthMethod_KEYBOARD_INTERACTIVE:
|
||||
return "keyboard-interactive"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func AuthMethodFromName(n string) AuthMethod {
|
||||
switch n {
|
||||
case "none":
|
||||
return AuthMethod_NONE
|
||||
case "password":
|
||||
return AuthMethod_PASSWORD
|
||||
case "publickey":
|
||||
return AuthMethod_PUBLICKEY
|
||||
case "keyboard-interactive":
|
||||
return AuthMethod_KEYBOARD_INTERACTIVE
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func ConfigStdioLogrus(p SshPiperPlugin, logger *logrus.Logger) {
|
||||
if logger == nil {
|
||||
logger = logrus.StandardLogger()
|
||||
}
|
||||
logger.SetOutput(p.GetLoggerOutput())
|
||||
logger.SetFormatter(&logrus.TextFormatter{ForceColors: true})
|
||||
}
|
||||
|
||||
// SplitHostPortForSSH is the modified version of net.SplitHostPort but return port 22 is no port is specified
|
||||
func SplitHostPortForSSH(addr string) (host string, port int, err error) {
|
||||
host = addr
|
||||
h, p, err := net.SplitHostPort(host)
|
||||
if err == nil {
|
||||
host = h
|
||||
port, err = strconv.Atoi(p)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else if host != "" {
|
||||
// test valid after concat :22
|
||||
if _, _, err = net.SplitHostPort(host + ":22"); err == nil {
|
||||
port = 22
|
||||
}
|
||||
}
|
||||
|
||||
if host == "" {
|
||||
err = fmt.Errorf("empty addr")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DialForSSH is the modified version of net.Dial, would add ":22" automaticlly
|
||||
func DialForSSH(addr string) (net.Conn, error) {
|
||||
|
||||
if _, _, err := net.SplitHostPort(addr); err != nil && addr != "" {
|
||||
// test valid after concat :22
|
||||
if _, _, err := net.SplitHostPort(addr + ":22"); err == nil {
|
||||
addr += ":22"
|
||||
}
|
||||
}
|
||||
|
||||
return net.Dial("tcp", addr)
|
||||
}
|
||||
|
||||
func CreateNoneAuth(password []byte) *Upstream_None {
|
||||
return &Upstream_None{
|
||||
None: &UpstreamNoneAuth{},
|
||||
}
|
||||
}
|
||||
|
||||
func CreatePasswordAuth(password []byte) *Upstream_Password {
|
||||
return CreatePasswordAuthFromString(string(password))
|
||||
}
|
||||
|
||||
func CreatePasswordAuthFromString(password string) *Upstream_Password {
|
||||
return &Upstream_Password{
|
||||
Password: &UpstreamPasswordAuth{
|
||||
Password: password,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func CreatePrivateKeyAuth(key []byte) *Upstream_PrivateKey {
|
||||
return &Upstream_PrivateKey{
|
||||
PrivateKey: &UpstreamPrivateKeyAuth{
|
||||
PrivateKey: key,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func CreateRemoteSignerAuth(meta string) *Upstream_RemoteSigner {
|
||||
return &Upstream_RemoteSigner{
|
||||
RemoteSigner: &UpstreamRemoteSignerAuth{
|
||||
Meta: meta,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func CreateNextPluginAuth(meta map[string]string) *Upstream_NextPlugin {
|
||||
return &Upstream_NextPlugin{
|
||||
NextPlugin: &UpstreamNextPluginAuth{
|
||||
Meta: meta,
|
||||
},
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue