Authorization
A common question is how to enforce fine-grained access control in Vitess. This question comes up because Vitess uses connection pooling with fixed MySQL users at the VTTablet level, and implements its own authentication at the VTGate level. As a result, you cannot use the normal MySQL GRANTs system to give certain application-level MySQL users more or less permissions than others.
The MySQL GRANT system is very extensive, and we have not reimplemented all of this functionality in Vitess. What we have done is to enable you to provide authorization via table-level ACLs, with a few basic characteristics:
- Individual users can be assigned 3 levels of permissions:
- Read (corresponding to read DML, e.g.
SELECT
) - Write (corresponding to write DML, e.g.
INSERT
,UPDATE
,DELETE
) - Admin (corresponding to DDL, e.g.
ALTER TABLE
)
- Read (corresponding to read DML, e.g.
- Permissions are applied on a specified set of tables, which can be enumerated or specified by regex.
VTTablet parameters for table ACLs
Note that the Vitess authorization via ACLs are applied at the VTTablet level, as opposed to on VTGate, where authentication is enforced. There are a number of VTTablet command line parameters that control the behavior of ACLs. Let’s review these:
--enforce-tableacl-config
: Set this totrue
to ensure VTTablet will not start unless there is a valid ACL configuration. This is used to catch misconfigurations resulting in blanket access to authenticated users.--queryserver-config-enable-table-acl-dry-run
: Set totrue
to check the table ACL at runtime, and only emit the TableACLPseudoDenied metric if a request would have been blocked. The request is then allowed to pass, even if the ACL determined it should be blocked. This can be used for testing new or updated ACL policies. Default isfalse
.--queryserver-config-strict-table-acl
: Set totrue
to enforce table ACL checking. This needs to be enabled for your ACLs to have any effect. Any users that are not specified in an ACL policy will be denied. Default isfalse
.--queryserver-config-acl-exempt-acl
: Allows you to specify the name of an ACL (see below for format) that is exempt from enforcement. Allows you to separate the rollout and the subsequent enforcement of a specific ACL.--table-acl-config
: Path to a file defining the table ACL config.--table-acl-config-reload-interval
: How often thetable-acl-config
should be reloaded. Set this to allow you to update the ACL file on disk, and then have VTTablet automatically reload the file within this period. Default is not to reload the ACL file after VTTablet startup. Note that even if you do not set this parameter, you can always force VTTablet to reload the ACL config file from disk by sending a SIGHUP signal to your VTTablet process.
Warning regarding ACL reloading
If you choose to reload the ACL config manually or on an interval, and you are using the -enforce-tableacl-config
option, your VTTablet processes will exit if your table ACL config file contains an invalid configuration at reload time. While this might be unexpected, this ensures the highest level of security. Accordingly, it is very important to test your ACL config thoroughly before applying, pay attention to access permissions on the ACL config file, etc.
ACLs and schema tracking
If you are using the VTGate schema tracking feature, you will want to review https://vitess.io/docs/reference/features/schema-tracking/#vtgate . Specifically, you will need to specify a user with the appropriate access for your ACL config in the VTGate -schema_change_signal_user
parameter.
Format of the table ACL config file
The file specified in the --table-acl-config
parameter above is a JSON file with the following example to explain the format:
{
"table_groups": [
{
"name": "aclname",
"table_names_or_prefixes": [
"%"
],
"readers": [
"vtgate-user1"
],
"writers": [
"vtgate-user2"
],
"admins": [
"vtgate-user3"
]
},
{ "... more ACLs here if necessary ..." }
]
}
Notes:
name
: This is the name of the ACL (aclname
in the example above) and is what needs to be specified in--queryserver-config-acl-exempt-acl
, if you need to exempt a specific ACL from enforcement.table_names_or_prefixes
: A list of strings and/or regexes that allow a rule to target a specific table or set of tables. Use%
as in the example to specify all tables. Note that only the SQL%
“regex” wildcard is supported here at the moment.readers
: A list of VTGate users, specified by their UserData field in the authentication specification, that are allowed to read the tables targeted by this ACL rule. Typically allowsSELECT
.writers
: A list of VTGate users that are allowed to write to the tables targeted by this ACL rule. Typically allowsINSERT
,UPDATE
andDELETE
.admins
: A list of VTGate users that are allowed admin privileges on the tables targeted by this ACL rule. Typically allows DDL privileges, e.g.ALTER TABLE
. Note that this also includes some commands that might be thought of as DML, which are really DDL, likeTRUNCATE
)- Note that
writers
privilege does not implyreaders
privilege, andadmins
privilege does not implyreaders
orwriters
. You need to therefore add your users to each list explicitly if you want them to have that level of access. - You cannot use multiple ACL rules to target the same (sub)set of tables. Therefore the tablenames specified by
table_names_or_prefixes
(or expanded by regexes) need to be non-overlapping between ACL rules. Additionally, you cannot have duplicate tablenames or overlapping regexes in thetable_names_or_prefixes
list in a single ACL rule.
Example
Let’s assume your Vitess cluster already has two keyspaces setup:
keyspace1
with a single tablet
that should only be accessed bymyuser1
keyspace2
with a single tablet
that should only be accessed bymyuser2
For the VTTablet configuration for keyspace1
:
$ cat > acls_for_keyspace1.json << EOF
{
"table_groups": [
{
"name": "keyspace1acls",
"table_names_or_prefixes": ["%"],
"readers": ["myuser1", "vitess"],
"writers": ["myuser1", "vitess"],
"admins": ["myuser1", "vitess"]
}
]
}
EOF
$ vttablet --init_keyspace "keyspace1" --table-acl-config=acls_for_keyspace1.json --enforce-tableacl-config --queryserver-config-strict-table-acl ........
Note that the %
specifier for table_names_or_prefixes
translates to “all tables”.
Do the same thing for keyspace2
:
$ cat > acls_for_keyspace2.json << EOF
{
"table_groups": [
{
"name": "keyspace2acls",
"table_names_or_prefixes": ["%"],
"readers": ["myuser2", "vitess"],
"writers": ["myuser2", "vitess"],
"admins": ["myuser2", "vitess"]
}
]
}
EOF
$ vttablet --init_keyspace "keyspace2" --table-acl-config=acls_for_keyspace2.json --enforce-tableacl-config --queryserver-config-strict-table-acl ........
With this setup, the myuser1
and myuser2
users can only access their respective keyspaces, but the vitess
user can access both.
# Attempt to access keyspace1 with myuser2 credentials through vtgate
$ mysql -h 127.0.0.1 -u myuser2 -ppassword2 -D keyspace1 -e "select * from t"
ERROR 1045 (HY000) at line 1: vtgate: http://vtgate-zone1-7fbfd8cc47-tchbz:15001/: target: keyspace1.-80.primary, used tablet: zone1-476565201 (zone1-keyspace1-x-80-replica-1.vttablet): vttablet: rpc error: code = PermissionDenied desc = table acl error: "myuser2" [] cannot run PASS_SELECT on table "t" (CallerID: myuser2)
target: keyspace1.80-.primary, used tablet: zone1-1289569200 (zone1-keyspace1-80-x-replica-0.vttablet): vttablet: rpc error: code = PermissionDenied desc = table acl error: "myuser2" [] cannot run PASS_SELECT on table "t" (CallerID: myuser2)
$
Whereas myuser1 is able to access its keyspace without error:
$ mysql -h 127.0.0.1 -u myuser1 -ppassword1 -D keyspace1 -e "select * from t"
$
Negative ACLs
If you want to set up an authorization structure like the following:
- Assume a database with the tables
t1
,t2
andt3
, and the database (vtgate
) usersregular
andprivileged
. - Give read and write access to only tables
t1
andt2
to userregular
. - Only give user
privileged
access to read or write tablet3
.
You will need to construct an ACL config with two ACLs, and enumerate all the necessary table names in each ACL (t1
and t2
in the first ACL; t3
in the second ACL). This type of configuration could be called “completely specified”.
However, every time a non-privileged table is added to the schema, the ACL config needs to be updated to add the table name to the config, or user regular
will not have access to it. For schemas with large numbers of tables, and that change frequently, this can be a burden.
In general, it is not possible to express a “negative” target ACL in Vitess’ ACL config syntax, e.g.: Give this user access to all tables except these specific ones
. It is possible to express an ACL config that is equivalent to the above “completely specified” ACL config, but somewhat easier to manage, even for large numbers of tables.
Consider the following example:
- Your schema has a 100+ tables.
- You regularly add new tables.
- You have a special set of tables called
secret
andsupersecret
that you only want a specificvtgate
user calledsuper
to have access to. - You have 3 other users:
readonly
for read access to all tables, exceptsecret
andsupersecret
readwrite
for read and write access to all tables, exceptsecret
andsupersecret
dba
for read, write and admin access to all tables, exceptsecret
andsupersecret
.
- You only a few other tables that start with the letter
s
, calleds1
,s2
,s3
. - We assume you do not use table names with upper case or other characters.
The idea of this configuration is that you construct access to the non-sensitive data using wildcards of table names for each letter of the alphabet. You then only need to specify table names fully for the letter of the alphabet that our “special” tables start with. In other words this still requires us to specify a list of table names, but only for the letters of the alphabet that the “special” tables start with.
Here is the ACL config that satisfies the requirements:
{
"table_groups": [
{
"name": "acl1",
"table_names_or_prefixes": ["a%", "b%", "c%", "d%", "e%", "f%", "g%", "h%", "i%", "j%", "k%", "l%", "m%", "n%", "o%", "p%", "q%", "r%", "t%", "u%", "v%", "w%", "x%", "y%", "z%", "s1", "s2", "s3"],
"readers": ["readonly", "readwrite", "dba"],
"writers": ["readwrite", "dba"],
"admins": ["dba"]
},
{
"name": "acl2",
"table_names_or_prefixes": ["secret", "supersecret"],
"readers": ["super"],
"writers": ["super"],
"admins": ["super"]
}
]
}
Now, with the above ACL config, you only need to update the ACL config if you add a new table that starts with the letter s
.