mirror of
https://github.com/kubenetworks/kubevpn.git
synced 2025-10-13 19:13:43 +08:00
feat: add command ssh
This commit is contained in:
50
cmd/kubevpn/cmds/ssh.go
Normal file
50
cmd/kubevpn/cmds/ssh.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package cmds
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||
"k8s.io/kubectl/pkg/util/i18n"
|
||||
"k8s.io/kubectl/pkg/util/templates"
|
||||
|
||||
"github.com/wencaiwulue/kubevpn/pkg/config"
|
||||
"github.com/wencaiwulue/kubevpn/pkg/handler"
|
||||
"github.com/wencaiwulue/kubevpn/pkg/util"
|
||||
)
|
||||
|
||||
// CmdSSH
|
||||
// 设置本地的IP是223.254.0.1/32 ,记得一定是掩码 32位,
|
||||
// 这样别的路由不会走到这里来
|
||||
func CmdSSH(_ cmdutil.Factory) *cobra.Command {
|
||||
var sshConf = &util.SshConfig{}
|
||||
cmd := &cobra.Command{
|
||||
Use: "ssh",
|
||||
Hidden: true,
|
||||
Short: "Ssh to jump server",
|
||||
Long: `Ssh to jump server`,
|
||||
Example: templates.Examples(i18n.T(`
|
||||
# Jump to server behind of bastion host or ssh jump host
|
||||
kubevpn ssh --ssh-addr 192.168.1.100:22 --ssh-username root --ssh-keyfile ~/.ssh/ssh.pem
|
||||
|
||||
# it also support ProxyJump, like
|
||||
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌────────┐
|
||||
│ pc ├────►│ ssh1 ├────►│ ssh2 ├────►│ ssh3 ├─────►... ─────► │ server │
|
||||
└──────┘ └──────┘ └──────┘ └──────┘ └────────┘
|
||||
kubevpn ssh --ssh-alias <alias>
|
||||
`)),
|
||||
PreRun: func(*cobra.Command, []string) {
|
||||
if !util.IsAdmin() {
|
||||
util.RunWithElevated()
|
||||
os.Exit(0)
|
||||
}
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
_ = os.Setenv(config.EnvKubeVPNTransportEngine, string(config.EngineGvisor))
|
||||
err := handler.SSH(cmd.Context(), sshConf)
|
||||
return err
|
||||
},
|
||||
}
|
||||
addSshFlags(cmd, sshConf)
|
||||
return cmd
|
||||
}
|
109
pkg/handler/ssh.go
Normal file
109
pkg/handler/ssh.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/wencaiwulue/kubevpn/pkg/core"
|
||||
"github.com/wencaiwulue/kubevpn/pkg/util"
|
||||
)
|
||||
|
||||
// SSH
|
||||
// 0) remote server install kubevpn if not found
|
||||
// 1) start remote kubevpn server
|
||||
// 2) start local tunnel
|
||||
// 3) ssh terminal
|
||||
func SSH(ctx context.Context, config *util.SshConfig) error {
|
||||
cancel, cancelFunc := context.WithCancel(ctx)
|
||||
defer cancelFunc()
|
||||
err := portMap(ctx, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
stdout, stderr, err := util.RemoteRun(config, fmt.Sprintf(`kubevpn serve -L "tcp://:10800" -L "tun://127.0.0.1:8422?net=223.254.0.123/32"`), nil)
|
||||
if err != nil {
|
||||
log.Errorf("run error: %v", err)
|
||||
log.Errorf("run stdout: %v", string(stdout))
|
||||
log.Errorf("run stderr: %v", string(stderr))
|
||||
cancelFunc()
|
||||
}
|
||||
}()
|
||||
|
||||
r := core.Route{
|
||||
ServeNodes: []string{
|
||||
fmt.Sprintf("tun:/127.0.0.1:8422?net=%s&route=%s", "223.254.0.124/32", "223.254.0.124/32"),
|
||||
},
|
||||
ChainNode: "tcp://172.17.64.35:10800",
|
||||
Retries: 5,
|
||||
}
|
||||
servers, err := Parse(r)
|
||||
if err != nil {
|
||||
log.Errorf("parse route error: %v", err)
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
log.Error(Run(cancel, servers))
|
||||
}()
|
||||
log.Info("tunnel connected")
|
||||
<-cancel.Done()
|
||||
return err
|
||||
}
|
||||
|
||||
func portMap(ctx context.Context, conf *util.SshConfig) (err error) {
|
||||
port := 10800
|
||||
var remote netip.AddrPort
|
||||
remote, err = netip.ParseAddrPort(net.JoinHostPort("127.0.0.1", strconv.Itoa(port)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var local netip.AddrPort
|
||||
local, err = netip.ParseAddrPort(net.JoinHostPort("127.0.0.1", strconv.Itoa(port)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// pre-check network ip connect
|
||||
var cli *ssh.Client
|
||||
cli, err = util.DialSshRemote(conf)
|
||||
if err != nil {
|
||||
return
|
||||
} else {
|
||||
_ = cli.Close()
|
||||
}
|
||||
errChan := make(chan error, 1)
|
||||
readyChan := make(chan struct{}, 1)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
err := util.Main(ctx, remote, local, conf, readyChan)
|
||||
if err != nil {
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
log.Errorf("ssh forward failed err: %v", err)
|
||||
}
|
||||
select {
|
||||
case errChan <- err:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-readyChan:
|
||||
return
|
||||
case err = <-errChan:
|
||||
log.Errorf("ssh proxy err: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user