From 4a4305247eb8d9031146b47c8b043f70f8286352 Mon Sep 17 00:00:00 2001 From: Kevin Petremann Date: Wed, 5 Jul 2023 16:22:01 +0200 Subject: [PATCH 01/11] feat(auth): make LDAP optional --- internal/api/auth/basic_auth.go | 40 +++++++++++++++++++++++++++++++++ internal/api/router/manager.go | 18 ++++++++++----- internal/config/settings.go | 2 ++ settings.example.yaml | 1 + 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/internal/api/auth/basic_auth.go b/internal/api/auth/basic_auth.go index 555f354..37b42ab 100644 --- a/internal/api/auth/basic_auth.go +++ b/internal/api/auth/basic_auth.go @@ -1,6 +1,7 @@ package auth import ( + "errors" "net/http" "github.com/julienschmidt/httprouter" @@ -12,6 +13,45 @@ const ( realm = `Basic realm="restricted"` ) +type authMode int + +const ( + NoAuth authMode = iota + LDAPMode +) + +type BasicAuth struct { + mode authMode + ldapAuth *LDAPAuth +} + +func NewBasicAuth() BasicAuth { return BasicAuth{mode: NoAuth} } + +func (b *BasicAuth) SetMode(mode authMode) { b.mode = mode } + +func (b *BasicAuth) ConfigureLdap(ldap *LDAPAuth) error { + if ldap == nil { + return errors.New("LDAP configuration is missing") + } + b.ldapAuth = ldap + + return nil +} + +func (b *BasicAuth) Wrap(next httprouter.Handle) httprouter.Handle { + switch b.mode { + case NoAuth: + return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { next(w, r, ps) } + case LDAPMode: + return BasicAuthLDAP(b.ldapAuth, next) + default: + return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + http.Error(w, "authentication issue: bad server configuration", http.StatusInternalServerError) + } + } + +} + // BasicAuthLDAP is a middleware wrapping the target HTTP HandlerFunc. // It retrieves BasicAuth credentials and authenticate against LDAP. func BasicAuthLDAP(ldapAuth *LDAPAuth, next httprouter.Handle) httprouter.Handle { diff --git a/internal/api/router/manager.go b/internal/api/router/manager.go index d085c9f..d0f9e76 100644 --- a/internal/api/router/manager.go +++ b/internal/api/router/manager.go @@ -42,16 +42,22 @@ func (m *Manager) ListenAndServe(ctx context.Context, address string, port int) tlsConfig := &tls.Config{ InsecureSkipVerify: config.Cfg.LDAP.InsecureSkipVerify, //nolint:gosec // configurable on purpose } - ldap := auth.NewLDAPAuth(config.Cfg.LDAP.URL, config.Cfg.LDAP.BindDN, config.Cfg.LDAP.Password, config.Cfg.LDAP.BaseDN, tlsConfig) + + withAuth := auth.NewBasicAuth() + if config.Cfg.LDAP.Enabled { + ldap := auth.NewLDAPAuth(config.Cfg.LDAP.URL, config.Cfg.LDAP.BindDN, config.Cfg.LDAP.Password, config.Cfg.LDAP.BaseDN, tlsConfig) + withAuth.ConfigureLdap(ldap) + withAuth.SetMode(auth.LDAPMode) + } router := httprouter.New() router.GET("/api/health", healthCheck) - router.GET("/v1/devices/:hostname/afk_enabled", auth.BasicAuthLDAP(ldap, m.getAFKEnabled)) - router.GET("/v1/devices/:hostname/openconfig", auth.BasicAuthLDAP(ldap, m.getDeviceOpenConfig)) - router.GET("/v1/report/last", auth.BasicAuthLDAP(ldap, m.getLastReport)) - router.GET("/v1/report/last/complete", auth.BasicAuthLDAP(ldap, m.getLastCompleteReport)) - router.GET("/v1/report/last/successful", auth.BasicAuthLDAP(ldap, m.getLastSuccessfulReport)) + router.GET("/v1/devices/:hostname/afk_enabled", withAuth.Wrap(m.getAFKEnabled)) + router.GET("/v1/devices/:hostname/openconfig", withAuth.Wrap(m.getDeviceOpenConfig)) + router.GET("/v1/report/last", withAuth.Wrap(m.getLastReport)) + router.GET("/v1/report/last/complete", withAuth.Wrap(m.getLastCompleteReport)) + router.GET("/v1/report/last/successful", withAuth.Wrap(m.getLastSuccessfulReport)) listenSocket := fmt.Sprint(address, ":", port) log.Info().Msgf("Start webserver - listening on %s", listenSocket) diff --git a/internal/config/settings.go b/internal/config/settings.go index 3c07081..54e913b 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -31,6 +31,7 @@ type Config struct { APIKey string } LDAP struct { + Enabled bool URL string BaseDN string BindDN string @@ -56,6 +57,7 @@ func setDefaults() { viper.SetDefault("Build.Interval", time.Minute) viper.SetDefault("Build.AllDevicesMustBuild", false) + viper.SetDefault("LDAP.Enabled", false) viper.SetDefault("LDAP.URL", "") viper.SetDefault("LDAP.BaseDN", "") viper.SetDefault("LDAP.BindDN", "") diff --git a/settings.example.yaml b/settings.example.yaml index 3767bfb..95c24ff 100644 --- a/settings.example.yaml +++ b/settings.example.yaml @@ -6,6 +6,7 @@ API: ListenPort: 1234 LDAP: + Enabled: true InsecureSkipVerify: "false" URL: "ldaps://URL.local" BindDN: "cn=,OU=,DC=" From 70a78a8702bbaf476790d20aac8dd961ff7a4736 Mon Sep 17 00:00:00 2001 From: Kevin Petremann Date: Tue, 11 Jul 2023 18:56:23 +0200 Subject: [PATCH 02/11] ci: fix issues reported by golangci --- cmd/data-aggregation-api/main.go | 4 +++- internal/api/auth/basic_auth.go | 3 +-- internal/api/router/manager.go | 8 ++++++-- internal/config/settings.go | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/cmd/data-aggregation-api/main.go b/cmd/data-aggregation-api/main.go index fc2989b..b06289f 100644 --- a/cmd/data-aggregation-api/main.go +++ b/cmd/data-aggregation-api/main.go @@ -56,7 +56,9 @@ func run() error { // TODO: be able to close goroutine when the context is closed (graceful shutdown) go job.StartBuildLoop(&deviceRepo, &reports) - router.NewManager(&deviceRepo, &reports).ListenAndServe(ctx, config.Cfg.API.ListenAddress, config.Cfg.API.ListenPort) + if err := router.NewManager(&deviceRepo, &reports).ListenAndServe(ctx, config.Cfg.API.ListenAddress, config.Cfg.API.ListenPort); err != nil { + log.Error().Err(err).Msg("webserver error") + } return nil } diff --git a/internal/api/auth/basic_auth.go b/internal/api/auth/basic_auth.go index 37b42ab..607d932 100644 --- a/internal/api/auth/basic_auth.go +++ b/internal/api/auth/basic_auth.go @@ -21,8 +21,8 @@ const ( ) type BasicAuth struct { - mode authMode ldapAuth *LDAPAuth + mode authMode } func NewBasicAuth() BasicAuth { return BasicAuth{mode: NoAuth} } @@ -49,7 +49,6 @@ func (b *BasicAuth) Wrap(next httprouter.Handle) httprouter.Handle { http.Error(w, "authentication issue: bad server configuration", http.StatusInternalServerError) } } - } // BasicAuthLDAP is a middleware wrapping the target HTTP HandlerFunc. diff --git a/internal/api/router/manager.go b/internal/api/router/manager.go index d0f9e76..9bb82c5 100644 --- a/internal/api/router/manager.go +++ b/internal/api/router/manager.go @@ -34,7 +34,7 @@ func NewManager(deviceRepo DevicesRepository, reports *report.Repository) *Manag } // ListenAndServe starts to serve Web API requests. -func (m *Manager) ListenAndServe(ctx context.Context, address string, port int) { +func (m *Manager) ListenAndServe(ctx context.Context, address string, port int) error { defer func() { log.Warn().Msg("Shutdown.") }() @@ -46,7 +46,9 @@ func (m *Manager) ListenAndServe(ctx context.Context, address string, port int) withAuth := auth.NewBasicAuth() if config.Cfg.LDAP.Enabled { ldap := auth.NewLDAPAuth(config.Cfg.LDAP.URL, config.Cfg.LDAP.BindDN, config.Cfg.LDAP.Password, config.Cfg.LDAP.BaseDN, tlsConfig) - withAuth.ConfigureLdap(ldap) + if err := withAuth.ConfigureLdap(ldap); err != nil { + return fmt.Errorf("failed to configure: %w", err) + } withAuth.SetMode(auth.LDAPMode) } @@ -75,4 +77,6 @@ func (m *Manager) ListenAndServe(ctx context.Context, address string, port int) if err := httpServer.Shutdown(context.Background()); err != nil { log.Error().Err(err).Send() } + + return nil } diff --git a/internal/config/settings.go b/internal/config/settings.go index 54e913b..2f3723e 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -31,12 +31,12 @@ type Config struct { APIKey string } LDAP struct { - Enabled bool URL string BaseDN string BindDN string Password string InsecureSkipVerify bool + Enabled bool } Build struct { Interval time.Duration From da350144dd144c361f9108cc98c99fdd8ef802ea Mon Sep 17 00:00:00 2001 From: Kevin Petremann <13746171+kpetremann@users.noreply.github.com> Date: Wed, 12 Jul 2023 12:51:23 +0200 Subject: [PATCH 03/11] chore: improve failure authentication log Co-authored-by: Sergey Shpak --- internal/api/router/manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/api/router/manager.go b/internal/api/router/manager.go index 9bb82c5..a7e3d5d 100644 --- a/internal/api/router/manager.go +++ b/internal/api/router/manager.go @@ -47,7 +47,7 @@ func (m *Manager) ListenAndServe(ctx context.Context, address string, port int) if config.Cfg.LDAP.Enabled { ldap := auth.NewLDAPAuth(config.Cfg.LDAP.URL, config.Cfg.LDAP.BindDN, config.Cfg.LDAP.Password, config.Cfg.LDAP.BaseDN, tlsConfig) if err := withAuth.ConfigureLdap(ldap); err != nil { - return fmt.Errorf("failed to configure: %w", err) + return fmt.Errorf("failed to configure the request authenticator: %w", err) } withAuth.SetMode(auth.LDAPMode) } From 2c53896f8c09cd16a620cf48dd1bf4549027f7f8 Mon Sep 17 00:00:00 2001 From: Kevin Petremann Date: Wed, 12 Jul 2023 12:57:26 +0200 Subject: [PATCH 04/11] refactor(auth): set mode at BasicAuth instantiation --- internal/api/auth/basic_auth.go | 4 +--- internal/api/router/manager.go | 7 +++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/api/auth/basic_auth.go b/internal/api/auth/basic_auth.go index 607d932..417824e 100644 --- a/internal/api/auth/basic_auth.go +++ b/internal/api/auth/basic_auth.go @@ -25,9 +25,7 @@ type BasicAuth struct { mode authMode } -func NewBasicAuth() BasicAuth { return BasicAuth{mode: NoAuth} } - -func (b *BasicAuth) SetMode(mode authMode) { b.mode = mode } +func NewBasicAuth(mode authMode) BasicAuth { return BasicAuth{mode: mode} } func (b *BasicAuth) ConfigureLdap(ldap *LDAPAuth) error { if ldap == nil { diff --git a/internal/api/router/manager.go b/internal/api/router/manager.go index a7e3d5d..68efd12 100644 --- a/internal/api/router/manager.go +++ b/internal/api/router/manager.go @@ -43,13 +43,16 @@ func (m *Manager) ListenAndServe(ctx context.Context, address string, port int) InsecureSkipVerify: config.Cfg.LDAP.InsecureSkipVerify, //nolint:gosec // configurable on purpose } - withAuth := auth.NewBasicAuth() + var withAuth auth.BasicAuth if config.Cfg.LDAP.Enabled { + withAuth = auth.NewBasicAuth(auth.LDAPMode) + ldap := auth.NewLDAPAuth(config.Cfg.LDAP.URL, config.Cfg.LDAP.BindDN, config.Cfg.LDAP.Password, config.Cfg.LDAP.BaseDN, tlsConfig) if err := withAuth.ConfigureLdap(ldap); err != nil { return fmt.Errorf("failed to configure the request authenticator: %w", err) } - withAuth.SetMode(auth.LDAPMode) + } else { + withAuth = auth.NewBasicAuth(auth.NoAuth) } router := httprouter.New() From 7ac5c5dea5144a8568f109186b1e3aace6de89f3 Mon Sep 17 00:00:00 2001 From: Kevin Petremann Date: Wed, 12 Jul 2023 13:03:59 +0200 Subject: [PATCH 05/11] chore: log 500 authentication issue --- internal/api/auth/basic_auth.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/api/auth/basic_auth.go b/internal/api/auth/basic_auth.go index 417824e..8841e13 100644 --- a/internal/api/auth/basic_auth.go +++ b/internal/api/auth/basic_auth.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/julienschmidt/httprouter" + "github.com/rs/zerolog/log" ) const ( @@ -44,6 +45,7 @@ func (b *BasicAuth) Wrap(next httprouter.Handle) httprouter.Handle { return BasicAuthLDAP(b.ldapAuth, next) default: return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + log.Error().Str("authentication issue", "bad server configuration").Send() http.Error(w, "authentication issue: bad server configuration", http.StatusInternalServerError) } } From 3b5a20d9e961b786292c1080766e5b4426cbcb9d Mon Sep 17 00:00:00 2001 From: Kevin Petremann Date: Wed, 12 Jul 2023 13:07:17 +0200 Subject: [PATCH 06/11] chore: return the error to main when webserver error occurs --- cmd/data-aggregation-api/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/data-aggregation-api/main.go b/cmd/data-aggregation-api/main.go index b06289f..27cb2c7 100644 --- a/cmd/data-aggregation-api/main.go +++ b/cmd/data-aggregation-api/main.go @@ -57,7 +57,7 @@ func run() error { go job.StartBuildLoop(&deviceRepo, &reports) if err := router.NewManager(&deviceRepo, &reports).ListenAndServe(ctx, config.Cfg.API.ListenAddress, config.Cfg.API.ListenPort); err != nil { - log.Error().Err(err).Msg("webserver error") + return fmt.Errorf("webserver error: %w", err) } return nil From d1d6126b4782ce0fe4c7ccea225638346e116879 Mon Sep 17 00:00:00 2001 From: Kevin Petremann Date: Wed, 12 Jul 2023 14:35:17 +0200 Subject: [PATCH 07/11] refactor(auth)!: simplify ldap init BREAKING CHANGE: configuration tree change --- internal/api/auth/basic_auth.go | 22 +++++++++++++++++++++- internal/api/router/manager.go | 18 +++--------------- internal/config/settings.go | 23 +++++++++++++++-------- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/internal/api/auth/basic_auth.go b/internal/api/auth/basic_auth.go index 8841e13..34b8ee1 100644 --- a/internal/api/auth/basic_auth.go +++ b/internal/api/auth/basic_auth.go @@ -1,9 +1,12 @@ package auth import ( + "crypto/tls" "errors" + "fmt" "net/http" + "github.com/criteo/data-aggregation-api/internal/config" "github.com/julienschmidt/httprouter" "github.com/rs/zerolog/log" ) @@ -26,7 +29,24 @@ type BasicAuth struct { mode authMode } -func NewBasicAuth(mode authMode) BasicAuth { return BasicAuth{mode: mode} } +func NewBasicAuth(cfg config.AuthConfig) (BasicAuth, error) { + b := BasicAuth{mode: NoAuth} + + if cfg.LDAP == nil { + return b, nil + } + + tlsConfig := &tls.Config{ + InsecureSkipVerify: cfg.LDAP.InsecureSkipVerify, //nolint:gosec // configurable on purpose + } + ldap := NewLDAPAuth(cfg.LDAP.URL, cfg.LDAP.BindDN, cfg.LDAP.Password, cfg.LDAP.BaseDN, tlsConfig) + if err := b.ConfigureLdap(ldap); err != nil { + return b, fmt.Errorf("failed to configure the request authenticator: %w", err) + } + b.mode = LDAPMode + + return b, nil +} func (b *BasicAuth) ConfigureLdap(ldap *LDAPAuth) error { if ldap == nil { diff --git a/internal/api/router/manager.go b/internal/api/router/manager.go index 68efd12..3aacd9d 100644 --- a/internal/api/router/manager.go +++ b/internal/api/router/manager.go @@ -2,7 +2,6 @@ package router import ( "context" - "crypto/tls" "fmt" "net/http" @@ -39,20 +38,9 @@ func (m *Manager) ListenAndServe(ctx context.Context, address string, port int) log.Warn().Msg("Shutdown.") }() - tlsConfig := &tls.Config{ - InsecureSkipVerify: config.Cfg.LDAP.InsecureSkipVerify, //nolint:gosec // configurable on purpose - } - - var withAuth auth.BasicAuth - if config.Cfg.LDAP.Enabled { - withAuth = auth.NewBasicAuth(auth.LDAPMode) - - ldap := auth.NewLDAPAuth(config.Cfg.LDAP.URL, config.Cfg.LDAP.BindDN, config.Cfg.LDAP.Password, config.Cfg.LDAP.BaseDN, tlsConfig) - if err := withAuth.ConfigureLdap(ldap); err != nil { - return fmt.Errorf("failed to configure the request authenticator: %w", err) - } - } else { - withAuth = auth.NewBasicAuth(auth.NoAuth) + withAuth, err := auth.NewBasicAuth(config.Cfg.Authentication) + if err != nil { + return err } router := httprouter.New() diff --git a/internal/config/settings.go b/internal/config/settings.go index 2f3723e..3d3804b 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -30,20 +30,27 @@ type Config struct { URL string APIKey string } - LDAP struct { - URL string - BaseDN string - BindDN string - Password string - InsecureSkipVerify bool - Enabled bool - } + + Authentication AuthConfig + Build struct { Interval time.Duration AllDevicesMustBuild bool } } +type AuthConfig struct { + LDAP *LDAPConfig +} + +type LDAPConfig struct { + URL string + BaseDN string + BindDN string + Password string + InsecureSkipVerify bool +} + func setDefaults() { viper.SetDefault("Datacenter", "") viper.SetDefault("LogLevel", "info") From fa961588adeb64b89385fb3683a736eaa218913d Mon Sep 17 00:00:00 2001 From: Kevin Petremann Date: Wed, 12 Jul 2023 14:39:27 +0200 Subject: [PATCH 08/11] chore: fix settings struct alignment --- internal/config/settings.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/internal/config/settings.go b/internal/config/settings.go index 3d3804b..4875c00 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -19,20 +19,17 @@ var ( ) type Config struct { + Authentication AuthConfig + NetBox struct { + URL string + APIKey string + } LogLevel string Datacenter string - - API struct { + API struct { ListenAddress string ListenPort int } - NetBox struct { - URL string - APIKey string - } - - Authentication AuthConfig - Build struct { Interval time.Duration AllDevicesMustBuild bool From 3dbb7b2dc1ec0947547c009e962d78bb83f29cd3 Mon Sep 17 00:00:00 2001 From: Kevin Petremann Date: Wed, 12 Jul 2023 14:42:30 +0200 Subject: [PATCH 09/11] chore(auth): add auth-method in log failure --- internal/api/auth/basic_auth.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/api/auth/basic_auth.go b/internal/api/auth/basic_auth.go index 34b8ee1..c081a72 100644 --- a/internal/api/auth/basic_auth.go +++ b/internal/api/auth/basic_auth.go @@ -17,11 +17,11 @@ const ( realm = `Basic realm="restricted"` ) -type authMode int +type authMode string const ( - NoAuth authMode = iota - LDAPMode + noAuth authMode = "None" + ldapMode authMode = "LDAP" ) type BasicAuth struct { @@ -30,7 +30,7 @@ type BasicAuth struct { } func NewBasicAuth(cfg config.AuthConfig) (BasicAuth, error) { - b := BasicAuth{mode: NoAuth} + b := BasicAuth{mode: noAuth} if cfg.LDAP == nil { return b, nil @@ -43,7 +43,7 @@ func NewBasicAuth(cfg config.AuthConfig) (BasicAuth, error) { if err := b.ConfigureLdap(ldap); err != nil { return b, fmt.Errorf("failed to configure the request authenticator: %w", err) } - b.mode = LDAPMode + b.mode = ldapMode return b, nil } @@ -59,13 +59,13 @@ func (b *BasicAuth) ConfigureLdap(ldap *LDAPAuth) error { func (b *BasicAuth) Wrap(next httprouter.Handle) httprouter.Handle { switch b.mode { - case NoAuth: + case noAuth: return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { next(w, r, ps) } - case LDAPMode: + case ldapMode: return BasicAuthLDAP(b.ldapAuth, next) default: return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - log.Error().Str("authentication issue", "bad server configuration").Send() + log.Error().Str("auth-method", string(b.mode)).Str("authentication issue", "bad server configuration").Send() http.Error(w, "authentication issue: bad server configuration", http.StatusInternalServerError) } } From c051d3cbadce677b1772752461f1dea2908c9fb6 Mon Sep 17 00:00:00 2001 From: Kevin Petremann Date: Wed, 12 Jul 2023 18:32:49 +0200 Subject: [PATCH 10/11] chore(auth): do not export configureLDAP method --- internal/api/auth/basic_auth.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/api/auth/basic_auth.go b/internal/api/auth/basic_auth.go index c081a72..f83d995 100644 --- a/internal/api/auth/basic_auth.go +++ b/internal/api/auth/basic_auth.go @@ -40,7 +40,7 @@ func NewBasicAuth(cfg config.AuthConfig) (BasicAuth, error) { InsecureSkipVerify: cfg.LDAP.InsecureSkipVerify, //nolint:gosec // configurable on purpose } ldap := NewLDAPAuth(cfg.LDAP.URL, cfg.LDAP.BindDN, cfg.LDAP.Password, cfg.LDAP.BaseDN, tlsConfig) - if err := b.ConfigureLdap(ldap); err != nil { + if err := b.configureLdap(ldap); err != nil { return b, fmt.Errorf("failed to configure the request authenticator: %w", err) } b.mode = ldapMode @@ -48,7 +48,7 @@ func NewBasicAuth(cfg config.AuthConfig) (BasicAuth, error) { return b, nil } -func (b *BasicAuth) ConfigureLdap(ldap *LDAPAuth) error { +func (b *BasicAuth) configureLdap(ldap *LDAPAuth) error { if ldap == nil { return errors.New("LDAP configuration is missing") } From 2103960503dfa519834d78bd9078793680e6b980 Mon Sep 17 00:00:00 2001 From: Kevin Petremann Date: Wed, 12 Jul 2023 18:33:48 +0200 Subject: [PATCH 11/11] docs: update authentication configuration example --- settings.example.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/settings.example.yaml b/settings.example.yaml index 95c24ff..dc77092 100644 --- a/settings.example.yaml +++ b/settings.example.yaml @@ -5,13 +5,13 @@ API: ListenAddress: "127.0.0.1" ListenPort: 1234 -LDAP: - Enabled: true - InsecureSkipVerify: "false" - URL: "ldaps://URL.local" - BindDN: "cn=,OU=,DC=" - BaseDN: "DC=" - Password: "" +Authentication: + LDAP: + InsecureSkipVerify: "false" + URL: "ldaps://URL.local" + BindDN: "cn=,OU=,DC=" + BaseDN: "DC=" + Password: "" NetBox: URL: "https://netbox.local"