Compare commits

...

38 Commits

Author SHA1 Message Date
Manfred Touron
e74f7221b5 Merge pull request #168 from moul/dev/moul/linters
fix: add more linters
2020-07-01 14:51:03 +02:00
Manfred Touron
f4fc3a90bc fix: add more linters
Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com>
2020-07-01 14:46:23 +02:00
Manfred Touron
df3aa6e165 Merge pull request #181 from moul/renovate/all
chore(deps): update all
2020-07-01 14:46:01 +02:00
Manfred Touron
986bcd7971 Merge pull request #185 from moul/dev/moul/windows 2020-07-01 14:17:50 +02:00
Manfred Touron
7f3ea431a1 Merge pull request #183 from jrrdev/exit_fix 2020-07-01 14:16:14 +02:00
Manfred Touron
dae0252857 chore: small fix for build on windows 2020-07-01 14:10:50 +02:00
Manfred Touron
33b8e5272c Merge pull request #184 from moul/dev/moul/renovate-tidy
chore: tell renovate to run go mod tidy
2020-07-01 14:10:15 +02:00
Manfred Touron
21e73757ac chore: tell renovate to run go mod tidy 2020-07-01 14:01:14 +02:00
jerard@alfa-safety.fr
bcb5d3b7ef fixup! Fix early closure of data stream. 2020-06-30 18:28:47 +02:00
jerard@alfa-safety.fr
d2f3f460b2 Fix early closure of data stream.
Closes moul/sshportal#55 and closes moul/sshportal#127
2020-06-30 10:28:39 +02:00
Renovate Bot
e06fe6f5a3 chore(deps): update all 2020-06-22 22:25:28 +00:00
Manfred Touron
fb9dabfe6b chore: bump deps 2020-06-09 12:11:41 +02:00
Manfred Touron
0e0cd8fed5 Merge pull request #180 from moul/dev/moul/update-project-layout
fix: update project layout
2020-06-09 12:10:17 +02:00
Manfred Touron
8959e1782f fix: update project layout
Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com>
2020-06-09 11:45:45 +02:00
Manfred Touron
33151105e0 Merge pull request #173 from moul/renovate/all
chore(deps): update all
2020-06-09 11:07:15 +02:00
Manfred Touron
77b40eb9ed Merge pull request #177 from NocFlame/master
Update README.md; Info about missing host keys
2020-06-09 11:07:01 +02:00
Manfred Touron
075dfd0aa7 Merge pull request #176 from Zatte/update_readme_shell_conditions
Update readme; special host names
2020-06-09 11:06:38 +02:00
Manfred Touron
5cf6b1c218 Merge pull request #174 from moul/renovate/docker-all
chore(deps): update all docker tags to v1.14.4
2020-06-06 18:21:26 +02:00
Renovate Bot
6527746a91 chore(deps): update all 2020-06-04 21:46:38 +00:00
Renovate Bot
020ca9c6b3 chore(deps): update all docker tags to v1.14.4 2020-06-03 00:16:26 +00:00
NocFlame
8c7831480b Update README.md
*What this PR does / why we need it:*
Current readme does not inform user of how to fix the behaviour of password promt when linking admin account

*Which issue this PR fixes: fixes #128*
Updated the readme to inform the user of a quick fix (ssh-keygen -t rsa) when association fails and user is asked for password
2020-05-28 11:12:28 +02:00
Mikael Rapp
e399dfd8e4 Update readme; special host names 2020-05-27 23:30:28 +02:00
Manfred Touron
be83c7148d Merge pull request #172 from moul/dev/moul/bump-deps4 2020-05-12 03:35:22 +02:00
Manfred Touron
ce187e8675 fix: bump deps 2020-05-12 03:30:27 +02:00
Manfred Touron
f13ede4ba7 Update README.md 2020-05-12 03:27:15 +02:00
Manfred Touron
fb061ed419 Merge pull request #138 from moul/renovate/all
Update all
2020-05-10 15:45:44 +02:00
Renovate Bot
b4a377f269 Update all 2020-05-02 08:14:28 +00:00
Manfred Touron
de6f37aa64 Merge pull request #170 from moul/renovate/docker-all
Update all Docker tags to v1.14.2
2020-04-10 10:11:05 +02:00
Renovate Bot
32219577b8 Update all Docker tags to v1.14.2 2020-04-10 00:21:11 +00:00
Manfred Touron
abc7329a71 Merge pull request #167 from moul/renovate/docker-all
Update all Docker tags to v1.14.1
2020-03-22 14:58:30 +01:00
Renovate Bot
675942e967 Update all Docker tags to v1.14.1 2020-03-21 00:28:54 +00:00
Manfred Touron
5b20cd501e Update README.md
See https://github.com/moul/sshportal/issues/164
2020-03-10 11:01:26 +01:00
Manfred Touron
b6aaf4d7cf Update README.md 2020-03-05 15:33:52 +01:00
Manfred Touron
972e232559 Merge pull request #143 from moul/renovate/docker-all
chore(deps): update all docker tags to v1.14.0
2020-03-01 17:17:37 +01:00
Manfred Touron
851a91b1a0 fix: fix deps 2020-02-28 22:12:46 +01:00
Renovate Bot
6a068dc430 chore(deps): update all docker tags to v1.14.0 2020-02-28 22:05:37 +01:00
Manfred Touron
2cdfcf60fe Merge pull request #163 from MitaliBo/master
Security fix for github.com/moby/moby
2020-02-28 22:04:24 +01:00
MitaliBo
cbf8263033 Update go.mod 2020-02-24 14:27:29 -08:00
23 changed files with 651 additions and 212 deletions

View File

@@ -1,7 +1,7 @@
defaults: &defaults
working_directory: /go/src/moul.io/sshportal
docker:
- image: circleci/golang:1.12.6
- image: circleci/golang:1.14.4
environment:
GO111MODULE: "on"
@@ -16,18 +16,6 @@ install_retry: &install_retry
version: 2
jobs:
go.build:
<<: *defaults
steps:
- checkout
- *install_retry
- run: /tmp/retry -m 3 go mod download
- run: /tmp/retry -m 3 go mod vendor
- run: /tmp/retry -m 3 make install
- run: GO111MODULE=off /tmp/retry -m 3 go test -v ./...
- run: /tmp/retry -m 3 curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s v1.12.2
- run: PATH=$PATH:$(pwd)/bin /tmp/retry -m 3 make lint
docker.integration:
<<: *defaults
steps:
@@ -48,6 +36,4 @@ workflows:
version: 2
build_and_integration:
jobs:
- go.build
- docker.integration
# requires: docker.build?

