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

Updating credentials of a running server #7209

Open
TalLerner opened this issue May 7, 2024 · 4 comments
Open

Updating credentials of a running server #7209

TalLerner opened this issue May 7, 2024 · 4 comments
Assignees

Comments

@TalLerner
Copy link

Please see the FAQ in our main README.md before submitting your issue.

Use case(s) - what problem will this feature solve?

When using server certificates, while the server is running new certificates are created. The new certificate must be updated in the server's options. Currently this is not possible without restarting the server and disconnecting the clients.

Proposed Solution

If the options can be updated while the server is running this problem can be resolved.

Alternatives Considered

I didn't find any alternatives. Do you have any suggestions?

Additional Context

@TalLerner TalLerner added the Type: Feature New features or improvements in behavior label May 7, 2024
@arjan-bal arjan-bal self-assigned this May 8, 2024
@arjan-bal
Copy link
Collaborator

Hi @TalLerner, a possible solution is to implement your own TransportCredentials that delegates to TLS credentials created using one of the available constructors. Your custom TransportCredentials can provide the option to switch the delegate during runtime. An example of such a TransportCredentials implementation is as follows:

type DynamicCreds struct {
	delegate credentials.TransportCredentials
	rwMutex  sync.RWMutex
}

func (d *DynamicCreds) ClientHandshake(ctx context.Context, host string, conn net.Conn) (net.Conn, credentials.AuthInfo, error) {
	d.rwMutex.RLock()
	defer d.rwMutex.RUnlock()
	return d.delegate.ClientHandshake(ctx, host, conn)
}

func (d *DynamicCreds) ServerHandshake(conn net.Conn) (net.Conn, credentials.AuthInfo, error) {
	d.rwMutex.RLock()
	defer d.rwMutex.RUnlock()
	return d.delegate.ServerHandshake(conn)
}

func (d *DynamicCreds) Info() credentials.ProtocolInfo {
	d.rwMutex.RLock()
	defer d.rwMutex.RUnlock()
	return d.delegate.Info()
}

func (d *DynamicCreds) Clone() credentials.TransportCredentials {
	d.rwMutex.RLock()
	defer d.rwMutex.RUnlock()
	return NewDynamicCreds(d.delegate.Clone())
}

func (d *DynamicCreds) OverrideServerName(name string) error {
	d.rwMutex.RLock()
	defer d.rwMutex.RUnlock()
	return d.delegate.OverrideServerName(name)
}

func (d *DynamicCreds) UpdateDelegate(newCreds credentials.TransportCredentials) {
	d.rwMutex.Lock()
	defer d.rwMutex.Unlock()
	if newCreds == d {
		fmt.Printf("Can't point to self!")
		return
	}
	d.delegate = newCreds
}

func NewDynamicCreds(delegate credentials.TransportCredentials) *DynamicCreds {
	return &DynamicCreds{
		delegate: delegate,
		rwMutex:  sync.RWMutex{},
	}
}

You can then create DynamicCreds and use them while starting your server as follows:

serverCertFile := data.Path("x509/server_cert.pem")
serverKeyFile := data.Path("x509/server_key.pem")
serverCreds, err := credentials.NewServerTLSFromFile(serverCertFile, serverKeyFile)
if err != nil {
	log.Fatalf("Failed to generate credentials: %v", err)
}
dynCreds := NewDynamicCreds(serverCreds)
opts = []grpc.ServerOption{grpc.Creds(dynCreds)}
grpcServer := grpc.NewServer(opts...)

When you want to change the delegate, you can call dynCreds.UpdateDelegate() while passing in the new credentials. This way you gain the ability to change only the transport credentials without updating the server options.

I tried this out in arjan-bal/routeguide@b7b0608 which has a server that switches it's TLS certs every 5 seconds.

Let me know if this works for you.

@arjan-bal
Copy link
Collaborator

Another option suggested by @atollena is to create a tls.Config with empty Certificates, write a closure that gets the latest certificates and assign it to the GetCertificate field of the tls.Config. The tls library will call your closure during every handshake to fetch the certificates.

Use this tls.Config to create gRPC transport credentials by calling the constructor.

Copy link

This issue is labeled as requiring an update from the reporter, and no update has been received after 6 days. If no update is provided in the next 7 days, this issue will be automatically closed.

@github-actions github-actions bot added the stale label May 20, 2024
@TalLerner
Copy link
Author

TalLerner commented May 21, 2024 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants