Compare commits

...

180 Commits

Author SHA1 Message Date
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
Manfred Touron
84a0a31eda Merge pull request #253 from jwessel/feat_postgres
feat: Add postgres support
2021-03-26 16:26:05 +01:00
Manfred Touron
40bbea590c Merge pull request #248 from jwessel/master
feat: Allow removal by user for 'userkey rm'
2021-03-26 16:18:27 +01:00
Manfred Touron
e455d50db9 Merge pull request #251 from jwessel/feat_user_udpate
feat: Allow for update or removal of the invite token
2021-03-26 16:11:28 +01:00
Manfred Touron
be3f215e24 Merge pull request #256 from moul/renovate/docker-all
chore(deps): update all docker tags to v1.16.2
2021-03-26 11:46:56 +01:00
Renovate Bot
c290253546 chore(deps): update all docker tags to v1.16.2 2021-03-12 02:24:27 +00: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
19605f0054 feat: Add postgres support
Postgres is more picky about submitting a string to the id column in a
table.  Postgres requires the use of only integers for the array of
values in a select statement containing: where id IN (...array...)

This patch fixes all the following class of problems:

   SELECT * FROM "ssh_keys" WHERE
   "ssh_keys"."deleted_at" IS NULL AND ((id IN ('host')) OR (name IN
   ('host'))) ORDER BY "ssh_keys"."id" ASC LIMIT 1 [0 rows affected or
   returned ] error: pq: invalid input syntax for
   type integer: "host"

Signed-off-by: Jason Wessel <jason.wessel@windriver.com>
2021-03-09 08:57:08 -06:00
Jason Wessel
5b4332072c feat: Allow for update or removal of the invite token
If the invite leaks for the admin user it is possible for the admin
user to be compromised by another invite request.  It needs to be
possible to entirely remove the invite capability for any given user.

New arguments added to user update:

   --invite_token value, -i value            Updates the invite token
   --remove_invite, -R                       Remove invite token

Signed-off-by: Jason Wessel <jason.wessel@windriver.com>
2021-03-09 08:51:45 -06:00
Jason Wessel
c1c4c556b4 feat: Allow removal by user for 'userkey rm'
The userkey rm command implies that it can remove a key by user or the
id key, but it only works against the data base id of the key.  This
patch allows the userkey rm command to work with the user name, so
that all the keys for the user can be cleared out in one command.

