MySQL 5.7 Encryption

Part I 安装与使用

InnoDB支持对单文件的tablespace进行加密。

它使用两层结构,包含master key和tablespace key(in tablespace header)。master key用于加密tablespace key,tablespace key用于加密数据。master key可以按需更改,称为master key rotation。 MySQL 默认带有一个管理master key的插件keyring_file(指MySQL 5.7.11及以后版本),keyring_file把master key存储在服务器本地文件里。

这并不是一个规范的做法,一些安全标准如PCI, FIPS等,要求专用的key管理系统或者硬件安全模块(HSMs)。MySQL提供了keyring_okv插件, 通过KMIP client (KMIP 1.1) 访问Oracle Key Vault (OKV),提供key管理功能,这在企业版里称为“MySQL Enterprise Transparent Data Encryption (TDE)”

InnoDB tablespace encryption支持基于块的AES加密,使用两种模式:Electronic Codebook (ECB,用于tablespace key加密);Cipher Block Chaining (CBC,用于数据加密)

版本

  * MySQL 5.7.11之后,所有版本包含keyring_file插件
  * MySQL 5.7.12之后,企业版包含keyring_okv插件
  * MySQL 5.7.13之后,所有版本包含key管理的SQL接口(UDF,用户定义函数)

keyring_okv简介

keyring_okv使用Oracle Key Vault(OKV)作为后端,通过OKV管理key而不是通过keyring_okv插件。使用KMIP协议进行通讯,意味着也可以使用兼容KMIP协议的产品。

keyring_okv对外提供的功能和接口与keyring_file一致,下文会有介绍。

keyring_file插件安装

通常有如下种方法都可以加载插件

  --plugin-load/ --plugin-load-add/INSTALL PLUGIN语句

但是这几种方法都会在InnoDB初始化后加载插件,如果有加密的数据,将无法访问;所以最好使用--early-plugin-load加载keyring插件,在InnoDB初始化前加载

  --early-plugin-load=keyring_file.so
  需要注意的是,在5.7.11中,此参数默认值为keyring_file;但是在5.7.12及以后,没有此默认值。所以升级时需要显式指定。
  • 查看:通过show plugins或者information_schema.plugins查看
  mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE 'keyring%';
+--------------+---------------+
| PLUGIN_NAME  | PLUGIN_STATUS |
+--------------+---------------+
| keyring_file | ACTIVE        |
+--------------+---------------+
  • 配置keyring_file插件:如前所述,需要配置如下参数
  [mysqld]
early-plugin-load=keyring_file.so  
keyring_file_data=/usr/local/mysql/mysql-keyring/keyring  

使用

  • 预先检查

    • 创建加密表之后,keyring_plugin要一直启用,否则会报错
    • show plugin:查看插件是否启用
    • innodb_table_per_file:必需参数
    • 确保master key不会丢失:创建加密表后,master key rotation 之后要备份keyring_file(或者相应的OKV配置)
  • 启用和禁用:设置表选项

    • CREATE TABLE t1 (c1 INT) ENCRYPTION='Y';
    • ALTER TABLE t1 ENCRYPTION='Y';
    • ALTER TABLE t1 ENCRYPTION='N';
    • 对已经存在的表指定加密,会使用ALGORITHM=COPY;不支持ALGORITM=INPLACE
  • rotating master key(更新master key)

    • ALTER INSTANCE ROTATE INNODB MASTER KEY;
    • 原子操作,只涉及master key
    • super 权限
    • 可以和DML并发,但是和启用禁用语句互斥
  • 加密和恢复

    • 如果在做rotation的时候故障,实例重启时会继续这个过程
    • keyring插件必须在Innodb引擎初始化之前加载,才能访问tablespace header 数据页
    • 恢复的时候Innodb会判断tablespace key是否用老的master key加密,是的话会更新,使用新的master key
  • 导出加密表

  • 复制

    • ALTER INSTANCE ROTATE INNODB MASTER KEY 语句,必须主备都支持加密,并且加载keyring plugin
    • 成功执行会记录binlog,不成功则不会
    • 如果备不支持keyring plugin,那么复制不会成功

注意

  • server重启的时候最好用相同的加密配置
  • master key第一次创建是在生成加密表的时候
  • 为表禁用、启用加密才会更改tablespace key(algorithm=copy操作)
  • 如果有压缩和加密两个选项,先做压缩
  • keyring_file plugin
    • 如果keyring file不存在,执行rotation语句时会生成文件
    • 卸载插件不会删除这个文件
    • 建议keyring_file_data和表文件放在不同目录
    • 运行时或重启时更改keyring_file_data,可能导致表无法访问
  • 更新keyring文件

    更新时保留一个.backup文件,以备在更新失败时回滚; 5.7.17及以后会在写入时校验SHA-256 checksum

  • 支持的key类型和长度 由插件决定,需要注意的是UDF接口有2048Bytes的限制,详情

