-
Notifications
You must be signed in to change notification settings - Fork 0
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 SqlServer MSI Support #2
Conversation
Add MSI Auth option to SQL Server connection. Default if no password is provided in connection string.
update host name parsing to get just the resource endpoint for msi
update go-mssqldb to resolve panic issue referenced here: denisenkom/go-mssqldb#642
switch from deprecated methods. NewServicePrincipalTokenFromManagedIdentity calls the two methods that are deprecated for us
add useMsi param instead of looking for nil password
Update sqlserver readme for msi auth. make useMsi a bit safer
remove comment
database/sqlserver/sqlserver.go
Outdated
|
||
var db *sql.DB | ||
if useMsi { | ||
tokenProvider, err := getMSITokenProvider(fmt.Sprintf("%s%s", "https://", strings.Join(strings.Split(purl.Hostname(), ".")[1:], "."))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this line be refactored into a helper in order to make its purpose more clear and maybe make it testable? I'm a little worried about some of these string operations being brittle with unexpected input.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, I agree. The main purpose here is to get the resource uri which could change across clouds. I'll see what I can do. Currently, if the uri is incorrect, I believe it would result in a 4xx (invalid resource uri for tenant or something similar). Will validate that behavior and update
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, thank you! The cloud change use case is not something I had picked up from reading the code but that's super important.
var db *sql.DB | ||
if useMsi { | ||
tokenProvider, err := getMSITokenProvider(fmt.Sprintf("%s%s", "https://", strings.Join(strings.Split(purl.Hostname(), ".")[1:], "."))) | ||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of curiosity, what happens if I pass useMsi
as true
but there's no provider available? Is it a timeout or an immediate failure?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the resource uri is invalid it'll timeout, but if there isn't an identity available it seems to just hang - looking into why
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good! I'm trying to think like the maintainer of the upstream package might and try to identify any failure modes introduced. I don't think there's necessarily a specific action item here and we shouldn't block merging on this comment, but I am certainly curious.
return nil, err | ||
} | ||
|
||
db = sql.OpenDB(connector) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the difference between this sql.OpenDB()
call and the below sql.Open()
call? What's the reason for using two different methods to open the database?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sql.Open will actually end up calling sql.OpenDB behind the scenes, but it builds the connector first based on what's passed in/the db context. sql.OpenDB just expects a connector, which is this case we're building ourselves, so we can call sql.OpenDB directly in that case. This follows the mssql guidance for using msi connections: mssql msi
source code which has sql.Open and sql.OpenDB (sorry, can't permalink to sql.Open): https://golang.org/src/database/sql/sql.go
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, thanks for the explanation!
@@ -344,3 +374,16 @@ func (ss *SQLServer) ensureVersionTable() (err error) { | |||
|
|||
return nil | |||
} | |||
|
|||
func getMSITokenProvider(resource string) (func() (string, error), error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mind briefly walking me through the decision to use a higher order function here? What is it buying us?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohh, I see in this PR it's due to the short-lived nature of AAD tokens and this function ensures a refresh.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mssql.NewAccessTokenConnector expects a callback which is used to build the connection and that will be used to retrieve fresh tokens. msi.EnsureFresh() will update the token if it is about to expire, and then msi.OAuthToken() gets the token to be used for the connection
I like these changes and I'm looking forward to being able to use them in the usage service! Managed identity SQL auth will save us a lot of headaches around rolling passwords and disaster recovery. Do you have thoughts on the strategy to merge with the upstream repo after this merges in your branch? Some idle thoughts:
|
Co-authored-by: Keegan Campbell <[email protected]>
that was my initial thought too, have it reviewed here first then merge into the fork's main, then open a PR against the upstream |
That sounds good to me! I'm looking forward to seeing this PR on the main module. I won't "approve" this for now since I'm not quite sure of the etiquette in this fork scenario, but I'd definitely be interested in getting the maintainers' feedback soon. |
refactor resource uri logic into its own method
add tests for msi connection. can only test whether it fails with useMsi= true, or succeeds with useMsi=false
Add the option for MSI Auth to Azure SQL Server. Expects a param to be passed to enable MSI Auth, useMsi=true.
PR from denisenkom/go-mssqldb that added msi support: #546