Thanks to visit codestin.com
Credit goes to github.com

Skip to content

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

Merged
merged 1 commit into from
Dec 5, 2024
Merged

Conversation

ethanndickson
Copy link
Member

@ethanndickson ethanndickson commented Nov 21, 2024

Addresses #14734.

This PR wires up tunnel.go to a tailnet.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.

Copy link
Member Author

ethanndickson commented Nov 21, 2024

@@ -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;
Copy link
Member Author

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) {
Copy link
Member Author

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.

Copy link
Member

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
Copy link
Member Author

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.

@ethanndickson ethanndickson marked this pull request as ready for review November 22, 2024 05:40
@ethanndickson ethanndickson changed the title chore: implement vpn client & dylib tunnel chore: implement CoderVPN client & tunnel Nov 22, 2024
vpn/tunnel.go Outdated
for i, agent := range update.UpsertedAgents {
out.UpsertedAgents[i] = &Agent{
Id: agent.Id,
Name: agent.Name,
Copy link
Contributor

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.

Comment on lines +82 to +93
// 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))
// }
Copy link
Member

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?

Comment on lines +256 to +260
if options.TUNDev == nil {
dialer.NetstackDialTCP = func(ctx context.Context, dst netip.AddrPort) (net.Conn, error) {
return netStack.DialContextTCP(ctx, dst)
}
netStack.ProcessLocalIPs = true
Copy link
Member

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?

Copy link
Member Author

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.

Copy link
Contributor

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 {
Copy link
Member Author

@ethanndickson ethanndickson Nov 25, 2024

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)
Copy link
Member Author

@ethanndickson ethanndickson Nov 25, 2024

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
Comment on lines 156 to 177
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)
}
}
Copy link
Member

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

Copy link
Member Author

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)

Copy link
Member

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

@ethanndickson ethanndickson merged commit ba48069 into main Dec 5, 2024
31 checks passed
@ethanndickson ethanndickson deleted the ethan/vpn-client branch December 5, 2024 02:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants