Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for includedir and include #515

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 51 additions & 5 deletions config/krb5conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"net"
"os"
"os/user"
"path/filepath"
"regexp"
"strconv"
"strings"
Expand Down Expand Up @@ -515,15 +516,60 @@ func (c *Config) ResolveRealm(domainName string) string {
return c.LibDefaults.DefaultRealm
}

// Load the KRB5 configuration from the specified file path.
func Load(cfgPath string) (*Config, error) {
// Recursively load the contents of the KRB5 config files using include and includedir
func recurLoadFile(cfgPath string) ([]string, error) {
fh, err := os.Open(cfgPath)
if err != nil {
return nil, errors.New("configuration file could not be opened: " + cfgPath + " " + err.Error())
}
defer fh.Close()
var lines []string
var recurlines []string
scanner := bufio.NewScanner(fh)
return NewConfigFromScanner(scanner)
reinclude := regexp.MustCompile(`^include\s*(\S*)$`)
reincludedir := regexp.MustCompile(`^includedir\s*(\S*)$`)
for scanner.Scan() {
result := reincludedir.FindStringSubmatch(scanner.Text())
if len(result) > 1 {
cfgDir := result[1]
files, err := os.ReadDir(cfgDir)
if err != nil {
return nil, errors.New("configuration directory could not be opened: " + cfgDir + " " + err.Error())
}
for _, file := range files {
if matched, _ := regexp.MatchString(`^[\w-\.]+$`, file.Name()); matched {
filePath := filepath.Join(cfgDir, file.Name())
recurlines, err = recurLoadFile(filePath)
if err != nil {
return nil, errors.New("subconfiguration file could not be opened: " + filePath + " " + err.Error())
}
lines = append(lines, recurlines...)
}
}
continue
}
result = reinclude.FindStringSubmatch(scanner.Text())
if len(result) > 1 {
subCfgPath := result[1]
recurlines, err = recurLoadFile(subCfgPath)
if err != nil {
return nil, errors.New("subconfiguration file could not be opened: " + subCfgPath + " " + err.Error())
}
lines = append(lines, recurlines...)
continue
}
lines = append(lines, scanner.Text())
}
return lines, nil
}

// Load the KRB5 configuration from the specified file path.
func Load(cfgPath string) (*Config, error) {
lines, err := recurLoadFile(cfgPath)
if err != nil {
return nil, errors.New("configuration file could not be opened: " + cfgPath + " " + err.Error())
}
return NewConfigFromString(strings.Join(lines, "\n"))
}

// NewConfigFromString creates a new Config struct from a string.
Expand Down Expand Up @@ -596,12 +642,12 @@ func NewConfigFromScanner(scanner *bufio.Scanner) (*Config, error) {
}
e = err
}
c.Realms = realms
c.Realms = append(c.Realms, realms...)
case "domain_realm":
err := c.DomainRealm.parseLines(lines[start:end])
if err != nil {
if _, ok := err.(UnsupportedDirective); !ok {
return nil, fmt.Errorf("error processing domaain_realm section: %v", err)
return nil, fmt.Errorf("error processing domain_realm section: %v", err)
}
e = err
}
Expand Down
194 changes: 194 additions & 0 deletions config/krb5conf_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,123 @@
package config

import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

