-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Connect: Add prerequisites for gracefully handling expired db proxy certs #18259
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
Changes from all commits
b4935c9
7134d69
cb6cd8b
7c7a6b2
67297d3
e27e75f
7676f38
faeff11
fd42fe1
9d855c5
9172db4
408fcae
1cb6411
a9cd805
9d4a55f
4f93f0f
6f0cb3f
35a22d0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -22,6 +22,7 @@ import ( | |||
| "github.com/gravitational/trace" | ||||
|
|
||||
| "github.com/gravitational/teleport/lib/teleterm/gateway" | ||||
| "github.com/gravitational/teleport/lib/tlsca" | ||||
| ) | ||||
|
|
||||
| type CreateGatewayParams struct { | ||||
|
|
@@ -36,6 +37,7 @@ type CreateGatewayParams struct { | |||
| LocalPort string | ||||
| CLICommandProvider gateway.CLICommandProvider | ||||
| TCPPortAllocator gateway.TCPPortAllocator | ||||
| OnExpiredCert gateway.OnExpiredCertFunc | ||||
| } | ||||
|
|
||||
| // CreateGateway creates a gateway | ||||
|
|
@@ -45,7 +47,13 @@ func (c *Cluster) CreateGateway(ctx context.Context, params CreateGatewayParams) | |||
| return nil, trace.Wrap(err) | ||||
| } | ||||
|
|
||||
| if err := c.ReissueDBCerts(ctx, params.TargetUser, db); err != nil { | ||||
| routeToDatabase := tlsca.RouteToDatabase{ | ||||
| ServiceName: db.GetName(), | ||||
| Protocol: db.GetProtocol(), | ||||
| Username: params.TargetUser, | ||||
| } | ||||
|
Comment on lines
+50
to
+54
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about database schema name (--db-name in the cli)?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Connect never encodes the schema name in the certificate. From what I know, it can be skipped. In Connect, the user is able to change the schema name at any moment and it'll affect only the CLI command and nothing else.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I was not aware of this - I just tested this and my postgres db cert omits the schema name extension (1.3.9999.2.4). However, if I try to connect it uses my --db-user as the --db-name. Not sure where that's happening
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In case of Postgres and Mongo databaseName is part part of Wire database protocol: For instance: teleport/lib/srv/db/postgres/engine.go Line 177 in 1b0a083
The |
||||
|
|
||||
| if err := c.ReissueDBCerts(ctx, routeToDatabase); err != nil { | ||||
| return nil, trace.Wrap(err) | ||||
| } | ||||
|
|
||||
|
|
@@ -60,9 +68,11 @@ func (c *Cluster) CreateGateway(ctx context.Context, params CreateGatewayParams) | |||
| CertPath: c.status.DatabaseCertPathForCluster(c.clusterClient.SiteName, db.GetName()), | ||||
| Insecure: c.clusterClient.InsecureSkipVerify, | ||||
| WebProxyAddr: c.clusterClient.WebProxyAddr, | ||||
| Log: c.Log.WithField("gateway", params.TargetURI), | ||||
| Log: c.Log, | ||||
| CLICommandProvider: params.CLICommandProvider, | ||||
| TCPPortAllocator: params.TCPPortAllocator, | ||||
| OnExpiredCert: params.OnExpiredCert, | ||||
| Clock: c.clock, | ||||
| }) | ||||
| if err != nil { | ||||
| return nil, trace.Wrap(err) | ||||
|
|
||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -65,6 +65,8 @@ func New(cfg Config) (*Gateway, error) { | |
| return nil, trace.Wrap(err) | ||
| } | ||
|
|
||
| cfg.LocalPort = port | ||
|
|
||
| protocol, err := alpncommon.ToALPNProtocol(cfg.Protocol) | ||
| if err != nil { | ||
| return nil, trace.Wrap(err) | ||
|
|
@@ -80,35 +82,52 @@ func New(cfg Config) (*Gateway, error) { | |
| return nil, trace.Wrap(err) | ||
| } | ||
|
|
||
| localProxy, err := alpn.NewLocalProxy(alpn.LocalProxyConfig{ | ||
| localProxyConfig := alpn.LocalProxyConfig{ | ||
| InsecureSkipVerify: cfg.Insecure, | ||
| RemoteProxyAddr: cfg.WebProxyAddr, | ||
| Protocols: []alpncommon.Protocol{protocol}, | ||
| Listener: listener, | ||
| ParentContext: closeContext, | ||
| SNI: address.Host(), | ||
| Certs: []tls.Certificate{tlsCert}, | ||
| }) | ||
| Clock: cfg.Clock, | ||
| } | ||
|
|
||
| localProxyMiddleware := &localProxyMiddleware{ | ||
| log: cfg.Log, | ||
| dbRoute: cfg.RouteToDatabase(), | ||
| } | ||
|
|
||
| if cfg.OnExpiredCert != nil { | ||
| localProxyConfig.Middleware = localProxyMiddleware | ||
| } | ||
|
|
||
| localProxy, err := alpn.NewLocalProxy(localProxyConfig) | ||
| if err != nil { | ||
| return nil, trace.Wrap(err) | ||
| } | ||
|
|
||
| cfg.LocalPort = port | ||
|
|
||
| gateway := &Gateway{ | ||
| cfg: &cfg, | ||
| closeContext: closeContext, | ||
| closeCancel: closeCancel, | ||
| localProxy: localProxy, | ||
| } | ||
|
|
||
| if cfg.OnExpiredCert != nil { | ||
| localProxyMiddleware.onExpiredCert = func(ctx context.Context) error { | ||
| err := cfg.OnExpiredCert(ctx, gateway) | ||
| return trace.Wrap(err) | ||
| } | ||
| } | ||
|
Comment on lines
+117
to
+122
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's kind of an awkward way to initialize the middleware and add the callback later. However, I want to be able to pass |
||
|
|
||
| ok = true | ||
| return gateway, nil | ||
| } | ||
|
|
||
| // NewWithLocalPort initializes a copy of an existing gateway which has all config fields identical | ||
| // to the existing gateway with the exception of the local port. | ||
| func NewWithLocalPort(gateway Gateway, port string) (*Gateway, error) { | ||
| func NewWithLocalPort(gateway *Gateway, port string) (*Gateway, error) { | ||
| if port == gateway.LocalPort() { | ||
| return nil, trace.BadParameter("port is already set to %s", port) | ||
| } | ||
|
|
@@ -207,6 +226,24 @@ func (g *Gateway) CLICommand() (string, error) { | |
| return cliCommand, nil | ||
| } | ||
|
|
||
| // ReloadCert loads the key pair from cfg.CertPath & cfg.KeyPath and updates the cert of the running | ||
| // local proxy. This is typically done after the cert is reissued and saved to disk. | ||
| // | ||
| // In the future, we're probably going to make this method accept the cert as an arg rather than | ||
| // reading from disk. | ||
| func (g *Gateway) ReloadCert() error { | ||
| g.cfg.Log.Debug("Reloading cert") | ||
|
|
||
| tlsCert, err := keys.LoadX509KeyPair(g.cfg.CertPath, g.cfg.KeyPath) | ||
| if err != nil { | ||
| return trace.Wrap(err) | ||
| } | ||
|
|
||
| g.localProxy.SetCerts([]tls.Certificate{tlsCert}) | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // Gateway describes local proxy that creates a gateway to the remote Teleport resource. | ||
| // | ||
| // Gateway is not safe for concurrent use in itself. However, all access to gateways is gated by | ||
|
|
||

Uh oh!
There was an error while loading. Please reload this page.