Allow for both TLS and non-TLS connections

This commit is contained in:
Alex 2020-01-27 16:32:39 +01:00
parent 66c6479770
commit dce432426e
3 changed files with 100 additions and 58 deletions

View file

@ -44,25 +44,39 @@ suffix in the `suffix` key of the json config file.
By default, `gobottin` connects to the Consul server on localhost. By default, `gobottin` connects to the Consul server on localhost.
Change this by specifying the `consul_host` key in the json config file. Change this by specifying the `consul_host` key in the json config file.
## Bind address ## Bind addresses
By default, `gobottin` listens on all interfaces on port 389. ### Insecure port
Change this by setting the `bind_address` key in the json config file.
By default, `gobottin` listens on all interfaces on port 389 for standard
non-TLS connections. Change the value of the `bind` key in the json config
file to change this behaviour (default value: `0.0.0.0:389`). An empty string
will disable this port and `gobottin` will not listen for non-TLS connections.
### Secure port
If a TLS configuration is provided (see next section), `gobottin` also listens
on all interfaces on port 636 for TLS connections. Change the value of the
`bind_secure` key in the json config file to change this behaviour (default
value: `0.0.0.0:636`). An empty string will disable this port and `gobottin`
will not listen for TLS connections.
## TLS ## TLS
`gobottin` supports TLS connections either as a mandatory default for all `gobottin` supports TLS connections using either fully secure connections or
connections or using the STARTLS functionnality of the LDAP protocol. To use using the STARTLS functionnality of the LDAP protocol to upgrade from an
it, specify the following three keys in the json config file: insecure connection. To use it, specify the following three keys in the json
config file:
- `tls_server_name`: the host name that clients will use to reach your LDAP server - `tls_server_name`: the host name that clients will use to reach your LDAP server
- `tls_cert_file`: path to your TLS certificate (a `.pem` file) - `tls_cert_file`: path to your TLS certificate (a `.pem` file)
- `tls_key_file`: path to your TLS key (a `.pem` file) - `tls_key_file`: path to your TLS key (a `.pem` file)
Specify `"use_starttls": true` to allow connections to start as insecure If a TLS configuration is provided, the `STARTTLS` mechanism may be used on the
connections and allow them to use the STARTTLS mechanism to upgrade to a secure insecure port, independently of whether the secure port is enabled or not.
connection. If `use_starttls` is not specified or set to `false`, TLS is made
mandatory for all clients. The secure port is disabled and a warning is shown if the `bind_secure` value
is set (non-empty) and no valid TLS configuration is provided.
## Access control list ## Access control list

View file