const (
krb5ConfIncHead = `
# To opt out of the system crypto-policies configuration of krb5, remove the
# symlink at /etc/krb5.conf.d/crypto-policies which will not be recreated.
#includedir /etc/krb5.conf.d/
`

krb5ConfInc = `
[logging]
default = FILE:/var/log/krb5libs.log
kdc = FILE:/var/log/krb5kdc.log
admin_server = FILE:/var/log/kadmind.log

[libdefaults]
dns_lookup_realm = false
ticket_lifetime = 24h
renew_lifetime = 7d
forwardable = true
rdns = false
pkinit_anchors = FILE:/etc/pki/tls/certs/ca-bundle.crt
spake_preauth_groups = edwards25519
# default_realm = EXAMPLE.COM
default_ccache_name = KEYRING:persistent:%{uid}

[realms]
# EXAMPLE.COM = {
# kdc = kerberos.example.com
# admin_server = kerberos.example.com
# }

[domain_realm]
# .example.com = EXAMPLE.COM
# example.com = EXAMPLE.COM
`
krb5ConfIHead = `
[logging]
default = FILE:/var/log/kerberos/krb5libs.log
kdc = FILE:/var/log/kerberos/krb5kdc.log
admin_server = FILE:/var/log/kerberos/kadmind.log

`
krb5Include = `
[libdefaults]
default_realm = TEST.GOKRB5 ; comment to be ignored
dns_lookup_realm = false

dns_lookup_kdc = false
#dns_lookup_kdc = true
;dns_lookup_kdc = true
#dns_lookup_kdc = true
;dns_lookup_kdc = true
ticket_lifetime = 10h ;comment to be ignored
forwardable = yes #comment to be ignored
default_keytab_name = FILE:/etc/krb5.keytab

default_client_keytab_name = FILE:/home/gokrb5/client.keytab
default_tkt_enctypes = aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 # comment to be ignored

`
krb5ConfI = `
[realms]
TEST.GOKRB5 = {
kdc = 10.80.88.88:88 #comment to be ignored
kdc = assume.port.num ;comment to be ignored
kdc = some.other.port:1234 # comment to be ignored

kdc = 10.80.88.88*
kdc = 10.1.2.3.4:88

admin_server = 10.80.88.88:749 ; comment to be ignored
default_domain = test.gokrb5
}
EXAMPLE.COM = {
kdc = kerberos.example.com
kdc = kerberos-1.example.com
admin_server = kerberos.example.com
auth_to_local = RULE:[1:$1@$0](.*@EXAMPLE.COM)s/.*//
}
lowercase.org = {
kdc = kerberos.lowercase.org
admin_server = kerberos.lowercase.org
}


[domain_realm]
.test.gokrb5 = TEST.GOKRB5 #comment to be ignored

test.gokrb5 = TEST.GOKRB5 ;comment to be ignored

.example.com = EXAMPLE.COM # comment to be ignored
hostname1.example.com = EXAMPLE.COM ; comment to be ignored
hostname2.example.com = TEST.GOKRB5
.testlowercase.org = lowercase.org


[appdefaults]
pam = {
debug = false

ticket_lifetime = 36000

renew_lifetime = 36000
forwardable = true
krb4_convert = false
}
`

krb5Conf = `
[logging]
default = FILE:/var/log/kerberos/krb5libs.log
Expand Down Expand Up @@ -308,6 +416,92 @@ const (
`
)

func TestLoadincludedir(t *testing.T) {
t.Parallel()
cf, _ := ioutil.TempFile(os.TempDir(), "TEST-gokrb5-krb5inc.conf")
defer os.Remove(cf.Name())
cdir, _ := ioutil.TempDir("", "example")
defer os.RemoveAll(cdir)
cfgDir := "testkrb5.conf.d"
files, err := os.ReadDir(cfgDir)
if err != nil {
t.Fatalf("Error loading config dir: %v", err)
}
for _, file := range files {
srcPath := filepath.Join(cfgDir, file.Name())
data, err := ioutil.ReadFile(srcPath)
if err != nil {
t.Fatalf("Error reading config file: %v", err)
}
dstPath := filepath.Join(cdir, file.Name())
err = ioutil.WriteFile(dstPath, data, 0644)
if err != nil {
t.Fatalf("Error writting config file: %v", err)
}
}
krb5ContentsInc := krb5ConfIncHead + fmt.Sprintf("includedir %s\n", cdir) + krb5ConfInc
cf.WriteString(krb5ContentsInc)

c, err := Load(cf.Name())
if err != nil {
t.Fatalf("Error loading config: %v", err)
}

assert.Equal(t, "TEST.GOKRB5", c.LibDefaults.DefaultRealm, "[libdefaults] default_realm not as expected")
assert.Equal(t, false, c.LibDefaults.DNSLookupRealm, "[libdefaults] dns_lookup_realm not as expected")
assert.Equal(t, false, c.LibDefaults.DNSLookupKDC, "[libdefaults] dns_lookup_kdc not as expected")
assert.Equal(t, time.Duration(24)*time.Hour, c.LibDefaults.TicketLifetime, "[libdefaults] Ticket lifetime not as expected")
assert.Equal(t, true, c.LibDefaults.Forwardable, "[libdefaults] forwardable not as expected")
assert.Equal(t, "/etc/krb5.keytab", c.LibDefaults.DefaultKeytabName, "[libdefaults] default_keytab_name not as expected")
assert.Equal(t, "FILE:/home/gokrb5/client.keytab", c.LibDefaults.DefaultClientKeytabName, "[libdefaults] default_client_keytab_name not as expected")
assert.Equal(t, []string{"aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha1-96", "des3-cbc-sha1", "arcfour-hmac-md5", "camellia256-cts-cmac", "camellia128-cts-cmac", "des-cbc-crc", "des-cbc-md5", "des-cbc-md4"}, c.LibDefaults.DefaultTktEnctypes, "[libdefaults] default_tkt_enctypes not as expected")
assert.Equal(t, 7, len(c.Realms), "Number of realms not as expected")
assert.Equal(t, "TEST.GOKRB5", c.Realms[6].Realm, "[realm] realm name not as expectd")
assert.Equal(t, []string{"cndc.test.gokrb5"}, c.Realms[6].AdminServer, "[realm] Admin_server not as expectd")
assert.Equal(t, []string{"cndc.test.gokrb5"}, c.Realms[6].KPasswdServer, "[realm] Kpasswd_server not as expectd")
assert.Equal(t, "test.gokrb5", c.Realms[6].DefaultDomain, "[realm] Default_domain not as expectd")
assert.Equal(t, []string{"cndc.test.gokrb5:88"}, c.Realms[6].KDC, "[realm] Kdc not as expectd")

}

