Add support for multiple unix socket forwards over ssh

This commit is contained in:
Jason T. Greene
2021-10-20 22:23:21 -05:00
parent be221fa11b
commit 38243c6a55
5 changed files with 75 additions and 49 deletions

View File

@@ -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
@@ -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())
@@ -95,26 +95,16 @@ func main() {
protocol = types.QemuProtocol
}
forwardCount := 0
if forwardDest != "" {
forwardCount++
}
if forwardIdentify != "" {
_, err := os.Stat(forwardIdentify)
if err != nil {
exitWithError(errors.Wrapf(err, "Identity file %s can't be loaded", forwardIdentify))
}
forwardCount++
}
if forwardUser != "" {
forwardCount++
}
if forwardSocket != "" {
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 forwardCount > 0 && forwardCount < 4 {
exitWithError(errors.New("-forward-sock, --forward-dest, --forward-user, and --forward-identity must all be specified together, or none specified"))
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[i]))
}
}
// Create a PID file if requested
@@ -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
}

View File

@@ -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 {
@@ -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
}

View File

@@ -51,6 +51,13 @@ ExecStart=/usr/bin/sleep infinity
"sudo",
},
},
{
Name: "root",
PasswordHash: &password,
SSHAuthorizedKeys: []SSHAuthorizedKey{
SSHAuthorizedKey(publicKey),
},
},
},
}

View File

@@ -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) {
@@ -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())
})
})

View File

@@ -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() {
@@ -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() {
@@ -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())