forked from Deuxfleurs/bottin
Externalize config
This commit is contained in:
parent
611d182907
commit
c1f0247586
3 changed files with 120 additions and 34 deletions
16
README.md
Normal file
16
README.md
Normal file
|
@ -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:*:*"
|
||||||
|
```
|
13
config.json
Normal file
13
config.json
Normal file
|
@ -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:*:*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
125
main.go
125
main.go
|
@ -1,14 +1,15 @@
|
||||||
package main
|
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: Implement missing search filters (in applyFilter)
|
||||||
// @FIXME: Add an initial prefix to the consul key value
|
// @FIXME: Add an initial prefix to the consul key value
|
||||||
// @FIXME: Add TLS connections
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
|
@ -21,9 +22,24 @@ import (
|
||||||
message "github.com/vjeantet/goldap/message"
|
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 {
|
type Config struct {
|
||||||
Suffix string
|
Suffix string
|
||||||
Acl ACL
|
BindAddress string
|
||||||
|
ConsulHost string
|
||||||
|
|
||||||
|
Acl ACL
|
||||||
|
|
||||||
|
TlsConfig *tls.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
|
@ -37,42 +53,77 @@ type State struct {
|
||||||
|
|
||||||
type Entry map[string][]string
|
type Entry map[string][]string
|
||||||
|
|
||||||
|
var configFlag = flag.String("config", "./config.json", "Configuration file path")
|
||||||
|
|
||||||
|
func readConfig() Config {
|
||||||
|
config_file := ConfigFile{
|
||||||
|
BindAddress: "0.0.0.0:389",
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := ioutil.ReadFile(*configFlag)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(bytes, &config_file)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
acl, err := ParseACL(config_file.Acl)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
func main() {
|
||||||
//ldap logger
|
|
||||||
ldap.Logger = log.New(os.Stdout, "[server] ", log.LstdFlags)
|
ldap.Logger = log.New(os.Stdout, "[server] ", log.LstdFlags)
|
||||||
|
|
||||||
|
config := readConfig()
|
||||||
|
|
||||||
// Connect to Consul
|
// Connect to Consul
|
||||||
client, err := consul.NewClient(consul.DefaultConfig())
|
consul_config := consul.DefaultConfig()
|
||||||
|
if config.ConsulHost != "" {
|
||||||
|
consul_config.Address = config.ConsulHost
|
||||||
|
}
|
||||||
|
consul_client, err := consul.NewClient(consul_config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
kv := client.KV()
|
kv := consul_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:*:*",
|
|
||||||
}
|
|
||||||
acl, err := ParseACL(aclStr)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO read config from somewhere
|
|
||||||
config := Config{
|
|
||||||
Suffix: "dc=gobottin,dc=eu",
|
|
||||||
Acl: acl,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Create gobottin server
|
||||||
gobottin := Server{config: config, kv: kv}
|
gobottin := Server{config: config, kv: kv}
|
||||||
err = gobottin.init()
|
err = gobottin.init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -99,8 +150,14 @@ func main() {
|
||||||
routes.Modify(gobottin.handleModify)
|
routes.Modify(gobottin.handleModify)
|
||||||
ldapserver.Handle(routes)
|
ldapserver.Handle(routes)
|
||||||
|
|
||||||
// listen on 10389
|
if config.TlsConfig != nil {
|
||||||
go ldapserver.ListenAndServe("127.0.0.1:10389")
|
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
|
// When CTRL+C, SIGINT and SIGTERM signal occurs
|
||||||
// Then stop server gracefully
|
// Then stop server gracefully
|
||||||
|
|
Loading…
Reference in a new issue