diff --git a/.circleci/config.yml b/.circleci/config.yml index 36beac6..2b90d3d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,7 @@ defaults: &defaults working_directory: /go/src/moul.io/sshportal docker: - - image: circleci/golang:1.15.2 + - image: circleci/golang:1.16.2 environment: GO111MODULE: "on" diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index cd66899..10a92c9 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,25 +1,15 @@ - +### 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?**: - - +Any screenshot to share? +`sshportal --version`? +`ssh sshportal info`? +OS/Go version? +... diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index a1dda50..5b1e9d3 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1 @@ - - -**What this PR does / why we need it**: - -**Which issue this PR fixes**: fixes #xxx, fixes #xxx... - -**Special notes for your reviewer**: + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..96d0d69 --- /dev/null +++ b/.github/dependabot.yml @@ -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 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index caf9c1e..7d3f3a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: lint - uses: golangci/golangci-lint-action@v0.1.7 + uses: golangci/golangci-lint-action@v2.5.1 with: version: v1.28 github-token: ${{ secrets.GITHUB_TOKEN }} @@ -30,7 +30,7 @@ jobs: strategy: matrix: golang: - - 1.15.0 + - 1.16.x steps: - uses: actions/checkout@v2 - name: Install Go @@ -46,14 +46,14 @@ jobs: strategy: matrix: golang: - - 1.15.0 + - 1.16.x steps: - uses: actions/checkout@v2 - name: Install Go uses: actions/setup-go@v2 with: go-version: ${{ matrix.golang }} - - uses: actions/cache@v1 + - uses: actions/cache@v2.1.4 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ matrix.golang }}-${{ hashFiles('**/go.sum') }} @@ -67,18 +67,17 @@ jobs: strategy: matrix: golang: - - 1.11 - - 1.12 - - 1.13 - - 1.14 - - 1.15.0 + - 1.13.x + - 1.14.x + - 1.15.x + - 1.16.x steps: - uses: actions/checkout@v2 - name: Install Go uses: actions/setup-go@v2 with: go-version: ${{ matrix.golang }} - - uses: actions/cache@v1 + - uses: actions/cache@v2.1.4 with: path: ~/go/pkg/mod key: ${{ runner.os }}-go-${{ matrix.golang }}-${{ hashFiles('**/go.sum') }} diff --git a/AUTHORS b/AUTHORS index 78e221c..0899dd6 100644 --- a/AUTHORS +++ b/AUTHORS @@ -5,18 +5,23 @@ ahh Alen Masic Alexander Turner bozzo +dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> fossabot +ImgBotApp +Jason Wessel Jean-Louis Férey jerard@alfa-safety.fr Jess Jonathan Lestrelin Julien Dessaux +Konstantin Bakaras Manfred Touron <94029+moul@users.noreply.github.com> Manfred Touron Manuel Manuel Sabban Manuel Sabban Mathieu Pasquet +matteyeux Mikael Rapp MitaliBo moul-bot @@ -24,6 +29,7 @@ Nelly Asher NocFlame Quentin Perez Renovate Bot +Sergey Yashchuk Shawn Wang Valentin Daviot valentin.daviot diff --git a/Dockerfile b/Dockerfile index 8e3ae5c..fa586e5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # build -FROM golang:1.15.2 as builder +FROM golang:1.16.2 as builder ENV GO111MODULE=on WORKDIR /go/src/moul.io/sshportal COPY go.mod go.sum ./ diff --git a/LICENSE b/LICENSE index 6250c03..86ee754 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2017 Manfred Touron + Copyright 2017-2021 Manfred Touron Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 9c1cb6f..fae2771 100644 --- a/README.md +++ b/README.md @@ -366,7 +366,7 @@ user update [-h] [--name=] [--email=] [--set-admin] [--unset-admin # usergroup management usergroup help -hostgroup create [-h] [--name=] [--comment=] +usergroup create [-h] [--name=] [--comment=] usergroup inspect [-h] USERGROUP... usergroup ls [-h] [--latest] [--quiet] usergroup rm [-h] USERGROUP... diff --git a/depaware.txt b/depaware.txt new file mode 100644 index 0000000..77986fa --- /dev/null +++ b/depaware.txt @@ -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+ diff --git a/go.mod b/go.mod index c327f8c..1c95cb7 100644 --- a/go.mod +++ b/go.mod @@ -2,33 +2,27 @@ 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/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/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef github.com/docker/docker v1.13.1 github.com/dustin/go-humanize v1.0.0 - github.com/gliderlabs/ssh v0.3.0 + github.com/gliderlabs/ssh v0.3.2 github.com/go-sql-driver/mysql v1.5.0 - github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect - github.com/jinzhu/gorm v1.9.15 + 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/mattn/go-colorable v0.1.8 // 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/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-20201112155050-0c6587e931a9 + golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58 gopkg.in/gormigrate.v1 v1.6.0 - moul.io/srand v1.4.0 + moul.io/srand v1.6.1 ) go 1.14 diff --git a/go.sum b/go.sum index a6dea70..5b4a26f 100644 --- a/go.sum +++ b/go.sum @@ -4,17 +4,14 @@ 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-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg= +github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/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= @@ -24,8 +21,8 @@ github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4 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= @@ -34,12 +31,11 @@ github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZ 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 +51,21 @@ 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/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-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/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= @@ -82,43 +78,68 @@ github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0 github.com/russross/blackfriday/v2 v2.0.1/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-20201112155050-0c6587e931a9 h1:umElSU9WZirRdgu2yFHY0ayQkEnKiOC1TtM3fWXFnoU= +golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +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/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/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/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= diff --git a/internal/tools/tools.go b/internal/tools/tools.go new file mode 100644 index 0000000..1a9e9cc --- /dev/null +++ b/internal/tools/tools.go @@ -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" +) diff --git a/main.go b/main.go index d80e63f..734fc80 100644 --- a/main.go +++ b/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]) diff --git a/pkg/bastion/dbinit.go b/pkg/bastion/dbinit.go index 3cab6f7..09a88ad 100644 --- a/pkg/bastion/dbinit.go +++ b/pkg/bastion/dbinit.go @@ -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 } diff --git a/pkg/bastion/shell.go b/pkg/bastion/shell.go index 6abcabb..f7c9a31 100644 --- a/pkg/bastion/shell.go +++ b/pkg/bastion/shell.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "net/url" "os" "regexp" @@ -24,6 +25,7 @@ import ( "golang.org/x/crypto/ssh/terminal" "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) } diff --git a/pkg/bastion/telnet.go b/pkg/bastion/telnet.go index 30e9f40..40b2863 100644 --- a/pkg/bastion/telnet.go +++ b/pkg/bastion/telnet.go @@ -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) diff --git a/pkg/crypto/crypto.go b/pkg/crypto/crypto.go index 7924ecc..c5b3593 100644 --- a/pkg/crypto/crypto.go +++ b/pkg/crypto/crypto.go @@ -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", diff --git a/pkg/dbmodels/dbmodels.go b/pkg/dbmodels/dbmodels.go index 0197ab3..ca0c793 100644 --- a/pkg/dbmodels/dbmodels.go +++ b/pkg/dbmodels/dbmodels.go @@ -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 } diff --git a/pkg/utils/emailvalidator.go b/pkg/utils/emailvalidator.go new file mode 100644 index 0000000..6a5923d --- /dev/null +++ b/pkg/utils/emailvalidator.go @@ -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) +} diff --git a/pkg/utils/emailvalidator_test.go b/pkg/utils/emailvalidator_test.go new file mode 100644 index 0000000..2b5e652 --- /dev/null +++ b/pkg/utils/emailvalidator_test.go @@ -0,0 +1,22 @@ +package utils + +import ( + "testing" +) + +func TestValidateEmail(t *testing.T) { + + goodEmail := "goodemail@email.com" + badEmail := "b@2323.22" + + got := ValidateEmail(goodEmail) + if got == false { + t.Errorf("got1= %v; want true", got) + } + + got2 := ValidateEmail(badEmail) + if got2 == false { + t.Errorf("got2= %v; want false", got2) + } + +} diff --git a/rules.mk b/rules.mk index 1d90565..cc1fe61 100644 --- a/rules.mk +++ b/rules.mk @@ -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,10 +112,11 @@ 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; \ + @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 | tee -a $(GOTESTJSON_FILE) 3>&1 1>&2 2>&3 | tee -a $(GOBUILDLOG_FILE); \ + (($(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; \ @@ -120,7 +124,7 @@ ifeq ($(CI),true) @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 \ @@ -132,7 +136,7 @@ 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 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 ## @@ -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 diff --git a/server.go b/server.go index bbc05ca..b688930 100644 --- a/server.go +++ b/server.go @@ -61,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 { diff --git a/testserver.go b/testserver.go index c2edaaf..9fd6233 100644 --- a/testserver.go +++ b/testserver.go @@ -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()