aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md16
-rw-r--r--config.json13
-rw-r--r--main.go117
3 files changed, 116 insertions, 30 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b4a95bf
--- /dev/null
+++ b/README.md
@@ -0,0 +1,16 @@
+## ACL examples
+
+```
+// Anybody (before binding) can bind to an entity under ou=users,dc=gobottin,dc=eu
+"ANONYMOUS::bind:*,ou=users,dc=gobottin,dc=eu:",
+// Anybody (before binding) can bind to the specific admin entity
+"ANONYMOUS::bind:cn=admin,dc=gobottin,dc=eu:",
+// Anybody who is logged in can read anything that is not a userpassword attribute
+"*,dc=gobottin,dc=eu::read:*:* !userpassword",
+// Anybody can read and modify anything from their own entry
+"*::read modify:SELF:*",
+// The admin can read, add, modify, delete anything
+"cn=admin,dc=gobottin,dc=eu::read add modify delete:*:*",
+// Members of the admin group can read, add, modify, delete anything
+"*:cn=admin,ou=groups,dc=gobottin,dc=eu:read add modify delete:*:*"
+```
diff --git a/config.json b/config.json
new file mode 100644
index 0000000..f4c2781
--- /dev/null
+++ b/config.json
@@ -0,0 +1,13 @@
+{
+ "suffix": "dc=gobottin,dc=eu",
+ "bind_address": "127.0.0.1:10389",
+ "acl": [
+ "ANONYMOUS::bind:*,ou=users,dc=gobottin,dc=eu:",
+ "ANONYMOUS::bind:cn=admin,dc=gobottin,dc=eu:",
+ "*,dc=gobottin,dc=eu::read:*:* !userpassword",
+ "*::read modify:SELF:*",
+ "cn=admin,dc=gobottin,dc=eu::read add modify delete:*:*",
+ "*:cn=admin,ou=groups,dc=gobottin,dc=eu:read add modify delete:*:*"
+ ]
+}
+
diff --git a/main.go b/main.go
index 6516724..8894f26 100644
--- a/main.go
+++ b/main.go
@@ -1,14 +1,15 @@
package main
-// @FIXME: Implement a real permission system: limit read/write scope/attributes, possibly based on group membership
// @FIXME: Implement missing search filters (in applyFilter)
// @FIXME: Add an initial prefix to the consul key value
-// @FIXME: Add TLS connections
import (
+ "crypto/tls"
"encoding/base64"
"encoding/json"
+ "flag"
"fmt"
+ "io/ioutil"
"log"
"math/rand"
"os"
@@ -21,9 +22,24 @@ import (
message "github.com/vjeantet/goldap/message"
)
+type ConfigFile struct {
+ Suffix string `json:"suffix"`
+ BindAddress string `json:"bind_address"`
+ ConsulHost string `json:"consul_host"`
+ Acl []string `json:"acl"`
+ SSLCertFile string `json:"ssl_cert_file"`
+ SSLKeyFile string `json:"ssl_key_file"`
+ SSLServerName string `json:"ssl_server_name"`
+}
+
type Config struct {
- Suffix string
- Acl ACL
+ Suffix string
+ BindAddress string
+ ConsulHost string
+
+ Acl ACL
+
+ TlsConfig *tls.Config
}
type Server struct {
@@ -37,42 +53,77 @@ type State struct {
type Entry map[string][]string
-func main() {
- //ldap logger
- ldap.Logger = log.New(os.Stdout, "[server] ", log.LstdFlags)
+var configFlag = flag.String("config", "./config.json", "Configuration file path")
- // Connect to Consul
- client, err := consul.NewClient(consul.DefaultConfig())
+func readConfig() Config {
+ config_file := ConfigFile{
+ BindAddress: "0.0.0.0:389",
+ }
+
+ bytes, err := ioutil.ReadFile(*configFlag)
if err != nil {
panic(err)
}
- kv := client.KV()
- aclStr := []string{
- // Anybody (before binding) can bind to an entity under ou=users,dc=gobottin,dc=eu
- "ANONYMOUS::bind:*,ou=users,dc=gobottin,dc=eu:",
- // Anybody (before binding) can bind to the specific admin entity
- "ANONYMOUS::bind:cn=admin,dc=gobottin,dc=eu:",
- // Anybody who is logged in can read anything that is not a userpassword attribute
- "*,dc=gobottin,dc=eu::read:*:* !userpassword",
- // Anybody can read and modify anything from their own entry
- "*::read modify:SELF:*",
- // The admin can read, add, modify, delete anything
- "cn=admin,dc=gobottin,dc=eu::read add modify delete:*:*",
- // Members of the admin group can read, add, modify, delete anything
- "*:cn=admin,ou=groups,dc=gobottin,dc=eu:read add modify delete:*:*",
+ err = json.Unmarshal(bytes, &config_file)
+ if err != nil {
+ panic(err)
}
- acl, err := ParseACL(aclStr)
+
+ acl, err := ParseACL(config_file.Acl)
if err != nil {
panic(err)
}
- // TODO read config from somewhere
- config := Config{
- Suffix: "dc=gobottin,dc=eu",
- Acl: acl,
+ ret := Config{
+ Suffix: config_file.Suffix,
+ BindAddress: config_file.BindAddress,
+ ConsulHost: config_file.ConsulHost,
+ Acl: acl,
}
+ if config_file.SSLCertFile != "" && config_file.SSLKeyFile != "" && config_file.SSLServerName != "" {
+ cert_txt, err := ioutil.ReadFile(config_file.SSLCertFile)
+ if err != nil {
+ panic(err)
+ }
+ key_txt, err := ioutil.ReadFile(config_file.SSLKeyFile)
+ if err != nil {
+ panic(err)
+ }
+ cert, err := tls.X509KeyPair(cert_txt, key_txt)
+ if err != nil {
+ panic(err)
+ }
+ ret.TlsConfig = &tls.Config{
+ MinVersion: tls.VersionSSL30,
+ MaxVersion: tls.VersionTLS12,
+ Certificates: []tls.Certificate{cert},
+ ServerName: config_file.SSLServerName,
+ }
+
+ }
+
+ return ret
+}
+
+func main() {
+ ldap.Logger = log.New(os.Stdout, "[server] ", log.LstdFlags)
+
+ config := readConfig()
+
+ // Connect to Consul
+ consul_config := consul.DefaultConfig()
+ if config.ConsulHost != "" {
+ consul_config.Address = config.ConsulHost
+ }
+ consul_client, err := consul.NewClient(consul_config)
+ if err != nil {
+ panic(err)
+ }
+ kv := consul_client.KV()
+
+ // Create gobottin server
gobottin := Server{config: config, kv: kv}
err = gobottin.init()
if err != nil {
@@ -99,8 +150,14 @@ func main() {
routes.Modify(gobottin.handleModify)
ldapserver.Handle(routes)
- // listen on 10389
- go ldapserver.ListenAndServe("127.0.0.1:10389")
+ if config.TlsConfig != nil {
+ secureConn := func(s *ldap.Server) {
+ s.Listener = tls.NewListener(s.Listener, config.TlsConfig)
+ }
+ go ldapserver.ListenAndServe(config.BindAddress, secureConn)
+ } else {
+ go ldapserver.ListenAndServe(config.BindAddress)
+ }
// When CTRL+C, SIGINT and SIGTERM signal occurs
// Then stop server gracefully