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 Kerberos auth #35

Merged
merged 13 commits into from
Oct 11, 2022
Merged

add Kerberos auth #35

merged 13 commits into from
Oct 11, 2022

Conversation

chandanjainn
Copy link
Collaborator

@chandanjainn chandanjainn commented Jul 8, 2022

Have added support for kerberos authentication for go-mssqldb using the GOKRB5 package.

To enable the support for krb the users need to pass the connection string as the following

  1. To use the keytab file

server=server;user id=user;port=1345;realm=domain;trustservercertificate=true;krb5conffile=/etc/krb5.conf;keytabfile=/path/to/keytabFile.keytab

  1. To use kerberos cache file

"server=server;port=1345;realm=domain;trustservercertificate=true;krb5conffile=/etc/krb5.conf;krbcache=/tmp/krb5cc_1000"

krb5conffile        : path to the krb5 configuration file, defaults to "/etc/krb5.conf"
krbcache            : path to the kerberos cache file, required if initkrbwithkeytab = false
keytabfile          : path to the keytab file, required if initkrbwithkeytab = true
realm               : domain name, required when using kerberos cache

@ghost
Copy link

ghost commented Jul 8, 2022

CLA assistant check
All CLA requirements met.

@shueybubbles
Copy link
Collaborator

shueybubbles commented Jul 14, 2022

Can you separate the krb5 code into non-Windows .go files and stub the implementation in a Windows .go file so Windows apps don't need to pick up this code?


In reply to: 1184565905

@shueybubbles
Copy link
Collaborator

shueybubbles commented Jul 20, 2022

@dzsquared @chandanjainn @stuartpa the krb code requires go 1.13 and higher.
Should Chandan put the new code in .go files with a 1.13 and higher build directive (and linux only), or should we go ahead and remove 1.12 and prior versions from the appveyor tests?


In reply to: 1190471174

@chandanjainn
Copy link
Collaborator Author

chandanjainn commented Jul 21, 2022

Can you separate the krb5 code into non-Windows .go files and stub the implementation in a Windows .go file so Windows apps don't need to pick up this code?

apologies for the late response @shueybubbles working on this. the only issue at my end is lack of a windows system to test out the changes.


In reply to: 1191093407

chandan jain and others added 2 commits August 12, 2022 17:59
* add windows stub for krb

* add import for msdsn

* fix ci lint issue

Co-authored-by: chandan jain <Chandan.Jain@ocean.ibm.com>
@chandanjainn
Copy link
Collaborator Author

chandanjainn commented Aug 12, 2022

@shueybubbles have added the stub file for windows so that krb is not added as a dependency for windows based systems.
Please review the approach.


In reply to: 1213081795

kerbauth.go Outdated Show resolved Hide resolved
types.go Outdated Show resolved Hide resolved
msdsn/conn_str.go Outdated Show resolved Hide resolved
@shueybubbles
Copy link
Collaborator

shueybubbles commented Aug 14, 2022

you can also add a go113 directive to the files with !windows directives so pre-go 1.13 won't try to use it.


In reply to: 1214441216

* remove krb dep for windows

* remove krb dep

* unit test coverage

Co-authored-by: chandan jain <Chandan.Jain@ocean.ibm.com>
@chandanjainn
Copy link
Collaborator Author

@shueybubbles done with the suggested changes. please review

@PeteBassettBet365
Copy link
Collaborator

PeteBassettBet365 commented Aug 17, 2022

Hi. There appear to be some logic issues and opportunities for more thorough error checking here.

For instance, I'm attempting to connect using a keytab. I dont supply a KrbCache param in the connection string, which I believe is valid and correct. However the code panics inside auth.go func getKrbParams in the dereference of :

krb["KrbCache"].(string)
in
krbParams["Cache"], err = setupKerbCache(krb["KrbCache"].(string))

Aside from the unchecked type assertion, the function as a whole enforces the connection string contain both a KrbCache and KeytabFile parameter. Their use should be mutually exclusive.

