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

rsa2 fix #139

Merged
merged 9 commits into from
Dec 4, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion Dockerfile.uptermd
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ FROM golang:alpine as builder
WORKDIR $GOPATH/src/github.com/owenthereal/upterm
COPY . .
ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64
RUN go install -mod=vendor ./cmd/uptermd/...
RUN go install ./cmd/uptermd/...

# Prepare for image
FROM alpine:latest
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ proto:
docker run -v $(CURDIR)/host/api:/defs namely/protoc-all -f api.proto -l go --go-source-relative -o .

build:
go build -o build/upterm -mod=vendor ./cmd/upterm
go build -o build/uptermd -mod=vendor ./cmd/uptermd
go build -o build/upterm ./cmd/upterm
go build -o build/uptermd ./cmd/uptermd

install:
go install ./cmd/...
Expand All @@ -29,7 +29,7 @@ docker_push: docker_build

GO_TEST_FLAGS ?= ""
test:
go test ./... -timeout=120s -coverprofile=c.out -covermode=atomic -mod=vendor -count=1 -race -v $(GO_TEST_FLAGS)
go test ./... -timeout=120s -coverprofile=c.out -covermode=atomic -count=1 -race -v $(GO_TEST_FLAGS)

vet:
docker run --rm -v $$(pwd):/app:z -w /app golangci/golangci-lint:latest golangci-lint run -v
24 changes: 0 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,30 +172,6 @@ A community Upterm server is running at `uptermd.upterm.dev` and `upterm` points

