Merge branch 'master' into chore/rewire-ldap-packages

This commit is contained in:
Chris F Ravenscroft
2023-12-10 15:22:10 -08:00
committed by GitHub
18 changed files with 211 additions and 48 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ Go-lang LDAP Authentication (GLAuth) is a secure, easy-to-use, LDAP server w/ co
![GitHub all releases](https://img.shields.io/github/downloads/glauth/glauth/total)
![Docker pulls](https://badgen.net/docker/pulls/glauth/glauth)
![GitHub last commit (branch)](https://img.shields.io/github/last-commit/glauth/glauth/maaster)
![GitHub last commit (branch)](https://img.shields.io/github/last-commit/glauth/glauth/master)
![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability-percentage/glauth/glauth)
* Centrally manage accounts across your infrastructure
+14
View File
@@ -1,6 +1,7 @@
package main
import (
"crypto/tls"
"fmt"
"net/http"
"os"
@@ -14,6 +15,7 @@ import (
docopt "github.com/docopt/docopt-go"
"github.com/fsnotify/fsnotify"
"github.com/glauth/glauth/v2/internal/monitoring"
_tls "github.com/glauth/glauth/v2/internal/tls"
"github.com/glauth/glauth/v2/internal/toml"
"github.com/glauth/glauth/v2/internal/version"
"github.com/glauth/glauth/v2/pkg/config"
@@ -131,10 +133,22 @@ func startService() {
startConfigWatcher()
var err error
var tlsConfig *tls.Config
if c := activeConfig.LDAP; c.Enabled && c.TLS {
// TODO check if tls params are string or bytes and change config accordingly
tlsConfig, err = _tls.MakeTLS([]byte(c.TLSCert), []byte(c.TLSKey))
if err != nil {
log.Warn().Err(err).Msg("unable to configure TLS, proceeding without....StartTLS won't be supported")
}
}
s, err := server.NewServer(
server.Logger(log),
server.Config(activeConfig),
server.Monitor(monitor),
server.TLSConfig(tlsConfig),
)
if err != nil {
+53 -41
View File
@@ -1,6 +1,7 @@
package main
import (
"os"
"os/exec"
"strings"
"testing"
@@ -26,50 +27,61 @@ type testEnv struct {
func TestIntegerStuff(t *testing.T) {
Convey("Testing sample-simple local file-based LDAP server", t, func() {
svc := startSvc(SD, "bin/glauth", "-c", "sample-simple.cfg")
batteryOfTests(
t,
svc, testEnv{
checkanonymousrootDSE: true,
checkTOTP: true,
checkbindUPN: true,
expectedinfo: "supportedLDAPVersion: 3",
svcdn: "cn=serviceuser,ou=svcaccts,dc=glauth,dc=com",
svcdnnogroup: "cn=serviceuser,dc=glauth,dc=com",
otpdn: "cn=otpuser,ou=superheros,dc=glauth,dc=com",
expectedaccount: "dn: cn=hackers,ou=superheros,ou=users,dc=glauth,dc=com",
scopedaccount: "dn: cn=hackers,ou=superheros,dc=glauth,dc=com",
expectedfirstaccount: "dn: cn=hackers,ou=superheros,ou=users,dc=glauth,dc=com",
expectedgroup: "dn: ou=superheros,ou=users,dc=glauth,dc=com",
checkemployeetype: "cn=hackers",
})
stopSvc(svc)
})
Convey("Preflight: Testing pre-requisites", t, func() {
Convey("bin/glauth should exist", func() {
info, err := os.Stat("bin/glauth")
So(err, ShouldEqual, nil)
Convey("bin/glauth should be executable", func() {
mode := uint32(info.Mode())
So(mode&0b001001001, ShouldNotEqual, 0)
matchingLibrary := doRunGetFirst(RD, "ls", "bin/sqlite.so")
if matchingLibrary == "bin/sqlite.so" {
Convey("Testing sample-database LDAP server", t, func() {
svc := startSvc(SD, "bin/glauth", "-c", "pkg/plugins/sample-database.cfg")
batteryOfTests(
t,
svc, testEnv{
checkanonymousrootDSE: true,
checkTOTP: false,
checkbindUPN: true,
expectedinfo: "supportedLDAPVersion: 3",
svcdn: "cn=serviceuser,ou=svcaccts,dc=glauth,dc=com",
svcdnnogroup: "cn=serviceuser,dc=glauth,dc=com",
otpdn: "cn=otpuser,ou=superheros,dc=glauth,dc=com",
expectedaccount: "dn: cn=hackers,ou=superheros,ou=users,dc=glauth,dc=com",
scopedaccount: "dn: cn=hackers,ou=superheros,dc=glauth,dc=com",
expectedfirstaccount: "dn: cn=hackers,ou=superheros,ou=users,dc=glauth,dc=com",
expectedgroup: "dn: ou=superheros,ou=users,dc=glauth,dc=com",
checkemployeetype: "",
Convey("Testing sample-simple local file-based LDAP server", func() {
svc := startSvc(SD, "bin/glauth", "-c", "sample-simple.cfg")
batteryOfTests(
t,
svc, testEnv{
checkanonymousrootDSE: true,
checkTOTP: true,
checkbindUPN: true,
expectedinfo: "supportedLDAPVersion: 3",
svcdn: "cn=serviceuser,ou=svcaccts,dc=glauth,dc=com",
svcdnnogroup: "cn=serviceuser,dc=glauth,dc=com",
otpdn: "cn=otpuser,ou=superheros,dc=glauth,dc=com",
expectedaccount: "dn: cn=hackers,ou=superheros,ou=users,dc=glauth,dc=com",
scopedaccount: "dn: cn=hackers,ou=superheros,dc=glauth,dc=com",
expectedfirstaccount: "dn: cn=hackers,ou=superheros,ou=users,dc=glauth,dc=com",
expectedgroup: "dn: ou=superheros,ou=users,dc=glauth,dc=com",
checkemployeetype: "cn=hackers",
})
stopSvc(svc)
})
stopSvc(svc)
matchingLibrary := doRunGetFirst(RD, "ls", "bin/sqlite.so")
if matchingLibrary == "bin/sqlite.so" {
Convey("Testing sample-database LDAP server", func() {
svc := startSvc(SD, "bin/glauth", "-c", "pkg/plugins/sample-database.cfg")
batteryOfTests(
t,
svc, testEnv{
checkanonymousrootDSE: true,
checkTOTP: false,
checkbindUPN: true,
expectedinfo: "supportedLDAPVersion: 3",
svcdn: "cn=serviceuser,ou=svcaccts,dc=glauth,dc=com",
svcdnnogroup: "cn=serviceuser,dc=glauth,dc=com",
otpdn: "cn=otpuser,ou=superheros,dc=glauth,dc=com",
expectedaccount: "dn: cn=hackers,ou=superheros,ou=users,dc=glauth,dc=com",
scopedaccount: "dn: cn=hackers,ou=superheros,dc=glauth,dc=com",
expectedfirstaccount: "dn: cn=hackers,ou=superheros,ou=users,dc=glauth,dc=com",
expectedgroup: "dn: ou=superheros,ou=users,dc=glauth,dc=com",
checkemployeetype: "",
})
stopSvc(svc)
})
}
})
})
}
})
matchingContainers := doRunGetFirst(RD, "sh", "-c", "docker ps | grep openldap-service | wc -l")
if matchingContainers == "1" {
+4
View File
@@ -42,3 +42,7 @@ require (
golang.org/x/sys v0.13.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
)
replace github.com/nmcclain/ldap => github.com/shipperizer/ldap v0.0.0-20231117095347-c1d6afe484ca
// replace github.com/nmcclain/ldap => ../../../ldap
+2
View File
@@ -74,6 +74,8 @@ github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDN
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/shipperizer/ldap v0.0.0-20231117095347-c1d6afe484ca h1:KSpZYIDb2ei4d+AxsFmC9KJaTNlmSiONSl6hLd5Qmfg=
github.com/shipperizer/ldap v0.0.0-20231117095347-c1d6afe484ca/go.mod h1:/KinL0zHM54Sk4gMgvODL26YGG7u4SFBOAKNpJmsJvc=
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
+81
View File
@@ -0,0 +1,81 @@
package tls
import (
tls "crypto/tls"
"crypto/x509"
"encoding/pem"
"github.com/rs/zerolog"
)
var (
log zerolog.Logger
)
// MakeTLS generates a tls.Config
func MakeTLS(clientCert, key []byte) (*tls.Config, error) {
if clientCert == nil && key == nil {
return new(tls.Config), nil
}
var err error
cert, err := tls.X509KeyPair(clientCert, key)
if err != nil {
return nil, err
}
log.Debug().Str("key", string(key))
log.Debug().Str("client.certificate", string(clientCert))
log.Debug().Interface("certificate", cert)
if err != nil {
return nil, err
}
// Get the SystemCertPool, continue with an empty pool on error
rootCAs, err := x509.SystemCertPool()
if rootCAs == nil {
rootCAs = x509.NewCertPool()
log.Warn().Err(err).Msg("Using empty cert-pool")
} else {
log.Info().Msg("Using system cert-pool")
}
for _, cert := range DecodePEM(clientCert).Certificate {
x509Cert, err := x509.ParseCertificate(cert)
if err != nil {
log.Error().Err(err).Msg("issue parsing cert PEM")
}
rootCAs.AddCert(x509Cert)
}
log.Debug().Interface("root.ca", rootCAs)
log.Debug().Interface("certificates", []tls.Certificate{cert})
return &tls.Config{
RootCAs: rootCAs,
MinVersion: tls.VersionTLS10,
MaxVersion: tls.VersionTLS13,
Certificates: []tls.Certificate{cert},
}, nil
}
// DecodePEM builds a PEM certificate object
func DecodePEM(certPEM []byte) tls.Certificate {
var cert tls.Certificate
var certDER *pem.Block
for {
certDER, certPEM = pem.Decode(certPEM)
if certDER == nil {
break
}
if certDER.Type == "CERTIFICATE" {
cert.Certificate = append(cert.Certificate, certDER.Bytes)
}
}
return cert
}
+19
View File
@@ -400,6 +400,25 @@ func validateConfig(cfg *config.Config) (*config.Config, error) {
if len(cfg.LDAP.Listen) == 0 {
return cfg, fmt.Errorf("no LDAP bind address was specified: please disable LDAP or use the 'listen' option")
}
if cfg.LDAP.TLS && cfg.LDAP.TLSCert == "" && cfg.LDAP.TLSCertPath != "" {
byteData, err := os.ReadFile(cfg.LDAP.TLSCertPath)
cfg.LDAP.TLSCert = string(byteData)
if err != nil {
return cfg, fmt.Errorf("unable to read TLS certificate file")
}
}
if cfg.LDAP.TLS && cfg.LDAP.TLSKey == "" && cfg.LDAP.TLSKeyPath != "" {
byteData, err := os.ReadFile(cfg.LDAP.TLSKeyPath)
cfg.LDAP.TLSKey = string(byteData)
if err != nil {
return cfg, fmt.Errorf("unable to read TLS key file")
}
}
}
//spew.Dump(cfg)
+6
View File
@@ -36,6 +36,12 @@ type Frontend struct {
type LDAP struct {
Enabled bool
Listen string
// StartTLS parameters
TLS bool
TLSCert string
TLSKey string
TLSCertPath string
TLSKeyPath string
}
type LDAPS struct {
Enabled bool
+1
View File
@@ -2,6 +2,7 @@ package frontend
import (
"context"
"github.com/rs/zerolog"
"github.com/glauth/glauth/v2/pkg/config"
+2 -1
View File
@@ -5,7 +5,6 @@ package logging
import (
"fmt"
"github.com/rs/zerolog"
"io"
"log"
"log/syslog"
@@ -13,6 +12,8 @@ import (
"regexp"
"strings"
"time"
"github.com/rs/zerolog"
)
// We will use this package to wrap log messages coming from libraries who have no interest
+2 -1
View File
@@ -5,13 +5,14 @@ package logging
import (
"fmt"
"github.com/rs/zerolog"
"io"
"log"
"os"
"regexp"
"strings"
"time"
"github.com/rs/zerolog"
)
// We will use this package to wrap log messages coming from libraries who have no interest
+13 -4
View File
@@ -2,6 +2,7 @@ package server
import (
"context"
"crypto/tls"
"github.com/rs/zerolog"
@@ -14,10 +15,11 @@ type Option func(o *Options)
// Options defines the available options for this package.
type Options struct {
Logger zerolog.Logger
Config *config.Config
Monitor monitoring.MonitorInterface
Context context.Context
Logger zerolog.Logger
Config *config.Config
Monitor monitoring.MonitorInterface
TLSConfig *tls.Config
Context context.Context
}
// newOptions initializes the available default options.
@@ -52,6 +54,13 @@ func Context(val context.Context) Option {
}
}
// TLSConfig provides a function to set the TLS config option.
func TLSConfig(val *tls.Config) Option {
return func(o *Options) {
o.TLSConfig = val
}
}
// Monitor provides a function to set the monitor option.
func Monitor(val monitoring.MonitorInterface) Option {
return func(o *Options) {
+6
View File
@@ -90,6 +90,12 @@ func NewServer(opts ...Option) (*LdapSvc, error) {
// configure the backends
s.l = ldap.NewServer()
s.l.EnforceLDAP = true
if tlsConfig := options.TLSConfig; tlsConfig != nil {
s.l.TLSConfig = tlsConfig
s.log.Info().Interface("tls.certificates", tlsConfig.Certificates).Msg("enabling LDAP over TLS")
}
for i, backend := range s.c.Backends {
var h handler.Handler
switch backend.Datastore {
+3
View File
@@ -21,6 +21,9 @@ debug = true
enabled = true
# run on a non privileged port
listen = "0.0.0.0:3893"
tls = false # enable StartTLS support
tlsCertPath = "glauth.crt"
tlsKeyPath = "glauth.key"
[ldaps]
# to enable ldaps generate a certificate, eg. with: