WL#13768: START GROUP_REPLICATION to support credentials as parameters

Affects: Server-8.0   —   Status: Complete

EXECUTIVE SUMMARY
=================
This worklog extends START GROUP_REPLICATION command to accept USER,
PASSWORD, DEFAULT_AUTH and PLUGIN_DIR as optional parameters for
recovery channel.

SCOPE
=====
This work shall make it possible to use START group replication with
credentials stored only in memory.
This work shall not handle encrypting credentials in any way.

LIMITS
======
Starting group replication using
`START GROUP_REPLICATION USER=..., PASSWORD=...`
will not make it possible to start GROUP_REPLICATION automatically on
boot, since credentials shall not outlive the server
(they are not persisted, exist only in memory).

RATIONALE
=========
START GROUP_REPLICATION should accept credentials used for recovery
as parameters.

MySQL Replication configuration is performed using a set of
configuration variables and a few commands such as CHANGE MASTER TO.
In particular the CHANGE MASTER TO contains two important
configuration options:
 (a) MASTER_USER - the user that the replication connection thread
                   will use when reaching out the to source; and
 (b) MASTER_PASSWORD - the password that the user will use to
                       authenticate against the source.

For more details please refer user manual:
https://dev.mysql.com/doc/refman/8.0/en/change-master-to.html

To make it simple and easy to use, and not require the user to enter
the credentials everytime she/he wants to connect to the master, the
credentials need to be persisted.
Thence, the DBA is not required to enter the same user and password
everytime he restarts the replication threads.
Currently, these credentials are stored along side the other options
of CHANGE MASTER TO, in plaintext, which is a security issue.

To overcome that issue, START GROUP_REPLICATION command will be
extended with the following optional parameters:
 USER: User name. Cannot be set to an empty or null string,
       or left unset if PASSWORD is used.
 PASSWORD: Password.
 DEFAULT_AUTH: Name of the authentication plugin; default is MySQL
               native authentication.
 PLUGIN_DIR: Location of the authentication plugin.

The same connection_options that START SLAVE commands supports
https://dev.mysql.com/doc/refman/8.0/en/start-slave.html

These parameters have precedence over the persisted credentials.

It also means that if these parameters are provided, there is no
need to persist recovery channel credentials before hand.

These parameters will affect both implementations of distributed
recovery: asynchronous replication and clone.

These parameters will be stored in memory, meaning that after a
server restart the DBA needs to set them again though START
GROUP_REPLICATION command.
This means that automatic Group Replication start[1] is not
supported with these credentials input method.

[1] https://dev.mysql.com/doc/refman/8.0/en/group-replication-
options.html#sysvar_group_replication_start_on_boot

USER STORIES
============
- As a MySQL DBA I want to pass credentials required for recovery as
  parameters for START GROUP_REPLICATION rather then storing them in
  plain text in CHANGE MASTER.
FR1: START GROUP_REPLICATION command must accept USER, PASSWORD,
DEFAULT_AUTH and PLUGIN_DIR as parameters.

FR2: When passed as parameters to START GROUP_REPLICATION command,
credentials shall not be stored in any file or table.

FR3: USER parameter must always be present if PASSWORD is provided.

FR4: Empty USER must not be accepted.

FR5: START GROUP_REPLICATION without parameters must continue to work
if customer has provided credentials via CHANGE MASTER command for
group_replication_recovery channel.

FR6: The password parameter must be masked on query log, query rewrite
or any other query log component.

FR7: STOP GROUP_REPLICATION command must remove stored credentials from
memory only.

FR8: Credentials passed via START GROUP_REPLICATION options must take
precedence over CHANGE MASTER credentials.

FR9: Credentials passed via START GROUP_REPLICATION options must be
used by both asynchronous replication and clone.
User Visible Changes
====================
User will be able to supply username and password for recovery in
START GROUP_REPLICATION command.
There will be no need to execute CHANGE MASTER command.

START GROUP_REPLICATION will have options:
USER: User name. Cannot be set to an empty or null string,
or left unset if PASSWORD is used.
PASSWORD: Password.
DEFAULT_AUTH: Name of plugin; default is MySQL native authentication.
PLUGIN_DIR: Location of plugin.

e.g.
START GROUP_REPLICATION 
USER='rpl_user',PASSWORD='rpl_password',DEFAULT_AUTH='auth_test_plugin' and 
PLUGIN_DIR='PLUGIN_AUTH_DIR';
START GROUP_REPLICATION USER='rpl_user',PASSWORD='rpl_password';
START GROUP_REPLICATION;

Upgrades
========
Not applicable. Since START GROUP_REPLICATION command will still work.

Security
========
The password parameter will be masked on query log, query rewrite
or any other query log component.

Observability
=============
Following client errors shall be added to make sure USER is not empty or if
credentials are added USER option must be present:
ER_GROUP_REPLICATION_START_ERROR_USER_EMPTY_MSG
  eng "User name cannot be empty"

ER_GROUP_REPLICATION_START_ERROR_USER_MANDATORY_MSG
  eng "USER option must be supplied."

Cross-version Replication
=========================
Not applicable.

Protocol
========
No changes.

User Interface
==============
START GROUP_REPLICATION will have option USER, PASSWORD,
DEFAULT_AUTH and PLUGIN_DIR.

Deployment and Installation
===========================
Recovery credentials may be passed via START GROUP_REPLICATION command.
START GROUP_REPLICATION without parameter will continue to work,
if the credentials are previously set through CHANGE MASTER.
If credentials are passed as START GROUP_REPLICATION parameters
(credentials are not set in CHANGE MASTER), automatic group start on server
boot will not work.
plugin/group_replication/include/replication_threads_api.h (may be)
plugin/group_replication/src/plugin.cc (may be)
plugin/group_replication/src/replication_threads_api.cc (may be)
plugin/replication_observers_example/replication_observers_example.cc
share/messages_to_clients.txt
sql/rpl_channel_service_interface.cc
sql/rpl_channel_service_interface.h
sql/rpl_group_replication.cc
sql/rpl_group_replication.h
sql/sql_lex.h
sql/sql_parse.cc
sql/sql_yacc.yy

sql_yacc and sql_lex and lex files will be modified for parser.
Since group_replication creates the channel, mi interface may not be used.
Instead we shall create new variable in LEX or use
LEX_SLAVE_CONNECTION slave_connection;
sql_parse will pass captured details to START GROUP_REPLICATION.

sql/rpl_channel_service_interface.cc
sql/rpl_channel_service_interface.h
```
class Slave_credentials {
  struct Channel_cred_param {
    /* opt params may be removed */
    bool username_opt;
    std::string username;
    bool password_opt;
    std::string password;
    bool auth_opt;
    std::string plugin_auth;
    bool dir_opt;
    std::string plugin_dir;
  };
  typedef std::pair channel_credential_set;
  std::map m_credential_set;
  // MAY BE, RIGHT NOW NOT USED
  std::map m_buffer;

 private:
  static Slave_credentials *m_inst;
..

 private:
  int insert_credential_internal(const char *channel_name,
                                 Channel_cred_param ¶m,
                                 bool fail_if_exist = true,
                                 bool in_buffer = true);

  int delete_credential_internal(const char *channel_name, bool store,
                                 bool buffer);

  int get_credential_internal(const char *channel_name,
                              Channel_cred_param **element);

..
 public:
  static int get_credential(const char *channel_name, std::string &user,
                            std::string &pass, std::string &auth,
                            std::string &dir);

  static int delete_credential(const char *channel_name, bool store = true,
                               bool buffer = true);

  static int store_credentials(const char *channel_name, char *username,
                               char *password, char *plugin_auth,
                               char *plugin_dir, bool fail_if_exist = true,
                               bool in_buffer = true);

  // MAY BE, RIGHT NOW NOT USED
  static int move(const char *channel_name);

  static void reset();
};
```
files already has provision for START SLAVE options.
If parameters were supplied by user, struct LEX_SLAVE_CONNECTION will be
filled.

sql/rpl_group_replication.cc
sql/rpl_group_replication.h
will save credentials in Slave_credentials and remove on stop.
Duplicate inserts will not be allowed and GR runing will be checked to make
sure parallel starts do not overwrite credentials.
channel_get_credentials shall first check if channel credentials are present
in Slave_credentials and fetch same from Slave_credentials, if present.

plugin/group_replication/src/plugin.cc
On failure and stop will request Slave_credentials to delete credentials.
Parallel starts with without parameters will be checked to make sure
credentials do not cross.
Stop GR will remove credentials from from store and buffer(if any).
All above code may move to rpl_group_replication.cc or plugin.cc