Compare commits
83 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bae5ad94a9 | ||
|
|
5369a4f966 | ||
|
|
1c98ef283e | ||
|
|
3dc2801c60 | ||
|
|
0415f116ea | ||
|
|
68ce353c5d | ||
|
|
f7ed3a66f2 | ||
|
|
4e9c5205c7 | ||
|
|
63b4aa5533 | ||
|
|
d580b14d62 | ||
|
|
669577de47 | ||
|
|
868be6af11 | ||
|
|
97bf5d3168 | ||
|
|
32fcfa370c | ||
|
|
a710e50b1e | ||
|
|
55010dcc09 | ||
|
|
9413b75dc8 | ||
|
|
2648418463 | ||
|
|
79cbaa3afe | ||
|
|
2def328f6a | ||
|
|
ab9c53f1b0 | ||
|
|
614418e7be | ||
|
|
a5bade8761 | ||
|
|
7404704bfe | ||
|
|
84a0a31eda | ||
|
|
40bbea590c | ||
|
|
e455d50db9 | ||
|
|
be3f215e24 | ||
|
|
c290253546 | ||
|
|
28a5fd1846 | ||
|
|
19605f0054 | ||
|
|
5b4332072c | ||
|
|
c1c4c556b4 | ||
|
|
3c32177213 | ||
|
|
762736d622 | ||
|
|
bbbc484fe8 | ||
|
|
e1602364c8 | ||
|
|
2540d1e861 | ||
|
|
177a198420 | ||
|
|
51612aab13 | ||
|
|
e20af1dde5 | ||
|
|
6caa1f1657 | ||
|
|
e0f76d15ec | ||
|
|
05225a4b25 | ||
|
|
bcc150727f | ||
|
|
9062417d13 | ||
|
|
baeade4043 | ||
|
|
b9552e98b5 | ||
|
|
715ccde829 | ||
|
|
f5dc1bd1b9 | ||
|
|
c79c50aeb6 | ||
|
|
df3542c6ee | ||
|
|
e40f5307a3 | ||
|
|
6e6045306b | ||
|
|
874467b1e6 | ||
|
|
5c1c559a9a | ||
|
|
6872c727ef | ||
|
|
cae996d041 | ||
|
|
a23b77282c | ||
|
|
24814c4152 | ||
|
|
07359988d0 | ||
|
|
db6eb63297 | ||
|
|
5fdb31b97d | ||
|
|
bce6b1998b | ||
|
|
f7fa60da97 | ||
|
|
d2cd6b64a3 | ||
|
|
1ef0cc8725 | ||
|
|
d894005c3f | ||
|
|
af7206d114 | ||
|
|
1f9d962cd6 | ||
|
|
460041c6e3 | ||
|
|
7068565ab1 | ||
|
|
74bd885c1d | ||
|
|
9317f206d1 | ||
|
|
6c3f803dc6 | ||
|
|
9c3d29eb83 | ||
|
|
e339a73931 | ||
|
|
0dcab1b380 | ||
|
|
c697c9aaeb | ||
|
|
032f802348 | ||
|
|
7fd9be9058 | ||
|
|
83b54aeeff | ||
|
|
2323d6fd1e |
@@ -1,7 +1,7 @@
|
||||
defaults: &defaults
|
||||
working_directory: /go/src/moul.io/sshportal
|
||||
docker:
|
||||
- image: circleci/golang:1.14.7
|
||||
- image: circleci/golang:1.16.2
|
||||
environment:
|
||||
GO111MODULE: "on"
|
||||
|
||||
@@ -27,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
|
||||
|
||||
30
.github/ISSUE_TEMPLATE.md
vendored
30
.github/ISSUE_TEMPLATE.md
vendored
@@ -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?
|
||||
...
|
||||
|
||||
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -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
20
.github/dependabot.yml
vendored
Normal 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
|
||||
21
.github/workflows/ci.yml
vendored
21
.github/workflows/ci.yml
vendored
@@ -20,9 +20,9 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: lint
|
||||
uses: golangci/golangci-lint-action@v0.1.7
|
||||
uses: golangci/golangci-lint-action@v2.5.2
|
||||
with:
|
||||
version: v1.28
|
||||
version: v1.38
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tests-on-windows:
|
||||
needs: golangci-lint # run after golangci-lint action to not produce duplicated errors
|
||||
@@ -30,8 +30,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
golang:
|
||||
#- 1.13
|
||||
- 1.14
|
||||
- 1.16.x
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Go
|
||||
@@ -47,14 +46,14 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
golang:
|
||||
- 1.14
|
||||
- 1.16.x
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.golang }}
|
||||
- uses: actions/cache@v1
|
||||
- uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ matrix.golang }}-${{ hashFiles('**/go.sum') }}
|
||||
@@ -68,17 +67,17 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
golang:
|
||||
- 1.11
|
||||
- 1.12
|
||||
- 1.13
|
||||
- 1.14
|
||||
- 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@v1
|
||||
- uses: actions/cache@v2.1.5
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ matrix.golang }}-${{ hashFiles('**/go.sum') }}
|
||||
|
||||
8
AUTHORS
generated
8
AUTHORS
generated
@@ -5,18 +5,24 @@ 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>
|
||||
@@ -24,6 +30,8 @@ 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>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# build
|
||||
FROM golang:1.14.7 as builder
|
||||
FROM golang:1.16.2 as builder
|
||||
ENV GO111MODULE=on
|
||||
WORKDIR /go/src/moul.io/sshportal
|
||||
COPY go.mod go.sum ./
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -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.
|
||||
|
||||
@@ -366,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...
|
||||
|
||||
128
depaware.txt
Normal file
128
depaware.txt
Normal 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+
|
||||
35
go.mod
generated
35
go.mod
generated
@@ -2,33 +2,36 @@ module moul.io/sshportal
|
||||
|
||||
require (
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
|
||||
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
github.com/creack/pty v1.1.11 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/docker/docker v1.13.1
|
||||
github.com/docker/docker v20.10.6+incompatible
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/gliderlabs/ssh v0.3.0
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
|
||||
github.com/jinzhu/gorm v1.9.15
|
||||
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/mattn/go-colorable v0.1.6 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
|
||||
github.com/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.4
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/reiver/go-oi v1.0.0
|
||||
github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sabban/bastion v0.0.0-20180110125408-b9d3c9b1f4d3
|
||||
github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3 // indirect
|
||||
github.com/smartystreets/goconvey v1.6.4
|
||||
github.com/urfave/cli v1.22.4
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 // indirect
|
||||
github.com/tailscale/depaware v0.0.0-20201214215404-77d1e9757027
|
||||
github.com/urfave/cli v1.22.5
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 // indirect
|
||||
golang.org/x/term v0.0.0-20210422114643-f5beecf764ed // indirect
|
||||
golang.org/x/tools v0.1.0
|
||||
gopkg.in/gormigrate.v1 v1.6.0
|
||||
moul.io/srand v1.4.0
|
||||
moul.io/srand v1.6.1
|
||||
)
|
||||
|
||||
go 1.14
|
||||
|
||||
108
go.sum
generated
108
go.sum
generated
@@ -4,42 +4,44 @@ github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBK
|
||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
||||
github.com/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 h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.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-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
|
||||
github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v20.10.6+incompatible h1:oXI3Vas8TI8Eu/EjH4srKHJBVqraSzJybhxY7Om9faQ=
|
||||
github.com/docker/docker v20.10.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/gliderlabs/ssh v0.3.0 h1:7GcKy4erEljCE/QeQ2jTVpu+3f3zkpZOxOJjFYkMqYU=
|
||||
github.com/gliderlabs/ssh v0.3.0/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/gliderlabs/ssh v0.3.2 h1:gcfd1Aj/9RQxvygu4l3sak711f/5+VOwBw9C/7+N4EI=
|
||||
github.com/gliderlabs/ssh v0.3.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/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/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/jinzhu/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
|
||||
github.com/jinzhu/gorm v1.9.15 h1:OdR1qFvtXktlxk73XFYMiYn9ywzTwytqe4QkuMRqc38=
|
||||
github.com/jinzhu/gorm v1.9.15/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
|
||||
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/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
@@ -55,21 +57,27 @@ 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/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
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.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-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/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
|
||||
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.14.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.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||
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=
|
||||
@@ -78,47 +86,87 @@ 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 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
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/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 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
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-20190401211740-f487f9de1cd3 h1:hBSHahWMEgzwRyS6dRpxY0XyjZsHyQ61s084wo5PJe0=
|
||||
github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
|
||||
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
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=
|
||||
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-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-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
|
||||
golang.org/x/mod v0.4.0/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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-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/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/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-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-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y=
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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.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-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58 h1:1Bs6RVeBFtLZ8Yi1Hk07DiOqzvwLD/4hln4iahvFlag=
|
||||
golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
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=
|
||||
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/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=
|
||||
moul.io/srand v1.4.0 h1:r5ZMiWDN0ni0lTV7KzJR/jx0K7GivJYW5WaXmufgeik=
|
||||
moul.io/srand v1.4.0/go.mod h1:P2uaZB+GFstFNo8sEj6/U8FRV1n25kD0LLckFpJ+qvc=
|
||||
moul.io/srand v1.6.1 h1:SJ335F+54ivLdlH7wH52Rtyv0Ffos6DpsF5wu3ZVMXU=
|
||||
moul.io/srand v1.6.1/go.mod h1:P2uaZB+GFstFNo8sEj6/U8FRV1n25kD0LLckFpJ+qvc=
|
||||
|
||||
11
internal/tools/tools.go
Normal file
11
internal/tools/tools.go
Normal 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"
|
||||
)
|
||||
7
main.go
7
main.go
@@ -22,7 +22,7 @@ var (
|
||||
)
|
||||
|
||||
func main() {
|
||||
rand.Seed(srand.Secure())
|
||||
rand.Seed(srand.MustSecure())
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Name = path.Base(os.Args[0])
|
||||
@@ -83,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",
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
package bastion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"moul.io/sshportal/pkg/dbmodels"
|
||||
)
|
||||
|
||||
// ACLHookTimeout is timeout for external ACL hook execution
|
||||
const ACLHookTimeout = 2 * time.Second
|
||||
|
||||
type byWeight []*dbmodels.ACL
|
||||
|
||||
func (a byWeight) Len() int { return len(a) }
|
||||
func (a byWeight) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a byWeight) Less(i, j int) bool { return a[i].Weight < a[j].Weight }
|
||||
|
||||
func checkACLs(user dbmodels.User, host dbmodels.Host) string {
|
||||
func checkACLs(user dbmodels.User, host dbmodels.Host, aclCheckCmd string) string {
|
||||
currentTime := time.Now()
|
||||
|
||||
// shared ACLs between user and host
|
||||
@@ -34,9 +43,13 @@ func checkACLs(user dbmodels.User, host dbmodels.Host) string {
|
||||
}
|
||||
// FIXME: add ACLs that match host pattern
|
||||
|
||||
// deny by default if no shared ACL
|
||||
// if no shared ACL then execute ACLs hook if it exists and return its result
|
||||
if len(aclMap) == 0 {
|
||||
return string(dbmodels.ACLActionDeny) // default action
|
||||
action, err := checkACLsHook(aclCheckCmd, string(dbmodels.ACLActionDeny), user, host)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
return action
|
||||
}
|
||||
|
||||
// transform map to slice and sort it
|
||||
@@ -46,5 +59,62 @@ func checkACLs(user dbmodels.User, host dbmodels.Host) string {
|
||||
}
|
||||
sort.Sort(byWeight(acls))
|
||||
|
||||
return acls[0].Action
|
||||
action, err := checkACLsHook(aclCheckCmd, acls[0].Action, user, host)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
return action
|
||||
}
|
||||
|
||||
// checkACLsHook executes external command to check ACL and passes following parameters:
|
||||
// $1 - SSH Portal `action` (`allow` or `deny`)
|
||||
// $2 - User as JSON string
|
||||
// $3 - Host as JSON string
|
||||
// External program has to return `allow` or `deny` in stdout.
|
||||
// In case of any error function returns `action`.
|
||||
func checkACLsHook(aclCheckCmd string, action string, user dbmodels.User, host dbmodels.Host) (string, error) {
|
||||
if aclCheckCmd == "" {
|
||||
return action, nil
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), ACLHookTimeout)
|
||||
defer cancel()
|
||||
|
||||
jsonUser, err := json.Marshal(user)
|
||||
if err != nil {
|
||||
return action, err
|
||||
}
|
||||
|
||||
jsonHost, err := json.Marshal(host)
|
||||
if err != nil {
|
||||
return action, err
|
||||
}
|
||||
|
||||
args := []string{
|
||||
action,
|
||||
string(jsonUser),
|
||||
string(jsonHost),
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, aclCheckCmd, args...)
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return action, err
|
||||
}
|
||||
|
||||
if ctx.Err() == context.DeadlineExceeded {
|
||||
return action, fmt.Errorf("external ACL hook command timed out")
|
||||
}
|
||||
|
||||
outStr := strings.TrimSuffix(string(out), "\n")
|
||||
|
||||
switch outStr {
|
||||
case string(dbmodels.ACLActionAllow):
|
||||
return string(dbmodels.ACLActionAllow), nil
|
||||
case string(dbmodels.ACLActionDeny):
|
||||
return string(dbmodels.ACLActionDeny), nil
|
||||
default:
|
||||
return action, fmt.Errorf("acl-check-cmd wrong output '%s'", outStr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ func TestCheckACLs(t *testing.T) {
|
||||
db.Preload("Groups").Preload("Groups.ACLs").Find(&users)
|
||||
|
||||
// test
|
||||
action := checkACLs(users[0], hosts[0])
|
||||
action := checkACLs(users[0], hosts[0], "")
|
||||
c.So(action, ShouldEqual, dbmodels.ACLActionAllow)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -537,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
|
||||
}
|
||||
@@ -644,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
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
@@ -21,9 +22,10 @@ import (
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/urfave/cli"
|
||||
gossh "golang.org/x/crypto/ssh"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"golang.org/x/crypto/ssh/terminal" // nolint:staticcheck
|
||||
"moul.io/sshportal/pkg/crypto"
|
||||
"moul.io/sshportal/pkg/dbmodels"
|
||||
"moul.io/sshportal/pkg/utils"
|
||||
)
|
||||
|
||||
var banner = `
|
||||
@@ -510,7 +512,6 @@ GLOBAL OPTIONS:
|
||||
"host_groups",
|
||||
"host_host_groups",
|
||||
"hosts",
|
||||
//"migrations",
|
||||
"sessions",
|
||||
"settings",
|
||||
"ssh_keys",
|
||||
@@ -521,6 +522,7 @@ GLOBAL OPTIONS:
|
||||
"user_user_groups",
|
||||
"user_user_roles",
|
||||
"users",
|
||||
// "migrations",
|
||||
}
|
||||
for _, tableName := range tableNames {
|
||||
/* #nosec */
|
||||
@@ -828,12 +830,14 @@ GLOBAL OPTIONS:
|
||||
}
|
||||
|
||||
var hosts []*dbmodels.Host
|
||||
db = db.Preload("Groups")
|
||||
if myself.HasRole("admin") {
|
||||
db = db.Preload("SSHKey")
|
||||
}
|
||||
if err := dbmodels.HostsByIdentifiers(db, c.Args()).Find(&hosts).Error; err != nil {
|
||||
return err
|
||||
if err := dbmodels.HostsByIdentifiers(db.Preload("Groups").Preload("SSHKey"), c.Args()).Find(&hosts).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := dbmodels.HostsByIdentifiers(db.Preload("Groups"), c.Args()).Find(&hosts).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if c.Bool("decrypt") {
|
||||
@@ -1276,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 {
|
||||
@@ -1290,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
|
||||
@@ -1439,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),
|
||||
@@ -1604,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")
|
||||
@@ -1719,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`"},
|
||||
@@ -1751,7 +1775,7 @@ GLOBAL OPTIONS:
|
||||
for _, user := range users {
|
||||
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()
|
||||
@@ -1759,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
|
||||
@@ -2000,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
|
||||
},
|
||||
}, {
|
||||
@@ -2115,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
|
||||
},
|
||||
},
|
||||
@@ -2276,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)
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ type authContext struct {
|
||||
db *gorm.DB
|
||||
userKey dbmodels.UserKey
|
||||
logsLocation string
|
||||
aclCheckCmd string
|
||||
aesKey string
|
||||
dbDriver, dbURL string
|
||||
bindAddr string
|
||||
@@ -206,7 +207,7 @@ func bastionClientConfig(ctx ssh.Context, host *dbmodels.Host) (*gossh.ClientCon
|
||||
return nil, err
|
||||
}
|
||||
|
||||
action := checkACLs(tmpUser, tmpHost)
|
||||
action := checkACLs(tmpUser, tmpHost, actx.aclCheckCmd)
|
||||
switch action {
|
||||
case string(dbmodels.ACLActionAllow):
|
||||
// do nothing
|
||||
@@ -251,12 +252,13 @@ func ShellHandler(s ssh.Session, version, gitSha, gitTag string) {
|
||||
panic("should not happen")
|
||||
}
|
||||
|
||||
func PasswordAuthHandler(db *gorm.DB, logsLocation, aesKey, dbDriver, dbURL, bindAddr string, demo bool) ssh.PasswordHandler {
|
||||
func PasswordAuthHandler(db *gorm.DB, logsLocation, aclCheckCmd, aesKey, dbDriver, dbURL, bindAddr string, demo bool) ssh.PasswordHandler {
|
||||
return func(ctx ssh.Context, pass string) bool {
|
||||
actx := &authContext{
|
||||
db: db,
|
||||
inputUsername: ctx.User(),
|
||||
logsLocation: logsLocation,
|
||||
aclCheckCmd: aclCheckCmd,
|
||||
aesKey: aesKey,
|
||||
dbDriver: dbDriver,
|
||||
dbURL: dbURL,
|
||||
@@ -287,12 +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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -167,6 +167,25 @@ const (
|
||||
BastionSchemeTelnet BastionScheme = "telnet"
|
||||
)
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
func (host *Host) DialAddr() string {
|
||||
@@ -268,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
|
||||
@@ -308,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
|
||||
@@ -317,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
|
||||
@@ -326,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
|
||||
@@ -335,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 {
|
||||
@@ -371,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
|
||||
@@ -425,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
|
||||
}
|
||||
|
||||
13
pkg/utils/emailvalidator.go
Normal file
13
pkg/utils/emailvalidator.go
Normal 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)
|
||||
}
|
||||
30
pkg/utils/emailvalidator_test.go
Normal file
30
pkg/utils/emailvalidator_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
86
rules.mk
vendored
86
rules.mk
vendored
@@ -23,7 +23,8 @@
|
||||
# || | | | | | /_/_/_/\___/\_,_/_/ |
|
||||
# +--------------------------------------------------------------+
|
||||
|
||||
all: help
|
||||
.PHONY: _default_entrypoint
|
||||
_default_entrypoint: help
|
||||
|
||||
##
|
||||
## Common helpers
|
||||
@@ -31,6 +32,8 @@ all: help
|
||||
|
||||
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
|
||||
@@ -71,7 +74,7 @@ GO ?= go
|
||||
GOPATH ?= $(HOME)/go
|
||||
GO_INSTALL_OPTS ?=
|
||||
GO_TEST_OPTS ?= -test.timeout=30s
|
||||
GOMOD_DIR ?= .
|
||||
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
|
||||
@@ -109,30 +112,31 @@ go.unittest:
|
||||
ifeq ($(CI),true)
|
||||
@echo "mode: atomic" > /tmp/gocoverage
|
||||
@rm -f $(GOTESTJSON_FILE)
|
||||
@set -e; for dir in `find $(GOMOD_DIR) -type f -name "go.mod" | grep -v /vendor/ | sed 's@/[^/]*$$@@' | sort | uniq`; do (set -e; (set -euf pipefail; \
|
||||
cd $$dir; \
|
||||
($(GO) test ./... $(GO_TEST_OPTS) -cover -coverprofile=/tmp/profile.out -covermode=atomic -race -json | tee -a $(GOTESTJSON_FILE) 3>&1 1>&2 2>&3 | tee -a $(GOBUILDLOG_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; \
|
||||
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 `find $(GOMOD_DIR) -type f -name "go.mod" | grep -v /vendor/ | sed 's@/[^/]*$$@@' | sort | uniq`; do (set -e; (set -xe; \
|
||||
@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; \
|
||||
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 $(GOMOD_DIR)
|
||||
go doc $(first $(GOMOD_DIRS))
|
||||
|
||||
.PHONY: go.coverfunc
|
||||
go.coverfunc: go.unittest
|
||||
@@ -140,46 +144,74 @@ go.coverfunc: go.unittest
|
||||
|
||||
.PHONY: go.lint
|
||||
go.lint:
|
||||
@set -e; for dir in `find $(GOMOD_DIR) -type f -name "go.mod" | grep -v /vendor/ | sed 's@/[^/]*$$@@' | sort | uniq`; do ( set -xe; \
|
||||
@set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \
|
||||
cd $$dir; \
|
||||
golangci-lint run --verbose ./...; \
|
||||
); done
|
||||
|
||||
.PHONY: go.tidy
|
||||
go.tidy:
|
||||
@set -e; for dir in `find $(GOMOD_DIR) -type f -name "go.mod" | grep -v /vendor/ | sed 's@/[^/]*$$@@' | sort | uniq`; do ( set -xe; \
|
||||
@# 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 `find $(GOMOD_DIR) -type f -name "go.mod" | grep -v /vendor/ | sed 's@/[^/]*$$@@' | sort | uniq`; do ( set -xe; \
|
||||
@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 `find $(GOMOD_DIR) -type f -name "go.mod" | grep -v /vendor/ | sed 's@/[^/]*$$@@' | sort | uniq`; do ( set -xe; \
|
||||
@set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \
|
||||
cd $$dir; \
|
||||
$(GO) get -u ./...; \
|
||||
); done
|
||||
|
||||
.PHONY: go.bump-deps
|
||||
.PHONY: go.fmt
|
||||
go.fmt:
|
||||
if ! command -v goimports &>/dev/null; then GO111MODULE=off go get golang.org/x/tools/cmd/goimports; fi
|
||||
@set -e; for dir in `find $(GOMOD_DIR) -type f -name "go.mod" | grep -v /vendor/ | sed 's@/[^/]*$$@@' | sort | uniq`; do ( set -xe; \
|
||||
@set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \
|
||||
cd $$dir; \
|
||||
goimports -w `go list -f '{{.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
|
||||
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
|
||||
|
||||
##
|
||||
@@ -211,8 +243,8 @@ npm.publish:
|
||||
@echo -n "Do you want to npm publish? [y/N] " && read ans && \
|
||||
@if [ $${ans:-N} = y ]; then \
|
||||
set -e; for dir in $(NPM_PACKAGES); do ( set -xe; \
|
||||
cd $$dir; \
|
||||
npm publish --access=public; \
|
||||
cd $$dir; \
|
||||
npm publish --access=public; \
|
||||
); done; \
|
||||
fi
|
||||
RELEASE_STEPS += npm.publish
|
||||
@@ -222,7 +254,7 @@ endif
|
||||
## Docker
|
||||
##
|
||||
|
||||
docker_build = docker build \
|
||||
docker_build = docker build \
|
||||
--build-arg VCS_REF=`git rev-parse --short HEAD` \
|
||||
--build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
|
||||
--build-arg VERSION=`git describe --tags --always` \
|
||||
@@ -285,6 +317,11 @@ ifdef BUILD_STEPS
|
||||
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)
|
||||
@@ -318,4 +355,7 @@ help::
|
||||
@[ "$(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
|
||||
|
||||
@@ -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 {
|
||||
@@ -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),
|
||||
} {
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user