Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 37 additions & 4 deletions lib/config/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -1940,11 +1940,13 @@ func applyWindowsDesktopConfig(fc *FileConfig, cfg *servicecfg.Config) error {
return trace.Wrap(err)
}
cfg.WindowsDesktop.ShowDesktopWallpaper = fc.WindowsDesktop.ShowDesktopWallpaper
cfg.WindowsDesktop.Hosts, err = utils.AddrsFromStrings(fc.WindowsDesktop.Hosts, defaults.RDPListenPort)
if err != nil {
return trace.Wrap(err)
if len(fc.WindowsDesktop.ADHosts) > 0 {
log.Warnln("hosts field is deprecated, prefer static_hosts instead")
}
cfg.WindowsDesktop.NonADHosts, err = utils.AddrsFromStrings(fc.WindowsDesktop.NonADHosts, defaults.RDPListenPort)
if len(fc.WindowsDesktop.NonADHosts) > 0 {
log.Warnln("non_ad_hosts field is deprecated, prefer static_hosts instead")
}
cfg.WindowsDesktop.StaticHosts, err = staticHostsWithAddress(fc.WindowsDesktop)
if err != nil {
return trace.Wrap(err)
}
Expand Down Expand Up @@ -2018,6 +2020,37 @@ func applyWindowsDesktopConfig(fc *FileConfig, cfg *servicecfg.Config) error {
return nil
}

func staticHostsWithAddress(ws WindowsDesktopService) ([]servicecfg.WindowsHost, error) {
var hostsWithAddress []servicecfg.WindowsHost
var cfgHosts []WindowsHost
cfgHosts = append(cfgHosts, ws.StaticHosts...)
for _, host := range ws.NonADHosts {
cfgHosts = append(cfgHosts, WindowsHost{
Address: host,
AD: false,
})
}
for _, host := range ws.ADHosts {
cfgHosts = append(cfgHosts, WindowsHost{
Address: host,
AD: true,
})
}
for _, host := range cfgHosts {
addr, err := utils.ParseHostPortAddr(host.Address, defaults.RDPListenPort)
if err != nil {
return nil, trace.BadParameter("invalid addr %q", host.Address)
}
hostsWithAddress = append(hostsWithAddress, servicecfg.WindowsHost{
Name: host.Name,
Address: *addr,
Labels: host.Labels,
AD: host.AD,
})
}
return hostsWithAddress, nil
}

// applyTracingConfig applies file configuration for the "tracing_service" section.
func applyTracingConfig(fc *FileConfig, cfg *servicecfg.Config) error {
// Tracing is enabled.
Expand Down
16 changes: 8 additions & 8 deletions lib/config/configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@ func TestConfigReading(t *testing.T) {
ListenAddress: "tcp://windows_desktop",
},
PublicAddr: apiutils.Strings([]string{"winsrv.example.com:3028", "no-port.winsrv.example.com"}),
Hosts: apiutils.Strings([]string{"win.example.com:3389", "no-port.win.example.com"}),
ADHosts: apiutils.Strings([]string{"win.example.com:3389", "no-port.win.example.com"}),
},
Tracing: TracingService{
EnabledFlag: "yes",
Expand Down Expand Up @@ -1647,7 +1647,7 @@ func makeConfigFixture() string {
ListenAddress: "tcp://windows_desktop",
},
PublicAddr: apiutils.Strings([]string{"winsrv.example.com:3028", "no-port.winsrv.example.com"}),
Hosts: apiutils.Strings([]string{"win.example.com:3389", "no-port.win.example.com"}),
ADHosts: apiutils.Strings([]string{"win.example.com:3389", "no-port.win.example.com"}),
}

// Tracing service.
Expand Down Expand Up @@ -2128,7 +2128,7 @@ func TestWindowsDesktopService(t *testing.T) {
desc: "NOK - invalid static host addr",
expectError: require.Error,
mutate: func(fc *FileConfig) {
fc.WindowsDesktop.Hosts = []string{"badscheme://foo:1:2"}
fc.WindowsDesktop.ADHosts = []string{"badscheme://foo:1:2"}
},
},
{
Expand Down Expand Up @@ -2160,7 +2160,7 @@ func TestWindowsDesktopService(t *testing.T) {
desc: "NOK - hosts specified but ldap not specified",
expectError: require.Error,
mutate: func(fc *FileConfig) {
fc.WindowsDesktop.Hosts = []string{"127.0.0.1:3389"}
fc.WindowsDesktop.ADHosts = []string{"127.0.0.1:3389"}
fc.WindowsDesktop.LDAP = LDAPConfig{
Addr: "",
}
Expand All @@ -2170,7 +2170,7 @@ func TestWindowsDesktopService(t *testing.T) {
desc: "OK - hosts specified and ldap specified",
expectError: require.NoError,
mutate: func(fc *FileConfig) {
fc.WindowsDesktop.Hosts = []string{"127.0.0.1:3389"}
fc.WindowsDesktop.ADHosts = []string{"127.0.0.1:3389"}
fc.WindowsDesktop.LDAP = LDAPConfig{
Addr: "something",
}
Expand All @@ -2180,7 +2180,7 @@ func TestWindowsDesktopService(t *testing.T) {
desc: "OK - no hosts specified and ldap not specified",
expectError: require.NoError,
mutate: func(fc *FileConfig) {
fc.WindowsDesktop.Hosts = []string{}
fc.WindowsDesktop.ADHosts = []string{}
fc.WindowsDesktop.LDAP = LDAPConfig{
Addr: "",
}
Expand All @@ -2190,7 +2190,7 @@ func TestWindowsDesktopService(t *testing.T) {
desc: "OK - no hosts specified and ldap specified",
expectError: require.NoError,
mutate: func(fc *FileConfig) {
fc.WindowsDesktop.Hosts = []string{}
fc.WindowsDesktop.ADHosts = []string{}
fc.WindowsDesktop.LDAP = LDAPConfig{
Addr: "something",
}
Expand Down Expand Up @@ -2250,7 +2250,7 @@ func TestWindowsDesktopService(t *testing.T) {
mutate: func(fc *FileConfig) {
fc.WindowsDesktop.EnabledFlag = "yes"
fc.WindowsDesktop.ListenAddress = "0.0.0.0:3028"
fc.WindowsDesktop.Hosts = []string{"127.0.0.1:3389"}
fc.WindowsDesktop.ADHosts = []string{"127.0.0.1:3389"}
fc.WindowsDesktop.LDAP = LDAPConfig{
Addr: "something",
}
Expand Down
36 changes: 30 additions & 6 deletions lib/config/fileconf.go
Original file line number Diff line number Diff line change
Expand Up @@ -2202,13 +2202,20 @@ type WindowsDesktopService struct {
PKIDomain string `yaml:"pki_domain"`
// Discovery configures desktop discovery via LDAP.
Discovery LDAPDiscoveryConfig `yaml:"discovery,omitempty"`
// Hosts is a list of static, AD-connected Windows hosts. This gives users
// ADHosts is a list of static, AD-connected Windows hosts. This gives users
// a way to specify AD-connected hosts that won't be found by the filters
// specified in `discovery` (or if `discovery` is omitted).
Hosts []string `yaml:"hosts,omitempty"`
//
// Deprecated: prefer StaticHosts instead.
ADHosts []string `yaml:"hosts,omitempty"`
// NonADHosts is a list of standalone Windows hosts that are not
// jointed to an Active Directory domain.
//
// Deprecated: prefer StaticHosts instead.
NonADHosts []string `yaml:"non_ad_hosts,omitempty"`
// StaticHosts is a list of Windows hosts (both AD-connected and standalone).
// User can specify name for each host and labels specific to it.
StaticHosts []WindowsHost `yaml:"static_hosts,omitempty"`
// HostLabels optionally applies labels to Windows hosts for RBAC.
// A host can match multiple rules and will get a union of all
// the matched labels.
Expand All @@ -2217,9 +2224,13 @@ type WindowsDesktopService struct {

// Check checks whether the WindowsDesktopService is valid or not
func (wds *WindowsDesktopService) Check() error {
if len(wds.Hosts) > 0 && wds.LDAP.Addr == "" {
return trace.BadParameter("if hosts are specified in the windows_desktop_service, " +
"the ldap configuration for their corresponding Active Directory domain controller must also be specified")
hasAD := len(wds.ADHosts) > 0 || slices.ContainsFunc(wds.StaticHosts, func(host WindowsHost) bool {
return host.AD
})

if hasAD && wds.LDAP.Addr == "" {
return trace.BadParameter("if Active Directory hosts are specified in the windows_desktop_service, " +
"the ldap configuration must also be specified")
}

if wds.Discovery.BaseDN != "" && wds.LDAP.Addr == "" {
Expand All @@ -2230,7 +2241,7 @@ func (wds *WindowsDesktopService) Check() error {
return nil
}

// WindowsHostLabelRule describes how a set of labels should be a applied to
// WindowsHostLabelRule describes how a set of labels should be applied to
// a Windows host.
type WindowsHostLabelRule struct {
// Match is a regexp that is checked against the Windows host's DNS name.
Expand All @@ -2240,6 +2251,19 @@ type WindowsHostLabelRule struct {
Labels map[string]string `yaml:"labels"`
}

// WindowsHost describes single host in configuration
type WindowsHost struct {
// Name of the host
Name string `yaml:"name"`
// Address of the host, with an optional port.
// 10.1.103.4 or 10.1.103.4:3389, for example.
Address string `yaml:"addr"`
// Labels is the set of labels to apply to this host
Labels map[string]string `yaml:"labels"`
// AD tells if host is part of Active Directory domain
AD bool `yaml:"ad"`
}

// LDAPConfig is the LDAP connection parameters.
type LDAPConfig struct {
// Addr is the host:port of the LDAP server (typically port 389).
Expand Down
3 changes: 1 addition & 2 deletions lib/service/desktop.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,7 @@ func (process *TeleportProcess) initWindowsDesktopServiceRegistered(log *logrus.
Heartbeat: desktop.HeartbeatConfig{
HostUUID: cfg.HostUUID,
PublicAddr: publicAddr,
StaticHosts: cfg.WindowsDesktop.Hosts,
NonADHosts: cfg.WindowsDesktop.NonADHosts,
StaticHosts: cfg.WindowsDesktop.StaticHosts,
OnHeartbeat: process.OnHeartbeat(teleport.ComponentWindowsDesktop),
},
ShowDesktopWallpaper: cfg.WindowsDesktop.ShowDesktopWallpaper,
Expand Down
23 changes: 14 additions & 9 deletions lib/service/servicecfg/windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,9 @@ type WindowsDesktopConfig struct {
// Discovery configures automatic desktop discovery via LDAP.
Discovery LDAPDiscoveryConfig

// Hosts is an optional list of static Windows hosts to expose through this
// StaticHosts is an optional list of static Windows hosts to expose through this
// service.
// Hosts is an optional list of static, AD-connected Windows hosts. This gives users
// a way to specify AD-connected hosts that won't be found by the filters
// specified in Discovery (or if Discovery is omitted).
Hosts []utils.NetAddr

// NonADHosts is an optional list of static Windows hosts to expose through this
// service. These hosts are not part of Active Directory.
NonADHosts []utils.NetAddr
StaticHosts []WindowsHost

// ConnLimiter limits the connection and request rates.
ConnLimiter limiter.Config
Expand All @@ -63,6 +56,18 @@ type WindowsDesktopConfig struct {
Labels map[string]string
}

// WindowsHost is configuration for single Windows desktop host
type WindowsHost struct {
// Name that will be used in the Teleport UI
Name string
// Address of the remote Windows host
Address utils.NetAddr
// AD is true if the host is part of the Active Directory domain
AD bool
// Labels to be applied to the host
Labels map[string]string
}

// LDAPDiscoveryConfig is LDAP discovery configuration for windows desktop discovery service.
type LDAPDiscoveryConfig struct {
// BaseDN is the base DN to search for desktops.
Expand Down
48 changes: 24 additions & 24 deletions lib/srv/desktop/windows_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import (
"github.com/gravitational/teleport/lib/limiter"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/reversetunnel"
"github.com/gravitational/teleport/lib/service/servicecfg"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/session"
"github.com/gravitational/teleport/lib/srv"
Expand Down Expand Up @@ -205,10 +206,8 @@ type HeartbeatConfig struct {
PublicAddr string
// OnHeartbeat is called after each heartbeat attempt.
OnHeartbeat func(error)
// StaticHosts is an optional list of AD-connected static Windows hosts to register.
StaticHosts []utils.NetAddr
// NonADHosts is an optional list of static Windows hosts to register, that are not part of Active Directory.
NonADHosts []utils.NetAddr
// StaticHosts is an optional list of static Windows hosts to register
StaticHosts []servicecfg.WindowsHost
}

func (cfg *WindowsServiceConfig) checkAndSetDiscoveryDefaults() error {
Expand Down Expand Up @@ -398,7 +397,7 @@ func NewWindowsService(cfg WindowsServiceConfig) (*WindowsService, error) {
if err := s.startDesktopDiscovery(); err != nil {
return nil, trace.Wrap(err)
}
} else if len(s.cfg.Heartbeat.StaticHosts) == 0 && len(s.cfg.Heartbeat.NonADHosts) == 0 {
} else if len(s.cfg.Heartbeat.StaticHosts) == 0 {
s.cfg.Log.Warnln("desktop discovery via LDAP is disabled, and no hosts are defined in the configuration; there will be no Windows desktops available to connect")
} else {
s.cfg.Log.Infoln("desktop discovery via LDAP is disabled, set 'base_dn' to enable")
Expand Down Expand Up @@ -598,25 +597,21 @@ func (s *WindowsService) startServiceHeartbeat() error {
// service itself is running.
func (s *WindowsService) startStaticHostHeartbeats() error {
for _, host := range s.cfg.Heartbeat.StaticHosts {
if err := s.startStaticHostHeartbeat(host, false); err != nil {
if err := s.startStaticHostHeartbeat(host); err != nil {
return err
}
}
for _, host := range s.cfg.Heartbeat.NonADHosts {
if err := s.startStaticHostHeartbeat(host, true); err != nil {
return trace.Wrap(err)
}
}
return nil
}

func (s *WindowsService) startStaticHostHeartbeat(host utils.NetAddr, nonAD bool) error {
// startStaticHostHeartbeats spawns heartbeat goroutine for single host
func (s *WindowsService) startStaticHostHeartbeat(host servicecfg.WindowsHost) error {
heartbeat, err := srv.NewHeartbeat(srv.HeartbeatConfig{
Context: s.closeCtx,
Component: teleport.ComponentWindowsDesktop,
Mode: srv.HeartbeatModeWindowsDesktop,
Announcer: s.cfg.AccessPoint,
GetServerInfo: s.staticHostHeartbeatInfo(host, s.cfg.HostLabelsFn, nonAD),
GetServerInfo: s.staticHostHeartbeatInfo(host, s.cfg.HostLabelsFn),
KeepAlivePeriod: apidefaults.ServerKeepAliveTTL(),
AnnouncePeriod: apidefaults.ServerAnnounceTTL/2 + utils.RandomDuration(apidefaults.ServerAnnounceTTL/10),
CheckPeriod: 5 * time.Minute,
Expand Down Expand Up @@ -1102,28 +1097,33 @@ func (s *WindowsService) getServiceHeartbeatInfo() (types.Resource, error) {

// staticHostHeartbeatInfo generates the Windows Desktop resource
// for heartbeating statically defined hosts
func (s *WindowsService) staticHostHeartbeatInfo(netAddr utils.NetAddr,
getHostLabels func(string) map[string]string, nonAD bool,
func (s *WindowsService) staticHostHeartbeatInfo(host servicecfg.WindowsHost,
getHostLabels func(string) map[string]string,
) func() (types.Resource, error) {
return func() (types.Resource, error) {
addr := netAddr.String()
name, err := s.nameForStaticHost(addr)
if err != nil {
return nil, trace.Wrap(err)
}
// for static hosts, we match against the host's addr,
// as the name is a randomly generated UUID
addr := host.Address.String()
labels := getHostLabels(addr)
for k, v := range host.Labels {
labels[k] = v
}
name := host.Name
if name == "" {
var err error
name, err = s.nameForStaticHost(addr)
if err != nil {
return nil, trace.Wrap(err)
}
}
labels[types.OriginLabel] = types.OriginConfigFile
labels[types.ADLabel] = strconv.FormatBool(!nonAD)
labels[types.ADLabel] = strconv.FormatBool(host.AD)
desktop, err := types.NewWindowsDesktopV3(
name,
labels,
types.WindowsDesktopSpecV3{
Addr: addr,
Domain: s.cfg.Domain,
HostID: s.cfg.Heartbeat.HostUUID,
NonAD: nonAD,
NonAD: !host.AD,
})
if err != nil {
return nil, trace.Wrap(err)
Expand Down