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

Automatically calculate public_addr field for dynamic apps (#10941). #10943

Merged
merged 10 commits into from
Mar 15, 2022
34 changes: 2 additions & 32 deletions lib/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -4008,7 +4008,8 @@ func getPublicAddr(authClient auth.ReadAppsAccessPoint, a App) (string, error) {
for {
select {
case <-ticker.C:
publicAddr, err := findPublicAddr(authClient, a)
publicAddr, err := app.FindPublicAddr(authClient, a.PublicAddr, a.Name)

if err == nil {
return publicAddr, nil
}
Expand All @@ -4018,37 +4019,6 @@ func getPublicAddr(authClient auth.ReadAppsAccessPoint, a App) (string, error) {
}
}

// findPublicAddr tries to resolve the public address of the proxy of this cluster.
func findPublicAddr(authClient auth.ReadAppsAccessPoint, a App) (string, error) {
// If the application has a public address already set, use it.
if a.PublicAddr != "" {
return a.PublicAddr, nil
}

// Fetch list of proxies, if first has public address set, use it.
servers, err := authClient.GetProxies()
if err != nil {
return "", trace.Wrap(err)
}
if len(servers) == 0 {
return "", trace.BadParameter("cluster has no proxied registered, at least one proxy must be registered for application access")
}
if servers[0].GetPublicAddr() != "" {
addr, err := utils.ParseAddr(servers[0].GetPublicAddr())
if err != nil {
return "", trace.Wrap(err)
}
return fmt.Sprintf("%v.%v", a.Name, addr.Host()), nil
}

// Fall back to cluster name.
cn, err := authClient.GetClusterName()
if err != nil {
return "", trace.Wrap(err)
}
return fmt.Sprintf("%v.%v", a.Name, cn.GetClusterName()), nil
}

// newHTTPFileSystem creates a new HTTP file system for the web handler.
// It uses external configuration to make the decision
func newHTTPFileSystem() (http.FileSystem, error) {
Expand Down
56 changes: 54 additions & 2 deletions lib/srv/app/watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ package app

import (
"context"
"fmt"

"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/services"

"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/trace"
)

Expand Down Expand Up @@ -82,7 +84,11 @@ func (s *Server) startResourceWatcher(ctx context.Context) (*services.AppWatcher
for {
select {
case apps := <-watcher.AppsC:
s.monitoredApps.setResources(apps)
appsWithAddr := make(types.Apps, 0, len(apps))
for _, app := range apps {
appsWithAddr = append(appsWithAddr, s.guessPublicAddr(app))
}
s.monitoredApps.setResources(appsWithAddr)
select {
case s.reconcileCh <- struct{}{}:
case <-ctx.Done():
Expand All @@ -97,6 +103,52 @@ func (s *Server) startResourceWatcher(ctx context.Context) (*services.AppWatcher
return watcher, nil
}

// guessPublicAddr will guess PublicAddr for given application if it is missing, based on proxy information and app name.
func (s *Server) guessPublicAddr(app types.Application) types.Application {
Tener marked this conversation as resolved.
Show resolved Hide resolved
if app.GetPublicAddr() != "" {
return app
}
appCopy := app.Copy()
pubAddr, err := FindPublicAddr(s.c.AccessPoint, app.GetPublicAddr(), app.GetName())
if err == nil {
appCopy.Spec.PublicAddr = pubAddr
} else {
s.log.WithError(err).Errorf("Unable to find public address for app %q, leaving empty.", app.GetName())
}
return appCopy
}

// FindPublicAddr tries to resolve the public address of the proxy of this cluster.
func FindPublicAddr(authClient auth.ReadAppsAccessPoint, appPublicAddr string, appName string) (string, error) {
// If the application has a public address already set, use it.
if appPublicAddr != "" {
return appPublicAddr, nil
}

// Fetch list of proxies, if first has public address set, use it.
servers, err := authClient.GetProxies()
if err != nil {
return "", trace.Wrap(err)
}
if len(servers) == 0 {
return "", trace.BadParameter("cluster has no proxy registered, at least one proxy must be registered for application access")
}
if servers[0].GetPublicAddr() != "" {
addr, err := utils.ParseAddr(servers[0].GetPublicAddr())
if err != nil {
return "", trace.Wrap(err)
}
return fmt.Sprintf("%v.%v", appName, addr.Host()), nil
}

// Fall back to cluster name.
cn, err := authClient.GetClusterName()
if err != nil {
return "", trace.Wrap(err)
}
return fmt.Sprintf("%v.%v", appName, cn.GetClusterName()), nil
}

func (s *Server) getResources() (resources types.ResourcesWithLabelsMap) {
return s.getApps().AsResources().ToMap()
}
Expand Down