Server socket
* The UNIX socket will be listened on for control messages * A message containing a single 'Q' will cause it to exit * A message containing a single 'R' will cause a configuration reload. If the new configuration is incorrect, the old configuration will be kept. A new socket will be opened if the path has changed (failure when doing so will restore the previous configuration as well). * A message starting with 'U' requests an update. The next character may be '!' to force updates or anything else to update only as needed. The rest of the string is the selector: either a DN or '*'. * The selector is ignored in this commit; all certificates are re-examined.
This commit is contained in:
parent
50bff6a5cb
commit
658ee30bc6
3 changed files with 161 additions and 26 deletions
|
@ -284,7 +284,7 @@ func defaultConfiguration() tConfiguration {
|
|||
}
|
||||
|
||||
// Load and check the configuration file
|
||||
func loadConfiguration(file string) (tConfiguration, error) {
|
||||
func LoadConfiguration(file string) (tConfiguration, error) {
|
||||
cfg := defaultConfiguration()
|
||||
cfgData, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
|
|
50
main.go
50
main.go
|
@ -60,7 +60,7 @@ func main() {
|
|||
log.WithField("error", err).Fatal("Failed to configure logging.")
|
||||
}
|
||||
|
||||
cfg, err := loadConfiguration(flags.cfgFile)
|
||||
cfg, err := LoadConfiguration(flags.cfgFile)
|
||||
if err != nil {
|
||||
log.WithField("error", err).Fatal("Failed to load initial configuration.")
|
||||
}
|
||||
|
@ -69,32 +69,32 @@ func main() {
|
|||
if err != nil {
|
||||
log.WithField("error", err).Fatal("Failed to initialize socket.")
|
||||
}
|
||||
listener.Close()
|
||||
|
||||
conn := NewLdapConnection(cfg.LdapConfig)
|
||||
if conn == nil {
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
for i := range cfg.Certificates {
|
||||
builder := NewCertificateBuilder(conn, &cfg.Certificates[i])
|
||||
err := builder.Build()
|
||||
if err != nil {
|
||||
log.WithField("error", err).Error("Failed to build data for certificate '", cfg.Certificates[i].Path, "'")
|
||||
continue
|
||||
}
|
||||
if builder.MustWrite() {
|
||||
err := builder.WriteFile()
|
||||
defer listener.Close()
|
||||
for {
|
||||
cmd := socketServer(&cfg, listener)
|
||||
if cmd == CMD_QUIT {
|
||||
break
|
||||
} else if cmd == CMD_RELOAD {
|
||||
new_cfg, err := LoadConfiguration(flags.cfgFile)
|
||||
if err != nil {
|
||||
log.WithField("error", err).Error("Failed to write '", cfg.Certificates[i].Path, "'")
|
||||
continue
|
||||
log.WithField("error", err).Error("Failed to load updated configuration.")
|
||||
} else {
|
||||
replace_ok := true
|
||||
if new_cfg.Socket.Path != cfg.Socket.Path {
|
||||
new_listener, err := initSocket(new_cfg.Socket)
|
||||
if err != nil {
|
||||
log.WithField("error", err).Error("Failed to initialize new server socket.")
|
||||
replace_ok = false
|
||||
} else {
|
||||
listener.Close()
|
||||
listener = new_listener
|
||||
}
|
||||
}
|
||||
if replace_ok {
|
||||
cfg = new_cfg
|
||||
log.Info("Configuration reloaded")
|
||||
}
|
||||
}
|
||||
}
|
||||
err = builder.UpdatePrivileges()
|
||||
if err != nil {
|
||||
log.WithField("error", err).Error("Failed to update privileges on '", cfg.Certificates[i].Path, "'")
|
||||
continue
|
||||
}
|
||||
builder.RunCommandsIfChanged()
|
||||
}
|
||||
}
|
||||
|
|
135
socket.go
135
socket.go
|
@ -6,8 +6,27 @@ import (
|
|||
"os"
|
||||
"os/user"
|
||||
"strconv"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type TCommandType int
|
||||
|
||||
const (
|
||||
CMD_IGNORE TCommandType = iota
|
||||
CMD_QUIT
|
||||
CMD_RELOAD
|
||||
CMD_UPDATE
|
||||
)
|
||||
|
||||
type TCommand struct {
|
||||
CommandType TCommandType
|
||||
Force bool
|
||||
Selector string
|
||||
}
|
||||
|
||||
func configureSocket(cfg tSocketConfig) error {
|
||||
if cfg.Group != "" {
|
||||
group, err := user.LookupGroup(cfg.Group)
|
||||
|
@ -47,3 +66,119 @@ func initSocket(cfg tSocketConfig) (net.Listener, error) {
|
|||
log.WithField("path", cfg.Path).Info("UNIX socket created")
|
||||
return listener, nil
|
||||
}
|
||||
|
||||
func socketServer(cfg *tConfiguration, listener net.Listener) TCommandType {
|
||||
for {
|
||||
fd, err := listener.Accept()
|
||||
if err != nil {
|
||||
log.WithField("error", err).Fatal("Error while waiting for connections.")
|
||||
}
|
||||
cmd := executeFromSocket(cfg, fd)
|
||||
if cmd != CMD_IGNORE {
|
||||
return cmd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func executeFromSocket(cfg *tConfiguration, conn net.Conn) TCommandType {
|
||||
defer conn.Close()
|
||||
log.Debug("Received connection")
|
||||
|
||||
buf := make([]byte, 512)
|
||||
conn.SetReadDeadline(time.Now().Add(1 * time.Second))
|
||||
n, err := conn.Read(buf)
|
||||
if err != nil {
|
||||
log.WithField("error", err).Error("Could not read from socket")
|
||||
return CMD_IGNORE
|
||||
}
|
||||
command := parseCommand(n, buf)
|
||||
if command == nil {
|
||||
return CMD_IGNORE
|
||||
}
|
||||
if command.CommandType == CMD_UPDATE {
|
||||
log.WithFields(logrus.Fields{
|
||||
"force": command.Force,
|
||||
"selector": command.Selector,
|
||||
}).Info("Update request received")
|
||||
success := executeUpdate(cfg, command)
|
||||
conn.SetWriteDeadline(time.Now().Add(1 * time.Second))
|
||||
var bval byte
|
||||
if success {
|
||||
bval = '1'
|
||||
} else {
|
||||
bval = '0'
|
||||
}
|
||||
conn.Write([]byte{bval})
|
||||
return CMD_IGNORE
|
||||
}
|
||||
return command.CommandType
|
||||
}
|
||||
|
||||
func parseCommand(n int, buf []byte) *TCommand {
|
||||
if n == 512 {
|
||||
log.Warn("Too much data received")
|
||||
return nil
|
||||
}
|
||||
if n == 0 {
|
||||
log.Warn("Not enough data received")
|
||||
return nil
|
||||
}
|
||||
if n == 1 {
|
||||
if buf[0] == 'Q' {
|
||||
return &TCommand{CommandType: CMD_QUIT}
|
||||
} else if buf[0] == 'R' {
|
||||
return &TCommand{CommandType: CMD_RELOAD}
|
||||
}
|
||||
} else if n > 2 && buf[0] == 'U' {
|
||||
res := &TCommand{CommandType: CMD_UPDATE}
|
||||
if buf[1] == '!' {
|
||||
res.Force = true
|
||||
}
|
||||
if utf8.Valid(buf[2:]) {
|
||||
res.Selector = string(buf[2:n])
|
||||
return res
|
||||
}
|
||||
}
|
||||
log.Warn("Invalid command received")
|
||||
return nil
|
||||
}
|
||||
|
||||
func executeUpdate(cfg *tConfiguration, cmd *TCommand) bool {
|
||||
conn := NewLdapConnection(cfg.LdapConfig)
|
||||
if conn == nil {
|
||||
return false
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
had_errors := false
|
||||
for i := range cfg.Certificates {
|
||||
// TODO apply selector
|
||||
builder := NewCertificateBuilder(conn, &cfg.Certificates[i])
|
||||
err := builder.Build()
|
||||
if err != nil {
|
||||
log.WithField("error", err).Error("Failed to build data for certificate '", cfg.Certificates[i].Path, "'")
|
||||
had_errors = true
|
||||
continue
|
||||
}
|
||||
if builder.MustWrite() || cmd.Force {
|
||||
err := builder.WriteFile()
|
||||
if err != nil {
|
||||
log.WithField("error", err).Error("Failed to write '", cfg.Certificates[i].Path, "'")
|
||||
had_errors = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
err = builder.UpdatePrivileges()
|
||||
if err != nil {
|
||||
log.WithField("error", err).Error("Failed to update privileges on '", cfg.Certificates[i].Path, "'")
|
||||
had_errors = true
|
||||
continue
|
||||
}
|
||||
err = builder.RunCommandsIfChanged()
|
||||
if err != nil {
|
||||
log.WithField("error", err).Error("Failed to run commands after update of '", cfg.Certificates[i].Path, "'")
|
||||
had_errors = true
|
||||
}
|
||||
}
|
||||
return !had_errors
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue