Skip to content

Commit 2b177e8

Browse files
authored
Feat: Allow krb5 config through environment variables (#157)
* FEAT:support environment config of krb5 * update readme and version * use client keytab file as default
1 parent 670fd58 commit 2b177e8

File tree

4 files changed

+306
-56
lines changed

4 files changed

+306
-56
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@
1313

1414
* Improved speed of CharsetToUTF8 (#154)
1515

16+
## 1.7.0
17+
18+
### Changed
19+
20+
* krb5 authenticator supports standard Kerberos environment variables for configuration
21+
1622
## 1.6.0
1723

1824
### Changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,10 @@ The package supports authentication via 3 methods.
124124
### Kerberos Parameters
125125

126126
* `authenticator` - set this to `krb5` to enable kerberos authentication. If this is not present, the default provider would be `ntlm` for unix and `winsspi` for windows.
127-
* `krb5-configfile` (mandatory) - path to kerberos configuration file.
128-
* `krb5-realm` (required with keytab and raw credentials) - Domain name for kerberos authentication.
129-
* `krb5-keytabfile` - path to Keytab file.
130-
* `krb5-credcachefile` - path to Credential cache.
127+
* `krb5-configfile` (optional) - path to kerberos configuration file. Defaults to `/etc/krb5.conf`. Can also be set using `KRB5_CONFIG` environment variable.
128+
* `krb5-realm` (required with keytab and raw credentials) - Domain name for kerberos authentication. Omit this parameter if the realm is part of the user name like `username@REALM`.
129+
* `krb5-keytabfile` - path to Keytab file. Can also be set using environment variable `KRB5_KTNAME`. If no parameter or environment variable is set, the `DefaultClientKeytabName` value from the krb5 config file is used.
130+
* `krb5-credcachefile` - path to Credential cache. Can also be set using environment variable `KRBCCNAME`.
131131
* `krb5-dnslookupkdc` - Optional parameter in all contexts. Set to lookup KDCs in DNS. Boolean. Default is true.
132132
* `krb5-udppreferencelimit` - Optional parameter in all contexts. 1 means to always use tcp. MIT krb5 has a default value of 1465, and it prevents user setting more than 32700. Integer. Default is 1.
133133

integratedauth/krb5/krb5.go

Lines changed: 68 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ package krb5
44
import (
55
"errors"
66
"fmt"
7-
"io/ioutil"
87
"net"
98
"os"
109
"strconv"
@@ -44,9 +43,10 @@ var (
4443
)
4544

4645
var (
47-
_ integratedauth.IntegratedAuthenticator = (*krbAuth)(nil)
48-
fileExists = fileExistsOS
49-
AuthProviderFunc integratedauth.Provider = integratedauth.ProviderFunc(getAuth)
46+
_ integratedauth.IntegratedAuthenticator = (*krbAuth)(nil)
47+
fileExists = fileExistsOS
48+
loadDefaultConfigFromFile = newKrb5ConfigFromFile
49+
AuthProviderFunc integratedauth.Provider = integratedauth.ProviderFunc(getAuth)
5050
)
5151

5252
func init() {
@@ -95,39 +95,90 @@ type krb5Login struct {
9595
}
9696

9797
// copies string parameters from connection string, parses optional parameters
98-
func readKrb5Config(config msdsn.Config) (*krb5Login, error) {
98+
// Environment variables for Kerberos config are listed at https://web.mit.edu/kerberos/krb5-1.12/doc/admin/env_variables.html
99+
func readKrb5Config(cfg msdsn.Config) (*krb5Login, error) {
99100
login := &krb5Login{
100-
Krb5ConfigFile: config.Parameters[keytabConfigFile],
101-
KeytabFile: config.Parameters[keytabFile],
102-
CredCacheFile: config.Parameters[credCacheFile],
103-
Realm: config.Parameters[realm],
104-
UserName: config.User,
105-
Password: config.Password,
106-
ServerSPN: config.ServerSPN,
101+
Krb5ConfigFile: cfg.Parameters[keytabConfigFile],
102+
KeytabFile: cfg.Parameters[keytabFile],
103+
CredCacheFile: cfg.Parameters[credCacheFile],
104+
Realm: cfg.Parameters[realm],
105+
UserName: cfg.User,
106+
Password: cfg.Password,
107+
ServerSPN: cfg.ServerSPN,
107108
DNSLookupKDC: true,
108109
UDPPreferenceLimit: 1,
109110
loginMethod: none,
110111
}
111112

113+
// If no conf file is provided , use the environment variable first then just use the default conf file location if not set
114+
if len(login.Krb5ConfigFile) == 0 {
115+
login.Krb5ConfigFile = os.Getenv("KRB5_CONFIG")
116+
}
117+
118+
if len(login.Krb5ConfigFile) == 0 {
119+
login.Krb5ConfigFile = `/etc/krb5.conf`
120+
}
121+
122+
defaults, err := loadDefaultConfigFromFile(login)
123+
if err != nil {
124+
return nil, fmt.Errorf("Unable to load krb5 config to get default values: %w", err)
125+
}
126+
127+
// If no Realm passed, try to split out the user name as `username@realm`
128+
if len(login.Realm) == 0 {
129+
nameParts := strings.SplitN(login.UserName, "@", 2)
130+
if len(nameParts) > 1 {
131+
login.UserName = nameParts[0]
132+
login.Realm = nameParts[1]
133+
}
134+
}
135+
136+
if len(login.Realm) == 0 {
137+
login.Realm = defaults.LibDefaults.DefaultRealm
138+
}
139+
140+
// If the app provides a user name with no password, give the keytab file precedence over the credential cache
141+
if len(login.UserName) > 0 && len(login.Password) == 0 {
142+
if len(login.KeytabFile) == 0 {
143+
login.KeytabFile = os.Getenv("KRB5_KTNAME")
144+
}
145+
if len(login.KeytabFile) == 0 {
146+
kt := defaults.LibDefaults.DefaultClientKeytabName
147+
if ok, _ := fileExists(kt, nil); ok {
148+
login.KeytabFile = kt
149+
}
150+
}
151+
if len(login.KeytabFile) == 0 {
152+
kt := defaults.LibDefaults.DefaultKeytabName
153+
if ok, _ := fileExists(kt, nil); ok {
154+
login.KeytabFile = kt
155+
}
156+
}
157+
}
158+
159+
// We fall back to the environment variable if set, but it will be ignored for login if login.KeytabFile is set
160+
if len(login.CredCacheFile) == 0 {
161+
login.CredCacheFile = os.Getenv("KRB5CCNAME")
162+
}
163+
112164
// read optional parameters
113-
val, ok := config.Parameters[dnsLookupKDC]
165+
val, ok := cfg.Parameters[dnsLookupKDC]
114166
if ok {
115167
parsed, err := strconv.ParseBool(val)
116168
if err != nil {
117-
return nil, fmt.Errorf("invalid '%s' parameter '%s': %s", dnsLookupKDC, val, err.Error())
169+
return nil, fmt.Errorf("invalid '%s' parameter '%s': %w", dnsLookupKDC, val, err)
118170
}
119171
login.DNSLookupKDC = parsed
120172
}
121173

122-
val, ok = config.Parameters[udpPreferenceLimit]
174+
val, ok = cfg.Parameters[udpPreferenceLimit]
123175
if ok {
124176
parsed, err := strconv.Atoi(val)
125177
if err != nil {
126178
return nil, fmt.Errorf("invalid '%s' parameter '%s': %s", udpPreferenceLimit, val, err.Error())
127179
}
128180
login.UDPPreferenceLimit = parsed
129181
}
130-
131182
return login, nil
132183
}
133184

@@ -292,7 +343,7 @@ func clientFromUsernameAndPassword(krb5Login *krb5Login, cfg *config.Config) (*c
292343

293344
// loads keytab file specified in keytabFile and creates a client from its content, username and realm
294345
func clientFromKeytab(krb5Login *krb5Login, cfg *config.Config) (*client.Client, error) {
295-
data, err := ioutil.ReadFile(krb5Login.KeytabFile)
346+
data, err := os.ReadFile(krb5Login.KeytabFile)
296347
if err != nil {
297348
return nil, err
298349
}

0 commit comments

Comments
 (0)