![upterm flowchart](https://raw.githubusercontent.com/owenthereal/upterm/gh-pages/upterm-flowchart.svg?sanitize=true)

## A note about RSA keys

Since openssh 8.8 (2021-09-26), the host algorithm type SHA-1 (`ssh-rsa`) was retired in favor of SHA-2 (`rsa-sha2-256` or `rsa-sha2-512`) ([release note](https://www.openssh.com/txt/release-8.8)).
Unfortunately, due to a shortcoming in Go’s `x/crypto/ssh` package, `upterm` does not completely support SSH clients using SHA-2 keys: only the old SHA-1 ones will work.

You can check your `openssh` version with the following:

```console
$ ssh -V
```

If you are not sure what type of keys you have, you can check with the following:

```console
$ find ~/.ssh/id_*.pub -exec ssh-keygen -l -f {} \;
```

Until this is sorted out, you are recommended to use key with another algorithm, e.g. Ed25519.

If you're curious about the inner workings of this problem, have a look at:

- https://github.com/owenthereal/upterm/issues/93#issuecomment-1045387517
- https://github.com/golang/go/issues/49952

## Deploy Uptermd

### Kubernetes
Expand Down
3 changes: 1 addition & 2 deletions cmd/upterm/command/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package command
import (
"context"
"fmt"
"io/ioutil"
"net"
"net/url"
"os"
Expand Down Expand Up @@ -129,7 +128,7 @@ func currentRunE(c *cobra.Command, args []string) error {
func listSessions(dir string) ([][]string, error) {
result := make([][]string, 0)

files, err := ioutil.ReadDir(dir)
files, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
Expand Down
61 changes: 47 additions & 14 deletions cmd/upterm/command/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ import (
"strings"
"time"

ggh "github.com/google/go-github/github"
ggh "github.com/google/go-github/v48/github"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/tj/go-update"
"github.com/tj/go-update/progress"
"github.com/tj/go-update/stores/github"
"github.com/tj/go/term"
)

Expand All @@ -39,11 +38,9 @@ func upgradeRunE(c *cobra.Command, args []string) error {
m := &update.Manager{
Command: "upterm",
Store: &store{
Store: &github.Store{
Owner: "owenthereal",
Repo: "upterm",
Version: Version,
},
Owner: "owenthereal",
Repo: "upterm",
Version: Version,
},
}

Expand Down Expand Up @@ -118,7 +115,28 @@ func (r *release) FindTarballWithVersion(os, arch string) *update.Asset {
}

type store struct {
*github.Store
Owner string
Repo string
Version string
}

func (s *store) GetRelease(version string) (*update.Release, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

gh := ggh.NewClient(nil)

r, res, err := gh.Repositories.GetReleaseByTag(ctx, s.Owner, s.Repo, "v"+version)

if res.StatusCode == 404 {
return nil, update.ErrNotFound
}

if err != nil {
return nil, err
}

return githubRelease(r), nil
}

func (s *store) LatestReleases() ([]*update.Release, error) {
Expand All @@ -133,11 +151,26 @@ func (s *store) LatestReleases() ([]*update.Release, error) {
}

return []*update.Release{
{
Version: r.GetTagName(),
Notes: r.GetBody(),
PublishedAt: r.GetPublishedAt().Time,
URL: r.GetURL(),
},
githubRelease(r),
}, nil
}

func githubRelease(r *ggh.RepositoryRelease) *update.Release {
out := &update.Release{
Version: r.GetTagName(),
Notes: r.GetBody(),
PublishedAt: r.GetPublishedAt().Time,
URL: r.GetURL(),
}

for _, a := range r.Assets {
out.Assets = append(out.Assets, &update.Asset{
Name: a.GetName(),
Size: a.GetSize(),
URL: a.GetBrowserDownloadURL(),
Downloads: a.GetDownloadCount(),
})
}

return out
}
51 changes: 25 additions & 26 deletions ftests/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package ftests

import (
"context"
"io/ioutil"
"os"
"path/filepath"
"strings"
Expand All @@ -15,7 +14,7 @@ import (
log "github.com/sirupsen/logrus"
)

func testHostNoAuthorizedKeyAnyClientJoin(t *testing.T, hostURL, nodeAddr string) {
func testHostNoAuthorizedKeyAnyClientJoin(t *testing.T, hostShareURL, hostNodeAddr, clientJoinURL string) {
adminSockDir, err := newAdminSocketDir()
if err != nil {
t.Fatal(err)
Expand All @@ -29,24 +28,24 @@ func testHostNoAuthorizedKeyAnyClientJoin(t *testing.T, hostURL, nodeAddr string
PrivateKeys: []string{HostPrivateKey},
AdminSocketFile: adminSocketFile,
}
if err := h.Share(hostURL); err != nil {
if err := h.Share(hostShareURL); err != nil {
t.Fatal(err)
}
defer h.Close()

// verify admin server
session := getAndVerifySession(t, adminSocketFile, hostURL, nodeAddr)
session := getAndVerifySession(t, adminSocketFile, hostShareURL, hostNodeAddr)

c := &Client{
PrivateKeys: []string{HostPrivateKey}, // use the wrong key
}

if err := c.Join(session, hostURL); err != nil {
if err := c.Join(session, clientJoinURL); err != nil {
t.Fatal(err)
}
}

func testClientAuthorizedKeyNotMatching(t *testing.T, hostURL, nodeAddr string) {
func testClientAuthorizedKeyNotMatching(t *testing.T, hostShareURL, hostNodeAddr, clientJoinURL string) {
adminSockDir, err := newAdminSocketDir()
if err != nil {
t.Fatal(err)
Expand All @@ -61,19 +60,19 @@ func testClientAuthorizedKeyNotMatching(t *testing.T, hostURL, nodeAddr string)
AdminSocketFile: adminSocketFile,
PermittedClientPublicKey: ClientPublicKeyContent,
}
if err := h.Share(hostURL); err != nil {
if err := h.Share(hostShareURL); err != nil {
t.Fatal(err)
}
defer h.Close()

// verify admin server
session := getAndVerifySession(t, adminSocketFile, hostURL, nodeAddr)
session := getAndVerifySession(t, adminSocketFile, hostShareURL, hostNodeAddr)

c := &Client{
PrivateKeys: []string{HostPrivateKey}, // use the wrong key
}

err = c.Join(session, hostURL)
err = c.Join(session, clientJoinURL)

// Unfortunately there is no explicit error to the client.
// SSH handshake should fail with the connection closed.
Expand All @@ -83,7 +82,7 @@ func testClientAuthorizedKeyNotMatching(t *testing.T, hostURL, nodeAddr string)
}
}

func testClientNonExistingSession(t *testing.T, hostURL, nodeAddr string) {
func testClientNonExistingSession(t *testing.T, hostShareURL, hostNodeAddr, clientJoinURL string) {
adminSockDir, err := newAdminSocketDir()
if err != nil {
t.Fatal(err)
Expand All @@ -98,13 +97,13 @@ func testClientNonExistingSession(t *testing.T, hostURL, nodeAddr string) {
AdminSocketFile: adminSocketFile,
PermittedClientPublicKey: ClientPublicKeyContent,
}
if err := h.Share(hostURL); err != nil {
if err := h.Share(hostShareURL); err != nil {
t.Fatal(err)
}
defer h.Close()

// verify admin server
session := getAndVerifySession(t, adminSocketFile, hostURL, nodeAddr)
session := getAndVerifySession(t, adminSocketFile, hostShareURL, hostNodeAddr)

// verify input/output
hostInputCh, hostOutputCh := h.InputOutput()
Expand All @@ -122,7 +121,7 @@ func testClientNonExistingSession(t *testing.T, hostURL, nodeAddr string) {
PrivateKeys: []string{ClientPrivateKey},
}
session.SessionId = "not-existance" // set session ID to non-existance
err = c.Join(session, hostURL)
err = c.Join(session, clientJoinURL)

// Unfortunately there is no explicit error to the client.
// But ssh handshake fails with the connection closed
Expand All @@ -131,7 +130,7 @@ func testClientNonExistingSession(t *testing.T, hostURL, nodeAddr string) {
}
}

func testClientAttachHostWithSameCommand(t *testing.T, hostURL, nodeAddr string) {
func testClientAttachHostWithSameCommand(t *testing.T, hostShareURL, hostNodeAddr, clientJoinURL string) {
adminSockDir, err := newAdminSocketDir()
if err != nil {
t.Fatal(err)
Expand All @@ -146,13 +145,13 @@ func testClientAttachHostWithSameCommand(t *testing.T, hostURL, nodeAddr string)
AdminSocketFile: adminSocketFile,
PermittedClientPublicKey: ClientPublicKeyContent,
}
if err := h.Share(hostURL); err != nil {
if err := h.Share(hostShareURL); err != nil {
t.Fatal(err)
}
defer h.Close()

// verify admin server
session := getAndVerifySession(t, adminSocketFile, hostURL, nodeAddr)
session := getAndVerifySession(t, adminSocketFile, hostShareURL, hostNodeAddr)

// verify input/output
hostInputCh, hostOutputCh := h.InputOutput()
Expand All @@ -161,7 +160,7 @@ func testClientAttachHostWithSameCommand(t *testing.T, hostURL, nodeAddr string)
c := &Client{
PrivateKeys: []string{ClientPrivateKey},
}
if err := c.Join(session, hostURL); err != nil {
if err := c.Join(session, clientJoinURL); err != nil {
t.Fatal(err)
}

Expand Down Expand Up @@ -204,7 +203,7 @@ func testClientAttachHostWithSameCommand(t *testing.T, hostURL, nodeAddr string)
}
}

func testClientAttachHostWithDifferentCommand(t *testing.T, hostURL string, nodeAddr string) {
func testClientAttachHostWithDifferentCommand(t *testing.T, hostShareURL string, hostNodeAddr, clientJoinURL string) {
adminSockDir, err := newAdminSocketDir()
if err != nil {
t.Fatal(err)
Expand All @@ -220,13 +219,13 @@ func testClientAttachHostWithDifferentCommand(t *testing.T, hostURL string, node
AdminSocketFile: adminSocketFile,
PermittedClientPublicKey: ClientPublicKeyContent,
}
if err := h.Share(hostURL); err != nil {
if err := h.Share(hostShareURL); err != nil {
t.Fatal(err)
}
defer h.Close()

// verify admin server
session := getAndVerifySession(t, adminSocketFile, hostURL, nodeAddr)
session := getAndVerifySession(t, adminSocketFile, hostShareURL, hostNodeAddr)

// verify input/output
hostInputCh, hostOutputCh := h.InputOutput()
Expand All @@ -243,7 +242,7 @@ func testClientAttachHostWithDifferentCommand(t *testing.T, hostURL string, node
c := &Client{
PrivateKeys: []string{ClientPrivateKey},
}
if err := c.Join(session, hostURL); err != nil {
if err := c.Join(session, clientJoinURL); err != nil {
t.Fatal(err)
}

Expand All @@ -269,7 +268,7 @@ func testClientAttachHostWithDifferentCommand(t *testing.T, hostURL string, node
}
}

func testClientAttachReadOnly(t *testing.T, hostURL, nodeAddr string) {
func testClientAttachReadOnly(t *testing.T, hostShareURL, hostNodeAddr, clientJoinURL string) {
adminSockDir, err := newAdminSocketDir()
if err != nil {
t.Fatal(err)
Expand All @@ -285,13 +284,13 @@ func testClientAttachReadOnly(t *testing.T, hostURL, nodeAddr string) {
PermittedClientPublicKey: ClientPublicKeyContent,
ReadOnly: true,
}
if err := h.Share(hostURL); err != nil {
if err := h.Share(hostShareURL); err != nil {
t.Fatal(err)
}
defer h.Close()

// verify admin server
session := getAndVerifySession(t, adminSocketFile, hostURL, nodeAddr)
session := getAndVerifySession(t, adminSocketFile, hostShareURL, hostNodeAddr)

// verify input/output
hostInputCh, hostOutputCh := h.InputOutput()
Expand All @@ -300,7 +299,7 @@ func testClientAttachReadOnly(t *testing.T, hostURL, nodeAddr string) {
c := &Client{
PrivateKeys: []string{ClientPrivateKey},
}
if err := c.Join(session, hostURL); err != nil {
if err := c.Join(session, clientJoinURL); err != nil {
t.Fatal(err)
}

Expand Down Expand Up @@ -372,5 +371,5 @@ func checkSessionPayload(t *testing.T, sess *api.GetSessionResponse, wantHostURL
}

func newAdminSocketDir() (string, error) {
return ioutil.TempDir("", "upterm")
return os.MkdirTemp("", "upterm")
}
Loading