Open tv42 opened 5 years ago
@tv42 Totally support your idea, and this was the original intention. However, pipes are not well supported on Windows (at least in Go). So if you have an idea how it can be implemented on Windows without much cgo
hassle - please let me know!
Oh, funny, because I understood the motivation for implementing the pipe functionality in Chrome was that the original FD passing mechanism wasn't good for Windows.
My understanding is that os.Pipe
and os/exec.Cmd.ExtraFiles
work on Windows just like they would on anything else (click those links to see the implementation), but I don't "do Windows" so I'm not the right person to ask.
Even if this was something where Windows was not capable of doing a thing, the current mechanism essentially makes Lorca unusable for anything beyond a demo. Really.
@tv42 Actually, in the exec.Cmd.ExtraFiles comment it states: // ExtraFiles is not supported on Windows.
and it seems to be true.
Oh. Funky. Sorry for missing that. This seems to be the relevant Go issue: https://github.com/golang/go/issues/21085
Based on the that issue, it sounds like this restriction in Go comes from Windows file handles not being sequentially numbered, but then the Chrome docs talking about fd 3/4 get confusing. Plus, their issue tracking implies -pipe
won over -fd
because of Windows support ("I don't think you can wrap single pipe with TCP socket in Windows" is the argument against using a single fd in https://chromium-review.googlesource.com/c/chromium/src/+/954405/ ). I added a comment to the Go issue in hopes that someone will know if these puzzle pieces connect.
Chasing down what happens on Windows, links to interesting bits in source/docs:
https://cs.chromium.org/chromium/src/content/browser/devtools/devtools_pipe_handler.cc?q=read_handle_&l=49&dr=C https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle?view=vs-2017
Here's evidence of things using fds 3/4 happily, with further resources in comments. Puppeteer especially is specially advertised as working on Windows: https://pptr.dev
https://github.com/GoogleChrome/puppeteer/issues/2079#issuecomment-373568350 https://github.com/GoogleChromeLabs/carlo/issues/11 https://github.com/cyrus-and/chrome-remote-interface/issues/381
I managed to use pipes in my library https://github.com/Srinivasa314/alcro in the master branch, but you need to use cgo for it. Also headless mode does not work (I dont know why)
For what it's worth, https://github.com/golang/go/issues/21085 is now closed, and thus this should be fixable?
FWIW, I've implemented the pipes solution for Linux (and it works great), please let me know if that's something you'd like to add (even if it does not implement it on Windows).
I've split out the websocket connection, built a Channel
interface, and here's how to connect to Chrome via pipes:
package lorca
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
)
// Channel exchanges messages with the running browser instance.
type Channel interface {
Send(interface{}) error
Receive(interface{}) error
Close() error
}
type pipeChannel struct {
pout io.WriteCloser
pin io.ReadCloser
rd *bufio.Reader
}
func runChromeWithPipes(chromeBinary string, args ...string) (*exec.Cmd, Channel, error) {
rIn, wIn, err := os.Pipe()
if err != nil {
return nil, nil, fmt.Errorf("create pipe failed: %w", err)
}
rOut, wOut, err := os.Pipe()
if err != nil {
return nil, nil, fmt.Errorf("create pipe failed: %w", err)
}
// Start chrome process
args = append(args, "--remote-debugging-pipe")
cmd := exec.Command(chromeBinary, args...)
cmd.ExtraFiles = []*os.File{rIn, wOut}
if err != nil {
return nil, nil, err
}
if err := cmd.Start(); err != nil {
return nil, nil, err
}
ch := &pipeChannel{
pout: wIn,
pin: rOut,
rd: bufio.NewReader(rOut),
}
return cmd, ch, nil
}
func (ch *pipeChannel) Close() error {
err := ch.pout.Close()
errOut := ch.pin.Close()
if err == nil {
err = errOut
}
if err != nil {
return fmt.Errorf("close pipe channel failed: %w", err)
}
return nil
}
func (ch *pipeChannel) Send(obj interface{}) error {
buf, err := json.Marshal(obj)
if err != nil {
return fmt.Errorf("json encode failed: %w", err)
}
// terminate the message with a null byte
buf = append(buf, 0)
_, err = ch.pout.Write(buf)
if err != nil {
return fmt.Errorf("write to chrome instance failed: %w", err)
}
return nil
}
func (ch *pipeChannel) Receive(obj interface{}) error {
// read until the next null byte
buf, err := ch.rd.ReadBytes(0)
if err != nil {
return fmt.Errorf("read message from chrome failed: %w", err)
}
buf = bytes.TrimRight(buf, "\x00")
err = json.Unmarshal(buf, obj)
if err != nil {
return fmt.Errorf("json unmarshal failed: %w", err)
}
return nil
}
Hi. Lorca currently exposes all information about the UI to all processes on localhost, and allows any local process to hijack the UI.
https://peter.sh/experiments/chromium-command-line-switches/#remote-debugging-address
As the attacker:
I would recommend you switch to
--remote-debugging-pipe
: https://github.com/chromium/chromium/blob/d925e7f357d93b95631c22c47e2cc93b093dacc5/content/public/common/content_switches.cc#L700-L703