-
Notifications
You must be signed in to change notification settings - Fork 881
chore: implement CoderVPN client & tunnel #15612
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
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
@@ -178,6 +178,12 @@ message StartRequest { | |||
int32 tunnel_file_descriptor = 1; | |||
string coder_url = 2; | |||
string api_token = 3; | |||
// Additional HTTP headers added to all requests | |||
message Header { | |||
string name = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Deliberately not a map to support repeated headers
"github.com/coder/coder/v2/vpn" | ||
) | ||
|
||
func TestClient_WorkspaceUpdates(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test might be a bit much, but I wanted to test the Client without spinning up a whole coderd
, and it turns out that takes a bit of code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a lot. Is there any particular reason to not spin up coderd (other than wanting to be lean I guess) to avoid all this boilerplate?
|
||
type Conn interface { | ||
CurrentWorkspaceState() *proto.WorkspaceUpdate | ||
Close() error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect this interface will grow with whatever functions from tailnet.Conn
we end up needing.
vpn/tunnel.go
Outdated
for i, agent := range update.UpsertedAgents { | ||
out.UpsertedAgents[i] = &Agent{ | ||
Id: agent.Id, | ||
Name: agent.Name, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing workspace_id, fqdn.
Also missing ip address and last handshake, but we can save those for another PR.
// TODO: Wait for the reply to be sent before closing the speaker. | ||
// err := t.speaker.Close() | ||
// if err != nil { | ||
// t.logger.Error(t.ctx, "failed to close speaker", slog.Error(err)) | ||
// } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think our tunnel may need a mutex or sync.Once to handle being closed properly?
if options.TUNDev == nil { | ||
dialer.NetstackDialTCP = func(ctx context.Context, dst netip.AddrPort) (net.Conn, error) { | ||
return netStack.DialContextTCP(ctx, dst) | ||
} | ||
netStack.ProcessLocalIPs = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we consolidate the places we do this check on options.TUNDev
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if changing some of the ordering here breaks anything, may need to revisit cleaning this up once we have tests that pass in a real TUN fd. The conn.go
changes I just copied from Spike's prototype branch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, ordering does matter here in general. It might be possible to do it better.
func (w workspace) addAllDNSNames(names map[dnsname.FQDN][]netip.Addr, owner string) error { | ||
for _, a := range w.agents { | ||
// updateDNSNames updates the DNS names for all agents in the workspace. | ||
func (w *Workspace) updateDNSNames() error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Including all the FQDNs as part of an outgoing workspace update was kinda tricky, particularly because of the len(agents) == 1
alias.
This solution just has this function update the hosts on each agent, and then store all the agents and workspaces as pointers. This lets us build our workspace update as we apply the diff to the state, and then before sending, we fill in all the correct FQDNs, which applies to both the state, and the outgoing update.
names := make(map[dnsname.FQDN][]netip.Addr) | ||
for _, w := range t.workspaces { | ||
err := w.addAllDNSNames(names, t.ownerUsername) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved the ownerUsername
from an argument when updating a workspace to a private field on the Workspace
, as it made more sense for future-proofing w.r.t. shared workspaces.
vpn/tunnel.go
Outdated
func UseAsRouter() TunnelOption { | ||
return func(t *Tunnel) { | ||
t.router = NewRouter(t) | ||
} | ||
} | ||
|
||
func UseAsLogger() TunnelOption { | ||
return func(t *Tunnel) { | ||
t.logger = t.logger.AppendSinks(t) | ||
} | ||
} | ||
|
||
func UseAsDNSConfig() TunnelOption { | ||
return func(t *Tunnel) { | ||
t.dnsConfigurator = NewDNSConfigurator(t) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we ever not want these things? They should just be the default and we should remove TunnelOption
until the need arises
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spike suggested earlier: #15612 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I honestly prefer the separate build tag files that do the implementations for each of these on each platform but to each their own
469ff7a
to
07dc614
Compare
07dc614
to
359aaeb
Compare
Addresses #14734.
This PR wires up
tunnel.go
to atailnet.Conn
via the new/tailnet
endpoint, with all the necessary controllers such that a VPN connection can be started, stopped and inspected via the CoderVPN protocol.