Signed-off-by: Jason Wessel <jason.wessel@windriver.com>
2021-03-08 11:22:38 -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
Manfred Touron
762736d622 Merge pull request #216 from jle64/ecdsa 2021-02-28 07:46:06 +01:00
Manfred Touron
bbbc484fe8 Merge pull request #247 from moul/dependabot/github_actions/golangci/golangci-lint-action-v2.5.1
chore(deps): bump golangci/golangci-lint-action from v2.3.0 to v2.5.1
2021-02-28 07:44:57 +01:00
Manfred Touron
e1602364c8 Merge pull request #235 from moul/dependabot/go_modules/github.com/gliderlabs/ssh-0.3.2
chore(deps): bump github.com/gliderlabs/ssh from 0.3.1 to 0.3.2
2021-02-28 07:44:52 +01:00
Manfred Touron
2540d1e861 Merge pull request #241 from moul/dependabot/go_modules/github.com/olekukonko/tablewriter-0.0.5
chore(deps): bump github.com/olekukonko/tablewriter from 0.0.4 to 0.0.5
2021-02-28 07:44:48 +01:00
Manfred Touron
177a198420 Merge pull request #244 from moul/renovate/docker-all
chore(deps): update all docker tags to v1.16.0
2021-02-28 07:44:44 +01:00
Manfred Touron
51612aab13 Merge pull request #245 from moul/dependabot/docker/golang-1.16.0
chore(deps): bump golang from 1.15.8 to 1.16.0
2021-02-28 07:44:38 +01:00
dependabot[bot]
e20af1dde5 chore(deps): bump golangci/golangci-lint-action from v2.3.0 to v2.5.1
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from v2.3.0 to v2.5.1.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v2.3.0...d9f0e73c0497685d68af8c58280f49fcaf0545ff)

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-25 04:18:52 +00:00
Renovate Bot
6caa1f1657 chore(deps): update all docker tags to v1.16.0 2021-02-18 03:57:04 +00:00
dependabot[bot]
e0f76d15ec chore(deps): bump golang from 1.15.8 to 1.16.0
Bumps golang from 1.15.8 to 1.16.0.

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-17 04:17:32 +00:00
matteyeux
05225a4b25 Fix typo in "shell commands" section in README.md 2021-02-15 11:27:12 +01:00
dependabot[bot]
bcc150727f chore(deps): bump github.com/olekukonko/tablewriter from 0.0.4 to 0.0.5
Bumps [github.com/olekukonko/tablewriter](https://github.com/olekukonko/tablewriter) from 0.0.4 to 0.0.5.
- [Release notes](https://github.com/olekukonko/tablewriter/releases)
- [Commits](https://github.com/olekukonko/tablewriter/compare/v0.0.4...v0.0.5)

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-11 04:21:57 +00:00
Manfred Touron
9062417d13 Merge pull request #237 from moul/renovate/docker-all
chore(deps): update all docker tags to v1.15.8
2021-02-07 12:12:52 +01:00
Manfred Touron
baeade4043 Merge pull request #238 from moul/dependabot/docker/golang-1.15.8
chore(deps): bump golang from 1.15.7 to 1.15.8
2021-02-07 12:12:41 +01:00
Renovate Bot
b9552e98b5 chore(deps): update all docker tags to v1.15.8 2021-02-06 02:14:21 +00:00
dependabot[bot]
715ccde829 chore(deps): bump golang from 1.15.7 to 1.15.8
Bumps golang from 1.15.7 to 1.15.8.

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-05 04:16:20 +00:00
dependabot[bot]
f5dc1bd1b9 chore(deps): bump github.com/gliderlabs/ssh from 0.3.1 to 0.3.2
Bumps [github.com/gliderlabs/ssh](https://github.com/gliderlabs/ssh) from 0.3.1 to 0.3.2.
- [Release notes](https://github.com/gliderlabs/ssh/releases)
- [Commits](https://github.com/gliderlabs/ssh/compare/v0.3.1...v0.3.2)

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-02 04:22:33 +00:00
Jonathan Lestrelin
c79c50aeb6 Remove go versions with missing requirements for ecdsa/ed2519 from CI. 2021-01-25 18:21:44 +01:00
Manfred Touron
df3542c6ee Merge pull request #233 from moul/dependabot/docker/golang-1.15.7
chore(deps): bump golang from 1.15.6 to 1.15.7
2021-01-25 14:22:48 +01:00
Manfred Touron
e40f5307a3 Merge pull request #232 from moul/renovate/docker-all
chore(deps): update all docker tags to v1.15.7
2021-01-25 14:22:04 +01:00
Renovate Bot
6e6045306b chore(deps): update all docker tags to v1.15.7 2021-01-21 00:14:55 +00:00
dependabot[bot]
874467b1e6 chore(deps): bump golang from 1.15.6 to 1.15.7
Bumps golang from 1.15.6 to 1.15.7.

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-20 04:03:40 +00:00
Jonathan Lestrelin
5c1c559a9a Merge remote-tracking branch 'upstream/master' into ecdsa 2021-01-12 08:18:28 +01:00
Manfred Touron
6872c727ef Merge pull request #231 from moul/dev/moul/maintenance
chore: repo maintenance 🤖
2021-01-02 10:51:40 +01:00
moul-bot
cae996d041 chore: repo maintenance 🤖
more details: https://github.com/moul/repoman

Signed-off-by: moul-bot <bot@moul.io>
2021-01-01 15:24:08 +01:00
Manfred Touron
a23b77282c Merge pull request #229 from moul/renovate/docker-all
chore(deps): update circleci/golang docker tag to v1.15.6
2020-12-27 12:12:16 +01:00
Manfred Touron
24814c4152 Merge pull request #230 from moul/dependabot/docker/golang-1.15.6
chore(deps): bump golang from 1.15.5 to 1.15.6
2020-12-26 19:01:22 +01:00
Renovate Bot
07359988d0 chore(deps): update all docker tags to v1.15.6 2020-12-05 00:51:28 +00:00
dependabot[bot]
db6eb63297 chore(deps): bump golang from 1.15.5 to 1.15.6
Bumps golang from 1.15.5 to 1.15.6.

Signed-off-by: dependabot[bot] <support@github.com>
2020-12-04 04:22:41 +00:00
Manfred Touron
5fdb31b97d Merge pull request #221 from moul/dependabot/github_actions/golangci/golangci-lint-action-v2.3.0
chore(deps): bump golangci/golangci-lint-action from v0.1.7 to v2.3.0
2020-11-15 21:48:47 +01:00
Manfred Touron
bce6b1998b Merge pull request #220 from moul/dependabot/github_actions/actions/cache-v2.1.3
chore(deps): bump actions/cache from v1 to v2.1.3
2020-11-15 21:48:34 +01:00
dependabot[bot]
f7fa60da97 chore(deps): bump golangci/golangci-lint-action from v0.1.7 to v2.3.0
Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from v0.1.7 to v2.3.0.
- [Release notes](https://github.com/golangci/golangci-lint-action/releases)
- [Commits](https://github.com/golangci/golangci-lint-action/compare/v0.1.7...e868220d9fd3b523f1a8fcfb69749e8c7521ba14)

Signed-off-by: dependabot[bot] <support@github.com>
2020-11-15 20:40:22 +00:00
Manfred Touron
d2cd6b64a3 Merge pull request #215 from moul/renovate/all
chore(deps): update all
2020-11-15 21:39:39 +01:00
Manfred Touron
1ef0cc8725 Merge pull request #225 from moul/renovate/docker-all
chore(deps): update all docker tags to v1.15.5
2020-11-15 21:38:37 +01:00
Renovate Bot
d894005c3f chore(deps): update all docker tags to v1.15.5 2020-11-13 01:41:26 +00:00
Renovate Bot
af7206d114 chore(deps): update all 2020-11-12 16:47:33 +00:00
dependabot[bot]
1f9d962cd6 chore(deps): bump actions/cache from v1 to v2.1.3
Bumps [actions/cache](https://github.com/actions/cache) from v1 to v2.1.3.
- [Release notes](https://github.com/actions/cache/releases)
- [Commits](https://github.com/actions/cache/compare/v1...0781355a23dac32fd3bac414512f4b903437991a)

Signed-off-by: dependabot[bot] <support@github.com>
2020-11-11 01:04:31 +00:00
Manfred Touron
460041c6e3 Merge pull request #219 from moul/dev/moul/maintenance
chore: repo maintenance 🤖
2020-11-11 02:04:08 +01:00
moul-bot
7068565ab1 chore: repo maintenance 🤖
more details: https://github.com/moul/repoman

Signed-off-by: moul-bot <bot@moul.io>
2020-11-09 22:53:21 +01:00
Manfred Touron
74bd885c1d Merge pull request #218 from moul/renovate/docker-all
chore(deps): update all docker tags to v1.15.4
2020-11-07 16:54:04 +01:00
Renovate Bot
9317f206d1 chore(deps): update all docker tags to v1.15.4 2020-11-07 00:30:51 +00:00
Jonathan Lestrelin
6c3f803dc6 Add generation of ecdsa and ed25519 keys.
Make RSA keys use value from --length parameter.
Set default length when --length is unspecified based on key type.
Change default key format to ed25519 both in shell and for keys created
at initialization.
2020-10-10 04:21:11 +02:00
Renovate Bot
9c3d29eb83 chore(deps): update module gliderlabs/ssh to v0.3.1 2020-10-07 19:19:15 +00:00
Manfred Touron
e339a73931 Merge pull request #214 from moul/dev/moul/bump-deps4
chore: bump deps
2020-10-04 10:51:32 +02:00
Manfred Touron
0dcab1b380 chore: bump deps 2020-10-04 10:18:07 +02:00
Sergey Yashchuk
c697c9aaeb dev: ACLs external command hook 2020-09-18 01:24:51 +07:00
Manfred Touron
032f802348 Merge pull request #208 from moul/renovate/docker-all
chore(deps): update all docker tags to v1.15.2
2020-09-14 20:57:45 +02:00
Renovate Bot
7fd9be9058 chore(deps): update all docker tags to v1.15.2 2020-09-10 00:31:00 +00:00
Manfred Touron
83b54aeeff Merge pull request #205 from moul/dev/moul/go115
chore: go1.15
2020-08-19 20:28:55 +02:00
Manfred Touron
2323d6fd1e chore: go1.15 2020-08-19 19:33:18 +02:00
Manfred Touron
4c947ce391 Merge pull request #204 from GreyOBox/increase-size-of-name-fields
fix: increase size of name fields
2020-08-19 18:27:33 +02:00
Sergey Yashchuk
44559f0547 fix: increase size of name fields 2020-08-19 18:23:36 +02:00
Manfred Touron
8234119cd4 Merge pull request #197 from moul/renovate/all
chore(deps): update golang.org/x/crypto commit hash to 123391f
2020-08-10 18:22:45 +02:00
Manfred Touron
7a75c13ac4 Merge pull request #200 from moul/renovate/docker-all
chore(deps): update all docker tags to v1.14.7
2020-08-10 18:22:15 +02:00
Manfred Touron
4b10131790 Merge pull request #201 from moul/imgbot
[ImgBot] Optimize images
2020-08-09 17:14:22 +02:00
Manfred Touron
a29c6e8338 chore: add intro image 2020-08-09 00:30:36 +02:00
ImgBotApp
198e0717b5 [ImgBot] Optimize images
*Total -- 887.71kb -> 587.48kb (33.82%)

/.assets/bastion.jpg -- 503.44kb -> 249.40kb (50.46%)
/.assets/flow-diagram.png -- 104.11kb -> 79.45kb (23.69%)
/.assets/overview.png -- 32.65kb -> 26.50kb (18.82%)
/.assets/cluster-mysql.svg -- 8.50kb -> 7.08kb (16.74%)
/.assets/overview.svg -- 9.23kb -> 8.03kb (13.03%)
/.assets/flow-diagram.svg -- 13.85kb -> 12.39kb (10.51%)
/.assets/sql-schema.svg -- 36.89kb -> 33.99kb (7.85%)
/.assets/demo.gif -- 179.03kb -> 170.63kb (4.69%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
2020-08-08 22:28:50 +00:00
Manfred Touron
d8fa2f6925 Add files via upload 2020-08-09 00:28:34 +02:00
Renovate Bot
16c8c0092e chore(deps): update all docker tags to v1.14.7 2020-08-08 00:28:27 +00:00
Renovate Bot
b0dfff2d90 chore(deps): update golang.org/x/crypto commit hash to 123391f 2020-07-28 20:38:36 +00:00
Manfred Touron
9d2badf253 Merge pull request #196 from moul/dev/moul/pr-194 2020-07-23 18:55:42 +02:00
Konstantin Bakaras
428344da17 feat: MySQL, Postgres support
Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com>
2020-07-23 18:50:42 +02:00
Konstantin Bakaras
0c07ac790a feat: ACL Check with inception and expiration
Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com>
2020-07-23 18:50:35 +02:00
Konstantin Bakaras
365a37959a chore: Model and edit
Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com>
2020-07-23 18:50:35 +02:00
Manfred Touron
90fd6057cf Merge pull request #193 from moul/renovate/docker-all
chore(deps): update all docker tags to v1.14.6
2020-07-23 18:24:07 +02:00
Manfred Touron
4220f3fb89 Merge pull request #190 from moul/renovate/all
chore(deps): update all
2020-07-23 17:22:27 +02:00
Renovate Bot
3e2acfc992 chore(deps): update all 2020-07-20 06:19:00 +00:00
Renovate Bot
9c464b2610 chore(deps): update all docker tags to v1.14.6 2020-07-18 00:55:40 +00:00
Manfred Touron
5760aece65 Merge pull request #192 from moul/dev/moul/maintenance
chore: repo maintenance 🤖
2020-07-12 14:08:51 +02:00
moul-bot
a24e20252a chore: repo maintenance 🤖
more details: https://github.com/moul/repoman

Signed-off-by: moul-bot <bot@moul.io>
2020-07-12 14:04:09 +02:00
Manfred Touron
37a7fa1917 Merge pull request #189 from moul/renovate/all
chore(deps): update all
2020-07-09 17:47:46 +02:00
Renovate Bot
f1b28b0363 chore(deps): update all 2020-07-08 08:14:45 +00:00
Manfred Touron
e43bb55e70 Merge pull request #188 from moul/dev/moul/fix-166 2020-07-08 10:12:52 +02:00
Manfred Touron
763ced7524 feat: host logging modes (disabled, commands, everything)
Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com>
2020-07-04 22:16:46 +02:00
Manfred Touron
54128beb12 chore: point CHANGELOG.md to releases page
Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com>
2020-07-04 21:53:26 +02:00
Manfred Touron
64ba179cc7 chore: add .gitattributes
Signed-off-by: Manfred Touron <94029+moul@users.noreply.github.com>
2020-07-04 21:53:26 +02:00
Manfred Touron
bbdb4851a5 Merge pull request #187 from moul/dev/moul/maintenance
chore: repo maintenance 🤖
2020-07-04 01:59:48 +02:00
moul-bot
63719ec00e chore: repo maintenance 🤖
more details: https://github.com/moul/repoman

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

*Which issue this PR fixes: fixes #128*
Updated the readme to inform the user of a quick fix (ssh-keygen -t rsa) when association fails and user is asked for password
2020-05-28 11:12:28 +02:00
Mikael Rapp
e399dfd8e4 Update readme; special host names 2020-05-27 23:30:28 +02:00
Manfred Touron
be83c7148d Merge pull request #172 from moul/dev/moul/bump-deps4 2020-05-12 03:35:22 +02:00
Manfred Touron
ce187e8675 fix: bump deps 2020-05-12 03:30:27 +02:00
Manfred Touron
f13ede4ba7 Update README.md 2020-05-12 03:27:15 +02:00
Manfred Touron
fb061ed419 Merge pull request #138 from moul/renovate/all
Update all
2020-05-10 15:45:44 +02:00
Renovate Bot
b4a377f269 Update all 2020-05-02 08:14:28 +00:00
Manfred Touron
de6f37aa64 Merge pull request #170 from moul/renovate/docker-all
Update all Docker tags to v1.14.2
2020-04-10 10:11:05 +02:00
Renovate Bot
32219577b8 Update all Docker tags to v1.14.2 2020-04-10 00:21:11 +00:00
Manfred Touron
abc7329a71 Merge pull request #167 from moul/renovate/docker-all
Update all Docker tags to v1.14.1
2020-03-22 14:58:30 +01:00
Renovate Bot
675942e967 Update all Docker tags to v1.14.1 2020-03-21 00:28:54 +00:00
Manfred Touron
5b20cd501e Update README.md
See https://github.com/moul/sshportal/issues/164
2020-03-10 11:01:26 +01:00
Manfred Touron
b6aaf4d7cf Update README.md 2020-03-05 15:33:52 +01:00
Manfred Touron
972e232559 Merge pull request #143 from moul/renovate/docker-all
chore(deps): update all docker tags to v1.14.0
2020-03-01 17:17:37 +01:00
Manfred Touron
851a91b1a0 fix: fix deps 2020-02-28 22:12:46 +01:00
Renovate Bot
6a068dc430 chore(deps): update all docker tags to v1.14.0 2020-02-28 22:05:37 +01:00
Manfred Touron
2cdfcf60fe Merge pull request #163 from MitaliBo/master
Security fix for github.com/moby/moby
2020-02-28 22:04:24 +01:00
Manfred Touron
5d9e0c367a feat: use secure rand seed 2020-02-25 11:49:38 +01:00
MitaliBo
cbf8263033 Update go.mod 2020-02-24 14:27:29 -08:00
Manfred Touron
846c73d9bc Merge pull request #160 from moul/dev/moul/bump-deps
chore: bump-deps
2020-02-22 20:51:29 +01:00
Manfred Touron
e0b43b1976 Merge pull request #162 from moul/dev/moul/semantic-release
feat: set up semantic release
2020-02-22 20:48:51 +01:00
Manfred Touron
6a6e788968 feat: set up semantic release 2020-02-22 20:43:54 +01:00
Manfred Touron
4754cad42a chore: bump-deps 2020-02-18 00:12:56 +01:00
Manfred Touron
db58e53f3b Merge pull request #156 from jeanlouisferey/master
Little correction
2020-02-18 00:06:27 +01:00
Manfred Touron
b31acb4348 Merge pull request #148 from jle64/fix_shell_connection_log
Fix db user id type in shell connection log.
2020-02-18 00:06:20 +01:00
Manfred Touron
c794c2c076 chore: add funding + update go.sum 2020-02-18 00:05:35 +01:00
Jonathan Lestrelin
42d6cd44bb Fix db user id type in shell connection log. 2020-02-17 23:55:35 +01:00
Manfred Touron
f9057ca56a Merge pull request #157 from bozzo/master
Add Helm chart to deploy SSHPortal on Kubernetes
2020-02-17 23:54:39 +01:00
bozzo
c2f1999037 Set theme jekyll-theme-slate 2020-02-17 09:08:51 +01:00
bozzo
44b386f7a7 Add Helm chart to deploy SSHPortal on Kubernetes
Support HA with Mysql database
Support autoscaling
2020-02-08 23:06:19 +01:00
Jean-Louis Férey
89b296db4e Little correction 2020-01-29 16:35:39 +01:00
Manfred Touron
c16403fb3f Merge pull request #146 from opencollective/opencollective
Activating Open Collective
2019-09-18 09:11:08 +02:00
Jess
5e21fb72e6 Added financial contributors to the README 2019-09-17 18:02:58 -07:00
Manfred Touron
c5681bf880 chore: post-release version bump 2019-06-24 11:43:32 +02:00
58 changed files with 2090 additions and 1544 deletions

BIN
.assets/bastion.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 KiB

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 79 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -1,7 +1,7 @@
defaults: &defaults
working_directory: /go/src/moul.io/sshportal
docker:
- image: circleci/golang:1.12.6
- image: circleci/golang:1.16.5
environment:
GO111MODULE: "on"
@@ -16,18 +16,6 @@ install_retry: &install_retry
version: 2
jobs:
go.build:
<<: *defaults
steps:
- checkout
- *install_retry
- run: /tmp/retry -m 3 go mod download
- run: /tmp/retry -m 3 go mod vendor
- run: /tmp/retry -m 3 make install
- run: GO111MODULE=off /tmp/retry -m 3 go test -v ./...
- run: /tmp/retry -m 3 curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s v1.12.2
- run: PATH=$PATH:$(pwd)/bin /tmp/retry -m 3 make lint
docker.integration:
<<: *defaults
steps:
@@ -39,6 +27,7 @@ jobs:
curl -L https://github.com/docker/compose/releases/download/1.11.4/docker-compose-`uname -s`-`uname -m` > ~/docker-compose
- setup_remote_docker:
docker_layer_caching: true
version: 18.09.3 # https://github.com/golang/go/issues/40893
- *install_retry
- run: /tmp/retry -m 3 docker build -t moul/sshportal .
- run: /tmp/retry -m 3 make integration
@@ -48,6 +37,4 @@ workflows:
version: 2
build_and_integration:
jobs:
- go.build
- docker.integration
# requires: docker.build?

17
.gitattributes vendored Normal file
View File

@@ -0,0 +1,17 @@
# Auto detect text files and perform LF normalization
* text=auto
# Collapse vendored and generated files on GitHub
AUTHORS linguist-generated
vendor/* linguist-vendored
rules.mk linguist-vendored
*/vendor/* linguist-vendored
*.gen.* linguist-generated
*.pb.go linguist-generated
*.pb.gw.go linguist-generated
go.sum linguist-generated
go.mod linguist-generated
gen.sum linguist-generated
# Reduce conflicts on markdown files
*.md merge=union

6
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,6 @@
github: ["moul"]
patreon: moul
open_collective: sshportal
custom:
- "https://www.buymeacoffee.com/moul"
- "https://manfred.life/donate"

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! ❤️ -->

20
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
version: 2
updates:
- package-ecosystem: docker
directory: "/"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10
- package-ecosystem: gomod
directory: "/"
schedule:
interval: daily
time: "04:00"
open-pull-requests-limit: 10

View File

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

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

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

13
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
name: Semantic Release
on: push
jobs:
semantic-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: codfish/semantic-release-action@v1.9.0
if: github.ref == 'refs/heads/master'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

5
.gitignore vendored
View File

@@ -1,3 +1,4 @@
coverage.txt
dist/
*~
*#
@@ -6,4 +7,6 @@ dist/
/log/
/sshportal
*.db
/data
/data
sshportal.history
.idea

View File

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

8
.releaserc.js Normal file
View File

@@ -0,0 +1,8 @@
module.exports = {
branch: 'master',
plugins: [
'@semantic-release/commit-analyzer',
'@semantic-release/release-notes-generator',
'@semantic-release/github',
],
};

39
AUTHORS generated Normal file
View File

@@ -0,0 +1,39 @@
# This file lists all individuals having contributed content to the repository.
# For how it is generated, see 'https://github.com/moul/rules.mk'
ahh <ahamidullah@gmail.com>
Alen Masic <alenn.masic@gmail.com>
Alexander Turner <me@alexturner.co>
bozzo <bozzo@users.noreply.github.com>
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>
Jonathan Lestrelin <jonathan.lestrelin@gmail.com>
Julien Dessaux <julien.dessaux@adyxax.org>
Konstantin Bakaras <k.bakaras@voskhod.ru>
Manfred Touron <94029+moul@users.noreply.github.com>
Manfred Touron <m@42.am>
Manuel <manuel.sabban@nbs-system.com>
Manuel Sabban <manu@sabban.eu>
Manuel Sabban <msa@nbs-system.com>
Mathieu Pasquet <mathieu.pasquet@alterway.fr>
matteyeux <matteyeux@users.noreply.github.com>
Mikael Rapp <micke.rapp@gmail.com>
MitaliBo <mitali.bisht14@gmail.com>
moul-bot <bot@moul.io>
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>
valentin.daviot <valentin.daviot@alterway.fr>
welderpb <welderpb@users.noreply.github.com>
Дмитрий Шульгачик <tech@uniplug.ru>

View File

@@ -1,117 +1,3 @@
# Changelog
## v1.10.0 (2019-06-24)
* Bump deps, now using github.com/gliderlabs/ssh upstream
* Fix Windows build ([#101](https://github.com/moul/sshportal/pull/101)) by [@Raerten](https://github.com/Raerten)
* Use environment variables for settings ([#98](https://github.com/moul/sshportal/pull/98)) by [@Raerten](https://github.com/Raerten)
* Fix 'userkey create' ([#111](https://github.com/moul/sshportal/pull/111)) by [@shawn111](https://github.com/shawn111)
* Set log files mode to 440 instead of 640 ([#134](https://github.com/moul/sshportal/pull/134)) by [@jle64](https://github.com/jle64)
* Allow to create a host using an IP as name ([#135](https://github.com/moul/sshportal/pull/135)) by [@jle64](https://github.com/jle64)
* Add username and session ID to session log filename ([#133](https://github.com/moul/sshportal/pull/133)) by [@jle64](https://github.com/jle64)
* Unable to use encrypted SSH private keys ([#124](https://github.com/moul/sshportal/pull/124)) by [@welderpb](https://github.com/welderpb)
* Fix format of ID in new session + closing channel if host is unreachable ([#123](https://github.com/moul/sshportal/pull/123)) by [@vdaviot](https://github.com/vdaviot)
* Refactor the main package with a focus on splitting up into packages ([#113](https://github.com/moul/sshportal/pull/113)) by [@ahamidullah](https://github.com/ahamidullah)
## v1.9.0 (2018-11-18)
* Add `hostgroup update` and `usergroup update` commands ([#58](https://github.com/moul/sshportal/pull/58)) by [@adyxax](https://github.com/adyxax)
* Add socket timeout ([#80](https://github.com/moul/sshportal/pull/80)) by [@ahhx](https://github.com/ahhx)
* Add a flag to list only active sessions ([#76](https://github.com/moul/sshportal/pull/76)) by [@vdaviot](https://github.com/vdaviot)
* Unset hop on host ([#74](https://github.com/moul/sshportal/pull/74)) by [@vdaviot](https://github.com/vdaviot)
* Fix session status and duration display ([#75](https://github.com/moul/sshportal/pull/75)) by [@vdaviot](https://github.com/vdaviot)
* Fix log path and filename on Windows ([#78](https://github.com/moul/sshportal/pull/78)) by [@Raerten](https://github.com/Raerten)
* Admin user is not editable ([#69](https://github.com/moul/sshportal/pull/69)) by [@alenn-m](https://github.com/alenn-m)
* Switch to go modules (go1.11) ([#83](https://github.com/moul/sshportal/pull/83))
* Switch to moul.io/sshportal canonical URL ([#86](https://github.com/moul/sshportal/pull/86))
* Switch to golangci-lint ([#87](https://github.com/moul/sshportal/pull/87))
## v1.8.0 (2018-04-02)
* The default created user now has the same username as the user starting sshportal (was hardcoded "admin")
* Add Telnet support
* Add TTY audit feature ([#23](https://github.com/moul/sshportal/issues/23)) by [@sabban](https://github.com/sabban)
* Fix `--assign-*` commands when using MySQL driver ([#45](https://github.com/moul/sshportal/issues/45))
* Add *HOP* support, an efficient and integrated way of using a jump host transparently ([#47](https://github.com/moul/sshportal/issues/47)) by [@mathieui](https://github.com/mathieui)
* Fix panic on some `ls` commands ([#54](https://github.com/moul/sshportal/pull/54)) by [@jle64](https://github.com/jle64)
* Add tunnels (`direct-tcp`) support with logging ([#44](https://github.com/moul/sshportal/issues/44)) by [@sabban](https://github.com/sabban)
* Add `key import` command ([#52](https://github.com/moul/sshportal/issues/52)) by [@adyxax](https://github.com/adyxax)
* Add 'exec' logging ([#40](https://github.com/moul/sshportal/issues/40)) by [@sabban](https://github.com/sabban)
## v1.7.1 (2018-01-03)
* Return non-null exit-code on authentication error
* **hotfix**: repair invite system (broken in v1.7.0)
## v1.7.0 (2018-01-02)
Breaking changes:
* Use `sshportal server` instead of `sshportal` to start a new server (nothing to change if using the docker image)
* Remove `--config-user` and `--healthcheck-user` global options
Changes:
* Fix connection failure when sending too many environment variables (fix [#22](https://github.com/moul/sshportal/issues/22))
* Fix panic when entering empty command (fix [#13](https://github.com/moul/sshportal/issues/13))
* Add `config backup --ignore-events` option
* Add `sshportal healthcheck [--addr=] [--wait] [--quiet]` cli command
* Add [Docker Healthcheck](https://docs.docker.com/engine/reference/builder/#healthcheck) helper
* Support Putty (fix [#24](https://github.com/moul/sshportal/issues/24))
## v1.6.0 (2017-12-12)
* Add `--latest` and `--quiet` options to `ls` commands
* Add `healthcheck` user
* Add `key show KEY` command
## v1.5.0 (2017-12-02)
* Create Session objects on each connections (history)
* Connection history
* Audit log
* Add dynamic strict host key checking (learning on the first time, strict on the next ones)
* Add-back MySQL support (experimental)
* Fix some backup/restore bugs
## v1.4.0 (2017-11-24)
* Add 'key setup' command (easy SSH key installation)
* Add Updated and Created fields in 'ls' commands
* Add `--aes-key` option to encrypt sensitive data
## v1.3.0 (2017-11-23)
* More details in 'ls' commands
* Add 'host update' command (fix [#2](https://github.com/moul/sshportal/issues/2))
* Add 'user update' command (fix [#3](https://github.com/moul/sshportal/issues/3))
* Add 'acl update' command (fix [#4](https://github.com/moul/sshportal/issues/4))
* Allow connecting to the shell mode with the registered username or email (fix [#5](https://github.com/moul/sshportal/issues/5))
* Add 'listhosts' role (fix [#5](https://github.com/moul/sshportal/issues/5))
## v1.2.0 (2017-11-22)
* Support adding multiple `--group` links on `host create` and `user create`
* Use govalidator to perform more consistent input validation
* Use a database migration system
## v1.1.0 (2017-11-15)
* Improve versionning (static VERSION + dynamic GIT_* info)
* Configuration management (backup + restore)
* Implement Exit (fix [#6](https://github.com/moul/sshportal/pull/6))
* Disable mysql support (not fully working right now)
* Set random seed properly
## v1.0.0 (2017-11-14)
Initial version
* Host management
* User management
* User Group management
* Host Group management
* Host Key management
* User Key management
* ACL management
* Connect to host using key or password
* Admin commands can be run directly or in an interactive shell
Here: https://github.com/moul/sshportal/releases

View File

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

View File

@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2017 Manfred Touron <m@42.am>
Copyright 2017-2021 Manfred Touron <m@42.am>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

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

View File

@@ -3,12 +3,14 @@
[![CircleCI](https://circleci.com/gh/moul/sshportal.svg?style=svg)](https://circleci.com/gh/moul/sshportal)
[![Go Report Card](https://goreportcard.com/badge/moul.io/sshportal)](https://goreportcard.com/report/moul.io/sshportal)
[![GoDoc](https://godoc.org/moul.io/sshportal?status.svg)](https://godoc.org/moul.io/sshportal)
[![License](https://img.shields.io/github/license/moul/sshportal.svg)](https://github.com/moul/sshportal/blob/master/LICENSE)
[![Financial Contributors on Open Collective](https://opencollective.com/sshportal/all/badge.svg?label=financial+contributors)](https://opencollective.com/sshportal) [![License](https://img.shields.io/github/license/moul/sshportal.svg)](https://github.com/moul/sshportal/blob/master/LICENSE)
[![GitHub release](https://img.shields.io/github/release/moul/sshportal.svg)](https://github.com/moul/sshportal/releases)
<!-- temporarily broken? [![Docker Build Status](https://img.shields.io/docker/build/moul/sshportal.svg)](https://hub.docker.com/r/moul/sshportal/) -->
Jump host/Jump server without the jump, a.k.a Transparent SSH bastion
<img src="https://raw.githubusercontent.com/moul/sshportal/master/.assets/bastion.jpg" width="50%">
Features include: independence of users and hosts, convenient user invite system, connecting to servers that don't support SSH keys, various levels of access, and many more. Easy to install, run and configure.
![Flow Diagram](https://raw.githubusercontent.com/moul/sshportal/master/.assets/flow-diagram.png)
@@ -58,6 +60,8 @@ Shared connection to localhost closed.
$
```
If the association fails and you are promted for a password, verify that the host you're connecting from has a SSH key set up or generate one with ```ssh-keygen -t rsa```
Drop an interactive administrator shell
```console
@@ -158,7 +162,7 @@ If you need to invite multiple people to an event (hackathon, course, etc), the
* Sensitive data encryption
* Session management (see active connections, history, stats, stop)
* Audit log (logging every user action)
* Record TTY Session
* Record TTY Session (with [ttyrec](https://en.wikipedia.org/wiki/Ttyrec) format, use `ttyplay` for replay)
* Tunnels logging
* Host Keys verifications shared across users
* Healthcheck user (replying OK to any user)
@@ -178,6 +182,8 @@ If you need to invite multiple people to an event (hackathon, course, etc), the
**(Known) limitations**
* Does not work (yet?) with [`mosh`](https://mosh.org/)
* It is not possible for a user to access a host with the same name as the user. This is easily circumvented by changing the user name, especially since the most common use cases does not expose it.
* It is not possible access a host named `healthcheck` as this is a built in command.
---
@@ -232,7 +238,7 @@ docker logs -f sshportal
Get the latest version using GO.
```sh
go get -u moul.io/sshportal
GO111MODULE=on go get -u moul.io/sshportal
```
---
@@ -267,7 +273,7 @@ cp sshportal.db sshportal.db.bkp
`sshportal` embeds a configuration CLI.
By default, the configuration user is `admin`, (can be changed using `--config-user=<value>` when starting the server.
By default, the configuration user is `admin`, (can be changed using `--config-user=<value>` when starting the server. The shell is also accessible through `ssh [username]@portal.example.org`.
Each commands can be run directly by using this syntax: `ssh admin@portal.example.org <command> [args]`:
@@ -322,11 +328,11 @@ event inspect [-h] EVENT...
# host management
host help
host create [-h] [--name=<value>] [--password=<value>] [--comment=<value>] [--key=KEY] [--group=HOSTGROUP...] [--hop=HOST] <username>[:<password>]@<host>[:<port>]
host create [-h] [--name=<value>] [--password=<value>] [--comment=<value>] [--key=KEY] [--group=HOSTGROUP...] [--hop=HOST] [--logging=MODE] <username>[:<password>]@<host>[:<port>]
host inspect [-h] [--decrypt] HOST...
host ls [-h] [--latest] [--quiet]
host rm [-h] HOST...
host update [-h] [--name=<value>] [--comment=<value>] [--key=KEY] [--assign-group=HOSTGROUP...] [--unassign-group=HOSTGROUP...] [--set-hop=HOST] [--unset-hop] HOST...
host update [-h] [--name=<value>] [--comment=<value>] [--key=KEY] [--assign-group=HOSTGROUP...] [--unassign-group=HOSTGROUP...] [--logging-MODE] [--set-hop=HOST] [--unset-hop] HOST...
# hostgroup management
hostgroup help
@@ -360,7 +366,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...
@@ -465,3 +471,37 @@ See [examples/mysql](http://github.com/moul/sshportal/tree/master/examples/mysql
* https://github.com/urfave/cli: CLI flag parsing with subcommands support
![sshportal data model](https://raw.github.com/moul/sshportal/master/.assets/sql-schema.png)
## Contributors
### Code Contributors
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
<a href="https://github.com/moul/sshportal/graphs/contributors"><img src="https://opencollective.com/sshportal/contributors.svg?width=890&button=false" /></a>
### Financial Contributors
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/sshportal/contribute)]
#### Individuals
<a href="https://opencollective.com/sshportal"><img src="https://opencollective.com/sshportal/individuals.svg?width=890"></a>
#### Organizations
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/sshportal/contribute)]
<a href="https://opencollective.com/sshportal/organization/0/website"><img src="https://opencollective.com/sshportal/organization/0/avatar.svg"></a>
<a href="https://opencollective.com/sshportal/organization/1/website"><img src="https://opencollective.com/sshportal/organization/1/avatar.svg"></a>
<a href="https://opencollective.com/sshportal/organization/2/website"><img src="https://opencollective.com/sshportal/organization/2/avatar.svg"></a>
<a href="https://opencollective.com/sshportal/organization/3/website"><img src="https://opencollective.com/sshportal/organization/3/avatar.svg"></a>
<a href="https://opencollective.com/sshportal/organization/4/website"><img src="https://opencollective.com/sshportal/organization/4/avatar.svg"></a>
<a href="https://opencollective.com/sshportal/organization/5/website"><img src="https://opencollective.com/sshportal/organization/5/avatar.svg"></a>
<a href="https://opencollective.com/sshportal/organization/6/website"><img src="https://opencollective.com/sshportal/organization/6/avatar.svg"></a>
<a href="https://opencollective.com/sshportal/organization/7/website"><img src="https://opencollective.com/sshportal/organization/7/avatar.svg"></a>
<a href="https://opencollective.com/sshportal/organization/8/website"><img src="https://opencollective.com/sshportal/organization/8/avatar.svg"></a>
<a href="https://opencollective.com/sshportal/organization/9/website"><img src="https://opencollective.com/sshportal/organization/9/avatar.svg"></a>
### Stargazers over time
[![Stargazers over time](https://starchart.cc/moul/sshportal.svg)](https://starchart.cc/moul/sshportal)

1
_config.yml Normal file
View File

@@ -0,0 +1 @@
theme: jekyll-theme-slate

128
depaware.txt Normal file
View File

@@ -0,0 +1,128 @@
moul.io/sshportal dependencies: (generated by github.com/tailscale/depaware)
github.com/anmitsu/go-shlex from github.com/gliderlabs/ssh+
github.com/asaskevich/govalidator from moul.io/sshportal/pkg/bastion+
github.com/cpuguy83/go-md2man/v2/md2man from github.com/urfave/cli
LD 💣 github.com/creack/pty from github.com/kr/pty
github.com/docker/docker/pkg/namesgenerator from moul.io/sshportal/pkg/bastion
github.com/docker/docker/pkg/random from github.com/docker/docker/pkg/namesgenerator
github.com/dustin/go-humanize from moul.io/sshportal/pkg/bastion
github.com/gliderlabs/ssh from moul.io/sshportal+
github.com/go-sql-driver/mysql from github.com/jinzhu/gorm/dialects/mysql+
github.com/jinzhu/gorm from gopkg.in/gormigrate.v1+
github.com/jinzhu/gorm/dialects/mysql from moul.io/sshportal
github.com/jinzhu/gorm/dialects/postgres from moul.io/sshportal
github.com/jinzhu/gorm/dialects/sqlite from moul.io/sshportal
github.com/jinzhu/inflection from github.com/jinzhu/gorm
LD github.com/kr/pty from moul.io/sshportal
github.com/lib/pq from github.com/jinzhu/gorm/dialects/postgres
github.com/lib/pq/hstore from github.com/jinzhu/gorm/dialects/postgres
github.com/lib/pq/oid from github.com/lib/pq
github.com/lib/pq/scram from github.com/lib/pq
💣 github.com/mattn/go-colorable from github.com/mgutz/ansi
💣 github.com/mattn/go-isatty from github.com/mattn/go-colorable
github.com/mattn/go-runewidth from github.com/olekukonko/tablewriter
💣 github.com/mattn/go-sqlite3 from github.com/jinzhu/gorm/dialects/sqlite
github.com/mgutz/ansi from moul.io/sshportal/pkg/bastion
github.com/olekukonko/tablewriter from moul.io/sshportal/pkg/bastion
github.com/pkg/errors from moul.io/sshportal/pkg/bastion
github.com/reiver/go-oi from github.com/reiver/go-telnet+
github.com/reiver/go-telnet from moul.io/sshportal/pkg/bastion
github.com/russross/blackfriday/v2 from github.com/cpuguy83/go-md2man/v2/md2man
github.com/sabban/bastion/pkg/logchannel from moul.io/sshportal/pkg/bastion
github.com/shurcooL/sanitized_anchor_name from github.com/russross/blackfriday/v2
github.com/urfave/cli from moul.io/sshportal+
gopkg.in/gormigrate.v1 from moul.io/sshportal/pkg/bastion
moul.io/srand from moul.io/sshportal
moul.io/sshportal/pkg/bastion from moul.io/sshportal
moul.io/sshportal/pkg/crypto from moul.io/sshportal/pkg/bastion
moul.io/sshportal/pkg/dbmodels from moul.io/sshportal/pkg/bastion+
golang.org/x/crypto/blowfish from golang.org/x/crypto/ssh/internal/bcrypt_pbkdf
golang.org/x/crypto/chacha20 from golang.org/x/crypto/chacha20poly1305+
golang.org/x/crypto/chacha20poly1305 from crypto/tls
golang.org/x/crypto/cryptobyte from crypto/ecdsa+
golang.org/x/crypto/cryptobyte/asn1 from crypto/ecdsa+
golang.org/x/crypto/curve25519 from crypto/tls+
golang.org/x/crypto/ed25519 from golang.org/x/crypto/ssh
golang.org/x/crypto/hkdf from crypto/tls
golang.org/x/crypto/poly1305 from golang.org/x/crypto/chacha20poly1305+
golang.org/x/crypto/ssh from github.com/gliderlabs/ssh+
golang.org/x/crypto/ssh/terminal from moul.io/sshportal/pkg/bastion
golang.org/x/net/dns/dnsmessage from net
D golang.org/x/net/route from net
golang.org/x/sys/cpu from golang.org/x/crypto/chacha20poly1305
LD golang.org/x/sys/unix from github.com/mattn/go-isatty+
W golang.org/x/sys/windows from golang.org/x/crypto/ssh/terminal
bufio from crypto/rand+
bytes from bufio+
container/list from crypto/tls
context from crypto/tls+
crypto from crypto/ecdsa+
crypto/aes from crypto/ecdsa+
crypto/cipher from crypto/aes+
crypto/des from crypto/tls+
crypto/dsa from crypto/x509+
crypto/ecdsa from crypto/tls+
crypto/ed25519 from crypto/tls+
crypto/elliptic from crypto/ecdsa+
crypto/hmac from crypto/tls+
crypto/md5 from crypto/tls+
crypto/rand from crypto/ed25519+
crypto/rc4 from crypto/tls+
crypto/rsa from crypto/tls+
crypto/sha1 from crypto/tls+
crypto/sha256 from crypto/tls+
crypto/sha512 from crypto/ecdsa+
crypto/subtle from crypto/aes+
crypto/tls from github.com/go-sql-driver/mysql+
crypto/x509 from crypto/tls+
crypto/x509/pkix from crypto/x509
database/sql from github.com/go-sql-driver/mysql+
database/sql/driver from database/sql+
encoding from encoding/json
encoding/asn1 from crypto/x509+
encoding/base64 from encoding/json+
encoding/binary from crypto/aes+
encoding/csv from github.com/olekukonko/tablewriter
encoding/hex from crypto/x509+
encoding/json from github.com/asaskevich/govalidator+
encoding/pem from crypto/tls+
errors from bufio+
flag from github.com/urfave/cli
fmt from crypto/tls+
go/ast from github.com/jinzhu/gorm
go/scanner from go/ast
go/token from go/ast+
hash from crypto+
html from github.com/asaskevich/govalidator+
io from bufio+
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+
math/bits from crypto/md5+
math/rand from github.com/docker/docker/pkg/random+
net from crypto/tls+
net/url from crypto/x509+
os from crypto/rand+
LD os/exec from github.com/creack/pty+
os/user from github.com/lib/pq+
path from github.com/asaskevich/govalidator+
path/filepath from crypto/x509+
reflect from crypto/x509+
regexp from github.com/asaskevich/govalidator+
regexp/syntax from regexp
sort from database/sql+
strconv from crypto+
strings from bufio+
sync from context+
sync/atomic from context+
syscall from crypto/rand+
text/tabwriter from github.com/urfave/cli
text/template from github.com/urfave/cli
text/template/parse from text/template
time from context+
unicode from bytes+
unicode/utf16 from encoding/asn1+
unicode/utf8 from bufio+

View File

@@ -28,7 +28,7 @@ ssh sshportal -l invite:integration
ssh sshportal -l admin hostgroup create
ssh sshportal -l admin hostgroup create --name=hg1
ssh sshportal -l admin hostgroup create --name=hg2 --comment=test
ssh sshportal -l admin usergroup inspect hg1 hg2
ssh sshportal -l admin hostgroup inspect hg1 hg2
ssh sshportal -l admin hostgroup ls
ssh sshportal -l admin usergroup create

52
go.mod generated
View File

@@ -1,30 +1,36 @@
module moul.io/sshportal
require (
cloud.google.com/go v0.40.0 // indirect
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239
github.com/asaskevich/govalidator v0.0.0-20190528192650-f61b66f89f4a
github.com/denisenkom/go-mssqldb v0.0.0-20190528192650-eb9f6a1743f3 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
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/docker/docker v20.10.7+incompatible
github.com/dustin/go-humanize v1.0.0
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
github.com/gliderlabs/ssh v0.2.2
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
github.com/jinzhu/gorm v1.9.2
github.com/kr/pty v1.1.3
github.com/mattn/go-colorable v0.0.9 // indirect
github.com/mattn/go-isatty v0.0.4 // indirect
github.com/mattn/go-runewidth v0.0.4 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
github.com/moby/moby v0.7.3-0.20190103212154-2b7e084dc98b
github.com/olekukonko/tablewriter v0.0.1
github.com/reiver/go-oi v0.0.0-20160325061615-431c83978379
github.com/gliderlabs/ssh v0.3.2
github.com/go-sql-driver/mysql v1.6.0
github.com/jinzhu/gorm v1.9.16
github.com/kr/pty v1.1.8
github.com/lib/pq v1.10.1 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-runewidth v0.0.12 // indirect
github.com/mattn/go-sqlite3 v1.14.7 // 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-20190528192650-f487f9de1cd3 // indirect
github.com/smartystreets/goconvey v0.0.0-20190528192650-68dc04aab96a
github.com/urfave/cli v1.20.0
golang.org/x/crypto v0.0.0-20190614082836-5c40567a22f8
golang.org/x/sys v0.0.0-20190614082836-5ed2794edfdc // indirect
google.golang.org/appengine v1.6.1 // indirect
gopkg.in/gormigrate.v1 v1.5.0
github.com/smartystreets/goconvey v1.6.4
github.com/tailscale/depaware v0.0.0-20201214215404-77d1e9757027
github.com/urfave/cli v1.22.5
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a
golang.org/x/term v0.0.0-20210422114643-f5beecf764ed // indirect
golang.org/x/tools v0.1.3
gopkg.in/gormigrate.v1 v1.6.0
moul.io/srand v1.6.1
)
go 1.14

301
go.sum generated
View File

@@ -1,227 +1,166 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.33.1/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.40.0 h1:FjSY7bOj+WzJe6TZRVtXI2b9kAYvtNg4lMbcH2+MUkk=
cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlro=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/asaskevich/govalidator v0.0.0-20190528192650-f61b66f89f4a h1:yIrJuC1qNWAdlArS1a9jucA5nYzFRI6dehM7vpjL3kU=
github.com/asaskevich/govalidator v0.0.0-20190528192650-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
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-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/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
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-20190528192650-eb9f6a1743f3 h1:QJLPPsGf20+lPdQw+EBUc3xVzqBwNDsPBcnr5pCW7s8=
github.com/denisenkom/go-mssqldb v0.0.0-20190528192650-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
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 v20.10.7+incompatible h1:Z6O9Nhsjv+ayUEeI1IojKbYcsGdgYSNqxe1s2MYzUhQ=
github.com/docker/docker v20.10.7+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/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
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/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
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/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
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/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jinzhu/gorm v1.9.2 h1:lCvgEaqe/HVE+tjAR2mt4HbbHAZsQOv3XAZiEZV37iw=
github.com/jinzhu/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k=
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/jinzhu/now v0.0.0-20181116074157-8ec929ed50c3 h1:xvj06l8iSwiWpYgm8MbPp+naBg+pwfqmdXabzqPCn/8=
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/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
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/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pty v1.1.3 h1:/Um6a/ZmD5tF7peoOJ5oN5KMQ0DrGVQSXLNwyckutPk=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
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/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.1 h1:6VXZrLU0jHBYyAqrSPa+MgPfnSvTPuMgK+k0o5kVFWo=
github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
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.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
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.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/moby/moby v0.7.3-0.20190103212154-2b7e084dc98b h1:z/nBoaNNRdlg5oyabuQyZshr/YsANYEcjI+IiNT/U6U=
github.com/moby/moby v0.7.3-0.20190103212154-2b7e084dc98b/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA=
github.com/mattn/go-sqlite3 v1.14.7/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.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=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/reiver/go-oi v0.0.0-20160325061615-431c83978379 h1:NBPkf14RzPYmr3478XQcmQyMKkxSvguL7+cyKKNvGxY=
github.com/reiver/go-oi v0.0.0-20160325061615-431c83978379/go.mod h1:RrDBct90BAhoDTxB1fenZwfykqeGvhI6LsNfStJoEkI=
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/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/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/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
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/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v0.0.0-20190528192650-f487f9de1cd3 h1:ObF780pbR2OP5tS/Hd8+yG2dQWxLzWe6FGRT4eZPA0M=
github.com/smartystreets/assertions v0.0.0-20190528192650-f487f9de1cd3/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190528192650-68dc04aab96a h1:Lrry3qb+29a2vBtpCYZMRTd6FUsZi6fkeYWVsdlkYLQ=
github.com/smartystreets/goconvey v0.0.0-20190528192650-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/tailscale/depaware v0.0.0-20201214215404-77d1e9757027 h1:lK99QQdH3yBWY6aGilF+IRlQIdmhzLrsEmF6JgN+Ryw=
github.com/tailscale/depaware v0.0.0-20201214215404-77d1e9757027/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=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190614082836-5c40567a22f8 h1:6HZxWy7rHlyxUWwYzid4tPCdV05RK9A1RD7eDTsaSsY=
golang.org/x/crypto v0.0.0-20190614082836-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
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.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
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-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/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-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190614082836-5ed2794edfdc h1:Tn4hxhC8ZV8oYgzx27sFCL4tC1bbazLSzRqw9plYpYQ=
golang.org/x/sys v0.0.0-20190614082836-5ed2794edfdc/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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.3 h1:L69ShwSZEyCsLKoAxDKeMvLDZkumEe8gXUZAjab0tX8=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
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-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gormigrate.v1 v1.5.0 h1:M667uzFRcnBf5cNAcSyYyNdJTJ1KQnUTmc+mjCLfPfw=
gopkg.in/gormigrate.v1 v1.5.0/go.mod h1:Lf00lQrHqfSYWiTtPcyQabsDdM6ejZaMgV0OU6JMSlw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
moul.io/srand v1.6.1 h1:SJ335F+54ivLdlH7wH52Rtyv0Ffos6DpsF5wu3ZVMXU=
moul.io/srand v1.6.1/go.mod h1:P2uaZB+GFstFNo8sEj6/U8FRV1n25kD0LLckFpJ+qvc=

View File

@@ -0,0 +1,22 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

21
helm/sshportal/Chart.yaml Normal file
View File

@@ -0,0 +1,21 @@
apiVersion: v2
name: sshportal
description: A Helm chart for SSHPortal on Kubernetes
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
version: 0.1.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application.
appVersion: 1.10.0

View File

@@ -0,0 +1,33 @@
1. Get the admin invitation token (only on first install):
export INVITE=$(kubectl --namespace sshportal logs -l "app.kubernetes.io/name={{ include "sshportal.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" | grep -Eo "invite:[a-zA-Z0-9]+")
2. Get the service IP and Port:
{{- if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "sshportal.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "sshportal.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "sshportal.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "sshportal.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 2222:{{ .Values.service.port }}
{{- end }}
3. Enroll your SSH public key:
{{- if contains "NodePort" .Values.service.type }}
ssh $NODE_IP -p $NODE_PORT -l $INVITE
{{- else if contains "LoadBalancer" .Values.service.type }}
ssh $SERVICE_IP -p {{ .Values.service.port }} -l $INVITE
{{- else if contains "ClusterIP" .Values.service.type }}
ssh localhost -p 2222 -l $INVITE
{{- end }}
4. Configure your {{ include "sshportal.name" . }} install:
{{- if contains "NodePort" .Values.service.type }}
ssh admin@$NODE_IP -p $NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
ssh admin@$SERVICE_IP -p {{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
ssh admin@localhost -p 2222
{{- end }}

View File

@@ -0,0 +1,63 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "sshportal.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "sshportal.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "sshportal.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Common labels
*/}}
{{- define "sshportal.labels" -}}
helm.sh/chart: {{ include "sshportal.chart" . }}
{{ include "sshportal.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
{{/*
Selector labels
*/}}
{{- define "sshportal.selectorLabels" -}}
app.kubernetes.io/name: {{ include "sshportal.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}
{{/*
Create the name of the service account to use
*/}}
{{- define "sshportal.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
{{ default (include "sshportal.fullname" .) .Values.serviceAccount.name }}
{{- else -}}
{{ default "default" .Values.serviceAccount.name }}
{{- end -}}
{{- end -}}

View File

@@ -0,0 +1,69 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "sshportal.fullname" . }}
labels:
{{- include "sshportal.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "sshportal.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "sshportal.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:v{{ .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: ssh
containerPort: 2222
protocol: TCP
livenessProbe:
exec:
command:
- sshportal
- healthcheck
- --quiet
readinessProbe:
exec:
command:
- sshportal
- healthcheck
- --quiet
resources:
{{- toYaml .Values.resources | nindent 12 }}
env:
{{- if .Values.mysql.enabled }}
- name: SSHPORTAL_DATABASE_URL
value: {{ .Values.mysql.user }}:{{ .Values.mysql.password }}@tcp({{ .Values.mysql.server }}:{{ .Values.mysql.port }})/{{ .Values.mysql.database }}?charset=utf8&parseTime=true&loc=Local
- name: SSHPORTAL_DB_DRIVER
value: mysql
{{- end }}
{{- if .Values.debug}}
- name: SSHPORTAL_DEBUG
value: "1"
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -0,0 +1,21 @@
{{- if .Values.mysql.enabled }}
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "sshportal.fullname" . }}
labels:
{{- include "sshportal.labels" . | nindent 4 }}
spec:
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "sshportal.fullname" . }}
metrics:
- type: Resource
resource:
name: cpu
targetAverageUtilization: {{ .Values.autoscaling.cpuTarget }}
{{- end }}

View File

@@ -0,0 +1,17 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "sshportal.fullname" . }}
annotations:
{{- toYaml .Values.service.annotations | nindent 4 }}
labels:
{{- include "sshportal.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: 2222
protocol: TCP
name: ssh
selector:
{{- include "sshportal.selectorLabels" . | nindent 4 }}

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "sshportal.fullname" . }}-test-connection"
labels:
{{ include "sshportal.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test-success
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "sshportal.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never

119
helm/sshportal/values.yaml Normal file
View File

@@ -0,0 +1,119 @@
# Default values for sshportal.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
## Enable SSHPortal debug mode
##
debug: false
## SSH Portal Docker image
##
image:
repository: moul/sshportal
pullPolicy: IfNotPresent
## Reference to one or more secrets to be used when pulling images
## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
##
imagePullSecrets: []
## Provide a name in place of sshportal for `app:` labels
##
nameOverride: ""
## Provide a name to substitute for the full names of resources
##
fullnameOverride: ""
## PodSecurityContext holds pod-level security attributes.
## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/
##
podSecurityContext: {}
# fsGroup: 2000
## SecurityContext holds container-level security attributes.
## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/
##
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
## Service
##
service:
## Configure additional annotations for SSHPortal service
##
annotations: {}
# service.beta.kubernetes.io/openstack-internal-load-balancer: "true"
## Service type, one of
## NodePort, ClusterIP, LoadBalancer
##
type: LoadBalancer
## Port to expose on the service
##
port: 22
## Define resources requests and limits
## ref: https://kubernetes.io/docs/user-guide/compute-resources/
##
resources: {}
# requests:
# cpu: 100m
# memory: 128Mi
# limits:
# cpu: 2
# memory: 2Gi
## Mysql/MariaDB configuration for HA
##
mysql:
enabled: false
## Database user
##
user: sshportal
## Database password
##
password: change_me
## Database name
##
database: sshportal
## Database server FQDN or IP
##
server: mariadb-mariadb-galera
## Database port
##
port: 3306
## Define which Nodes the Pods are scheduled on.
## ref: https://kubernetes.io/docs/user-guide/node-selection/
##
nodeSelector: {}
## The pod's tolerations.
## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
##
tolerations: []
## Assign custom affinity rules
## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
##
affinity: {}
## HPA support, require `mysql.enable: true`
## This section enables sshportal to autoscale based on metrics.
##
autoscaling:
maxReplicas: 4
minReplicas: 2
cpuTarget: 60

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"
)

21
main.go
View File

@@ -5,31 +5,29 @@ import (
"math/rand"
"os"
"path"
"time"
_ "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"
)
var (
// Version should be updated by hand at each release
Version = "1.10.0"
// GitTag will be overwritten automatically by the build system
GitTag string
GitTag = "n/a"
// GitSha will be overwritten automatically by the build system
GitSha string
// GitBranch will be overwritten automatically by the build system
GitBranch string
GitSha = "n/a"
)
func main() {
rand.Seed(time.Now().UnixNano())
rand.Seed(srand.MustSecure())
app := cli.NewApp()
app.Name = path.Base(os.Args[0])
app.Author = "Manfred Touron"
app.Version = Version + " (" + GitSha + ")"
app.Version = GitTag + " (" + GitSha + ")"
app.Email = "https://moul.io/sshportal"
app.Commands = []cli.Command{
{
@@ -85,6 +83,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,18 +1,30 @@
package bastion // import "moul.io/sshportal/pkg/bastion"
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, error) {
func checkACLs(user dbmodels.User, host dbmodels.Host, aclCheckCmd string) string {
currentTime := time.Now()
// shared ACLs between user and host
aclMap := map[uint]*dbmodels.ACL{}
for _, userGroup := range user.Groups {
@@ -20,7 +32,10 @@ func checkACLs(user dbmodels.User, host dbmodels.Host) (string, error) {
for _, hostGroup := range host.Groups {
for _, hostGroupACL := range hostGroup.ACLs {
if userGroupACL.ID == hostGroupACL.ID {
aclMap[userGroupACL.ID] = userGroupACL
if (userGroupACL.Inception == nil || currentTime.After(*userGroupACL.Inception)) &&
(userGroupACL.Expiration == nil || currentTime.Before(*userGroupACL.Expiration)) {
aclMap[userGroupACL.ID] = userGroupACL
}
}
}
}
@@ -28,9 +43,13 @@ func checkACLs(user dbmodels.User, host dbmodels.Host) (string, error) {
}
// 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), nil // 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
@@ -40,5 +59,62 @@ func checkACLs(user dbmodels.User, host dbmodels.Host) (string, error) {
}
sort.Sort(byWeight(acls))
return acls[0].Action, nil
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

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

View File

@@ -40,14 +40,13 @@ func DBInit(db *gorm.DB) error {
ID: "2",
Migrate: func(tx *gorm.DB) error {
type SSHKey struct {
// FIXME: use uuid for ID
gorm.Model
Name string
Type string
Length uint
Fingerprint string
PrivKey string `sql:"size:10000"`
PubKey string `sql:"size:10000"`
PrivKey string `sql:"size:5000"`
PubKey string `sql:"size:1000"`
Hosts []*dbmodels.Host `gorm:"ForeignKey:SSHKeyID"`
Comment string
}
@@ -60,7 +59,6 @@ func DBInit(db *gorm.DB) error {
ID: "3",
Migrate: func(tx *gorm.DB) error {
type Host struct {
// FIXME: use uuid for ID
gorm.Model
Name string `gorm:"size:32"`
Addr string
@@ -82,7 +80,7 @@ func DBInit(db *gorm.DB) error {
Migrate: func(tx *gorm.DB) error {
type UserKey struct {
gorm.Model
Key []byte `sql:"size:10000"`
Key []byte `sql:"size:1000"`
UserID uint ``
User *dbmodels.User `gorm:"ForeignKey:UserID"`
Comment string
@@ -96,7 +94,6 @@ func DBInit(db *gorm.DB) error {
ID: "5",
Migrate: func(tx *gorm.DB) error {
type User struct {
// FIXME: use uuid for ID
gorm.Model
IsAdmin bool
Email string
@@ -261,14 +258,14 @@ func DBInit(db *gorm.DB) error {
return err
}
var users []dbmodels.User
var users []*dbmodels.User
if err := db.Preload("Roles").Where("is_admin = ?", true).Find(&users).Error; err != nil {
return err
}
for _, user := range users {
user.Roles = append(user.Roles, &adminRole)
if err := tx.Save(&user).Error; err != nil {
if err := tx.Save(user).Error; err != nil {
return err
}
}
@@ -344,8 +341,8 @@ func DBInit(db *gorm.DB) error {
Migrate: func(tx *gorm.DB) error {
type UserKey struct {
gorm.Model
Key []byte `sql:"size:10000" valid:"required,length(1|10000)"`
AuthorizedKey string `sql:"size:10000" valid:"required,length(1|10000)"`
Key []byte `sql:"size:1000" valid:"required,length(1|1000)"`
AuthorizedKey string `sql:"size:1000" valid:"required,length(1|1000)"`
UserID uint ``
User *dbmodels.User `gorm:"ForeignKey:UserID"`
Comment string `valid:"optional"`
@@ -358,7 +355,7 @@ func DBInit(db *gorm.DB) error {
}, {
ID: "24",
Migrate: func(tx *gorm.DB) error {
var userKeys []dbmodels.UserKey
var userKeys []*dbmodels.UserKey
if err := db.Find(&userKeys).Error; err != nil {
return err
}
@@ -369,7 +366,7 @@ func DBInit(db *gorm.DB) error {
return err
}
userKey.AuthorizedKey = string(gossh.MarshalAuthorizedKey(key))
if err := db.Model(&userKey).Updates(&userKey).Error; err != nil {
if err := db.Model(userKey).Updates(userKey).Error; err != nil {
return err
}
}
@@ -382,17 +379,16 @@ func DBInit(db *gorm.DB) error {
ID: "25",
Migrate: func(tx *gorm.DB) error {
type Host struct {
// FIXME: use uuid for ID
gorm.Model
Name string `gorm:"size:32" valid:"required,length(1|32),unix_user"`
Addr string `valid:"required"`
User string `valid:"optional"`
Password string `valid:"optional"`
SSHKey *dbmodels.SSHKey `gorm:"ForeignKey:SSHKeyID"` // SSHKey used to connect by the client
SSHKey *dbmodels.SSHKey `gorm:"ForeignKey:SSHKeyID"`
SSHKeyID uint `gorm:"index"`
HostKey []byte `sql:"size:10000" valid:"optional"`
HostKey []byte `sql:"size:1000" valid:"optional"`
Groups []*dbmodels.HostGroup `gorm:"many2many:host_host_groups;"`
Fingerprint string `valid:"optional"` // FIXME: replace with hostKey ?
Fingerprint string `valid:"optional"`
Comment string `valid:"optional"`
}
return tx.AutoMigrate(&Host{}).Error
@@ -422,14 +418,14 @@ func DBInit(db *gorm.DB) error {
}, {
ID: "27",
Migrate: func(tx *gorm.DB) error {
var sessions []dbmodels.Session
var sessions []*dbmodels.Session
if err := db.Find(&sessions).Error; err != nil {
return err
}
for _, session := range sessions {
if session.StoppedAt != nil && session.StoppedAt.IsZero() {
if err := db.Model(&session).Updates(map[string]interface{}{"stopped_at": nil}).Error; err != nil {
if err := db.Model(session).Updates(map[string]interface{}{"stopped_at": nil}).Error; err != nil {
return err
}
}
@@ -443,7 +439,6 @@ func DBInit(db *gorm.DB) error {
ID: "28",
Migrate: func(tx *gorm.DB) error {
type Host struct {
// FIXME: use uuid for ID
gorm.Model
Name string `gorm:"size:32"`
Addr string
@@ -452,7 +447,7 @@ func DBInit(db *gorm.DB) error {
URL string
SSHKey *dbmodels.SSHKey `gorm:"ForeignKey:SSHKeyID"`
SSHKeyID uint `gorm:"index"`
HostKey []byte `sql:"size:10000"`
HostKey []byte `sql:"size:1000"`
Groups []*dbmodels.HostGroup `gorm:"many2many:host_host_groups;"`
Comment string
}
@@ -465,7 +460,29 @@ func DBInit(db *gorm.DB) error {
ID: "29",
Migrate: func(tx *gorm.DB) error {
type Host struct {
// FIXME: use uuid for ID
gorm.Model
Name string `gorm:"size:32"`
Addr string
User string
Password string
URL string
SSHKey *dbmodels.SSHKey `gorm:"ForeignKey:SSHKeyID"`
SSHKeyID uint `gorm:"index"`
HostKey []byte `sql:"size:1000"`
Groups []*dbmodels.HostGroup `gorm:"many2many:host_host_groups;"`
Comment string
Hop *dbmodels.Host
HopID uint
}
return tx.AutoMigrate(&Host{}).Error
},
Rollback: func(tx *gorm.DB) error {
return fmt.Errorf("not implemented")
},
}, {
ID: "30",
Migrate: func(tx *gorm.DB) error {
type Host struct {
gorm.Model
Name string `gorm:"size:32"`
Addr string
@@ -478,13 +495,35 @@ func DBInit(db *gorm.DB) error {
Groups []*dbmodels.HostGroup `gorm:"many2many:host_host_groups;"`
Comment string
Hop *dbmodels.Host
Logging string
HopID uint
}
return tx.AutoMigrate(&Host{}).Error
},
Rollback: func(tx *gorm.DB) error {
return fmt.Errorf("not implemented")
Rollback: func(tx *gorm.DB) error { return fmt.Errorf("not implemented") },
}, {
ID: "31",
Migrate: func(tx *gorm.DB) error {
return tx.Model(&dbmodels.Host{}).Updates(&dbmodels.Host{Logging: "everything"}).Error
},
Rollback: func(tx *gorm.DB) error { return fmt.Errorf("not implemented") },
}, {
ID: "32",
Migrate: func(tx *gorm.DB) error {
type ACL struct {
gorm.Model
HostGroups []*dbmodels.HostGroup `gorm:"many2many:host_group_acls;"`
UserGroups []*dbmodels.UserGroup `gorm:"many2many:user_group_acls;"`
HostPattern string `valid:"optional"`
Action string `valid:"required"`
Weight uint ``
Comment string `valid:"optional"`
Inception *time.Time
Expiration *time.Time
}
return tx.AutoMigrate(&ACL{}).Error
},
Rollback: func(tx *gorm.DB) error { return fmt.Errorf("not implemented") },
},
})
if err := m.Migrate(); err != nil {
@@ -498,7 +537,7 @@ func DBInit(db *gorm.DB) error {
return err
}
if count == 0 {
key, err := crypto.NewSSHKey("rsa", 2048)
key, err := crypto.NewSSHKey("ed25519", 1)
if err != nil {
return err
}
@@ -605,7 +644,7 @@ func DBInit(db *gorm.DB) error {
return err
}
if count == 0 {
key, err := crypto.NewSSHKey("rsa", 2048)
key, err := crypto.NewSSHKey("ed25519", 1)
if err != nil {
return err
}

View File

@@ -1,26 +1,28 @@
package bastion // import "moul.io/sshportal/pkg/bastion"
import (
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"strings"
"path/filepath"
"time"
"github.com/gliderlabs/ssh"
"github.com/pkg/errors"
"github.com/sabban/bastion/pkg/logchannel"
gossh "golang.org/x/crypto/ssh"
)
type sessionConfig struct {
Addr string
Logs string
LogsLocation string
ClientConfig *gossh.ClientConfig
LoggingMode string
}
func multiChannelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx ssh.Context, configs []sessionConfig, sessionID uint) error {
func multiChannelHandler(conn *gossh.ServerConn, newChan gossh.NewChannel, ctx ssh.Context, configs []sessionConfig, sessionID uint) error {
var lastClient *gossh.Client
switch newChan.ChannelType() {
case "session":
@@ -63,7 +65,7 @@ func multiChannelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.
actx := ctx.Value(authContextKey).(*authContext)
username := actx.user.Name
// pipe everything
return pipe(lreqs, rreqs, lch, rch, configs[len(configs)-1].Logs, user, username, sessionID, newChan)
return pipe(lreqs, rreqs, lch, rch, configs[len(configs)-1], user, username, sessionID, newChan)
case "direct-tcpip":
lch, lreqs, err := newChan.Accept()
// TODO: defer clean closer
@@ -108,7 +110,7 @@ func multiChannelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.
actx := ctx.Value(authContextKey).(*authContext)
username := actx.user.Name
// pipe everything
return pipe(lreqs, rreqs, lch, rch, configs[len(configs)-1].Logs, user, username, sessionID, newChan)
return pipe(lreqs, rreqs, lch, rch, configs[len(configs)-1], user, username, sessionID, newChan)
default:
if err := newChan.Reject(gossh.UnknownChannelType, "unsupported channel type"); err != nil {
log.Printf("failed to reject chan: %v", err)
@@ -117,65 +119,77 @@ func multiChannelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.
}
}
func pipe(lreqs, rreqs <-chan *gossh.Request, lch, rch gossh.Channel, logsLocation string, user string, username string, sessionID uint, newChan gossh.NewChannel) error {
func pipe(lreqs, rreqs <-chan *gossh.Request, lch, rch gossh.Channel, sessConfig sessionConfig, user string, username string, sessionID uint, newChan gossh.NewChannel) error {
defer func() {
_ = lch.Close()
_ = rch.Close()
}()
errch := make(chan error, 1)
quit := make(chan string, 1)
channeltype := newChan.ChannelType()
filename := strings.Join([]string{logsLocation, "/", user, "-", username, "-", channeltype, "-", fmt.Sprint(sessionID), "-", time.Now().Format(time.RFC3339)}, "") // get user
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0440)
defer func() {
_ = f.Close()
}()
if err != nil {
log.Fatalf("error: %v", err)
var logWriter io.WriteCloser = newDiscardWriteCloser()
if sessConfig.LoggingMode != "disabled" {
filename := filepath.Join(sessConfig.LogsLocation, fmt.Sprintf("%s-%s-%s-%d-%s", user, username, channeltype, sessionID, time.Now().Format(time.RFC3339)))
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0440)
if err != nil {
return errors.Wrap(err, "open log file")
}
defer func() {
_ = f.Close()
}()
log.Printf("Session %v is recorded in %v", channeltype, filename)
logWriter = f
}
log.Printf("Session %v is recorded in %v", channeltype, filename)
if channeltype == "session" {
wrappedlch := logchannel.New(lch, f)
go func() {
_, _ = io.Copy(wrappedlch, rch)
errch <- errors.New("lch closed the connection")
}()
go func() {
_, _ = io.Copy(rch, lch)
errch <- errors.New("rch closed the connection")
}()
switch sessConfig.LoggingMode {
case "input":
wrappedrch := logchannel.New(rch, logWriter)
go func(quit chan string) {
_, _ = io.Copy(lch, rch)
quit <- "rch"
}(quit)
go func(quit chan string) {
_, _ = io.Copy(wrappedrch, lch)
quit <- "lch"
}(quit)
default: // everything, disabled
wrappedlch := logchannel.New(lch, logWriter)
go func(quit chan string) {
_, _ = io.Copy(wrappedlch, rch)
quit <- "rch"
}(quit)
go func(quit chan string) {
_, _ = io.Copy(rch, lch)
quit <- "lch"
}(quit)
}
}
if channeltype == "direct-tcpip" {
d := logTunnelForwardData{}
if err := gossh.Unmarshal(newChan.ExtraData(), &d); err != nil {
return err
}
wrappedlch := newLogTunnel(lch, f, d.SourceHost)
wrappedrch := newLogTunnel(rch, f, d.DestinationHost)
go func() {
wrappedlch := newLogTunnel(lch, logWriter, d.SourceHost)
wrappedrch := newLogTunnel(rch, logWriter, d.DestinationHost)
go func(quit chan string) {
_, _ = io.Copy(wrappedlch, rch)
errch <- errors.New("lch closed the connection")
}()
quit <- "rch"
}(quit)
go func() {
go func(quit chan string) {
_, _ = io.Copy(wrappedrch, lch)
errch <- errors.New("rch closed the connection")
}()
quit <- "lch"
}(quit)
}
for {
select {
case req := <-lreqs: // forward ssh requests from local to remote
if req == nil {
return nil
}
go func(quit chan string) {
for req := range lreqs {
b, err := rch.SendRequest(req.Type, req.WantReply, req.Payload)
if req.Type == "exec" {
wrappedlch := logchannel.New(lch, f)
wrappedlch := logchannel.New(lch, logWriter)
command := append(req.Payload, []byte("\n")...)
if _, err := wrappedlch.LogWrite(command); err != nil {
log.Printf("failed to write log: %v", err)
@@ -183,24 +197,68 @@ func pipe(lreqs, rreqs <-chan *gossh.Request, lch, rch gossh.Channel, logsLocati
}
if err != nil {
return err
errch <- err
}
if err2 := req.Reply(b, nil); err2 != nil {
return err2
}
case req := <-rreqs: // forward ssh requests from remote to local
if req == nil {
return nil
errch <- err2
}
}
quit <- "lreqs"
}(quit)
go func(quit chan string) {
for req := range rreqs {
b, err := lch.SendRequest(req.Type, req.WantReply, req.Payload)
if err != nil {
return err
errch <- err
}
if err2 := req.Reply(b, nil); err2 != nil {
return err2
errch <- err2
}
}
quit <- "rreqs"
}(quit)
lchEOF, rchEOF, lchClosed, rchClosed := false, false, false, false
for {
select {
case err := <-errch:
return err
case q := <-quit:
switch q {
case "lch":
lchEOF = true
_ = rch.CloseWrite()
case "rch":
rchEOF = true
_ = lch.CloseWrite()
case "lreqs":
lchClosed = true
case "rreqs":
rchClosed = true
}
if lchEOF && lchClosed && !rchClosed {
rch.Close()
}
if rchEOF && rchClosed && !lchClosed {
lch.Close()
}
if lchEOF && rchEOF && lchClosed && rchClosed {
return nil
}
}
}
}
func newDiscardWriteCloser() io.WriteCloser { return &discardWriteCloser{ioutil.Discard} }
type discardWriteCloser struct {
io.Writer
}
func (discardWriteCloser) Close() error {
return nil
}

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"net/url"
"os"
"regexp"
@@ -14,16 +15,17 @@ import (
shlex "github.com/anmitsu/go-shlex"
"github.com/asaskevich/govalidator"
"github.com/docker/docker/pkg/namesgenerator"
humanize "github.com/dustin/go-humanize"
"github.com/gliderlabs/ssh"
"github.com/mgutz/ansi"
"github.com/moby/moby/pkg/namesgenerator"
"github.com/olekukonko/tablewriter"
"github.com/urfave/cli"
gossh "golang.org/x/crypto/ssh"
"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 = `
@@ -41,7 +43,7 @@ const (
naMessage = "n/a"
)
func shell(s ssh.Session, version, gitSha, gitTag, gitBranch string) error {
func shell(s ssh.Session, version, gitSha, gitTag string) error {
var (
sshCommand = s.Command()
actx = s.Context().Value(authContextKey).(*authContext)
@@ -67,6 +69,8 @@ GLOBAL OPTIONS:
app.Writer = s
app.HideVersion = true
dbmodels.InitValidator()
var (
myself = &actx.user
db = actx.db
@@ -88,17 +92,31 @@ GLOBAL OPTIONS:
cli.StringFlag{Name: "comment", Usage: "Adds a comment"},
cli.StringFlag{Name: "action", Usage: "Assigns the ACL action (allow,deny)", Value: string(dbmodels.ACLActionAllow)},
cli.UintFlag{Name: "weight, w", Usage: "Assigns the ACL weight (priority)"},
cli.StringFlag{Name: "inception, i", Usage: "Assigns inception date-time"},
cli.StringFlag{Name: "expiration, e", Usage: "Assigns expiration date-time"},
},
Action: func(c *cli.Context) error {
if err := myself.CheckRoles([]string{"admin"}); err != nil {
return err
}
inception, err := parseOptionalTime(c.String("inception"))
if err != nil {
return err
}
expiration, err := parseOptionalTime(c.String("expiration"))
if err != nil {
return err
}
acl := dbmodels.ACL{
Comment: c.String("comment"),
HostPattern: c.String("pattern"),
UserGroups: []*dbmodels.UserGroup{},
HostGroups: []*dbmodels.HostGroup{},
Weight: c.Uint("weight"),
Inception: inception,
Expiration: expiration,
Action: c.String("action"),
}
if acl.Action != string(dbmodels.ACLActionAllow) && acl.Action != string(dbmodels.ACLActionDeny) {
@@ -173,10 +191,8 @@ GLOBAL OPTIONS:
return err
}
acls = append(acls, &acl)
} else {
if err := query.Find(&acls).Error; err != nil {
return err
}
} else if err := query.Find(&acls).Error; err != nil {
return err
}
if c.Bool("quiet") {
for _, acl := range acls {
@@ -186,7 +202,7 @@ GLOBAL OPTIONS:
}
table := tablewriter.NewWriter(s)
table.SetHeader([]string{"ID", "Weight", "User groups", "Host groups", "Host pattern", "Action", "Updated", "Created", "Comment"})
table.SetHeader([]string{"ID", "Weight", "User groups", "Host groups", "Host pattern", "Action", "Inception", "Expiration", "Updated", "Created", "Comment"})
table.SetBorder(false)
table.SetCaption(true, fmt.Sprintf("Total: %d ACLs.", len(acls)))
for _, acl := range acls {
@@ -199,6 +215,15 @@ GLOBAL OPTIONS:
hostGroups = append(hostGroups, entity.Name)
}
inception := ""
if acl.Inception != nil {
inception = acl.Inception.Format("2006-01-02 15:04 MST")
}
expiration := ""
if acl.Expiration != nil {
expiration = acl.Expiration.Format("2006-01-02 15:04 MST")
}
table.Append([]string{
fmt.Sprintf("%d", acl.ID),
fmt.Sprintf("%d", acl.Weight),
@@ -206,6 +231,8 @@ GLOBAL OPTIONS:
strings.Join(hostGroups, ", "),
acl.HostPattern,
acl.Action,
inception,
expiration,
humanize.Time(acl.UpdatedAt),
humanize.Time(acl.CreatedAt),
acl.Comment,
@@ -236,6 +263,10 @@ GLOBAL OPTIONS:
cli.StringFlag{Name: "action, a", Usage: "Update action"},
cli.StringFlag{Name: "pattern, p", Usage: "Update host-pattern"},
cli.UintFlag{Name: "weight, w", Usage: "Update weight"},
cli.StringFlag{Name: "inception, i", Usage: "Update inception date-time"},
cli.BoolFlag{Name: "unset-inception", Usage: "Unset inception date-time"},
cli.BoolFlag{Name: "unset-expiration", Usage: "Unset expiration date-time"},
cli.StringFlag{Name: "expiration, e", Usage: "Update expiration date-time"},
cli.StringFlag{Name: "comment, c", Usage: "Update comment"},
cli.StringSliceFlag{Name: "assign-usergroup, ug", Usage: "Assign the ACL to new `USERGROUPS`"},
cli.StringSliceFlag{Name: "unassign-usergroup", Usage: "Unassign the ACL from `USERGROUPS`"},
@@ -250,18 +281,29 @@ GLOBAL OPTIONS:
return err
}
var acls []dbmodels.ACL
var acls []*dbmodels.ACL
if err := dbmodels.ACLsByIdentifiers(db, c.Args()).Find(&acls).Error; err != nil {
return err
}
tx := db.Begin()
for _, acl := range acls {
model := tx.Model(&acl)
model := tx.Model(acl)
inception, err := parseOptionalTime(c.String("inception"))
if err != nil {
return err
}
expiration, err := parseOptionalTime(c.String("expiration"))
if err != nil {
return err
}
update := dbmodels.ACL{
Action: c.String("action"),
HostPattern: c.String("pattern"),
Weight: c.Uint("weight"),
Inception: inception,
Expiration: expiration,
Comment: c.String("comment"),
}
if err := model.Updates(update).Error; err != nil {
@@ -269,6 +311,19 @@ GLOBAL OPTIONS:
return err
}
if c.Bool("unset-inception") {
if err := model.Update("inception", nil).Error; err != nil {
tx.Rollback()
return err
}
}
if c.Bool("unset-expiration") {
if err := model.Update("expiration", nil).Error; err != nil {
tx.Rollback()
return err
}
}
// associations
var appendUserGroups []dbmodels.UserGroup
var deleteUserGroups []dbmodels.UserGroup
@@ -457,7 +512,6 @@ GLOBAL OPTIONS:
"host_groups",
"host_host_groups",
"hosts",
//"migrations",
"sessions",
"settings",
"ssh_keys",
@@ -468,6 +522,7 @@ GLOBAL OPTIONS:
"user_user_groups",
"user_user_roles",
"users",
// "migrations",
}
for _, tableName := range tableNames {
/* #nosec */
@@ -477,6 +532,7 @@ GLOBAL OPTIONS:
}
}
for _, host := range config.Hosts {
host := host
crypto.HostDecrypt(actx.aesKey, host)
if !c.Bool("decrypt") {
if err := crypto.HostEncrypt(actx.aesKey, host); err != nil {
@@ -489,30 +545,35 @@ GLOBAL OPTIONS:
}
}
for _, user := range config.Users {
user := user
if err := tx.FirstOrCreate(&user).Error; err != nil {
tx.Rollback()
return err
}
}
for _, acl := range config.ACLs {
acl := acl
if err := tx.FirstOrCreate(&acl).Error; err != nil {
tx.Rollback()
return err
}
}
for _, hostGroup := range config.HostGroups {
hostGroup := hostGroup
if err := tx.FirstOrCreate(&hostGroup).Error; err != nil {
tx.Rollback()
return err
}
}
for _, userGroup := range config.UserGroups {
userGroup := userGroup
if err := tx.FirstOrCreate(&userGroup).Error; err != nil {
tx.Rollback()
return err
}
}
for _, sshKey := range config.SSHKeys {
sshKey := sshKey
crypto.SSHKeyDecrypt(actx.aesKey, sshKey)
if !c.Bool("decrypt") {
if err := crypto.SSHKeyEncrypt(actx.aesKey, sshKey); err != nil {
@@ -525,24 +586,28 @@ GLOBAL OPTIONS:
}
}
for _, userKey := range config.UserKeys {
userKey := userKey
if err := tx.FirstOrCreate(&userKey).Error; err != nil {
tx.Rollback()
return err
}
}
for _, setting := range config.Settings {
setting := setting
if err := tx.FirstOrCreate(&setting).Error; err != nil {
tx.Rollback()
return err
}
}
for _, session := range config.Sessions {
session := session
if err := tx.FirstOrCreate(&session).Error; err != nil {
tx.Rollback()
return err
}
}
for _, event := range config.Events {
event := event
if err := tx.FirstOrCreate(&event).Error; err != nil {
tx.Rollback()
return err
@@ -612,10 +677,8 @@ GLOBAL OPTIONS:
return err
}
events = append(events, event)
} else {
if err := query.Find(&events).Error; err != nil {
return err
}
} else if err := query.Find(&events).Error; err != nil {
return err
}
if c.Bool("quiet") {
@@ -664,6 +727,7 @@ GLOBAL OPTIONS:
cli.StringFlag{Name: "comment, c"},
cli.StringFlag{Name: "key, k", Usage: "`KEY` to use for authentication"},
cli.StringFlag{Name: "hop, o", Usage: "Hop to use for connecting to the server"},
cli.StringFlag{Name: "logging, l", Usage: "Logging mode (disabled, input, everything)"},
cli.StringSliceFlag{Name: "group, g", Usage: "Assigns the host to `HOSTGROUPS` (default: \"default\")"},
},
Action: func(c *cli.Context) error {
@@ -706,6 +770,11 @@ GLOBAL OPTIONS:
if c.String("name") != "" {
host.Name = c.String("name")
}
host.Logging = "everything" // default is everything
if c.String("logging") != "" {
host.Logging = c.String("logging")
}
// FIXME: check if name already exists
if _, err := govalidator.ValidateStruct(host); err != nil {
@@ -761,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") {
@@ -799,10 +870,8 @@ GLOBAL OPTIONS:
return err
}
hosts = append(hosts, &host)
} else {
if err := query.Find(&hosts).Error; err != nil {
return err
}
} else if err := query.Find(&hosts).Error; err != nil {
return err
}
if c.Bool("quiet") {
@@ -813,14 +882,14 @@ GLOBAL OPTIONS:
}
table := tablewriter.NewWriter(s)
table.SetHeader([]string{"ID", "Name", "URL", "Key", "Groups", "Updated", "Created", "Comment", "Hop"})
table.SetHeader([]string{"ID", "Name", "URL", "Key", "Groups", "Updated", "Created", "Comment", "Hop", "Logging"})
table.SetBorder(false)
table.SetCaption(true, fmt.Sprintf("Total: %d hosts.", len(hosts)))
for _, host := range hosts {
authKey := ""
if host.SSHKeyID > 0 {
var key dbmodels.SSHKey
db.Model(&host).Related(&key)
db.Model(host).Related(&key)
authKey = key.Name
}
groupNames := []string{}
@@ -830,7 +899,7 @@ GLOBAL OPTIONS:
var hop string
if host.HopID != 0 {
var hopHost dbmodels.Host
db.Model(&host).Related(&hopHost, "HopID")
db.Model(host).Related(&hopHost, "HopID")
hop = hopHost.Name
} else {
hop = ""
@@ -845,6 +914,7 @@ GLOBAL OPTIONS:
humanize.Time(host.CreatedAt),
host.Comment,
hop,
host.Logging,
//FIXME: add some stats about last access time etc
})
}
@@ -876,6 +946,7 @@ GLOBAL OPTIONS:
cli.StringFlag{Name: "comment, c", Usage: "Update/set a host comment"},
cli.StringFlag{Name: "key, k", Usage: "Link a `KEY` to use for authentication"},
cli.StringFlag{Name: "hop, o", Usage: "Change the hop to use for connecting to the server"},
cli.StringFlag{Name: "logging, l", Usage: "Logging mode (disabled, input, everything)"},
cli.BoolFlag{Name: "unset-hop", Usage: "Remove the hop set for this host"},
cli.StringSliceFlag{Name: "assign-group, g", Usage: "Assign the host to a new `HOSTGROUPS`"},
cli.StringSliceFlag{Name: "unassign-group", Usage: "Unassign the host from a `HOSTGROUPS`"},
@@ -900,6 +971,7 @@ GLOBAL OPTIONS:
tx := db.Begin()
for _, host := range hosts {
host := host
model := tx.Model(&host)
// simple fields
for _, fieldname := range []string{"name", "comment"} {
@@ -937,6 +1009,17 @@ GLOBAL OPTIONS:
}
}
// logging
if logging := c.String("logging"); logging != "" {
if !dbmodels.IsValidHostLoggingMode(logging) {
return fmt.Errorf("invalid host logging mode: %q", logging)
}
if err := model.Update("logging", logging).Error; err != nil {
tx.Rollback()
return err
}
}
// remove the hop
if c.Bool("unset-hop") {
var hopHost dbmodels.Host
@@ -1063,10 +1146,8 @@ GLOBAL OPTIONS:
return err
}
hostGroups = append(hostGroups, &hostGroup)
} else {
if err := query.Find(&hostGroups).Error; err != nil {
return err
}
} else if err := query.Find(&hostGroups).Error; err != nil {
return err
}
if c.Bool("quiet") {
@@ -1127,7 +1208,7 @@ GLOBAL OPTIONS:
return err
}
var hostgroups []dbmodels.HostGroup
var hostgroups []*dbmodels.HostGroup
if err := dbmodels.HostGroupsByIdentifiers(db, c.Args()).Find(&hostgroups).Error; err != nil {
return err
}
@@ -1138,7 +1219,7 @@ GLOBAL OPTIONS:
tx := db.Begin()
for _, hostgroup := range hostgroups {
model := tx.Model(&hostgroup)
model := tx.Model(hostgroup)
// simple fields
for _, fieldname := range []string{"name", "comment"} {
if c.String(fieldname) != "" {
@@ -1180,9 +1261,9 @@ GLOBAL OPTIONS:
fmt.Fprintf(s, "User email: %s\n", myself.Email)
fmt.Fprintf(s, "Version: %s\n", version)
fmt.Fprintf(s, "GIT SHA: %s\n", gitSha)
fmt.Fprintf(s, "GIT Branch: %s\n", gitBranch)
fmt.Fprintf(s, "GIT Tag: %s\n", gitTag)
// FIXME: gormigrate version
// FIXME: add info about current server (network, cpu, ram, OS)
// FIXME: add info about current user
// FIXME: add active connections
@@ -1199,8 +1280,8 @@ GLOBAL OPTIONS:
Description: "$> key create\n $> key create --name=mykey",
Flags: []cli.Flag{
cli.StringFlag{Name: "name", Usage: "Assigns a name to the key"},
cli.StringFlag{Name: "type", Value: "rsa"},
cli.UintFlag{Name: "length", Value: 2048},
cli.StringFlag{Name: "type", Value: "ed25519"},
cli.UintFlag{Name: "length", Value: 0},
cli.StringFlag{Name: "comment", Usage: "Adds a comment"},
},
Action: func(c *cli.Context) error {
@@ -1213,7 +1294,24 @@ GLOBAL OPTIONS:
name = c.String("name")
}
key, err := crypto.NewSSHKey(c.String("type"), c.Uint("length"))
length := c.Uint("length")
if length == 0 {
switch c.String("type") {
case "rsa":
// same default as ssh-keygen
length = 3072
case "ecdsa":
// same default as ssh-keygen
length = 256
case "ed25519":
// irrelevant for ed25519
// set it to 1 to enforce consistency
// and because 0 is invalid
length = 1
}
}
key, err := crypto.NewSSHKey(c.String("type"), length)
if actx.aesKey != "" {
if err2 := crypto.SSHKeyEncrypt(actx.aesKey, key); err2 != nil {
return err2
@@ -1342,10 +1440,8 @@ GLOBAL OPTIONS:
return err
}
sshKeys = append(sshKeys, &sshKey)
} else {
if err := query.Find(&sshKeys).Error; err != nil {
return err
}
} else if err := query.Find(&sshKeys).Error; err != nil {
return err
}
if c.Bool("quiet") {
for _, sshKey := range sshKeys {
@@ -1364,7 +1460,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),
@@ -1529,9 +1624,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")
@@ -1584,10 +1681,8 @@ GLOBAL OPTIONS:
return err
}
users = append(users, &user)
} else {
if err := query.Find(&users).Error; err != nil {
return err
}
} else if err := query.Find(&users).Error; err != nil {
return err
}
if c.Bool("quiet") {
for _, user := range users {
@@ -1646,6 +1741,8 @@ GLOBAL OPTIONS:
Flags: []cli.Flag{
cli.StringFlag{Name: "name, n", Usage: "Renames the user"},
cli.StringFlag{Name: "email, e", Usage: "Updates the email"},
cli.StringFlag{Name: "invite_token, i", Usage: "Updates the invite token"},
cli.BoolFlag{Name: "remove_invite, R", Usage: "Remove invite token"},
cli.StringSliceFlag{Name: "assign-role, r", Usage: "Assign the user to new `USERROLES`"},
cli.StringSliceFlag{Name: "unassign-role", Usage: "Unassign the user from `USERROLES`"},
cli.StringSliceFlag{Name: "assign-group, g", Usage: "Assign the user to new `USERGROUPS`"},
@@ -1661,7 +1758,7 @@ GLOBAL OPTIONS:
}
// FIXME: check if unset-admin + user == myself
var users []dbmodels.User
var users []*dbmodels.User
if err := dbmodels.UsersByIdentifiers(db, c.Args()).Find(&users).Error; err != nil {
return err
}
@@ -1676,9 +1773,9 @@ GLOBAL OPTIONS:
tx := db.Begin()
for _, user := range users {
model := tx.Model(&user)
model := tx.Model(user)
// simple fields
for _, fieldname := range []string{"name", "email", "comment"} {
for _, fieldname := range []string{"name", "email", "comment", "invite_token"} {
if c.String(fieldname) != "" {
if err := model.Update(fieldname, c.String(fieldname)).Error; err != nil {
tx.Rollback()
@@ -1686,6 +1783,13 @@ GLOBAL OPTIONS:
}
}
}
// invite remove
if c.Bool("remove_invite") {
if err := model.Update("invite_token", "").Error; err != nil {
tx.Rollback()
return err
}
}
// associations
var appendGroups []dbmodels.UserGroup
@@ -1814,10 +1918,8 @@ GLOBAL OPTIONS:
return err
}
userGroups = append(userGroups, &userGroup)
} else {
if err := query.Find(&userGroups).Error; err != nil {
return err
}
} else if err := query.Find(&userGroups).Error; err != nil {
return err
}
if c.Bool("quiet") {
for _, userGroup := range userGroups {
@@ -1877,7 +1979,7 @@ GLOBAL OPTIONS:
return err
}
var usergroups []dbmodels.UserGroup
var usergroups []*dbmodels.UserGroup
if err := dbmodels.UserGroupsByIdentifiers(db, c.Args()).Find(&usergroups).Error; err != nil {
return err
}
@@ -1888,7 +1990,7 @@ GLOBAL OPTIONS:
tx := db.Begin()
for _, usergroup := range usergroups {
model := tx.Model(&usergroup)
model := tx.Model(usergroup)
// simple fields
for _, fieldname := range []string{"name", "comment"} {
if c.String(fieldname) != "" {
@@ -1929,34 +2031,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
},
}, {
@@ -2001,10 +2127,8 @@ GLOBAL OPTIONS:
return err
}
userKeys = append(userKeys, &userKey)
} else {
if err := query.Find(&userKeys).Error; err != nil {
return err
}
} else if err := query.Find(&userKeys).Error; err != nil {
return err
}
if c.Bool("quiet") {
for _, userKey := range userKeys {
@@ -2046,7 +2170,16 @@ GLOBAL OPTIONS:
if err := myself.CheckRoles([]string{"admin"}); err != nil {
return err
}
if err := dbmodels.UserKeysByIdentifiers(db, c.Args()).Find(&dbmodels.UserKey{}).Error; err != nil {
var user dbmodels.User
if err := dbmodels.UsersByIdentifiers(db, c.Args()).First(&user).Error; err != nil {
return err
}
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.UserKeysByIdentifiers(db, c.Args()).Delete(&dbmodels.UserKey{}).Error
},
},
@@ -2112,7 +2245,6 @@ GLOBAL OPTIONS:
factor := 1
for len(sessions) >= limit*factor {
var additionnalSessions []*dbmodels.Session
offset = limit * factor
@@ -2208,7 +2340,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)
}
@@ -2247,3 +2379,14 @@ func parseInputURL(input string) (*url.URL, error) {
}
return u, nil
}
func parseOptionalTime(input string) (*time.Time, error) {
if input != "" {
parsed, err := time.ParseInLocation("2006-01-02 15:04", input, time.Local)
if err != nil {
return nil, err
}
return &parsed, nil
}
return nil, nil
}

View File

@@ -28,6 +28,7 @@ type authContext struct {
db *gorm.DB
userKey dbmodels.UserKey
logsLocation string
aclCheckCmd string
aesKey string
dbDriver, dbURL string
bindAddr string
@@ -122,7 +123,8 @@ func ChannelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewCh
sessionConfigs = append([]sessionConfig{{
Addr: currentHost.DialAddr(),
ClientConfig: clientConfig,
Logs: actx.logsLocation,
LogsLocation: actx.logsLocation,
LoggingMode: currentHost.Logging,
}}, sessionConfigs...)
if currentHost.HopID != 0 {
var newHost dbmodels.Host
@@ -149,7 +151,7 @@ func ChannelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewCh
return
}
go func() {
err = multiChannelHandler(srv, conn, newChan, ctx, sessionConfigs, sess.ID)
err = multiChannelHandler(conn, newChan, ctx, sessionConfigs, sess.ID)
if err != nil {
log.Printf("Error: %v", err)
}
@@ -160,8 +162,7 @@ func ChannelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewCh
ErrMsg: fmt.Sprintf("%v", err),
StoppedAt: &now,
}
switch sessUpdate.ErrMsg {
case "lch closed the connection", "rch closed the connection":
if err == nil {
sessUpdate.ErrMsg = ""
}
actx.db.Model(&sess).Updates(&sessUpdate)
@@ -205,13 +206,11 @@ func bastionClientConfig(ctx ssh.Context, host *dbmodels.Host) (*gossh.ClientCon
if err = actx.db.Preload("Groups").Preload("Groups.ACLs").Where("id = ?", host.ID).First(&tmpHost).Error; err != nil {
return nil, err
}
action, err2 := checkACLs(tmpUser, tmpHost)
if err2 != nil {
return nil, err2
}
action := checkACLs(tmpUser, tmpHost, actx.aclCheckCmd)
switch action {
case string(dbmodels.ACLActionAllow):
// do nothing
case string(dbmodels.ACLActionDeny):
return nil, fmt.Errorf("you don't have permission to that host")
default:
@@ -220,10 +219,10 @@ func bastionClientConfig(ctx ssh.Context, host *dbmodels.Host) (*gossh.ClientCon
return clientConfig, nil
}
func ShellHandler(s ssh.Session, version, gitSha, gitTag, gitBranch string) {
func ShellHandler(s ssh.Session, version, gitSha, gitTag string) {
actx := s.Context().Value(authContextKey).(*authContext)
if actx.userType() != userTypeHealthcheck {
log.Printf("New connection(shell): sshUser=%q remote=%q local=%q command=%q dbUser=id:%q,email:%s", s.User(), s.RemoteAddr(), s.LocalAddr(), s.Command(), actx.user.ID, actx.user.Email)
log.Printf("New connection(shell): sshUser=%q remote=%q local=%q command=%q dbUser=id:%d,email:%s", s.User(), s.RemoteAddr(), s.LocalAddr(), s.Command(), actx.user.ID, actx.user.Email)
}
if actx.err != nil {
@@ -241,7 +240,7 @@ func ShellHandler(s ssh.Session, version, gitSha, gitTag, gitBranch string) {
fmt.Fprintln(s, "OK")
return
case userTypeShell:
if err := shell(s, version, gitSha, gitTag, gitBranch); err != nil {
if err := shell(s, version, gitSha, gitTag); err != nil {
fmt.Fprintf(s, "error: %v\n", err)
_ = s.Exit(1)
}
@@ -253,12 +252,13 @@ func ShellHandler(s ssh.Session, version, gitSha, gitTag, gitBranch 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,
@@ -289,12 +289,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

@@ -4,6 +4,9 @@ import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
@@ -25,35 +28,108 @@ func NewSSHKey(keyType string, length uint) (*dbmodels.SSHKey, error) {
}
// generate the private key
if keyType != "rsa" {
return nil, fmt.Errorf("key type not supported: %q", key.Type)
var err error
var pemKey *pem.Block
var publicKey gossh.PublicKey
switch keyType {
case "rsa":
pemKey, publicKey, err = NewRSAKey(length)
case "ecdsa":
pemKey, publicKey, err = NewECDSAKey(length)
case "ed25519":
pemKey, publicKey, err = NewEd25519Key()
default:
return nil, fmt.Errorf("key type not supported: %q, supported types are: rsa, ecdsa, ed25519", key.Type)
}
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
// convert priv key to x509 format
var pemKey = &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
}
buf := bytes.NewBufferString("")
if err = pem.Encode(buf, pemKey); err != nil {
return nil, err
}
key.PrivKey = buf.String()
// generte authorized-key formatted pubkey output
pub, err := gossh.NewPublicKey(&privateKey.PublicKey)
if err != nil {
return nil, err
}
key.PubKey = strings.TrimSpace(string(gossh.MarshalAuthorizedKey(pub)))
// generate authorized-key formatted pubkey output
key.PubKey = strings.TrimSpace(string(gossh.MarshalAuthorizedKey(publicKey)))
return &key, nil
}
func NewRSAKey(length uint) (*pem.Block, gossh.PublicKey, error) {
if length < 1024 || length > 16384 {
return nil, nil, fmt.Errorf("key length not supported: %d, supported values are between 1024 and 16384", length)
}
privateKey, err := rsa.GenerateKey(rand.Reader, int(length))
if err != nil {
return nil, nil, err
}
// convert priv key to x509 format
pemKey := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
}
publicKey, err := gossh.NewPublicKey(&privateKey.PublicKey)
if err != nil {
return nil, nil, err
}
return pemKey, publicKey, err
}
func NewECDSAKey(length uint) (*pem.Block, gossh.PublicKey, error) {
var curve elliptic.Curve
switch length {
case 256:
curve = elliptic.P256()
case 384:
curve = elliptic.P384()
case 521:
curve = elliptic.P521()
default:
return nil, nil, fmt.Errorf("key length not supported: %d, supported values are 256, 384, 521", length)
}
privateKey, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
return nil, nil, err
}
// convert priv key to x509 format
marshaledKey, err := x509.MarshalPKCS8PrivateKey(privateKey)
pemKey := &pem.Block{
Type: "PRIVATE KEY",
Bytes: marshaledKey,
}
if err != nil {
return nil, nil, err
}
publicKey, err := gossh.NewPublicKey(&privateKey.PublicKey)
if err != nil {
return nil, nil, err
}
return pemKey, publicKey, err
}
func NewEd25519Key() (*pem.Block, gossh.PublicKey, error) {
publicKeyEd25519, privateKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, nil, err
}
// convert priv key to x509 format
marshaledKey, err := x509.MarshalPKCS8PrivateKey(privateKey)
pemKey := &pem.Block{
Type: "PRIVATE KEY",
Bytes: marshaledKey,
}
if err != nil {
return nil, nil, err
}
publicKey, err := gossh.NewPublicKey(publicKeyEd25519)
if err != nil {
return nil, nil, err
}
return pemKey, publicKey, err
}
func ImportSSHKey(keyValue string) (*dbmodels.SSHKey, error) {
key := dbmodels.SSHKey{
Type: "rsa",

View File

@@ -5,12 +5,10 @@ import (
"fmt"
"log"
"net/url"
"regexp"
"strconv"
"strings"
"time"
"github.com/asaskevich/govalidator"
"github.com/jinzhu/gorm"
gossh "golang.org/x/crypto/ssh"
)
@@ -40,12 +38,12 @@ type Setting struct {
type SSHKey struct {
// FIXME: use uuid for ID
gorm.Model
Name string `valid:"required,length(1|32),unix_user"`
Name string `valid:"required,length(1|255),unix_user"`
Type string `valid:"required"`
Length uint `valid:"required"`
Fingerprint string `valid:"optional"`
PrivKey string `sql:"size:10000" valid:"required"`
PubKey string `sql:"size:10000" valid:"optional"`
PrivKey string `sql:"size:5000" valid:"required"`
PubKey string `sql:"size:1000" valid:"optional"`
Hosts []*Host `gorm:"ForeignKey:SSHKeyID"`
Comment string `valid:"optional"`
}
@@ -53,16 +51,17 @@ type SSHKey struct {
type Host struct {
// FIXME: use uuid for ID
gorm.Model
Name string `gorm:"size:32" valid:"required,length(1|32)"`
Name string `gorm:"size: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
URL string `valid:"optional"`
SSHKey *SSHKey `gorm:"ForeignKey:SSHKeyID"` // SSHKey used to connect by the client
SSHKeyID uint `gorm:"index"`
HostKey []byte `sql:"size:10000" valid:"optional"`
HostKey []byte `sql:"size:1000" valid:"optional"`
Groups []*HostGroup `gorm:"many2many:host_host_groups;"`
Comment string `valid:"optional"`
Logging string `valid:"optional,host_logging_mode"`
Hop *Host
HopID uint
}
@@ -70,8 +69,8 @@ type Host struct {
// UserKey defines a user public key used by sshportal to identify the user
type UserKey struct {
gorm.Model
Key []byte `sql:"size:10000" valid:"length(1|10000)"`
AuthorizedKey string `sql:"size:10000" valid:"required,length(1|10000)"`
Key []byte `sql:"size:1000" valid:"length(1|1000)"`
AuthorizedKey string `sql:"size:1000" valid:"required,length(1|1000)"`
UserID uint ``
User *User `gorm:"ForeignKey:UserID"`
Comment string `valid:"optional"`
@@ -79,7 +78,7 @@ type UserKey struct {
type UserRole struct {
gorm.Model
Name string `valid:"required,length(1|32),unix_user"`
Name string `valid:"required,length(1|255),unix_user"`
Users []*User `gorm:"many2many:user_user_roles"`
}
@@ -88,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|32),unix_user"`
Name string `valid:"required,length(1|255),unix_user"`
Keys []*UserKey `gorm:"ForeignKey:UserID"`
Groups []*UserGroup `gorm:"many2many:user_user_groups;"`
Comment string `valid:"optional"`
@@ -97,7 +96,7 @@ type User struct {
type UserGroup struct {
gorm.Model
Name string `valid:"required,length(1|32),unix_user"`
Name string `valid:"required,length(1|255),unix_user"`
Users []*User `gorm:"many2many:user_user_groups;"`
ACLs []*ACL `gorm:"many2many:user_group_acls;"`
Comment string `valid:"optional"`
@@ -105,7 +104,7 @@ type UserGroup struct {
type HostGroup struct {
gorm.Model
Name string `valid:"required,length(1|32),unix_user"`
Name string `valid:"required,length(1|255),unix_user"`
Hosts []*Host `gorm:"many2many:host_host_groups;"`
ACLs []*ACL `gorm:"many2many:host_group_acls;"`
Comment string `valid:"optional"`
@@ -119,6 +118,8 @@ type ACL struct {
Action string `valid:"required"`
Weight uint ``
Comment string `valid:"optional"`
Inception *time.Time
Expiration *time.Time
}
type Session struct {
@@ -166,16 +167,23 @@ const (
BastionSchemeTelnet BastionScheme = "telnet"
)
func init() {
unixUserRegexp := regexp.MustCompile("[a-z_][a-z0-9_-]*")
govalidator.CustomTypeTagMap.Set("unix_user", govalidator.CustomTypeValidator(func(i interface{}, context interface{}) bool {
name, ok := i.(string)
if !ok {
return false
// Generic Helper
func GenericNameOrID(db *gorm.DB, identifiers []string) *gorm.DB {
var ids []string
var names []string
for _, s := range identifiers {
if _, err := strconv.Atoi(s); err == nil {
ids = append(ids, s)
} else {
names = append(names, s)
}
return unixUserRegexp.MatchString(name)
}))
}
if len(ids) > 0 && len(names) > 0 {
return db.Where("id IN (?)", ids).Or("name IN (?)", names)
} else if len(ids) > 0 {
return db.Where("id IN (?)", ids)
}
return db.Where("name IN (?)", names)
}
// Host helpers
@@ -279,7 +287,7 @@ func HostsPreload(db *gorm.DB) *gorm.DB {
return db.Preload("Groups").Preload("SSHKey")
}
func HostsByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
return db.Where("id IN (?)", identifiers).Or("name IN (?)", identifiers)
return GenericNameOrID(db, identifiers)
}
func HostByName(db *gorm.DB, name string) (*Host, error) {
var host Host
@@ -319,7 +327,7 @@ func SSHKeysPreload(db *gorm.DB) *gorm.DB {
return db.Preload("Hosts")
}
func SSHKeysByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
return db.Where("id IN (?)", identifiers).Or("name IN (?)", identifiers)
return GenericNameOrID(db, identifiers)
}
// HostGroup helpers
@@ -328,7 +336,7 @@ func HostGroupsPreload(db *gorm.DB) *gorm.DB {
return db.Preload("ACLs").Preload("Hosts")
}
func HostGroupsByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
return db.Where("id IN (?)", identifiers).Or("name IN (?)", identifiers)
return GenericNameOrID(db, identifiers)
}
// UserGroup helpers
@@ -337,7 +345,7 @@ func UserGroupsPreload(db *gorm.DB) *gorm.DB {
return db.Preload("ACLs").Preload("Users")
}
func UserGroupsByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
return db.Where("id IN (?)", identifiers).Or("name IN (?)", identifiers)
return GenericNameOrID(db, identifiers)
}
// User helpers
@@ -346,7 +354,21 @@ func UsersPreload(db *gorm.DB) *gorm.DB {
return db.Preload("Groups").Preload("Keys").Preload("Roles")
}
func UsersByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
return db.Where("id IN (?)", identifiers).Or("email IN (?)", identifiers).Or("name IN (?)", identifiers)
var ids []string
var names []string
for _, s := range identifiers {
if _, err := strconv.Atoi(s); err == nil {
ids = append(ids, s)
} else {
names = append(names, s)
}
}
if len(ids) > 0 && len(names) > 0 {
db.Where("id IN (?)", identifiers).Or("email IN (?)", identifiers).Or("name IN (?)", identifiers)
} else if len(ids) > 0 {
return db.Where("id IN (?)", ids)
}
return db.Where("email IN (?)", identifiers).Or("name IN (?)", identifiers)
}
func (u *User) HasRole(name string) bool {
for _, role := range u.Roles {
@@ -382,14 +404,14 @@ func UserKeysPreload(db *gorm.DB) *gorm.DB {
func UserKeysByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
return db.Where("id IN (?)", identifiers)
}
func UserKeysByUserID(db *gorm.DB, identifiers []string) *gorm.DB {
return db.Where("user_id IN (?)", identifiers)
}
// UserRole helpers
//func UserRolesPreload(db *gorm.DB) *gorm.DB {
// return db.Preload("Users")
//}
func UserRolesByIdentifiers(db *gorm.DB, identifiers []string) *gorm.DB {
return db.Where("id IN (?)", identifiers).Or("name IN (?)", identifiers)
return GenericNameOrID(db, identifiers)
}
// Session helpers
@@ -436,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
}

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

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

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)
}
})
}
}

361
rules.mk vendored Normal file
View File

@@ -0,0 +1,361 @@
# +--------------------------------------------------------------+
# | * * * moul.io/rules.mk |
# +--------------------------------------------------------------+
# | |
# | ++ ______________________________________ |
# | ++++ / \ |
# | ++++ | | |
# | ++++++++++ | https://moul.io/rules.mk is a set | |
# | +++ | | of common Makefile rules that can | |
# | ++ | | be configured from the Makefile | |
# | + -== ==| | or with environment variables. | |
# | ( <*> <*> | | |
# | | | /| Manfred Touron | |
# | | _) / | manfred.life | |
# | | +++ / \______________________________________/ |
# | \ =+ / |
# | \ + |
# | |\++++++ |
# | | ++++ ||// |
# | ___| |___ _||/__ __|
# | / --- \ \| ||| __ _ ___ __ __/ /|
# |/ | | \ \ / / ' \/ _ \/ // / / |
# || | | | | | /_/_/_/\___/\_,_/_/ |
# +--------------------------------------------------------------+
.PHONY: _default_entrypoint
_default_entrypoint: help
##
## Common helpers
##
rwildcard = $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
check-program = $(foreach exec,$(1),$(if $(shell PATH="$(PATH)" which $(exec)),,$(error "No $(exec) in PATH")))
my-filter-out = $(foreach v,$(2),$(if $(findstring $(1),$(v)),,$(v)))
novendor = $(call my-filter-out,vendor/,$(1))
##
## rules.mk
##
ifneq ($(wildcard rules.mk),)
.PHONY: rulesmk.bumpdeps
rulesmk.bumpdeps:
wget -O rules.mk https://raw.githubusercontent.com/moul/rules.mk/master/rules.mk
BUMPDEPS_STEPS += rulesmk.bumpdeps
endif
##
## Maintainer
##
ifneq ($(wildcard .git/HEAD),)
.PHONY: generate.authors
generate.authors: AUTHORS
AUTHORS: .git/
echo "# This file lists all individuals having contributed content to the repository." > AUTHORS
echo "# For how it is generated, see 'https://github.com/moul/rules.mk'" >> AUTHORS
echo >> AUTHORS
git log --format='%aN <%aE>' | LC_ALL=C.UTF-8 sort -uf >> AUTHORS
GENERATE_STEPS += generate.authors
endif
##
## Golang
##
ifndef GOPKG
ifneq ($(wildcard go.mod),)
GOPKG = $(shell sed '/module/!d;s/^omdule\ //' go.mod)
endif
endif
ifdef GOPKG
GO ?= go
GOPATH ?= $(HOME)/go
GO_INSTALL_OPTS ?=
GO_TEST_OPTS ?= -test.timeout=30s
GOMOD_DIRS ?= $(sort $(call novendor,$(dir $(call rwildcard,*,*/go.mod go.mod))))
GOCOVERAGE_FILE ?= ./coverage.txt
GOTESTJSON_FILE ?= ./go-test.json
GOBUILDLOG_FILE ?= ./go-build.log
GOINSTALLLOG_FILE ?= ./go-install.log
ifdef GOBINS
.PHONY: go.install
go.install:
ifeq ($(CI),true)
@rm -f /tmp/goinstall.log
@set -e; for dir in $(GOBINS); do ( set -xe; \
cd $$dir; \
$(GO) install -v $(GO_INSTALL_OPTS) .; \
); done 2>&1 | tee $(GOINSTALLLOG_FILE)
else
@set -e; for dir in $(GOBINS); do ( set -xe; \
cd $$dir; \
$(GO) install $(GO_INSTALL_OPTS) .; \
); done
endif
INSTALL_STEPS += go.install
.PHONY: go.release
go.release:
$(call check-program, goreleaser)
goreleaser --snapshot --skip-publish --rm-dist
@echo -n "Do you want to release? [y/N] " && read ans && \
if [ $${ans:-N} = y ]; then set -xe; goreleaser --rm-dist; fi
RELEASE_STEPS += go.release
endif
.PHONY: go.unittest
go.unittest:
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); \
); \
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; \
fi)); done
@mv /tmp/gocoverage $(GOCOVERAGE_FILE)
else
@echo "mode: atomic" > /tmp/gocoverage
@set -e; for dir in $(GOMOD_DIRS); do (set -e; (set -xe; \
cd $$dir; \
$(GO) test ./... $(GO_TEST_OPTS) -cover -coverprofile=/tmp/profile.out -covermode=atomic -race); \
if [ -f /tmp/profile.out ]; then \
cat /tmp/profile.out | sed "/mode: atomic/d" >> /tmp/gocoverage; \
rm -f /tmp/profile.out; \
fi); done
@mv /tmp/gocoverage $(GOCOVERAGE_FILE)
endif
.PHONY: go.checkdoc
go.checkdoc:
go doc $(first $(GOMOD_DIRS))
.PHONY: go.coverfunc
go.coverfunc: go.unittest
go tool cover -func=$(GOCOVERAGE_FILE) | grep -v .pb.go: | grep -v .pb.gw.go:
.PHONY: go.lint
go.lint:
@set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \
cd $$dir; \
golangci-lint run --verbose ./...; \
); done
.PHONY: go.tidy
go.tidy:
@# tidy dirs with go.mod files
@set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \
cd $$dir; \
$(GO) mod tidy; \
); done
.PHONY: go.depaware-update
go.depaware-update: go.tidy
@# gen depaware for bins
@set -e; for dir in $(GOBINS); do ( set -xe; \
cd $$dir; \
$(GO) run github.com/tailscale/depaware --update .; \
); done
@# tidy unused depaware deps if not in a tools_test.go file
@set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \
cd $$dir; \
$(GO) mod tidy; \
); done
.PHONY: go.depaware-check
go.depaware-check: go.tidy
@# gen depaware for bins
@set -e; for dir in $(GOBINS); do ( set -xe; \
cd $$dir; \
$(GO) run github.com/tailscale/depaware --check .; \
); done
.PHONY: go.build
go.build:
@set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \
cd $$dir; \
$(GO) build ./...; \
); done
.PHONY: go.bump-deps
go.bumpdeps:
@set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \
cd $$dir; \
$(GO) get -u ./...; \
); done
.PHONY: go.fmt
go.fmt:
@set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \
cd $$dir; \
$(GO) run golang.org/x/tools/cmd/goimports -w `go list -f '{{.Dir}}' ./...` \
); done
VERIFY_STEPS += go.depaware-check
BUILD_STEPS += go.build
BUMPDEPS_STEPS += go.bumpdeps go.depaware-update
TIDY_STEPS += go.tidy
LINT_STEPS += go.lint
UNITTEST_STEPS += go.unittest
FMT_STEPS += go.fmt
# FIXME: disabled, because currently slow
# new rule that is manually run sometimes, i.e. `make pre-release` or `make maintenance`.
# alternative: run it each time the go.mod is changed
#GENERATE_STEPS += go.depaware-update
endif
##
## Gitattributes
##
ifneq ($(wildcard .gitattributes),)
.PHONY: _linguist-ignored
_linguist-kept:
@git check-attr linguist-vendored $(shell git check-attr linguist-generated $(shell find . -type f | grep -v .git/) | grep unspecified | cut -d: -f1) | grep unspecified | cut -d: -f1 | sort
.PHONY: _linguist-kept
_linguist-ignored:
@git check-attr linguist-vendored linguist-ignored `find . -not -path './.git/*' -type f` | grep '\ set$$' | cut -d: -f1 | sort -u
endif
##
## Node
##
ifndef NPM_PACKAGES
ifneq ($(wildcard package.json),)
NPM_PACKAGES = .
endif
endif
ifdef NPM_PACKAGES
.PHONY: npm.publish
npm.publish:
@echo -n "Do you want to npm publish? [y/N] " && read ans && \
@if [ $${ans:-N} = y ]; then \
set -e; for dir in $(NPM_PACKAGES); do ( set -xe; \
cd $$dir; \
npm publish --access=public; \
); done; \
fi
RELEASE_STEPS += npm.publish
endif
##
## Docker
##
docker_build = docker build \
--build-arg VCS_REF=`git rev-parse --short HEAD` \
--build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
--build-arg VERSION=`git describe --tags --always` \
-t "$2" -f "$1" "$(dir $1)"
ifndef DOCKERFILE_PATH
DOCKERFILE_PATH = ./Dockerfile
endif
ifndef DOCKER_IMAGE
ifneq ($(wildcard Dockerfile),)
DOCKER_IMAGE = $(notdir $(PWD))
endif
endif
ifdef DOCKER_IMAGE
ifneq ($(DOCKER_IMAGE),none)
.PHONY: docker.build
docker.build:
$(call check-program, docker)
$(call docker_build,$(DOCKERFILE_PATH),$(DOCKER_IMAGE))
BUILD_STEPS += docker.build
endif
endif
##
## Common
##
TEST_STEPS += $(UNITTEST_STEPS)
TEST_STEPS += $(LINT_STEPS)
TEST_STEPS += $(TIDY_STEPS)
ifneq ($(strip $(TEST_STEPS)),)
.PHONY: test
test: $(PRE_TEST_STEPS) $(TEST_STEPS)
endif
ifdef INSTALL_STEPS
.PHONY: install
install: $(PRE_INSTALL_STEPS) $(INSTALL_STEPS)
endif
ifdef UNITTEST_STEPS
.PHONY: unittest
unittest: $(PRE_UNITTEST_STEPS) $(UNITTEST_STEPS)
endif
ifdef LINT_STEPS
.PHONY: lint
lint: $(PRE_LINT_STEPS) $(FMT_STEPS) $(LINT_STEPS)
endif
ifdef TIDY_STEPS
.PHONY: tidy
tidy: $(PRE_TIDY_STEPS) $(TIDY_STEPS)
endif
ifdef BUILD_STEPS
.PHONY: build
build: $(PRE_BUILD_STEPS) $(BUILD_STEPS)
endif
ifdef VERIFY_STEPS
.PHONY: verify
verify: $(PRE_VERIFY_STEPS) $(VERIFY_STEPS)
endif
ifdef RELEASE_STEPS
.PHONY: release
release: $(PRE_RELEASE_STEPS) $(RELEASE_STEPS)
endif
ifdef BUMPDEPS_STEPS
.PHONY: bumpdeps
bumpdeps: $(PRE_BUMDEPS_STEPS) $(BUMPDEPS_STEPS)
endif
ifdef FMT_STEPS
.PHONY: fmt
fmt: $(PRE_FMT_STEPS) $(FMT_STEPS)
endif
ifdef GENERATE_STEPS
.PHONY: generate
generate: $(PRE_GENERATE_STEPS) $(GENERATE_STEPS)
endif
.PHONY: help
help::
@echo "General commands:"
@[ "$(BUILD_STEPS)" != "" ] && echo " build" || true
@[ "$(BUMPDEPS_STEPS)" != "" ] && echo " bumpdeps" || true
@[ "$(FMT_STEPS)" != "" ] && echo " fmt" || true
@[ "$(GENERATE_STEPS)" != "" ] && echo " generate" || true
@[ "$(INSTALL_STEPS)" != "" ] && echo " install" || true
@[ "$(LINT_STEPS)" != "" ] && echo " lint" || true
@[ "$(RELEASE_STEPS)" != "" ] && echo " release" || true
@[ "$(TEST_STEPS)" != "" ] && echo " test" || true
@[ "$(TIDY_STEPS)" != "" ] && echo " tidy" || true
@[ "$(UNITTEST_STEPS)" != "" ] && echo " unittest" || true
@[ "$(VERIFY_STEPS)" != "" ] && echo " verify" || true
@# FIXME: list other commands
print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true

View File

@@ -22,6 +22,7 @@ type serverConfig struct {
bindAddr string
debug, demo bool
idleTimeout time.Duration
aclCheckCmd string
}
func parseServerConfig(c *cli.Context) (*serverConfig, error) {
@@ -34,6 +35,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:
@@ -59,7 +61,7 @@ func ensureLogDirectory(location string) error {
}
func server(c *serverConfig) (err error) {
var db = (*gorm.DB)(nil)
var db *gorm.DB
// try to setup the local DB
if db, err = gorm.Open(c.dbDriver, c.dbURL); err != nil {
@@ -89,8 +91,8 @@ func server(c *serverConfig) (err error) {
// configure server
srv := &ssh.Server{
Addr: c.bindAddr,
Handler: func(s ssh.Session) { bastion.ShellHandler(s, Version, GitSha, GitTag, GitBranch) }, // ssh.Server.Handler is the handler for the DefaultSessionHandler
Version: fmt.Sprintf("sshportal-%s", Version),
Handler: func(s ssh.Session) { bastion.ShellHandler(s, GitTag, GitSha, GitTag) }, // ssh.Server.Handler is the handler for the DefaultSessionHandler
Version: fmt.Sprintf("sshportal-%s", GitTag),
ChannelHandlers: map[string]ssh.ChannelHandler{
"default": bastion.ChannelHandler,
},
@@ -119,8 +121,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

@@ -60,7 +60,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()

14
testserver_unsupported.go Normal file
View File

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