Add Docker healthcheck helper

This commit is contained in:
Manfred Touron 2018-01-01 10:41:21 +01:00
parent d6ea80dab1
commit 6494e69632
6 changed files with 74 additions and 8 deletions

View file

@ -9,6 +9,8 @@ Changes:
* Fix connection failure when sending too many environment variables (fix [#22](https://github.com/moul/sshportal/issues/22))
* Fix panic when entering empty command (fix [#13](https://github.com/moul/sshportal/issues/13))
* Add `config backup --ignore-events` option
* Add `sshportal healthcheck` cli command
* Add [Docker Healthcheck](https://docs.docker.com/engine/reference/builder/#healthcheck) helper
## v1.6.0 (2017-12-12)

View file

@ -5,8 +5,9 @@ WORKDIR /go/src/github.com/moul/sshportal
RUN make _docker_install
# minimal runtime
FROM scratch
FROM alpine
COPY --from=builder /go/bin/sshportal /bin/sshportal
ENTRYPOINT ["/bin/sshportal"]
CMD ["server"]
EXPOSE 2222
HEALTHCHECK --interval=10s --timeout=10s CMD /bin/sshportal healthcheck

View file

@ -43,7 +43,7 @@ Jump host/Jump server without the jump, a.k.a Transparent SSH bastion
* [`scp`](https://linux.die.net/man/1/scp) support
* [`rsync`](https://linux.die.net/man/1/rsync) support
* Git support (can be used to easily use multiple user keys on GitHub, or access your own firewalled gitlab server)
* Do not require any SSH client modification or custom `.ssh/config`, works with every tested SSH programming libraries and every tested SSH
* Do not require any SSH client modification or custom `.ssh/config`, works with every tested SSH programming libraries and every tested SSH
## (Known) limitations
@ -54,7 +54,7 @@ Jump host/Jump server without the jump, a.k.a Transparent SSH bastion
Start the server
```console
$ sshportal
$ sshportal server
2017/11/13 10:58:35 Admin user created, use the user 'invite:BpLnfgDsc2WD8F2q' to associate a public key with this account
2017/11/13 10:58:35 SSH Server accepting connections on :2222
```
@ -346,6 +346,15 @@ $
the `healtcheck` user can be changed using the `healthcheck-user` option.
---
Alternatively, you can run the built-in healthcheck helper (requiring no ssh client nor ssh key):
```console
$ sshportal healthcheck --addr=localhost:2222; echo $?
$ 0
```
## Scaling
`sshportal` is stateless but relies on a database to store configuration and logs.
@ -383,4 +392,4 @@ This is totally experimental for now, so please file issues to let me know what
## License
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmoul%2Fsshportal.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmoul%2Fsshportal?ref=badge_large)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmoul%2Fsshportal.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmoul%2Fsshportal?ref=badge_large)

View file

@ -1,6 +1,11 @@
run:
docker-compose down
docker-compose up -d sshportal
@echo "Waiting for sshportal to be healthy"
@sleep 3
docker-compose build client
docker-compose run client /integration/_client.sh
docker-compose down

View file

@ -11,9 +11,6 @@ Host sshportal
HostName sshportal
EOF
#ping -c 1 sshportal
sleep 3
set -x
# login

54
main.go
View file

@ -1,10 +1,12 @@
package main
import (
"bytes"
"errors"
"fmt"
"log"
"math/rand"
"net"
"os"
"path"
"strings"
@ -86,6 +88,15 @@ func main() {
Usage: "Encrypt sensitive data in database (length: 16, 24 or 32)",
},
},
}, {
Name: "healthcheck",
Action: healthcheck,
Flags: []cli.Flag{
cli.StringFlag{
Name: "addr, a",
Value: "localhost:2222",
},
},
},
}
if err := app.Run(os.Args); err != nil {
@ -122,7 +133,9 @@ func server(c *cli.Context) error {
// ssh server
ssh.Handle(func(s ssh.Session) {
currentUser := s.Context().Value(userContextKey).(User)
log.Printf("New connection: sshUser=%q remote=%q local=%q command=%q dbUser=id:%q,email:%s", s.User(), s.RemoteAddr(), s.LocalAddr(), s.Command(), currentUser.ID, currentUser.Email)
if s.User() != "healthcheck" {
log.Printf("New connection: sshUser=%q remote=%q local=%q command=%q dbUser=id:%q,email:%s", s.User(), s.RemoteAddr(), s.LocalAddr(), s.Command(), currentUser.ID, currentUser.Email)
}
if err := s.Context().Value(errorContextKey); err != nil {
fmt.Fprintf(s, "error: %v\n", err)
@ -208,6 +221,11 @@ func server(c *cli.Context) error {
})
opts := []ssh.Option{}
opts = append(opts, ssh.PasswordAuth(func(ctx ssh.Context, pass string) bool {
ctx.SetValue(userContextKey, User{})
return ctx.User() == "healthcheck"
}))
opts = append(opts, ssh.PublicKeyAuth(func(ctx ssh.Context, key ssh.PublicKey) bool {
var (
userKey UserKey
@ -278,3 +296,37 @@ func server(c *cli.Context) error {
log.Printf("info: SSH Server accepting connections on %s", c.String("bind-address"))
return ssh.ListenAndServe(c.String("bind-address"), nil, opts...)
}
// perform a healthcheck test without requiring an ssh client or an ssh key (used for Docker's HEALTHCHECK)
func healthcheck(c *cli.Context) error {
config := gossh.ClientConfig{
User: "healthcheck",
HostKeyCallback: func(hostname string, remote net.Addr, key gossh.PublicKey) error { return nil },
Auth: []gossh.AuthMethod{gossh.Password("healthcheck")},
}
client, err := gossh.Dial("tcp", c.String("addr"), &config)
if err != nil {
return err
}
session, err := client.NewSession()
if err != nil {
return err
}
defer func() {
if err := session.Close(); err != nil {
panic(err)
}
}()
var b bytes.Buffer
session.Stdout = &b
if err := session.Run(""); err != nil {
return err
}
stdout := strings.TrimSpace(b.String())
if stdout != "OK" {
return fmt.Errorf("invalid stdout: %q expected 'OK'", stdout)
}
return nil
}