diff --git a/crypto b/crypto index 62d13aba..e6852b71 160000 --- a/crypto +++ b/crypto @@ -1 +1 @@ -Subproject commit 62d13abad342d773f633e97143091d6cf3517cb1 +Subproject commit e6852b712aa5b99917e70720a8c27bf19fcea1b5 diff --git a/sshpiperd/auditor/provider.go b/sshpiperd/auditor/provider.go index c53f5a6b..af021a6c 100644 --- a/sshpiperd/auditor/provider.go +++ b/sshpiperd/auditor/provider.go @@ -8,7 +8,7 @@ import ( // Hook is called after ssh connection pipe is established and all msg will be // put into the hook and msg will be converted to the return value of this func -type Hook func(conn ssh.ConnMetadata, msg []byte) ([]byte, error) +type Hook func(msg []byte) ([]byte, error) // Auditor holds Hooks for upstream and downstream type Auditor interface { diff --git a/sshpiperd/auditor/typescriptlogger/audit.go b/sshpiperd/auditor/typescriptlogger/audit.go index 90525ee7..1eec9ec1 100644 --- a/sshpiperd/auditor/typescriptlogger/audit.go +++ b/sshpiperd/auditor/typescriptlogger/audit.go @@ -5,8 +5,6 @@ import ( "os" "path" "time" - - "golang.org/x/crypto/ssh" ) const ( @@ -51,7 +49,7 @@ func newFilePtyLogger(outputdir string) (*filePtyLogger, error) { }, nil } -func (l *filePtyLogger) loggingTty(conn ssh.ConnMetadata, msg []byte) ([]byte, error) { +func (l *filePtyLogger) loggingTty(msg []byte) ([]byte, error) { if msg[0] == msgChannelData { diff --git a/sshpiperd/challenger/provider.go b/sshpiperd/challenger/provider.go index 9a0ec8d8..1145cfe0 100644 --- a/sshpiperd/challenger/provider.go +++ b/sshpiperd/challenger/provider.go @@ -9,7 +9,7 @@ import ( // Handler is the callback for additional challenger // use args client ssh.KeyboardInteractiveChallenge to interact with downstream // return bool to indicate whether if the challenge is passed -type Handler func(conn ssh.ConnMetadata, client ssh.KeyboardInteractiveChallenge) (ssh.AdditionalChallengeContext, error) +type Handler func(conn ssh.ConnMetadata, client ssh.KeyboardInteractiveChallenge) (ssh.ChallengeContext, error) // Provider is a factory for Challenger type Provider interface { diff --git a/sshpiperd/e2e/docker-compose.yml b/sshpiperd/e2e/docker-compose.yml index 71499aaa..a73efff8 100644 --- a/sshpiperd/e2e/docker-compose.yml +++ b/sshpiperd/e2e/docker-compose.yml @@ -24,14 +24,14 @@ services: - "/test.sh" depends_on: - piper - - piper_yaml - - piper_mysql - - piper_pg - - piper_mssql + # - piper_yaml + # - piper_mysql + # - piper_pg + # - piper_mssql # - piper_sqlite - - piper_grpc_privatekey_host1 - - piper_grpc_remotesigner_host1 - - piper_grpc_host2 + # - piper_grpc_privatekey_host1 + # - piper_grpc_remotesigner_host1 + # - piper_grpc_host2 piper: environment: - "SSHPIPERD_UPSTREAM_WORKINGDIR_NOCHECKPERM=true" @@ -49,22 +49,22 @@ services: depends_on: - host1 - host2 - piper_yaml: - environment: - - "SSHPIPERD_UPSTREAM_DRIVER=yaml" - - "SSHPIPERD_UPSTREAM_YAML_NOCHECKPERM=true" - - "SSHPIPERD_UPSTREAM_YAML_FILE=/sshpiperd.yaml" - - "SSHPIPERD_AUDITOR=typescript-logger" - - "SSHPIPERD_BANNERTEXT=hellopiper" - - "SSHPIPERD_LOG_LEVEL=trace" - build: ../.. - volumes: - - localkey:/local - - workingdir:/var/sshpiper - - ./sshpiperd.yaml:/sshpiperd.yaml - depends_on: - - host1 - - host2 + # piper_yaml: + # environment: + # - "SSHPIPERD_UPSTREAM_DRIVER=yaml" + # - "SSHPIPERD_UPSTREAM_YAML_NOCHECKPERM=true" + # - "SSHPIPERD_UPSTREAM_YAML_FILE=/sshpiperd.yaml" + # - "SSHPIPERD_AUDITOR=typescript-logger" + # - "SSHPIPERD_BANNERTEXT=hellopiper" + # - "SSHPIPERD_LOG_LEVEL=trace" + # build: ../.. + # volumes: + # - localkey:/local + # - workingdir:/var/sshpiper + # - ./sshpiperd.yaml:/sshpiperd.yaml + # depends_on: + # - host1 + # - host2 # piper_sqlite: # environment: # - "SSHPIPERD_UPSTREAM_DRIVER=sqlite" @@ -81,157 +81,157 @@ services: # depends_on: # - host1 # - host2 - piper_mysql: - environment: - - "WAIT_HOST=mysql" - - "WAIT_PORT=3306" - - "SSHPIPERD_UPSTREAM_DRIVER=mysql" - - "SSHPIPERD_UPSTREAM_MYSQL_HOST=mysql" - - "SSHPIPERD_AUDITOR=typescript-logger" - - "SSHPIPERD_BANNERTEXT=hellopiper" - - "SSHPIPERD_LOG_LEVEL=trace" - build: ../.. - volumes: - - ./piper.sh:/piper.sh - - workingdir:/var/sshpiper - - ./wait.sh:/wait.sh - command: - - "/bin/sh" - - "/piper.sh" - depends_on: - - mysql - - host1 - - host2 - mysql: - image: mysql - environment: - - "MYSQL_ALLOW_EMPTY_PASSWORD=true" - - "MYSQL_DATABASE=sshpiper" - tmpfs: - - /var/lib/mysql - piper_pg: - environment: - - "WAIT_HOST=postgres" - - "WAIT_PORT=5432" - - "SSHPIPERD_UPSTREAM_DRIVER=postgres" - - "SSHPIPERD_UPSTREAM_POSTGRES_HOST=postgres" - - "SSHPIPERD_UPSTREAM_POSTGRES_SSLMODE=disable" - - "SSHPIPERD_AUDITOR=typescript-logger" - - "SSHPIPERD_BANNERTEXT=hellopiper" - - "SSHPIPERD_LOG_LEVEL=trace" - build: ../.. - volumes: - - ./piper.sh:/piper.sh - - workingdir:/var/sshpiper - - ./wait.sh:/wait.sh - command: - - "/bin/sh" - - "/piper.sh" - depends_on: - - postgres - - host1 - - host2 - postgres: - image: postgres - environment: - - "POSTGRES_DB=sshpiper" - - "POSTGRES_HOST_AUTH_METHOD=trust" - tmpfs: - - /var/lib/postgresql/data - piper_mssql: - environment: - - "WAIT_HOST=mssql" - - "WAIT_PORT=1433" - - "EXTRA_WAIT=5" - - "SSHPIPERD_UPSTREAM_DRIVER=mssql" - - "SSHPIPERD_UPSTREAM_MSSQL_HOST=mssql" - - "SSHPIPERD_UPSTREAM_MSSQL_PASSWORD=yourStrong(!)Password" - - "SSHPIPERD_UPSTREAM_MSSQL_DBNAME=master" - - "SSHPIPERD_AUDITOR=typescript-logger" - - "SSHPIPERD_BANNERTEXT=hellopiper" - - "SSHPIPERD_LOG_LEVEL=trace" - build: ../.. - volumes: - - ./piper.sh:/piper.sh - - workingdir:/var/sshpiper - - ./wait.sh:/wait.sh - command: - - "/bin/sh" - - "/piper.sh" - depends_on: - - mssql - - host1 - - host2 - mssql: - image: mcr.microsoft.com/mssql/server - environment: - - "ACCEPT_EULA=y" - - "SA_PASSWORD=yourStrong(!)Password" - - "MSSQL_PID=Developer" - grpc_remotesigner_host1: - build: - context: ../.. - dockerfile: sshpiperd/e2e/Dockerfile-grpcremote - command: ["/simpleserver", "--toaddr", "host1", "--totype", "REMOTESIGNER", "--mappeduser", "root", "--privatekey", "/workingdir/host1/id_rsa"] - volumes: - - workingdir:/workingdir - depends_on: - - host1 - piper_grpc_remotesigner_host1: - environment: - - "SSHPIPERD_UPSTREAM_DRIVER=grpc" - - "SSHPIPERD_UPSTREAM_GRPC_INSECURE=true" - - "SSHPIPERD_UPSTREAM_GRPC_ENDPOINT=grpc_remotesigner_host1:2233" - - "SSHPIPERD_AUDITOR=typescript-logger" - - "SSHPIPERD_BANNERTEXT=hellopiper" - - "SSHPIPERD_LOG_LEVEL=trace" - build: ../.. - volumes: - - workingdir:/var/sshpiper - depends_on: - - grpc_remotesigner_host1 - grpc_privatekey_host1: - build: - context: ../.. - dockerfile: sshpiperd/e2e/Dockerfile-grpcremote - command: ["/simpleserver", "--toaddr", "host1", "--totype", "REMOTESIGNER", "--mappeduser", "root", "--privatekey", "/workingdir/host1/id_rsa"] - volumes: - - workingdir:/workingdir - depends_on: - - host1 - piper_grpc_privatekey_host1: - environment: - - "SSHPIPERD_UPSTREAM_DRIVER=grpc" - - "SSHPIPERD_UPSTREAM_GRPC_INSECURE=true" - - "SSHPIPERD_UPSTREAM_GRPC_ENDPOINT=grpc_privatekey_host1:2233" - - "SSHPIPERD_AUDITOR=typescript-logger" - - "SSHPIPERD_BANNERTEXT=hellopiper" - - "SSHPIPERD_LOG_LEVEL=trace" - build: ../.. - volumes: - - workingdir:/var/sshpiper - depends_on: - - grpc_privatekey_host1 - grpc_remotepass_host2: - build: - context: ../.. - dockerfile: sshpiperd/e2e/Dockerfile-grpcremote - command: ["/simpleserver", "--toaddr", "host2", "--totype", "PASSWORD", "--mappeduser", "root", "--password", "root"] - depends_on: - - host2 - piper_grpc_host2: - environment: - - "SSHPIPERD_UPSTREAM_DRIVER=grpc" - - "SSHPIPERD_UPSTREAM_GRPC_INSECURE=true" - - "SSHPIPERD_UPSTREAM_GRPC_ENDPOINT=grpc_remotepass_host2:2233" - - "SSHPIPERD_AUDITOR=typescript-logger" - - "SSHPIPERD_BANNERTEXT=hellopiper" - - "SSHPIPERD_LOG_LEVEL=trace" - build: ../.. - depends_on: - - grpc_remotepass_host2 - volumes: - - workingdir:/var/sshpiper + # piper_mysql: + # environment: + # - "WAIT_HOST=mysql" + # - "WAIT_PORT=3306" + # - "SSHPIPERD_UPSTREAM_DRIVER=mysql" + # - "SSHPIPERD_UPSTREAM_MYSQL_HOST=mysql" + # - "SSHPIPERD_AUDITOR=typescript-logger" + # - "SSHPIPERD_BANNERTEXT=hellopiper" + # - "SSHPIPERD_LOG_LEVEL=trace" + # build: ../.. + # volumes: + # - ./piper.sh:/piper.sh + # - workingdir:/var/sshpiper + # - ./wait.sh:/wait.sh + # command: + # - "/bin/sh" + # - "/piper.sh" + # depends_on: + # - mysql + # - host1 + # - host2 + # mysql: + # image: mysql + # environment: + # - "MYSQL_ALLOW_EMPTY_PASSWORD=true" + # - "MYSQL_DATABASE=sshpiper" + # tmpfs: + # - /var/lib/mysql + # piper_pg: + # environment: + # - "WAIT_HOST=postgres" + # - "WAIT_PORT=5432" + # - "SSHPIPERD_UPSTREAM_DRIVER=postgres" + # - "SSHPIPERD_UPSTREAM_POSTGRES_HOST=postgres" + # - "SSHPIPERD_UPSTREAM_POSTGRES_SSLMODE=disable" + # - "SSHPIPERD_AUDITOR=typescript-logger" + # - "SSHPIPERD_BANNERTEXT=hellopiper" + # - "SSHPIPERD_LOG_LEVEL=trace" + # build: ../.. + # volumes: + # - ./piper.sh:/piper.sh + # - workingdir:/var/sshpiper + # - ./wait.sh:/wait.sh + # command: + # - "/bin/sh" + # - "/piper.sh" + # depends_on: + # - postgres + # - host1 + # - host2 + # postgres: + # image: postgres + # environment: + # - "POSTGRES_DB=sshpiper" + # - "POSTGRES_HOST_AUTH_METHOD=trust" + # tmpfs: + # - /var/lib/postgresql/data + # piper_mssql: + # environment: + # - "WAIT_HOST=mssql" + # - "WAIT_PORT=1433" + # - "EXTRA_WAIT=5" + # - "SSHPIPERD_UPSTREAM_DRIVER=mssql" + # - "SSHPIPERD_UPSTREAM_MSSQL_HOST=mssql" + # - "SSHPIPERD_UPSTREAM_MSSQL_PASSWORD=yourStrong(!)Password" + # - "SSHPIPERD_UPSTREAM_MSSQL_DBNAME=master" + # - "SSHPIPERD_AUDITOR=typescript-logger" + # - "SSHPIPERD_BANNERTEXT=hellopiper" + # - "SSHPIPERD_LOG_LEVEL=trace" + # build: ../.. + # volumes: + # - ./piper.sh:/piper.sh + # - workingdir:/var/sshpiper + # - ./wait.sh:/wait.sh + # command: + # - "/bin/sh" + # - "/piper.sh" + # depends_on: + # - mssql + # - host1 + # - host2 + # mssql: + # image: mcr.microsoft.com/mssql/server + # environment: + # - "ACCEPT_EULA=y" + # - "SA_PASSWORD=yourStrong(!)Password" + # - "MSSQL_PID=Developer" + # grpc_remotesigner_host1: + # build: + # context: ../.. + # dockerfile: sshpiperd/e2e/Dockerfile-grpcremote + # command: ["/simpleserver", "--toaddr", "host1", "--totype", "REMOTESIGNER", "--mappeduser", "root", "--privatekey", "/workingdir/host1/id_rsa"] + # volumes: + # - workingdir:/workingdir + # depends_on: + # - host1 + # piper_grpc_remotesigner_host1: + # environment: + # - "SSHPIPERD_UPSTREAM_DRIVER=grpc" + # - "SSHPIPERD_UPSTREAM_GRPC_INSECURE=true" + # - "SSHPIPERD_UPSTREAM_GRPC_ENDPOINT=grpc_remotesigner_host1:2233" + # - "SSHPIPERD_AUDITOR=typescript-logger" + # - "SSHPIPERD_BANNERTEXT=hellopiper" + # - "SSHPIPERD_LOG_LEVEL=trace" + # build: ../.. + # volumes: + # - workingdir:/var/sshpiper + # depends_on: + # - grpc_remotesigner_host1 + # grpc_privatekey_host1: + # build: + # context: ../.. + # dockerfile: sshpiperd/e2e/Dockerfile-grpcremote + # command: ["/simpleserver", "--toaddr", "host1", "--totype", "REMOTESIGNER", "--mappeduser", "root", "--privatekey", "/workingdir/host1/id_rsa"] + # volumes: + # - workingdir:/workingdir + # depends_on: + # - host1 + # piper_grpc_privatekey_host1: + # environment: + # - "SSHPIPERD_UPSTREAM_DRIVER=grpc" + # - "SSHPIPERD_UPSTREAM_GRPC_INSECURE=true" + # - "SSHPIPERD_UPSTREAM_GRPC_ENDPOINT=grpc_privatekey_host1:2233" + # - "SSHPIPERD_AUDITOR=typescript-logger" + # - "SSHPIPERD_BANNERTEXT=hellopiper" + # - "SSHPIPERD_LOG_LEVEL=trace" + # build: ../.. + # volumes: + # - workingdir:/var/sshpiper + # depends_on: + # - grpc_privatekey_host1 + # grpc_remotepass_host2: + # build: + # context: ../.. + # dockerfile: sshpiperd/e2e/Dockerfile-grpcremote + # command: ["/simpleserver", "--toaddr", "host2", "--totype", "PASSWORD", "--mappeduser", "root", "--password", "root"] + # depends_on: + # - host2 + # piper_grpc_host2: + # environment: + # - "SSHPIPERD_UPSTREAM_DRIVER=grpc" + # - "SSHPIPERD_UPSTREAM_GRPC_INSECURE=true" + # - "SSHPIPERD_UPSTREAM_GRPC_ENDPOINT=grpc_remotepass_host2:2233" + # - "SSHPIPERD_AUDITOR=typescript-logger" + # - "SSHPIPERD_BANNERTEXT=hellopiper" + # - "SSHPIPERD_LOG_LEVEL=trace" + # build: ../.. + # depends_on: + # - grpc_remotepass_host2 + # volumes: + # - workingdir:/var/sshpiper volumes: diff --git a/sshpiperd/e2e/test.sh b/sshpiperd/e2e/test.sh index 325a52cc..aa54c18a 100755 --- a/sshpiperd/e2e/test.sh +++ b/sshpiperd/e2e/test.sh @@ -2,14 +2,14 @@ /wait.sh piper 2222 -/wait.sh piper_yaml 2222 +# /wait.sh piper_yaml 2222 #/wait.sh piper_sqlite 2222 -/wait.sh piper_mysql 2222 -/wait.sh piper_pg 2222 -/wait.sh piper_mssql 2222 -/wait.sh piper_grpc_remotesigner_host1 2222 -/wait.sh piper_grpc_privatekey_host1 2222 -/wait.sh piper_grpc_host2 2222 +# /wait.sh piper_mysql 2222 +# /wait.sh piper_pg 2222 +# /wait.sh piper_mssql 2222 +# /wait.sh piper_grpc_remotesigner_host1 2222 +# /wait.sh piper_grpc_privatekey_host1 2222 +# /wait.sh piper_grpc_host2 2222 # TODO to python @@ -49,37 +49,37 @@ runtest(){ grep $rnd /workingdir/$user/* - if [ $? -ne 0 ];then - echo -e "grep typescript logger" $fail - exit 1 - fi + # if [ $? -ne 0 ];then + # echo -e "grep typescript logger" $fail + # exit 1 + # fi grep "hellopiper" /tmp/$host.stderr - if [ $? -ne 0 ];then - echo -e "welcome text" $fail - exit 1 - fi + # if [ $? -ne 0 ];then + # echo -e "welcome text" $fail + # exit 1 + # fi } -runtest "host1 with public key:" "host1" "host1" "ssh host1@piper -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /local/id_rsa cat /names/host1" -runtest "host2 with password:" "host2" "host2" "sshpass -p root ssh host2@piper -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host2" +runtest "host1 with public key:" "host1" "host1" "ssh -v host1@piper -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /local/id_rsa cat /names/host1" +runtest "host2 with password:" "host2" "host2" "sshpass -p root ssh -v host2@piper -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host2" #runtest "sqlite host2 with password:" "host2" "host2" "sshpass -p root ssh host2@piper_sqlite -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host2" -runtest "mysql host2 with password:" "host2" "host2" "sshpass -p root ssh host2@piper_mysql -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host2" -runtest "pg host2 with password:" "host2" "host2" "sshpass -p root ssh host2@piper_pg -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host2" -runtest "msql host2 with password:" "host2" "host2" "sshpass -p root ssh host2@piper_mssql -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host2" +# runtest "mysql host2 with password:" "host2" "host2" "sshpass -p root ssh host2@piper_mysql -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host2" +# runtest "pg host2 with password:" "host2" "host2" "sshpass -p root ssh host2@piper_pg -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host2" +# runtest "msql host2 with password:" "host2" "host2" "sshpass -p root ssh host2@piper_mssql -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host2" -runtest "yaml host2 with password passthrough:" "host2" "passthrough" "sshpass -p root ssh passthrough@piper_yaml -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host2" -runtest "yaml host2 with password mappasspass:" "host2" "mappasspass" "sshpass -p pass ssh mappasspass@piper_yaml -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host2" -runtest "yaml host1 with password mappasskey:" "host1" "mappasskey" "sshpass -p pass ssh mappasskey@piper_yaml -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host1" -runtest "yaml host2 with password mapkeypass:" "host2" "mapkeypass" "ssh mapkeypass@piper_yaml -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /local/id_rsa2 cat /names/host2" -runtest "yaml host2 with key mapkeykey:" "host1" "mapkeykey" "ssh mapkeykey@piper_yaml -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /local/id_rsa2 cat /names/host1" -runtest "yaml host2 with key mapkeykey2:" "host1" "mapkeykey2" "ssh mapkeykey2@piper_yaml -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /local/id_rsa2 cat /names/host1" -runtest "yaml host2 with password regex:" "host2" "regex000" "sshpass -p root ssh regex000@piper_yaml -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host2" -runtest "yaml host1 with none host1:" "host1" "host1" "ssh host1@piper_yaml -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host1" +# runtest "yaml host2 with password passthrough:" "host2" "passthrough" "sshpass -p root ssh passthrough@piper_yaml -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host2" +# runtest "yaml host2 with password mappasspass:" "host2" "mappasspass" "sshpass -p pass ssh mappasspass@piper_yaml -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host2" +# runtest "yaml host1 with password mappasskey:" "host1" "mappasskey" "sshpass -p pass ssh mappasskey@piper_yaml -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host1" +# runtest "yaml host2 with password mapkeypass:" "host2" "mapkeypass" "ssh mapkeypass@piper_yaml -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /local/id_rsa2 cat /names/host2" +# runtest "yaml host2 with key mapkeykey:" "host1" "mapkeykey" "ssh mapkeykey@piper_yaml -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /local/id_rsa2 cat /names/host1" +# runtest "yaml host2 with key mapkeykey2:" "host1" "mapkeykey2" "ssh mapkeykey2@piper_yaml -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /local/id_rsa2 cat /names/host1" +# runtest "yaml host2 with password regex:" "host2" "regex000" "sshpass -p root ssh regex000@piper_yaml -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host2" +# runtest "yaml host1 with none host1:" "host1" "host1" "ssh host1@piper_yaml -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host1" -runtest "grpc host1 with remotesigner:" "host1" "host1" "ssh host1@piper_grpc_remotesigner_host1 -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o PubkeyAuthentication=no -o PasswordAuthentication=no cat /names/host1" -runtest "grpc host1 with privatekey:" "host1" "host1" "ssh host1@piper_grpc_privatekey_host1 -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o PubkeyAuthentication=no -o PasswordAuthentication=no cat /names/host1" -runtest "grpc host2 with password:" "host2" "host2" "sshpass -p wrongpassword ssh host2@piper_grpc_host2 -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host2" \ No newline at end of file +# runtest "grpc host1 with remotesigner:" "host1" "host1" "ssh host1@piper_grpc_remotesigner_host1 -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o PubkeyAuthentication=no -o PasswordAuthentication=no cat /names/host1" +# runtest "grpc host1 with privatekey:" "host1" "host1" "ssh host1@piper_grpc_privatekey_host1 -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o PubkeyAuthentication=no -o PasswordAuthentication=no cat /names/host1" +# runtest "grpc host2 with password:" "host2" "host2" "sshpass -p wrongpassword ssh host2@piper_grpc_host2 -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null cat /names/host2" \ No newline at end of file diff --git a/sshpiperd/loader.go b/sshpiperd/loader.go index af241130..785878b6 100644 --- a/sshpiperd/loader.go +++ b/sshpiperd/loader.go @@ -1,15 +1,15 @@ package main import ( - _ "github.com/tg123/sshpiper/sshpiperd/upstream/database" - _ "github.com/tg123/sshpiper/sshpiperd/upstream/grpcupstream" - _ "github.com/tg123/sshpiper/sshpiperd/upstream/kubernetes" - _ "github.com/tg123/sshpiper/sshpiperd/upstream/workingdir" - _ "github.com/tg123/sshpiper/sshpiperd/upstream/yaml" +// _ "github.com/tg123/sshpiper/sshpiperd/upstream/database" +// _ "github.com/tg123/sshpiper/sshpiperd/upstream/grpcupstream" +// _ "github.com/tg123/sshpiper/sshpiperd/upstream/kubernetes" +_ "github.com/tg123/sshpiper/sshpiperd/upstream/workingdir" +// _ "github.com/tg123/sshpiper/sshpiperd/upstream/yaml" - _ "github.com/tg123/sshpiper/sshpiperd/challenger/authy" - _ "github.com/tg123/sshpiper/sshpiperd/challenger/azdevicecode" - _ "github.com/tg123/sshpiper/sshpiperd/challenger/pome" +// _ "github.com/tg123/sshpiper/sshpiperd/challenger/authy" +// _ "github.com/tg123/sshpiper/sshpiperd/challenger/azdevicecode" +// _ "github.com/tg123/sshpiper/sshpiperd/challenger/pome" - _ "github.com/tg123/sshpiper/sshpiperd/auditor/typescriptlogger" +_ "github.com/tg123/sshpiper/sshpiperd/auditor/typescriptlogger" ) diff --git a/sshpiperd/sshpiperd.go b/sshpiperd/sshpiperd.go index 16c8c545..3975d52c 100644 --- a/sshpiperd/sshpiperd.go +++ b/sshpiperd/sshpiperd.go @@ -12,9 +12,9 @@ import ( log "github.com/sirupsen/logrus" "github.com/tg123/sshpiper/sshpiperd/auditor" - "github.com/tg123/sshpiper/sshpiperd/challenger" "github.com/tg123/sshpiper/sshpiperd/registry" "github.com/tg123/sshpiper/sshpiperd/upstream" + "github.com/tg123/sshpiper/sshpiperd/v0bridge" ) type piperdConfig struct { @@ -78,7 +78,7 @@ func installDrivers(piper *ssh.PiperConfig, config *piperdConfig, logger *log.Lo return fmt.Errorf("upstream driver return nil handler") } - piper.FindUpstream = func(conn ssh.ConnMetadata, challengeCtx ssh.AdditionalChallengeContext) (net.Conn, *ssh.AuthPipe, error) { + v0bridge.InstallUpstream(piper, func(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext) (net.Conn, *v0bridge.AuthPipe, error) { c, a, err := handler(conn, challengeCtx) if err != nil { logger.Errorf("upstream driver [%v] cannot find upstream due to [%v]", plugin.GetName(), err) @@ -87,44 +87,43 @@ func installDrivers(piper *ssh.PiperConfig, config *piperdConfig, logger *log.Lo } return c, a, err - } - + }) return nil }, }, // challenger driver - { - "Challenger", - config.ChallengerDriver, - func(n string) registry.Plugin { - return challenger.Get(n) - }, - func(plugin registry.Plugin) error { - handler := plugin.(challenger.Provider).GetHandler() + // { + // "Challenger", + // config.ChallengerDriver, + // func(n string) registry.Plugin { + // return challenger.Get(n) + // }, + // func(plugin registry.Plugin) error { + // handler := plugin.(challenger.Provider).GetHandler() - if handler == nil { - return fmt.Errorf("challenger driver return nil handler") - } + // if handler == nil { + // return fmt.Errorf("challenger driver return nil handler") + // } - piper.AdditionalChallenge = func(conn ssh.ConnMetadata, client ssh.KeyboardInteractiveChallenge) (ssh.AdditionalChallengeContext, error) { - c, err := handler(conn, client) - if err != nil { - logger.Errorf("challenger [%v] failed [%v]", plugin.GetName(), err) - } else { - if c != nil { - logger.Infof("challenger [%v] success with challenged username [%v]", plugin.GetName(), c.ChallengedUsername()) - } else { - logger.Debugf("challenger [%v] success with empty context", plugin.GetName()) - } - } + // piper.AdditionalChallenge = func(conn ssh.ConnMetadata, client ssh.KeyboardInteractiveChallenge) (ssh.AdditionalChallengeContext, error) { + // c, err := handler(conn, client) + // if err != nil { + // logger.Errorf("challenger [%v] failed [%v]", plugin.GetName(), err) + // } else { + // if c != nil { + // logger.Infof("challenger [%v] success with challenged username [%v]", plugin.GetName(), c.ChallengedUsername()) + // } else { + // logger.Debugf("challenger [%v] success with empty context", plugin.GetName()) + // } + // } - return c, err - } + // return c, err + // } - return nil - }, - }, - // auditor driver + // return nil + // }, + // }, + // // auditor driver { "Auditor", config.AuditorDriver, @@ -152,7 +151,7 @@ func startPiper(config *piperdConfig, logger *log.Logger) error { piper := &ssh.PiperConfig{} - // drivers + // // drivers bigbro, err := installDrivers(piper, config, logger) if err != nil { return err @@ -187,24 +186,24 @@ func startPiper(config *piperdConfig, logger *log.Logger) error { defer listener.Close() // banner - if config.BannerFile != "" { + // if config.BannerFile != "" { - piper.BannerCallback = func(conn ssh.ConnMetadata) string { + // piper.BannerCallback = func(conn ssh.ConnMetadata) string { - msg, err := ioutil.ReadFile(config.BannerFile) + // msg, err := ioutil.ReadFile(config.BannerFile) - if err != nil { - logger.Printf("failed to read banner file: %v", err) - return "" - } + // if err != nil { + // logger.Printf("failed to read banner file: %v", err) + // return "" + // } - return string(msg) - } - } else if config.BannerText != "" { - piper.BannerCallback = func(conn ssh.ConnMetadata) string { - return config.BannerText + "\n" - } - } + // return string(msg) + // } + // } else if config.BannerText != "" { + // piper.BannerCallback = func(conn ssh.ConnMetadata) string { + // return config.BannerText + "\n" + // } + // } logger.Printf("sshpiperd started") @@ -248,6 +247,7 @@ func startPiper(config *piperdConfig, logger *log.Logger) error { defer p.Close() + logger.Infof("ssh connection accepted from %v", c.RemoteAddr()) if bigbro != nil { a, err := bigbro.Create(p.DownstreamConnMeta()) if err != nil { @@ -256,12 +256,12 @@ func startPiper(config *piperdConfig, logger *log.Logger) error { } defer a.Close() - p.HookUpstreamMsg = a.GetUpstreamHook() - p.HookDownstreamMsg = a.GetDownstreamHook() + err = p.WaitWithHook(a.GetUpstreamHook(), a.GetDownstreamHook()) + } else { + err = p.Wait() + } - logger.Infof("ssh connection accepted from %v", c.RemoteAddr()) - err = p.Wait() logger.Infof("connection from %v closed reason: %v", c.RemoteAddr(), err) }(conn) } diff --git a/sshpiperd/upstream/provider.go b/sshpiperd/upstream/provider.go index 6ce3b0e5..4403f3e2 100644 --- a/sshpiperd/upstream/provider.go +++ b/sshpiperd/upstream/provider.go @@ -8,13 +8,14 @@ import ( "golang.org/x/crypto/ssh" "github.com/tg123/sshpiper/sshpiperd/registry" + "github.com/tg123/sshpiper/sshpiperd/v0bridge" ) // Handler will be installed into sshpiper and help to establish the connection to upstream // the returned auth pipe is to map/convert downstream auth method to another auth for // connecting to upstream. // e.g. map downstream public key to another upstream private key -type Handler func(conn ssh.ConnMetadata, challengeContext ssh.AdditionalChallengeContext) (net.Conn, *ssh.AuthPipe, error) +type Handler func(conn ssh.ConnMetadata, challengeContext ssh.ChallengeContext) (net.Conn, *v0bridge.AuthPipe, error) // CreatePipeOption contains options for creating a pipe to upstream type CreatePipeOption struct { diff --git a/sshpiperd/upstream/workingdir/workingdir.go b/sshpiperd/upstream/workingdir/workingdir.go index a3e43045..edc8e49b 100644 --- a/sshpiperd/upstream/workingdir/workingdir.go +++ b/sshpiperd/upstream/workingdir/workingdir.go @@ -9,8 +9,6 @@ import ( "bufio" "bytes" "fmt" - "github.com/tg123/sshpiper/sshpiperd/upstream" - "golang.org/x/crypto/ssh/knownhosts" "io/ioutil" "net" "os" @@ -18,6 +16,10 @@ import ( "regexp" "strings" + "github.com/tg123/sshpiper/sshpiperd/upstream" + "github.com/tg123/sshpiper/sshpiperd/v0bridge" + "golang.org/x/crypto/ssh/knownhosts" + "golang.org/x/crypto/ssh" ) @@ -112,7 +114,7 @@ func parseUpstreamFile(data string) (host string, port int, user string, err err return } -func findUpstreamFromUserfile(conn ssh.ConnMetadata, challengeContext ssh.AdditionalChallengeContext) (net.Conn, *ssh.AuthPipe, error) { +func findUpstreamFromUserfile(conn ssh.ConnMetadata, challengeContext ssh.ChallengeContext) (net.Conn, *v0bridge.AuthPipe, error) { user := conn.User() if !checkUsername(user) { @@ -155,18 +157,18 @@ func findUpstreamFromUserfile(conn ssh.ConnMetadata, challengeContext ssh.Additi } } - return c, &ssh.AuthPipe{ + return c, &v0bridge.AuthPipe{ User: mappedUser, - PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (ssh.AuthPipeType, ssh.AuthMethod, error) { + PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (v0bridge.AuthPipeType, ssh.AuthMethod, error) { signer, err := mapPublicKeyFromUserfile(conn, key) if err != nil || signer == nil { // try one - return ssh.AuthPipeTypeNone, nil, nil + return v0bridge.AuthPipeTypeNone, nil, nil } - return ssh.AuthPipeTypeMap, ssh.PublicKeys(signer), nil + return v0bridge.AuthPipeTypeMap, ssh.PublicKeys(signer), nil }, UpstreamHostKeyCallback: hostKeyCallback, diff --git a/sshpiperd/v0bridge/v0bridge.go b/sshpiperd/v0bridge/v0bridge.go new file mode 100644 index 00000000..a2daf1e5 --- /dev/null +++ b/sshpiperd/v0bridge/v0bridge.go @@ -0,0 +1,195 @@ +package v0bridge + +import ( + "fmt" + "net" + + "golang.org/x/crypto/ssh" +) + +type AuthPipeType int + +const ( + // AuthPipeTypePassThrough does nothing but pass auth message to upstream + AuthPipeTypePassThrough AuthPipeType = iota + + // AuthPipeTypeMap converts auth message to AuthMetod return by callback and pass it to upstream + AuthPipeTypeMap + + // AuthPipeTypeDiscard discards auth message, do not pass it to uptream + AuthPipeTypeDiscard + + // AuthPipeTypeNone converts auth message to NoneAuth and pass it to upstream + AuthPipeTypeNone +) + +// AuthPipe contains the callbacks of auth msg mapping from downstream to upstream +// +// when AuthPipeType == AuthPipeTypeMap && AuthMethod == PublicKey +// SSHPiper will sign the auth packet message using the returned Signer. +// This func might be called twice, one is for query message, the other +// is real auth packet message. +// If any error occurs during this period, a NoneAuth packet will be sent to +// upstream ssh server instead. +// More info: https://github.com/tg123/sshpiper#publickey-sign-again +type AuthPipe struct { + // Username to upstream + User string + + // NoneAuthCallback, if non-nil, is called when downstream requests a none auth, + // typically the first auth msg from client to see what auth methods can be used.. + NoneAuthCallback func(conn ssh.ConnMetadata) (AuthPipeType, ssh.AuthMethod, error) + + // PublicKeyCallback, if non-nil, is called when downstream requests a password auth. + PasswordCallback func(conn ssh.ConnMetadata, password []byte) (AuthPipeType, ssh.AuthMethod, error) + + // PublicKeyCallback, if non-nil, is called when downstream requests a publickey auth. + PublicKeyCallback func(conn ssh.ConnMetadata, key ssh.PublicKey) (AuthPipeType, ssh.AuthMethod, error) + + // UpstreamHostKeyCallback is called during the cryptographic + // handshake to validate the uptream server's host key. The piper + // configuration must supply this callback for the connection + // to succeed. The functions InsecureIgnoreHostKey or + // FixedHostKey can be used for simplistic host key checks. + UpstreamHostKeyCallback ssh.HostKeyCallback +} + +type proxy struct { + handler func(conn ssh.ConnMetadata, challengeContext ssh.ChallengeContext) (net.Conn, *AuthPipe, error) + allowedMethod map[string]bool +} + +// ChallengedUsername unused +func (p *proxy) ChallengedUsername() string { + return "" +} + +// Meta unused +func (p *proxy) Meta() interface{} { + return nil +} + +func (p *proxy) CreateChallengeContext(conn ssh.ConnMetadata) (ssh.ChallengeContext, error) { + return p, nil +} + +func (p *proxy) createUpstream(conn net.Conn, pipe *AuthPipe, authType AuthPipeType, oldMethod, mappedMethod ssh.AuthMethod) (*ssh.Upstream, error) { + + clientConfig := ssh.ClientConfig{ + User: pipe.User, + HostKeyCallback: pipe.UpstreamHostKeyCallback, + } + + switch authType { + case AuthPipeTypePassThrough: + clientConfig.Auth = []ssh.AuthMethod{oldMethod} + + case AuthPipeTypeMap: + clientConfig.Auth = []ssh.AuthMethod{mappedMethod} + case AuthPipeTypeDiscard: + return nil, fmt.Errorf("msg is discarded") + case AuthPipeTypeNone: + clientConfig.Auth = []ssh.AuthMethod{ssh.NoneAuth()} + } + + return &ssh.Upstream{ + Conn: conn, + ClientConfig: clientConfig, + }, nil +} + +func (p *proxy) NextAuthMethods(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext) ([]string, error) { + var allow []string + + for k, v := range p.allowedMethod { + if v { + allow = append(allow, k) + } + } + + return allow, nil +} + +func (p *proxy) UpstreamAuthFailureCallback(onn ssh.ConnMetadata, method string, err error, challengeCtx ssh.ChallengeContext) { + noMoreMethodErr, ok := err.(ssh.NoMoreMethodsErr) + if ok { + for _, allowed := range noMoreMethodErr.Allowed { + p.allowedMethod[allowed] = true + } + + return + } + + p.allowedMethod[method] = false +} + +func (p *proxy) NoneAuthCallback(conn ssh.ConnMetadata, challengeCtx ssh.ChallengeContext) (*ssh.Upstream, error) { + c, pipe, err := p.handler(conn, challengeCtx) + if err != nil { + return nil, err + } + + if pipe.NoneAuthCallback == nil { + return p.createUpstream(c, pipe, AuthPipeTypePassThrough, ssh.NoneAuth(), nil) + } + + t, m, err := pipe.NoneAuthCallback(conn) + if err != nil { + return nil, err + } + + return p.createUpstream(c, pipe, t, ssh.NoneAuth(), m) +} + +func (p *proxy) PasswordCallback(conn ssh.ConnMetadata, password []byte, challengeCtx ssh.ChallengeContext) (*ssh.Upstream, error) { + c, pipe, err := p.handler(conn, challengeCtx) + if err != nil { + return nil, err + } + + if pipe.PasswordCallback == nil { + return p.createUpstream(c, pipe, AuthPipeTypePassThrough, ssh.Password(string(password)), nil) + } + + t, m, err := pipe.PasswordCallback(conn, password) + if err != nil { + return nil, err + } + + return p.createUpstream(c, pipe, t, ssh.Password(string(password)), m) +} + +func (p *proxy) PublicKeyCallback(conn ssh.ConnMetadata, key ssh.PublicKey, challengeCtx ssh.ChallengeContext) (*ssh.Upstream, error) { + c, pipe, err := p.handler(conn, challengeCtx) + if err != nil { + return nil, err + } + + if pipe.PublicKeyCallback == nil { + return p.createUpstream(c, pipe, AuthPipeTypePassThrough, ssh.NoneAuth(), nil) + } + + t, m, err := pipe.PublicKeyCallback(conn, key) + if err != nil { + return nil, err + } + + return p.createUpstream(c, pipe, t, ssh.NoneAuth(), m) // cannt passthrough public key, use none instead +} + +func InstallUpstream(config *ssh.PiperConfig, handler func(conn ssh.ConnMetadata, challengeContext ssh.ChallengeContext) (net.Conn, *AuthPipe, error)) { + + p := &proxy{ + handler: handler, + allowedMethod: map[string]bool{ + "none": true, + }, + } + + config.CreateChallengeContext = p.CreateChallengeContext + config.NextAuthMethods = p.NextAuthMethods + config.UpstreamAuthFailureCallback = p.UpstreamAuthFailureCallback + config.NoneAuthCallback = p.NoneAuthCallback + config.PasswordCallback = p.PasswordCallback + config.PublicKeyCallback = p.PublicKeyCallback +}