@ -1,6 +1,6 @@
{ {
"suffix": "dc=gobottin,dc=eu", "suffix": "dc=gobottin,dc=eu",
"bind_address": "127.0.0.1:10389", "bind": "127.0.0.1:1389",
"acl": [ "acl": [
"ANONYMOUS::bind:*,ou=users,dc=gobottin,dc=eu:", "ANONYMOUS::bind:*,ou=users,dc=gobottin,dc=eu:",
"ANONYMOUS::bind:cn=admin,dc=gobottin,dc=eu:", "ANONYMOUS::bind:cn=admin,dc=gobottin,dc=eu:",

122
main.go
View file

@ -35,24 +35,24 @@ const ATTR_MODIFYTIMESTAMP = "modifytimestamp"
type ConfigFile struct { type ConfigFile struct {
Suffix string `json:"suffix"` Suffix string `json:"suffix"`
BindAddress string `json:"bind_address"` Bind string `json:"bind"`
BindSecure string `json:"bind_secure"`
ConsulHost string `json:"consul_host"` ConsulHost string `json:"consul_host"`
Acl []string `json:"acl"` Acl []string `json:"acl"`
TLSCertFile string `json:"tls_cert_file"` TLSCertFile string `json:"tls_cert_file"`
TLSKeyFile string `json:"tls_key_file"` TLSKeyFile string `json:"tls_key_file"`
TLSServerName string `json:"tls_server_name"` TLSServerName string `json:"tls_server_name"`
UseStartTLS bool `json:"use_starttls"`
} }
type Config struct { type Config struct {
Suffix string Suffix string
BindAddress string Bind string
ConsulHost string BindSecure string
ConsulHost string
Acl ACL Acl ACL
TLSConfig *tls.Config TLSConfig *tls.Config
UseStartTLS bool
} }
type Server struct { type Server struct {
@ -71,7 +71,8 @@ var configFlag = flag.String("config", "./config.json", "Configuration file path
func readConfig() Config { func readConfig() Config {
config_file := ConfigFile{ config_file := ConfigFile{
BindAddress: "0.0.0.0:389", Bind: "0.0.0.0:389",
BindSecure: "0.0.0.0:636",
} }
bytes, err := ioutil.ReadFile(*configFlag) bytes, err := ioutil.ReadFile(*configFlag)
@ -90,11 +91,11 @@ func readConfig() Config {
} }
ret := Config{ ret := Config{
Suffix: config_file.Suffix, Suffix: config_file.Suffix,
BindAddress: config_file.BindAddress, Bind: config_file.Bind,
ConsulHost: config_file.ConsulHost, BindSecure: config_file.BindSecure,
Acl: acl, ConsulHost: config_file.ConsulHost,
UseStartTLS: config_file.UseStartTLS, Acl: acl,
} }
if config_file.TLSCertFile != "" && config_file.TLSKeyFile != "" && config_file.TLSServerName != "" { if config_file.TLSCertFile != "" && config_file.TLSKeyFile != "" && config_file.TLSServerName != "" {
@ -116,8 +117,6 @@ func readConfig() Config {
Certificates: []tls.Certificate{cert}, Certificates: []tls.Certificate{cert},
ServerName: config_file.TLSServerName, ServerName: config_file.TLSServerName,
} }
} else {
log.Printf("Warning: no TLS configuration provided, running an insecure server.")
} }
return ret return ret
@ -152,17 +151,7 @@ func main() {
panic(err) panic(err)
} }
//Create a new LDAP Server // Create routes
ldapserver := ldap.NewServer()
ldapserver.NewUserState = func() ldap.UserState {
return &State{
login: Login{
user: "ANONYMOUS",
groups: []string{},
},
}
}
routes := ldap.NewRouteMux() routes := ldap.NewRouteMux()
routes.Bind(gobottin.handleBind) routes.Bind(gobottin.handleBind)
@ -172,33 +161,72 @@ func main() {
routes.Delete(gobottin.handleDelete) routes.Delete(gobottin.handleDelete)
routes.Modify(gobottin.handleModify) routes.Modify(gobottin.handleModify)
if config.TLSConfig != nil && config.UseStartTLS { if config.TLSConfig != nil {
routes.Extended(gobottin.handleStartTLS). routes.Extended(gobottin.handleStartTLS).
RequestName(ldap.NoticeOfStartTLS).Label("StartTLS") RequestName(ldap.NoticeOfStartTLS).Label("StartTLS")
} }
ldapserver.Handle(routes) // Create LDAP servers
var ldapServer, ldapServerSecure *ldap.Server = nil, nil
go func() { // Bind on standard LDAP port without TLS
// When CTRL+C, SIGINT and SIGTERM signal occurs if config.Bind != "" {
// Then stop server gracefully ldapServer = ldap.NewServer()
ch := make(chan os.Signal) ldapServer.Handle(routes)
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) ldapServer.NewUserState = gobottin.newUserState
<-ch go func() {
close(ch) err := ldapServer.ListenAndServe(config.Bind)
ldapserver.Stop() if err != nil {
}() panic(err)
}
if config.TLSConfig != nil && !config.UseStartTLS { }()
secureConn := func(s *ldap.Server) {
s.Listener = tls.NewListener(s.Listener, config.TLSConfig)
}
err = ldapserver.ListenAndServe(config.BindAddress, secureConn)
} else {
err = ldapserver.ListenAndServe(config.BindAddress)
} }
if err != nil {
panic(err) // Bind on LDAP secure port with TLS
if config.BindSecure != "" {
if config.TLSConfig != nil {
ldapServerSecure := ldap.NewServer()
ldapServerSecure.Handle(routes)
ldapServerSecure.NewUserState = gobottin.newUserState
secureConn := func(s *ldap.Server) {
s.Listener = tls.NewListener(s.Listener, config.TLSConfig)
}
go func() {
err := ldapServerSecure.ListenAndServe(config.BindSecure, secureConn)
if err != nil {
panic(err)
}
}()
} else {
fmt.Printf("Warning: no valid TLS configuration was provided, not binding on %s", config.BindSecure)
}
}
if ldapServer == nil && ldapServerSecure == nil {
panic("Not doing anything.")
}
// When CTRL+C, SIGINT and SIGTERM signal occurs
// Then stop server gracefully
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
<-ch
close(ch)
if ldapServer != nil {
ldapServer.Stop()
}
if ldapServerSecure != nil {
ldapServerSecure.Stop()
}
}
func (server *Server) newUserState() ldap.UserState {
return &State{
login: Login{
user: "ANONYMOUS",
groups: []string{},
},
} }
} }