Skip to content

Commit

Permalink
tskagent: add tests for the Server type
Browse files Browse the repository at this point in the history
Run an in-memory setec server with a test key, and verify the basic plumbing
does the needful on the other side of a client.
  • Loading branch information
creachadair committed Oct 22, 2024
1 parent ac54fe2 commit 0c1d25c
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 1 deletion.
21 changes: 21 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,36 @@ require (
github.com/creachadair/command v0.1.15
github.com/creachadair/flax v0.0.0-20240212192608-428acafa3bbe
github.com/creachadair/taskgroup v0.12.0
github.com/google/go-cmp v0.6.0
github.com/tailscale/setec v0.0.0-20240930150730-e6eb93658ed3
golang.org/x/crypto v0.28.0
)

require (
github.com/aws/aws-sdk-go-v2 v1.26.1 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.11 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.11 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.5 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.5 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.53.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 // indirect
github.com/aws/smithy-go v1.20.2 // indirect
github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect
github.com/tink-crypto/tink-go/v2 v2.1.0 // indirect
go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.26.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
tailscale.com v1.73.0-pre.0.20240822193108-696711cc17c4 // indirect
)
1 change: 1 addition & 0 deletions testdata/test.key.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOxpWzp7ASaMLWEICuHuydkXLy1ua8tqc/ljbWzQJ8Ng Dummy key for testing
2 changes: 1 addition & 1 deletion tskagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func (s *Server) Unlock(passphrase []byte) error {
defer s.μ.Unlock()
if !s.locked {
return errors.New("agent: not locked")
} else if subtle.ConstantTimeCompare(passphrase, []byte(s.passphrase)) != 0 {
} else if subtle.ConstantTimeCompare(passphrase, []byte(s.passphrase)) == 0 {
return errors.New("agent: incorrect passphrase")
}
s.locked = false
Expand Down
123 changes: 123 additions & 0 deletions tskagent_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause

package tskagent_test

import (
"context"
"crypto/ed25519"
"net"
"net/http/httptest"
"testing"

"github.com/creachadair/taskgroup"
"github.com/google/go-cmp/cmp"
"github.com/tailscale/setec/client/setec"
"github.com/tailscale/setec/setectest"
"github.com/tailscale/tskagent"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"

_ "embed"
)

//go:embed testdata/test.key
var testPrivKey string

//go:embed testdata/test.key.pub
var testPubKey []byte

func TestAgent(t *testing.T) {
const testSecret = "test/ssh-agent/key"

// Set up a fake setec server containing the test private key.
db := setectest.NewDB(t, nil)
db.MustPut(db.Superuser, testSecret, testPrivKey)
ss := setectest.NewServer(t, db, nil)
hs := httptest.NewServer(ss.Mux)
defer hs.Close()

// Set up an agent communicating with the fake setec.
ts := tskagent.NewServer(tskagent.Config{
Client: setec.Client{Server: hs.URL, DoHTTP: hs.Client().Do},
Prefix: "test/ssh-agent",
Logf: t.Logf,
})
if err := ts.Update(context.Background()); err != nil {
t.Fatalf("Initial update failed: %v", err)
}

// Parse the public key to offer.
pubKey, _, _, rest, err := ssh.ParseAuthorizedKey(testPubKey)
if err != nil {
t.Fatalf("Parse authorized key: %v", err)
} else if len(rest) != 0 {
t.Error("Extra data after authorized key")
}

// Run the agent over a pipe and make sure client calls do what they should.
cconn, sconn := net.Pipe()
cli := taskgroup.Run(func() { agent.ServeAgent(ts, sconn) })
defer func() { cconn.Close(); cli.Wait() }()

ac := agent.NewClient(cconn)

t.Run("AddDoesNotWork", func(t *testing.T) {
pk := ed25519.NewKeyFromSeed([]byte("00000000000000000000000000000000")) // throwaway
err := ac.Add(agent.AddedKey{
PrivateKey: pk,
Comment: "Nothing to see here",
})
if err == nil {
t.Errorf("Add key %v: did not get expected error", pk)
}
})

t.Run("Locking", func(t *testing.T) {
const pp = "foo"
if err := ac.Lock([]byte(pp)); err != nil {
t.Fatalf("Lock: unexpected error: %v", err)
}
if err := ac.Lock([]byte(pp)); err == nil {
t.Error("Re-lock: did not get expected error")
}
if err := ac.Unlock([]byte("wrong")); err == nil {
t.Error("Unlock wrong: did not get expected error")
}
if err := ac.Unlock([]byte(pp)); err != nil {
t.Fatalf("Unlock: unexpected error: %v", err)
}
if err := ac.Unlock([]byte(pp)); err == nil {
t.Error("Re-unlock: did not get expected error")
}
})

t.Run("List", func(t *testing.T) {
lst, err := ac.List()
if err != nil {
t.Fatalf("List: unexpected error: %v", err)
}
if diff := cmp.Diff(lst, []*agent.Key{{
Format: "ssh-ed25519",
Blob: pubKey.Marshal(),
Comment: "Dummy key for testing",
}}); diff != "" {
t.Errorf("Wrong keys (-got, +want):\n%s", diff)
}
})

t.Run("Signers", func(t *testing.T) {
lst, err := ac.Signers()
if err != nil {
t.Fatalf("Signers: unexpected error: %v", err)
}
if len(lst) != 1 {
t.Fatalf("Got %d signers, want 1", len(lst))
}
if diff := cmp.Diff(lst[0].PublicKey().Marshal(), pubKey.Marshal()); diff != "" {
t.Errorf("Wrong signer (-got, +want):\n%s", diff)
}
})

// TODO: Test Sign, Remove, RemoveAll.
}

0 comments on commit 0c1d25c

Please sign in to comment.