func TestLoadinclude(t *testing.T) {
t.Parallel()
cf, _ := ioutil.TempFile(os.TempDir(), "TEST-gokrb5-krb5inc.conf")
defer os.Remove(cf.Name())
incf, _ := ioutil.TempFile(os.TempDir(), "TEST-gokrb5-krb5include")
defer os.Remove(incf.Name())
incf.WriteString(krb5Include)
krb5ContentsI := krb5ConfIHead + fmt.Sprintf("include %s\n", incf.Name()) + krb5ConfI
cf.WriteString(krb5ContentsI)

c, err := Load(cf.Name())
if err != nil {
t.Fatalf("Error loading config: %v", err)
}

assert.Equal(t, "TEST.GOKRB5", c.LibDefaults.DefaultRealm, "[libdefaults] default_realm not as expected")
assert.Equal(t, false, c.LibDefaults.DNSLookupRealm, "[libdefaults] dns_lookup_realm not as expected")
assert.Equal(t, false, c.LibDefaults.DNSLookupKDC, "[libdefaults] dns_lookup_kdc not as expected")
assert.Equal(t, time.Duration(10)*time.Hour, c.LibDefaults.TicketLifetime, "[libdefaults] Ticket lifetime not as expected")
assert.Equal(t, true, c.LibDefaults.Forwardable, "[libdefaults] forwardable not as expected")
assert.Equal(t, "FILE:/etc/krb5.keytab", c.LibDefaults.DefaultKeytabName, "[libdefaults] default_keytab_name not as expected")
assert.Equal(t, "FILE:/home/gokrb5/client.keytab", c.LibDefaults.DefaultClientKeytabName, "[libdefaults] default_client_keytab_name not as expected")
assert.Equal(t, []string{"aes256-cts-hmac-sha1-96", "aes128-cts-hmac-sha1-96"}, c.LibDefaults.DefaultTktEnctypes, "[libdefaults] default_tkt_enctypes not as expected")

assert.Equal(t, 3, len(c.Realms), "Number of realms not as expected")
assert.Equal(t, "TEST.GOKRB5", c.Realms[0].Realm, "[realm] realm name not as expectd")
assert.Equal(t, []string{"10.80.88.88:749"}, c.Realms[0].AdminServer, "[realm] Admin_server not as expectd")
assert.Equal(t, []string{"10.80.88.88:464"}, c.Realms[0].KPasswdServer, "[realm] Kpasswd_server not as expectd")
assert.Equal(t, "test.gokrb5", c.Realms[0].DefaultDomain, "[realm] Default_domain not as expectd")
assert.Equal(t, []string{"10.80.88.88:88", "assume.port.num:88", "some.other.port:1234", "10.80.88.88:88"}, c.Realms[0].KDC, "[realm] Kdc not as expectd")
assert.Equal(t, []string{"kerberos.example.com:88", "kerberos-1.example.com:88"}, c.Realms[1].KDC, "[realm] Kdc not as expectd")
assert.Equal(t, []string{"kerberos.example.com"}, c.Realms[1].AdminServer, "[realm] Admin_server not as expectd")

assert.Equal(t, "TEST.GOKRB5", c.DomainRealm[".test.gokrb5"], "Domain to realm mapping not as expected")
assert.Equal(t, "TEST.GOKRB5", c.DomainRealm["test.gokrb5"], "Domain to realm mapping not as expected")

}

