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

Add support for multiple unix socket forwards over ssh #66

Merged
merged 1 commit into from
Oct 21, 2021
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
51 changes: 21 additions & 30 deletions cmd/gvproxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ var (
endpoints arrayFlags
vpnkitSocket string
qemuSocket string
forwardSocket string
forwardDest string
forwardUser string
forwardIdentify string
forwardSocket arrayFlags
forwardDest arrayFlags
forwardUser arrayFlags
forwardIdentify arrayFlags
sshPort int
pidFile string
exitCode int
Expand All @@ -53,10 +53,10 @@ func main() {
flag.IntVar(&sshPort, "ssh-port", 2222, "Port to access the guest virtual machine. Must be between 1024 and 65535")
flag.StringVar(&vpnkitSocket, "listen-vpnkit", "", "VPNKit socket to be used by Hyperkit")
flag.StringVar(&qemuSocket, "listen-qemu", "", "Socket to be used by Qemu")
flag.StringVar(&forwardSocket, "forward-sock", "", "Forwards a unix socket to the guest virtual machine over SSH")
flag.StringVar(&forwardDest, "forward-dest", "", "Forwards a unix socket to the guest virtual machine over SSH")
flag.StringVar(&forwardUser, "forward-user", "", "SSH user to use for unix socket forward")
flag.StringVar(&forwardIdentify, "forward-identity", "", "Path to SSH identity key for forwarding")
flag.Var(&forwardSocket, "forward-sock", "Forwards a unix socket to the guest virtual machine over SSH")
flag.Var(&forwardDest, "forward-dest", "Forwards a unix socket to the guest virtual machine over SSH")
flag.Var(&forwardUser, "forward-user", "SSH user to use for unix socket forward")
flag.Var(&forwardIdentify, "forward-identity", "Path to SSH identity key for forwarding")
flag.StringVar(&pidFile, "pid-file", "", "Generate a file with the PID in it")
flag.Parse()
ctx, cancel := context.WithCancel(context.Background())
Expand Down Expand Up @@ -95,26 +95,16 @@ func main() {
protocol = types.QemuProtocol
}

forwardCount := 0
if forwardDest != "" {
forwardCount++
if c := len(forwardSocket); c != len(forwardDest) || c != len(forwardUser) || c != len(forwardIdentify) {
exitWithError(errors.New("-forward-sock, --forward-dest, --forward-user, and --forward-identity must all be specified together, " +
"the same number of times, or not at all"))
}
if forwardIdentify != "" {
_, err := os.Stat(forwardIdentify)

for i := 0; i < len(forwardSocket); i++ {
_, err := os.Stat(forwardIdentify[i])
if err != nil {
exitWithError(errors.Wrapf(err, "Identity file %s can't be loaded", forwardIdentify))
exitWithError(errors.Wrapf(err, "Identity file %s can't be loaded", forwardIdentify[i]))
}
forwardCount++
}
if forwardUser != "" {
forwardCount++
}
if forwardSocket != "" {
forwardCount++
}

if forwardCount > 0 && forwardCount < 4 {
exitWithError(errors.New("-forward-sock, --forward-dest, --forward-user, and --forward-identity must all be specified together, or none specified"))
}

// Create a PID file if requested
Expand Down Expand Up @@ -319,16 +309,17 @@ func run(ctx context.Context, g *errgroup.Group, configuration *types.Configurat
})
}

if forwardSocket != "" {
for i := 0; i < len(forwardSocket); i++ {
dest := url.URL{
Scheme: "ssh",
User: url.User(forwardUser),
User: url.User(forwardUser[i]),
Host: sshHostPort,
Path: forwardDest,
Path: forwardDest[i],
}
j := i
g.Go(func() error {
defer os.Remove(forwardSocket)
forward, err := CreateSSHForward(ctx, forwardSocket, dest, forwardIdentify, vn)
defer os.Remove(forwardSocket[j])
forward, err := CreateSSHForward(ctx, forwardSocket[j], dest, forwardIdentify[j], vn)
if err != nil {
return err
}
Expand Down
4 changes: 1 addition & 3 deletions cmd/gvproxy/ssh_forwarder.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,6 @@ func setupProxy(ctx context.Context, socketURI *url.URL, dest *url.URL, identity
return &SSHForward{}, err
}

logrus.Infof("Socket forward listening on: %s\n", socketURI)

connectFunc := func(bastion *sshclient.Bastion) (net.Conn, error) {
timeout := 5 * time.Second
if bastion != nil {
Expand All @@ -126,7 +124,7 @@ func setupProxy(ctx context.Context, socketURI *url.URL, dest *url.URL, identity
return &SSHForward{}, err
}

logrus.Infof("SSH Bastion connected: %s\n", dest)
logrus.Infof("Socket forward established: %s to %s\n", socketURI.Path, dest.Path)

return &SSHForward{listener, &bastion, socketURI}, nil
}
Expand Down
7 changes: 7 additions & 0 deletions test/ignition.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ ExecStart=/usr/bin/sleep infinity
"sudo",
},
},
{
Name: "root",
PasswordHash: &password,
SSHAuthorizedKeys: []SSHAuthorizedKey{
SSHAuthorizedKey(publicKey),
},
},
},
}

Expand Down
25 changes: 24 additions & 1 deletion test/port_forwarding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ address=/foobar/1.2.3.4
}).Should(Succeed())
})

It("should reach podman API using unix socket forwarding over ssh", func() {
It("should reach rootless podman API using unix socket forwarding over ssh", func() {
httpClient := &http.Client{
Transport: &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
Expand All @@ -198,4 +198,27 @@ address=/foobar/1.2.3.4
g.Expect(string(reply)).To(Equal("OK"))
}).Should(Succeed())
})

It("should reach rootful podman API using unix socket forwarding over ssh", func() {
httpClient := &http.Client{
Transport: &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return net.Dial("unix", forwardRootSock)
},
},
}

Eventually(func(g Gomega) {
resp, err := httpClient.Get("http://host/_ping")
g.Expect(err).ShouldNot(HaveOccurred())
g.Expect(resp.StatusCode).To(Equal(http.StatusOK))
g.Expect(resp.ContentLength).To(Equal(int64(2)))

reply := make([]byte, resp.ContentLength)
_, err = io.ReadAtLeast(resp.Body, reply, len(reply))

g.Expect(err).ShouldNot(HaveOccurred())
g.Expect(string(reply)).To(Equal("OK"))
}).Should(Succeed())
})
})
35 changes: 21 additions & 14 deletions test/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,28 @@ func TestSuite(t *testing.T) {
}

const (
sock = "/tmp/gvproxy-api.sock"
qemuPort = 5555
sshPort = 2222
ignitionUser = "test"
qconLog = "qcon.log"
podmanSock = "/run/user/1001/podman/podman.sock"
sock = "/tmp/gvproxy-api.sock"
qemuPort = 5555
sshPort = 2222
ignitionUser = "test"
qconLog = "qcon.log"
podmanSock = "/run/user/1001/podman/podman.sock"
podmanRootSock = "/run/podman/podman.sock"

// #nosec "test" (for manual usage)
ignitionPasswordHash = "$y$j9T$TqJWt3/mKJbH0sYi6B/LD1$QjVRuUgntjTHjAdAkqhkr4F73m.Be4jBXdAaKw98sPC"
)

var (
tmpDir string
binDir string
host *exec.Cmd
client *exec.Cmd
privateKeyFile string
publicKeyFile string
ignFile string
forwardSock string
tmpDir string
binDir string
host *exec.Cmd
client *exec.Cmd
privateKeyFile string
publicKeyFile string
ignFile string
forwardSock string
forwardRootSock string
)

func init() {
Expand All @@ -55,6 +57,8 @@ func init() {
publicKeyFile = privateKeyFile + ".pub"
ignFile = filepath.Join(tmpDir, "test.ign")
forwardSock = filepath.Join(tmpDir, "podman-remote.sock")
forwardRootSock = filepath.Join(tmpDir, "podman-root-remote.sock")

}

var _ = BeforeSuite(func() {
Expand All @@ -78,7 +82,10 @@ outer:
// #nosec
host = exec.Command(filepath.Join(binDir, "gvproxy"), fmt.Sprintf("--listen=unix://%s", sock), fmt.Sprintf("--listen-qemu=tcp://127.0.0.1:%d", qemuPort),
fmt.Sprintf("--forward-sock=%s", forwardSock), fmt.Sprintf("--forward-dest=%s", podmanSock), fmt.Sprintf("--forward-user=%s", ignitionUser),
fmt.Sprintf("--forward-identity=%s", privateKeyFile),
fmt.Sprintf("--forward-sock=%s", forwardRootSock), fmt.Sprintf("--forward-dest=%s", podmanRootSock), fmt.Sprintf("--forward-user=%s", "root"),
fmt.Sprintf("--forward-identity=%s", privateKeyFile))

host.Stderr = os.Stderr
host.Stdout = os.Stdout
Expect(host.Start()).Should(Succeed())
Expand Down