Skip to content

Commit

Permalink
Merge pull request #196 from moul/dev/moul/pr-194
Browse files Browse the repository at this point in the history
  • Loading branch information
moul authored Jul 23, 2020
2 parents 90fd605 + 428344d commit 9d2badf
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 31 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ dist/
/sshportal
*.db
/data
sshportal.history
sshportal.history
.idea
9 changes: 0 additions & 9 deletions go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

_ "github.com/go-sql-driver/mysql"
_ "github.com/jinzhu/gorm/dialects/mysql"
_ "github.com/jinzhu/gorm/dialects/postgres"
_ "github.com/jinzhu/gorm/dialects/sqlite"
"github.com/urfave/cli"
"moul.io/srand"
Expand Down
10 changes: 8 additions & 2 deletions pkg/bastion/acl.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package bastion // import "moul.io/sshportal/pkg/bastion"
package bastion

import (
"sort"
"time"

"moul.io/sshportal/pkg/dbmodels"
)
Expand All @@ -13,14 +14,19 @@ func (a byWeight) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byWeight) Less(i, j int) bool { return a[i].Weight < a[j].Weight }

func checkACLs(user dbmodels.User, host dbmodels.Host) string {
currentTime := time.Now()

// shared ACLs between user and host
aclMap := map[uint]*dbmodels.ACL{}
for _, userGroup := range user.Groups {
for _, userGroupACL := range userGroup.ACLs {
for _, hostGroup := range host.Groups {
for _, hostGroupACL := range hostGroup.ACLs {
if userGroupACL.ID == hostGroupACL.ID {
aclMap[userGroupACL.ID] = userGroupACL
if (userGroupACL.Inception == nil || currentTime.After(*userGroupACL.Inception)) &&
(userGroupACL.Expiration == nil || currentTime.Before(*userGroupACL.Expiration)) {
aclMap[userGroupACL.ID] = userGroupACL
}
}
}
}
Expand Down
39 changes: 26 additions & 13 deletions pkg/bastion/dbinit.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ func DBInit(db *gorm.DB) error {
Type string
Length uint
Fingerprint string
PrivKey string `sql:"size:10000"`
PubKey string `sql:"size:10000"`
PrivKey string `sql:"size:5000"`
PubKey string `sql:"size:1000"`
Hosts []*dbmodels.Host `gorm:"ForeignKey:SSHKeyID"`
Comment string
}
Expand Down Expand Up @@ -80,7 +80,7 @@ func DBInit(db *gorm.DB) error {
Migrate: func(tx *gorm.DB) error {
type UserKey struct {
gorm.Model
Key []byte `sql:"size:10000"`
Key []byte `sql:"size:1000"`
UserID uint ``
User *dbmodels.User `gorm:"ForeignKey:UserID"`
Comment string
Expand Down Expand Up @@ -341,8 +341,8 @@ func DBInit(db *gorm.DB) error {
Migrate: func(tx *gorm.DB) error {
type UserKey struct {
gorm.Model
Key []byte `sql:"size:10000" valid:"required,length(1|10000)"`
AuthorizedKey string `sql:"size:10000" valid:"required,length(1|10000)"`
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"`
Expand Down Expand Up @@ -386,7 +386,7 @@ func DBInit(db *gorm.DB) error {
Password string `valid:"optional"`
SSHKey *dbmodels.SSHKey `gorm:"ForeignKey:SSHKeyID"`
SSHKeyID uint `gorm:"index"`
HostKey []byte `sql:"size:10000" valid:"optional"`
HostKey []byte `sql:"size:1000" valid:"optional"`
Groups []*dbmodels.HostGroup `gorm:"many2many:host_host_groups;"`
Fingerprint string `valid:"optional"`
Comment string `valid:"optional"`
Expand Down Expand Up @@ -447,7 +447,7 @@ func DBInit(db *gorm.DB) error {
URL string
SSHKey *dbmodels.SSHKey `gorm:"ForeignKey:SSHKeyID"`
SSHKeyID uint `gorm:"index"`
HostKey []byte `sql:"size:10000"`
HostKey []byte `sql:"size:1000"`
Groups []*dbmodels.HostGroup `gorm:"many2many:host_host_groups;"`
Comment string
}
Expand All @@ -468,7 +468,7 @@ func DBInit(db *gorm.DB) error {
URL string
SSHKey *dbmodels.SSHKey `gorm:"ForeignKey:SSHKeyID"`
SSHKeyID uint `gorm:"index"`
HostKey []byte `sql:"size:10000"`
HostKey []byte `sql:"size:1000"`
Groups []*dbmodels.HostGroup `gorm:"many2many:host_host_groups;"`
Comment string
Hop *dbmodels.Host
Expand Down Expand Up @@ -500,17 +500,30 @@ func DBInit(db *gorm.DB) error {
}
return tx.AutoMigrate(&Host{}).Error
},
Rollback: func(tx *gorm.DB) error {
return fmt.Errorf("not implemented")
},
Rollback: func(tx *gorm.DB) error { return fmt.Errorf("not implemented") },
}, {
ID: "31",
Migrate: func(tx *gorm.DB) error {
return tx.Model(&dbmodels.Host{}).Updates(&dbmodels.Host{Logging: "everything"}).Error
},
Rollback: func(tx *gorm.DB) error {
return fmt.Errorf("not implemented")
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{}).Error
},
Rollback: func(tx *gorm.DB) error { return fmt.Errorf("not implemented") },
},
})
if err := m.Migrate(); err != nil {
Expand Down
66 changes: 65 additions & 1 deletion pkg/bastion/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,17 +90,31 @@ GLOBAL OPTIONS:
cli.StringFlag{Name: "comment", Usage: "Adds a comment"},
cli.StringFlag{Name: "action", Usage: "Assigns the ACL action (allow,deny)", Value: string(dbmodels.ACLActionAllow)},
cli.UintFlag{Name: "weight, w", Usage: "Assigns the ACL weight (priority)"},
cli.StringFlag{Name: "inception, i", Usage: "Assigns inception date-time"},
cli.StringFlag{Name: "expiration, e", Usage: "Assigns expiration date-time"},
},
Action: func(c *cli.Context) error {
if err := myself.CheckRoles([]string{"admin"}); err != nil {
return err
}

inception, err := parseOptionalTime(c.String("inception"))
if err != nil {
return err
}
expiration, err := parseOptionalTime(c.String("expiration"))
if err != nil {
return err
}

acl := dbmodels.ACL{
Comment: c.String("comment"),
HostPattern: c.String("pattern"),
UserGroups: []*dbmodels.UserGroup{},
HostGroups: []*dbmodels.HostGroup{},
Weight: c.Uint("weight"),
Inception: inception,
Expiration: expiration,
Action: c.String("action"),
}
if acl.Action != string(dbmodels.ACLActionAllow) && acl.Action != string(dbmodels.ACLActionDeny) {
Expand Down Expand Up @@ -186,7 +200,7 @@ GLOBAL OPTIONS:
}

table := tablewriter.NewWriter(s)
table.SetHeader([]string{"ID", "Weight", "User groups", "Host groups", "Host pattern", "Action", "Updated", "Created", "Comment"})
table.SetHeader([]string{"ID", "Weight", "User groups", "Host groups", "Host pattern", "Action", "Inception", "Expiration", "Updated", "Created", "Comment"})
table.SetBorder(false)
table.SetCaption(true, fmt.Sprintf("Total: %d ACLs.", len(acls)))
for _, acl := range acls {
Expand All @@ -199,13 +213,24 @@ GLOBAL OPTIONS:
hostGroups = append(hostGroups, entity.Name)
}

inception := ""
if acl.Inception != nil {
inception = acl.Inception.Format("2006-01-02 15:04 MST")
}
expiration := ""
if acl.Expiration != nil {
expiration = acl.Expiration.Format("2006-01-02 15:04 MST")
}

table.Append([]string{
fmt.Sprintf("%d", acl.ID),
fmt.Sprintf("%d", acl.Weight),
strings.Join(userGroups, ", "),
strings.Join(hostGroups, ", "),
acl.HostPattern,
acl.Action,
inception,
expiration,
humanize.Time(acl.UpdatedAt),
humanize.Time(acl.CreatedAt),
acl.Comment,
Expand Down Expand Up @@ -236,6 +261,10 @@ GLOBAL OPTIONS:
cli.StringFlag{Name: "action, a", Usage: "Update action"},
cli.StringFlag{Name: "pattern, p", Usage: "Update host-pattern"},
cli.UintFlag{Name: "weight, w", Usage: "Update weight"},
cli.StringFlag{Name: "inception, i", Usage: "Update inception date-time"},
cli.BoolFlag{Name: "unset-inception", Usage: "Unset inception date-time"},
cli.BoolFlag{Name: "unset-expiration", Usage: "Unset expiration date-time"},
cli.StringFlag{Name: "expiration, e", Usage: "Update expiration date-time"},
cli.StringFlag{Name: "comment, c", Usage: "Update comment"},
cli.StringSliceFlag{Name: "assign-usergroup, ug", Usage: "Assign the ACL to new `USERGROUPS`"},
cli.StringSliceFlag{Name: "unassign-usergroup", Usage: "Unassign the ACL from `USERGROUPS`"},
Expand All @@ -258,17 +287,41 @@ GLOBAL OPTIONS:
tx := db.Begin()
for _, acl := range acls {
model := tx.Model(acl)
inception, err := parseOptionalTime(c.String("inception"))
if err != nil {
return err
}
expiration, err := parseOptionalTime(c.String("expiration"))
if err != nil {
return err
}

update := dbmodels.ACL{
Action: c.String("action"),
HostPattern: c.String("pattern"),
Weight: c.Uint("weight"),
Inception: inception,
Expiration: expiration,
Comment: c.String("comment"),
}
if err := model.Updates(update).Error; err != nil {
tx.Rollback()
return err
}

if c.Bool("unset-inception") {
if err := model.Update("inception", nil).Error; err != nil {
tx.Rollback()
return err
}
}
if c.Bool("unset-expiration") {
if err := model.Update("expiration", nil).Error; err != nil {
tx.Rollback()
return err
}
}

// associations
var appendUserGroups []dbmodels.UserGroup
var deleteUserGroups []dbmodels.UserGroup
Expand Down Expand Up @@ -2262,3 +2315,14 @@ func parseInputURL(input string) (*url.URL, error) {
}
return u, nil
}

func parseOptionalTime(input string) (*time.Time, error) {
if input != "" {
parsed, err := time.ParseInLocation("2006-01-02 15:04", input, time.Local)
if err != nil {
return nil, err
}
return &parsed, nil
}
return nil, nil
}
12 changes: 7 additions & 5 deletions pkg/dbmodels/dbmodels.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ type SSHKey struct {
Type string `valid:"required"`
Length uint `valid:"required"`
Fingerprint string `valid:"optional"`
PrivKey string `sql:"size:10000" valid:"required"`
PubKey string `sql:"size:10000" valid:"optional"`
PrivKey string `sql:"size:5000" valid:"required"`
PubKey string `sql:"size:1000" valid:"optional"`
Hosts []*Host `gorm:"ForeignKey:SSHKeyID"`
Comment string `valid:"optional"`
}
Expand All @@ -58,7 +58,7 @@ type Host struct {
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"`
HostKey []byte `sql:"size:1000" valid:"optional"`
Groups []*HostGroup `gorm:"many2many:host_host_groups;"`
Comment string `valid:"optional"`
Logging string `valid:"optional,host_logging_mode"`
Expand All @@ -69,8 +69,8 @@ type Host struct {
// UserKey defines a user public key used by sshportal to identify the user
type UserKey struct {
gorm.Model
Key []byte `sql:"size:10000" valid:"length(1|10000)"`
AuthorizedKey string `sql:"size:10000" valid:"required,length(1|10000)"`
Key []byte `sql:"size:1000" valid:"length(1|1000)"`
AuthorizedKey string `sql:"size:1000" valid:"required,length(1|1000)"`
UserID uint ``
User *User `gorm:"ForeignKey:UserID"`
Comment string `valid:"optional"`
Expand Down Expand Up @@ -118,6 +118,8 @@ type ACL struct {
Action string `valid:"required"`
Weight uint ``
Comment string `valid:"optional"`
Inception *time.Time
Expiration *time.Time
}

type Session struct {
Expand Down

0 comments on commit 9d2badf

Please sign in to comment.