mirror of
https://github.com/moul/sshportal.git
synced 2025-03-09 22:06:34 +08:00
Support having different host.Scheme
This commit is contained in:
parent
2352a53e6e
commit
2a68fc3114
4 changed files with 154 additions and 43 deletions
137
db.go
137
db.go
|
@ -6,6 +6,7 @@ import (
|
|||
"log"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -53,9 +54,10 @@ type Host struct {
|
|||
// FIXME: use uuid for ID
|
||||
gorm.Model
|
||||
Name string `gorm:"size:32" valid:"required,length(1|32),unix_user"`
|
||||
Addr string `valid:"required"`
|
||||
User string `valid:"optional"`
|
||||
Password string `valid:"optional"`
|
||||
Addr string `valid:"optional"` // FIXME: to be removed in a future version in favor of URL
|
||||
User string `valid:"optional"` // FIXME: to be removed in a future version in favor of URL
|
||||
Password string `valid:"optional"` // FIXME: to be removed in a future version in favor of URL
|
||||
URL string `valid:"optional"`
|
||||
SSHKey *SSHKey `gorm:"ForeignKey:SSHKeyID"` // SSHKey used to connect by the client
|
||||
SSHKeyID uint `gorm:"index"`
|
||||
HostKey []byte `sql:"size:10000" valid:"optional"`
|
||||
|
@ -168,37 +170,112 @@ func init() {
|
|||
}))
|
||||
}
|
||||
|
||||
func (host *Host) URL() string {
|
||||
return fmt.Sprintf("%s@%s", host.User, host.Addr)
|
||||
}
|
||||
|
||||
func NewHostFromURL(rawurl string) (*Host, error) {
|
||||
if !strings.Contains(rawurl, "://") {
|
||||
rawurl = "ssh://" + rawurl
|
||||
// Host helpers
|
||||
func ParseInputURL(input string) (*url.URL, error) {
|
||||
if !strings.Contains(input, "://") {
|
||||
input = "ssh://" + input
|
||||
}
|
||||
u, err := url.Parse(rawurl)
|
||||
u, err := url.Parse(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
host := Host{Addr: u.Host}
|
||||
if !strings.Contains(host.Addr, ":") {
|
||||
host.Addr += ":22" // add port if not present
|
||||
}
|
||||
host.User = "root" // default username
|
||||
if u.User != nil {
|
||||
password, _ := u.User.Password()
|
||||
host.Password = password
|
||||
host.User = u.User.Username()
|
||||
}
|
||||
return &host, nil
|
||||
return u, nil
|
||||
}
|
||||
func (host *Host) DialAddr() string {
|
||||
return fmt.Sprintf("%s:%d", host.Hostname(), host.Port())
|
||||
}
|
||||
func (host *Host) String() string {
|
||||
if host.URL != "" {
|
||||
return host.URL
|
||||
} else if host.Addr != "" { // to be removed in a future version in favor of URL
|
||||
if host.Password != "" {
|
||||
return fmt.Sprintf("ssh://%s:%s@%s", host.User, strings.Repeat("*", 4), host.Addr)
|
||||
}
|
||||
return fmt.Sprintf("ssh://%s@%s", host.User, host.Addr)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
func (host *Host) Scheme() string {
|
||||
if host.URL != "" {
|
||||
u, err := url.Parse(host.URL)
|
||||
if err != nil {
|
||||
return "ssh"
|
||||
}
|
||||
return u.Scheme
|
||||
} else if host.Addr != "" {
|
||||
return "ssh"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (host *Host) Hostname() string {
|
||||
return strings.Split(host.Addr, ":")[0]
|
||||
if host.URL != "" {
|
||||
u, err := url.Parse(host.URL)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return u.Hostname()
|
||||
} else if host.Addr != "" { // to be removed in a future version in favor of URL
|
||||
return strings.Split(host.Addr, ":")[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
func (host *Host) Username() string {
|
||||
if host.URL != "" {
|
||||
u, err := url.Parse(host.URL)
|
||||
if err != nil {
|
||||
return "root"
|
||||
}
|
||||
if u.User != nil {
|
||||
return u.User.Username()
|
||||
}
|
||||
} else if host.User != "" { // to be removed in a future version in favor of URL
|
||||
return host.User
|
||||
}
|
||||
return "root"
|
||||
}
|
||||
func (host *Host) Passwd() string {
|
||||
if host.URL != "" {
|
||||
u, err := url.Parse(host.URL)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if u.User != nil {
|
||||
password, _ := u.User.Password()
|
||||
return password
|
||||
}
|
||||
} else if host.Password != "" { // to be removed in a future version in favor of URL
|
||||
return host.Password
|
||||
}
|
||||
return ""
|
||||
}
|
||||
func (host *Host) Port() uint64 {
|
||||
var portString string
|
||||
if host.URL != "" {
|
||||
u, err := url.Parse(host.URL)
|
||||
if err != nil {
|
||||
goto defaultPort
|
||||
}
|
||||
portString = u.Port()
|
||||
} else if host.Addr != "" { // to be removed in a future version in favor of URL
|
||||
portString = strings.Split(host.Addr, ":")[1]
|
||||
}
|
||||
if portString != "" {
|
||||
port, err := strconv.ParseUint(portString, 10, 64)
|
||||
if err != nil {
|
||||
goto defaultPort
|
||||
}
|
||||
return port
|
||||
}
|
||||
defaultPort:
|
||||
switch host.Scheme() {
|
||||
case "ssh":
|
||||
return 22
|
||||
case "telnet":
|
||||
return 23
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Host helpers
|
||||
|
||||
func HostsPreload(db *gorm.DB) *gorm.DB {
|
||||
return db.Preload("Groups").Preload("SSHKey")
|
||||
}
|
||||
|
@ -217,7 +294,7 @@ func HostByName(db *gorm.DB, name string) (*Host, error) {
|
|||
|
||||
func (host *Host) clientConfig(hk gossh.HostKeyCallback) (*gossh.ClientConfig, error) {
|
||||
config := gossh.ClientConfig{
|
||||
User: host.User,
|
||||
User: host.Username(),
|
||||
HostKeyCallback: hk,
|
||||
Auth: []gossh.AuthMethod{},
|
||||
}
|
||||
|
@ -228,8 +305,8 @@ func (host *Host) clientConfig(hk gossh.HostKeyCallback) (*gossh.ClientConfig, e
|
|||
}
|
||||
config.Auth = append(config.Auth, gossh.PublicKeys(signer))
|
||||
}
|
||||
if host.Password != "" {
|
||||
config.Auth = append(config.Auth, gossh.Password(host.Password))
|
||||
if host.Passwd() != "" {
|
||||
config.Auth = append(config.Auth, gossh.Password(host.Passwd()))
|
||||
}
|
||||
if len(config.Auth) == 0 {
|
||||
return nil, fmt.Errorf("no valid authentication method for host %q", host.Name)
|
||||
|
|
22
dbinit.go
22
dbinit.go
|
@ -436,6 +436,28 @@ func dbInit(db *gorm.DB) error {
|
|||
Rollback: func(tx *gorm.DB) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
},
|
||||
}, {
|
||||
ID: "28",
|
||||
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
|
||||
}
|
||||
return tx.AutoMigrate(&Host{}).Error
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return fmt.Errorf("not implemented")
|
||||
},
|
||||
},
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
|
|
36
shell.go
36
shell.go
|
@ -635,7 +635,7 @@ GLOBAL OPTIONS:
|
|||
{
|
||||
Name: "create",
|
||||
Usage: "Creates a new host",
|
||||
ArgsUsage: "<user>[:<password>]@<host>[:<port>]",
|
||||
ArgsUsage: "[scheme://]<user>[:<password>]@<host>[:<port>]",
|
||||
Description: "$> host create bart@foo.org\n $> host create bob:marley@example.com:2222",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "name, n", Usage: "Assigns a name to the host"},
|
||||
|
@ -653,10 +653,14 @@ GLOBAL OPTIONS:
|
|||
return err
|
||||
}
|
||||
|
||||
host, err := NewHostFromURL(c.Args().First())
|
||||
u, err := ParseInputURL(c.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
host := &Host{
|
||||
URL: u.String(),
|
||||
Comment: c.String("comment"),
|
||||
}
|
||||
if c.String("password") != "" {
|
||||
host.Password = c.String("password")
|
||||
}
|
||||
|
@ -666,7 +670,6 @@ GLOBAL OPTIONS:
|
|||
host.Name = c.String("name")
|
||||
}
|
||||
// FIXME: check if name already exists
|
||||
host.Comment = c.String("comment")
|
||||
|
||||
if _, err := govalidator.ValidateStruct(host); err != nil {
|
||||
return err
|
||||
|
@ -773,14 +776,11 @@ GLOBAL OPTIONS:
|
|||
}
|
||||
|
||||
table := tablewriter.NewWriter(s)
|
||||
table.SetHeader([]string{"ID", "Name", "URL", "Key", "Pass", "Groups", "Updated", "Created", "Comment"})
|
||||
table.SetHeader([]string{"ID", "Name", "URL", "Key", "Groups", "Updated", "Created", "Comment"})
|
||||
table.SetBorder(false)
|
||||
table.SetCaption(true, fmt.Sprintf("Total: %d hosts.", len(hosts)))
|
||||
for _, host := range hosts {
|
||||
authKey, authPass := "", ""
|
||||
if host.Password != "" {
|
||||
authPass = "yes"
|
||||
}
|
||||
authKey := ""
|
||||
if host.SSHKeyID > 0 {
|
||||
var key SSHKey
|
||||
db.Model(&host).Related(&key)
|
||||
|
@ -793,9 +793,8 @@ GLOBAL OPTIONS:
|
|||
table.Append([]string{
|
||||
fmt.Sprintf("%d", host.ID),
|
||||
host.Name,
|
||||
host.URL(),
|
||||
host.String(),
|
||||
authKey,
|
||||
authPass,
|
||||
strings.Join(groupNames, ", "),
|
||||
humanize.Time(host.UpdatedAt),
|
||||
humanize.Time(host.CreatedAt),
|
||||
|
@ -827,7 +826,7 @@ GLOBAL OPTIONS:
|
|||
ArgsUsage: "HOST...",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "name, n", Usage: "Rename the host"},
|
||||
cli.StringFlag{Name: "password, p", Usage: "Update/set a password, use \"none\" to unset"},
|
||||
cli.StringFlag{Name: "url, u", Usage: "Update connection URL"},
|
||||
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.StringSliceFlag{Name: "assign-group, g", Usage: "Assign the host to a new `HOSTGROUPS`"},
|
||||
|
@ -855,7 +854,7 @@ GLOBAL OPTIONS:
|
|||
for _, host := range hosts {
|
||||
model := tx.Model(&host)
|
||||
// simple fields
|
||||
for _, fieldname := range []string{"name", "comment", "password"} {
|
||||
for _, fieldname := range []string{"name", "comment"} {
|
||||
if c.String(fieldname) != "" {
|
||||
if err := model.Update(fieldname, c.String(fieldname)).Error; err != nil {
|
||||
tx.Rollback()
|
||||
|
@ -864,6 +863,19 @@ GLOBAL OPTIONS:
|
|||
}
|
||||
}
|
||||
|
||||
// url
|
||||
if c.String("url") != "" {
|
||||
u, err := ParseInputURL(c.String("url"))
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if err := model.Update("url", u.String()).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// associations
|
||||
if c.String("key") != "" {
|
||||
var key SSHKey
|
||||
|
|
2
ssh.go
2
ssh.go
|
@ -128,7 +128,7 @@ func channelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewCh
|
|||
}
|
||||
|
||||
err = bastionsession.ChannelHandler(srv, conn, newChan, ctx, bastionsession.Config{
|
||||
Addr: host.Addr,
|
||||
Addr: host.DialAddr(),
|
||||
ClientConfig: clientConfig,
|
||||
})
|
||||
|
||||
|
|
Loading…
Reference in a new issue