一些限制

  • 只支持AES算法:ECB、CBC两种模式
  • 只支持ALGORITHM=COPY
  • 只支持file_per_table表空间,其他表空间类型不支持(general tablespace/system tablespace/undo tablespace/temporary tablespace)
  • 不能把加密表从支持加密的表空间移动到不支持的表空间
  • 不加密redo log/undo log/binlog,只加密tablespace数据
  • 不支持从keyring_file直接转换到keyring_okv,需要解密、卸载keyring_file,安装配置keyring_okv,重新加密

FAQ

https://dev.mysql.com/doc/refman/5.7/en/faqs-tablespace-encryption.html

Part II 接口与扩展

keyring 对外提供两个层次的接口:

  • SQL:UDF,可以生成key、查询key、保存key、删除key
  • C interface:keyring service

keyring UDF

  • SQL函数的形式,提供key管理接口
  • 需要安装keyring_udf插件
  • 调用关系:(keyring) UDF-> keyring service -> keyring plugin
  • 必须具有global(. ?) EXECUTE 权限;或者使用wrapper存储过程方法
  • 存储的key只有同一个用户才能访问(InnoDB本身的使用不同);或者使用wrapper 存储过程方法
  • 支持的函数:
INSTALL PLUGIN keyring_udf SONAME 'keyring_udf.so';  
CREATE FUNCTION keyring_key_generate RETURNS INTEGER SONAME 'keyring_udf.so';  
CREATE FUNCTION keyring_key_fetch RETURNS STRING SONAME 'keyring_udf.so';  
CREATE FUNCTION keyring_key_length_fetch RETURNS INTEGER SONAME 'keyring_udf.so';  
CREATE FUNCTION keyring_key_type_fetch RETURNS STRING SONAME 'keyring_udf.so';  
CREATE FUNCTION keyring_key_store RETURNS INTEGER SONAME 'keyring_udf.so';  
CREATE FUNCTION keyring_key_remove RETURNS INTEGER SONAME 'keyring_udf.so';  
  keyring_key_generate      # 生成key
  keyring_key_fetch         # 获取key
  keyring_key_length_fetch  # 获取key长度
  keyring_key_type_fetch    # 获取key类型
  keyring_key_store         # 存储key
  keyring_key_remove        # 移除key

keyring service

  • 函数调用形式,为server内部组件和插件提供key管理功能
  • 在keyring service里,一条record包含一个唯一标识符 + 数据(key)
  • 唯一标识符包括两部分:key_id(或name) + user_id(可能是当前session用户,也可以是其他,取决于应用)
  • 上述UDF中,传递CURRENT_USER()作为user_id(所以只有创建用户才能访问)

  • keyring service 函数

my_bool my_key_generate(const char *key_id, const char *key_type, const char *user_id, size_t key_len)

my_bool my_key_fetch(const char *key_id, const char **key_type, const char* user_id, void **key, size_t *key_len)

my_bool my_key_store(const char *key_id, const char *key_type, const char* user_id, void *key, size_t key_len)

my_bool my_key_remove(const char *key_id, const char* user_id)  
keyring service 的函数和UDF函数基本同义,不再详述;具体参数参考详细说明  

Part III 实现一个keyring plugin

keyring_file把加密数据存储在服务器本地的文件中,可以参考其代码实现一个keyring plugin

  • 基于目录plugin/keyring
// 包含此头文件
#include <mysql/plugin_keyring.h>

// 声明插件,在keyring.cc中
mysql_declare_plugin(keyring_file)  
{
  MYSQL_KEYRING_PLUGIN,     /* type                                     */
  &keyring_descriptor,      /* descriptor                               */
  "keyring_file",           /* name                                     */
  "Oracle Corporation",     /* author                                   */
  "store/fetch authentication data to/from a flat file", /* description */
  PLUGIN_LICENSE_GPL,
  keyring_init,             /* init function (when loaded)              */
  keyring_deinit,           /* deinit function (when unloaded)          */
  0x0100,                   /* version                                  */
  NULL,                     /* status variables                         */
  keyring_system_variables, /* system variables                         */
  NULL,
  0,
}
mysql_declare_plugin_end;

static struct st_mysql_sys_var *keyring_system_variables[]= {  
  MYSQL_SYSVAR(data),      /* keyring_file的系统变量keyring_file_data    */
  NULL
};

// 上述keyring_descriptor的值和具体类型有关,对于keyring plugin,其指向如下结构
struct st_mysql_keyring  
{
  int interface_version;
  my_bool (*mysql_key_store)(const char *key_id, const char *key_type, const char* user_id, const void *key, size_t key_len);
  my_bool (*mysql_key_fetch)(const char *key_id, char **key_type, const char *user_id, void **key, size_t *key_len);
  my_bool (*mysql_key_remove)(const char *key_id, const char *user_id);
  my_bool (*mysql_key_generate)(const char *key_id, const char *key_type, const char *user_id, size_t key_len);
};

// 对keyring_file而言,其描述符结构如下:
static struct st_mysql_keyring keyring_descriptor=  
{
  MYSQL_KEYRING_INTERFACE_VERSION,
  mysql_key_store,
  mysql_key_fetch,
  mysql_key_remove,
  mysql_key_generate
};
  类似其他插件,不再详述

flacro

Read more posts by this author.