View File

@@ -2,5 +2,6 @@
"extends": [
"config:base"
],
"groupName": "all"
"groupName": "all",
"gomodTidy": true
}

88
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,88 @@
name: CI
on:
push:
tags:
- v*
branches:
- master
pull_request:
jobs:
docker-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build the Docker image
run: docker build . --file Dockerfile
golangci-lint:
name: golangci-lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: lint
uses: golangci/golangci-lint-action@v0.1.7
with:
version: v1.26
github-token: ${{ secrets.GITHUB_TOKEN }}
tests-on-windows:
needs: golangci-lint # run after golangci-lint action to not produce duplicated errors
runs-on: windows-latest
strategy:
matrix:
golang:
#- 1.13
- 1.14
steps:
- uses: actions/checkout@v2
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.golang }}
- name: Run tests on Windows
run: make.exe unittest
continue-on-error: true
tests-on-mac:
needs: golangci-lint # run after golangci-lint action to not produce duplicated errors
runs-on: macos-latest
strategy:
matrix:
golang:
- 1.14
steps:
- uses: actions/checkout@v2
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.golang }}
- uses: actions/cache@v1
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ matrix.golang }}-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-${{ matrix.golang }}-
- name: Run tests on Unix-like operating systems
run: make unittest
tests-on-linux:
needs: golangci-lint # run after golangci-lint action to not produce duplicated errors
runs-on: ubuntu-latest
strategy:
matrix:
golang:
- 1.11
- 1.12
- 1.13
- 1.14
steps:
- uses: actions/checkout@v2
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.golang }}
- uses: actions/cache@v1
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ matrix.golang }}-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-${{ matrix.golang }}-
- name: Run tests on Unix-like operating systems
run: make unittest

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
coverage.txt
dist/
*~
*#

View File

@@ -1,8 +1,10 @@
run:
deadline: 1m
tests: false
#skip-files:
# - ".*\\.gen\\.go"
skip-files:
- "testing.go"
- ".*\\.pb\\.go"
- ".*\\.gen\\.go"
linters-settings:
golint:
@@ -18,17 +20,36 @@ linters-settings:
linters:
disable-all: true
enable:
- goconst
- misspell
- bodyclose
- deadcode
- misspell
- structcheck
- depguard
- dogsled
#- dupl
- errcheck
- unused
- varcheck
- staticcheck
- unconvert
#- funlen
- gochecknoinits
#- gocognit
- goconst
- gocritic
#- gocyclo
- gofmt
- goimports
- golint
- gosimple
- govet
- ineffassign
- interfacer
#- maligned
- misspell
- nakedret
- prealloc
- scopelint
- staticcheck
- structcheck
#- stylecheck
- typecheck
- unconvert
- unparam
- unused
- varcheck
- whitespace

29
AUTHORS Normal file
View File

@@ -0,0 +1,29 @@
# This file lists all individuals having contributed content to the repository.
# For how it is generated, see 'https://github.com/moul/rules.mk'
ahh <ahamidullah@gmail.com>
Alen Masic <alenn.masic@gmail.com>
Alexander Turner <me@alexturner.co>
bozzo <bozzo@users.noreply.github.com>
fossabot <badges@fossa.io>
Jean-Louis Férey <jeanlouis.ferey@orange.com>
Jess <jessachandler@gmail.com>
Jonathan Lestrelin <jonathan.lestrelin@gmail.com>
Julien Dessaux <julien.dessaux@adyxax.org>
Manfred Touron <94029+moul@users.noreply.github.com>
Manfred Touron <m@42.am>
Manuel <manuel.sabban@nbs-system.com>
Manuel Sabban <manu@sabban.eu>
Manuel Sabban <msa@nbs-system.com>
Mathieu Pasquet <mathieu.pasquet@alterway.fr>
Mikael Rapp <micke.rapp@gmail.com>
MitaliBo <mitali.bisht14@gmail.com>
Nelly Asher <karmelylle@rambler.ru>
NocFlame <aad@nocflame.se>
Quentin Perez <qperez42@gmail.com>
Renovate Bot <bot@renovateapp.com>
Shawn Wang <shawn111@gmail.com>
Valentin Daviot <valentin.daviot@alterway.fr>
valentin.daviot <valentin.daviot@alterway.fr>
welderpb <welderpb@users.noreply.github.com>
Дмитрий Шульгачик <tech@uniplug.ru>

View File

@@ -1,5 +1,5 @@
# build
FROM golang:1.12.6 as builder
FROM golang:1.14.4 as builder
ENV GO111MODULE=on
WORKDIR /go/src/moul.io/sshportal
COPY go.mod go.sum ./

View File

@@ -1,18 +1,15 @@
GIT_SHA ?= $(shell git rev-parse HEAD)
GIT_TAG ?= $(shell git describe --tags --always)
GIT_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD)
LDFLAGS ?= -X main.GitSha=$(GIT_SHA) -X main.GitTag=$(GIT_TAG) -X main.GitBranch=$(GIT_BRANCH)
VERSION ?= $(shell grep 'VERSION =' main.go | cut -d'"' -f2)
GOPKG ?= moul.io/sshportal
GOBINS ?= .
DOCKER_IMAGE ?= moul/sshportal
VERSION ?= `git describe --tags --always`
VCS_REF ?= `git rev-parse --short HEAD`
GO_INSTALL_OPTS = -ldflags="-X main.GitSha=$(VCS_REF) -X main.GitTag=$(VERSION)"
include rules.mk
DB_VERSION ?= v$(shell grep -E 'ID: "[0-9]+",' pkg/bastion/dbinit.go | tail -n 1 | cut -d'"' -f2)
AES_KEY ?= my-dummy-aes-key
GO ?= GO111MODULE=on go
.PHONY: install
install:
$(GO) install -v -ldflags '$(LDFLAGS)' .
.PHONY: docker.build
docker.build:
docker build -t moul/sshportal .
.PHONY: integration
integration:
@@ -27,19 +24,10 @@ dev:
-$(GO) get github.com/githubnemo/CompileDaemon
CompileDaemon -exclude-dir=.git -exclude=".#*" -color=true -command="./sshportal server --debug --bind-address=:$(PORT) --aes-key=$(AES_KEY) $(EXTRA_RUN_OPTS)" .
.PHONY: test
test:
$(GO) test -i ./...
$(GO) test -v ./...
.PHONY: lint
lint:
golangci-lint run --verbose ./...
.PHONY: backup
backup:
mkdir -p data/backups
cp sshportal.db data/backups/$(shell date +%s)-$(VERSION)-sshportal.sqlite
cp sshportal.db data/backups/$(shell date +%s)-$(DB_VERSION)-sshportal.sqlite
doc:
dot -Tsvg ./.assets/overview.dot > ./.assets/overview.svg

