mirror of
https://github.com/moul/sshportal.git
synced 2025-09-12 15:44:41 +08:00
Merge branch 'master' of https://github.com/moul/sshportal into tunnel
This commit is contained in:
commit
c3d49fde95
11 changed files with 172 additions and 39 deletions
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
|
@ -11,7 +11,7 @@ If this is a FEATURE REQUEST, please:
|
||||||
|
|
||||||
**What you expected to happen**:
|
**What you expected to happen**:
|
||||||
|
|
||||||
**How to reproduce it (as minimally and precisely as possible):
|
**How to reproduce it (as minimally and precisely as possible)**:
|
||||||
|
|
||||||
**Anything else we need to know?**:
|
**Anything else we need to know?**:
|
||||||
|
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
/log/
|
||||||
/sshportal
|
/sshportal
|
||||||
*.db
|
*.db
|
||||||
/data
|
/data
|
|
@ -5,6 +5,8 @@
|
||||||
* The default created user now has the same username as the user starting sshportal (was hardcoded "admin")
|
* The default created user now has the same username as the user starting sshportal (was hardcoded "admin")
|
||||||
* Add Telnet support
|
* Add Telnet support
|
||||||
* Add TTY audit feature ([#23](https://github.com/moul/sshportal/issues/23)) by [@sabban](https://github.com/sabban)
|
* Add TTY audit feature ([#23](https://github.com/moul/sshportal/issues/23)) by [@sabban](https://github.com/sabban)
|
||||||
|
* Fix `--assign-*` commands when using MySQL driver ([#45](https://github.com/moul/sshportal/issues/45))
|
||||||
|
* Add *HOP* support, an efficient and integrated way of using a jump host transparently ([#47](https://github.com/moul/sshportal/issues/47)) by [@mathieui](https://github.com/mathieui)
|
||||||
|
|
||||||
## v1.7.1 (2018-01-03)
|
## v1.7.1 (2018-01-03)
|
||||||
|
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -24,7 +24,7 @@ _docker_install:
|
||||||
.PHONY: dev
|
.PHONY: dev
|
||||||
dev:
|
dev:
|
||||||
-go get github.com/githubnemo/CompileDaemon
|
-go get github.com/githubnemo/CompileDaemon
|
||||||
CompileDaemon -exclude-dir=.git -exclude=".#*" -color=true -command="./sshportal --debug --bind-address=:$(PORT) --aes-key=$(AES_KEY) $(EXTRA_RUN_OPTS)" .
|
CompileDaemon -exclude-dir=.git -exclude=".#*" -color=true -command="./sshportal server --debug --bind-address=:$(PORT) --aes-key=$(AES_KEY) $(EXTRA_RUN_OPTS)" .
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
|
|
|
@ -178,11 +178,11 @@ event inspect [-h] EVENT...
|
||||||
|
|
||||||
# host management
|
# host management
|
||||||
host help
|
host help
|
||||||
host create [-h] [--name=<value>] [--password=<value>] [--comment=<value>] [--key=KEY] [--group=HOSTGROUP...] <username>[:<password>]@<host>[:<port>]
|
host create [-h] [--name=<value>] [--password=<value>] [--comment=<value>] [--key=KEY] [--group=HOSTGROUP...] [--hop=HOST] <username>[:<password>]@<host>[:<port>]
|
||||||
host inspect [-h] [--decrypt] HOST...
|
host inspect [-h] [--decrypt] HOST...
|
||||||
host ls [-h] [--latest] [--quiet]
|
host ls [-h] [--latest] [--quiet]
|
||||||
host rm [-h] HOST...
|
host rm [-h] HOST...
|
||||||
host update [-h] [--name=<value>] [--comment=<value>] [--key=KEY] [--assign-group=HOSTGROUP...] [--unassign-group=HOSTGROUP...] HOST...
|
host update [-h] [--name=<value>] [--comment=<value>] [--key=KEY] [--assign-group=HOSTGROUP...] [--unassign-group=HOSTGROUP...] [--set-hop=HOST] [--unset-hop] HOST...
|
||||||
|
|
||||||
# hostgroup management
|
# hostgroup management
|
||||||
hostgroup help
|
hostgroup help
|
||||||
|
|
2
db.go
2
db.go
|
@ -63,6 +63,8 @@ type Host struct {
|
||||||
HostKey []byte `sql:"size:10000" valid:"optional"`
|
HostKey []byte `sql:"size:10000" valid:"optional"`
|
||||||
Groups []*HostGroup `gorm:"many2many:host_host_groups;"`
|
Groups []*HostGroup `gorm:"many2many:host_host_groups;"`
|
||||||
Comment string `valid:"optional"`
|
Comment string `valid:"optional"`
|
||||||
|
Hop *Host
|
||||||
|
HopID uint
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserKey defines a user public key used by sshportal to identify the user
|
// UserKey defines a user public key used by sshportal to identify the user
|
||||||
|
|
24
dbinit.go
24
dbinit.go
|
@ -458,6 +458,30 @@ func dbInit(db *gorm.DB) error {
|
||||||
Rollback: func(tx *gorm.DB) error {
|
Rollback: func(tx *gorm.DB) error {
|
||||||
return fmt.Errorf("not implemented")
|
return fmt.Errorf("not implemented")
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
ID: "29",
|
||||||
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
type Host struct {
|
||||||
|
// FIXME: use uuid for ID
|
||||||
|
gorm.Model
|
||||||
|
Name string `gorm:"size:32"`
|
||||||
|
Addr string
|
||||||
|
User string
|
||||||
|
Password string
|
||||||
|
URL string
|
||||||
|
SSHKey *SSHKey `gorm:"ForeignKey:SSHKeyID"`
|
||||||
|
SSHKeyID uint `gorm:"index"`
|
||||||
|
HostKey []byte `sql:"size:10000"`
|
||||||
|
Groups []*HostGroup `gorm:"many2many:host_host_groups;"`
|
||||||
|
Comment string
|
||||||
|
Hop *Host
|
||||||
|
HopID uint
|
||||||
|
}
|
||||||
|
return tx.AutoMigrate(&Host{}).Error
|
||||||
|
},
|
||||||
|
Rollback: func(tx *gorm.DB) error {
|
||||||
|
return fmt.Errorf("not implemented")
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err := m.Migrate(); err != nil {
|
if err := m.Migrate(); err != nil {
|
||||||
|
|
|
@ -47,6 +47,10 @@ ssh sshportal -l admin host create test42
|
||||||
ssh sshportal -l admin host create --name=testtest --comment=test --password=test test@test.test
|
ssh sshportal -l admin host create --name=testtest --comment=test --password=test test@test.test
|
||||||
ssh sshportal -l admin host create --group=hg1 --group=hg2 hostwithgroups.org
|
ssh sshportal -l admin host create --group=hg1 --group=hg2 hostwithgroups.org
|
||||||
ssh sshportal -l admin host inspect example test42 testtest hostwithgroups
|
ssh sshportal -l admin host inspect example test42 testtest hostwithgroups
|
||||||
|
ssh sshportal -l admin host update --assign-group=hg1 test42
|
||||||
|
ssh sshportal -l admin host update --unassign-group=hg1 test42
|
||||||
|
ssh sshportal -l admin host update --assign-group=hg1 test42
|
||||||
|
ssh sshportal -l admin host update --assign-group=hg2 --unassign-group=hg2 test42
|
||||||
ssh sshportal -l admin host ls
|
ssh sshportal -l admin host ls
|
||||||
|
|
||||||
# backup/restore
|
# backup/restore
|
||||||
|
|
|
@ -3,14 +3,14 @@ package bastionsession
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"os"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/gliderlabs/ssh"
|
|
||||||
"github.com/arkan/bastion/pkg/logchannel"
|
"github.com/arkan/bastion/pkg/logchannel"
|
||||||
"github.com/moul/sshportal/pkg/logtunnel"
|
"github.com/moul/sshportal/pkg/logtunnel"
|
||||||
|
"github.com/gliderlabs/ssh"
|
||||||
gossh "golang.org/x/crypto/ssh"
|
gossh "golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ type Config struct {
|
||||||
ClientConfig *gossh.ClientConfig
|
ClientConfig *gossh.ClientConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func ChannelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx ssh.Context, config Config) error {
|
func MultiChannelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx ssh.Context, configs []Config) error {
|
||||||
switch newChan.ChannelType() {
|
switch newChan.ChannelType() {
|
||||||
case "session" :
|
case "session" :
|
||||||
lch, lreqs, err := newChan.Accept()
|
lch, lreqs, err := newChan.Accept()
|
||||||
|
@ -37,20 +37,38 @@ func ChannelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewCh
|
||||||
// TODO: trigger event callback
|
// TODO: trigger event callback
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// open client channel
|
var lastClient *gossh.Client
|
||||||
rconn, err := gossh.Dial("tcp", config.Addr, config.ClientConfig)
|
|
||||||
if err != nil {
|
// go through all the hops
|
||||||
return err
|
for _, config := range configs {
|
||||||
|
var client *gossh.Client
|
||||||
|
if lastClient == nil {
|
||||||
|
client, err = gossh.Dial("tcp", config.Addr, config.ClientConfig)
|
||||||
|
} else {
|
||||||
|
rconn, err := lastClient.Dial("tcp", config.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ncc, chans, reqs, err := gossh.NewClientConn(rconn, config.Addr, config.ClientConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
client = gossh.NewClient(ncc, chans, reqs)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() { _ = client.Close() }()
|
||||||
|
lastClient = client
|
||||||
}
|
}
|
||||||
defer func() { _ = rconn.Close() }()
|
rch, rreqs, err := lastClient.OpenChannel("session", []byte{})
|
||||||
rch, rreqs, err := rconn.OpenChannel("session", []byte{})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user := conn.User()
|
user := conn.User()
|
||||||
// pipe everything
|
// pipe everything
|
||||||
return pipe(lreqs, rreqs, lch, rch, config.Logs, user, newChan)
|
return pipe(lreqs, rreqs, lch, rch, configs[len(configs)-1].Logs, user, newChan)
|
||||||
case "direct-tcpip":
|
case "direct-tcpip":
|
||||||
lch, lreqs, err := newChan.Accept()
|
lch, lreqs, err := newChan.Accept()
|
||||||
// TODO: defer clean closer
|
// TODO: defer clean closer
|
||||||
|
@ -97,7 +115,7 @@ func pipe(lreqs, rreqs <-chan *gossh.Request, lch, rch gossh.Channel, logsLocati
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error: %v", err)
|
log.Fatalf("error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Session %v is recorded in %v", channeltype, file_name)
|
log.Printf("Session %v is recorded in %v", channeltype, file_name)
|
||||||
if channeltype == "session" {
|
if channeltype == "session" {
|
||||||
|
|
87
shell.go
87
shell.go
|
@ -271,10 +271,16 @@ GLOBAL OPTIONS:
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := model.Association("UserGroups").Append(&appendUserGroups).Delete(deleteUserGroups).Error; err != nil {
|
if err := model.Association("UserGroups").Append(&appendUserGroups).Error; err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if len(deleteUserGroups) > 0 {
|
||||||
|
if err := model.Association("UserGroups").Delete(deleteUserGroups).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var appendHostGroups []HostGroup
|
var appendHostGroups []HostGroup
|
||||||
var deleteHostGroups []HostGroup
|
var deleteHostGroups []HostGroup
|
||||||
|
@ -286,10 +292,16 @@ GLOBAL OPTIONS:
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := model.Association("HostGroups").Append(&appendHostGroups).Delete(deleteHostGroups).Error; err != nil {
|
if err := model.Association("HostGroups").Append(&appendHostGroups).Error; err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if len(deleteHostGroups) > 0 {
|
||||||
|
if err := model.Association("HostGroups").Delete(deleteHostGroups).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return tx.Commit().Error
|
return tx.Commit().Error
|
||||||
|
@ -642,6 +654,7 @@ GLOBAL OPTIONS:
|
||||||
cli.StringFlag{Name: "password, p", Usage: "If present, sshportal will use password-based authentication"},
|
cli.StringFlag{Name: "password, p", Usage: "If present, sshportal will use password-based authentication"},
|
||||||
cli.StringFlag{Name: "comment, c"},
|
cli.StringFlag{Name: "comment, c"},
|
||||||
cli.StringFlag{Name: "key, k", Usage: "`KEY` to use for authentication"},
|
cli.StringFlag{Name: "key, k", Usage: "`KEY` to use for authentication"},
|
||||||
|
cli.StringFlag{Name: "hop, o", Usage: "Hop to use for connecting to the server"},
|
||||||
cli.StringSliceFlag{Name: "group, g", Usage: "Assigns the host to `HOSTGROUPS` (default: \"default\")"},
|
cli.StringSliceFlag{Name: "group, g", Usage: "Assigns the host to `HOSTGROUPS` (default: \"default\")"},
|
||||||
},
|
},
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
|
@ -665,7 +678,13 @@ GLOBAL OPTIONS:
|
||||||
host.Password = c.String("password")
|
host.Password = c.String("password")
|
||||||
}
|
}
|
||||||
host.Name = strings.Split(host.Hostname(), ".")[0]
|
host.Name = strings.Split(host.Hostname(), ".")[0]
|
||||||
|
if c.String("hop") != "" {
|
||||||
|
hop, err := HostByName(db, c.String("hop"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
host.Hop = hop
|
||||||
|
}
|
||||||
if c.String("name") != "" {
|
if c.String("name") != "" {
|
||||||
host.Name = c.String("name")
|
host.Name = c.String("name")
|
||||||
}
|
}
|
||||||
|
@ -776,7 +795,7 @@ GLOBAL OPTIONS:
|
||||||
}
|
}
|
||||||
|
|
||||||
table := tablewriter.NewWriter(s)
|
table := tablewriter.NewWriter(s)
|
||||||
table.SetHeader([]string{"ID", "Name", "URL", "Key", "Groups", "Updated", "Created", "Comment"})
|
table.SetHeader([]string{"ID", "Name", "URL", "Key", "Groups", "Updated", "Created", "Comment", "Hop"})
|
||||||
table.SetBorder(false)
|
table.SetBorder(false)
|
||||||
table.SetCaption(true, fmt.Sprintf("Total: %d hosts.", len(hosts)))
|
table.SetCaption(true, fmt.Sprintf("Total: %d hosts.", len(hosts)))
|
||||||
for _, host := range hosts {
|
for _, host := range hosts {
|
||||||
|
@ -790,6 +809,14 @@ GLOBAL OPTIONS:
|
||||||
for _, hostGroup := range host.Groups {
|
for _, hostGroup := range host.Groups {
|
||||||
groupNames = append(groupNames, hostGroup.Name)
|
groupNames = append(groupNames, hostGroup.Name)
|
||||||
}
|
}
|
||||||
|
var hop string
|
||||||
|
if host.HopID != 0 {
|
||||||
|
var hopHost Host
|
||||||
|
db.Model(&host).Related(&hopHost, "HopID")
|
||||||
|
hop = hopHost.Name
|
||||||
|
} else {
|
||||||
|
hop = ""
|
||||||
|
}
|
||||||
table.Append([]string{
|
table.Append([]string{
|
||||||
fmt.Sprintf("%d", host.ID),
|
fmt.Sprintf("%d", host.ID),
|
||||||
host.Name,
|
host.Name,
|
||||||
|
@ -799,6 +826,7 @@ GLOBAL OPTIONS:
|
||||||
humanize.Time(host.UpdatedAt),
|
humanize.Time(host.UpdatedAt),
|
||||||
humanize.Time(host.CreatedAt),
|
humanize.Time(host.CreatedAt),
|
||||||
host.Comment,
|
host.Comment,
|
||||||
|
hop,
|
||||||
//FIXME: add some stats about last access time etc
|
//FIXME: add some stats about last access time etc
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -829,6 +857,8 @@ GLOBAL OPTIONS:
|
||||||
cli.StringFlag{Name: "url, u", Usage: "Update connection URL"},
|
cli.StringFlag{Name: "url, u", Usage: "Update connection URL"},
|
||||||
cli.StringFlag{Name: "comment, c", Usage: "Update/set a host comment"},
|
cli.StringFlag{Name: "comment, c", Usage: "Update/set a host comment"},
|
||||||
cli.StringFlag{Name: "key, k", Usage: "Link a `KEY` to use for authentication"},
|
cli.StringFlag{Name: "key, k", Usage: "Link a `KEY` to use for authentication"},
|
||||||
|
cli.StringFlag{Name: "hop, o", Usage: "Change the hop to use for connecting to the server"},
|
||||||
|
cli.BoolFlag{Name: "unset-hop", Usage: "Remove the hop set for this host"},
|
||||||
cli.StringSliceFlag{Name: "assign-group, g", Usage: "Assign the host to a new `HOSTGROUPS`"},
|
cli.StringSliceFlag{Name: "assign-group, g", Usage: "Assign the host to a new `HOSTGROUPS`"},
|
||||||
cli.StringSliceFlag{Name: "unassign-group", Usage: "Unassign the host from a `HOSTGROUPS`"},
|
cli.StringSliceFlag{Name: "unassign-group", Usage: "Unassign the host from a `HOSTGROUPS`"},
|
||||||
},
|
},
|
||||||
|
@ -876,6 +906,29 @@ GLOBAL OPTIONS:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hop
|
||||||
|
if c.String("hop") != "" {
|
||||||
|
hop, err := HostByName(db, c.String("hop"))
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := model.Association("Hop").Replace(hop).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the hop
|
||||||
|
if c.Bool("unset-hop") {
|
||||||
|
var hopHost Host
|
||||||
|
db.Model(&host).Related(&hopHost, "HopID")
|
||||||
|
if err := model.Association("Hop").Delete(hopHost).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// associations
|
// associations
|
||||||
if c.String("key") != "" {
|
if c.String("key") != "" {
|
||||||
var key SSHKey
|
var key SSHKey
|
||||||
|
@ -898,10 +951,16 @@ GLOBAL OPTIONS:
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := model.Association("Groups").Append(&appendGroups).Delete(deleteGroups).Error; err != nil {
|
if err := model.Association("Groups").Append(&appendGroups).Error; err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if len(deleteGroups) > 0 {
|
||||||
|
if err := model.Association("Groups").Delete(deleteGroups).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return tx.Commit().Error
|
return tx.Commit().Error
|
||||||
|
@ -1525,11 +1584,16 @@ GLOBAL OPTIONS:
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := model.Association("Groups").Append(&appendGroups).Delete(deleteGroups).Error; err != nil {
|
if err := model.Association("Groups").Append(&appendGroups).Error; err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if len(deleteGroups) > 0 {
|
||||||
|
if err := model.Association("Groups").Delete(deleteGroups).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
var appendRoles []UserRole
|
var appendRoles []UserRole
|
||||||
if err := UserRolesByIdentifiers(db, c.StringSlice("assign-role")).Find(&appendRoles).Error; err != nil {
|
if err := UserRolesByIdentifiers(db, c.StringSlice("assign-role")).Find(&appendRoles).Error; err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
|
@ -1540,12 +1604,17 @@ GLOBAL OPTIONS:
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := model.Association("Roles").Append(&appendRoles).Delete(deleteRoles).Error; err != nil {
|
if err := model.Association("Roles").Append(&appendRoles).Error; err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if len(deleteRoles) > 0 {
|
||||||
|
if err := model.Association("Roles").Delete(deleteRoles).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return tx.Commit().Error
|
return tx.Commit().Error
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
37
ssh.go
37
ssh.go
|
@ -114,16 +114,33 @@ func channelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewCh
|
||||||
|
|
||||||
switch host.Scheme() {
|
switch host.Scheme() {
|
||||||
case BastionSchemeSSH:
|
case BastionSchemeSSH:
|
||||||
clientConfig, err := bastionClientConfig(ctx, host)
|
sessionConfigs := make([]bastionsession.Config, 0)
|
||||||
if err != nil {
|
currentHost := host
|
||||||
ch, _, err2 := newChan.Accept()
|
for currentHost != nil {
|
||||||
|
clientConfig, err2 := bastionClientConfig(ctx, currentHost)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
|
ch, _, err3 := newChan.Accept()
|
||||||
|
if err3 != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Fprintf(ch, "error: %v\n", err2)
|
||||||
|
// FIXME: force close all channels
|
||||||
|
_ = ch.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Fprintf(ch, "error: %v\n", err)
|
sessionConfigs = append([]bastionsession.Config{{
|
||||||
// FIXME: force close all channels
|
Addr: currentHost.DialAddr(),
|
||||||
_ = ch.Close()
|
ClientConfig: clientConfig,
|
||||||
return
|
Logs: actx.config.logsLocation,
|
||||||
|
}}, sessionConfigs...)
|
||||||
|
if currentHost.HopID != 0 {
|
||||||
|
var newHost Host
|
||||||
|
actx.db.Model(currentHost).Related(&newHost, "HopID")
|
||||||
|
hostname := newHost.Name
|
||||||
|
currentHost, _ = HostByName(actx.db, hostname)
|
||||||
|
} else {
|
||||||
|
currentHost = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sess := Session{
|
sess := Session{
|
||||||
|
@ -141,11 +158,7 @@ func channelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewCh
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
go bastionsession.ChannelHandler(srv, conn, newChan, ctx, bastionsession.Config{ // nolint: errcheck
|
err = bastionsession.MultiChannelHandler(srv, conn, newChan, ctx, sessionConfigs)
|
||||||
Addr: host.DialAddr(),
|
|
||||||
ClientConfig: clientConfig,
|
|
||||||
Logs: actx.config.logsLocation,
|
|
||||||
})
|
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
sessUpdate := Session{
|
sessUpdate := Session{
|
||||||
|
|
Loading…
Add table
Reference in a new issue