Skip to content
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

#47037 - Create Remote Dialer Proxy #90

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open

#47037 - Create Remote Dialer Proxy #90

wants to merge 10 commits into from

Conversation

gehrkefc
Copy link

maxsokolovsky and others added 3 commits November 8, 2024 12:12
* Add a port-forward type

* Add support for automatic reconnects when pods are terminated

* List clients

* Add a Dockerfile for the proxy

* Add proxy

* Update go.mod

* Update package name

* Add config parsing

* Immediately close connection when there are no clients
@gehrkefc gehrkefc changed the title added proxy client and server working impl, also some examples #47037 - Create Remote Dialer Proxy Jan 28, 2025
@gehrkefc gehrkefc marked this pull request as ready for review January 28, 2025 20:02
@gehrkefc gehrkefc requested a review from a team as a code owner January 28, 2025 20:02
@tomleb tomleb requested a review from joshmeranda January 28, 2025 21:52
Copy link

@joshmeranda joshmeranda left a comment

Choose a reason for hiding this comment

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

LGTM from an integration with imperative api perspective, but have some smaller comments

cmd/proxy/deployment.yaml Outdated Show resolved Hide resolved
cmd/proxy/deployment.yaml Outdated Show resolved Hide resolved
cmd/proxy/main.go Outdated Show resolved Hide resolved
cmd/proxy/main.go Outdated Show resolved Hide resolved
examples/fakek8s/Dockerfile Show resolved Hide resolved
forward/forward.go Show resolved Hide resolved
forward/forward.go Show resolved Hide resolved
forward/forward.go Outdated Show resolved Hide resolved
secret.go Outdated Show resolved Hide resolved
@tomleb
Copy link
Contributor

tomleb commented Jan 29, 2025

@gehrkefc Currently this PR points to imperative-api branch, but we should target the main branch of remotedialer. The imperative-api branch was a quick branch without review since Max was leaving. So let's review the entirety of the changes instead.

@gehrkefc gehrkefc changed the base branch from imperative-api to main January 29, 2025 15:18
joshmeranda
joshmeranda previously approved these changes Jan 29, 2025
Copy link
Contributor

@tomleb tomleb left a comment

Choose a reason for hiding this comment

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

First set of comments, reviewed mostly the core functionality.

for {
select {
case <-ctx.Done():
logrus.Infoln("Goroutine stopped.")
Copy link
Contributor

Choose a reason for hiding this comment

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

Unlikely but possible to have the context cancelled before we send anything in the r.readyCh. So Start() would never exit.

Copy link
Author

Choose a reason for hiding this comment

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

Ok. Just fixed it! Thanks!

forward/forward.go Show resolved Hide resolved
podClient v1.PodController
namespace string
labelSelector string
Ports []string
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason to have Ports public?

Copy link
Author

Choose a reason for hiding this comment

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

No reason, making it private. Thanks!

logrus.Infoln("Goroutine stopped.")
return
default:
err := r.runForwarder(ctx, r.readyCh, r.stopCh, r.Ports)
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we replace r.stopCh with ctx.Done() ?

Copy link
Author

@gehrkefc gehrkefc Jan 31, 2025

Choose a reason for hiding this comment

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

Sure. Replaced it! Thanks!

proxy/config.go Outdated
package proxy

import (
"github.com/kelseyhightower/envconfig"
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure this is necessary. Simple os.Getenv should be enough. Also, looking at github.com/kelseyhightower/envconfig, I can see the last release was in 2019 and last commit was three years ago.

Copy link
Author

Choose a reason for hiding this comment

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

Sure, this came from Max code. I'll change it.

Copy link
Author

Choose a reason for hiding this comment

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

Done. Thanks!

proxy/config.go Outdated
Comment on lines 14 to 16
ProxyPort int `required:"true" split_words:"true"`
PeerPort int `required:"true" split_words:"true"`
HTTPSPort int `required:"true" split_words:"true"`
Copy link
Contributor

Choose a reason for hiding this comment

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

Would be great to add comments to describe these, especially the ports because they can be pretty confusing.

Copy link
Author

Choose a reason for hiding this comment

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

Just added some comments. Please see if it's enough.

proxy/server.go Outdated
Comment on lines 106 to 110
core, err := core.NewFactoryFromConfigWithOptions(restConfig, nil)
if err != nil {
return fmt.Errorf("build secret controller failed w/ err: %w", err)
}
secretController := core.Core().V1().Secret()
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think the controller / factory is ever started, is that correct? I'm not sure what the behavior of dynamiclistener is in that case, but I do see that it calls OnChange somewhere, so I think we'll want this to work.

Copy link
Author

Choose a reason for hiding this comment

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

It currently stores the secret in the right place. Is there any other method I should call before using it ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah you'll have to run the following code:

if err := core.Start(ctx, 1); err != nil {
  // handle error
}

This will sync the Secrets object with k8s into the in-memory cache and will start 1 worker to react on Secret changes.

Copy link
Author

Choose a reason for hiding this comment

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

Sure, just added it. Thanks!

Comment on lines 94 to 95
logrus.Error("build secret controller failed: %w, defaulting to non TLS connection", err)
return nonTLSDialer, err
Copy link
Contributor

Choose a reason for hiding this comment

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

This makes me a bit nervous, is there a reason for falling back to insecure here?

Copy link
Author

Choose a reason for hiding this comment

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

Not specifically. Do you prefer I just return the err and fail it ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes let's just return an error and fail. We don't need to support non-TLS connections (at least for now).

Copy link
Author

Choose a reason for hiding this comment

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

Sure. Thanks!

Comment on lines 99 to 112
secret, err := secretController.Get(namespace, certSecretName, metav1.GetOptions{})
if err != nil {
return nil, err
}

crtData, exists := secret.Data["tls.crt"]
if !exists {
return nil, fmt.Errorf("secret %s/%s missing tls.crt field", namespace, certSecretName)
}

rootCAs := x509.NewCertPool()
if ok := rootCAs.AppendCertsFromPEM(crtData); !ok {
return nil, fmt.Errorf("failed to parse tls.crt from secret into a CA pool")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The certs will be rotated by dynamiclistener before they expire (or at least that's the intention) so we should react to changes for this. We can add that functionality in the next PR, same as we're doing with @joshmeranda 's PR. We do need that though as part of the GH issue.

Copy link
Author

Choose a reason for hiding this comment

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

I've implemented this. Please check it. I tested it modifying a local copy of dynamic listener to generate a new cert at each 20s. Please check the image below to see both working.

In the lower terminal you'll see the server regenerating the secret and updating it. Then in the upper terminal you'll see the client receiving the event with the new cert.

image

@gehrkefc
Copy link
Author

First set of comments, reviewed mostly the core functionality.

I've made another change in the server in the runProxyListener function because of a high cpu usage.

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