Skip to content

Commit 3d7d21c

Browse files
authored
fix(build): fix ssh auth sock validation (#6599)
Signed-off-by: Yaroslav Pershin <62902094+iapershin@users.noreply.github.com>
1 parent 9b9463e commit 3d7d21c

File tree

2 files changed

+170
-10
lines changed

2 files changed

+170
-10
lines changed

pkg/ssh_agent/ssh_agent.go

+72-10
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package ssh_agent
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"io"
78
"io/ioutil"
@@ -31,6 +32,11 @@ var (
3132
tmpSockPath string
3233
)
3334

35+
var (
36+
ErrSocketPathTooLong = errors.New("system ssh-agent socket path length exceeds the limit")
37+
ErrSocketPathEmpty = errors.New("system ssh-agent socket path is empty")
38+
)
39+
3440
func setupProcessSSHAgent(sshAuthSock string) error {
3541
SSHAuthSock = sshAuthSock
3642
return os.Setenv(SSHAuthSockEnv, SSHAuthSock)
@@ -134,8 +140,15 @@ func Init(ctx context.Context, userKeys []string) error {
134140
}
135141

136142
systemAgentSock := os.Getenv(SSHAuthSockEnv)
137-
systemAgentSockExists, _ := util.FileExists(systemAgentSock)
138-
if systemAgentSock != "" && systemAgentSockExists {
143+
validSystemAgentSock, err := validateAgentSock(systemAgentSock)
144+
if err != nil {
145+
if errors.Is(err, ErrSocketPathEmpty) {
146+
logboek.Context(ctx).Debug().LogF("System ssh agent not found\n")
147+
} else {
148+
return err
149+
}
150+
}
151+
if systemAgentSock != "" && validSystemAgentSock {
139152
SSHAuthSock = systemAgentSock
140153
logboek.Context(ctx).Info().LogF("Using system ssh-agent: %s\n", systemAgentSock)
141154
return nil
@@ -198,18 +211,17 @@ func runSSHAgentWithKeys(ctx context.Context, keys []sshKey) (string, error) {
198211
}
199212

200213
func runSSHAgent(ctx context.Context) (string, error) {
201-
var sockPath string
202-
// darwin does not support path more than 108 characters
203-
if runtime.GOOS == "darwin" {
204-
// TODO(iapershin): needs refactoring. since user can change tmpDir we want to prevent it from setting path more than 108 chars
205-
sockPath = filepath.Join(os.TempDir(), "werf", "Listeners", uuid.NewString())
206-
} else {
207-
sockPath = filepath.Join(werf.GetTmpDir(), "werf-ssh-agent", uuid.NewString())
214+
sockPath := filepath.Join(werf.GetTmpDir(), "werf-ssh-agent", uuid.NewString())
215+
err := validateSockPathLength(sockPath)
216+
if err != nil {
217+
logboek.Context(ctx).Warn().LogF("WARNING: unable to use unix sock path %s: %s\n", sockPath, err)
218+
sockPath = fallbackToDefaultUnix()
219+
logboek.Context(ctx).Warn().LogF("WARNING: fallback to %s\n", sockPath)
208220
}
209221

210222
tmpSockPath = sockPath
211223

212-
err := os.MkdirAll(filepath.Dir(sockPath), os.ModePerm)
224+
err = os.MkdirAll(filepath.Dir(sockPath), os.ModePerm)
213225
if err != nil {
214226
return "", err
215227
}
@@ -310,3 +322,53 @@ func parsePrivateSSHKey(cfg sshKeyConfig) (sshKey, error) {
310322

311323
return sshKey{Config: cfg, PrivateKey: privateKey}, nil
312324
}
325+
326+
func validateAgentSock(sock string) (bool, error) {
327+
if sock == "" {
328+
return false, ErrSocketPathEmpty
329+
}
330+
331+
err := validateSockPathLength(sock)
332+
if err != nil {
333+
return false, fmt.Errorf("unable to use system ssh sock '%s': %w", sock, err)
334+
}
335+
336+
if runtime.GOOS != "windows" {
337+
info, err := os.Stat(sock)
338+
if err != nil {
339+
return false, err
340+
}
341+
342+
if info.Mode()&os.ModeSocket == 0 {
343+
return false, fmt.Errorf("system ssh-agent socket `%s` is not a socket", sock)
344+
}
345+
}
346+
conn, err := net.Dial("unix", sock)
347+
if err != nil {
348+
return false, fmt.Errorf("unable to connect to system ssh-agent socket %s: %w", sock, err)
349+
}
350+
defer conn.Close()
351+
352+
return true, nil
353+
}
354+
355+
func getUnixSocketPathLimit() int {
356+
switch runtime.GOOS {
357+
case "darwin", "freebsd", "openbsd", "netbsd":
358+
return 104
359+
default:
360+
return 108
361+
}
362+
}
363+
364+
func validateSockPathLength(sockPath string) error {
365+
if len(sockPath) > getUnixSocketPathLimit() {
366+
return ErrSocketPathTooLong
367+
}
368+
return nil
369+
}
370+
371+
func fallbackToDefaultUnix() string {
372+
// since user can change tmpDir we want to prevent it from setting path more than 104/108 chars
373+
return filepath.Join(os.TempDir(), "werf-ssh-agent", uuid.NewString())
374+
}

pkg/ssh_agent/ssh_agent_test.go

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package ssh_agent
2+
3+
import (
4+
"context"
5+
"os"
6+
"runtime"
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
11+
"github.com/werf/werf/v2/pkg/werf"
12+
)
13+
14+
const (
15+
longPath = "/tmp/werf-test-agent-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
16+
)
17+
18+
func TestLinuxFallback_with_SSHenv(t *testing.T) {
19+
if os.Getenv(SSHAuthSockEnv) == "" {
20+
t.Skip("Skipping test because SSH_AUTH_SOCK is not set")
21+
}
22+
ctx := context.Background()
23+
err := runTest(ctx, t, false, false, func() {
24+
valid, err := validateAgentSock(SSHAuthSock)
25+
assert.NoError(t, err)
26+
assert.True(t, valid)
27+
})
28+
assert.NoError(t, err)
29+
}
30+
31+
func TestLinuxFallback_without_SSHenv_wildTmpPath(t *testing.T) {
32+
ctx := context.Background()
33+
err := runTest(ctx, t, false, false, func() {
34+
if SSHAuthSock == "" {
35+
return
36+
}
37+
valid, err := validateAgentSock(SSHAuthSock)
38+
assert.NoError(t, err)
39+
assert.True(t, valid)
40+
})
41+
assert.NoError(t, err)
42+
}
43+
44+
func TestLinuxFallback_with_SSHenv_wildTmpPath(t *testing.T) {
45+
if os.Getenv(SSHAuthSockEnv) == "" {
46+
t.Skip("Skipping test because SSH_AUTH_SOCK is not set")
47+
}
48+
ctx := context.Background()
49+
err := runTest(ctx, t, false, false, func() {
50+
valid, err := validateAgentSock(SSHAuthSock)
51+
assert.NoError(t, err)
52+
assert.True(t, valid)
53+
})
54+
assert.NoError(t, err)
55+
}
56+
57+
func TestLinuxFallback_withLongSSHenv(t *testing.T) {
58+
ctx := context.Background()
59+
os.Setenv(SSHAuthSockEnv, longPath)
60+
err := runTest(ctx, t, false, false, func() {})
61+
assert.Error(t, err)
62+
}
63+
64+
func runTest(ctx context.Context, t *testing.T, unsetSSHenv, wildTmpPath bool, validationfunc func()) error {
65+
if unsetSSHenv {
66+
os.Unsetenv("SSH_AUTH_SOCK")
67+
}
68+
if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
69+
t.Skip("Skipping test on non-linux OS")
70+
}
71+
72+
home, _ := os.UserHomeDir()
73+
74+
testPath := "/tmp/werf-test-agent"
75+
if wildTmpPath {
76+
testPath = longPath
77+
}
78+
79+
err := os.MkdirAll(testPath, os.ModePerm)
80+
if err != nil {
81+
return err
82+
}
83+
defer os.RemoveAll(testPath)
84+
85+
err = werf.Init(testPath, home)
86+
if err != nil {
87+
return err
88+
}
89+
90+
err = Init(ctx, []string{})
91+
if err != nil {
92+
return err
93+
}
94+
95+
validationfunc()
96+
97+
return nil
98+
}

0 commit comments

Comments
 (0)