mirror of
https://github.com/moul/sshportal.git
synced 2025-09-08 13:44:55 +08:00
commit
9cc09b320d
9 changed files with 122 additions and 8 deletions
|
@ -4,6 +4,7 @@
|
|||
|
||||
* The default created user now has the same username as the user starting sshportal (was hardcoded "admin")
|
||||
* Add Telnet support
|
||||
* Add TTY audit feature ([#23](https://github.com/moul/sshportal/issues/23)) by [@sabban](https://github.com/sabban)
|
||||
|
||||
## v1.7.1 (2018-01-03)
|
||||
|
||||
|
|
2
Makefile
2
Makefile
|
@ -33,7 +33,7 @@ test:
|
|||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
gometalinter --disable-all --enable=errcheck --enable=vet --enable=vetshadow --enable=golint --enable=gas --enable=ineffassign --enable=goconst --enable=goimports --enable=gofmt --exclude="should have comment" --enable=staticcheck --enable=gosimple --enable=misspell --deadline=20s .
|
||||
gometalinter --disable-all --enable=errcheck --enable=vet --enable=vetshadow --enable=golint --enable=gas --enable=ineffassign --enable=goconst --enable=goimports --enable=gofmt --exclude="should have comment" --enable=staticcheck --enable=gosimple --enable=misspell --deadline=60s .
|
||||
|
||||
.PHONY: backup
|
||||
backup:
|
||||
|
|
|
@ -37,6 +37,7 @@ Jump host/Jump server without the jump, a.k.a Transparent SSH bastion
|
|||
* Sensitive data encryption
|
||||
* Session management (see active connections, history, stats, stop)
|
||||
* Audit log (logging every user action)
|
||||
* Record TTY Session
|
||||
* Host Keys verifications shared across users
|
||||
* Healthcheck user (replying OK to any user)
|
||||
* SSH compatibility
|
||||
|
|
20
main.go
20
main.go
|
@ -74,6 +74,11 @@ func main() {
|
|||
Name: "aes-key",
|
||||
Usage: "Encrypt sensitive data in database (length: 16, 24 or 32)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "logs-location",
|
||||
Value: "./log",
|
||||
Usage: "Store user session files",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Name: "healthcheck",
|
||||
|
@ -130,12 +135,25 @@ func server(c *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// check for the logdir existence
|
||||
logsLocation, e := os.Stat(c.String("logs-location"))
|
||||
if e != nil {
|
||||
err = os.MkdirAll(c.String("logs-location"), os.ModeDir|os.FileMode(0750))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if !logsLocation.IsDir() {
|
||||
log.Fatal("log directory cannot be created")
|
||||
}
|
||||
}
|
||||
|
||||
opts := []ssh.Option{}
|
||||
// custom PublicKeyAuth handler
|
||||
opts = append(opts, ssh.PublicKeyAuth(publicKeyAuthHandler(db, c)))
|
||||
opts = append(opts, ssh.PasswordAuth(passwordAuthHandler(db, c)))
|
||||
|
||||
// retrieve sshportal SSH private key from databse
|
||||
// retrieve sshportal SSH private key from database
|
||||
opts = append(opts, func(srv *ssh.Server) error {
|
||||
var key SSHKey
|
||||
if err = SSHKeysByIdentifiers(db, []string{"host"}).First(&key).Error; err != nil {
|
||||
|
|
|
@ -3,13 +3,19 @@ package bastionsession
|
|||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"strings"
|
||||
"time"
|
||||
"os"
|
||||
"log"
|
||||
|
||||
"github.com/gliderlabs/ssh"
|
||||
"github.com/arkan/bastion/pkg/logchannel"
|
||||
gossh "golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Addr string
|
||||
Logs string
|
||||
ClientConfig *gossh.ClientConfig
|
||||
}
|
||||
|
||||
|
@ -35,24 +41,33 @@ func ChannelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewCh
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user := conn.User()
|
||||
// pipe everything
|
||||
return pipe(lreqs, rreqs, lch, rch)
|
||||
return pipe(lreqs, rreqs, lch, rch, config.Logs, user)
|
||||
}
|
||||
|
||||
func pipe(lreqs, rreqs <-chan *gossh.Request, lch, rch gossh.Channel) error {
|
||||
func pipe(lreqs, rreqs <-chan *gossh.Request, lch, rch gossh.Channel, logsLocation string, user string) error {
|
||||
defer func() {
|
||||
_ = lch.Close()
|
||||
_ = rch.Close()
|
||||
}()
|
||||
|
||||
errch := make(chan error, 1)
|
||||
file_name := strings.Join([]string{logsLocation, "/", user, "-", time.Now().Format(time.RFC3339)}, "") // get user
|
||||
f, err := os.OpenFile(file_name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0640)
|
||||
if err != nil {
|
||||
log.Fatalf("error: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("Session is recorded in %v", file_name)
|
||||
wrappedlch := logchannel.New(lch, f)
|
||||
go func() {
|
||||
_, _ = io.Copy(lch, rch)
|
||||
_, _ = io.Copy(wrappedlch, rch)
|
||||
errch <- errors.New("lch closed the connection")
|
||||
}()
|
||||
|
||||
|
||||
defer f.Close()
|
||||
|
||||
go func() {
|
||||
_, _ = io.Copy(rch, lch)
|
||||
errch <- errors.New("rch closed the connection")
|
||||
|
|
2
ssh.go
2
ssh.go
|
@ -96,6 +96,7 @@ func channelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewCh
|
|||
}
|
||||
|
||||
actx := ctx.Value(authContextKey).(*authContext)
|
||||
logsLocation := actx.globalContext.String("logs-location")
|
||||
|
||||
switch actx.userType() {
|
||||
case UserTypeBastion:
|
||||
|
@ -144,6 +145,7 @@ func channelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewCh
|
|||
err = bastionsession.ChannelHandler(srv, conn, newChan, ctx, bastionsession.Config{
|
||||
Addr: host.DialAddr(),
|
||||
ClientConfig: clientConfig,
|
||||
Logs: logsLocation,
|
||||
})
|
||||
|
||||
now := time.Now()
|
||||
|
|
22
vendor/github.com/arkan/bastion/LICENSE
generated
vendored
Normal file
22
vendor/github.com/arkan/bastion/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2016-2017 Florian Bertholin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
49
vendor/github.com/arkan/bastion/pkg/logchannel/logchannel.go
generated
vendored
Normal file
49
vendor/github.com/arkan/bastion/pkg/logchannel/logchannel.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
package logchannel
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type logChannel struct {
|
||||
channel ssh.Channel
|
||||
writer io.WriteCloser
|
||||
}
|
||||
|
||||
func writeTTYRecHeader(fd io.Writer, length int) {
|
||||
t := time.Now()
|
||||
|
||||
tv := syscall.NsecToTimeval(t.UnixNano())
|
||||
|
||||
binary.Write(fd, binary.LittleEndian, int32(tv.Sec))
|
||||
binary.Write(fd, binary.LittleEndian, int32(tv.Usec))
|
||||
binary.Write(fd, binary.LittleEndian, int32(length))
|
||||
}
|
||||
|
||||
func New(channel ssh.Channel, writer io.WriteCloser) *logChannel {
|
||||
return &logChannel{
|
||||
channel: channel,
|
||||
writer: writer,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *logChannel) Read(data []byte) (int, error) {
|
||||
return l.Read(data)
|
||||
}
|
||||
|
||||
func (l *logChannel) Write(data []byte) (int, error) {
|
||||
writeTTYRecHeader(l.writer, len(data))
|
||||
l.writer.Write(data)
|
||||
|
||||
return l.channel.Write(data)
|
||||
}
|
||||
|
||||
func (l *logChannel) Close() error {
|
||||
l.writer.Close()
|
||||
|
||||
return l.channel.Close()
|
||||
}
|
6
vendor/vendor.json
vendored
6
vendor/vendor.json
vendored
|
@ -8,6 +8,12 @@
|
|||
"revision": "648efa622239a2f6ff949fed78ee37b48d499ba4",
|
||||
"revisionTime": "2016-10-02T11:37:05Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "MHJo0MQ1wV3xSm0ncSn/aHaZR3Y=",
|
||||
"path": "github.com/arkan/bastion/pkg/logchannel",
|
||||
"revision": "0eb93ed2121907205ca69f46667a25f8b4320fde",
|
||||
"revisionTime": "2018-01-04T15:54:52Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "qe14CYEIsrbHmel1u0gsdZNFPLQ=",
|
||||
"path": "github.com/asaskevich/govalidator",
|
||||
|
|
Loading…
Add table
Reference in a new issue