The conn string is user supplied and as such can be mangled entirely, a sensible error message should tell the caller of the issue.

Also I dont believe there is currently a way to connect using specified username and password on the connection string.

e.g. something like
server=localhost;user id=MyUsername;password=MyPassword;database=DBName;krb5conffile=path/to/file;realm=domain.co.uk

We use the methodology in certain scenarios where distribution of key keytab files across a server estate would prohibitive. When initially investigating use of keytabs we found that if a domain account password is somehow locked out or the password becomes changed, even changing the password back to it's original value will not make the currently existing keytabs valid again. New keytabs must the created and distributed to all servers. Some services are sufficiently critical that they must be able to re connect in the face of malicious action or similar, even if the acc in question is set to 1) Password never expires 2) User can not change his own password

@chandanjainn
Copy link
Collaborator Author

chandanjainn commented Aug 17, 2022

@PeteBassettBet365 have fixed all unchecked assertion based issues by using structs everywhere and made cache and keytab mutually exclusive with proper error messages. All of this was was there in the earlier commits but got lost while removing the dependencies for windows system in the last couple of commits.

Regarding the ability to connect using username and password, am not sure what you are expecting here? Why would we use password in the connection string along with kerberos config?

if you see the code at https://github.com/microsoft/go-mssqldb/pull/35/files#diff-1f48700829d7e0cdba7f3729d6a7c1c5df3aa11f2aeeed583c7e1e39aa7b66d1R21 , it falls back to username/password based authN given that the user is not sending any krb related config in the conn string and if the user does we'll get relevant errors thrown.

@PeteBassettBet365
Copy link
Collaborator

Hi again @chandanjainn thanks for sorting the few issues!

Sorry, I'm not talking about sqlserver auth fallback, this is kerberos auth. The reason I'd like to support supplying the kerberos username and password is because, for certain use cases as I said previously, it is easier/faster than distribution of new keytabs to servers. github.com/jcmturner/gokrb5/v8/client supports this via NewWithPassword

msdsn/conn_str.go Outdated Show resolved Hide resolved
tds.go Outdated Show resolved Hide resolved
@codecov-commenter
Copy link

codecov-commenter commented Aug 30, 2022

Codecov Report

Merging #35 (cde874d) into main (ba5a4a0) will increase coverage by 0.16%.
The diff coverage is n/a.

@@            Coverage Diff             @@
##             main      #35      +/-   ##
==========================================
+ Coverage   73.08%   73.25%   +0.16%     
==========================================
  Files          25       25              
  Lines        5458     5458              
==========================================
+ Hits         3989     3998       +9     
+ Misses       1230     1224       -6     
+ Partials      239      236       -3     
Impacted Files Coverage Δ
tds.go 68.08% <0.00%> (+1.09%) ⬆️

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@chandanjainn chandanjainn requested review from shueybubbles and removed request for apoorvdeshmukh, stuartpa and gambtho August 30, 2022 10:39
README.md Outdated Show resolved Hide resolved
AuthProviderFunc integratedauth.Provider = integratedauth.ProviderFunc(getAuth)
)

func init() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If my app includes this package, and my connection string doesn't specify a specific authenticator for integrated auth, will it use this authenticator with some default setting ?

IE, I'd like my connection string to be the same on Windows as Linux.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this init also do:
integratedAuth.DefaultProviderName = "krb5"
?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shueybubbles this comment by Pete should answer your question https://github.com/microsoft/go-mssqldb/blob/main/integratedauth/auth.go#L23

yes, it will fallback to default provider i.e ntlm for unix and winsspi for windows if the authenticator is not provided in the connection string.

refer https://github.com/microsoft/go-mssqldb/blob/main/auth_windows.go#L17 and https://github.com/microsoft/go-mssqldb/blob/main/auth_unix.go#L14

And for your second question, no I dont think so we should do integratedAuth.DefaultProviderName = "krb5" as this is meant to be a fallback.

@PeteBassettBet365 this is correct right?

