From e765b32cf38fe4b5581d734f48aa74eb9717a6af Mon Sep 17 00:00:00 2001 From: Will Boyce Date: Thu, 1 Jun 2017 11:29:07 +0100 Subject: [PATCH 1/5] Do not pass env vars from client to shell by default Connects to #17 Change-Type: minor --- resin/README.md | 4 +++- resin/main.go | 4 +++- sshproxy.go | 32 ++++++++++++++++++-------------- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/resin/README.md b/resin/README.md index 3e86252..4512350 100644 --- a/resin/README.md +++ b/resin/README.md @@ -14,7 +14,7 @@ work dir. The following config file formats are supported: * [HCL](https://github.com/hashicorp/hcl) (`sshproxy.hcl`) * [Java .properties](https://en.wikipedia.org/wiki/.properties) (`sshproxy.properties`) -There are a total of 8 configuration options. With the exception of `dir` +There are a total of 9 configuration options. With the exception of `dir` they can all be set via commandline, environment or config file. | Name | Commandline | Environment | Config | @@ -27,9 +27,11 @@ they can all be set via commandline, environment or config file. | Shell | `--shell` `-s` | `SSHPROXY_SHELL` | `shell` | | Auth Failed Banner | `--auth-failed-banner` `-b` | `SSHPROXY_AUTH_FAILED_BANNER` | `auth-failed-banner` | | Max Auth Tries | `--max-auth-tries` `-m` | `SSHPROXY_MAX_AUTH_TRIES` | `max-auth-tries` | +| Allow Env | `--allow-env` `-E` | `SSHPROXY_ALLOW_ENV` | `allow-env` | ``` Usage of sshproxy: + -E, --allow-env Pass environment from client to shell (default: false) (warning: security implications) -H, --apihost string Resin API Host (default "api.resin.io") -K, --apikey string Resin API Key (required) -P, --apiport string Resin API Port (default "443") diff --git a/resin/main.go b/resin/main.go index 51e7e56..2958d76 100644 --- a/resin/main.go +++ b/resin/main.go @@ -151,6 +151,7 @@ func init() { pflag.CommandLine.StringP("shell", "s", "shell.sh", "Path to shell to execute post-authentication") pflag.CommandLine.StringP("auth-failed-banner", "b", "", "Path to template displayed after failed authentication") pflag.CommandLine.IntP("max-auth-tries", "m", 0, "Maximum number of authentication attempts per connection (default 0; unlimited)") + pflag.CommandLine.BoolP("allow-env", "E", false, "Pass environment from client to shell (default: false) (warning: security implications)") viper.BindPFlags(pflag.CommandLine) viper.SetConfigName("sshproxy") @@ -163,6 +164,7 @@ func init() { viper.BindEnv("shell") viper.BindEnv("auth-failed-banner", "SSHPROXY_AUTH_FAILED_BANNER") viper.BindEnv("max-auth-tries", "SSHPROXY_MAX_AUTH_TRIES") + viper.BindEnv("allow-env", "SSHPROXY_ALLOW_ENV") } func main() { @@ -216,5 +218,5 @@ func main() { sshConfig.KeyboardInteractiveCallback = auth.keyboardInteractiveCallback } - sshproxy.New(viper.GetString("dir"), viper.GetString("shell"), sshConfig).Listen(viper.GetString("port")) + sshproxy.New(viper.GetString("dir"), viper.GetString("shell"), viper.GetBool("allow-env"), sshConfig).Listen(viper.GetString("port")) } diff --git a/sshproxy.go b/sshproxy.go index ca886c8..3e28599 100644 --- a/sshproxy.go +++ b/sshproxy.go @@ -36,20 +36,22 @@ import ( // Server holds server specific configuration data. type Server struct { - keyDir string - config *ssh.ServerConfig - shell string + keyDir string + config *ssh.ServerConfig + shell string + passEnv bool } // New takes a directory to generate/store server keys, a path to the shell // and an ssh.ServerConfig. If no ServerConfig is provided, then // ServerConfig.NoClientAuth is set to true. ed25519, rsa, ecdsa and dsa // keys are loaded, and generated if they do not exist. Returns a new Server. -func New(keyDir, shell string, sshConfig *ssh.ServerConfig) *Server { +func New(keyDir, shell string, passEnv bool, sshConfig *ssh.ServerConfig) *Server { s := &Server{ - keyDir: keyDir, - config: sshConfig, - shell: shell, + keyDir: keyDir, + config: sshConfig, + shell: shell, + passEnv: passEnv, } if s.config == nil { s.config = &ssh.ServerConfig{ @@ -160,13 +162,15 @@ func (s *Server) handleRequests(reqs <-chan *ssh.Request, channel ssh.Channel, c log.Printf("New SSH request '%s' from %s", req.Type, conn.RemoteAddr()) switch req.Type { case "env": - // append client env to the command environment - keyLen := req.Payload[3] - valLen := req.Payload[keyLen+7] - key := string(req.Payload[4 : keyLen+4]) - val := string(req.Payload[keyLen+8 : keyLen+valLen+8]) - env = append(env, fmt.Sprintf("%s=%s", key, val)) - req.Reply(true, nil) + if s.passEnv { + // append client env to the command environment + keyLen := req.Payload[3] + valLen := req.Payload[keyLen+7] + key := string(req.Payload[4: keyLen+4]) + val := string(req.Payload[keyLen+8: keyLen+valLen+8]) + env = append(env, fmt.Sprintf("%s=%s", key, val)) + } + req.Reply(s.passEnv, nil) case "pty-req": // client has requested a PTY wantsPty = true From 804dac248fe3e58fd3826c2504b9c1dd1175080b Mon Sep 17 00:00:00 2001 From: Will Boyce Date: Thu, 1 Jun 2017 15:52:08 +0100 Subject: [PATCH 2/5] lint fixes Change-Type: patch --- resin/main.go | 9 ++++----- sshproxy.go | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/resin/main.go b/resin/main.go index 2958d76..9df4101 100644 --- a/resin/main.go +++ b/resin/main.go @@ -114,9 +114,8 @@ func (a *authHandler) keyboardInteractiveCallback(meta ssh.ConnMetadata, client delete(a.rejectedSessions, sessionKey) } return nil, errors.New("Unauthorised") - } else { - a.rejectedSessions[sessionKey] = 1 } + a.rejectedSessions[sessionKey] = 1 // fetch user's keys... keys, err := a.getUserKeys(meta.User()) @@ -188,7 +187,7 @@ func main() { } // if paths are relative, prepend with dir and verify files exist - fix_path_check_exists := func(key string) { + fixPathCheckExists := func(key string) { if viper.GetString(key)[0] != '/' { viper.Set(key, path.Join(viper.GetString("dir"), viper.GetString(key))) } @@ -197,9 +196,9 @@ func main() { os.Exit(2) } } - fix_path_check_exists("shell") + fixPathCheckExists("shell") if viper.GetString("auth-failed-banner") != "" { - fix_path_check_exists("auth-failed-banner") + fixPathCheckExists("auth-failed-banner") } apiURL := fmt.Sprintf("https://%s:%d", viper.GetString("apihost"), viper.GetInt("apiport")) diff --git a/sshproxy.go b/sshproxy.go index 3e28599..84b7d5a 100644 --- a/sshproxy.go +++ b/sshproxy.go @@ -166,8 +166,8 @@ func (s *Server) handleRequests(reqs <-chan *ssh.Request, channel ssh.Channel, c // append client env to the command environment keyLen := req.Payload[3] valLen := req.Payload[keyLen+7] - key := string(req.Payload[4: keyLen+4]) - val := string(req.Payload[keyLen+8: keyLen+valLen+8]) + key := string(req.Payload[4 : keyLen+4]) + val := string(req.Payload[keyLen+8 : keyLen+valLen+8]) env = append(env, fmt.Sprintf("%s=%s", key, val)) } req.Reply(s.passEnv, nil) From cb3d0e37ceb4cdb722b5e5de92f5c0bfdfbc5da9 Mon Sep 17 00:00:00 2001 From: Will Boyce Date: Thu, 1 Jun 2017 15:53:09 +0100 Subject: [PATCH 3/5] update Makefile and add circleci config Connects to #16 Change-Type: patch --- Makefile | 92 ++++++++++++++++++++++++++++++++---------------------- circle.yml | 24 ++++++++++++++ 2 files changed, 78 insertions(+), 38 deletions(-) create mode 100644 circle.yml diff --git a/Makefile b/Makefile index 0089ecf..b61b919 100644 --- a/Makefile +++ b/Makefile @@ -1,48 +1,64 @@ -PKG := resin -VERSION := $(shell git describe --abbrev=0 --tags --dirty) -EXECUTABLE := sshproxy +USERNAME ?= resin-io +PROJECT ?= sshproxy +PACKAGE ?= resin +EXECUTABLE ?= sshproxy +VERSION := $(shell git describe --abbrev=0 --tags) +BUILD_PLATFORMS ?= darwin/amd64 freebsd/amd64 linux/arm linux/arm64 linux/amd64 openbsd/amd64 netbsd/amd64 +SHASUM ?= sha256sum all: bin/$(EXECUTABLE) -bin/$(EXECUTABLE): - go build -o "$@" ./$(PKG) +dep: + go get -v ./... + go get github.com/mitchellh/gox -release: release/$(EXECUTABLE)-$(VERSION)_linux_arm5.tar.bz2 \ - release/$(EXECUTABLE)-$(VERSION)_linux_arm7.tar.bz2 \ - release/$(EXECUTABLE)-$(VERSION)_darwin_386.tar.bz2 \ - release/$(EXECUTABLE)-$(VERSION)_linux_386.tar.bz2 \ - release/$(EXECUTABLE)-$(VERSION)_darwin_amd64.tar.bz2 \ - release/$(EXECUTABLE)-$(VERSION)_freebsd_amd64.tar.bz2 \ - release/$(EXECUTABLE)-$(VERSION)_linux_amd64.tar.bz2 +lint-dep: dep + go get github.com/golang/lint/golint + go get golang.org/x/tools/cmd/goimports -release-sign: release - for f in release/*.tar.bz2; do gpg --armor --detach-sign $$f; done +lint: lint-dep + goimports -d . + gofmt -e -l -s . + golint -set_exit_status ./... + go tool vet . + +test-dep: dep + go test -i -v ./... + +test: test-dep + go test -v ./... + +release: $(addsuffix .tar.gz,$(addprefix build/$(EXECUTABLE)-$(VERSION)_,$(subst /,_,$(BUILD_PLATFORMS)))) +release: $(addsuffix .tar.gz.sha256,$(addprefix build/$(EXECUTABLE)-$(VERSION)_,$(subst /,_,$(BUILD_PLATFORMS)))) + +upload-dep: + go get github.com/aktau/github-release + +upload: lint test upload-dep +ifndef GITHUB_TOKEN + $(error GITHUB_TOKEN is undefined) +endif + git describe --exact-match --tags >/dev/null + + git log --format='* %s' --grep=$(VERSION) --invert-grep --no-merges $(shell git describe --tag --abbrev=0 $(VERSION)^)...$(VERSION) + $(foreach FILE, $(addsuffix .tar.gz,$(addprefix build/$(EXECUTABLE)-$(VERSION)_,$(subst /,_,$(BUILD_PLATFORMS)))), \ + echo github-release upload -u $(USERNAME) -r $(EXECUTABLE) -t $(VERSION) -n $(notdir $(FILE)) -f $(FILE) && \ + echo github-release upload -u $(USERNAME) -r $(EXECUTABLE) -t $(VERSION) -n $(notdir $(addsuffix .sha256,$(FILE))) -f $(addsuffix .sha256,$(FILE)) ;) clean: - rm -vrf bin/* build/* release/* - -# arm -build/linux_arm5/$(EXECUTABLE): - GOARM=5 GOARCH=arm GOOS=linux go build -o "$@" ./$(PKG) -build/linux_arm7/$(EXECUTABLE): - GOARM=7 GOARCH=arm GOOS=linux go build -o "$@" ./$(PKG) - -# 386 -build/darwin_386/$(EXECUTABLE): - GOARCH=386 GOOS=darwin go build -o "$@" ./$(PKG) -build/linux_386/$(EXECUTABLE): - GOARCH=386 GOOS=linux go build -o "$@" ./$(PKG) - -# amd64 -build/darwin_amd64/$(EXECUTABLE): - GOARCH=amd64 GOOS=darwin go build -o "$@" ./$(PKG) -build/freebsd_amd64/$(EXECUTABLE): - GOARCH=amd64 GOOS=freebsd go build -o "$@" ./$(PKG) -build/linux_amd64/$(EXECUTABLE): - GOARCH=amd64 GOOS=linux go build -o "$@" ./$(PKG) + rm -vrf bin/* build/* +# binary +bin/$(EXECUTABLE): dep + go build -o "$@" -v ./$(PACKAGE) +# release binaries +build/%/$(EXECUTABLE): dep + gox -parallel=1 -osarch=$(subst _,/,$(subst build/,,$(@:/$(EXECUTABLE)=))) -output="build/{{.OS}}_{{.Arch}}/$(EXECUTABLE)" ./$(PACKAGE) # compressed artifacts -release/$(EXECUTABLE)-$(VERSION)_%.tar.bz2: build/%/$(EXECUTABLE) - tar -jcvf "$@" -C "`dirname $<`" $(EXECUTABLE) +build/$(EXECUTABLE)-$(VERSION)_%.tar.gz: build/%/$(EXECUTABLE) + tar -zcf "$@" -C "$(dir $<)" $(EXECUTABLE) +# signed artifacts +%.sha256: % + cd $(dir $<) && $(SHASUM) $(notdir $<) > $(addsuffix .sha256,$(notdir $<)) -.PHONY: clean release-sign +.PHONY: dep lint-dep lint test-dep test release upload-dep upload clean diff --git a/circle.yml b/circle.yml new file mode 100644 index 0000000..1dad843 --- /dev/null +++ b/circle.yml @@ -0,0 +1,24 @@ +--- +version: 2 +jobs: + build: + working_directory: /go/src/github.com/resin-io/sshproxy + docker: + - image: golang:1.8.3-stretch + steps: + - checkout + - run: + name: Install Dependencies + command: make dep + - run: + name: Lint Code + command: make lint + - run: + name: Run Tests + command: make test + - run: + name: Build Releases + command: make -j release + - deploy: + name: Upload Releases + command: if git describe --exact-match --tags 2>/dev/null; then make upload; fi From 2929e916ae189c96bd2848c5ef20059098384475 Mon Sep 17 00:00:00 2001 From: Will Boyce Date: Mon, 5 Jun 2017 13:03:10 +0100 Subject: [PATCH 4/5] Update README Change-Type: patch --- resin/README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/resin/README.md b/resin/README.md index 4512350..2865986 100644 --- a/resin/README.md +++ b/resin/README.md @@ -42,9 +42,9 @@ Usage of sshproxy: -s, --shell string Path to shell to execute post-authentication (default "shell.sh") ``` -## Unauth Template +## Auth Failed Banner/Template -The 'unauth template' is a template rendered and displayed to the user after failed authentication. It should be a +The 'auth failed banner' is a template rendered and displayed to the user after failed authentication. It should be a [Go template](https://golang.org/pkg/text/template/) has two available properties; `.user` and `.fingerprints`. ## Example Usage @@ -69,3 +69,10 @@ SSH_ORIGINAL_COMMAND=some command LC_CTYPE=en_GB.UTF-8 _=/usr/bin/env ``` + +### Building + +The `Makefile` in the project root contains all necessary rules for linting, testing and building sshproxy packages. +Building via a Docker image can be achieved with, for example: +`docker run --rm -v $PWD:/go/src/github.com/resin-io/sshproxy golang make -C /go/src/github.com/resin-io/sshproxy lint test release`. + From 7d53d029415f98a4ab7d9cb45c4078fa986cea81 Mon Sep 17 00:00:00 2001 From: "resin-io-versionbot[bot]" Date: Wed, 7 Jun 2017 12:19:20 +0000 Subject: [PATCH 5/5] v1.2.0 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4422eb6..56a8fb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY! This project adheres to [Semantic Versioning](http://semver.org/). +## v1.2.0 - 2017-06-07 + +* Update README [Will Boyce] +* Update Makefile and add circleci config [Will Boyce] +* Lint fixes [Will Boyce] +* Do not pass env vars from client to shell by default [Will Boyce] + ## v1.1.2 - 2017-05-18 * Also use ecdsa and dsa host keys [Will Boyce]