Skip to content

feat(internal/cmd): Add 'instance tunnel' command#248

Open
craciunoiuc wants to merge 2 commits intostagingfrom
craciunoiuc/add-instance-tunnel
Open

feat(internal/cmd): Add 'instance tunnel' command#248
craciunoiuc wants to merge 2 commits intostagingfrom
craciunoiuc/add-instance-tunnel

Conversation

@craciunoiuc
Copy link
Copy Markdown
Member

@craciunoiuc craciunoiuc commented Apr 7, 2026

Nodes need this option --net-internal-traffic=vm in order to test it.

tunnel-3uhz1 │ [    0.141932] ERR:  [apptunlr] Target remote not in the same subnet.
tunnel-3uhz1 │ [    0.142826] ERR:  [apptunlr] Failed to hook TCP2TCP.

The Tunlr also needs updating to remove the check for inter-vm comms.
Done.

TODO: Update tests when platform is stable.

Blocked on: https://github.com/unikraft-cloud/platform/pull/705
Closes: TOOL-714

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new unikraft instance tunnel subcommand intended to forward a local port to a non-public instance via an intermediate TLS tunnel service, enabling testing scenarios that require internal VM traffic.

Changes:

  • Adds InstancesTunnelCmd to the instances command group.
  • Implements target parsing, instance/IP resolution, tunnel-proxy instance creation, and local-to-remote relaying over TLS.
  • Adds control-channel heartbeats to keep the tunnel service connection alive.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/cmd/instances.go Outdated
Comment thread internal/cmd/instances.go Outdated
Comment thread internal/cmd/instances.go Outdated
Comment thread internal/cmd/instances.go Outdated
Comment thread internal/cmd/instances.go Outdated
Comment thread internal/cmd/instances.go Outdated
Comment thread internal/cmd/instances.go Outdated
Comment thread internal/cmd/instances.go Outdated
@craciunoiuc craciunoiuc force-pushed the craciunoiuc/add-instance-tunnel branch 2 times, most recently from 49d2539 to 8ae136d Compare April 7, 2026 09:26
Comment thread internal/cmd/instances.go Outdated
// exposedProxyPorts holds the port exposed by the tunnel service per target.
exposedProxyPorts []uint16
// portIterator tracks the next port offset when a single proxy port is given.
portIterator uint16
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👀 why are these all on this struct?

The only things that should be here are the args that can be read by kong.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

equivalent of passing them by arg everywhere

If I move to a separate package I can keep them there.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, these kind of act like global variables. It's unclear where they get created / passed through. Passing them through as args would be better if we can.

If they grow to be very long, we can group them into structs.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, if i'm moving this to a separate package it will need them as arguments anyway

Copy link
Copy Markdown
Member Author

@craciunoiuc craciunoiuc Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I simplified it as much as I could, let's see what Copilot says.

I think the exposed interface of the library is quite simple now:

New <- you create the instance
RunProxy <- start the proxying instance
TerminateProxy <- stop the proxying instance
StartRelay <- block and send messages

Comment thread internal/cmd/instances.go
Comment thread internal/cmd/instances.go Outdated
Comment thread internal/cmd/instances.go Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 8 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/tunnel/tunnel.go
Comment thread internal/tunnel/tunnel.go Outdated
Comment thread internal/tunnel/tunnel.go
Comment thread internal/tunnel/tunnel.go Outdated
Comment thread internal/cmd/instances.go Outdated
Comment thread internal/cmd/instances.go
Comment thread internal/cmd/instances.go Outdated
Comment thread internal/cmd/instances.go Outdated
@craciunoiuc
Copy link
Copy Markdown
Member Author

I need to fix these + do one more pass so no rush

@craciunoiuc craciunoiuc force-pushed the craciunoiuc/add-instance-tunnel branch from 5e8f3e5 to b8e8437 Compare April 7, 2026 19:02
@craciunoiuc craciunoiuc requested a review from jedevc April 7, 2026 19:05
@craciunoiuc
Copy link
Copy Markdown
Member Author

Should be as decent as it gets, definitely an upgrade compared to kraftkit

@craciunoiuc craciunoiuc force-pushed the craciunoiuc/add-instance-tunnel branch from b8e8437 to a5b1451 Compare April 9, 2026 14:17
@craciunoiuc
Copy link
Copy Markdown
Member Author

Have to rebase the code and to generate tests on Monday.

