mirror of
https://github.com/coder/coder.git
synced 2026-06-03 13:08:25 +00:00
1069ce6e19
relates to #21335 Adds support for the agentsock and thus `coder exp sync` commands on Windows. This support was initially missing.
64 lines
2.1 KiB
Go
64 lines
2.1 KiB
Go
//go:build windows
|
|
|
|
package agentsocket
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"os/user"
|
|
"strings"
|
|
|
|
"github.com/Microsoft/go-winio"
|
|
"golang.org/x/xerrors"
|
|
)
|
|
|
|
const defaultSocketPath = `\\.\pipe\com.coder.agentsocket`
|
|
|
|
func createSocket(path string) (net.Listener, error) {
|
|
if path == "" {
|
|
path = defaultSocketPath
|
|
}
|
|
if !strings.HasPrefix(path, `\\.\pipe\`) {
|
|
return nil, xerrors.Errorf("%q is not a valid local socket path", path)
|
|
}
|
|
|
|
user, err := user.Current()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to look up current user: %w", err)
|
|
}
|
|
sid := user.Uid
|
|
|
|
// SecurityDescriptor is in SDDL format. c.f.
|
|
// https://learn.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-string-format for full details.
|
|
// D: indicates this is a Discretionary Access Control List (DACL), which is Windows-speak for ACLs that allow or
|
|
// deny access (as opposed to SACL which controls audit logging).
|
|
// P indicates that this DACL is "protected" from being modified thru inheritance
|
|
// () delimit access control entries (ACEs), here we only have one, which, allows (A) generic all (GA) access to our
|
|
// specific user's security ID (SID).
|
|
//
|
|
// Note that although Microsoft docs at https://learn.microsoft.com/en-us/windows/win32/ipc/named-pipes warns that
|
|
// named pipes are accessible from remote machines in the general case, the `winio` package sets the flag
|
|
// windows.FILE_PIPE_REJECT_REMOTE_CLIENTS when creating pipes, so connections from remote machines are always
|
|
// denied. This is important because we sort of expect customers to run the Coder agent under a generic user
|
|
// account unless they are very sophisticated. We don't want this socket to cross the boundary of the local machine.
|
|
configuration := &winio.PipeConfig{
|
|
SecurityDescriptor: fmt.Sprintf("D:P(A;;GA;;;%s)", sid),
|
|
}
|
|
|
|
listener, err := winio.ListenPipe(path, configuration)
|
|
if err != nil {
|
|
return nil, xerrors.Errorf("failed to open named pipe: %w", err)
|
|
}
|
|
return listener, nil
|
|
}
|
|
|
|
func cleanupSocket(path string) error {
|
|
return os.Remove(path)
|
|
}
|
|
|
|
func dialSocket(ctx context.Context, path string) (net.Conn, error) {
|
|
return winio.DialPipeContext(ctx, path)
|
|
}
|