View File

@@ -58,6 +58,8 @@ Shared connection to localhost closed.
$
```
If the association fails and you are promted for a password, verify that the host you're connecting from has a SSH key set up or generate one with ```ssh-keygen -t rsa```
Drop an interactive administrator shell
```console
@@ -158,7 +160,7 @@ If you need to invite multiple people to an event (hackathon, course, etc), the
* Sensitive data encryption
* Session management (see active connections, history, stats, stop)
* Audit log (logging every user action)
* Record TTY Session
* Record TTY Session (with [ttyrec](https://en.wikipedia.org/wiki/Ttyrec) format, use `ttyplay` for replay)
* Tunnels logging
* Host Keys verifications shared across users
* Healthcheck user (replying OK to any user)
@@ -178,6 +180,8 @@ If you need to invite multiple people to an event (hackathon, course, etc), the
**(Known) limitations**
* Does not work (yet?) with [`mosh`](https://mosh.org/)
* It is not possible for a user to access a host with the same name as the user. This is easily circumvented by changing the user name, especially since the most common use cases does not expose it.
* It is not possible access a host named `healthcheck` as this is a built in command.
---
@@ -232,7 +236,7 @@ docker logs -f sshportal
Get the latest version using GO.
```sh
go get -u moul.io/sshportal
GO111MODULE=on go get -u moul.io/sshportal
```
---
@@ -267,7 +271,7 @@ cp sshportal.db sshportal.db.bkp
`sshportal` embeds a configuration CLI.
By default, the configuration user is `admin`, (can be changed using `--config-user=<value>` when starting the server.
By default, the configuration user is `admin`, (can be changed using `--config-user=<value>` when starting the server. The shell is also accessible through `ssh [username]@portal.example.org`.
Each commands can be run directly by using this syntax: `ssh admin@portal.example.org <command> [args]`:

28
go.mod
View File

@@ -1,35 +1,33 @@
module moul.io/sshportal
require (
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/creack/pty v1.1.9 // indirect
github.com/creack/pty v1.1.11 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/docker v1.13.1
github.com/dustin/go-humanize v1.0.0
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
github.com/gliderlabs/ssh v0.2.2
github.com/gliderlabs/ssh v0.3.0
github.com/go-sql-driver/mysql v1.5.0 // indirect
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
github.com/jinzhu/gorm v1.9.12
github.com/jinzhu/gorm v1.9.14
github.com/kr/pty v1.1.8
github.com/mattn/go-colorable v0.1.4 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/mattn/go-runewidth v0.0.8 // indirect
github.com/mattn/go-colorable v0.1.6 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
github.com/moby/moby v0.7.3-0.20190103212154-2b7e084dc98b
github.com/olekukonko/tablewriter v0.0.4
github.com/reiver/go-oi v1.0.0
github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e
github.com/sabban/bastion v0.0.0-20180110125408-b9d3c9b1f4d3
github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3 // indirect
github.com/smartystreets/goconvey v1.6.4-0.20190330032615-68dc04aab96a
github.com/urfave/cli v1.22.2
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6
golang.org/x/sys v0.0.0-20200217220822-9197077df867 // indirect
github.com/smartystreets/goconvey v1.6.4
github.com/urfave/cli v1.22.4
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 // indirect
gopkg.in/gormigrate.v1 v1.6.0
moul.io/srand v1.4.0
)
go 1.12.6
go 1.14

60
go.sum
View File

@@ -1,31 +1,33 @@
cloud.google.com/go v0.33.1/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06GQ59hwDQAvmK1qxOQGB3WuVTRoY0okPTAv0=
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY=
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/gliderlabs/ssh v0.3.0 h1:7GcKy4erEljCE/QeQ2jTVpu+3f3zkpZOxOJjFYkMqYU=
github.com/gliderlabs/ssh v0.3.0/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
@@ -43,6 +45,8 @@ github.com/jinzhu/gorm v1.9.2 h1:lCvgEaqe/HVE+tjAR2mt4HbbHAZsQOv3XAZiEZV37iw=
github.com/jinzhu/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q=
github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs=
github.com/jinzhu/gorm v1.9.14 h1:Kg3ShyTPcM6nzVo148fRrcMO6MNKuqtOUwnzqMgVniM=
github.com/jinzhu/gorm v1.9.14/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k=
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
@@ -61,23 +65,21 @@ github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/moby/moby v0.7.3-0.20190103212154-2b7e084dc98b h1:z/nBoaNNRdlg5oyabuQyZshr/YsANYEcjI+IiNT/U6U=
github.com/moby/moby v0.7.3-0.20190103212154-2b7e084dc98b/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -95,28 +97,34 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3 h1:hBSHahWMEgzwRyS6dRpxY0XyjZsHyQ61s084wo5PJe0=
github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4-0.20190330032615-68dc04aab96a h1:XmieTxr5Ejfoo1izsMZO4qWqOTpYagCqNMJyP87ONS0=
github.com/smartystreets/goconvey v1.6.4-0.20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg=
golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867 h1:JoRuNIf+rpHl+VhScRQQvzbHed86tKkqwPMV34T8myw=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y=
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=

10
main.go
View File

@@ -13,14 +13,10 @@ import (
)
var (
// Version should be updated by hand at each release
Version = "1.10.0+dev"
// GitTag will be overwritten automatically by the build system
GitTag string
GitTag = "n/a"
// GitSha will be overwritten automatically by the build system
GitSha string
// GitBranch will be overwritten automatically by the build system
GitBranch string
GitSha = "n/a"
)
func main() {
@@ -29,7 +25,7 @@ func main() {
app := cli.NewApp()
app.Name = path.Base(os.Args[0])
app.Author = "Manfred Touron"
app.Version = Version + " (" + GitSha + ")"
app.Version = GitTag + " (" + GitSha + ")"
app.Email = "https://moul.io/sshportal"
app.Commands = []cli.Command{
{

View File

@@ -12,7 +12,7 @@ func (a byWeight) Len() int { return len(a) }
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, error) {
func checkACLs(user dbmodels.User, host dbmodels.Host) string {
// shared ACLs between user and host
aclMap := map[uint]*dbmodels.ACL{}
for _, userGroup := range user.Groups {
@@ -30,7 +30,7 @@ func checkACLs(user dbmodels.User, host dbmodels.Host) (string, error) {
// deny by default if no shared ACL
if len(aclMap) == 0 {
return string(dbmodels.ACLActionDeny), nil // default action
return string(dbmodels.ACLActionDeny) // default action
}
// transform map to slice and sort it
@@ -40,5 +40,5 @@ func checkACLs(user dbmodels.User, host dbmodels.Host) (string, error) {
}
sort.Sort(byWeight(acls))
return acls[0].Action, nil
return acls[0].Action
}

View File

@@ -43,8 +43,7 @@ func TestCheckACLs(t *testing.T) {
db.Preload("Groups").Preload("Groups.ACLs").Find(&users)
// test
action, err := checkACLs(users[0], hosts[0])
c.So(err, ShouldBeNil)
action := checkACLs(users[0], hosts[0])
c.So(action, ShouldEqual, dbmodels.ACLActionAllow)
})
}

View File

@@ -261,14 +261,14 @@ func DBInit(db *gorm.DB) error {
return err
}
var users []dbmodels.User
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 {
if err := tx.Save(user).Error; err != nil {
return err
}
}
@@ -358,7 +358,7 @@ func DBInit(db *gorm.DB) error {
}, {
ID: "24",
Migrate: func(tx *gorm.DB) error {
var userKeys []dbmodels.UserKey
var userKeys []*dbmodels.UserKey
if err := db.Find(&userKeys).Error; err != nil {
return err
}
@@ -369,7 +369,7 @@ func DBInit(db *gorm.DB) error {
return err
}
userKey.AuthorizedKey = string(gossh.MarshalAuthorizedKey(key))
if err := db.Model(&userKey).Updates(&userKey).Error; err != nil {
if err := db.Model(userKey).Updates(userKey).Error; err != nil {
return err
}
}
@@ -422,14 +422,14 @@ func DBInit(db *gorm.DB) error {
}, {
ID: "27",
Migrate: func(tx *gorm.DB) error {
var sessions []dbmodels.Session
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 {
if err := db.Model(session).Updates(map[string]interface{}{"stopped_at": nil}).Error; err != nil {
return err
}
}

View File

@@ -1,7 +1,6 @@
package bastion // import "moul.io/sshportal/pkg/bastion"
import (
"errors"
"fmt"
"io"
"log"
@@ -20,7 +19,7 @@ type sessionConfig struct {
ClientConfig *gossh.ClientConfig
}
func multiChannelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx ssh.Context, configs []sessionConfig, sessionID uint) error {
func multiChannelHandler(conn *gossh.ServerConn, newChan gossh.NewChannel, ctx ssh.Context, configs []sessionConfig, sessionID uint) error {
var lastClient *gossh.Client
switch newChan.ChannelType() {
case "session":
@@ -124,6 +123,7 @@ func pipe(lreqs, rreqs <-chan *gossh.Request, lch, rch gossh.Channel, logsLocati
}()
errch := make(chan error, 1)
quit := make(chan string, 1)
channeltype := newChan.ChannelType()
filename := strings.Join([]string{logsLocation, "/", user, "-", username, "-", channeltype, "-", fmt.Sprint(sessionID), "-", time.Now().Format(time.RFC3339)}, "") // get user
@@ -139,15 +139,15 @@ func pipe(lreqs, rreqs <-chan *gossh.Request, lch, rch gossh.Channel, logsLocati
log.Printf("Session %v is recorded in %v", channeltype, filename)
if channeltype == "session" {
wrappedlch := logchannel.New(lch, f)
go func() {
go func(quit chan string) {
_, _ = io.Copy(wrappedlch, rch)
errch <- errors.New("lch closed the connection")
}()
quit <- "rch"
}(quit)
go func() {
go func(quit chan string) {
_, _ = io.Copy(rch, lch)
errch <- errors.New("rch closed the connection")
}()
quit <- "lch"
}(quit)
}
if channeltype == "direct-tcpip" {
d := logTunnelForwardData{}
@@ -156,23 +156,19 @@ func pipe(lreqs, rreqs <-chan *gossh.Request, lch, rch gossh.Channel, logsLocati
}
wrappedlch := newLogTunnel(lch, f, d.SourceHost)
wrappedrch := newLogTunnel(rch, f, d.DestinationHost)
go func() {
go func(quit chan string) {
_, _ = io.Copy(wrappedlch, rch)
errch <- errors.New("lch closed the connection")
}()
quit <- "rch"
}(quit)
go func() {
go func(quit chan string) {
_, _ = io.Copy(wrappedrch, lch)
errch <- errors.New("rch closed the connection")
}()
quit <- "lch"
}(quit)
}
for {
select {
case req := <-lreqs: // forward ssh requests from local to remote
if req == nil {
return nil
}
go func(quit chan string) {
for req := range lreqs {
b, err := rch.SendRequest(req.Type, req.WantReply, req.Payload)
if req.Type == "exec" {
wrappedlch := logchannel.New(lch, f)
@@ -183,24 +179,58 @@ func pipe(lreqs, rreqs <-chan *gossh.Request, lch, rch gossh.Channel, logsLocati
}
if err != nil {
return err
errch <- err
}
if err2 := req.Reply(b, nil); err2 != nil {
return err2
}
case req := <-rreqs: // forward ssh requests from remote to local
if req == nil {
return nil
errch <- err2
}
}
quit <- "lreqs"
}(quit)
go func(quit chan string) {
for req := range rreqs {
b, err := lch.SendRequest(req.Type, req.WantReply, req.Payload)
if err != nil {
return err
errch <- err
}
if err2 := req.Reply(b, nil); err2 != nil {
return err2
errch <- err2
}
}
quit <- "rreqs"
}(quit)
lchEOF, rchEOF, lchClosed, rchClosed := false, false, false, false
for {
select {
case err := <-errch:
return err
case q := <-quit:
switch q {
case "lch":
lchEOF = true
_ = rch.CloseWrite()
case "rch":
rchEOF = true
_ = lch.CloseWrite()
case "lreqs":
lchClosed = true
case "rreqs":
rchClosed = true
}
if lchEOF && lchClosed && !rchClosed {
rch.Close()
}
if rchEOF && rchClosed && !lchClosed {
lch.Close()
}
if lchEOF && rchEOF && lchClosed && rchClosed {
return nil
}
}
}
}

View File

@@ -14,10 +14,10 @@ import (
shlex "github.com/anmitsu/go-shlex"
"github.com/asaskevich/govalidator"
"github.com/docker/docker/pkg/namesgenerator"
humanize "github.com/dustin/go-humanize"
"github.com/gliderlabs/ssh"
"github.com/mgutz/ansi"
"github.com/moby/moby/pkg/namesgenerator"
"github.com/olekukonko/tablewriter"
"github.com/urfave/cli"
gossh "golang.org/x/crypto/ssh"
@@ -41,7 +41,7 @@ const (
naMessage = "n/a"
)
func shell(s ssh.Session, version, gitSha, gitTag, gitBranch string) error {
func shell(s ssh.Session, version, gitSha, gitTag string) error {
var (
sshCommand = s.Command()
actx = s.Context().Value(authContextKey).(*authContext)
@@ -67,6 +67,8 @@ GLOBAL OPTIONS:
app.Writer = s
app.HideVersion = true
dbmodels.InitValidator()
var (
myself = &actx.user
db = actx.db
@@ -173,10 +175,8 @@ GLOBAL OPTIONS:
return err
}
acls = append(acls, &acl)
} else {
if err := query.Find(&acls).Error; err != nil {
return err
}
} else if err := query.Find(&acls).Error; err != nil {
return err
}
if c.Bool("quiet") {
for _, acl := range acls {
@@ -250,14 +250,14 @@ GLOBAL OPTIONS:
return err
}
var acls []dbmodels.ACL
var acls []*dbmodels.ACL
if err := dbmodels.ACLsByIdentifiers(db, c.Args()).Find(&acls).Error; err != nil {
return err
}
tx := db.Begin()
for _, acl := range acls {
model := tx.Model(&acl)
model := tx.Model(acl)
update := dbmodels.ACL{
Action: c.String("action"),
HostPattern: c.String("pattern"),
@@ -477,6 +477,7 @@ GLOBAL OPTIONS:
}
}
for _, host := range config.Hosts {
host := host
crypto.HostDecrypt(actx.aesKey, host)
if !c.Bool("decrypt") {
if err := crypto.HostEncrypt(actx.aesKey, host); err != nil {
@@ -489,30 +490,35 @@ GLOBAL OPTIONS:
}
}
for _, user := range config.Users {
user := user
if err := tx.FirstOrCreate(&user).Error; err != nil {
tx.Rollback()
return err
}
}
for _, acl := range config.ACLs {
acl := acl
if err := tx.FirstOrCreate(&acl).Error; err != nil {
tx.Rollback()
return err
}
}
for _, hostGroup := range config.HostGroups {
hostGroup := hostGroup
if err := tx.FirstOrCreate(&hostGroup).Error; err != nil {
tx.Rollback()
return err
}
}
for _, userGroup := range config.UserGroups {
userGroup := userGroup
if err := tx.FirstOrCreate(&userGroup).Error; err != nil {
tx.Rollback()
return err
}
}
for _, sshKey := range config.SSHKeys {
sshKey := sshKey
crypto.SSHKeyDecrypt(actx.aesKey, sshKey)
if !c.Bool("decrypt") {
if err := crypto.SSHKeyEncrypt(actx.aesKey, sshKey); err != nil {
@@ -525,24 +531,28 @@ GLOBAL OPTIONS:
}
}
for _, userKey := range config.UserKeys {
userKey := userKey
if err := tx.FirstOrCreate(&userKey).Error; err != nil {
tx.Rollback()
return err
}
}
for _, setting := range config.Settings {
setting := setting
if err := tx.FirstOrCreate(&setting).Error; err != nil {
tx.Rollback()
return err
}
}
for _, session := range config.Sessions {
session := session
if err := tx.FirstOrCreate(&session).Error; err != nil {
tx.Rollback()
return err
}
}
for _, event := range config.Events {
event := event
if err := tx.FirstOrCreate(&event).Error; err != nil {
tx.Rollback()
return err
@@ -612,10 +622,8 @@ GLOBAL OPTIONS:
return err
}
events = append(events, event)
} else {
if err := query.Find(&events).Error; err != nil {
return err
}
} else if err := query.Find(&events).Error; err != nil {
return err
}
if c.Bool("quiet") {
@@ -799,10 +807,8 @@ GLOBAL OPTIONS:
return err
}
hosts = append(hosts, &host)
} else {
if err := query.Find(&hosts).Error; err != nil {
return err
}
} else if err := query.Find(&hosts).Error; err != nil {
return err
}
if c.Bool("quiet") {
@@ -820,7 +826,7 @@ GLOBAL OPTIONS:
authKey := ""
if host.SSHKeyID > 0 {
var key dbmodels.SSHKey
db.Model(&host).Related(&key)
db.Model(host).Related(&key)
authKey = key.Name
}
groupNames := []string{}
@@ -830,7 +836,7 @@ GLOBAL OPTIONS:
var hop string
if host.HopID != 0 {
var hopHost dbmodels.Host
db.Model(&host).Related(&hopHost, "HopID")
db.Model(host).Related(&hopHost, "HopID")
hop = hopHost.Name
} else {
hop = ""
@@ -900,6 +906,7 @@ GLOBAL OPTIONS:
tx := db.Begin()
for _, host := range hosts {
host := host
model := tx.Model(&host)
// simple fields
for _, fieldname := range []string{"name", "comment"} {
@@ -1063,10 +1070,8 @@ GLOBAL OPTIONS:
return err
}
hostGroups = append(hostGroups, &hostGroup)
} else {
if err := query.Find(&hostGroups).Error; err != nil {
return err
}
} else if err := query.Find(&hostGroups).Error; err != nil {
return err
}
if c.Bool("quiet") {
@@ -1127,7 +1132,7 @@ GLOBAL OPTIONS:
return err
}
var hostgroups []dbmodels.HostGroup
var hostgroups []*dbmodels.HostGroup
if err := dbmodels.HostGroupsByIdentifiers(db, c.Args()).Find(&hostgroups).Error; err != nil {
return err
}
@@ -1138,7 +1143,7 @@ GLOBAL OPTIONS:
tx := db.Begin()
for _, hostgroup := range hostgroups {
model := tx.Model(&hostgroup)
model := tx.Model(hostgroup)
// simple fields
for _, fieldname := range []string{"name", "comment"} {
if c.String(fieldname) != "" {
@@ -1180,9 +1185,9 @@ GLOBAL OPTIONS:
fmt.Fprintf(s, "User email: %s\n", myself.Email)
fmt.Fprintf(s, "Version: %s\n", version)
fmt.Fprintf(s, "GIT SHA: %s\n", gitSha)
fmt.Fprintf(s, "GIT Branch: %s\n", gitBranch)
fmt.Fprintf(s, "GIT Tag: %s\n", gitTag)
// FIXME: gormigrate version
// FIXME: add info about current server (network, cpu, ram, OS)
// FIXME: add info about current user
// FIXME: add active connections
@@ -1342,10 +1347,8 @@ GLOBAL OPTIONS:
return err
}
sshKeys = append(sshKeys, &sshKey)
} else {
if err := query.Find(&sshKeys).Error; err != nil {
return err
}
} else if err := query.Find(&sshKeys).Error; err != nil {
return err
}
if c.Bool("quiet") {
for _, sshKey := range sshKeys {
@@ -1584,10 +1587,8 @@ GLOBAL OPTIONS:
return err
}
users = append(users, &user)
} else {
if err := query.Find(&users).Error; err != nil {
return err
}
} else if err := query.Find(&users).Error; err != nil {
return err
}
if c.Bool("quiet") {
for _, user := range users {
@@ -1661,7 +1662,7 @@ GLOBAL OPTIONS:
}
// FIXME: check if unset-admin + user == myself
var users []dbmodels.User
var users []*dbmodels.User
if err := dbmodels.UsersByIdentifiers(db, c.Args()).Find(&users).Error; err != nil {
return err
}
@@ -1676,7 +1677,7 @@ GLOBAL OPTIONS:
tx := db.Begin()
for _, user := range users {
model := tx.Model(&user)
model := tx.Model(user)
// simple fields
for _, fieldname := range []string{"name", "email", "comment"} {
if c.String(fieldname) != "" {
@@ -1814,10 +1815,8 @@ GLOBAL OPTIONS:
return err
}
userGroups = append(userGroups, &userGroup)
} else {
if err := query.Find(&userGroups).Error; err != nil {
return err
}
} else if err := query.Find(&userGroups).Error; err != nil {
return err
}
if c.Bool("quiet") {
for _, userGroup := range userGroups {
@@ -1877,7 +1876,7 @@ GLOBAL OPTIONS:
return err
}
var usergroups []dbmodels.UserGroup
var usergroups []*dbmodels.UserGroup
if err := dbmodels.UserGroupsByIdentifiers(db, c.Args()).Find(&usergroups).Error; err != nil {
return err
}
@@ -1888,7 +1887,7 @@ GLOBAL OPTIONS:
tx := db.Begin()
for _, usergroup := range usergroups {
model := tx.Model(&usergroup)
model := tx.Model(usergroup)
// simple fields
for _, fieldname := range []string{"name", "comment"} {
if c.String(fieldname) != "" {
@@ -2001,10 +2000,8 @@ GLOBAL OPTIONS:
return err
}
userKeys = append(userKeys, &userKey)
} else {
if err := query.Find(&userKeys).Error; err != nil {
return err
}
} else if err := query.Find(&userKeys).Error; err != nil {
return err
}
if c.Bool("quiet") {
for _, userKey := range userKeys {
@@ -2112,7 +2109,6 @@ GLOBAL OPTIONS:
factor := 1
for len(sessions) >= limit*factor {
var additionnalSessions []*dbmodels.Session
offset = limit * factor

View File

@@ -149,7 +149,7 @@ func ChannelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewCh
return
}
go func() {
err = multiChannelHandler(srv, conn, newChan, ctx, sessionConfigs, sess.ID)
err = multiChannelHandler(conn, newChan, ctx, sessionConfigs, sess.ID)
if err != nil {
log.Printf("Error: %v", err)
}
@@ -160,8 +160,7 @@ func ChannelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewCh
ErrMsg: fmt.Sprintf("%v", err),
StoppedAt: &now,
}
switch sessUpdate.ErrMsg {
case "lch closed the connection", "rch closed the connection":
if err == nil {
sessUpdate.ErrMsg = ""
}
actx.db.Model(&sess).Updates(&sessUpdate)
@@ -205,13 +204,11 @@ func bastionClientConfig(ctx ssh.Context, host *dbmodels.Host) (*gossh.ClientCon
if err = actx.db.Preload("Groups").Preload("Groups.ACLs").Where("id = ?", host.ID).First(&tmpHost).Error; err != nil {
return nil, err
}
action, err2 := checkACLs(tmpUser, tmpHost)
if err2 != nil {
return nil, err2
}
action := checkACLs(tmpUser, tmpHost)
switch action {
case string(dbmodels.ACLActionAllow):
// do nothing
case string(dbmodels.ACLActionDeny):
return nil, fmt.Errorf("you don't have permission to that host")
default:
@@ -220,7 +217,7 @@ func bastionClientConfig(ctx ssh.Context, host *dbmodels.Host) (*gossh.ClientCon
return clientConfig, nil
}
func ShellHandler(s ssh.Session, version, gitSha, gitTag, gitBranch string) {
func ShellHandler(s ssh.Session, version, gitSha, gitTag string) {
actx := s.Context().Value(authContextKey).(*authContext)
if actx.userType() != userTypeHealthcheck {
log.Printf("New connection(shell): sshUser=%q remote=%q local=%q command=%q dbUser=id:%d,email:%s", s.User(), s.RemoteAddr(), s.LocalAddr(), s.Command(), actx.user.ID, actx.user.Email)
@@ -241,7 +238,7 @@ func ShellHandler(s ssh.Session, version, gitSha, gitTag, gitBranch string) {
fmt.Fprintln(s, "OK")
return
case userTypeShell:
if err := shell(s, version, gitSha, gitTag, gitBranch); err != nil {
if err := shell(s, version, gitSha, gitTag); err != nil {
fmt.Fprintf(s, "error: %v\n", err)
_ = s.Exit(1)
}

View File

@@ -5,12 +5,10 @@ import (
"fmt"
"log"
"net/url"
"regexp"
"strconv"
"strings"
"time"
"github.com/asaskevich/govalidator"
"github.com/jinzhu/gorm"
gossh "golang.org/x/crypto/ssh"
)
@@ -166,18 +164,6 @@ const (
BastionSchemeTelnet BastionScheme = "telnet"
)
func init() {
unixUserRegexp := regexp.MustCompile("[a-z_][a-z0-9_-]*")
govalidator.CustomTypeTagMap.Set("unix_user", govalidator.CustomTypeValidator(func(i interface{}, context interface{}) bool {
name, ok := i.(string)
if !ok {
return false
}
return unixUserRegexp.MatchString(name)
}))
}
// Host helpers
func (host *Host) DialAddr() string {

19
pkg/dbmodels/validator.go Normal file
View File

@@ -0,0 +1,19 @@
package dbmodels
import (
"regexp"
"github.com/asaskevich/govalidator"
)
func InitValidator() {
unixUserRegexp := regexp.MustCompile("[a-z_][a-z0-9_-]*")
govalidator.CustomTypeTagMap.Set("unix_user", govalidator.CustomTypeValidator(func(i interface{}, context interface{}) bool {
name, ok := i.(string)
if !ok {
return false
}
return unixUserRegexp.MatchString(name)
}))
}

278
rules.mk Normal file
View File

@@ -0,0 +1,278 @@
# +--------------------------------------------------------------+
# | * * * moul.io/rules.mk |
# +--------------------------------------------------------------+
# | |
# | ++ ______________________________________ |
# | ++++ / \ |
# | ++++ | | |
# | ++++++++++ | https://moul.io/rules.mk is a set | |
# | +++ | | of common Makefile rules that can | |
# | ++ | | be configured from the Makefile | |
# | + -== ==| | or with environment variables. | |
# | ( <*> <*> | | |
# | | | /| Manfred Touron | |
# | | _) / | manfred.life | |
# | | +++ / \______________________________________/ |
# | \ =+ / |
# | \ + |
# | |\++++++ |
# | | ++++ ||// |
# | ___| |___ _||/__ __|
# | / --- \ \| ||| __ _ ___ __ __/ /|
# |/ | | \ \ / / ' \/ _ \/ // / / |
# || | | | | | /_/_/_/\___/\_,_/_/ |
# +--------------------------------------------------------------+
all: help
##
## Common helpers
##
rwildcard = $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
##
## rules.mk
##
ifneq ($(wildcard rules.mk),)
.PHONY: rulesmk.bumpdeps
rulesmk.bumpdeps:
wget -O rules.mk https://raw.githubusercontent.com/moul/rules.mk/master/rules.mk
BUMPDEPS_STEPS += rulesmk.bumpdeps
endif
##
## Maintainer
##
ifneq ($(wildcard .git/HEAD),)
.PHONY: generate.authors
generate.authors: AUTHORS
AUTHORS: .git/
echo "# This file lists all individuals having contributed content to the repository." > AUTHORS
echo "# For how it is generated, see 'https://github.com/moul/rules.mk'" >> AUTHORS
echo >> AUTHORS
git log --format='%aN <%aE>' | LC_ALL=C.UTF-8 sort -uf >> AUTHORS
GENERATE_STEPS += generate.authors
endif
##
## Golang
##
ifndef GOPKG
ifneq ($(wildcard go.mod),)
GOPKG = $(shell sed '/module/!d;s/^omdule\ //' go.mod)
endif
endif
ifdef GOPKG
GO ?= go
GOPATH ?= $(HOME)/go
GO_INSTALL_OPTS ?=
GO_TEST_OPTS ?= -test.timeout=30s
GOMOD_DIR ?= .
GOCOVERAGE_FILE ?= ./coverage.txt
ifdef GOBINS
.PHONY: go.install
go.install:
@set -e; for dir in $(GOBINS); do ( set -xe; \
cd $$dir; \
$(GO) install $(GO_INSTALL_OPTS) .; \
); done
INSTALL_STEPS += go.install
.PHONY: go.release
go.release:
goreleaser --snapshot --skip-publish --rm-dist
@echo -n "Do you want to release? [y/N] " && read ans && \
if [ $${ans:-N} = y ]; then set -xe; goreleaser --rm-dist; fi
RELEASE_STEPS += go.release
endif
.PHONY: go.unittest
go.unittest:
@echo "mode: atomic" > /tmp/gocoverage
@set -e; for dir in `find $(GOMOD_DIR) -type f -name "go.mod" | grep -v /vendor/ | sed 's@/[^/]*$$@@' | sort | uniq`; do (set -e; (set -xe; \
cd $$dir; \
$(GO) test $(GO_TEST_OPTS) -cover -coverprofile=/tmp/profile.out -covermode=atomic -race ./...); \
if [ -f /tmp/profile.out ]; then \
cat /tmp/profile.out | sed "/mode: atomic/d" >> /tmp/gocoverage; \
rm -f /tmp/profile.out; \
fi); done
@mv /tmp/gocoverage $(GOCOVERAGE_FILE)
.PHONY: go.checkdoc
go.checkdoc:
go doc $(GOMOD_DIR)
.PHONY: go.coverfunc
go.coverfunc: go.unittest
go tool cover -func=$(GOCOVERAGE_FILE) | grep -v .pb.go: | grep -v .pb.gw.go:
.PHONY: go.lint
go.lint:
@set -e; for dir in `find $(GOMOD_DIR) -type f -name "go.mod" | grep -v /vendor/ | sed 's@/[^/]*$$@@' | sort | uniq`; do ( set -xe; \
cd $$dir; \
golangci-lint run --verbose ./...; \
); done
.PHONY: go.tidy
go.tidy:
@set -e; for dir in `find $(GOMOD_DIR) -type f -name "go.mod" | grep -v /vendor/ | sed 's@/[^/]*$$@@' | sort | uniq`; do ( set -xe; \
cd $$dir; \
$(GO) mod tidy; \
); done
.PHONY: go.build
go.build:
@set -e; for dir in `find $(GOMOD_DIR) -type f -name "go.mod" | grep -v /vendor/ | sed 's@/[^/]*$$@@' | sort | uniq`; do ( set -xe; \
cd $$dir; \
$(GO) build ./...; \
); done
.PHONY: go.bump-deps
go.bumpdeps:
@set -e; for dir in `find $(GOMOD_DIR) -type f -name "go.mod" | grep -v /vendor/ | sed 's@/[^/]*$$@@' | sort | uniq`; do ( set -xe; \
cd $$dir; \
$(GO) get -u ./...; \
); done
.PHONY: go.bump-deps
go.fmt:
if ! command -v goimports &>/dev/null; then GO111MODULE=off go get golang.org/x/tools/cmd/goimports; fi
@set -e; for dir in `find $(GOMOD_DIR) -type f -name "go.mod" | grep -v /vendor/ | sed 's@/[^/]*$$@@' | sort | uniq`; do ( set -xe; \
cd $$dir; \
goimports -w `go list -f '{{.Dir}}' ./...)` \
); done
BUILD_STEPS += go.build
BUMPDEPS_STEPS += go.bumpdeps
TIDY_STEPS += go.tidy
LINT_STEPS += go.lint
UNITTEST_STEPS += go.unittest
FMT_STEPS += go.fmt
endif
##
## Node
##
ifndef NPM_PACKAGES
ifneq ($(wildcard package.json),)
NPM_PACKAGES = .
endif
endif
ifdef NPM_PACKAGES
.PHONY: npm.publish
npm.publish:
@echo -n "Do you want to npm publish? [y/N] " && read ans && \
@if [ $${ans:-N} = y ]; then \
set -e; for dir in $(NPM_PACKAGES); do ( set -xe; \
cd $$dir; \
npm publish --access=public; \
); done; \
fi
RELEASE_STEPS += npm.publish
endif
##
## Docker
##
docker_build = docker build \
--build-arg VCS_REF=`git rev-parse --short HEAD` \
--build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
--build-arg VERSION=`git describe --tags --always` \
-t "$2" -f "$1" "$(dir $1)"
ifndef DOCKERFILE_PATH
DOCKERFILE_PATH = ./Dockerfile
endif
ifndef DOCKER_IMAGE
ifneq ($(wildcard Dockerfile),)
DOCKER_IMAGE = $(notdir $(PWD))
endif
endif
ifdef DOCKER_IMAGE
ifneq ($(DOCKER_IMAGE),none)
.PHONY: docker.build
docker.build:
$(call docker_build,$(DOCKERFILE_PATH),$(DOCKER_IMAGE))
BUILD_STEPS += docker.build
endif
endif
##
## Common
##
TEST_STEPS += $(UNITTEST_STEPS)
TEST_STEPS += $(LINT_STEPS)
TEST_STEPS += $(TIDY_STEPS)
ifneq ($(strip $(TEST_STEPS)),)
.PHONY: test
test: $(PRE_TEST_STEPS) $(TEST_STEPS)
endif
ifdef INSTALL_STEPS
.PHONY: install
install: $(PRE_INSTALL_STEPS) $(INSTALL_STEPS)
endif
ifdef UNITTEST_STEPS
.PHONY: unittest
unittest: $(PRE_UNITTEST_STEPS) $(UNITTEST_STEPS)
endif
ifdef LINT_STEPS
.PHONY: lint
lint: $(PRE_LINT_STEPS) $(FMT_STEPS) $(LINT_STEPS)
endif
ifdef TIDY_STEPS
.PHONY: tidy
tidy: $(PRE_TIDY_STEPS) $(TIDY_STEPS)
endif
ifdef BUILD_STEPS
.PHONY: build
build: $(PRE_BUILD_STEPS) $(BUILD_STEPS)
endif
ifdef RELEASE_STEPS
.PHONY: release
release: $(PRE_RELEASE_STEPS) $(RELEASE_STEPS)
endif
ifdef BUMPDEPS_STEPS
.PHONY: bumpdeps
bumpdeps: $(PRE_BUMDEPS_STEPS) $(BUMPDEPS_STEPS)
endif
ifdef FMT_STEPS
.PHONY: fmt
fmt: $(PRE_FMT_STEPS) $(FMT_STEPS)
endif
ifdef GENERATE_STEPS
.PHONY: generate
generate: $(PRE_GENERATE_STEPS) $(GENERATE_STEPS)
endif
.PHONY: help
help::
@echo "General commands:"
@[ "$(BUILD_STEPS)" != "" ] && echo " build" || true
@[ "$(BUMPDEPS_STEPS)" != "" ] && echo " bumpdeps" || true
@[ "$(FMT_STEPS)" != "" ] && echo " fmt" || true
@[ "$(GENERATE_STEPS)" != "" ] && echo " generate" || true
@[ "$(INSTALL_STEPS)" != "" ] && echo " install" || true
@[ "$(LINT_STEPS)" != "" ] && echo " lint" || true
@[ "$(RELEASE_STEPS)" != "" ] && echo " release" || true
@[ "$(TEST_STEPS)" != "" ] && echo " test" || true
@[ "$(TIDY_STEPS)" != "" ] && echo " tidy" || true
@[ "$(UNITTEST_STEPS)" != "" ] && echo " unittest" || true
@# FIXME: list other commands

View File

@@ -89,8 +89,8 @@ func server(c *serverConfig) (err error) {
// configure server
srv := &ssh.Server{
Addr: c.bindAddr,
Handler: func(s ssh.Session) { bastion.ShellHandler(s, Version, GitSha, GitTag, GitBranch) }, // ssh.Server.Handler is the handler for the DefaultSessionHandler
Version: fmt.Sprintf("sshportal-%s", Version),
Handler: func(s ssh.Session) { bastion.ShellHandler(s, GitTag, GitSha, GitTag) }, // ssh.Server.Handler is the handler for the DefaultSessionHandler
Version: fmt.Sprintf("sshportal-%s", GitTag),
ChannelHandlers: map[string]ssh.ChannelHandler{
"default": bastion.ChannelHandler,
},

14
testserver_unsupported.go Normal file
View File

@@ -0,0 +1,14 @@
// +build windows
package main
import (
"fmt"
"github.com/urfave/cli"
)
// testServer is an hidden handler used for integration tests
func testServer(c *cli.Context) error {
return fmt.Errorf("not available on windows")
}