@@ -59,6 +60,31 @@ Other supported formats are listed below.
* `Workstation ID` - The workstation name (default is the host name)
* `ApplicationIntent` - Can be given the value `ReadOnly` to initiate a read-only connection to an Availability Group listener. The `database` must be specified when connecting with `Application Intent` set to `ReadOnly`.

### Kerberos Active Directory authentication outside Windows
The package supports authentication via 3 methods.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the default approach used if the connection string doesn't specify one explicitly? If a keytab file exists in some default location will it be used without having to specify it in the connection string?

Copy link
Collaborator Author

@chandanjainn chandanjainn Sep 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shueybubbles as of now, no. The user needs to explicitly specify the locations for keytab/cache.

A default location can be added. Should we?

Refer this comment

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the MIT doc itself says to put it in /etc by default, or set environment variable KRB5_CONFIG. Couldn't we just rely on those too? I'm a bit surprised the jcmturner/gokrb5 module isn't already doing that

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd expect someone to be able to set the kerberos environment variables https://web.mit.edu/kerberos/krb5-1.12/doc/admin/env_variables.html and not have to put them all explicitly in the connection string here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I read https://github.com/jcmturner/gokrb5/blob/master/config/krb5conf.go and have a recommendation for a fallback model for the kr5b.conf file location.

  1. Specified in the connection string
  2. Check KRB5_CONFIG environment variable
  3. Check for /etc/krb5.conf
  4. just use config.NewConfig()

If the user doesn't specify paths to keytab or cache, they probably can rely on the defaults defined in krb5.conf.
If there's no krb5.conf file in a default location or the environment variables aren't set, the authentication will fail but that's by design.

Does this seem reasonable?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shueybubbles yes, it does. will make the changes.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thx I think trying to follow the MIT spec will help developers. If jcmturner/go-krb5 isn't following the spec for environment variables and defaults, you should open some issues against that module to do so.

@@ -28,6 +28,7 @@ Other supported formats are listed below.
* `false` - Data sent between client and server is not encrypted beyond the login packet. (Default)
* `true` - Data sent between client and server is encrypted.
* `app name` - The application name (default is go-mssqldb)
* `authenticator` - Can be used to specify use of a registered authentication provider. (e.g. ntlm, winsspi (on windows) or krb5 (on linux))

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if I don't set a specific authenticator, can the integratedAuth code in the core try to connect using the default provider first, then fall back to any other registered providers ?

This is the scenario:

  1. My service has a single connection string of a server name with no user name.
  2. The client apps run on both Linux and Windows hosts
  3. My preproduction environment Linux only has NTLM while production has krb5

If my client app imports the krb5 package will the runtime behavior correctly use SSPI on Windows, krb5 in prod, and ntlm in preprod?

