Skip to content

Commit

Permalink
Feat: Allow krb5 config through environment variables (#157)
Browse files Browse the repository at this point in the history
* FEAT:support environment config of krb5

* update readme and version

* use client keytab file as default
  • Loading branch information
shueybubbles authored Oct 19, 2023
1 parent 670fd58 commit 2b177e8
Show file tree
Hide file tree
Showing 4 changed files with 306 additions and 56 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@

* Improved speed of CharsetToUTF8 (#154)

## 1.7.0

### Changed

* krb5 authenticator supports standard Kerberos environment variables for configuration

## 1.6.0

### Changed
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,10 @@ The package supports authentication via 3 methods.
### Kerberos Parameters

* `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.
* `krb5-configfile` (mandatory) - path to kerberos configuration file.
* `krb5-realm` (required with keytab and raw credentials) - Domain name for kerberos authentication.
* `krb5-keytabfile` - path to Keytab file.
* `krb5-credcachefile` - path to Credential cache.
* `krb5-configfile` (optional) - path to kerberos configuration file. Defaults to `/etc/krb5.conf`. Can also be set using `KRB5_CONFIG` environment variable.
* `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`.
* `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.
* `krb5-credcachefile` - path to Credential cache. Can also be set using environment variable `KRBCCNAME`.
* `krb5-dnslookupkdc` - Optional parameter in all contexts. Set to lookup KDCs in DNS. Boolean. Default is true.
* `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.

Expand Down
85 changes: 68 additions & 17 deletions integratedauth/krb5/krb5.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package krb5
import (
"errors"
"fmt"
"io/ioutil"
"net"
"os"
"strconv"
Expand Down Expand Up @@ -44,9 +43,10 @@ var (
)

var (
_ integratedauth.IntegratedAuthenticator = (*krbAuth)(nil)
fileExists = fileExistsOS
AuthProviderFunc integratedauth.Provider = integratedauth.ProviderFunc(getAuth)
_ integratedauth.IntegratedAuthenticator = (*krbAuth)(nil)
fileExists = fileExistsOS
loadDefaultConfigFromFile = newKrb5ConfigFromFile
AuthProviderFunc integratedauth.Provider = integratedauth.ProviderFunc(getAuth)
)

func init() {
Expand Down Expand Up @@ -95,39 +95,90 @@ type krb5Login struct {
}

// copies string parameters from connection string, parses optional parameters
func readKrb5Config(config msdsn.Config) (*krb5Login, error) {
// Environment variables for Kerberos config are listed at https://web.mit.edu/kerberos/krb5-1.12/doc/admin/env_variables.html
func readKrb5Config(cfg msdsn.Config) (*krb5Login, error) {
login := &krb5Login{
Krb5ConfigFile: config.Parameters[keytabConfigFile],
KeytabFile: config.Parameters[keytabFile],
CredCacheFile: config.Parameters[credCacheFile],
Realm: config.Parameters[realm],
UserName: config.User,
Password: config.Password,
ServerSPN: config.ServerSPN,
Krb5ConfigFile: cfg.Parameters[keytabConfigFile],
KeytabFile: cfg.Parameters[keytabFile],
CredCacheFile: cfg.Parameters[credCacheFile],
Realm: cfg.Parameters[realm],
UserName: cfg.User,
Password: cfg.Password,
ServerSPN: cfg.ServerSPN,
DNSLookupKDC: true,
UDPPreferenceLimit: 1,
loginMethod: none,
}

// If no conf file is provided , use the environment variable first then just use the default conf file location if not set
if len(login.Krb5ConfigFile) == 0 {
login.Krb5ConfigFile = os.Getenv("KRB5_CONFIG")
}

if len(login.Krb5ConfigFile) == 0 {
login.Krb5ConfigFile = `/etc/krb5.conf`
}

defaults, err := loadDefaultConfigFromFile(login)
if err != nil {
return nil, fmt.Errorf("Unable to load krb5 config to get default values: %w", err)
}

// If no Realm passed, try to split out the user name as `username@realm`
if len(login.Realm) == 0 {
nameParts := strings.SplitN(login.UserName, "@", 2)
if len(nameParts) > 1 {
login.UserName = nameParts[0]
login.Realm = nameParts[1]
}
}

if len(login.Realm) == 0 {
login.Realm = defaults.LibDefaults.DefaultRealm
}

// If the app provides a user name with no password, give the keytab file precedence over the credential cache
if len(login.UserName) > 0 && len(login.Password) == 0 {
if len(login.KeytabFile) == 0 {
login.KeytabFile = os.Getenv("KRB5_KTNAME")
}
if len(login.KeytabFile) == 0 {
kt := defaults.LibDefaults.DefaultClientKeytabName
if ok, _ := fileExists(kt, nil); ok {
login.KeytabFile = kt
}
}
if len(login.KeytabFile) == 0 {
kt := defaults.LibDefaults.DefaultKeytabName
if ok, _ := fileExists(kt, nil); ok {
login.KeytabFile = kt
}
}
}

// We fall back to the environment variable if set, but it will be ignored for login if login.KeytabFile is set
if len(login.CredCacheFile) == 0 {
login.CredCacheFile = os.Getenv("KRB5CCNAME")
}

// read optional parameters
val, ok := config.Parameters[dnsLookupKDC]
val, ok := cfg.Parameters[dnsLookupKDC]
if ok {
parsed, err := strconv.ParseBool(val)
if err != nil {
return nil, fmt.Errorf("invalid '%s' parameter '%s': %s", dnsLookupKDC, val, err.Error())
return nil, fmt.Errorf("invalid '%s' parameter '%s': %w", dnsLookupKDC, val, err)
}
login.DNSLookupKDC = parsed
}

val, ok = config.Parameters[udpPreferenceLimit]
val, ok = cfg.Parameters[udpPreferenceLimit]
if ok {
parsed, err := strconv.Atoi(val)
if err != nil {
return nil, fmt.Errorf("invalid '%s' parameter '%s': %s", udpPreferenceLimit, val, err.Error())
}
login.UDPPreferenceLimit = parsed
}

return login, nil
}

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

// loads keytab file specified in keytabFile and creates a client from its content, username and realm
func clientFromKeytab(krb5Login *krb5Login, cfg *config.Config) (*client.Client, error) {
data, err := ioutil.ReadFile(krb5Login.KeytabFile)
data, err := os.ReadFile(krb5Login.KeytabFile)
if err != nil {
return nil, err
}
Expand Down
Loading

0 comments on commit 2b177e8

Please sign in to comment.