mirror of
https://github.com/glauth/glauth.git
synced 2026-10-24 13:13:02 +00:00
Merge branch 'master' into chore/rewire-ldap-packages
This commit is contained in:
@@ -7,7 +7,7 @@ Go-lang LDAP Authentication (GLAuth) is a secure, easy-to-use, LDAP server w/ co
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
* Centrally manage accounts across your infrastructure
|
||||
|
||||
@@ -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
@@ -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" {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2,6 +2,7 @@ package frontend
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"github.com/glauth/glauth/v2/pkg/config"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Submodule
+1
Submodule v2/pkg/plugins/glauth-mysql added at 8379828f13
Submodule
+1
Submodule v2/pkg/plugins/glauth-pam added at 0e6e4240ef
Submodule
+1
Submodule v2/pkg/plugins/glauth-postgres added at 61fa8ee872
Submodule
+1
Submodule v2/pkg/plugins/glauth-sqlite added at e8b3b6a8b0
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user