Skip to content

Connect: Accommodate for making gRPC client creds from tshd key pair#16782

Merged
ravicious merged 8 commits into
masterfrom
ravicious/refactor-grpc-certs
Oct 20, 2022
Merged

Connect: Accommodate for making gRPC client creds from tshd key pair#16782
ravicious merged 8 commits into
masterfrom
ravicious/refactor-grpc-certs

Conversation

@ravicious
Copy link
Copy Markdown
Member

@ravicious ravicious commented Sep 28, 2022

Equivalent webapps PR: gravitational/webapps#1220.

On Windows, we use gRPC over TCP with mTLS since Node.js doesn't support UDS on Windows. Each process creates its own keypair and saves the public key to a predetermined location.

Up until now, these were the client-server pairs we had to support:

Server Client
tsh daemon renderer
shared (Electron process) renderer

For tshd-initiated communication, the tshd process will need to create a client that will connect to a gRPC server operated by the renderer process of the Electron app.

The previous code assumes that tshd is only going to need server credentials. This commit makes it possible to create client credentials from the same key pair.

Comment thread lib/teleterm/apiserver/apiserver.go Outdated
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.

This function used to do two things. I extracted them into separate functions.

  • Create a key pair for the tshd process and save the public key on disk.
    • This is now generateAndSaveCert.
  • Create gRPC server credentials.
    • This is now createServerCredentials.

For tshd-initiated communication, the tshd process will need to create a
client that will connect to a gRPC server operated by the renderer
process of the Electron app.

On Windows, we use gRPC over TCP with mTLS. Each process creates its own
keypair and saves the public key to a predetermined location.

The previous code assumes that tshd is only going to need server
credentials. This commit makes it possible to create client credentials
from the same key pair.
@ravicious ravicious force-pushed the ravicious/refactor-grpc-certs branch from 53ad1fe to 867f227 Compare September 28, 2022 10:59
@ravicious ravicious marked this pull request as ready for review September 28, 2022 11:09
@ravicious ravicious requested a review from avatus October 3, 2022 08:57
@ravicious
Copy link
Copy Markdown
Member Author

@nklaassen @xacrimon ping


certificate, err := keys.X509KeyPair(cert.Cert, cert.PrivateKey)
if err != nil {
return tls.Certificate{}, trace.Wrap(err)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I know this tls.Certificate{} is thrown away anyway if an error is returned but is there a reason to create the new struct on error? Or is this just to satisfy return types?

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 know this tls.Certificate{} is thrown away anyway if an error is returned but is there a reason to create the new struct on error? Or is this just to satisfy return types?

Yes, that is to satisfy return types.

// File is first saved using under tempPath and then renamed to fullPath.
// This prevents other processes from reading a half written file.
fullPath := filepath.Join(path)
tempPath := fullPath + ".tmp"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could there possibly be concurrent calls across different processes that could race here?

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.

Multiple calls to generateAndSaveCert? No, this is called when we run tsh daemon start and we create only one such process.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

still, os.CreateTemp might be appropriate and would avoid that problem altogether

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.

e019eb2 makes it so that os.CreateTemp is used instead of manually creating a "temp" file.

Copy link
Copy Markdown
Contributor

@xacrimon xacrimon left a comment

Choose a reason for hiding this comment

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

lgtm

Comment thread lib/teleterm/apiserver/apiserver.go Outdated
Comment thread lib/teleterm/apiserver/apiserver.go Outdated
Comment thread lib/teleterm/apiserver/apiserver.go Outdated
Comment on lines 66 to 68
grpcServer := grpc.NewServer(tshdCreds, grpc.ChainUnaryInterceptor(
withErrorHandling(cfg.Log),
))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
grpcServer := grpc.NewServer(tshdCreds, grpc.ChainUnaryInterceptor(
withErrorHandling(cfg.Log),
))
grpcServer := grpc.NewServer(serverOptions...)

Comment on lines +40 to +41
// createServerCredentials creates mTLS credentials for a gRPC server. The client cert file is read
// only on an incoming connection, not upfront.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I can see that the client cert file is read on every connection, the comment should probably explain why

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 can see that the client cert file is read on every connection, the comment should probably explain why

Comment added:

// createServerCredentials creates mTLS credentials for a gRPC server. The client cert file is read
// only on an incoming connection, not upfront. Otherwise we'd need to wait for the client cert file
// to exist before booting up the server.
func createServerCredentials(serverKeyPair tls.Certificate, clientCertPath string) (grpc.ServerOption, error) {

func generateAndSaveCert(path string) (tls.Certificate, error) {
// File is first saved using under tempPath and then renamed to fullPath.
// This prevents other processes from reading a half written file.
fullPath := filepath.Join(path)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

is there a reason to call filepath.Join with a single arg?

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, good catch, thanks. That was a leftover, it used to look like this, then I refactored the function args but forgot to remove the call to Join.

tshServerCertFullPath := filepath.Join(certsDir, tshServerCertFileName)

// File is first saved using under tempPath and then renamed to fullPath.
// This prevents other processes from reading a half written file.
fullPath := filepath.Join(path)
tempPath := fullPath + ".tmp"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

still, os.CreateTemp might be appropriate and would avoid that problem altogether

Comment on lines +40 to +41
// createServerCredentials creates mTLS credentials for a gRPC server. The client cert file is read
// only on an incoming connection, not upfront.
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 can see that the client cert file is read on every connection, the comment should probably explain why

Comment added:

// createServerCredentials creates mTLS credentials for a gRPC server. The client cert file is read
// only on an incoming connection, not upfront. Otherwise we'd need to wait for the client cert file
// to exist before booting up the server.
func createServerCredentials(serverKeyPair tls.Certificate, clientCertPath string) (grpc.ServerOption, error) {

func generateAndSaveCert(path string) (tls.Certificate, error) {
// File is first saved using under tempPath and then renamed to fullPath.
// This prevents other processes from reading a half written file.
fullPath := filepath.Join(path)
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, good catch, thanks. That was a leftover, it used to look like this, then I refactored the function args but forgot to remove the call to Join.

tshServerCertFullPath := filepath.Join(certsDir, tshServerCertFileName)

// File is first saved using under tempPath and then renamed to fullPath.
// This prevents other processes from reading a half written file.
fullPath := filepath.Join(path)
tempPath := fullPath + ".tmp"
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.

e019eb2 makes it so that os.CreateTemp is used instead of manually creating a "temp" file.


err = os.WriteFile(tempPath, cert.Cert, 0600)
if err != nil {
if err = tempFile.Chmod(0600); err != nil {
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'm not sure if I need that extra Chmod here. AFAIK os.CreateTemp already creates a file with 0600 and I suppose we can expect a temp file to have the strictest permissions possible. But the docs for os.CreateTemp don't state any guarantees about it so I'd rather be explicit.

@ravicious ravicious requested a review from nklaassen October 19, 2022 09:53
@ravicious ravicious merged commit 5adcebb into master Oct 20, 2022
@ravicious
Copy link
Copy Markdown
Member Author

I’m going to backport this once v11.0.0 is released.

@github-actions
Copy link
Copy Markdown
Contributor

@ravicious See the table below for backport results.

Branch Result
branch/v11 Create PR

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