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

Added HANA database plugin #2811

Merged
merged 15 commits into from
Jul 7, 2017
Merged

Added HANA database plugin #2811

merged 15 commits into from
Jul 7, 2017

Conversation

Caiyeon
Copy link
Contributor

@Caiyeon Caiyeon commented Jun 5, 2017

This set of commits adds HANA db as a database plugin, allowing for dynamic database user creation much like other database plugins.

This plugin allows for an {{expiration}} value in role definition. If defined in a role, upon credential generation, the plugin requests for HANA's server time, plus the default_ttl, and uses this value to create a user with a defined valid until field. The valid until field is a HANA side user property that ensures the user will be deactivated when the time is up, regardless of whether vault is available to revoke the user.

Revocation by default will perform a RESTRICT drop. This is a soft drop that will only drop users if they have no dependent objects. A CASCADE (hard) drop can be used by overwriting the revocation statements.

Renewing a lease will cause the plugin to request for HANA's time again, adding the current default_ttl, and changing the valid until property of the HANA user, to ensure the server will not deactivate the user until the lease is up.

Vendored dependencies:

Acceptance tests have been written and executed. Documentation html pages have also been provided.

Let me know if there's anything missing

@jefferai jefferai added this to the 0.7.3 milestone Jun 5, 2017
@jefferai
Copy link
Member

jefferai commented Jun 5, 2017

@briankassouf @calvn Note - no rush as this will definitely not be in 0.7.3, but don't have another milestone up yet.

@Caiyeon
Copy link
Contributor Author

Caiyeon commented Jun 5, 2017

Note: providing a {{expiration}} value in role definition combined with default_ttl=0 will cause:
* SQL HdbError 438 - invalid user parameter value: VALID UNTIL not allowed to be in the past

This is because the time is calculated just before sending, and thus the server will receive a time that is in the past.

This is documented in the documentation pages, where the expiration value is described. Let me know if you guys have a more elegant solution.

@briankassouf
Copy link
Contributor

@Caiyeon I'll try and take a look a little later this week! But wanted to bring up that #2812 just changed the arguments of plugins slightly, so you'll probably want to keep an eye on that PR. I think it's likely to get merged today or tomorrow.


credsProducer := &credsutil.SQLCredentialsProducer{
DisplayNameLen: 32,
UsernameLen: 128,
Copy link
Contributor

Choose a reason for hiding this comment

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

RolenameLen and Separator should be specified here as well!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will change. Seems like that functionality was added only recently


// CreateUser generates the username/password on the underlying HANA secret backend
// as instructed by the CreationStatement provided.
func (h *HANA) CreateUser(statements dbplugin.Statements, usernamePrefix string, expiration time.Time) (username string, password string, err error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The function definition for CreateUser is changing in the next release you'll need to upgrade this to instead of receiving usernamePrefix, to receive a dbplugin.UsernameConfig object. See https://github.com/hashicorp/vault/blob/master/plugins/database/mysql/mysql.go#L85 for an example.

}

// HANA does not allow hyphens in usernames, and highly prefers capital letters
username = strings.Replace(username, "-", "_", -1)
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be done by setting the Separator variable in the Credential producer object.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This in fact, cannot be done simply by setting the Separator variable.
If the displayname or rolename contains -, the username will be invalid for HANA db.
Therefore, even though Separator = "_", there is still an explicit conversion here.

// Most HANA configurations have password constraints.
// Prefix with A1a (user must change password upon login anyway)
password = strings.Replace(password, "-", "_", -1)
password = "A1a" + password
Copy link
Contributor

Choose a reason for hiding this comment

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

what does the "A1a" string do?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Most HANA dbs have a strict password policy. That is, they require one upper case, one numeric, and one lower case character (most widely used policy).

This prefix ensures that the newly generated password will conform to the policy.

// This ensures hana will deactivate the user when time is up, regardless of vault's actions
var expirationStr string
timeQuery := fmt.Sprintf("SELECT TO_NVARCHAR(add_seconds(CURRENT_TIMESTAMP,"+
"%f), 'YYYY-MM-DD HH24:MI:SS') FROM DUMMY", (expiration.Sub(time.Now())).Seconds())
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of adding the number of seconds you could just pass a formatted timestamp into the query, https://github.com/hashicorp/vault/blob/master/plugins/helper/database/credsutil/sql.go#L53 will format it correctly.

Copy link
Contributor

Choose a reason for hiding this comment

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

In fact, i'm not sure why an extra DB call is needed to get the expiration string?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will change. This was a leftover from when the formatted timestamp function was not available

var expirationStr string
timeQuery := fmt.Sprintf("SELECT TO_NVARCHAR(add_seconds(CURRENT_TIMESTAMP,"+
"%f), 'YYYY-MM-DD HH24:MI:SS') FROM DUMMY", (expiration.Sub(time.Now())).Seconds())
if err := db.QueryRow(timeQuery).Scan(&expirationStr); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comments as above.

}

// Renew user's valid until property field
stmt, err := tx.Prepare("ALTER USER " + username + " VALID UNTIL " + "'" + expirationStr + "'")
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't know a lot about HanaDB, would there be a usecase for a custom renew statement?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If you are asking the use case for this particular SQL:

If expiration field is in the role's SQL, the generated credential will be set to expire when the lease expires on HANA server side. This means, in order to renew a credential, the expiration date that HANA keeps for that user must also be pushed back. Hence this ALTER USER sql.

If you are asking for the use case of potential custom renew statements that users may want to input:

I am unaware of any workflows where this would be needed. But if the convention is to make custom renew statements available, I can do that too.

@jefferai jefferai modified the milestones: 0.7.3, 0.7.4 Jun 8, 2017
Copy link
Contributor Author

@Caiyeon Caiyeon left a comment

Choose a reason for hiding this comment

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

Requested changes have been made

}

// HANA does not allow hyphens in usernames, and highly prefers capital letters
username = strings.Replace(username, "-", "_", -1)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This in fact, cannot be done simply by setting the Separator variable.
If the displayname or rolename contains -, the username will be invalid for HANA db.
Therefore, even though Separator = "_", there is still an explicit conversion here.

// Most HANA configurations have password constraints
// Prefix with A1a to satisfy these constraints. User will be forced to change upon login
password = strings.Replace(password, "-", "_", -1)
password = "A1a" + password
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Password is prefixed with A1a to satisfy the most common HANA db password requirement (one upper case, one lower case, one numeric)


// If expiration is in the role SQL, HANA will deactivate the user when time is up,
// regardless of whether vault is alive to revoke lease
expirationStr, err := h.GenerateExpiration(expiration)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Expiration string generation has been changed to the default way


usernameConfig := dbplugin.UsernameConfig{
DisplayName: "test-test",
RoleName: "test-test",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Displayname and rolename have been specifically injected with - in test, to make sure that these values are acceptable.

@Caiyeon
Copy link
Contributor Author

Caiyeon commented Jun 28, 2017

@briankassouf are there any other requested changes? Hoping to see this in 0.7.4 if possible :)

Copy link
Contributor

@calvn calvn left a comment

Choose a reason for hiding this comment

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

Looking good! Left a couple of comments on the tests.


func TestHANA_Initialize(t *testing.T) {
if os.Getenv("HANA_URL") == "" || os.Getenv("VAULT_ACC") != "1" {
return
Copy link
Contributor

Choose a reason for hiding this comment

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

I think t.SkipNow() would be more appropriate rather than assume the test passes. Same with the other test cases.

)

var (
testHANAImagePull sync.Once
Copy link
Contributor

@calvn calvn Jun 30, 2017

Choose a reason for hiding this comment

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

This is unused and probably carried over from dockertest.v2 test code.

testHANAImagePull sync.Once
)

func TestHANA_Initialize(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it possible to test this using a HANA docker image, if one is available? Otherwise the test environment would somehow need to have a HANA DB instance running, with the proper environment variables set as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not aware of any officially supported docker images, although there may be a couple of examples in the wild. One thing to note is that HANA is entirely in-memory, and (I believe) requires 64GB or above ram to function.

Copy link
Member

Choose a reason for hiding this comment

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

Just....to function? At all? 8-O

There is at least one unofficial image, with the express edition

I wonder though if, given the lack of easy testing with anything official, we should actually pull this into an external repo like the oracle plugin. That way we can have a separate release cycle with more rapid releases if there are issues.

What do you two think?

Copy link
Member

Choose a reason for hiding this comment

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

@Caiyeon let us discuss this bit internally, about where it should be hosted.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

At the risk of making this unit test different from other db unit tests, could this be used?
https://github.com/DATA-DOG/go-sqlmock

Copy link
Member

Choose a reason for hiding this comment

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

I'm actually less concerned about the overall logic and more about actually testing it against a real server. It's a possibility, sure.

BTW, can you send me an email? It's my email address at my company dot com.

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, I meant, my name.

* If env vars are not configured, tests will skip rather than succeed

* Fixed some improperly named string variables
@Caiyeon
Copy link
Contributor Author

Caiyeon commented Jul 7, 2017

@calvn @jefferai Any updates on how the testing should be done?

"github.com/hashicorp/vault/plugins"
"github.com/hashicorp/vault/plugins/helper/database/connutil"
"github.com/hashicorp/vault/plugins/helper/database/credsutil"
"github.com/hashicorp/vault/plugins/helper/database/dbutil"
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that here you will need to import _ "github.com/SAP/go-hdb/driver"

Copy link
Contributor

@briankassouf briankassouf left a comment

Choose a reason for hiding this comment

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

Aside from my last comment this is looking good to go! Great work!

@briankassouf briankassouf merged commit f92f4d4 into hashicorp:master Jul 7, 2017
@jefferai jefferai modified the milestones: 0.7.4, 0.8.0 Jul 24, 2017
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.

4 participants