func TestLoad(t *testing.T) {
t.Parallel()
cf, _ := ioutil.TempFile(os.TempDir(), "TEST-gokrb5-krb5.conf")
Expand Down
2 changes: 2 additions & 0 deletions config/testkrb5.conf.d/crypto-policies
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[libdefaults]
permitted_enctypes = aes256-cts-hmac-sha1-96 aes256-cts-hmac-sha384-192 camellia256-cts-cmac aes128-cts-hmac-sha1-96 aes128-cts-hmac-sha256-128 camellia128-cts-cmac
11 changes: 11 additions & 0 deletions config/testkrb5.conf.d/fedoraproject_org
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[realms]
FEDORAPROJECT.ORG = {
kdc = https://id.fedoraproject.org/KdcProxy
pkinit_anchors = FILE:/etc/pki/ipa/fedoraproject_ipa_ca.crt
}
[domain_realm]
.fedoraproject.org = FEDORAPROJECT.ORG
fedoraproject.org = FEDORAPROJECT.ORG
.centos.org = FEDORAPROJECT.ORG
centos.org = FEDORAPROJECT.ORG

17 changes: 17 additions & 0 deletions config/testkrb5.conf.d/puppet_FOTO.FR.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
; Puppet managed file for realm FOTO.FR
; Files matching /etc/krb5.conf.d/puppet_* will be purged if
; no longer configured in puppet.

[domain_realm]
.foto.fr = FOTO.FR

[realms]
IN2P3.FR = {
default_domain = foto.fr
kpasswd_server = kerbadmin.foto.fr
admin_server = kerbadmin.foto.fr
kdc = kerb441.foto.fr
kdc = kerb442.foto.fr
kdc = kerb443.foto.fr
}

18 changes: 18 additions & 0 deletions config/testkrb5.conf.d/puppet_MEP.TOTO.AC.UK.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
; Puppet totoaged file for realm MEP.TOTO.AC.UK
; Files matching /etc/krb5.conf.d/puppet_* will be purged if
; no longer configured in puppet.

[domain_realm]
.mep.toto.ac.uk = MEP.TOTO.AC.UK

[realms]
MEP.TOTO.AC.UK = {
default_domain = mep.toto.ac.uk
kpasswd_server = afs444.mep.toto.ac.uk
admin_server = afs4444.mep.toto.ac.uk
kdc = afs441.mep.toto.ac.uk
kdc = afs442.mep.toto.ac.uk
kdc = afs443.mep.toto.ac.uk
kdc = afs444.mep.toto.ac.uk
}

12 changes: 12 additions & 0 deletions config/testkrb5.conf.d/puppet_MOTO.HU.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
; Puppet managed file for realm MOTO.HU
; Files matching /etc/krb5.conf.d/puppet_* will be purged if
; no longer configured in puppet.

[domain_realm]

[realms]
MOTO.HU = {
admin_server = kerberos.moto.hu
kdc = kerberos.moto.hu
}

23 changes: 23 additions & 0 deletions config/testkrb5.conf.d/puppet_TOTO.GOV.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
; Puppet managed file for realm TOTO.GOV
; Files matching /etc/krb5.conf.d/puppet_* will be purged if
; no longer configured in puppet.

[domain_realm]
.toto.gov = TOTO.GOV

[realms]
FNAL.GOV = {
default_domain = toto.gov
admin_server = krbtoto-admin.toto.gov
kdc = krbtoto-fcc33.toto.gov:88
kdc = krbtoto-42.toto.gov:88
kdc = krbtoto-43.toto.gov:88
kdc = krbtoto-41.toto.gov:88
kdc = krbtoto-44.toto.gov:88
kdc = krbtoto-enstr.toto.gov:88
kdc = krbtoto-fg32.toto.gov:88
kdc = krbtoto-cms88.toto.gov:88
kdc = krbtoto-cms04.toto.gov:88
kdc = krbtoto-d033on.toto.gov:88
}

3 changes: 3 additions & 0 deletions config/testkrb5.conf.d/puppet_default_ccache_name
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
; Puppet managed
[libdefaults]
default_ccache_name = KEYRING:persistent:%{uid}
Loading