Compare commits

..

86 Commits

Author SHA1 Message Date
dependabot[bot]
1acfc9cdb6 chore(deps): bump codfish/semantic-release-action from 1.9.0 to 2.0.0
Bumps [codfish/semantic-release-action](https://github.com/codfish/semantic-release-action) from 1.9.0 to 2.0.0.
- [Release notes](https://github.com/codfish/semantic-release-action/releases)
- [Commits](https://github.com/codfish/semantic-release-action/compare/v1.9.0...v2.0.0)

---
updated-dependencies:
- dependency-name: codfish/semantic-release-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-10 04:03:21 +00:00
semgrep.dev on behalf of @moul
73926212d5 Add Semgrep CI 2022-05-18 18:23:41 +00:00
Manfred Touron
773b7d5a8b Merge pull request #333 from moul/dependabot/go_modules/golang.org/x/tools-0.1.10 2022-03-22 23:06:12 +01:00
Manfred Touron
3ee75e47dd Merge pull request #334 from moul/dependabot/docker/golang-1.18.0 2022-03-22 23:06:05 +01:00
dependabot[bot]
bb6e7c46cc chore(deps): bump golang from 1.17.6 to 1.18.0
Bumps golang from 1.17.6 to 1.18.0.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-17 04:06:08 +00:00
dependabot[bot]
111ced03ad chore(deps): bump golang.org/x/tools from 0.1.9 to 0.1.10
Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.1.9 to 0.1.10.
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.1.9...v0.1.10)

---
updated-dependencies:
- dependency-name: golang.org/x/tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-16 04:06:02 +00:00
Manfred Touron
46970b6d17 Merge pull request #318 from moul/renovate/all 2022-02-08 12:27:47 +01:00
Renovate Bot
afc3888afe fix(deps): update golang.org/x/crypto commit hash to 20e1d8d 2022-02-08 05:38:20 +00:00
Manfred Touron
7ecdb808df Merge pull request #317 from moul/renovate/all
fix(deps): update golang.org/x/crypto commit hash to 30dcbda
2022-02-06 21:07:55 +01:00
Renovate Bot
d3d45da163 fix(deps): update golang.org/x/crypto commit hash to 30dcbda 2022-02-02 20:50:26 +00:00
Manfred Touron
2287353585 Merge pull request #315 from moul/renovate/all
fix(deps): update golang.org/x/crypto commit hash to 198e437
2022-01-30 20:37:31 +01:00
Manfred Touron
ee5a89413e Merge pull request #316 from moul/dependabot/go_modules/golang.org/x/tools-0.1.9
chore(deps): bump golang.org/x/tools from 0.1.8 to 0.1.9
2022-01-29 21:08:51 +01:00
Renovate Bot
9b30972e1e fix(deps): update all 2022-01-28 21:08:08 +00:00
dependabot[bot]
9b849441fa chore(deps): bump golang.org/x/tools from 0.1.8 to 0.1.9
Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.1.8 to 0.1.9.
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.1.8...v0.1.9)

---
updated-dependencies:
- dependency-name: golang.org/x/tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-27 04:04:25 +00:00
Manfred Touron
0a6ee0f985 Merge pull request #310 from moul/renovate/all 2022-01-24 11:14:17 +01:00
Manfred Touron
271f10d389 Merge pull request #313 from moul/dependabot/go_modules/gorm.io/driver/mysql-1.2.3 2022-01-20 16:28:04 +01:00
Manfred Touron
ec0a59a72b Merge pull request #312 from moul/dependabot/go_modules/github.com/docker/docker-20.10.12incompatible 2022-01-20 16:27:55 +01:00
Manfred Touron
e7ca57196e Merge pull request #311 from moul/dependabot/docker/golang-1.17.6 2022-01-20 16:27:50 +01:00
Manfred Touron
d05a6cd3bf Merge pull request #309 from Gurkengewuerz/dev/failed-auth-log 2022-01-20 16:27:24 +01:00
dependabot[bot]
68a4bf2bd3 chore(deps): bump gorm.io/driver/mysql from 1.2.2 to 1.2.3
Bumps [gorm.io/driver/mysql](https://github.com/go-gorm/mysql) from 1.2.2 to 1.2.3.
- [Release notes](https://github.com/go-gorm/mysql/releases)
- [Commits](https://github.com/go-gorm/mysql/compare/v1.2.2...v1.2.3)

---
updated-dependencies:
- dependency-name: gorm.io/driver/mysql
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-15 08:01:36 +00:00
Manfred Touron
3f6c5f1860 Merge pull request #314 from moul/dependabot/go_modules/gorm.io/gorm-1.22.5
chore(deps): bump gorm.io/gorm from 1.22.4 to 1.22.5
2022-01-15 09:00:49 +01:00
dependabot[bot]
4a2648f6be chore(deps): bump gorm.io/gorm from 1.22.4 to 1.22.5
Bumps [gorm.io/gorm](https://github.com/go-gorm/gorm) from 1.22.4 to 1.22.5.
- [Release notes](https://github.com/go-gorm/gorm/releases)
- [Commits](https://github.com/go-gorm/gorm/compare/v1.22.4...v1.22.5)

---
updated-dependencies:
- dependency-name: gorm.io/gorm
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-13 04:04:26 +00:00
Renovate Bot
3610fbeb04 chore(deps): update all docker tags 2022-01-12 19:14:52 +00:00
dependabot[bot]
d0ab97ebf7 chore(deps): bump github.com/docker/docker
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 20.10.9+incompatible to 20.10.12+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Changelog](https://github.com/moby/moby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/docker/docker/compare/v20.10.9...v20.10.12)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-11 04:05:03 +00:00
dependabot[bot]
0e8da37c80 chore(deps): bump golang from 1.17.5 to 1.17.6
Bumps golang from 1.17.5 to 1.17.6.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-07 04:05:13 +00:00
Gurkengewuerz
2d7b79703a feat: added auth failed logging 2022-01-02 23:56:10 +01:00
Manfred Touron
d95f1bd145 Merge pull request #305 from moul/renovate/all 2021-12-29 23:33:42 +01:00
Renovate Bot
f8b0224c44 chore(deps): update all 2021-12-24 07:59:57 +00:00
Manfred Touron
1b97531c6e Merge pull request #308 from moul/dependabot/docker/golang-1.17.5
chore(deps): bump golang from 1.17.3 to 1.17.5
2021-12-12 14:52:40 +01:00
dependabot[bot]
4f38f0f507 chore(deps): bump golang from 1.17.3 to 1.17.5
Bumps golang from 1.17.3 to 1.17.5.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-10 04:05:10 +00:00
Manfred Touron
ef0083ed71 Merge pull request #304 from moul/renovate/all
fix(deps): update all
2021-11-21 08:36:55 +01:00
Renovate Bot
2215b5e1a7 fix(deps): update all 2021-11-17 20:25:01 +00:00
Manfred Touron
f72618618b Merge pull request #299 from moul/renovate/all 2021-11-14 08:35:48 +01:00
Renovate Bot
9f5b05461b chore(deps): update all docker tags 2021-11-13 09:49:43 +00:00
b0undl3ss
4c7fcec0e3 docs: add testing info to readme (#301)
* refactor: Upgrade to Gorm 2.0

* Clean up dependencies

* Fix linting errors

Remove gorm v1 delete callback

* Resolve unittest failures

* Fix association query

* Add testing section to readme

Co-authored-by: Jordan Craven <jordan.craven@wearepop.com>
2021-11-04 20:25:17 +01:00
b0undl3ss
6068e6e48e refactor: upgrade to Gorm 2.0 (#300)
Co-authored-by: Jordan Craven <jordan.craven@wearepop.com>
2021-11-02 08:31:49 +01:00
Manfred Touron
01e4dcb686 Merge pull request #282 from moul/renovate/all 2021-10-15 11:32:06 +02:00
Renovate Bot
b140f70abd chore(deps): update all docker tags 2021-10-15 09:27:54 +00:00
Manfred Touron
5fc247810b Merge pull request #296 from moul/dependabot/docker/golang-1.17.2 2021-10-15 11:27:00 +02:00
Manfred Touron
9205d8faca Merge pull request #284 from moul/dependabot/go_modules/github.com/gliderlabs/ssh-0.3.3 2021-10-15 11:26:51 +02:00
Manfred Touron
71bb63d122 Merge pull request #283 from moul/dependabot/go_modules/golang.org/x/tools-0.1.4 2021-10-15 11:26:41 +02:00
dependabot[bot]
daf4af9225 chore(deps): bump golang from 1.16.5 to 1.17.2
Bumps golang from 1.16.5 to 1.17.2.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-08 04:04:45 +00:00
dependabot[bot]
f366e52d9e chore(deps): bump github.com/gliderlabs/ssh from 0.3.2 to 0.3.3
Bumps [github.com/gliderlabs/ssh](https://github.com/gliderlabs/ssh) from 0.3.2 to 0.3.3.
- [Release notes](https://github.com/gliderlabs/ssh/releases)
- [Commits](https://github.com/gliderlabs/ssh/compare/v0.3.2...v0.3.3)

---
updated-dependencies:
- dependency-name: github.com/gliderlabs/ssh
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-29 04:07:42 +00:00
dependabot[bot]
f1137fd16b chore(deps): bump golang.org/x/tools from 0.1.3 to 0.1.4
Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.1.3 to 0.1.4.
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.1.3...v0.1.4)

---
updated-dependencies:
- dependency-name: golang.org/x/tools
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-24 04:07:41 +00:00
Manfred Touron
65de503b2e Merge pull request #280 from moul/renovate/all
fix(deps): update module golang.org/x/tools to v0.1.3
2021-06-11 06:08:25 +02:00
Renovate Bot
728cb237a0 fix(deps): update module golang.org/x/tools to v0.1.3 2021-06-10 18:36:37 +00:00
Manfred Touron
3c701b2405 Merge pull request #271 from moul/renovate/all
chore(deps): update all docker tags
2021-06-05 16:20:23 +02:00
Renovate Bot
173839c7c1 chore(deps): update all docker tags 2021-06-05 01:03:25 +00:00
Manfred Touron
caf80be30c Merge pull request #277 from moul/dependabot/github_actions/actions/cache-2.1.6
chore(deps): bump actions/cache from 2.1.5 to 2.1.6
2021-05-29 01:52:52 +02:00
Manfred Touron
c57cb0073a Merge pull request #273 from moul/dependabot/github_actions/codfish/semantic-release-action-1.9.0 2021-05-29 01:52:36 +02:00
dependabot[bot]
a496f3ce36 chore(deps): bump actions/cache from 2.1.5 to 2.1.6
Bumps [actions/cache](https://github.com/actions/cache) from 2.1.5 to 2.1.6.
- [Release notes](https://github.com/actions/cache/releases)
- [Commits](https://github.com/actions/cache/compare/v2.1.5...v2.1.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-28 04:03:32 +00:00
dependabot[bot]
e83216a34f chore(deps): bump codfish/semantic-release-action from 1 to 1.9.0
Bumps [codfish/semantic-release-action](https://github.com/codfish/semantic-release-action) from 1 to 1.9.0.
- [Release notes](https://github.com/codfish/semantic-release-action/releases)
- [Commits](https://github.com/codfish/semantic-release-action/compare/v1...v1.9.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-12 04:03:20 +00:00
Manfred Touron
b0ec9c99f9 Merge pull request #272 from moul/dependabot/docker/golang-1.16.4
chore(deps): bump golang from 1.16.3 to 1.16.4
2021-05-07 08:18:19 +02:00
dependabot[bot]
cfaf215713 chore(deps): bump golang from 1.16.3 to 1.16.4
Bumps golang from 1.16.3 to 1.16.4.

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-07 04:04:32 +00:00
Manfred Touron
debb5bf179 Merge pull request #266 from moul/dependabot/docker/golang-1.16.3
chore(deps): bump golang from 1.16.2 to 1.16.3
2021-05-01 09:32:27 +02:00
dependabot[bot]
d5bf550e96 chore(deps): bump golang from 1.16.2 to 1.16.3
Bumps golang from 1.16.2 to 1.16.3.

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-27 08:37:08 +00:00
Manfred Touron
7c68c861bd Merge pull request #267 from moul/dependabot/github_actions/golangci/golangci-lint-action-v2.5.2
chore(deps): bump golangci/golangci-lint-action from v2.5.1 to v2.5.2
2021-04-27 10:11:38 +02:00
Manfred Touron
bae5ad94a9 Merge pull request #227 from moul/renovate/all
fix(deps): update all
2021-04-27 10:03:47 +02:00
Renovate Bot
5369a4f966 fix(deps): update all 2021-04-25 16:18:08 +00:00
Manfred Touron
1c98ef283e Merge pull request #270 from moul/dev/moul/maintenance
chore: repo maintenance 🤖
2021-04-25 18:15:51 +02:00
moul-bot
3dc2801c60 chore: repo maintenance 🤖
more details: https://github.com/moul/repoman

Signed-off-by: moul-bot <bot@moul.io>
Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com>
2021-04-25 16:08:45 +00:00
dependabot[bot]
04913f1f4c chore(deps): bump golangci/golangci-lint-action from v2.5.1 to v2.5.2
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from v2.5.1 to v2.5.2.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v2.5.1...5c56cd6c9dc07901af25baab6f2b0d9f3b7c3018)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-25 09:53:46 +00:00
Manfred Touron
0415f116ea Merge pull request #212 from GreyOBox/dev/GreyOBox/acls-cmd-hook 2021-04-25 11:50:28 +02:00
Manfred Touron
68ce353c5d Merge pull request #269 from moul/dev/moul/maintenance 2021-04-25 11:47:36 +02:00
Manfred Touron
f7ed3a66f2 fix: email address validator 2021-04-24 12:54:42 +00:00
moul-bot
4e9c5205c7 chore: repo maintenance 🤖
more details: https://github.com/moul/repoman

Signed-off-by: moul-bot <bot@moul.io>
2021-04-24 12:35:39 +00:00
Manfred Touron
63b4aa5533 Merge pull request #268 from moul/dependabot/github_actions/actions/cache-v2.1.5
chore(deps): bump actions/cache from v2.1.4 to v2.1.5
2021-04-21 09:09:52 +02:00
Sergey Yashchuk
d580b14d62 Merge branch 'master' into dev/GreyOBox/acls-cmd-hook 2021-04-20 18:42:36 +07:00
Sergey Yashchuk
669577de47 Merge pull request #1 from moul/master
Update from upstream master
2021-04-20 18:28:12 +07:00
dependabot[bot]
868be6af11 chore(deps): bump actions/cache from v2.1.4 to v2.1.5
Bumps [actions/cache](https://github.com/actions/cache) from v2.1.4 to v2.1.5.
- [Release notes](https://github.com/actions/cache/releases)
- [Commits](https://github.com/actions/cache/compare/v2.1.4...1a9e2138d905efd099035b49d8b7a3888c653ca8)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-13 04:13:10 +00:00
Sergey Yashchuk
97bf5d3168 lint fix 2021-04-02 10:49:18 +07:00
Sergey Yashchuk
32fcfa370c Fixes related to comments in PR 2021-04-02 10:29:46 +07:00
Manfred Touron
a710e50b1e Merge pull request #239 from moul/dependabot/github_actions/actions/cache-v2.1.4
chore(deps): bump actions/cache from v2.1.3 to v2.1.4
2021-03-31 13:50:53 +02:00
dependabot[bot]
55010dcc09 chore(deps): bump actions/cache from v2.1.3 to v2.1.4
Bumps [actions/cache](https://github.com/actions/cache) from v2.1.3 to v2.1.4.
- [Release notes](https://github.com/actions/cache/releases)
- [Commits](https://github.com/actions/cache/compare/v2.1.3...26968a09c0ea4f3e233fdddbafd1166051a095f6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-31 10:56:17 +00:00
Manfred Touron
9413b75dc8 Merge pull request #263 from moul/dev/moul/bump-ci-go
chore: bump CI's go version
2021-03-31 12:55:17 +02:00
Manfred Touron
2648418463 chore: bump CI's go version 2021-03-31 10:52:14 +00:00
Manfred Touron
79cbaa3afe Merge pull request #262 from moul/fix/email-validator
feat: New Email validator
2021-03-28 22:46:58 +02:00
Darko Djalevski
2def328f6a fix: fix email validating in shell input
fix: test cases

fix feedback

fix: validate email with custom validator in shell input
2021-03-28 22:25:25 +02:00
Manfred Touron
ab9c53f1b0 chore: maintenance (#260) 2021-03-26 17:26:10 +01:00
Manfred Touron
614418e7be Merge pull request #243 from matteyeux/master
Fix typo in "shell commands" section in README.md
2021-03-26 16:29:29 +01:00
Manfred Touron
a5bade8761 Merge pull request #249 from jwessel/fix_host_inspect
fix: host inspect causes db errors with later operations
2021-03-26 16:28:44 +01:00
Manfred Touron
7404704bfe Merge pull request #254 from jwessel/feat_userkey_create
feat: Allow user multiple keys with userkey create
2021-03-26 16:27:43 +01:00
Jason Wessel
28a5fd1846 feat: Allow user multiple keys with userkey create
And end user may have more than one ssh key, the userkey create
command should be able to accept more than one key so you can do
something like:

   curl https://github.com/USER.keys | ssh sshportal -p 2222 -l admin userkey create USER

The userkey create command also does not work properly from an
interactive shell due to the use of bufio.  This patch adds the
ability to use either the interactive shell or direct ssh command to
input one or more keys.

Signed-off-by: Jason Wessel <jason.wessel@windriver.com>
2021-03-09 09:29:10 -06:00
Jason Wessel
3c32177213 fix: host inspect causes db errors with later operations
The most simple case with a fresh install of sshportal using the
following commands put the shell into a unrecoverable state.

config> host create test1@test1
1
config> host inspect 1
config> host create test2@test2
error: can't preload field Groups for dbmodels.SSHKey

The issue is caused because the global db handle is replaced with the
inspect command.

Signed-off-by: Jason Wessel <jason.wessel@windriver.com>
2021-03-01 07:37:15 -08:00
matteyeux
05225a4b25 Fix typo in "shell commands" section in README.md 2021-02-15 11:27:12 +01:00
Sergey Yashchuk
c697c9aaeb dev: ACLs external command hook 2020-09-18 01:24:51 +07:00
28 changed files with 735 additions and 305 deletions

View File

@@ -1,7 +1,7 @@
defaults: &defaults
working_directory: /go/src/moul.io/sshportal
docker:
- image: circleci/golang:1.16.2
- image: circleci/golang:1.17.5
environment:
GO111MODULE: "on"

View File

@@ -1,25 +1,15 @@
<!-- Thanks for filling an issue!
### Actual Result / Problem
If this is a BUG REPORT, please:
- Fill in as much of the template below as you can
When I do Foo, Bar happens...
If this is a FEATURE REQUEST, please:
- Describe *in detail* the feature/behavior/change you would like to see
-->
### Expected Result / Suggestion
**What happened**:
I expect that Foobar happens...
**What you expected to happen**:
### Some context
**How to reproduce it (as minimally and precisely as possible)**:
**Anything else we need to know?**:
<!--
**Environment**:
- sshportal --version
- ssh sshportal info
- OS (e.g. from /etc/os-release):
- install method (e.g. go/docker/brew/...):
- others:
-->
Any screenshot to share?
`sshportal --version`?
`ssh sshportal info`?
OS/Go version?
...

View File

@@ -1,7 +1 @@
<!-- Thanks for sending a pull request! Here are some tips for you -->
**What this PR does / why we need it**:
**Which issue this PR fixes**: fixes #xxx, fixes #xxx...
**Special notes for your reviewer**:
<!-- thank you for your contribution! ❤️ -->

View File

@@ -20,9 +20,9 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: lint
uses: golangci/golangci-lint-action@v2.5.1
uses: golangci/golangci-lint-action@v2.5.2
with:
version: v1.28
version: v1.38
github-token: ${{ secrets.GITHUB_TOKEN }}
tests-on-windows:
needs: golangci-lint # run after golangci-lint action to not produce duplicated errors
@@ -30,7 +30,7 @@ jobs:
strategy:
matrix:
golang:
- 1.15.0
- 1.16.x
steps:
- uses: actions/checkout@v2
- name: Install Go
@@ -46,14 +46,14 @@ jobs:
strategy:
matrix:
golang:
- 1.15.0
- 1.16.x
steps:
- uses: actions/checkout@v2
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.golang }}
- uses: actions/cache@v2.1.3
- uses: actions/cache@v2.1.7
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ matrix.golang }}-${{ hashFiles('**/go.sum') }}
@@ -67,16 +67,17 @@ jobs:
strategy:
matrix:
golang:
- 1.13
- 1.14
- 1.15.0
- 1.13.x
- 1.14.x
- 1.15.x
- 1.16.x
steps:
- uses: actions/checkout@v2
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.golang }}
- uses: actions/cache@v2.1.3
- uses: actions/cache@v2.1.7
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ matrix.golang }}-${{ hashFiles('**/go.sum') }}

View File

@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: codfish/semantic-release-action@v1
- uses: codfish/semantic-release-action@v2.0.0
if: github.ref == 'refs/heads/master'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

21
.github/workflows/semgrep.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
on:
pull_request: {}
push:
branches:
- master
paths:
- .github/workflows/semgrep.yml
schedule:
- cron: '0 0 * * 0'
name: Semgrep
jobs:
semgrep:
name: Scan
runs-on: ubuntu-20.04
env:
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
container:
image: returntocorp/semgrep
steps:
- uses: actions/checkout@v3
- run: semgrep ci

4
AUTHORS generated
View File

@@ -5,9 +5,11 @@ ahh <ahamidullah@gmail.com>
Alen Masic <alenn.masic@gmail.com>
Alexander Turner <me@alexturner.co>
bozzo <bozzo@users.noreply.github.com>
Darko Djalevski <darko.djalevski@inplayer.com>
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
fossabot <badges@fossa.io>
ImgBotApp <ImgBotHelp@gmail.com>
Jason Wessel <jason.wessel@windriver.com>
Jean-Louis Férey <jeanlouis.ferey@orange.com>
jerard@alfa-safety.fr <jrrdev@users.noreply.github.com>
Jess <jessachandler@gmail.com>
@@ -20,6 +22,7 @@ Manuel <manuel.sabban@nbs-system.com>
Manuel Sabban <manu@sabban.eu>
Manuel Sabban <msa@nbs-system.com>
Mathieu Pasquet <mathieu.pasquet@alterway.fr>
matteyeux <matteyeux@users.noreply.github.com>
Mikael Rapp <micke.rapp@gmail.com>
MitaliBo <mitali.bisht14@gmail.com>
moul-bot <bot@moul.io>
@@ -27,6 +30,7 @@ Nelly Asher <karmelylle@rambler.ru>
NocFlame <aad@nocflame.se>
Quentin Perez <qperez42@gmail.com>
Renovate Bot <bot@renovateapp.com>
Sergey Yashchuk <11705746+GreyOBox@users.noreply.github.com>
Sergey Yashchuk <sergey.yashchuk@coins.ph>
Shawn Wang <shawn111@gmail.com>
Valentin Daviot <valentin.daviot@alterway.fr>

View File

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

View File

@@ -34,6 +34,7 @@ Features include: independence of users and hosts, convenient user invite system
- [portal alias (.ssh/config)](#portal-alias-sshconfig)
- [Scaling](#scaling)
- [Under the hood](#under-the-hood)
- [Testing](#testing)
<!-- tocstop -->
@@ -366,7 +367,7 @@ user update [-h] [--name=<value>] [--email=<value>] [--set-admin] [--unset-admin
# usergroup management
usergroup help
hostgroup create [-h] [--name=<value>] [--comment=<value>]
usergroup create [-h] [--name=<value>] [--comment=<value>]
usergroup inspect [-h] USERGROUP...
usergroup ls [-h] [--latest] [--quiet]
usergroup rm [-h] USERGROUP...
@@ -472,6 +473,26 @@ See [examples/mysql](http://github.com/moul/sshportal/tree/master/examples/mysql
![sshportal data model](https://raw.github.com/moul/sshportal/master/.assets/sql-schema.png)
---
## Testing
[Install golangci-lint](https://golangci-lint.run/usage/install/#local-installation) and run this in project root:
```
golangci-lint run
```
---
Perform integration tests
```
make integration
```
---
Perform unit tests
```
make unittest
```
---
## Contributors
### Code Contributors

View File

@@ -96,7 +96,8 @@ moul.io/sshportal dependencies: (generated by github.com/tailscale/depaware)
hash from crypto+
html from github.com/asaskevich/govalidator+
io from bufio+
io/ioutil from crypto/tls+
io/fs from crypto/rand+
io/ioutil from crypto/x509+
log from github.com/gliderlabs/ssh+
math from crypto/rsa+
math/big from crypto/dsa+

31
go.mod generated
View File

@@ -2,33 +2,34 @@ module moul.io/sshportal
require (
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
github.com/cpuguy83/go-md2man/v2 v2.0.0 // 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/docker/docker v20.10.12+incompatible
github.com/dustin/go-humanize v1.0.0
github.com/gliderlabs/ssh v0.3.2
github.com/go-sql-driver/mysql v1.5.0
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
github.com/jinzhu/gorm v1.9.16
github.com/gliderlabs/ssh v0.3.3
github.com/go-gormigrate/gormigrate/v2 v2.0.0
github.com/kr/pty v1.1.8
github.com/lib/pq v1.8.0 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
github.com/mattn/go-runewidth v0.0.12 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d
github.com/olekukonko/tablewriter v0.0.5
github.com/pkg/errors v0.9.1
github.com/reiver/go-oi v1.0.0
github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e
github.com/rivo/uniseg v0.2.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
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
github.com/stretchr/testify v1.4.0 // indirect
github.com/smartystreets/goconvey v1.7.2
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502
github.com/urfave/cli v1.22.5
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect
gopkg.in/gormigrate.v1 v1.6.0
golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab
golang.org/x/term v0.0.0-20210422114643-f5beecf764ed // indirect
golang.org/x/tools v0.1.10
gorm.io/driver/mysql v1.2.3
gorm.io/driver/postgres v1.2.3
gorm.io/driver/sqlite v1.2.6
gorm.io/gorm v1.22.5
moul.io/srand v1.6.1
)

315
go.sum generated
View File

@@ -1,11 +1,16 @@
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/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
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-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg=
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
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=
@@ -15,62 +20,147 @@ github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ
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/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc h1:VRRKCwnzqk8QCaRC4os14xoKDdbHqqlJtJA0oc1ZAjg=
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/docker/docker v20.10.12+incompatible h1:CEeNmFM0QZIsJCZKMkZx0ZcahTiewkrgiwfYD+dfl1U=
github.com/docker/docker v20.10.12+incompatible/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/gliderlabs/ssh v0.3.2 h1:gcfd1Aj/9RQxvygu4l3sak711f/5+VOwBw9C/7+N4EI=
github.com/gliderlabs/ssh v0.3.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
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=
github.com/gliderlabs/ssh v0.3.3 h1:mBQ8NiOgDkINJrZtoizkC3nDNYgSaWtxyem6S2XHBtA=
github.com/gliderlabs/ssh v0.3.3/go.mod h1:ZSS+CUoKHDrqVakTfTWUlKSr9MtMFkC4UvtQKD7O914=
github.com/go-gormigrate/gormigrate/v2 v2.0.0 h1:e2A3Uznk4viUC4UuemuVgsNnvYZyOA8B3awlYk3UioU=
github.com/go-gormigrate/gormigrate/v2 v2.0.0/go.mod h1:YuVJ+D/dNt4HWrThTBnjgZuRbt7AuwINeg4q52ZE3Jw=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/jinzhu/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
github.com/jackc/pgconn v1.6.4/go.mod h1:w2pne1C2tZgP+TvjqLpOigGzNqjBgQW9dUw/4Chex78=
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.10.1 h1:DzdIHIjG1AxGwoEEqS+mGsURyjt4enSmqzACXvVzOT8=
github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.0.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.2.0 h1:r7JypeP2D3onoQTCxWdTpCtJ4D+qpKr0TxvoyMhZ5ns=
github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
github.com/jackc/pgtype v1.4.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig=
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
github.com/jackc/pgtype v1.9.0 h1:/SH1RxEtltvJgsDqp3TbiTFApD3mey3iygpuEGeuBXk=
github.com/jackc/pgtype v1.9.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
github.com/jackc/pgx/v4 v4.8.1/go.mod h1:4HOLxrl8wToZJReD04/yB20GDwf4KBYETvlHciCnwW0=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.14.0 h1:TgdrmgnM7VY72EuSQzBbBd4JA1RLqJolrw9nQVZABVc=
github.com/jackc/pgx/v4 v4.14.0/go.mod h1:jT3ibf/A0ZVCp89rtCIN0zCJxcE74ypROmHEZYsG/j8=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v0.0.0-20181116074157-8ec929ed50c3/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc=
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
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/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
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.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/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
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/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7 h1:+/+DxvQaYifJ+grD4klzrS5y+KJXldn/2YTl5JG+vZ8=
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -79,51 +169,170 @@ github.com/reiver/go-oi v1.0.0 h1:nvECWD7LF+vOs8leNGV/ww+F2iZKf3EYjYZ527turzM=
github.com/reiver/go-oi v1.0.0/go.mod h1:RrDBct90BAhoDTxB1fenZwfykqeGvhI6LsNfStJoEkI=
github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e h1:quuzZLi72kkJjl+f5AQ93FMcadG19WkS7MO6TXFOSas=
github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e/go.mod h1:+5vNVvEWwEIx86DB9Ke/+a5wBI464eDRo3eF0LcfpWg=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sabban/bastion v0.0.0-20180110125408-b9d3c9b1f4d3 h1:yxUGvEatvDMO6gkhwx82Va+Czdyui9LiCw6a5YB/2f8=
github.com/sabban/bastion v0.0.0-20180110125408-b9d3c9b1f4d3/go.mod h1:1Q04m7wmv/IMoZU9t8UkH+n9McWn4i3H9v9LnMgqloo=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
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 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=
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/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502 h1:34icjjmqJ2HPjrSuJYEkdZ+0ItmGQAQ75cRHIiftIyE=
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
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-20201112155050-0c6587e931a9 h1:umElSU9WZirRdgu2yFHY0ayQkEnKiOC1TtM3fWXFnoU=
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab h1:lnZ4LoV0UMdibeCUfIB2a4uFwRu491WX/VB2reB8xNc=
golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/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-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210422114643-f5beecf764ed h1:Ei4bQjjpYUsS4efOUz+5Nz++IVkHk87n2zBA0NxBWc0=
golang.org/x/term v0.0.0-20210422114643-f5beecf764ed/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
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=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/gormigrate.v1 v1.6.0 h1:XpYM6RHQPmzwY7Uyu+t+xxMXc86JYFJn4nEc9HzQjsI=
gopkg.in/gormigrate.v1 v1.6.0/go.mod h1:Lf00lQrHqfSYWiTtPcyQabsDdM6ejZaMgV0OU6JMSlw=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.0.1/go.mod h1:KtqSthtg55lFp3S5kUXqlGaelnWpKitn4k1xZTnoiPw=
gorm.io/driver/mysql v1.2.3 h1:cZqzlOfg5Kf1VIdLC1D9hT6Cy9BgxhExLj/2tIgUe7Y=
gorm.io/driver/mysql v1.2.3/go.mod h1:qsiz+XcAyMrS6QY+X3M9R6b/lKM1imKmcuK9kac5LTo=
gorm.io/driver/postgres v1.0.0/go.mod h1:wtMFcOzmuA5QigNsgEIb7O5lhvH1tHAF1RbWmLWV4to=
gorm.io/driver/postgres v1.2.3 h1:f4t0TmNMy9gh3TU2PX+EppoA6YsgFnyq8Ojtddb42To=
gorm.io/driver/postgres v1.2.3/go.mod h1:pJV6RgYQPG47aM1f0QeOzFH9HxQc8JcmAgjRCgS0wjs=
gorm.io/driver/sqlite v1.1.1/go.mod h1:hm2olEcl8Tmsc6eZyxYSeznnsDaMqamBvEXLNtBg4cI=
gorm.io/driver/sqlite v1.2.6 h1:SStaH/b+280M7C8vXeZLz/zo9cLQmIGwwj3cSj7p6l4=
gorm.io/driver/sqlite v1.2.6/go.mod h1:gyoX0vHiiwi0g49tv+x2E7l8ksauLK0U/gShcdUsjWY=
gorm.io/driver/sqlserver v1.0.2 h1:FzxAlw0/7hntMzSiNfotpYCo9Lz8dqWQGdmCGqIiFGo=
gorm.io/driver/sqlserver v1.0.2/go.mod h1:gb0Y9QePGgqjzrVyTQUZeh9zkd5v0iz71cM1B4ZycEY=
gorm.io/gorm v1.9.19/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
gorm.io/gorm v1.20.0/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/gorm v1.22.4/go.mod h1:1aeVC+pe9ZmvKZban/gW4QPra7PRoTEssyc922qCAkk=
gorm.io/gorm v1.22.5 h1:lYREBgc02Be/5lSCTuysZZDb6ffL2qrat6fg9CFbvXU=
gorm.io/gorm v1.22.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
moul.io/srand v1.6.1 h1:SJ335F+54ivLdlH7wH52Rtyv0Ffos6DpsF5wu3ZVMXU=
moul.io/srand v1.6.1/go.mod h1:P2uaZB+GFstFNo8sEj6/U8FRV1n25kD0LLckFpJ+qvc=

11
internal/tools/tools.go Normal file
View File

@@ -0,0 +1,11 @@
// +build tools
package tools
import (
// required by depaware
_ "github.com/tailscale/depaware/depaware"
// required by goimports
_ "golang.org/x/tools/cover"
)

View File

@@ -6,10 +6,6 @@ import (
"os"
"path"
_ "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"
)
@@ -83,6 +79,11 @@ func main() {
Value: 0,
Usage: "Duration before an inactive connection is timed out (0 to disable)",
},
cli.StringFlag{
Name: "acl-check-cmd",
EnvVar: "SSHPORTAL_ACL_CHECK_CMD",
Usage: "Execute external command to check ACL",
},
},
}, {
Name: "healthcheck",

View File

@@ -1,19 +1,28 @@
package bastion
import (
"context"
"encoding/json"
"fmt"
"log"
"os/exec"
"sort"
"strings"
"time"
"moul.io/sshportal/pkg/dbmodels"
)
// ACLHookTimeout is timeout for external ACL hook execution
const ACLHookTimeout = 2 * time.Second
type byWeight []*dbmodels.ACL
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 {
func checkACLs(user dbmodels.User, host dbmodels.Host, aclCheckCmd string) string {
currentTime := time.Now()
// shared ACLs between user and host
@@ -34,9 +43,13 @@ func checkACLs(user dbmodels.User, host dbmodels.Host) string {
}
// FIXME: add ACLs that match host pattern
// deny by default if no shared ACL
// if no shared ACL then execute ACLs hook if it exists and return its result
if len(aclMap) == 0 {
return string(dbmodels.ACLActionDeny) // default action
action, err := checkACLsHook(aclCheckCmd, string(dbmodels.ACLActionDeny), user, host)
if err != nil {
log.Println(err)
}
return action
}
// transform map to slice and sort it
@@ -46,5 +59,62 @@ func checkACLs(user dbmodels.User, host dbmodels.Host) string {
}
sort.Sort(byWeight(acls))
return acls[0].Action
action, err := checkACLsHook(aclCheckCmd, acls[0].Action, user, host)
if err != nil {
log.Println(err)
}
return action
}
// checkACLsHook executes external command to check ACL and passes following parameters:
// $1 - SSH Portal `action` (`allow` or `deny`)
// $2 - User as JSON string
// $3 - Host as JSON string
// External program has to return `allow` or `deny` in stdout.
// In case of any error function returns `action`.
func checkACLsHook(aclCheckCmd string, action string, user dbmodels.User, host dbmodels.Host) (string, error) {
if aclCheckCmd == "" {
return action, nil
}
ctx, cancel := context.WithTimeout(context.Background(), ACLHookTimeout)
defer cancel()
jsonUser, err := json.Marshal(user)
if err != nil {
return action, err
}
jsonHost, err := json.Marshal(host)
if err != nil {
return action, err
}
args := []string{
action,
string(jsonUser),
string(jsonHost),
}
cmd := exec.CommandContext(ctx, aclCheckCmd, args...)
out, err := cmd.Output()
if err != nil {
return action, err
}
if ctx.Err() == context.DeadlineExceeded {
return action, fmt.Errorf("external ACL hook command timed out")
}
outStr := strings.TrimSuffix(string(out), "\n")
switch outStr {
case string(dbmodels.ACLActionAllow):
return string(dbmodels.ACLActionAllow), nil
case string(dbmodels.ACLActionDeny):
return string(dbmodels.ACLActionDeny), nil
default:
return action, fmt.Errorf("acl-check-cmd wrong output '%s'", outStr)
}
}

View File

@@ -6,10 +6,9 @@ import (
"path/filepath"
"testing"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
_ "github.com/jinzhu/gorm/dialects/sqlite"
. "github.com/smartystreets/goconvey/convey"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"moul.io/sshportal/pkg/dbmodels"
)
@@ -23,9 +22,8 @@ func TestCheckACLs(t *testing.T) {
}()
// create sqlite db
db, err := gorm.Open("sqlite3", filepath.Join(tempDir, "sshportal.db"))
db, err := gorm.Open(sqlite.Open(filepath.Join(tempDir, "sshportal.db")), &gorm.Config{})
c.So(err, ShouldBeNil)
db.LogMode(false)
c.So(DBInit(db), ShouldBeNil)
// create dummy objects
@@ -43,7 +41,7 @@ func TestCheckACLs(t *testing.T) {
db.Preload("Groups").Preload("Groups.ACLs").Find(&users)
// test
action := checkACLs(users[0], hosts[0])
action := checkACLs(users[0], hosts[0], "")
c.So(action, ShouldEqual, dbmodels.ACLActionAllow)
})
}

View File

@@ -10,16 +10,15 @@ import (
"strings"
"time"
"github.com/jinzhu/gorm"
gormigrate "github.com/go-gormigrate/gormigrate/v2"
gossh "golang.org/x/crypto/ssh"
gormigrate "gopkg.in/gormigrate.v1"
"gorm.io/gorm"
"moul.io/sshportal/pkg/crypto"
"moul.io/sshportal/pkg/dbmodels"
)
func DBInit(db *gorm.DB) error {
log.SetOutput(ioutil.Discard)
db.Callback().Delete().Replace("gorm:delete", hardDeleteCallback)
log.SetOutput(os.Stderr)
m := gormigrate.New(db, gormigrate.DefaultOptions, []*gormigrate.Migration{
@@ -28,13 +27,13 @@ func DBInit(db *gorm.DB) error {
Migrate: func(tx *gorm.DB) error {
type Setting struct {
gorm.Model
Name string
Name string `gorm:"index:uix_settings_name,unique"`
Value string
}
return tx.AutoMigrate(&Setting{}).Error
return tx.AutoMigrate(&Setting{})
},
Rollback: func(tx *gorm.DB) error {
return tx.DropTable("settings").Error
return tx.Migrator().DropTable("settings")
},
}, {
ID: "2",
@@ -50,10 +49,10 @@ func DBInit(db *gorm.DB) error {
Hosts []*dbmodels.Host `gorm:"ForeignKey:SSHKeyID"`
Comment string
}
return tx.AutoMigrate(&SSHKey{}).Error
return tx.AutoMigrate(&SSHKey{})
},
Rollback: func(tx *gorm.DB) error {
return tx.DropTable("ssh_keys").Error
return tx.Migrator().DropTable("ssh_keys")
},
}, {
ID: "3",
@@ -70,10 +69,10 @@ func DBInit(db *gorm.DB) error {
Fingerprint string
Comment string
}
return tx.AutoMigrate(&Host{}).Error
return tx.AutoMigrate(&Host{})
},
Rollback: func(tx *gorm.DB) error {
return tx.DropTable("hosts").Error
return tx.Migrator().DropTable("hosts")
},
}, {
ID: "4",
@@ -85,10 +84,10 @@ func DBInit(db *gorm.DB) error {
User *dbmodels.User `gorm:"ForeignKey:UserID"`
Comment string
}
return tx.AutoMigrate(&UserKey{}).Error
return tx.AutoMigrate(&UserKey{})
},
Rollback: func(tx *gorm.DB) error {
return tx.DropTable("user_keys").Error
return tx.Migrator().DropTable("user_keys")
},
}, {
ID: "5",
@@ -103,10 +102,10 @@ func DBInit(db *gorm.DB) error {
Comment string
InviteToken string
}
return tx.AutoMigrate(&User{}).Error
return tx.AutoMigrate(&User{})
},
Rollback: func(tx *gorm.DB) error {
return tx.DropTable("users").Error
return tx.Migrator().DropTable("users")
},
}, {
ID: "6",
@@ -118,10 +117,10 @@ func DBInit(db *gorm.DB) error {
ACLs []*dbmodels.ACL `gorm:"many2many:user_group_acls;"`
Comment string
}
return tx.AutoMigrate(&UserGroup{}).Error
return tx.AutoMigrate(&UserGroup{})
},
Rollback: func(tx *gorm.DB) error {
return tx.DropTable("user_groups").Error
return tx.Migrator().DropTable("user_groups")
},
}, {
ID: "7",
@@ -133,10 +132,10 @@ func DBInit(db *gorm.DB) error {
ACLs []*dbmodels.ACL `gorm:"many2many:host_group_acls;"`
Comment string
}
return tx.AutoMigrate(&HostGroup{}).Error
return tx.AutoMigrate(&HostGroup{})
},
Rollback: func(tx *gorm.DB) error {
return tx.DropTable("host_groups").Error
return tx.Migrator().DropTable("host_groups")
},
}, {
ID: "8",
@@ -151,64 +150,76 @@ func DBInit(db *gorm.DB) error {
Comment string
}
return tx.AutoMigrate(&ACL{}).Error
return tx.AutoMigrate(&ACL{})
},
Rollback: func(tx *gorm.DB) error {
return tx.DropTable("acls").Error
return tx.Migrator().DropTable("acls")
},
}, {
ID: "9",
Migrate: func(tx *gorm.DB) error {
db.Model(&dbmodels.Setting{}).RemoveIndex("uix_settings_name")
return db.Model(&dbmodels.Setting{}).AddUniqueIndex("uix_settings_name", "name").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 db.Model(&dbmodels.Setting{}).RemoveIndex("uix_settings_name").Error
return tx.Migrator().DropIndex(&dbmodels.Setting{}, "uix_settings_name")
},
}, {
ID: "10",
Migrate: func(tx *gorm.DB) error {
db.Model(&dbmodels.SSHKey{}).RemoveIndex("uix_keys_name")
return db.Model(&dbmodels.SSHKey{}).AddUniqueIndex("uix_keys_name", "name").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 db.Model(&dbmodels.SSHKey{}).RemoveIndex("uix_keys_name").Error
return tx.Migrator().DropIndex(&dbmodels.SSHKey{}, "uix_keys_name")
},
}, {
ID: "11",
Migrate: func(tx *gorm.DB) error {
db.Model(&dbmodels.Host{}).RemoveIndex("uix_hosts_name")
return db.Model(&dbmodels.Host{}).AddUniqueIndex("uix_hosts_name", "name").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 db.Model(&dbmodels.Host{}).RemoveIndex("uix_hosts_name").Error
return tx.Migrator().DropIndex(&dbmodels.Host{}, "uix_hosts_name")
},
}, {
ID: "12",
Migrate: func(tx *gorm.DB) error {
db.Model(&dbmodels.User{}).RemoveIndex("uix_users_name")
return db.Model(&dbmodels.User{}).AddUniqueIndex("uix_users_name", "name").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 db.Model(&dbmodels.User{}).RemoveIndex("uix_users_name").Error
return tx.Migrator().DropIndex(&dbmodels.User{}, "uix_users_name")
},
}, {
ID: "13",
Migrate: func(tx *gorm.DB) error {
db.Model(&dbmodels.UserGroup{}).RemoveIndex("uix_usergroups_name")
return db.Model(&dbmodels.UserGroup{}).AddUniqueIndex("uix_usergroups_name", "name").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 db.Model(&dbmodels.UserGroup{}).RemoveIndex("uix_usergroups_name").Error
return tx.Migrator().DropIndex(&dbmodels.UserGroup{}, "uix_usergroups_name")
},
}, {
ID: "14",
Migrate: func(tx *gorm.DB) error {
db.Model(&dbmodels.HostGroup{}).RemoveIndex("uix_hostgroups_name")
return db.Model(&dbmodels.HostGroup{}).AddUniqueIndex("uix_hostgroups_name", "name").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 db.Model(&dbmodels.HostGroup{}).RemoveIndex("uix_hostgroups_name").Error
return tx.Migrator().DropIndex(&dbmodels.HostGroup{}, "uix_hostgroups_name")
},
}, {
ID: "15",
@@ -218,10 +229,10 @@ func DBInit(db *gorm.DB) error {
Name string `valid:"required,length(1|32),unix_user"`
Users []*dbmodels.User `gorm:"many2many:user_user_roles"`
}
return tx.AutoMigrate(&UserRole{}).Error
return tx.AutoMigrate(&UserRole{})
},
Rollback: func(tx *gorm.DB) error {
return tx.DropTable("user_roles").Error
return tx.Migrator().DropTable("user_roles")
},
}, {
ID: "16",
@@ -237,7 +248,7 @@ func DBInit(db *gorm.DB) error {
Comment string `valid:"optional"`
InviteToken string `valid:"optional,length(10|60)"`
}
return tx.AutoMigrate(&User{}).Error
return tx.AutoMigrate(&User{})
},
Rollback: func(tx *gorm.DB) error {
return fmt.Errorf("not implemented")
@@ -248,7 +259,7 @@ func DBInit(db *gorm.DB) error {
return tx.Create(&dbmodels.UserRole{Name: "admin"}).Error
},
Rollback: func(tx *gorm.DB) error {
return tx.Where("name = ?", "admin").Delete(&dbmodels.UserRole{}).Error
return tx.Where("name = ?", "admin").Unscoped().Delete(&dbmodels.UserRole{}).Error
},
}, {
ID: "18",
@@ -287,7 +298,7 @@ func DBInit(db *gorm.DB) error {
Comment string `valid:"optional"`
InviteToken string `valid:"optional,length(10|60)"`
}
return tx.AutoMigrate(&User{}).Error
return tx.AutoMigrate(&User{})
},
Rollback: func(tx *gorm.DB) error {
return fmt.Errorf("not implemented")
@@ -298,7 +309,7 @@ func DBInit(db *gorm.DB) error {
return tx.Create(&dbmodels.UserRole{Name: "listhosts"}).Error
},
Rollback: func(tx *gorm.DB) error {
return tx.Where("name = ?", "listhosts").Delete(&dbmodels.UserRole{}).Error
return tx.Where("name = ?", "listhosts").Unscoped().Delete(&dbmodels.UserRole{}).Error
},
}, {
ID: "21",
@@ -314,10 +325,10 @@ func DBInit(db *gorm.DB) error {
ErrMsg string `valid:"optional"`
Comment string `valid:"optional"`
}
return tx.AutoMigrate(&Session{}).Error
return tx.AutoMigrate(&Session{})
},
Rollback: func(tx *gorm.DB) error {
return tx.DropTable("sessions").Error
return tx.Migrator().DropTable("sessions")
},
}, {
ID: "22",
@@ -331,10 +342,10 @@ func DBInit(db *gorm.DB) error {
Entity string `valid:"optional"`
Args []byte `sql:"size:10000" valid:"optional,length(1|10000)"`
}
return tx.AutoMigrate(&Event{}).Error
return tx.AutoMigrate(&Event{})
},
Rollback: func(tx *gorm.DB) error {
return tx.DropTable("events").Error
return tx.Migrator().DropTable("events")
},
}, {
ID: "23",
@@ -347,7 +358,7 @@ func DBInit(db *gorm.DB) error {
User *dbmodels.User `gorm:"ForeignKey:UserID"`
Comment string `valid:"optional"`
}
return tx.AutoMigrate(&UserKey{}).Error
return tx.AutoMigrate(&UserKey{})
},
Rollback: func(tx *gorm.DB) error {
return fmt.Errorf("not implemented")
@@ -391,7 +402,7 @@ func DBInit(db *gorm.DB) error {
Fingerprint string `valid:"optional"`
Comment string `valid:"optional"`
}
return tx.AutoMigrate(&Host{}).Error
return tx.AutoMigrate(&Host{})
},
Rollback: func(tx *gorm.DB) error {
return fmt.Errorf("not implemented")
@@ -410,7 +421,7 @@ func DBInit(db *gorm.DB) error {
ErrMsg string `valid:"optional"`
Comment string `valid:"optional"`
}
return tx.AutoMigrate(&Session{}).Error
return tx.AutoMigrate(&Session{})
},
Rollback: func(tx *gorm.DB) error {
return fmt.Errorf("not implemented")
@@ -451,7 +462,7 @@ func DBInit(db *gorm.DB) error {
Groups []*dbmodels.HostGroup `gorm:"many2many:host_host_groups;"`
Comment string
}
return tx.AutoMigrate(&Host{}).Error
return tx.AutoMigrate(&Host{})
},
Rollback: func(tx *gorm.DB) error {
return fmt.Errorf("not implemented")
@@ -474,7 +485,7 @@ func DBInit(db *gorm.DB) error {
Hop *dbmodels.Host
HopID uint
}
return tx.AutoMigrate(&Host{}).Error
return tx.AutoMigrate(&Host{})
},
Rollback: func(tx *gorm.DB) error {
return fmt.Errorf("not implemented")
@@ -498,13 +509,13 @@ func DBInit(db *gorm.DB) error {
Logging string
HopID uint
}
return tx.AutoMigrate(&Host{}).Error
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.Model(&dbmodels.Host{}).Updates(&dbmodels.Host{Logging: "everything"}).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") },
}, {
@@ -521,7 +532,7 @@ func DBInit(db *gorm.DB) error {
Inception *time.Time
Expiration *time.Time
}
return tx.AutoMigrate(&ACL{}).Error
return tx.AutoMigrate(&ACL{})
},
Rollback: func(tx *gorm.DB) error { return fmt.Errorf("not implemented") },
},
@@ -532,7 +543,7 @@ func DBInit(db *gorm.DB) error {
dbmodels.NewEvent("system", "migrated").Log(db)
// create default ssh key
var count uint
var count int64
if err := db.Table("ssh_keys").Where("name = ?", "default").Count(&count).Error; err != nil {
return err
}
@@ -662,30 +673,6 @@ func DBInit(db *gorm.DB) error {
}).Error
}
func hardDeleteCallback(scope *gorm.Scope) {
if !scope.HasError() {
var extraOption string
if str, ok := scope.Get("gorm:delete_option"); ok {
extraOption = fmt.Sprint(str)
}
/* #nosec */
scope.Raw(fmt.Sprintf(
"DELETE FROM %v%v%v",
scope.QuotedTableName(),
addExtraSpaceIfExist(scope.CombinedConditionSql()),
addExtraSpaceIfExist(extraOption),
)).Exec()
}
}
func addExtraSpaceIfExist(str string) string {
if str != "" {
return " " + str
}
return ""
}
func randStringBytes(n int) string {
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

View File

@@ -190,8 +190,8 @@ func pipe(lreqs, rreqs <-chan *gossh.Request, lch, rch gossh.Channel, sessConfig
b, err := rch.SendRequest(req.Type, req.WantReply, req.Payload)
if req.Type == "exec" {
wrappedlch := logchannel.New(lch, logWriter)
command := append(req.Payload, []byte("\n")...)
if _, err := wrappedlch.LogWrite(command); err != nil {
req.Payload = append(req.Payload, []byte("\n")...)
if _, err := wrappedlch.LogWrite(req.Payload); err != nil {
log.Printf("failed to write log: %v", err)
}
}

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"net/url"
"os"
"regexp"
@@ -21,9 +22,10 @@ import (
"github.com/olekukonko/tablewriter"
"github.com/urfave/cli"
gossh "golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/crypto/ssh/terminal" // nolint:staticcheck
"moul.io/sshportal/pkg/crypto"
"moul.io/sshportal/pkg/dbmodels"
"moul.io/sshportal/pkg/utils"
)
var banner = `
@@ -251,7 +253,7 @@ GLOBAL OPTIONS:
return err
}
return dbmodels.ACLsByIdentifiers(db, c.Args()).Delete(&dbmodels.ACL{}).Error
return dbmodels.ACLsByIdentifiers(db, c.Args()).Unscoped().Delete(&dbmodels.ACL{}).Error
},
}, {
Name: "update",
@@ -333,12 +335,12 @@ GLOBAL OPTIONS:
tx.Rollback()
return err
}
if err := model.Association("UserGroups").Append(&appendUserGroups).Error; err != nil {
if err := model.Association("UserGroups").Append(&appendUserGroups); err != nil {
tx.Rollback()
return err
}
if len(deleteUserGroups) > 0 {
if err := model.Association("UserGroups").Delete(deleteUserGroups).Error; err != nil {
if err := model.Association("UserGroups").Delete(deleteUserGroups); err != nil {
tx.Rollback()
return err
}
@@ -354,12 +356,12 @@ GLOBAL OPTIONS:
tx.Rollback()
return err
}
if err := model.Association("HostGroups").Append(&appendHostGroups).Error; err != nil {
if err := model.Association("HostGroups").Append(&appendHostGroups); err != nil {
tx.Rollback()
return err
}
if len(deleteHostGroups) > 0 {
if err := model.Association("HostGroups").Delete(deleteHostGroups).Error; err != nil {
if err := model.Association("HostGroups").Delete(deleteHostGroups); err != nil {
tx.Rollback()
return err
}
@@ -510,7 +512,6 @@ GLOBAL OPTIONS:
"host_groups",
"host_host_groups",
"hosts",
//"migrations",
"sessions",
"settings",
"ssh_keys",
@@ -521,6 +522,7 @@ GLOBAL OPTIONS:
"user_user_groups",
"user_user_roles",
"users",
// "migrations",
}
for _, tableName := range tableNames {
/* #nosec */
@@ -828,12 +830,14 @@ GLOBAL OPTIONS:
}
var hosts []*dbmodels.Host
db = db.Preload("Groups")
if myself.HasRole("admin") {
db = db.Preload("SSHKey")
}
if err := dbmodels.HostsByIdentifiers(db, c.Args()).Find(&hosts).Error; err != nil {
return err
if err := dbmodels.HostsByIdentifiers(db.Preload("Groups").Preload("SSHKey"), c.Args()).Find(&hosts).Error; err != nil {
return err
}
} else {
if err := dbmodels.HostsByIdentifiers(db.Preload("Groups"), c.Args()).Find(&hosts).Error; err != nil {
return err
}
}
if c.Bool("decrypt") {
@@ -885,7 +889,9 @@ GLOBAL OPTIONS:
authKey := ""
if host.SSHKeyID > 0 {
var key dbmodels.SSHKey
db.Model(host).Related(&key)
if err := db.Model(host).Association("SSHKey").Find(&key); err != nil {
return err
}
authKey = key.Name
}
groupNames := []string{}
@@ -895,7 +901,9 @@ GLOBAL OPTIONS:
var hop string
if host.HopID != 0 {
var hopHost dbmodels.Host
db.Model(host).Related(&hopHost, "HopID")
if err := db.Model(host).Association("HopID").Find(&hopHost); err != nil {
return err
}
hop = hopHost.Name
} else {
hop = ""
@@ -930,7 +938,7 @@ GLOBAL OPTIONS:
return err
}
return dbmodels.HostsByIdentifiers(db, c.Args()).Delete(&dbmodels.Host{}).Error
return dbmodels.HostsByIdentifiers(db, c.Args()).Unscoped().Delete(&dbmodels.Host{}).Error
},
}, {
Name: "update",
@@ -999,7 +1007,7 @@ GLOBAL OPTIONS:
tx.Rollback()
return err
}
if err := model.Association("Hop").Replace(hop).Error; err != nil {
if err := model.Association("Hop").Replace(hop); err != nil {
tx.Rollback()
return err
}
@@ -1020,8 +1028,10 @@ GLOBAL OPTIONS:
if c.Bool("unset-hop") {
var hopHost dbmodels.Host
db.Model(&host).Related(&hopHost, "HopID")
if err := model.Association("Hop").Clear().Error; err != nil {
if err := db.Model(&host).Association("HopID").Find(&hopHost); err != nil {
return err
}
if err := model.Association("Hop").Clear(); err != nil {
tx.Rollback()
return err
}
@@ -1034,7 +1044,7 @@ GLOBAL OPTIONS:
tx.Rollback()
return err
}
if err := model.Association("SSHKey").Replace(&key).Error; err != nil {
if err := model.Association("SSHKey").Replace(&key); err != nil {
tx.Rollback()
return err
}
@@ -1049,12 +1059,12 @@ GLOBAL OPTIONS:
tx.Rollback()
return err
}
if err := model.Association("Groups").Append(&appendGroups).Error; err != nil {
if err := model.Association("Groups").Append(&appendGroups); err != nil {
tx.Rollback()
return err
}
if len(deleteGroups) > 0 {
if err := model.Association("Groups").Delete(deleteGroups).Error; err != nil {
if err := model.Association("Groups").Delete(deleteGroups); err != nil {
tx.Rollback()
return err
}
@@ -1185,7 +1195,7 @@ GLOBAL OPTIONS:
return err
}
return dbmodels.HostGroupsByIdentifiers(db, c.Args()).Delete(&dbmodels.HostGroup{}).Error
return dbmodels.HostGroupsByIdentifiers(db, c.Args()).Unscoped().Delete(&dbmodels.HostGroup{}).Error
},
}, {
Name: "update",
@@ -1456,7 +1466,6 @@ GLOBAL OPTIONS:
key.Name,
key.Type,
fmt.Sprintf("%d", key.Length),
//key.Fingerprint,
fmt.Sprintf("%d", len(key.Hosts)),
humanize.Time(key.UpdatedAt),
humanize.Time(key.CreatedAt),
@@ -1480,7 +1489,7 @@ GLOBAL OPTIONS:
return err
}
return dbmodels.SSHKeysByIdentifiers(db, c.Args()).Delete(&dbmodels.SSHKey{}).Error
return dbmodels.SSHKeysByIdentifiers(db, c.Args()).Unscoped().Delete(&dbmodels.SSHKey{}).Error
},
}, {
Name: "setup",
@@ -1621,9 +1630,11 @@ GLOBAL OPTIONS:
return err
}
// FIXME: validate email
email := c.Args().First()
valid := utils.ValidateEmail(email)
if !valid {
return errors.New("invalid email")
}
name := strings.Split(email, "@")[0]
if c.String("name") != "" {
name = c.String("name")
@@ -1727,7 +1738,7 @@ GLOBAL OPTIONS:
return err
}
return dbmodels.UsersByIdentifiers(db, c.Args()).Delete(&dbmodels.User{}).Error
return dbmodels.UsersByIdentifiers(db, c.Args()).Unscoped().Delete(&dbmodels.User{}).Error
},
}, {
Name: "update",
@@ -1797,12 +1808,12 @@ GLOBAL OPTIONS:
tx.Rollback()
return err
}
if err := model.Association("Groups").Append(&appendGroups).Error; err != nil {
if err := model.Association("Groups").Append(&appendGroups); err != nil {
tx.Rollback()
return err
}
if len(deleteGroups) > 0 {
if err := model.Association("Groups").Delete(deleteGroups).Error; err != nil {
if err := model.Association("Groups").Delete(deleteGroups); err != nil {
tx.Rollback()
return err
}
@@ -1817,12 +1828,12 @@ GLOBAL OPTIONS:
tx.Rollback()
return err
}
if err := model.Association("Roles").Append(&appendRoles).Error; err != nil {
if err := model.Association("Roles").Append(&appendRoles); err != nil {
tx.Rollback()
return err
}
if len(deleteRoles) > 0 {
if err := model.Association("Roles").Delete(deleteRoles).Error; err != nil {
if err := model.Association("Roles").Delete(deleteRoles); err != nil {
tx.Rollback()
return err
}
@@ -1955,7 +1966,7 @@ GLOBAL OPTIONS:
return err
}
return dbmodels.UserGroupsByIdentifiers(db, c.Args()).Delete(&dbmodels.UserGroup{}).Error
return dbmodels.UserGroupsByIdentifiers(db, c.Args()).Unscoped().Delete(&dbmodels.UserGroup{}).Error
},
}, {
Name: "update",
@@ -2026,34 +2037,58 @@ GLOBAL OPTIONS:
return err
}
fmt.Fprintf(s, "Enter key:\n")
reader := bufio.NewReader(s)
text, _ := reader.ReadString('\n')
key, comment, _, _, err := ssh.ParseAuthorizedKey([]byte(text))
if err != nil {
return err
var reader *bufio.Reader
var term *terminal.Terminal
if len(sshCommand) == 0 { // interactive mode
term = terminal.NewTerminal(s, "Paste your key(s) and end with a blank line> ")
} else {
fmt.Fprintf(s, "Enter key(s):\n")
reader = bufio.NewReader(s)
}
userkey := dbmodels.UserKey{
User: &user,
Key: key.Marshal(),
Comment: comment,
AuthorizedKey: string(gossh.MarshalAuthorizedKey(key)),
}
if c.String("comment") != "" {
userkey.Comment = c.String("comment")
}
for {
var text string
var errReadline error
if len(sshCommand) == 0 { // interactive mode
text, errReadline = term.ReadLine()
} else {
text, errReadline = reader.ReadString('\n')
}
if errReadline != nil && errReadline != io.EOF {
return errReadline
}
if text != "" && text != "\n" {
key, comment, _, _, err := ssh.ParseAuthorizedKey([]byte(text))
if err != nil {
return err
}
if _, err := govalidator.ValidateStruct(userkey); err != nil {
return err
}
userkey := dbmodels.UserKey{
User: &user,
Key: key.Marshal(),
Comment: comment,
AuthorizedKey: string(gossh.MarshalAuthorizedKey(key)),
}
if c.String("comment") != "" {
userkey.Comment = c.String("comment")
}
// save the userkey in database
if err := db.Create(&userkey).Error; err != nil {
return err
if _, err := govalidator.ValidateStruct(userkey); err != nil {
return err
}
// save the userkey in database
if err := db.Create(&userkey).Error; err != nil {
return err
}
fmt.Fprintf(s, "%d\n", userkey.ID)
if errReadline == io.EOF {
return nil
}
} else {
break
}
}
fmt.Fprintf(s, "%d\n", userkey.ID)
return nil
},
}, {
@@ -2149,9 +2184,9 @@ GLOBAL OPTIONS:
if err := dbmodels.UserKeysByUserID(db, []string{fmt.Sprint(user.ID)}).Find(&dbmodels.UserKey{}).Error; err != nil {
return err
}
return dbmodels.UserKeysByUserID(db, []string{fmt.Sprint(user.ID)}).Delete(&dbmodels.UserKey{}).Error
return dbmodels.UserKeysByUserID(db, []string{fmt.Sprint(user.ID)}).Unscoped().Delete(&dbmodels.UserKey{}).Error
}
return dbmodels.UserKeysByIdentifiers(db, c.Args()).Delete(&dbmodels.UserKey{}).Error
return dbmodels.UserKeysByIdentifiers(db, c.Args()).Unscoped().Delete(&dbmodels.UserKey{}).Error
},
},
},
@@ -2311,7 +2346,7 @@ GLOBAL OPTIONS:
if cliErr.ExitCode() != 0 {
fmt.Fprintf(s, "error: %v\n", err)
}
//s.Exit(cliErr.ExitCode())
// s.Exit(cliErr.ExitCode())
} else {
fmt.Fprintf(s, "error: %v\n", err)
}

View File

@@ -10,8 +10,8 @@ import (
"time"
"github.com/gliderlabs/ssh"
"github.com/jinzhu/gorm"
gossh "golang.org/x/crypto/ssh"
"gorm.io/gorm"
"moul.io/sshportal/pkg/crypto"
"moul.io/sshportal/pkg/dbmodels"
)
@@ -28,6 +28,7 @@ type authContext struct {
db *gorm.DB
userKey dbmodels.UserKey
logsLocation string
aclCheckCmd string
aesKey string
dbDriver, dbURL string
bindAddr string
@@ -88,6 +89,22 @@ func ChannelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewCh
actx := ctx.Value(authContextKey).(*authContext)
if actx.user.ID == 0 && actx.userType() != userTypeHealthcheck {
ip, err := net.ResolveTCPAddr(conn.RemoteAddr().Network(), conn.RemoteAddr().String())
if err == nil {
log.Printf("Auth failed: sshUser=%q remote=%q", conn.User(), ip.IP.String())
actx.err = errors.New("access denied")
ch, _, err2 := newChan.Accept()
if err2 != nil {
return
}
fmt.Fprintf(ch, "error: %v\n", actx.err)
_ = ch.Close()
return
}
}
switch actx.userType() {
case userTypeBastion:
log.Printf("New connection(bastion): sshUser=%q remote=%q local=%q dbUser=id:%d,email:%s", conn.User(), conn.RemoteAddr(), conn.LocalAddr(), actx.user.ID, actx.user.Email)
@@ -127,7 +144,10 @@ func ChannelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewCh
}}, sessionConfigs...)
if currentHost.HopID != 0 {
var newHost dbmodels.Host
actx.db.Model(currentHost).Related(&newHost, "HopID")
if err := actx.db.Model(currentHost).Association("HopID").Find(&newHost); err != nil {
log.Printf("Error: %v", err)
return
}
hostname := newHost.Name
currentHost, _ = dbmodels.HostByName(actx.db, hostname)
} else {
@@ -206,7 +226,7 @@ func bastionClientConfig(ctx ssh.Context, host *dbmodels.Host) (*gossh.ClientCon
return nil, err
}
action := checkACLs(tmpUser, tmpHost)
action := checkACLs(tmpUser, tmpHost, actx.aclCheckCmd)
switch action {
case string(dbmodels.ACLActionAllow):
// do nothing
@@ -251,12 +271,13 @@ func ShellHandler(s ssh.Session, version, gitSha, gitTag string) {
panic("should not happen")
}
func PasswordAuthHandler(db *gorm.DB, logsLocation, aesKey, dbDriver, dbURL, bindAddr string, demo bool) ssh.PasswordHandler {
func PasswordAuthHandler(db *gorm.DB, logsLocation, aclCheckCmd, aesKey, dbDriver, dbURL, bindAddr string, demo bool) ssh.PasswordHandler {
return func(ctx ssh.Context, pass string) bool {
actx := &authContext{
db: db,
inputUsername: ctx.User(),
logsLocation: logsLocation,
aclCheckCmd: aclCheckCmd,
aesKey: aesKey,
dbDriver: dbDriver,
dbURL: dbURL,
@@ -287,12 +308,13 @@ func PrivateKeyFromDB(db *gorm.DB, aesKey string) func(*ssh.Server) error {
}
}
func PublicKeyAuthHandler(db *gorm.DB, logsLocation, aesKey, dbDriver, dbURL, bindAddr string, demo bool) ssh.PublicKeyHandler {
func PublicKeyAuthHandler(db *gorm.DB, logsLocation, aclCheckCmd, aesKey, dbDriver, dbURL, bindAddr string, demo bool) ssh.PublicKeyHandler {
return func(ctx ssh.Context, key ssh.PublicKey) bool {
actx := &authContext{
db: db,
inputUsername: ctx.User(),
logsLocation: logsLocation,
aclCheckCmd: aclCheckCmd,
aesKey: aesKey,
dbDriver: dbDriver,
dbURL: dbURL,

View File

@@ -79,7 +79,7 @@ func scannerSplitFunc(data []byte, atEOF bool) (advance int, token []byte, err e
func telnetHandler(host *dbmodels.Host) ssh.Handler {
return func(s ssh.Session) {
// FIXME: log session in db
//actx := s.Context().Value(authContextKey).(*authContext)
// actx := s.Context().Value(authContextKey).(*authContext)
caller := bastionTelnetCaller{ssh: s}
if err := telnet.DialToAndCall(host.DialAddr(), caller); err != nil {
fmt.Fprintf(s, "error: %v", err)

View File

@@ -195,7 +195,7 @@ func decrypt(key []byte, cryptoText string) (string, error) {
ciphertext = ciphertext[aes.BlockSize:]
stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(ciphertext, ciphertext)
return fmt.Sprintf("%s", ciphertext), nil
return string(ciphertext), nil
}
func safeDecrypt(key []byte, cryptoText string) string {

View File

@@ -9,8 +9,8 @@ import (
"strings"
"time"
"github.com/jinzhu/gorm"
gossh "golang.org/x/crypto/ssh"
"gorm.io/gorm"
)
type Config struct {
@@ -30,7 +30,7 @@ type Config struct {
type Setting struct {
gorm.Model
Name string `valid:"required"`
Name string `valid:"required" gorm:"index:uix_settings_name,unique"`
Value string `valid:"required"`
}
@@ -38,7 +38,7 @@ type Setting struct {
type SSHKey struct {
// FIXME: use uuid for ID
gorm.Model
Name string `valid:"required,length(1|255),unix_user"`
Name string `valid:"required,length(1|255),unix_user" gorm:"index:uix_keys_name,unique"`
Type string `valid:"required"`
Length uint `valid:"required"`
Fingerprint string `valid:"optional"`
@@ -51,7 +51,7 @@ type SSHKey struct {
type Host struct {
// FIXME: use uuid for ID
gorm.Model
Name string `gorm:"size:255" valid:"required,length(1|255)"`
Name string `gorm:"index:uix_hosts_name,unique;type:varchar(255)" valid:"required,length(1|255)"`
Addr string `valid:"optional"` // FIXME: to be removed in a future version in favor of URL
User string `valid:"optional"` // FIXME: to be removed in a future version in favor of URL
Password string `valid:"optional"` // FIXME: to be removed in a future version in favor of URL
@@ -87,7 +87,7 @@ type User struct {
gorm.Model
Roles []*UserRole `gorm:"many2many:user_user_roles"`
Email string `valid:"required,email"`
Name string `valid:"required,length(1|255),unix_user"`
Name string `valid:"required,length(1|255),unix_user" gorm:"index:uix_users_name,unique"`
Keys []*UserKey `gorm:"ForeignKey:UserID"`
Groups []*UserGroup `gorm:"many2many:user_user_groups;"`
Comment string `valid:"optional"`
@@ -96,7 +96,7 @@ type User struct {
type UserGroup struct {
gorm.Model
Name string `valid:"required,length(1|255),unix_user"`
Name string `valid:"required,length(1|255),unix_user" gorm:"index:uix_usergroups_name,unique"`
Users []*User `gorm:"many2many:user_user_groups;"`
ACLs []*ACL `gorm:"many2many:user_group_acls;"`
Comment string `valid:"optional"`
@@ -104,7 +104,7 @@ type UserGroup struct {
type HostGroup struct {
gorm.Model
Name string `valid:"required,length(1|255),unix_user"`
Name string `valid:"required,length(1|255),unix_user" gorm:"index:uix_hostgroups_name,unique"`
Hosts []*Host `gorm:"many2many:host_host_groups;"`
ACLs []*ACL `gorm:"many2many:host_group_acls;"`
Comment string `valid:"optional"`
@@ -410,9 +410,6 @@ func UserKeysByUserID(db *gorm.DB, identifiers []string) *gorm.DB {
// UserRole helpers
//func UserRolesPreload(db *gorm.DB) *gorm.DB {
// return db.Preload("Users")
//}
func UserRolesByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
return GenericNameOrID(db, identifiers)
}
@@ -461,7 +458,6 @@ func (e *Event) Log(db *gorm.DB) {
}
func (e *Event) SetAuthor(user *User) *Event {
//e.Author = user
e.AuthorID = user.ID
return e
}

View File

@@ -0,0 +1,13 @@
package utils
import "regexp"
var emailRegex = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
// ValidateEmail validates email.
func ValidateEmail(e string) bool {
if len(e) < 3 || len(e) > 254 {
return false
}
return emailRegex.MatchString(e)
}

View File

@@ -0,0 +1,30 @@
package utils_test
import (
"testing"
"moul.io/sshportal/pkg/utils"
)
func TestValidateEmail(t *testing.T) {
tests := []struct {
input string
expected bool
}{
{"goodemail@email.com", true},
{"b@2323.22", true},
{"b@2322.", false},
{"", false},
{"blah", false},
{"blah.com", false},
}
for _, test := range tests {
t.Run(test.input, func(t *testing.T) {
got := utils.ValidateEmail(test.input)
if got != test.expected {
t.Errorf("expected %v, got %v", test.expected, got)
}
})
}
}

20
rules.mk vendored
View File

@@ -113,13 +113,13 @@ ifeq ($(CI),true)
@echo "mode: atomic" > /tmp/gocoverage
@rm -f $(GOTESTJSON_FILE)
@set -e; for dir in $(GOMOD_DIRS); do (set -e; (set -euf pipefail; \
cd $$dir; \
(($(GO) test ./... $(GO_TEST_OPTS) -cover -coverprofile=/tmp/profile.out -covermode=atomic -race -json && touch $@.ok) | tee -a $(GOTESTJSON_FILE) 3>&1 1>&2 2>&3 | tee -a $(GOBUILDLOG_FILE); \
cd $$dir; \
(($(GO) test ./... $(GO_TEST_OPTS) -cover -coverprofile=/tmp/profile.out -covermode=atomic -race -json && touch $@.ok) | tee -a $(GOTESTJSON_FILE) 3>&1 1>&2 2>&3 | tee -a $(GOBUILDLOG_FILE); \
); \
rm $@.ok 2>/dev/null || exit 1; \
if [ -f /tmp/profile.out ]; then \
cat /tmp/profile.out | sed "/mode: atomic/d" >> /tmp/gocoverage; \
rm -f /tmp/profile.out; \
cat /tmp/profile.out | sed "/mode: atomic/d" >> /tmp/gocoverage; \
rm -f /tmp/profile.out; \
fi)); done
@mv /tmp/gocoverage $(GOCOVERAGE_FILE)
else
@@ -128,8 +128,8 @@ else
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; \
cat /tmp/profile.out | sed "/mode: atomic/d" >> /tmp/gocoverage; \
rm -f /tmp/profile.out; \
fi); done
@mv /tmp/gocoverage $(GOCOVERAGE_FILE)
endif
@@ -193,7 +193,7 @@ go.bumpdeps:
$(GO) get -u ./...; \
); done
.PHONY: go.bump-deps
.PHONY: go.fmt
go.fmt:
@set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \
cd $$dir; \
@@ -243,8 +243,8 @@ 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; \
cd $$dir; \
npm publish --access=public; \
); done; \
fi
RELEASE_STEPS += npm.publish
@@ -254,7 +254,7 @@ endif
## Docker
##
docker_build = docker build \
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` \

View File

@@ -8,8 +8,13 @@ import (
"os"
"time"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"github.com/gliderlabs/ssh"
"github.com/jinzhu/gorm"
"github.com/urfave/cli"
gossh "golang.org/x/crypto/ssh"
"moul.io/sshportal/pkg/bastion"
@@ -22,6 +27,7 @@ type serverConfig struct {
bindAddr string
debug, demo bool
idleTimeout time.Duration
aclCheckCmd string
}
func parseServerConfig(c *cli.Context) (*serverConfig, error) {
@@ -34,6 +40,7 @@ func parseServerConfig(c *cli.Context) (*serverConfig, error) {
demo: c.Bool("demo"),
logsLocation: c.String("logs-location"),
idleTimeout: c.Duration("idle-timeout"),
aclCheckCmd: c.String("acl-check-cmd"),
}
switch len(ret.aesKey) {
case 0, 16, 24, 32:
@@ -58,24 +65,41 @@ func ensureLogDirectory(location string) error {
return nil
}
func server(c *serverConfig) (err error) {
var db = (*gorm.DB)(nil)
// try to setup the local DB
if db, err = gorm.Open(c.dbDriver, c.dbURL); err != nil {
return
func dbConnect(c *serverConfig, config gorm.Option) (*gorm.DB, error) {
var dbOpen func(string) gorm.Dialector
if c.dbDriver == "sqlite3" {
dbOpen = sqlite.Open
}
if c.dbDriver == "postgres" {
dbOpen = postgres.Open
}
if c.dbDriver == "mysql" {
dbOpen = mysql.Open
}
return gorm.Open(dbOpen(c.dbURL), config)
}
func server(c *serverConfig) (err error) {
// configure db logging
db, err := dbConnect(c, &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
sqlDB, err := db.DB()
defer func() {
origErr := err
err = db.Close()
err = sqlDB.Close()
if origErr != nil {
err = origErr
}
}()
if err = db.DB().Ping(); err != nil {
if err = sqlDB.Ping(); err != nil {
return
}
db.LogMode(c.debug)
if err = bastion.DBInit(db); err != nil {
return
}
@@ -119,8 +143,8 @@ func server(c *serverConfig) (err error) {
for _, opt := range []ssh.Option{
// custom PublicKeyAuth handler
ssh.PublicKeyAuth(bastion.PublicKeyAuthHandler(db, c.logsLocation, c.aesKey, c.dbDriver, c.dbURL, c.bindAddr, c.demo)),
ssh.PasswordAuth(bastion.PasswordAuthHandler(db, c.logsLocation, c.aesKey, c.dbDriver, c.dbURL, c.bindAddr, c.demo)),
ssh.PublicKeyAuth(bastion.PublicKeyAuthHandler(db, c.logsLocation, c.aclCheckCmd, c.aesKey, c.dbDriver, c.dbURL, c.bindAddr, c.demo)),
ssh.PasswordAuth(bastion.PasswordAuthHandler(db, c.logsLocation, c.aclCheckCmd, c.aesKey, c.dbDriver, c.dbURL, c.bindAddr, c.demo)),
// retrieve sshportal SSH private key from database
bastion.PrivateKeyFromDB(db, c.aesKey),
} {

View File

@@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
package main
@@ -60,7 +61,7 @@ func testServer(c *cli.Context) error {
_, _ = io.Copy(s, f) // #nosec
cmdErr = cmd.Wait()
} else {
//cmd.Stdin = s
// cmd.Stdin = s
cmd.Stdout = s
cmd.Stderr = s
cmdErr = cmd.Run()