Skip to content

Commit

Permalink
ssh-tpm-agent: Add ssh-agent proxy functionality with -A
Browse files Browse the repository at this point in the history
Allows `ssh-tpm-agent` act as a proxy between multiple ssh-agent
compatible agents.

It will deliver all available signers, from itself and all proxies, to
the ssh server, and then pass the signing requests to all the agents.

Fixes: #9

Signed-off-by: Morten Linderud <morten@linderud.pw>
  • Loading branch information
Foxboron committed Aug 2, 2023
1 parent ed9794c commit ead045b
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 8 deletions.
46 changes: 41 additions & 5 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type Agent struct {
quit chan interface{}
wg sync.WaitGroup
keys map[string]*key.Key
agents []agent.ExtendedAgent
}

var _ agent.ExtendedAgent = &Agent{}
Expand Down Expand Up @@ -64,6 +65,16 @@ func (a *Agent) Close() error {

func (a *Agent) signers() ([]ssh.Signer, error) {
var signers []ssh.Signer

for _, agent := range a.agents {
l, err := agent.Signers()
if err != nil {
log.Printf("failed getting Signers from agent: %f", err)
continue
}
signers = append(signers, l...)
}

for _, k := range a.keys {
s, err := ssh.NewSignerFromSigner(signer.NewTPMSigner(k, a.tpm, a.pin))
if err != nil {
Expand All @@ -86,6 +97,15 @@ func (a *Agent) List() ([]*agent.Key, error) {
a.mu.Lock()
defer a.mu.Unlock()

for _, agent := range a.agents {
l, err := agent.List()
if err != nil {
log.Printf("failed getting list from agent: %v", err)
continue
}
agentKeys = append(agentKeys, l...)
}

for _, k := range a.keys {
pk, err := k.SSHPublicKey()
if err != nil {
Expand Down Expand Up @@ -115,6 +135,21 @@ func (a *Agent) SignWithFlags(key ssh.PublicKey, data []byte, flags agent.Signat
return s.(ssh.AlgorithmSigner).SignWithAlgorithm(rand.Reader, data, key.Type())
}

log.Printf("trying to sign as proxy...")
for _, agent := range a.agents {
signers, err := agent.Signers()
if err != nil {
log.Printf("failed getting signers from agent: %v", err)
continue
}
for _, s := range signers {
if !bytes.Equal(s.PublicKey().Marshal(), key.Marshal()) {
continue
}
return s.(ssh.AlgorithmSigner).SignWithAlgorithm(rand.Reader, data, key.Type())
}
}

return nil, fmt.Errorf("no private keys match the requested public key")
}

Expand Down Expand Up @@ -230,12 +265,13 @@ func LoadKeys() (map[string]*key.Key, error) {
return keys, nil
}

func NewAgent(socketPath string, tpmFetch func() transport.TPMCloser, pin func(*key.Key) ([]byte, error)) *Agent {
func NewAgent(socketPath string, agents []agent.ExtendedAgent, tpmFetch func() transport.TPMCloser, pin func(*key.Key) ([]byte, error)) *Agent {
a := &Agent{
tpm: tpmFetch,
pin: pin,
quit: make(chan interface{}),
keys: make(map[string]*key.Key),
agents: agents,
tpm: tpmFetch,
pin: pin,
quit: make(chan interface{}),
keys: make(map[string]*key.Key),
}
l, err := net.Listen("unix", socketPath)
if err != nil {
Expand Down
54 changes: 51 additions & 3 deletions cmd/ssh-tpm-agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,21 @@ import (
"flag"
"fmt"
"log"
"net"
"os"
"os/signal"
"path"
"path/filepath"
"syscall"

"github.com/foxboron/ssh-tpm-agent/agent"
"github.com/foxboron/ssh-tpm-agent/key"
"github.com/foxboron/ssh-tpm-agent/pinentry"
"github.com/foxboron/ssh-tpm-agent/utils"
"github.com/google/go-tpm/tpm2/transport"
sshagent "golang.org/x/crypto/ssh/agent"
"golang.org/x/exp/slices"
"golang.org/x/term"
)

var Version string
Expand All @@ -21,10 +28,12 @@ const usage = `Usage:
ssh-tpm-agent -l [PATH]
Options:
-l path of the UNIX socket to listen on, defaults to
$XDG_RUNTIME_DIR/ssh-tpm-agent.sock
-l PATH Path of the UNIX socket to open, defaults to
$XDG_RUNTIME_DIR/ssh-tpm-agent.sock.
--print-socket prints the socket to STDIN
-A PATH Fallback ssh-agent sockets for additional key lookup.
--print-socket Prints the socket to STDIN.
ssh-tpm-agent is a program that loads TPM sealed keys for public key
authentication. It is an ssh-agent(1) compatible program and can be used for
Expand All @@ -43,6 +52,31 @@ Example:
$ export SSH_AUTH_SOCK=$(ssh-tpm-agent --print-socket)
$ ssh git@github.com`

type SocketSet struct {
Value []string
}

func (s SocketSet) String() string {
return "set"
}

func (s *SocketSet) Set(p string) error {
if !slices.Contains(s.Value, p) {
s.Value = append(s.Value, p)
}
return nil
}

func (s SocketSet) Type() string {
return "[PATH]"
}

func NewSocketSet(allowed []string, d string) *SocketSet {
return &SocketSet{
Value: []string{},
}
}

func main() {
flag.Usage = func() {
fmt.Println(usage)
Expand All @@ -62,7 +96,10 @@ func main() {
return path.Join(dir, "ssh-tpm-agent.sock")
}()

var sockets SocketSet

flag.StringVar(&socketPath, "l", defaultSocketPath, "path of the UNIX socket to listen on")
flag.Var(&sockets, "A", "fallback ssh-agent sockets")
flag.BoolVar(&swtpmFlag, "swtpm", false, "use swtpm instead of actual tpm")
flag.BoolVar(&printSocketFlag, "print-socket", false, "print path of UNIX socket to stdout")
flag.Parse()
Expand All @@ -89,7 +126,18 @@ func main() {
}
log.Printf("Listening on %v\n", socketPath)

var agents []sshagent.ExtendedAgent

for _, s := range sockets.Value {
conn, err := net.Dial("unix", s)
if err != nil {
log.Fatal(err)
}
agents = append(agents, sshagent.NewClient(conn))
}

a := agent.NewAgent(socketPath,
agents,
// TPM Callback
func() (tpm transport.TPMCloser) {
// the agent will close the TPM after this is called
Expand Down
2 changes: 2 additions & 0 deletions cmd/ssh-tpm-agent/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/google/go-tpm/tpm2/transport"
"github.com/google/go-tpm/tpm2/transport/simulator"
"golang.org/x/crypto/ssh"
sshagent "golang.org/x/crypto/ssh/agent"
)

func newSSHKey() ssh.Signer {
Expand Down Expand Up @@ -127,6 +128,7 @@ func TestSSHAuth(t *testing.T) {
socket := path.Join(t.TempDir(), "socket")

ag := agent.NewAgent(socket,
[]sshagent.ExtendedAgent{},
// TPM Callback
func() transport.TPMCloser {
return tpm
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ require (
github.com/rs/zerolog v1.26.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b // indirect
golang.org/x/sys v0.10.0 // indirect
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b h1:r+vk0EmXNmekl0S0BascoeeoHk/L7wmaW2QF90K+kYI=
golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
Expand Down

0 comments on commit ead045b

Please sign in to comment.