@craciunoiuc craciunoiuc force-pushed the craciunoiuc/add-instance-tunnel branch 2 times, most recently from dcaf1a6 to c7622e6 Compare April 20, 2026 09:02
Copy link
Copy Markdown
Member

@jedevc jedevc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a rebase, and to get the tests passing 🎉

cleanupCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
env.unikraftIgnoreErr(cleanupCtx, "instance", "delete", instanceName)
})
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use the same test style as in the rest of the tests, so we get a resulting golden file!

For managing a tunnel in the background - we should probably add some sort of way to run it in the background using the golden setup.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I don't have much context on that but I can try

Comment thread internal/cmd/instances.go
}

type InstancesTunnelCmd struct {
Targets []string `arg:"" name:"target" min:"1" help:"Forwarding target(s) in the form [LOCAL_PORT:]<INSTANCE>:DEST_PORT[/TYPE]."`
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use placeholder to show the form. See other examples of how this is done.

Comment thread internal/cmd/instances.go

func (cmd *InstancesTunnelCmd) Run(ctx context.Context, stdio config.Stdio) error {
if len(cmd.TunnelProxyPorts) == 0 {
cmd.TunnelProxyPorts = []string{"4444"}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not use the default tag?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are you referring to? you mean for the images? you can just set a different image

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean in the kong command struct.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah sorry, probably some old kraftkit copy paste

Comment thread internal/cmd/instances.go
for i, r := range resolved {
inst := r.(Instance)
if metro != "" && inst.key.Metro != "" && inst.key.Metro != metro {
return nil, "", fmt.Errorf("forwarding targets must be in the same metro: found %q and %q", metro, inst.key.Metro)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 we could support instances across multiple metros. We would just need to create multiple tunnels. This is supported for the logs cmd, so makes sense to support it here.

Instead of resolving all the metros, we'd do a call more like CollectRefs, and use that to resolve all the ips, and metros, and then just do multiple tunnel creations!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds crazy, but doable, the command has support for multiple tunnel connections

Comment thread internal/cmd/instances.go
Comment on lines +1548 to +1549
// NOTE(craciunoiuc): We do this here to have access to the Instances API
func (cmd *InstancesTunnelCmd) tunnelLookupInstances(ctx context.Context, t *tunnel.Tunnel) ([]string, string, error) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function could very easily be added to the tunnel package actually.

Yes, we'd have to do the GetInstances request directly there, but then it's a reusable package, for anyone to use. Also, we already are doing CreateInstance requests in there, so.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, was conflicted about that

Comment thread internal/tunnel/relay.go
Comment on lines +99 to +102
func (r *tunnelRelay) listenLocal(ctx context.Context) (net.Listener, error) {
var lc net.ListenConfig
return lc.Listen(ctx, r.ctype+"4", r.lAddr)
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can inline this right?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

Comment thread internal/tunnel/relay.go
Comment on lines +73 to +74
defer rc.Close()
go func() { <-ctx.Done(); rc.Close() }()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, closed twice?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😭

I though I caught all the places where this happened

Comment thread internal/tunnel/tunnel.go
Comment thread internal/tunnel/tunnel.go
Comment on lines +290 to +294
parts := strings.SplitN(portsArg, "/", 3)
if len(parts) == 3 {
rest = parts[0] + "/" + parts[1]
ctype = parts[2]
} else if len(parts) == 2 {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👀 what? What is this case for? What if there are 4 /s here?

We could simplify this by just splitting at the last /.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the format is now possibly metro/instance/connectionType

Comment is outdated needs an update

Comment thread internal/tunnel/tunnel.go
if parseErr != nil {
return "", 0, 0, "", fmt.Errorf("%q is not a valid port number", segments[1])
}
return segments[0], uint16(rport64), uint16(rport64), ctype, nil
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If no local port - shouldn't we select it to a random available port? (by setting it to 0)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it sets it to the default value?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to be just using rport64 for both?

@craciunoiuc
Copy link
Copy Markdown
Member Author

I'll rebase and fix afterwards

Signed-off-by: Cezar Craciunoiu <cezar@unikraft.io>
Signed-off-by: Cezar Craciunoiu <cezar@unikraft.io>
@craciunoiuc craciunoiuc force-pushed the craciunoiuc/add-instance-tunnel branch from c7622e6 to 41b74aa Compare May 1, 2026 13:43
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.

3 participants