If my connection string does have an authenticator tag then I'd expect it to fail to connect if that method isn't available or is mis-configured on the host.
If the connection string doesn't have an authenticator tag, I'd expect to have a predictable behavior based on what is available on the host at runtime and not have to create separate connection strings for every possible environment.

}
}
if c["krbcache"] == "" && c["keytabfile"] == "" {
missingParam = "atleast krbcache or keytab is required"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there no default location for these files or a default behavior that can be determined based on looking for krb5.conf in a well-known location ?

By contrast it seems most of these parameters are optional in the JDBC SQL driver.
https://docs.microsoft.com/en-us/sql/connect/jdbc/using-kerberos-integrated-authentication-to-connect-to-sql-server?view=sql-server-ver16

Copy link
Collaborator Author

@chandanjainn chandanjainn Sep 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, there's a default location for keytab and krbcache at /etc/krb5/ and /tmp/krb5cc_%{uid} respectively.

the reason we didn't look there in the first place was so that the user will be aware of the exact keytab file being used just in case their system has multiple keytabs.

do we want to check this location in case the user hasnt provided one ? can add this support.

README.md Outdated
### Kerberos Parameters

* `krb5conffile` - path to kerberos configuration file.
* `realm` - Domain name for kerberos authentication.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

realm` - Domain name for kerberos authentication.

The JDBC driver docs have "realm" as optional

https://docs.microsoft.com/sql/connect/jdbc/setting-the-connection-properties?view=sql-server-ver16

(Version 9.4+) The realm for Kerberos authentication. Set this value to override the Kerberos authentication realm the driver autodetects from the server's realm.

Do you know what it means by "autodetects from the server's realm"? Is that a feature in JDBC we are missing here? Or is it something handled in the krb module itself?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will check up on the JDBC part and revert. I hope, I have answered rest of your questions?

@shueybubbles
Copy link
Collaborator

I'd like to see my questions about how apps will need to manage integrated auth connection strings, protocol fallback behavior, etc answered in the README.md at least so it's clear what the limitations are about connection string reuse across Windows and Linux for integrated auth.
Maybe a single connection string for all environments isn't achievable, but let's document it appropriately. If there are potentially ways to enable protocol negotiation in the future, please open an Issue to discuss what that would look like.
thx!

@LessTravelledWay
Copy link

Activity on this has stalled - what do we need to do to get this moving again ?

@shueybubbles - Have all your points been addressed ?

@chandanjainn - @PeteBassettBet365 is available to assist with any blockers.

@chandanjainn
Copy link
Collaborator Author

@LessTravelledWay @shueybubbles apologies for the late response here. I have been on a medical leave for some time and will need some more time to recover fully.

Can we get the current PR merged as a V1 and then take up the suggestions as enhancement in future releases?

Copy link
Collaborator

@shueybubbles shueybubbles left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@PeteBassettBet365
Copy link
Collaborator

PeteBassettBet365 commented Oct 11, 2022

Hi. I would add one point, I think the server name needs to be canonicalized with a call to net.LookupCNAME. I'll confirm in the morning. @shueybubbles I'll go back and answer your questions where I can regarding the auth system behaviour.

@shueybubbles
Copy link
Collaborator

Should we merge this as-is then @PeteBassettBet365 could follow up with any tweaks after testing? I don't have a working kerberos Linux setup to test with.
It shouldn't break anybody to merge it, and I don't plan to add a tag until some others confirm it is suitable for production.


In reply to: 1274962421

@PeteBassettBet365
Copy link
Collaborator

Understood. I can give it a test this end.

@shueybubbles shueybubbles merged commit 537893a into microsoft:main Oct 11, 2022
@PeteBassettBet365
Copy link
Collaborator

PeteBassettBet365 commented Oct 14, 2022

Reply to 1225995716

@PeteBassettBet365 got it. thanks for the feedback. will incorporate the changes for NewWithPassword in the upcoming commits.

@shueybubbles will add the changes after integration with Pete's changes by today or tomorrow EOD max. Have the changes on a separate branch, testing them out.

@chandanjainn @shueybubbles Hi. There are some bugs to be addressed here.

Raw Credentials dont work as the validateKerbConfig function https://github.com/microsoft/go-mssqldb/blob/main/integratedauth/krb5/krb5.go#L224
which returns an error of "atleast krbcache or keytab is required" when using Raw credentials as per the readme example. That error also needs to be fixed to add a space in "at least".

Example conn string:
authenticator=krb5;server=DatabaseServerName;database=DBName;user id=MyUserName;password=foo;realm=comani.com;krb5conffile=/etc/krb5.conf;

If I bypass that validation issue then the ServerSpn parsing code fails to handle .co.uk domains e.g. "server.domain.co.uk" will result in a domain of "server.domain.co"

Also, the Realm field
https://github.com/microsoft/go-mssqldb/blob/main/integratedauth/krb5/krb5.go#L54
is read from the connection string here
https://github.com/microsoft/go-mssqldb/blob/main/integratedauth/krb5/krb5.go#L203
but is then never referenced again and instead the ServerSPN is parsed to get the realm, which then fails as it doesnt handle .co.uk etc

If I bypass that I get further errors regarding KDC issues. I will check this further.

This is not ready for a tag.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants