LGTM.

Ming Wen <wenm...@apache.org> 于2022年11月27日周日 14:37写道:
>
> please don’t use qq email
>
> 刘维 <levy...@qq.com.invalid>于2022年11月26日 周六23:23写道:
>
> > Background
> >
> > Nowadays, if a plugin needs to store sensitive information, it will design
> > a separate set of encryption mechanism, which causes the problem of
> > repeated development and cumbersome management. Now we consider introducing
> > Data Encryption function, hereinafter referred to as DE, to encrypt the
> > specified information, and any module/plugin that needs to encrypt
> > sensitive information only needs to specify the fields to be encrypted on
> > display, so that transparent encryption/decryption can be performed when
> > saving/reading the information.
> >
> > Benefits
> >
> > Unify the encryption mechanism of APISIX to avoid duplication of
> > development while improving product competitiveness
> >
> > Goals
> >
> > APISIX provides DE functionality to support the use of this capability in
> > plugins and elsewhere to protect sensitive information.
> >
> > The user scenarios are as follows
> >
> >
> > Turn on the DE function in the configuration file
> >
> > apisix:
> >  &nbsp;  enable_global_data_encryption: true
> >
> > set encrypted fields
> >
> > local schema = {
> >  &nbsp;  type = "object",
> >  &nbsp;  properties = {
> >  &nbsp; &nbsp; &nbsp;  username = { type = "string" },
> >  &nbsp; &nbsp; &nbsp;  password = { type = "string", encrypted = true },
> > &nbsp; <==== Here
> >  &nbsp;  },
> >  &nbsp;  required = {"username", "password"}, &nbsp; &nbsp;
> > }
> >
> > Set the plugin
> >
> > curl http://127.0.0.1:9180/apisix/admin/consumers -H 'X-API-KEY:
> > edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
> > {
> >  &nbsp; &nbsp;"username": "foo",
> >  &nbsp; &nbsp;"plugins": {
> >  &nbsp; &nbsp; &nbsp; &nbsp;"basic-auth": {
> >  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"username": "foo",
> >  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"password": "bar" &nbsp; &nbsp;
> > &nbsp; <= Sensitive Information
> >  &nbsp; &nbsp; &nbsp;  }
> >  &nbsp;  }
> > }'
> >
> > The plugin data read directly from ETCD is encrypted
> >
> > etcdctl get /apisix/consumers/foo
> > {
> >  &nbsp; &nbsp;"username":"foo",
> >  &nbsp; &nbsp;"update_time":1668584767,
> >  &nbsp; &nbsp;"create_time":1668584767,
> >  &nbsp; &nbsp;"plugins":{
> >  &nbsp; &nbsp; &nbsp; &nbsp;"basic-auth":{
> >  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"username":"foo",
> >  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
> > &nbsp;"password":"da61c0083b6d19ef3db2490d0da96a71572da0fa" &nbsp;<=
> > encrypted
> >  &nbsp; &nbsp; &nbsp;  }
> >  &nbsp;  }
> > }
> >
> > The data surface is read normally
> >
> > curl http://127.0.0.1:9180/apisix/admin/consumers -H 'X-API-KEY:
> > edd1c9f034335f136f87ad84b625c8f1'
> > {
> >  &nbsp;"list": [
> >  &nbsp;  {
> >  &nbsp; &nbsp; &nbsp;"createdIndex": 8627,
> >  &nbsp; &nbsp; &nbsp;"modifiedIndex": 8627,
> >  &nbsp; &nbsp; &nbsp;"value": {
> >  &nbsp; &nbsp; &nbsp; &nbsp;"username": "foo",
> >  &nbsp; &nbsp; &nbsp; &nbsp;"update_time": 1668584767,
> >  &nbsp; &nbsp; &nbsp; &nbsp;"create_time": 1668584767,
> >  &nbsp; &nbsp; &nbsp; &nbsp;"plugins": {
> >  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"basic-auth": {
> >  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"username": "foo",
> >  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"password": "bar" &nbsp; &nbsp;
> > &nbsp; &nbsp; <= Unencrypted
> >  &nbsp; &nbsp; &nbsp; &nbsp;  }
> >  &nbsp; &nbsp; &nbsp;  }
> >  &nbsp; &nbsp;  },
> >  &nbsp; &nbsp; &nbsp;"key": "/apisix/consumers/foo"
> >  &nbsp;  }
> >   ],
> >  &nbsp;"total": 1
> > }
> > Detailed Design
> >
> >
> > Add a field in the configuration file to enable data encryption
> >
> > apisix:
> >  &nbsp;  data_encryption: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
> > &nbsp;# use `encrypted = {fields}` in plugin schema to enable encryption
> >  &nbsp; &nbsp; &nbsp;  enable: false &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
> > &nbsp; &nbsp; # If not set, the default value is `false`.
> >  &nbsp; &nbsp; &nbsp;  keyring:
> >  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  - edd1c9f0985e76a2 &nbsp; &nbsp;
> > &nbsp;# If not set, will save origin value into etcd.
> >  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  - qeddd145sfvddff3 &nbsp; &nbsp;
> > &nbsp;# If set this, the keyring should be an array whose elements are
> > string, and the size is also 16, and it will encrypt fields with AES-128-CBC
> >  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
> > &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# !!! So do not
> > change it after encryption, it can't decrypt the fields have be saved if
> > you change !!
> >  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
> > &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# Only use the first
> > key to encrypt, and decrypt in the order of the array.
> >
> > Specify the fields to be encrypted in the schema of the plugin
> >
> > local consumer_schema = {
> >  &nbsp;  type = "object",
> >  &nbsp;  title = "work with consumer object",
> >  &nbsp;  properties = {
> >  &nbsp; &nbsp; &nbsp;  username = { type = "string" },
> >  &nbsp; &nbsp; &nbsp;  password = { type = "string", encrypted = true },
> > &nbsp;  <===== here
> >  &nbsp;  },
> >  &nbsp;  required = {"username", "password"},
> > }
> >
> > local schema = {
> >
> > }
> >
> > If data encryption is enabled, the corresponding fields of the following
> > plugins will be encrypted
> >
> > jwt-auth:secret,private_key
> > basic-auth:password
> > authz-keycloak:client_secret
> > authz-casdoor:client_secret
> > openid-connect:client_secret
> > hmac-auth: secret_key
> > csrf:key
> > rocketmq-logger:secret_key
> > clickhouse-logger:password
> > error-log-logger:clickhouse.password
> > sls-logger:access_key_secret
> > google-cloud-logging:auth_config.private_key
> > elasticsearch-logger:auth.password
> > tencent-cloud-cls:secret_key
> > kafka-proxy:sasl.password
> >
> > Encryption and Decryption
> >
> >
> >
> > Commonly used algorithms AES, ChaCha20, we use AES128CBC encryption
> > algorithm, the logic is the same as ssl encryption and decryption, that is,
> > only use the first key to encrypt, if it fails to take turns to use all
> > keys to decrypt, consider reusing the ssl encryption and decryption
> > function  (https://github.com/apache/apisix/blob/master/apisix/ssl.lua)
> >
> >
> >
> > Currently, only those distributed through the apisix admin interface can
> > be encrypted. Many users have developed their own admin and directly
> > manipulated storage media such as etcd, so they need to implement the
> > encryption function themselves.
> >
> >
> >
> > After the encryption function is enabled, the data that was not encrypted
> > needs to be sent down again through the interface in order to be encrypted,
> > is a refresh interface provided? Consider not providing it for now
> >
> >
> >
> > Combining the above, the original string is used directly after decryption
> > failure
> >
> >
> >
> > Admin now only uses etcd, considering that the first version only supports
> > etcd and only supports encryption of plugin parameters
> >
> >
> >
> > Encryption:
> >
> >
> > Add encryption and decryption to check_single_plugin_schema, note that
> > there are 2 different types of schema within the plugin, namely: schema and
> > consumer_schema
> > local function check_single_plugin_schema(name, plugin_conf, schema_type,
> > skip_disabled_plugin, encrypt_or_decrypt)
> >  &nbsp;  ...
> >  &nbsp; &nbsp;if plugin_obj.check_schema then
> >  &nbsp; &nbsp; &nbsp;  ...
> >  &nbsp; &nbsp;end
> >
> >  &nbsp; &nbsp;if schema_type == core.schema.TYPE_CONSUMER then
> >  &nbsp; &nbsp; &nbsp; &nbsp;ok, err = encrypt(plugin_conf,
> > plugin_obj.consumer_schema, encrypt_or_decrypt)
> >  &nbsp; &nbsp;else
> >  &nbsp; &nbsp; &nbsp; &nbsp;ok, err = encrypt(plugin_conf,
> > plugin_obj.schema, encrypt_or_decrypt)
> >  &nbsp; &nbsp;end
> >
> >  &nbsp; &nbsp;return true
> > end
> > The encryption and decryption function iterates through the corresponding
> > schema, and if it finds the encrypted = true field, it encrypts and
> > decrypts the field in the corresponding configuration
> > local function encrypt(plugin_conf, schema, encrypt_or_decrypt)
> >  &nbsp; &nbsp;if schema.type == "object" then
> >  &nbsp; &nbsp; &nbsp; &nbsp;for name, obj in pairs(schema.properties) do
> >  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;encrypt(plugin_conf[name], obj,
> > encrypt_or_decrypt)
> >  &nbsp; &nbsp; &nbsp; &nbsp;end
> >  &nbsp; &nbsp;elseif schema.type = "array" then
> >  &nbsp; &nbsp; &nbsp;  ...
> >  &nbsp; &nbsp;end
> >  &nbsp; &nbsp;
> >  &nbsp; &nbsp;if schema.encrypted then
> >  &nbsp; &nbsp; &nbsp; &nbsp;aes128(plugin_conf)
> >  &nbsp; &nbsp;end
> > end
> > Add corresponding options in check_schema and stream_check_schema, now
> > directly called in admin, no need to modify, encrypt_or_decrypt is nil by
> > default, i.e. encrypt
> > local function check_schema(plugins_conf, schema_type,
> > skip_disabled_plugin, encrypt_or_decrypt)
> >  &nbsp; &nbsp;for name, plugin_conf in pairs(plugins_conf) do
> >  &nbsp; &nbsp; &nbsp; &nbsp;local ok, err =
> > check_single_plugin_schema(name, plugin_conf,
> >  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;schema_type,
> > skip_disabled_plugin, encrypt_or_decrypt)
> >  &nbsp; &nbsp; &nbsp; &nbsp;if not ok then
> >  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return false, err
> >  &nbsp; &nbsp; &nbsp; &nbsp;end
> >  &nbsp; &nbsp;end
> >
> >  &nbsp; &nbsp;return true
> > end
> > plugin_checker and stream_plugin_checker are used for checksumming when
> > reading data from etcd, and are decrypted by default
> > <br class="Apple-interchange-newline"&gt;<div&gt;</div&gt;
> >
> >
> >
> >
> >
> >
> >
> > function _M.plugin_checker(item, schema_type)
> >  &nbsp;  if item.plugins then
> >  &nbsp; &nbsp; &nbsp;  return check_schema(item.plugins, schema_type,
> > true, decrypt) <=== decrypt &nbsp;  end
> >  &nbsp;  return true
> >
> > end
>
> --
> Thanks,
> Ming Wen, Apache APISIX PMC Chair
> Twitter: _WenMing

Reply via email to