diff options
-rw-r--r-- | README.md | 16 | ||||
-rw-r--r-- | config.json | 13 | ||||
-rw-r--r-- | main.go | 117 |
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:*:*" + ] +} + @@ -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 |