mirror of
https://github.com/moul/sshportal.git
synced 2025-09-06 04:35:32 +08:00
684 lines
20 KiB
Go
684 lines
20 KiB
Go
package bastion // import "moul.io/sshportal/pkg/bastion"
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"math/rand"
|
|
"os"
|
|
"os/user"
|
|
"strings"
|
|
"time"
|
|
|
|
gormigrate "github.com/go-gormigrate/gormigrate/v2"
|
|
gossh "golang.org/x/crypto/ssh"
|
|
"gorm.io/gorm"
|
|
"moul.io/sshportal/pkg/crypto"
|
|
"moul.io/sshportal/pkg/dbmodels"
|
|
)
|
|
|
|
func DBInit(db *gorm.DB) error {
|
|
log.SetOutput(ioutil.Discard)
|
|
log.SetOutput(os.Stderr)
|
|
|
|
m := gormigrate.New(db, gormigrate.DefaultOptions, []*gormigrate.Migration{
|
|
{
|
|
ID: "1",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type Setting struct {
|
|
gorm.Model
|
|
Name string `gorm:"index:uix_settings_name,unique"`
|
|
Value string
|
|
}
|
|
return tx.AutoMigrate(&Setting{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Migrator().DropTable("settings")
|
|
},
|
|
}, {
|
|
ID: "2",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type SSHKey struct {
|
|
gorm.Model
|
|
Name string
|
|
Type string
|
|
Length uint
|
|
Fingerprint string
|
|
PrivKey string `sql:"size:5000"`
|
|
PubKey string `sql:"size:1000"`
|
|
Hosts []*dbmodels.Host `gorm:"ForeignKey:SSHKeyID"`
|
|
Comment string
|
|
}
|
|
return tx.AutoMigrate(&SSHKey{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Migrator().DropTable("ssh_keys")
|
|
},
|
|
}, {
|
|
ID: "3",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type Host struct {
|
|
gorm.Model
|
|
Name string `gorm:"size:32"`
|
|
Addr string
|
|
User string
|
|
Password string
|
|
SSHKey *dbmodels.SSHKey `gorm:"ForeignKey:SSHKeyID"`
|
|
SSHKeyID uint `gorm:"index"`
|
|
Groups []*dbmodels.HostGroup `gorm:"many2many:host_host_groups;"`
|
|
Fingerprint string
|
|
Comment string
|
|
}
|
|
return tx.AutoMigrate(&Host{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Migrator().DropTable("hosts")
|
|
},
|
|
}, {
|
|
ID: "4",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type UserKey struct {
|
|
gorm.Model
|
|
Key []byte `sql:"size:1000"`
|
|
UserID uint ``
|
|
User *dbmodels.User `gorm:"ForeignKey:UserID"`
|
|
Comment string
|
|
}
|
|
return tx.AutoMigrate(&UserKey{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Migrator().DropTable("user_keys")
|
|
},
|
|
}, {
|
|
ID: "5",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type User struct {
|
|
gorm.Model
|
|
IsAdmin bool
|
|
Email string
|
|
Name string
|
|
Keys []*dbmodels.UserKey `gorm:"ForeignKey:UserID"`
|
|
Groups []*dbmodels.UserGroup `gorm:"many2many:user_user_groups;"`
|
|
Comment string
|
|
InviteToken string
|
|
}
|
|
return tx.AutoMigrate(&User{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Migrator().DropTable("users")
|
|
},
|
|
}, {
|
|
ID: "6",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type UserGroup struct {
|
|
gorm.Model
|
|
Name string
|
|
Users []*dbmodels.User `gorm:"many2many:user_user_groups;"`
|
|
ACLs []*dbmodels.ACL `gorm:"many2many:user_group_acls;"`
|
|
Comment string
|
|
}
|
|
return tx.AutoMigrate(&UserGroup{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Migrator().DropTable("user_groups")
|
|
},
|
|
}, {
|
|
ID: "7",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type HostGroup struct {
|
|
gorm.Model
|
|
Name string
|
|
Hosts []*dbmodels.Host `gorm:"many2many:host_host_groups;"`
|
|
ACLs []*dbmodels.ACL `gorm:"many2many:host_group_acls;"`
|
|
Comment string
|
|
}
|
|
return tx.AutoMigrate(&HostGroup{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Migrator().DropTable("host_groups")
|
|
},
|
|
}, {
|
|
ID: "8",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type ACL struct {
|
|
gorm.Model
|
|
HostGroups []*dbmodels.HostGroup `gorm:"many2many:host_group_acls;"`
|
|
UserGroups []*dbmodels.UserGroup `gorm:"many2many:user_group_acls;"`
|
|
HostPattern string
|
|
Action string
|
|
Weight uint
|
|
Comment string
|
|
}
|
|
|
|
return tx.AutoMigrate(&ACL{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Migrator().DropTable("acls")
|
|
},
|
|
}, {
|
|
ID: "9",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.Migrator().DropIndex(&dbmodels.Setting{}, "uix_settings_name"); err != nil {
|
|
return err
|
|
}
|
|
return tx.Migrator().CreateIndex(&dbmodels.Setting{}, "uix_settings_name")
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Migrator().DropIndex(&dbmodels.Setting{}, "uix_settings_name")
|
|
},
|
|
}, {
|
|
ID: "10",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.Migrator().DropIndex(&dbmodels.SSHKey{}, "uix_keys_name"); err != nil {
|
|
return err
|
|
}
|
|
return tx.Migrator().CreateIndex(&dbmodels.SSHKey{}, "uix_keys_name")
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Migrator().DropIndex(&dbmodels.SSHKey{}, "uix_keys_name")
|
|
},
|
|
}, {
|
|
ID: "11",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.Migrator().DropIndex(&dbmodels.Host{}, "uix_hosts_name"); err != nil {
|
|
return err
|
|
}
|
|
return tx.Migrator().CreateIndex(&dbmodels.Host{}, "uix_hosts_name")
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Migrator().DropIndex(&dbmodels.Host{}, "uix_hosts_name")
|
|
},
|
|
}, {
|
|
ID: "12",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.Migrator().DropIndex(&dbmodels.User{}, "uix_users_name"); err != nil {
|
|
return err
|
|
}
|
|
return tx.Migrator().CreateIndex(&dbmodels.User{}, "uix_users_name")
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Migrator().DropIndex(&dbmodels.User{}, "uix_users_name")
|
|
},
|
|
}, {
|
|
ID: "13",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.Migrator().DropIndex(&dbmodels.UserGroup{}, "uix_usergroups_name"); err != nil {
|
|
return err
|
|
}
|
|
return tx.Migrator().CreateIndex(&dbmodels.UserGroup{}, "uix_usergroups_name")
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Migrator().DropIndex(&dbmodels.UserGroup{}, "uix_usergroups_name")
|
|
},
|
|
}, {
|
|
ID: "14",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
if err := tx.Migrator().DropIndex(&dbmodels.HostGroup{}, "uix_hostgroups_name"); err != nil {
|
|
return err
|
|
}
|
|
return tx.Migrator().CreateIndex(&dbmodels.HostGroup{}, "uix_hostgroups_name")
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Migrator().DropIndex(&dbmodels.HostGroup{}, "uix_hostgroups_name")
|
|
},
|
|
}, {
|
|
ID: "15",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type UserRole struct {
|
|
gorm.Model
|
|
Name string `valid:"required,length(1|32),unix_user"`
|
|
Users []*dbmodels.User `gorm:"many2many:user_user_roles"`
|
|
}
|
|
return tx.AutoMigrate(&UserRole{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Migrator().DropTable("user_roles")
|
|
},
|
|
}, {
|
|
ID: "16",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type User struct {
|
|
gorm.Model
|
|
IsAdmin bool
|
|
Roles []*dbmodels.UserRole `gorm:"many2many:user_user_roles"`
|
|
Email string `valid:"required,email"`
|
|
Name string `valid:"required,length(1|32),unix_user"`
|
|
Keys []*dbmodels.UserKey `gorm:"ForeignKey:UserID"`
|
|
Groups []*dbmodels.UserGroup `gorm:"many2many:user_user_groups;"`
|
|
Comment string `valid:"optional"`
|
|
InviteToken string `valid:"optional,length(10|60)"`
|
|
}
|
|
return tx.AutoMigrate(&User{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return fmt.Errorf("not implemented")
|
|
},
|
|
}, {
|
|
ID: "17",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.Create(&dbmodels.UserRole{Name: "admin"}).Error
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Where("name = ?", "admin").Unscoped().Delete(&dbmodels.UserRole{}).Error
|
|
},
|
|
}, {
|
|
ID: "18",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
var adminRole dbmodels.UserRole
|
|
if err := db.Where("name = ?", "admin").First(&adminRole).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
var users []*dbmodels.User
|
|
if err := db.Preload("Roles").Where("is_admin = ?", true).Find(&users).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, user := range users {
|
|
user.Roles = append(user.Roles, &adminRole)
|
|
if err := tx.Save(user).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return fmt.Errorf("not implemented")
|
|
},
|
|
}, {
|
|
ID: "19",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type User struct {
|
|
gorm.Model
|
|
Roles []*dbmodels.UserRole `gorm:"many2many:user_user_roles"`
|
|
Email string `valid:"required,email"`
|
|
Name string `valid:"required,length(1|32),unix_user"`
|
|
Keys []*dbmodels.UserKey `gorm:"ForeignKey:UserID"`
|
|
Groups []*dbmodels.UserGroup `gorm:"many2many:user_user_groups;"`
|
|
Comment string `valid:"optional"`
|
|
InviteToken string `valid:"optional,length(10|60)"`
|
|
}
|
|
return tx.AutoMigrate(&User{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return fmt.Errorf("not implemented")
|
|
},
|
|
}, {
|
|
ID: "20",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.Create(&dbmodels.UserRole{Name: "listhosts"}).Error
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Where("name = ?", "listhosts").Unscoped().Delete(&dbmodels.UserRole{}).Error
|
|
},
|
|
}, {
|
|
ID: "21",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type Session struct {
|
|
gorm.Model
|
|
StoppedAt time.Time `valid:"optional"`
|
|
Status string `valid:"required"`
|
|
User *dbmodels.User `gorm:"ForeignKey:UserID"`
|
|
Host *dbmodels.Host `gorm:"ForeignKey:HostID"`
|
|
UserID uint `valid:"optional"`
|
|
HostID uint `valid:"optional"`
|
|
ErrMsg string `valid:"optional"`
|
|
Comment string `valid:"optional"`
|
|
}
|
|
return tx.AutoMigrate(&Session{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Migrator().DropTable("sessions")
|
|
},
|
|
}, {
|
|
ID: "22",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type Event struct {
|
|
gorm.Model
|
|
Author *dbmodels.User `gorm:"ForeignKey:AuthorID"`
|
|
AuthorID uint `valid:"optional"`
|
|
Domain string `valid:"required"`
|
|
Action string `valid:"required"`
|
|
Entity string `valid:"optional"`
|
|
Args []byte `sql:"size:10000" valid:"optional,length(1|10000)"`
|
|
}
|
|
return tx.AutoMigrate(&Event{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return tx.Migrator().DropTable("events")
|
|
},
|
|
}, {
|
|
ID: "23",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type UserKey struct {
|
|
gorm.Model
|
|
Key []byte `sql:"size:1000" valid:"required,length(1|1000)"`
|
|
AuthorizedKey string `sql:"size:1000" valid:"required,length(1|1000)"`
|
|
UserID uint ``
|
|
User *dbmodels.User `gorm:"ForeignKey:UserID"`
|
|
Comment string `valid:"optional"`
|
|
}
|
|
return tx.AutoMigrate(&UserKey{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return fmt.Errorf("not implemented")
|
|
},
|
|
}, {
|
|
ID: "24",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
var userKeys []*dbmodels.UserKey
|
|
if err := db.Find(&userKeys).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, userKey := range userKeys {
|
|
key, err := gossh.ParsePublicKey(userKey.Key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
userKey.AuthorizedKey = string(gossh.MarshalAuthorizedKey(key))
|
|
if err := db.Model(userKey).Updates(userKey).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return fmt.Errorf("not implemented")
|
|
},
|
|
}, {
|
|
ID: "25",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type Host struct {
|
|
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"`
|
|
SSHKey *dbmodels.SSHKey `gorm:"ForeignKey:SSHKeyID"`
|
|
SSHKeyID uint `gorm:"index"`
|
|
HostKey []byte `sql:"size:1000" valid:"optional"`
|
|
Groups []*dbmodels.HostGroup `gorm:"many2many:host_host_groups;"`
|
|
Fingerprint string `valid:"optional"`
|
|
Comment string `valid:"optional"`
|
|
}
|
|
return tx.AutoMigrate(&Host{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return fmt.Errorf("not implemented")
|
|
},
|
|
}, {
|
|
ID: "26",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type Session struct {
|
|
gorm.Model
|
|
StoppedAt *time.Time `sql:"index" valid:"optional"`
|
|
Status string `valid:"required"`
|
|
User *dbmodels.User `gorm:"ForeignKey:UserID"`
|
|
Host *dbmodels.Host `gorm:"ForeignKey:HostID"`
|
|
UserID uint `valid:"optional"`
|
|
HostID uint `valid:"optional"`
|
|
ErrMsg string `valid:"optional"`
|
|
Comment string `valid:"optional"`
|
|
}
|
|
return tx.AutoMigrate(&Session{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return fmt.Errorf("not implemented")
|
|
},
|
|
}, {
|
|
ID: "27",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
var sessions []*dbmodels.Session
|
|
if err := db.Find(&sessions).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, session := range sessions {
|
|
if session.StoppedAt != nil && session.StoppedAt.IsZero() {
|
|
if err := db.Model(session).Updates(map[string]interface{}{"stopped_at": nil}).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return fmt.Errorf("not implemented")
|
|
},
|
|
}, {
|
|
ID: "28",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type Host struct {
|
|
gorm.Model
|
|
Name string `gorm:"size:32"`
|
|
Addr string
|
|
User string
|
|
Password string
|
|
URL string
|
|
SSHKey *dbmodels.SSHKey `gorm:"ForeignKey:SSHKeyID"`
|
|
SSHKeyID uint `gorm:"index"`
|
|
HostKey []byte `sql:"size:1000"`
|
|
Groups []*dbmodels.HostGroup `gorm:"many2many:host_host_groups;"`
|
|
Comment string
|
|
}
|
|
return tx.AutoMigrate(&Host{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return fmt.Errorf("not implemented")
|
|
},
|
|
}, {
|
|
ID: "29",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type Host struct {
|
|
gorm.Model
|
|
Name string `gorm:"size:32"`
|
|
Addr string
|
|
User string
|
|
Password string
|
|
URL string
|
|
SSHKey *dbmodels.SSHKey `gorm:"ForeignKey:SSHKeyID"`
|
|
SSHKeyID uint `gorm:"index"`
|
|
HostKey []byte `sql:"size:1000"`
|
|
Groups []*dbmodels.HostGroup `gorm:"many2many:host_host_groups;"`
|
|
Comment string
|
|
Hop *dbmodels.Host
|
|
HopID uint
|
|
}
|
|
return tx.AutoMigrate(&Host{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error {
|
|
return fmt.Errorf("not implemented")
|
|
},
|
|
}, {
|
|
ID: "30",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type Host struct {
|
|
gorm.Model
|
|
Name string `gorm:"size:32"`
|
|
Addr string
|
|
User string
|
|
Password string
|
|
URL string
|
|
SSHKey *dbmodels.SSHKey `gorm:"ForeignKey:SSHKeyID"`
|
|
SSHKeyID uint `gorm:"index"`
|
|
HostKey []byte `sql:"size:10000"`
|
|
Groups []*dbmodels.HostGroup `gorm:"many2many:host_host_groups;"`
|
|
Comment string
|
|
Hop *dbmodels.Host
|
|
Logging string
|
|
HopID uint
|
|
}
|
|
return tx.AutoMigrate(&Host{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error { return fmt.Errorf("not implemented") },
|
|
}, {
|
|
ID: "31",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
return tx.Session(&gorm.Session{AllowGlobalUpdate: true}).Model(&dbmodels.Host{}).Updates(&dbmodels.Host{Logging: "everything"}).Error
|
|
},
|
|
Rollback: func(tx *gorm.DB) error { return fmt.Errorf("not implemented") },
|
|
}, {
|
|
ID: "32",
|
|
Migrate: func(tx *gorm.DB) error {
|
|
type ACL struct {
|
|
gorm.Model
|
|
HostGroups []*dbmodels.HostGroup `gorm:"many2many:host_group_acls;"`
|
|
UserGroups []*dbmodels.UserGroup `gorm:"many2many:user_group_acls;"`
|
|
HostPattern string `valid:"optional"`
|
|
Action string `valid:"required"`
|
|
Weight uint ``
|
|
Comment string `valid:"optional"`
|
|
Inception *time.Time
|
|
Expiration *time.Time
|
|
}
|
|
return tx.AutoMigrate(&ACL{})
|
|
},
|
|
Rollback: func(tx *gorm.DB) error { return fmt.Errorf("not implemented") },
|
|
},
|
|
})
|
|
if err := m.Migrate(); err != nil {
|
|
return err
|
|
}
|
|
dbmodels.NewEvent("system", "migrated").Log(db)
|
|
|
|
// create default ssh key
|
|
var count int64
|
|
if err := db.Table("ssh_keys").Where("name = ?", "default").Count(&count).Error; err != nil {
|
|
return err
|
|
}
|
|
if count == 0 {
|
|
key, err := crypto.NewSSHKey("ed25519", 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
key.Name = "default"
|
|
key.Comment = "created by sshportal"
|
|
if err := db.Create(&key).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// create default host group
|
|
if err := db.Table("host_groups").Where("name = ?", "default").Count(&count).Error; err != nil {
|
|
return err
|
|
}
|
|
if count == 0 {
|
|
hostGroup := dbmodels.HostGroup{
|
|
Name: "default",
|
|
Comment: "created by sshportal",
|
|
}
|
|
if err := db.Create(&hostGroup).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// create default user group
|
|
if err := db.Table("user_groups").Where("name = ?", "default").Count(&count).Error; err != nil {
|
|
return err
|
|
}
|
|
if count == 0 {
|
|
userGroup := dbmodels.UserGroup{
|
|
Name: "default",
|
|
Comment: "created by sshportal",
|
|
}
|
|
if err := db.Create(&userGroup).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// create default acl
|
|
if err := db.Table("acls").Count(&count).Error; err != nil {
|
|
return err
|
|
}
|
|
if count == 0 {
|
|
var defaultUserGroup dbmodels.UserGroup
|
|
db.Where("name = ?", "default").First(&defaultUserGroup)
|
|
var defaultHostGroup dbmodels.HostGroup
|
|
db.Where("name = ?", "default").First(&defaultHostGroup)
|
|
acl := dbmodels.ACL{
|
|
UserGroups: []*dbmodels.UserGroup{&defaultUserGroup},
|
|
HostGroups: []*dbmodels.HostGroup{&defaultHostGroup},
|
|
Action: "allow",
|
|
//HostPattern: "",
|
|
//Weight: 0,
|
|
Comment: "created by sshportal",
|
|
}
|
|
if err := db.Create(&acl).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// create admin user
|
|
var defaultUserGroup dbmodels.UserGroup
|
|
db.Where("name = ?", "default").First(&defaultUserGroup)
|
|
if err := db.Table("users").Count(&count).Error; err != nil {
|
|
return err
|
|
}
|
|
if count == 0 {
|
|
// if no admin, create an account for the first connection
|
|
inviteToken := randStringBytes(16)
|
|
if os.Getenv("SSHPORTAL_DEFAULT_ADMIN_INVITE_TOKEN") != "" {
|
|
inviteToken = os.Getenv("SSHPORTAL_DEFAULT_ADMIN_INVITE_TOKEN")
|
|
}
|
|
var adminRole dbmodels.UserRole
|
|
if err := db.Where("name = ?", "admin").First(&adminRole).Error; err != nil {
|
|
return err
|
|
}
|
|
var username string
|
|
if currentUser, err := user.Current(); err == nil {
|
|
username = currentUser.Username
|
|
}
|
|
if username == "" {
|
|
username = os.Getenv("USER")
|
|
}
|
|
username = strings.ToLower(username)
|
|
if username == "" {
|
|
username = "admin" // fallback username
|
|
}
|
|
user := dbmodels.User{
|
|
Name: username,
|
|
Email: fmt.Sprintf("%s@localhost", username),
|
|
Comment: "created by sshportal",
|
|
Roles: []*dbmodels.UserRole{&adminRole},
|
|
InviteToken: inviteToken,
|
|
Groups: []*dbmodels.UserGroup{&defaultUserGroup},
|
|
}
|
|
if err := db.Create(&user).Error; err != nil {
|
|
return err
|
|
}
|
|
log.Printf("info 'admin' user created, use the user 'invite:%s' to associate a public key with this account", user.InviteToken)
|
|
}
|
|
|
|
// create host ssh key
|
|
if err := db.Table("ssh_keys").Where("name = ?", "host").Count(&count).Error; err != nil {
|
|
return err
|
|
}
|
|
if count == 0 {
|
|
key, err := crypto.NewSSHKey("ed25519", 1)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
key.Name = "host"
|
|
key.Comment = "created by sshportal"
|
|
if err := db.Create(&key).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// close unclosed connections
|
|
return db.Table("sessions").Where("status = ?", "active").Updates(&dbmodels.Session{
|
|
Status: string(dbmodels.SessionStatusClosed),
|
|
ErrMsg: "sshportal was halted while the connection was still active",
|
|
}).Error
|
|
}
|
|
|
|
func randStringBytes(n int) string {
|
|
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
|
|
b := make([]byte, n)
|
|
for i := range b {
|
|
b[i] = letterBytes[rand.Intn(len(letterBytes))]
|
|
}
|
|
return string(b)
|
|
}
|