【Ovirt 笔记】engine 用户扩展管理分析与整理
2018-06-13 本文已影响18人
58bc06151329
文前说明
作为码农中的一员,需要不断的学习,我工作之余将一些分析总结和学习笔记写成博客与大家一起交流,也希望采用这种方式记录自己的学习之旅。
本文仅供学习交流使用,侵权必删。
不用于商业目的,转载请注明出处。
分析整理的版本为 Ovirt 4.2.3 版本。
1. 用户与角色
- Red Hat 虚拟化包含两种用户域(本地用户域和外部用户域)
- 本地用户域
- 在 engine(engine-setup) 安装的过程中会创建一个称之为内部域的本地用户域和一个 admin 的默认用户。
- 可以使用 ovirt-aaa-jdbc-tool 工具在内部域上创建附加用户。
- 在本地域上创建的用户被称之为本地用户。
- 外部用户域
- 可以将外部目录服务器(如 Red Hat Directory Server、ActiveDirectory、OpenLDAP 和许多其他支持的选项)附加到 Red Hat 虚拟化环境中,并将它们用作外部域。
- 在外部域上创建的用户帐户被称为目录用户。
- 本地用户域
- 本地用户和目录用户都需要 engine 分配适当的角色和权限,才能在环境中运行。
- 通过使用 ovirt-engine-extension-aaa-jdbc 可以创建除了默认内部域之外的本地用户域。
- 通过使用 ovirt-engine-extension-aaa-ldap 可以定制外部目录设置,扩展支持许多不同的 LDAP 服务器类型。
- 提供了一个交互式设置脚本,帮助设置大多数 LDAP 类型。
2. 本地用户域
-
aaa-jdbc 扩展来自于 ovirt 3.6 版本,提供了默认的 internal 内部配置文件。
- 升级 ovirt 3.5 时,将该版本之前传统供应商修改为 aaa-jdbc 供应商。
- 相关配置可以通过 aaa-jdbc 提供的配置文件完成。
- 配置在执行 engine-setup 的时候完成。
2.1 创建 aaa-jdbc 配置文件
- engien-setup 配置相关的执行
@util.export
def createPlugins(context):
aaa.Plugin(context=context)
aaainternal.Plugin(context=context)
......
- 其中 aaainternal.py 中创建了 aaa-jdbc 的默认内部域配置文件。
self.environment[otopicons.CoreEnv.MAIN_TRANSACTION].append(
filetransaction.FileTransaction(
name=(
os.path.join(
oenginecons.FileLocations.OVIRT_ENGINE_EXTENSIONS_DIR,
'%s-authn.properties' % profile
)
),
mode=0o600,
owner=self.environment[osetupcons.SystemEnv.USER_ENGINE],
enforcePermissions=True,
content=(
'ovirt.engine.extension.name = internal-authn\n'
'ovirt.engine.extension.bindings.method = jbossmodule\n'
'ovirt.engine.extension.binding.jbossmodule.module = '
'org.ovirt.engine.extensions.builtin\n'
'ovirt.engine.extension.binding.jbossmodule.class = '
'org.ovirt.engine.extensions.aaa.builtin.internal.'
'InternalAuthn\n'
'ovirt.engine.extension.provides = '
'org.ovirt.engine.api.extensions.aaa.Authn\n'
'ovirt.engine.aaa.authn.profile.name = {profile}\n'
'ovirt.engine.aaa.authn.authz.plugin = {authzName}\n'
'config.authn.user.name = {admin_user}\n'
'config.authn.user.password = {pbe}\n'
- 创建的配置文件路径在 /etc/ovirt-engine/extensions.d 目录中。
- internal-authz.properties 为授权配置。
- internal-authn.properties 为认证配置。
[root@rhvm extensions.d]# pwd
/etc/ovirt-engine/extensions.d
[root@rhvm extensions.d]# ls
internal-authn.properties internal-authz.properties
- internal-authn.properties 的内容。
[root@rhvm extensions.d]# cat internal-authn.properties
ovirt.engine.extension.name = internal-authn
ovirt.engine.extension.bindings.method = jbossmodule
ovirt.engine.extension.binding.jbossmodule.module = org.ovirt.engine.extensions.builtin
ovirt.engine.extension.binding.jbossmodule.class = org.ovirt.engine.extensions.aaa.builtin.internal.InternalAuthn
ovirt.engine.extension.provides = org.ovirt.engine.api.extensions.aaa.Authn
ovirt.engine.aaa.authn.profile.name = internal
ovirt.engine.aaa.authn.authz.plugin = internal-authz
参数 | 说明 |
---|---|
ovirt.engine.extension.name | 扩展模块的名称为 internal-authn。 |
ovirt.engine.extension.bindings.method | 扩展的方法,使用了 Jboss 的 module。 |
ovirt.engine.extension.binding.jbossmodule.module | 加载的 Jboss module。 |
ovirt.engine.extension.binding.jbossmodule.class | 加载 Jboss module 的 class 类。 |
ovirt.engine.extension.provides | 使用的供应者类(代理)。 |
ovirt.engine.aaa.authn.profile.name | 域名。 |
ovirt.engine.aaa.authn.authz.plugin | 依赖的模块。 |
config.datasource.file | 数据库相关配置文件路径。 |
ovirt.engine.extension.enabled | 是否启用,默认为 true。 |
- internal-authz.properties 的内容。
[root@rhvm extensions.d]# cat internal-authz.properties
ovirt.engine.extension.name = internal-authz
ovirt.engine.extension.bindings.method = jbossmodule
ovirt.engine.extension.binding.jbossmodule.module = org.ovirt.engine.extensions.builtin
ovirt.engine.extension.binding.jbossmodule.class = org.ovirt.engine.extensions.aaa.builtin.internal.InternalAuthz
ovirt.engine.extension.provides = org.ovirt.engine.api.extensions.aaa.Authz
参数 | 说明 |
---|---|
ovirt.engine.extension.name | 扩展模块的名称 internal-authz。 |
ovirt.engine.extension.bindings.method | 扩展的方法,使用了 Jboss 的 module。 |
ovirt.engine.extension.binding.jbossmodule.module | 加载的 Jboss module。 |
ovirt.engine.extension.binding.jbossmodule.class | 加载 Jboss module 的 class 类。 |
ovirt.engine.extension.provides | 使用的供应者类(代理)。 |
config.datasource.file | 数据库相关配置文件路径。 |
ovirt.engine.extension.enabled | 是否启用,默认为 true。 |
2.2 加载 aaa-jdbc 配置信息
- engine 使用
EngineExtensionsManager
类进行配置信息的加载。- 在 engine 初始化的 Backend 类中执行。
EngineExtensionsManager.getInstance().engineInitialize();
- 读取用户扩展管理的配置文件。
- 配置目录为 /user/share/ovirt-engine/extensions.d 和 /etc/ovirt-engine/extensions.d
[root@rhvm extensions.d]# cat /usr/share/ovirt-engine/services/ovirt-engine/ovirt-engine.conf | grep ENGINE_EXTENSION_PATH
ENGINE_EXTENSION_PATH="${ENGINE_USR}/extensions.d:${ENGINE_ETC}/extensions.d"
for (File directory : EngineLocalConfig.getInstance().getExtensionsDirectories()) {
if (!directory.exists()) {
log.warn("The directory '{}' cotaning configuration files does not exist.",
directory.getAbsolutePath());
} else {
// The order of the files inside the directory is relevant, as the objects are created in
// the same order
// that
// the files are processed, so it is better to sort them so that objects will always be
// created in the
// same
// order regardless of how the filesystem decides to store the entries of the directory:
File[] files = directory.listFiles();
if (files != null) {
sort(files);
for (File file : files) {
if (file.getName().endsWith(".properties")) {
try {
load(file);
} catch (Exception ex) {
log.error("Could not load extension based on configuration file '{}'. Please check the configuration file is valid. Exception message is: {}",
file.getAbsolutePath(),
ex.getMessage());
log.debug("", ex);
}
}
}
}
}
}
public String load(File file) {
try (
InputStream is = new FileInputStream(file);
Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8)
) {
Properties props = new Properties();
props.load(reader);
return loadImpl(props, file);
} catch (IOException exception) {
throw new ConfigurationException(String.format("Can't load object configuration file '%1$s': %2$s",
file.getAbsolutePath(), exception.getMessage()), exception);
}
}
- 根据配置等信息,生成扩展实体对象。
-
loadedEntries
链表中包含了不同用户域(ovirt.engine.extension.name)和其配置文件对象的映射关系。 -
bindingsLoaders
链表中包含了不同构造器(bindingsLoader)和其名称(ovirt.engine.extension.bindings.method)的映射关系,这里是 jbossmodule,对应了JBossBindingsLoader
构造器。 -
JBossBindingsLoader
可以通过指定 module 名称加载不同的 Jboss module。
-
ExtensionEntry entry = new ExtensionEntry(props, confFile);
......
entry.extension = loadExtension(props);
entry.extension.getContext().mput(
Base.ContextKeys.GLOBAL_CONTEXT,
globalContext
).mput(
TRACE_LOG_CONTEXT_KEY,
traceLog
).mput(
Base.ContextKeys.INTERFACE_VERSION_MIN,
0
).mput(
Base.ContextKeys.INTERFACE_VERSION_MAX,
Base.INTERFACE_VERSION_CURRENT
).mput(
Base.ContextKeys.LOCALE,
Locale.getDefault().toString()
).mput(
Base.ContextKeys.CONFIGURATION_FILE,
entry.file == null ? null : entry.file.getAbsolutePath()
).mput(
Base.ContextKeys.CONFIGURATION,
props
).mput(
Base.ContextKeys.CONFIGURATION_SENSITIVE_KEYS,
splitString(props.getProperty(Base.ConfigKeys.SENSITIVE_KEYS, ""))
).mput(
Base.ContextKeys.INSTANCE_NAME,
entry.name
).mput(
Base.ContextKeys.PROVIDES,
splitString(props.getProperty(Base.ConfigKeys.PROVIDES, ""))
);
- 根据配置文件中定义的构造器(bindingsLoader)名称,获得不同的构造器,再由此构造器加载配置文件中指定的 Jboss module 和执行指定的 Jboss module class。
BindingsLoader loader = bindingsLoaders.get(props.getProperty(Base.ConfigKeys.BINDINGS_METHOD));
......
public ExtensionProxy load(Properties props) throws Exception {
Module module = loadModule(
props.getProperty(Base.ConfigKeys.BINDINGS_JBOSSMODULE_MODULE)
);
return new ExtensionProxy(
module.getClassLoader(),
lookupService(
module,
Extension.class,
props.getProperty(Base.ConfigKeys.BINDINGS_JBOSSMODULE_CLASS)
).newInstance()
);
}
- 根据配置文件中指定的值
- 加载的 Jboss module 是 org.ovirt.engine.extensions.builtin
- 执行的类是 org.ovirt.engine.extensions.aaa.builtin.internal.InternalAuthn。
-
ExtensionProxy
是一个扩展代理类,在加载 Jboss module 的时候会进行创建,并把指定执行的 Jboss module class 类实例化后的对象放入其中。这里为InternalAuthn
对象和InternalAuthz
对象。 -
ExtensionProxy
类中包含了一个ExtMap
对象。该对象是一个ConcurrentMap
,包含有扩展属性和值的映射关系。
2.3 使用默认的本地用户域支持
- 上述配置文件中指定的 Jboss module class 就是对该扩展支持的实现策略。
- 默认情况下使用
InternalAuthn
处理用户的认证,使用InternalAuthz
处理用户的授权。
- 默认情况下使用
- 用户 Sso 登录使用了
AuthenticationUtils
类中的login
方法进行登录认证。- 其中 profile 为
ExtensionProxy
扩展代理类,authn 即InternalAuthn
。 - 执行
InternalAuthn
的invoke
进行认证处理。
- 其中 profile 为
ExtMap outputMap = profile.authn.invoke(new ExtMap().mput(
Base.InvokeKeys.COMMAND,
Authn.InvokeCommands.AUTHENTICATE_CREDENTIALS
).mput(
Authn.InvokeKeys.USER,
user
).mput(
Authn.InvokeKeys.CREDENTIALS,
credentials.getPassword()
)
public void invoke(ExtMap input, ExtMap output) {
try {
if (input.get(Base.InvokeKeys.COMMAND).equals(Base.InvokeCommands.LOAD)) {
doLoad(input);
} else if (input.get(Base.InvokeKeys.COMMAND).equals(Base.InvokeCommands.INITIALIZE)) {
// Do nothing
} else if (input.get(Base.InvokeKeys.COMMAND).equals(Authn.InvokeCommands.AUTHENTICATE_CREDENTIALS)) {
doAuthenticate(input, output);
} else {
output.put(Base.InvokeKeys.RESULT, Base.InvokeResult.UNSUPPORTED);
}
output.putIfAbsent(Base.InvokeKeys.RESULT, Base.InvokeResult.SUCCESS);
} catch (Exception ex) {
output.mput(Base.InvokeKeys.RESULT, Base.InvokeResult.FAILED).
mput(Base.InvokeKeys.MESSAGE, ex.getMessage());
}
}
- 根据传递不同的 COMMAND,进行不同的处理。
- AUTHENTICATE_CREDENTIALS 进行用户认证。
- USER 传递的是用户。
- CREDENTIALS 传递的是密码。
private void doAuthenticate(ExtMap input, ExtMap output) throws IOException, GeneralSecurityException {
output.put(Authn.InvokeKeys.PRINCIPAL, input.get(Authn.InvokeKeys.USER));
if (
input.get(Authn.InvokeKeys.USER).equals(adminUser) &&
EnvelopePBE.check(adminPassword, input.get(Authn.InvokeKeys.CREDENTIALS))
) {
output.mput(
Authn.InvokeKeys.RESULT,
Authn.AuthResult.SUCCESS
).mput(
Authn.InvokeKeys.AUTH_RECORD,
new ExtMap().mput(
Authn.AuthRecord.PRINCIPAL,
adminUser
)
);
} else {
output.put(Authn.InvokeKeys.RESULT, Authn.AuthResult.CREDENTIALS_INVALID);
}
}
......
Properties config = context.get(Base.ContextKeys.CONFIGURATION);
adminUser = config.getProperty("config.authn.user.name", "admin");
adminPassword = config.getProperty("config.authn.user.password");
- 认证后进行不同结果的组装并返回。至此完成默认本地用户域的认证,授权模块类似。
2.4 使用扩展的本地用户域支持
- Ovirt 4.2 版本中,安装 ovirt-engine-extension-aaa-jdbc 的 rpm 包, aaa-jdbc 提供的配置文件会被覆盖为新的配置文件。
- 新配置文件,同样在 engine-setup 过程中创建。
- 其中 aaajdbc.py 中创建了 aaa-jdbc 的扩展内部域配置文件。
- 使用 AuthnExtension 处理用户的认证,使用 AuthzExtension 处理用户的授权。
@util.export
def createPlugins(context):
aaa.Plugin(context=context)
......
aaajdbc.Plugin(context=context)
......
- internal-authn.properties 的内容。
yum install ovirt-engine-extension-aaa-jdbc
[root@rhvm extensions.d]# cat internal-authn.properties
ovirt.engine.extension.name = internal-authn
ovirt.engine.extension.bindings.method = jbossmodule
ovirt.engine.extension.binding.jbossmodule.module = org.ovirt.engine.extension.aaa.jdbc
ovirt.engine.extension.binding.jbossmodule.class = org.ovirt.engine.extension.aaa.jdbc.binding.api.AuthnExtension
ovirt.engine.extension.provides = org.ovirt.engine.api.extensions.aaa.Authn
ovirt.engine.aaa.authn.profile.name = internal
ovirt.engine.aaa.authn.authz.plugin = internal-authz
config.datasource.file = /etc/ovirt-engine/aaa/internal.properties
- internal-authz.properties 的内容。
[root@rhvm extensions.d]# cat internal-authz.properties
ovirt.engine.extension.name = internal-authz
ovirt.engine.extension.bindings.method = jbossmodule
ovirt.engine.extension.binding.jbossmodule.module = org.ovirt.engine.extension.aaa.jdbc
ovirt.engine.extension.binding.jbossmodule.class = org.ovirt.engine.extension.aaa.jdbc.binding.api.AuthzExtension
ovirt.engine.extension.provides = org.ovirt.engine.api.extensions.aaa.Authz
config.datasource.file = /etc/ovirt-engine/aaa/internal.properties
- 加载的 Jboss module 修改为 org.ovirt.engine.extension.aaa.jdbc,执行的 Jboss module class 修改为 org.ovirt.engine.extension.aaa.jdbc.binding.api.AuthnExtension。
-
AuthnExtension
类的代码,在 ovirt-engine-extension-aaa-jdbc 的源码包中可以找到。 - 与默认的本地用户域支持一样的加载和执行方式。Sso 登录调用了
AuthnExtension
的invoke
方法。
public void invoke(ExtMap input, ExtMap output) {
try {
if (tasks != null) {
tasks.execute();
}
if (input.get(Base.InvokeKeys.COMMAND).equals(Base.InvokeCommands.LOAD)) {
doLoad(input);
} else if (input.get(Base.InvokeKeys.COMMAND).equals(Base.InvokeCommands.INITIALIZE)) {
doInit(input);
} else if (input.get(Base.InvokeKeys.COMMAND).equals(Base.InvokeCommands.TERMINATE)) {
// Nothing to do.
} else if (
(input.get(Base.InvokeKeys.COMMAND).equals(Authn.InvokeCommands.AUTHENTICATE_CREDENTIALS)) ||
(input.get(Base.InvokeKeys.COMMAND).equals(Authn.InvokeCommands.CREDENTIALS_CHANGE))
) {
doAuth(input, output);
} else {
output.put(Base.InvokeKeys.RESULT, Base.InvokeResult.UNSUPPORTED);
throw new IllegalArgumentException("unsupported:" + input.get(Base.InvokeKeys.COMMAND));
}
output.mput(Base.InvokeKeys.RESULT, Base.InvokeResult.SUCCESS);
} catch (Throwable e) {
LOG.error(
"Unexpected Exception invoking: {}",
input.<ExtUUID>get(Base.InvokeKeys.COMMAND)
);
LOG.debug(
"Exception:",
e
);
output.putIfAbsent(Base.InvokeKeys.RESULT, Base.InvokeResult.FAILED);
output.put(Base.InvokeKeys.MESSAGE, e.getMessage());
}
}
- 用户认证执行。
Authentication.AuthResponse response = authentication.doAuth(
input.get(Authn.InvokeKeys.USER, String.class),
input.get(Authn.InvokeKeys.CREDENTIALS, String.class),
input.get(Base.InvokeKeys.COMMAND).equals(Authn.InvokeCommands.CREDENTIALS_CHANGE),
input.get(Authn.InvokeKeys.CREDENTIALS_NEW, String.class)
);
- 在数据库中查询用户的有效性等。
Schema.get(
new ExtMap().mput(Schema.InvokeKeys.ENTITY, Schema.Entities.USER)
.mput(Schema.InvokeKeys.DATA_SOURCE, ds)
.mput(Schema.InvokeKeys.SETTINGS_RESULT, settings)
.mput(
Schema.InvokeKeys.ENTITY_KEYS,
new ExtMap().mput(
Schema.UserIdentifiers.USERNAME,
subject
)
)
).get(
Schema.InvokeKeys.USER_RESULT,
Schema.User.class
);
if (input.get(InvokeKeys.ENTITY, ExtUUID.class).equals(Entities.USER)) {
ret.mput(
InvokeKeys.USER_RESULT,
new Sql.Query(
Formatter.format(
GET_USER,
generateWhere(input.get(InvokeKeys.ENTITY_KEYS, ExtMap.class))
)
).asResults(
ds,
USER_RESOLVER,
input.get(InvokeKeys.SETTINGS_RESULT, ExtMap.class)
)
);
- 数据库的连接配置在 aaa-jdbc 提供的配置文件中进行了指定。
config.datasource.file = /etc/ovirt-engine/aaa/internal.properties
[root@rhvm extensions.d]# cat /etc/ovirt-engine/aaa/internal.properties
config.datasource.jdbcurl=jdbc:postgresql://localhost:5432/engine?sslfactory=org.postgresql.ssl.NonValidatingFactory
config.datasource.dbuser=engine
config.datasource.dbpassword=Au1qXqSMx3nDRxCQD94RVq
config.datasource.jdbcdriver=org.postgresql.Driver
config.datasource.schemaname=aaa_jdbc
- 扩展的本地用户域支持创建了新的表空间 aaa_jdbc。
- 其中 users 表为用户表,保存了用户名、密码等信息。
- settings 为扩展属性表,是该扩展功能额外增加的本地用户属性。
- settings 表的内容在实例化 Schema 类时,进行了查询加载。
public static class Settings {
/**
* Authentication related
*/
public static final ExtKey PASSWORD_HISTORY_LIMIT = new ExtKey("", Integer.class, "e843bc2a-0878-4b6f-9be3-32e83169fb7c");
public static final ExtKey LOCK_MINUTES = new ExtKey("", Integer.class, "78b5138a-d52b-464d-a2a7-5fed55bdf7b3");
public static final ExtKey PRESENT_WELCOME_MESSAGE = new ExtKey("", Boolean.class, "0ae5affd-15e5-4bb1-9910-f091b64b7197");
public static final ExtKey MESSAGE_SEPARATOR = new ExtKey("", String.class, "ecf6d62a-10f8-4fad-b401-75c9d0788955");
public static final ExtKey MESSAGE_OF_THE_DAY = new ExtKey("", String.class, "df496823-6814-4720-a302-c723f629f847");
public static final ExtKey PASSWORD_EXPIRATION_DAYS = new ExtKey("", Integer.class, "dc3e2fb4-cbcc-4f5c-9b06-5db9ec534aa8");
......
private static class SettingsResolver implements Sql.ResultsResolver<ExtMap> {
@Override
public ExtMap resolve(ResultSet rs, ExtMap ctx) throws SQLException {
ExtMap settings = new ExtMap();
ExtMap descriptions = new ExtMap();
UUID uuid;
ExtKey key = null;
String value = null;
try {
while (rs.next()) {
uuid = UUID.fromString(rs.getString("uuid"));
key = SETTING_KEYS.get(uuid);
value = rs.getString("value");
if (key != null) {
settings.put(
new ExtKey(rs.getString("name"), key.getType(), key.getUuid().getUuid().toString()),
key.getType().getConstructor(java.lang.String.class).newInstance(value)
);
descriptions.put(
new ExtKey(rs.getString("name"), String.class, key.getUuid().getUuid().toString()),
rs.getString("description")
);
} else {
throw new RuntimeException("key for " + uuid + " not found");
}
}
} catch ( NoSuchMethodException|InvocationTargetException|InstantiationException|IllegalAccessException e) {
throw new RuntimeException(
Formatter.format(
"Could not convert setting to expected type. value: {} requested type: {}",
value,
key.getType()
),
e
);
}
return settings.mput(Settings.SETTING_DESCRIPTIONS, descriptions);
}
}
3. 外部用户域
- 与本地用户域使用方式类似,安装 ovirt-engine-extension-aaa-ldap,会创建新的 aaa-jdbc 配置文件。
-
AuthzExtension
类的代码,在 ovirt-engine-extension-aaa-ldap 的源码包中可以找到。
-
yum install ovirt-engine-extension-aaa-ldap
[root@rhvm extensions.d]# cat ad-authz.properties
ovirt.engine.extension.name = ad-authz
ovirt.engine.extension.bindings.method = jbossmodule
ovirt.engine.extension.binding.jbossmodule.module =
org.ovirt.engine-extensions.aaa.ldap
ovirt.engine.extension.binding.jbossmodule.class =
org.ovirt.engineextensions.aaa.ldap.AuthzExtension
ovirt.engine.extension.provides =
org.ovirt.engine.api.extensions.aaa.Authz
config.profile.file.1 = /etc/ovirt-engine/aaa/ad.properties
- LDAP 配置文件内容
# Select one
#
include = <openldap.properties>
#include = <389ds.properties>
#include = <rhds.properties>
#include = <ipa.properties>
#include = <iplanet.properties>
#include = <rfc2307-389ds.properties>
#include = <rfc2307-rhds.properties>
#include = <rfc2307-openldap.properties>
#include = <rfc2307-edir.properties>
#include = <rfc2307-generic.properties>
# Server
Red Hat Virtualization 4.2 Administration Guide
236
#
vars.server = ldap1.company.com
# Search user and its password.
#
vars.user = uid=search,cn=users,cn=accounts,dc=company,dc=com
vars.password = 123456
pool.default.serverset.single.server = ${global:vars.server}
pool.default.auth.simple.bindDN = ${global:vars.user}
pool.default.auth.simple.password = ${global:vars.password}
- 若要使用 TLS 或 SSL 协议与 LDAP 服务器进行交互,需要获取 LDAP 服务器的根 CA 证书并使用它创建公共密钥存储文件。
- 在配置文件中指定公共密钥存储文件的完整路径和访问文件的密码。
# Create keystore, import certificate chain and uncomment
# if using tls.
pool.default.ssl.startTLS = true
pool.default.ssl.truststore.file = /full/path/to/myrootca.jks
pool.default.ssl.truststore.password = password