Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support proxy jump #94

Merged
merged 8 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
- name: Set a different ld for each arch # ugly but there are no working flags to explicitly set the ld to use...
run: sudo cp /usr/bin/x86_64-linux-gnu-ld /usr/bin/amd64-ld && sudo cp /tmp/aarch64-linux-musl-cross/bin/aarch64-linux-musl-gcc /usr/bin/arm64-ld
- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: '1.21.x'
- name: Run GoReleaser
Expand Down
14 changes: 7 additions & 7 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
build-linux:
strategy:
matrix:
go-version: [ '1.20.x', '1.21.x' ]
go-version: [ '1.21.x', '1.22.0-rc.1' ]
goos: [linux]
testuser: [ssh3-testuser]
testpasswd: [ssh3-testpasswd]
Expand All @@ -18,7 +18,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: '${{matrix.go-version}}'
- name: Install dependencies to generate ssh keys and certificates
Expand Down Expand Up @@ -46,7 +46,7 @@ jobs:
- name: log authorized_identities
run: cat ${{matrix.testuserhome}}/.ssh3/authorized_identities
- name: Integration tests
run: sudo -E make -e integration-tests
run: sudo -E PATH=$PATH make -e integration-tests
env:
CERT_PEM: /cert.pem
CERT_PRIV_KEY: /priv.key
Expand All @@ -62,14 +62,14 @@ jobs:
build-macos:
strategy:
matrix:
go-version: [ '1.20.x', '1.21.x' ]
go-version: [ '1.21.x', '1.22.0-rc.1' ]
goos: [darwin]
goarch: [amd64,arm64]
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: '${{matrix.go-version}}'
- name: Install dependencies
Expand All @@ -89,7 +89,7 @@ jobs:
build-other-unix:
strategy:
matrix:
go-version: [ '1.20.x', '1.21.x' ]
go-version: [ '1.21.x', '1.22.0-rc.1' ]
goos: [openbsd,freebsd,linux]
goarch: [amd64,"386",arm64,arm]
exclude:
Expand All @@ -101,7 +101,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: '${{matrix.go-version}}'
- name: Install dependencies
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
build-linux:
strategy:
matrix:
go-version: [ '1.20.x', '1.21.x' ]
go-version: [ '1.21.x', '1.22.0-rc.1' ]
goos: [linux]
testuser: [ssh3-testuser]
testpasswd: [ssh3-testpasswd]
Expand All @@ -32,7 +32,7 @@ jobs:
# - name: Install lcrypt for arm64
# run: sudo apt-get -y install libc6:arm64 libcrypt-dev:arm64
- name: Setup Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: '${{matrix.go-version}}'
- name: Lint and vet code
Expand Down
86 changes: 76 additions & 10 deletions cmd/ssh3/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,24 @@ func homedir() string {
// If non-nil, use udpConn as transport (can be used for proxy jump)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This parameter no longer exists

Suggested change
// If non-nil, use udpConn as transport (can be used for proxy jump)
// If non-nil, establishes a proxy connection towards proxyRemoteAddr first

// Otherwise, create a UDPConn from udp://host:port
func setupQUICConnection(ctx context.Context, skipHostVerification bool, keylog io.Writer, ssh3Dir string, certPool *x509.CertPool, knownHostsPath string, knownHosts ssh3.KnownHosts,
oidcConfig []*auth.OIDCConfig, options *client.Options, udpConn *net.UDPConn, tty *os.File) (quic.EarlyConnection, int) {
oidcConfig []*auth.OIDCConfig, options *client.Options, proxyRemoteAddr *net.UDPAddr, tty *os.File) (quic.EarlyConnection, int) {

remoteAddr, err := net.ResolveUDPAddr("udp", options.URLHostnamePort())
if err != nil {
log.Error().Msgf("could not resolve UDP address: %s", err)
return nil, -1
}
if udpConn == nil {
udpConn, err = net.ListenUDP("udp", nil)
var err error
remoteAddr := proxyRemoteAddr
if remoteAddr == nil {
remoteAddr, err = net.ResolveUDPAddr("udp", options.URLHostnamePort())
if err != nil {
log.Error().Msgf("could not create UDP connection: %s", err)
log.Error().Msgf("could not resolve UDP address: %s", err)
return nil, -1
}
}

udpConn, err := net.ListenUDP("udp", nil)
if err != nil {
log.Error().Msgf("could not create UDP connection: %s", err)
return nil, -1
}

tlsConf := &tls.Config{
RootCAs: certPool,
InsecureSkipVerify: skipHostVerification,
Expand Down Expand Up @@ -299,6 +302,7 @@ func mainWithStatusCode() int {
forwardSSHAgent := flag.Bool("forward-agent", false, "if set, forwards ssh agent to be used with sshv2 connections on the remote host")
forwardUDP := flag.String("forward-udp", "", "if set, take a localport/remoteip@remoteport forwarding localhost@localport towards remoteip@remoteport")
forwardTCP := flag.String("forward-tcp", "", "if set, take a localport/remoteip@remoteport forwarding localhost@localport towards remoteip@remoteport")
proxyJump := flag.String("proxy-jump", "", "if set, performs a proxy jump using the specified remote host as proxy")
flag.Parse()
args := flag.Args()

Expand Down Expand Up @@ -524,7 +528,69 @@ func mainWithStatusCode() int {
log.Error().Msgf("Could not get connection material for %s: %s", parsedUrl, err)
return -1
}
qconn, status := setupQUICConnection(ctx, *insecure, keyLog, ssh3Dir, pool, knownHostsPath, knownHosts, oidcConfig, options, nil, tty)

if *proxyJump == "" && sshConfig != nil {
*proxyJump, err = sshConfig.Get(parsedUrl.Hostname(), "UDPProxyJump")
if err != nil {
log.Error().Msgf("Could not get UDPProxyJump config value: %s", err)
return -1
}
}

var proxyAddress *net.UDPAddr
if *proxyJump != "" {
if !strings.HasPrefix(*proxyJump, "https://") {
*proxyJump = fmt.Sprintf("https://%s", *proxyJump)
}
proxyParsedUrl, err := url.Parse(*proxyJump)
if err != nil {
log.Error().Msgf("Could not parse proxy host URL %s: %s", *proxyJump, err)
return -1
}
proxyAgentClient, proxyOptions, err := getConnectionMaterialFromURL(proxyParsedUrl, sshConfig, cliAuthMethods)
if err != nil {
log.Error().Msgf("Could not get connection material for proxy %s: %s", proxyParsedUrl, err)
return -1
}
qconn, status := setupQUICConnection(ctx, *insecure, keyLog, ssh3Dir, pool, knownHostsPath, knownHosts, oidcConfig, proxyOptions, nil, tty)

if qconn == nil {
if status != 0 {
log.Error().Msgf("could not setup transport for proxy client: %s", err)
}
return status
}

roundTripper := &http3.RoundTripper{
EnableDatagrams: true,
}

proxyClient, err := client.Dial(ctx, proxyOptions, qconn, roundTripper, proxyAgentClient)
if err != nil {
log.Error().Msgf("could not establish SSH3 proxy conversation: %s", err)
return -1
}

baseAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
if err != nil {
log.Error().Msgf("Could not resolve 127.0.0.1:0: %s", err)
return -1
}
remoteAddr, err := net.ResolveUDPAddr("udp", options.URLHostnamePort())
if err != nil {
log.Error().Msgf("Could not resolve remote address %s: %s", options.URLHostnamePort(), err)
return -1
}
addr, err := proxyClient.ForwardUDP(ctx, baseAddr, remoteAddr)
if err != nil {
log.Error().Msgf("Could not forward UDP for proxy jump: %s", err)
return -1
}
proxyAddress = addr
log.Debug().Msgf("started proxy jump at %s", proxyAddress)
}

qconn, status := setupQUICConnection(ctx, *insecure, keyLog, ssh3Dir, pool, knownHostsPath, knownHosts, oidcConfig, options, proxyAddress, tty)

if qconn == nil {
if status != 0 {
Expand Down
8 changes: 4 additions & 4 deletions conversation.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type Conversation struct {
maxPacketSize uint64
defaultDatagramsQueueSize uint64
streamCreator http3.StreamCreator
messageSender util.MessageSender
messageSender util.DatagramSender
channelsManager *channelsManager
context context.Context
cancelContext context.CancelCauseFunc
Expand Down Expand Up @@ -134,7 +134,7 @@ func (c *Conversation) EstablishClientConversation(req *http.Request, roundTripp
// currently does not work for several conversations in the same QUIC connection

for {
dgram, err := qconn.ReceiveMessage(c.Context())
dgram, err := qconn.ReceiveDatagram(c.Context())
if err != nil {
if err != context.Canceled {
log.Error().Msgf("could not receive message from conn: %s", err)
Expand Down Expand Up @@ -166,7 +166,7 @@ func (c *Conversation) EstablishClientConversation(req *http.Request, roundTripp
}
}

func NewServerConversation(ctx context.Context, controlStream http3.Stream, qconn quic.Connection, messageSender util.MessageSender, maxPacketsize uint64) (*Conversation, error) {
func NewServerConversation(ctx context.Context, controlStream http3.Stream, qconn quic.Connection, messageSender util.DatagramSender, maxPacketsize uint64) (*Conversation, error) {
backgroundContext, backgroundCancelFunc := context.WithCancelCause(ctx)

tls := qconn.ConnectionState().TLS
Expand Down Expand Up @@ -290,7 +290,7 @@ func (c *Conversation) getDatagramSenderForChannel(channelID util.ChannelID) fun
buf := util.AppendVarInt(nil, uint64(c.controlStream.StreamID()))
buf = util.AppendVarInt(buf, channelID)
buf = append(buf, datagram...)
return c.messageSender.SendMessage(buf)
return c.messageSender.SendDatagram(buf)
}
}

Expand Down
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require (
github.com/kevinburke/ssh_config v1.2.0
github.com/onsi/ginkgo/v2 v2.13.0
github.com/onsi/gomega v1.29.0
github.com/quic-go/quic-go v0.38.1
github.com/quic-go/quic-go v0.40.1-0.20240102075208-1083d1fb8f98
github.com/rs/zerolog v1.31.0
golang.org/x/crypto v0.14.0
golang.org/x/oauth2 v0.13.0
Expand All @@ -18,15 +18,14 @@ require (
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.3.3 // indirect
go.uber.org/mock v0.3.0 // indirect
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.17.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ github.com/quic-go/qtls-go1-20 v0.3.3 h1:17/glZSLI9P9fDAeyCHBFSWSqJcwx1byhLwP5eU
github.com/quic-go/qtls-go1-20 v0.3.3/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/quic-go v0.38.1 h1:M36YWA5dEhEeT+slOu/SwMEucbYd0YFidxG3KlGPZaE=
github.com/quic-go/quic-go v0.38.1/go.mod h1:ijnZM7JsFIkp4cRyjxJNIzdSfCLmUMg9wdyhGmg+SN4=
github.com/quic-go/quic-go v0.40.1-0.20240102075208-1083d1fb8f98 h1:XSdekoU+UVlq/Mav+6dYyPuyy5+qzDa/TglBVi+L7Rs=
github.com/quic-go/quic-go v0.40.1-0.20240102075208-1083d1fb8f98/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
Expand All @@ -68,6 +70,8 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
Expand Down
Loading