Selectors implemented

* The DN that is passed along with the update command is checked
    against the list of DNs a certificate file uses. If it is not in the
    list, then the file is skipped.
This commit is contained in:
Emmanuel BENOîT 2021-11-06 17:46:38 +01:00
parent 658ee30bc6
commit 7eb865e306
2 changed files with 59 additions and 19 deletions

View file

@ -9,6 +9,7 @@ import (
"os/exec" "os/exec"
"os/user" "os/user"
"strconv" "strconv"
"strings"
"syscall" "syscall"
"time" "time"
"unicode/utf8" "unicode/utf8"
@ -30,24 +31,39 @@ type (
// Certificate building, including the configuration, LDAP connection, // Certificate building, including the configuration, LDAP connection,
// and the array of chunks that's being built. // and the array of chunks that's being built.
tCertificateBuilder struct { tCertificateBuilder struct {
config *tCertificateFileConfig // The certificate file's current configuration
conn *tLdapConn config *tCertificateFileConfig
logger *logrus.Entry // The LDAP connection to read data from
data [][]byte conn *tLdapConn
text []byte // The command that caused the update
command TCommand
// The logger to use
logger *logrus.Entry
// The list of DNs that are involved in generating this certificate. If the
// command has a non-'*' selector, the list will be checked for a value
// matching the selector befor anything else is done.
dnList []string
// The various chunks of data that will be written to the resulting PEM file.
// Each chunk corresponds to a PEM block.
data [][]byte
// The output text
text []byte
// Information about the current file, if it exists.
existing *tExistingFileInfo existing *tExistingFileInfo
changed bool // Was the certificate file replaced?
changed bool
} }
) )
// Initialize a certificate file building using a LDAP connection and // Initialize a certificate file building using a LDAP connection and
// certificate file configuration. // certificate file configuration.
func NewCertificateBuilder(conn *tLdapConn, config *tCertificateFileConfig) tCertificateBuilder { func NewCertificateBuilder(conn *tLdapConn, config *tCertificateFileConfig, cmd *TCommand) tCertificateBuilder {
return tCertificateBuilder{ return tCertificateBuilder{
config: config, config: config,
conn: conn, conn: conn,
logger: log.WithField("file", config.Path), command: *cmd,
data: make([][]byte, 0), logger: log.WithField("file", config.Path),
data: make([][]byte, 0),
} }
} }
@ -78,7 +94,25 @@ func (b *tCertificateBuilder) Build() error {
return nil return nil
} }
// Check whether the data should be written to disk. // Check whether the command's selector matches one of the current certificate
// file's DNs.
func (b *tCertificateBuilder) SelectorMatches() bool {
if b.command.Selector == "*" {
return true
}
sel := strings.ToLower(b.command.Selector)
for _, v := range b.dnList {
if strings.ToLower(v) == sel {
return true
}
}
b.logger.WithField("selector", b.command.Selector).Debug("Selector does not match.")
return false
}
// Check whether the data should be written to disk. This also caches the
// file's owner, group and mode. If the update is being forced it will return
// `true` even if nothing changed.
func (b *tCertificateBuilder) MustWrite() bool { func (b *tCertificateBuilder) MustWrite() bool {
info, err := os.Lstat(b.config.Path) info, err := os.Lstat(b.config.Path)
if err != nil { if err != nil {
@ -92,7 +126,7 @@ func (b *tCertificateBuilder) MustWrite() bool {
eif.group = sys_stat.Gid eif.group = sys_stat.Gid
b.existing = eif b.existing = eif
if sys_stat.Size != int64(len(b.text)) { if b.command.Force || sys_stat.Size != int64(len(b.text)) {
return true return true
} }
existing, err := ioutil.ReadFile(b.config.Path) existing, err := ioutil.ReadFile(b.config.Path)
@ -254,6 +288,7 @@ func (b *tCertificateBuilder) appendCertificate() error {
dn = "," + dn dn = "," + dn
} }
dn = b.config.Certificate + dn dn = b.config.Certificate + dn
b.dnList = append(b.dnList, strings.ToLower(dn))
b.logger.WithField("dn", dn).Debug("Adding EE certificate from LDAP") b.logger.WithField("dn", dn).Debug("Adding EE certificate from LDAP")
data, err := b.conn.GetEndEntityCertificate(dn) data, err := b.conn.GetEndEntityCertificate(dn)
if err != nil { if err != nil {
@ -283,13 +318,15 @@ func (b *tCertificateBuilder) appendListedCaCerts() error {
bdn = "," + bdn bdn = "," + bdn
} }
for _, dn := range b.config.CACertificates { for _, dn := range b.config.CACertificates {
b.logger.WithField("dn", dn+bdn).Debug("Adding CA certificate from LDAP") full_dn := dn + bdn
data, _, err := b.conn.GetCaCertificate(dn + bdn) b.dnList = append(b.dnList, strings.ToLower(full_dn))
b.logger.WithField("dn", full_dn).Debug("Adding CA certificate from LDAP")
data, _, err := b.conn.GetCaCertificate(full_dn)
if err != nil { if err != nil {
return err return err
} }
if data == nil { if data == nil {
return fmt.Errorf("No CA certificate at DN '%s'", dn) return fmt.Errorf("No CA certificate at DN '%s'", full_dn)
} }
b.data = append(b.data, data) b.data = append(b.data, data)
} }
@ -304,6 +341,7 @@ func (b *tCertificateBuilder) appendChainedCaCerts() error {
dn = dn + "," + b.conn.BaseDN() dn = dn + "," + b.conn.BaseDN()
} }
for { for {
b.dnList = append(b.dnList, strings.ToLower(dn))
data, nextDn, err := b.conn.GetCaCertificate(dn) data, nextDn, err := b.conn.GetCaCertificate(dn)
if err != nil { if err != nil {
return err return err

View file

@ -152,15 +152,17 @@ func executeUpdate(cfg *tConfiguration, cmd *TCommand) bool {
had_errors := false had_errors := false
for i := range cfg.Certificates { for i := range cfg.Certificates {
// TODO apply selector builder := NewCertificateBuilder(conn, &cfg.Certificates[i], cmd)
builder := NewCertificateBuilder(conn, &cfg.Certificates[i])
err := builder.Build() err := builder.Build()
if err != nil { if err != nil {
log.WithField("error", err).Error("Failed to build data for certificate '", cfg.Certificates[i].Path, "'") log.WithField("error", err).Error("Failed to build data for certificate '", cfg.Certificates[i].Path, "'")
had_errors = true had_errors = true
continue continue
} }
if builder.MustWrite() || cmd.Force { if !builder.SelectorMatches() {
continue
}
if builder.MustWrite() {
err := builder.WriteFile() err := builder.WriteFile()
if err != nil { if err != nil {
log.WithField("error", err).Error("Failed to write '", cfg.Certificates[i].Path, "'") log.WithField("error", err).Error("Failed to write '", cfg.Certificates[i].Path, "'")