mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-30 18:17:55 +00:00 
			
		
		
		
	Fix MySQL legacy username regression (#3141)
* Fix the mysql legacy username length * Remove boolean parameter * Add a MySQL 5.6 container to test the legacy MySQL plugin against * Add database plugins to the make file * Fix credsutil test
This commit is contained in:
		
							
								
								
									
										21
									
								
								plugins/database/mysql/mysql-legacy-database-plugin/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								plugins/database/mysql/mysql-legacy-database-plugin/main.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"log" | ||||
| 	"os" | ||||
|  | ||||
| 	"github.com/hashicorp/vault/helper/pluginutil" | ||||
| 	"github.com/hashicorp/vault/plugins/database/mysql" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	apiClientMeta := &pluginutil.APIClientMeta{} | ||||
| 	flags := apiClientMeta.FlagSet() | ||||
| 	flags.Parse(os.Args) | ||||
|  | ||||
| 	err := mysql.RunLegacy(apiClientMeta.GetTLSConfig()) | ||||
| 	if err != nil { | ||||
| 		log.Println(err) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| } | ||||
| @@ -36,14 +36,14 @@ type MySQL struct { | ||||
| } | ||||
|  | ||||
| // New implements builtinplugins.BuiltinFactory | ||||
| func New(metadataLen, usernameLen int) func() (interface{}, error) { | ||||
| func New(displayNameLen, roleNameLen, usernameLen int) func() (interface{}, error) { | ||||
| 	return func() (interface{}, error) { | ||||
| 		connProducer := &connutil.SQLConnectionProducer{} | ||||
| 		connProducer.Type = mySQLTypeName | ||||
|  | ||||
| 		credsProducer := &credsutil.SQLCredentialsProducer{ | ||||
| 			DisplayNameLen: metadataLen, | ||||
| 			RoleNameLen:    metadataLen, | ||||
| 			DisplayNameLen: displayNameLen, | ||||
| 			RoleNameLen:    roleNameLen, | ||||
| 			UsernameLen:    usernameLen, | ||||
| 			Separator:      "-", | ||||
| 		} | ||||
| @@ -59,7 +59,21 @@ func New(metadataLen, usernameLen int) func() (interface{}, error) { | ||||
|  | ||||
| // Run instantiates a MySQL object, and runs the RPC server for the plugin | ||||
| func Run(apiTLSConfig *api.TLSConfig) error { | ||||
| 	f := New(MetadataLen, UsernameLen) | ||||
| 	return runCommon(false, apiTLSConfig) | ||||
| } | ||||
|  | ||||
| // Run instantiates a MySQL object, and runs the RPC server for the plugin | ||||
| func RunLegacy(apiTLSConfig *api.TLSConfig) error { | ||||
| 	return runCommon(true, apiTLSConfig) | ||||
| } | ||||
|  | ||||
| func runCommon(legacy bool, apiTLSConfig *api.TLSConfig) error { | ||||
| 	var f func() (interface{}, error) | ||||
| 	if legacy { | ||||
| 		f = New(credsutil.NoneLength, LegacyMetadataLen, LegacyUsernameLen) | ||||
| 	} else { | ||||
| 		f = New(MetadataLen, MetadataLen, UsernameLen) | ||||
| 	} | ||||
| 	dbType, err := f() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
|   | ||||
| @@ -5,19 +5,15 @@ import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/hashicorp/vault/builtin/logical/database/dbplugin" | ||||
| 	"github.com/hashicorp/vault/plugins/helper/database/connutil" | ||||
| 	"github.com/hashicorp/vault/plugins/helper/database/credsutil" | ||||
| 	dockertest "gopkg.in/ory-am/dockertest.v3" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	testMySQLImagePull sync.Once | ||||
| ) | ||||
|  | ||||
| func prepareMySQLTestContainer(t *testing.T) (cleanup func(), retURL string) { | ||||
| 	if os.Getenv("MYSQL_URL") != "" { | ||||
| 		return func() {}, os.Getenv("MYSQL_URL") | ||||
| @@ -58,6 +54,47 @@ func prepareMySQLTestContainer(t *testing.T) (cleanup func(), retURL string) { | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func prepareMySQLLegacyTestContainer(t *testing.T) (cleanup func(), retURL string) { | ||||
| 	if os.Getenv("MYSQL_URL") != "" { | ||||
| 		return func() {}, os.Getenv("MYSQL_URL") | ||||
| 	} | ||||
|  | ||||
| 	pool, err := dockertest.NewPool("") | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Failed to connect to docker: %s", err) | ||||
| 	} | ||||
|  | ||||
| 	// Mysql 5.6 is the last MySQL version to limit usernames to 16 characters. | ||||
| 	resource, err := pool.Run("mysql", "5.6", []string{"MYSQL_ROOT_PASSWORD=secret"}) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Could not start local MySQL docker container: %s", err) | ||||
| 	} | ||||
|  | ||||
| 	cleanup = func() { | ||||
| 		err := pool.Purge(resource) | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("Failed to cleanup local container: %s", err) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	retURL = fmt.Sprintf("root:secret@(localhost:%s)/mysql?parseTime=true", resource.GetPort("3306/tcp")) | ||||
|  | ||||
| 	// exponential backoff-retry | ||||
| 	if err = pool.Retry(func() error { | ||||
| 		var err error | ||||
| 		var db *sql.DB | ||||
| 		db, err = sql.Open("mysql", retURL) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return db.Ping() | ||||
| 	}); err != nil { | ||||
| 		t.Fatalf("Could not connect to MySQL docker container: %s", err) | ||||
| 	} | ||||
|  | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func TestMySQL_Initialize(t *testing.T) { | ||||
| 	cleanup, connURL := prepareMySQLTestContainer(t) | ||||
| 	defer cleanup() | ||||
| @@ -66,7 +103,7 @@ func TestMySQL_Initialize(t *testing.T) { | ||||
| 		"connection_url": connURL, | ||||
| 	} | ||||
|  | ||||
| 	f := New(MetadataLen, UsernameLen) | ||||
| 	f := New(MetadataLen, MetadataLen, UsernameLen) | ||||
| 	dbRaw, _ := f() | ||||
| 	db := dbRaw.(*MySQL) | ||||
| 	connProducer := db.ConnectionProducer.(*connutil.SQLConnectionProducer) | ||||
| @@ -105,7 +142,7 @@ func TestMySQL_CreateUser(t *testing.T) { | ||||
| 		"connection_url": connURL, | ||||
| 	} | ||||
|  | ||||
| 	f := New(MetadataLen, UsernameLen) | ||||
| 	f := New(MetadataLen, MetadataLen, UsernameLen) | ||||
| 	dbRaw, _ := f() | ||||
| 	db := dbRaw.(*MySQL) | ||||
|  | ||||
| @@ -115,8 +152,8 @@ func TestMySQL_CreateUser(t *testing.T) { | ||||
| 	} | ||||
|  | ||||
| 	usernameConfig := dbplugin.UsernameConfig{ | ||||
| 		DisplayName: "test", | ||||
| 		RoleName:    "test", | ||||
| 		DisplayName: "test-long-displayname", | ||||
| 		RoleName:    "test-long-rolename", | ||||
| 	} | ||||
|  | ||||
| 	// Test with no configured Creation Statememt | ||||
| @@ -137,6 +174,68 @@ func TestMySQL_CreateUser(t *testing.T) { | ||||
| 	if err := testCredsExist(t, connURL, username, password); err != nil { | ||||
| 		t.Fatalf("Could not connect with new credentials: %s", err) | ||||
| 	} | ||||
|  | ||||
| 	// Test a second time to make sure usernames don't collide | ||||
| 	username, password, err = db.CreateUser(statements, usernameConfig, time.Now().Add(time.Minute)) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("err: %s", err) | ||||
| 	} | ||||
|  | ||||
| 	if err := testCredsExist(t, connURL, username, password); err != nil { | ||||
| 		t.Fatalf("Could not connect with new credentials: %s", err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestMySQL_CreateUser_Legacy(t *testing.T) { | ||||
| 	cleanup, connURL := prepareMySQLLegacyTestContainer(t) | ||||
| 	defer cleanup() | ||||
|  | ||||
| 	connectionDetails := map[string]interface{}{ | ||||
| 		"connection_url": connURL, | ||||
| 	} | ||||
|  | ||||
| 	f := New(credsutil.NoneLength, LegacyMetadataLen, LegacyUsernameLen) | ||||
| 	dbRaw, _ := f() | ||||
| 	db := dbRaw.(*MySQL) | ||||
|  | ||||
| 	err := db.Initialize(connectionDetails, true) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("err: %s", err) | ||||
| 	} | ||||
|  | ||||
| 	usernameConfig := dbplugin.UsernameConfig{ | ||||
| 		DisplayName: "test-long-displayname", | ||||
| 		RoleName:    "test-long-rolename", | ||||
| 	} | ||||
|  | ||||
| 	// Test with no configured Creation Statememt | ||||
| 	_, _, err = db.CreateUser(dbplugin.Statements{}, usernameConfig, time.Now().Add(time.Minute)) | ||||
| 	if err == nil { | ||||
| 		t.Fatal("Expected error when no creation statement is provided") | ||||
| 	} | ||||
|  | ||||
| 	statements := dbplugin.Statements{ | ||||
| 		CreationStatements: testMySQLRoleWildCard, | ||||
| 	} | ||||
|  | ||||
| 	username, password, err := db.CreateUser(statements, usernameConfig, time.Now().Add(time.Minute)) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("err: %s", err) | ||||
| 	} | ||||
|  | ||||
| 	if err := testCredsExist(t, connURL, username, password); err != nil { | ||||
| 		t.Fatalf("Could not connect with new credentials: %s", err) | ||||
| 	} | ||||
|  | ||||
| 	// Test a second time to make sure usernames don't collide | ||||
| 	username, password, err = db.CreateUser(statements, usernameConfig, time.Now().Add(time.Minute)) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("err: %s", err) | ||||
| 	} | ||||
|  | ||||
| 	if err := testCredsExist(t, connURL, username, password); err != nil { | ||||
| 		t.Fatalf("Could not connect with new credentials: %s", err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestMySQL_RevokeUser(t *testing.T) { | ||||
| @@ -147,7 +246,7 @@ func TestMySQL_RevokeUser(t *testing.T) { | ||||
| 		"connection_url": connURL, | ||||
| 	} | ||||
|  | ||||
| 	f := New(MetadataLen, UsernameLen) | ||||
| 	f := New(MetadataLen, MetadataLen, UsernameLen) | ||||
| 	dbRaw, _ := f() | ||||
| 	db := dbRaw.(*MySQL) | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Brian Kassouf
					Brian Kassouf