如何部署Oracle Linux 9.x PrivacyIDEA?

privacyIDEA

1 基础知识

1.1 privacyIDEA的介绍

1.1.1 privacyIDEA是什么?

– 模块化的身份验证服务器
– 可通过双因素身份验证来增强应用程序的安全

1.1.2 privacyIDEA的作用

– 可增强本地登录的安全性
– 可增强VPN的安全性
– 可增强远程访问的安全性
– 可增强SSH连接的安全性
– 可增强访问网站或Web门户网站的安全性
注,privacyIDEA最初有如下作用,
– 作为OTP服务器的提供OTP(一次性密码)认证设备
– 也可用户其他设备的Challenge Response、U2F、Yubikeys、SSH秘钥和x509证书

1.1.3 软件特点

– 运行于Linux系统
– 完全开源,基于AGPLv3的许可发布
– 系统由Python语言编写
– 使用Flask的Web框架
– 使用SQL数据库作为数据存储

1.1.4 支持的用户来源

– LDAP服务
– Active Directory
– SQL数据库
– 平面文件
– SCIM服务

1.2 privacyIDEA的功能

1.2.1 支持的认证设备

– Simple pass token,即简单的令牌传递(实际总回应ACCEPTED)
– HOTP/TOTP,支持的品牌有SafeNet/eToken Pass、Safeword、Feitian、Smartdisplayer(所有OATH令牌)
– Push-Button key fobs,即按钮式密码卡
– OTP cards,即One-time Password或称动态口令
– Smartphone Apps,如Google Authenticator或FreeOTP
– MOTP,即移动密码系统
– QR code,即通过扫描QR码验证TiQR令牌
– RADIUS token,即将身份验证请求转发给RADIUS服务器
– REMOTE token,即将身份验证请求转发给别的privacyIDEA服务器
– Email-Token,即通过电子邮件发送的一次性密码
– SMS-Token,即通过短信发送一次性密码(SMSOTP,mTAN,mobileTAN)
– Yubikey in all Modes,即Yubikey的所有模式,包括OATH HOTP、Challenge Response、Yubico AES
– Day OTP token,即通过一天使用一个密码并在第二天更改令牌的密码的验证设备
– Password token,即将OTP PIN与附加密码的组合,适用于内部丢失令牌的场景
– SSH public key,即SSH令牌包含公共的SSH秘钥,此SSH秘钥可分发到计算机用于SSH登录
– x509 certificates,即由证书颁发机构颁发的x509证书
– Registration Token,即注册令牌,便于部署
– python class inherited from TokenClass,即从TokenClass继承的新Python类来添加新的身份验证设备

1.2.2 支持的认证接口

– 支持REST API(符合RESTful设计架构的接口)和JWT(JSON Web Token)身份验证
– 支持返回JSON输出
– 支持与SimpleSAMLphp一起充当SAML身份提供程序
– 插件可用于,
– – FreeRADIUS
– – PAM(支持离线OTP)
– – Apache2
– – OTRS
– – Django
– – ownCloud
– – WordPress
– – TYPO3
– – Contao
– – dokuwiki等等

1.2.3 支持的登录方式

– 用户或管理员可以登录privacyIDEA WebUI
– 用户或管理员可针对其他用户(例如域密码)或针对privacyIDEA(使用OTP)进行身份验证

1.2.4 支持的管理

– 支持创建解析器和领域
– 支持查看审核日志
– 支持管理令牌,具体是注册或删除、分配或取消分配、设置PIN、导入、启用或禁用、重新同步令牌、查看令牌详情
– 支持获取特定的令牌OTP值
– 支持获取特定OTP值的令牌序列号
– 支持定义令牌默认设置
– 支持令牌有效期
– 支持令牌最大使用量(根据每天或单个令牌的使用计数,例如可限制10此成功登录或20此尝试登录,或多少周内有效)
– 支持创建丢失的令牌,即将临时令牌注册给丢失令牌的用户
– 支持定义策略
– 支持通过RADIUS侧列进行简单迁移
– 支持定义根据任何的操作定义任何操作

1.2.5 支持的自助服务

– 支持用户可通过WebUI自行管理自己的令牌
– 支持通过QR码(Google身份验证器)、通过种子或令牌的序列号注册新令牌
– 支持删除、禁用、启用和设置PIN
– 支持查看与用户相关的账号、令牌的审核日志

1.2.6 支持的用户来源

– 支持用户的来源有,平面文件、SQL数据库、LDAP、openLDAP、Active Directory或其他LDAP服务器或SCIM服务器
– 支持根据SQL用户表定义设置,有些程序包含预定义配置,如WordPress、OTRS、onwCloud和Tine 2.0
– 支持根据LDAP目录定义设置,有些程序包含预定义配置,如openLDAP和Active Directory
– 支持将一个领域组合到另外一个领域,privacyIDEA可管理无限个领域
– 支持在SQL用户存储中增删改查privacyIDEA用户

1.2.7 支持的策略

– 支持详细的行为配置(由复杂的策略模块支持)
– 支持为令牌管理、系统配置、自助服务、身份验证、授权 、注册和审核定义策略
– 支持针对某些用户、领域、管理员、管理员操作、请求的客户端、请求令牌的类型等使用策略
– 支持管理范围策略,如定义管理界面中允许管理员执行的操作(每个操作)
– 支持系统范围策略,如定义允许那个管理员配置privacyIDEA
– 支持自助服务范围策略,如定义允许用户在自助服务门户中使用它的令牌执行操作(定制从本地网络或因特网登录权限不同)
– 支持身份验证范围策略,如定义要求用户使用OTP值以外的OTP PIN登录或额外使用LDAP密码登陆
– 支持授权范围策略,如定义需要某些类型的令牌或序列号登录
– 支持注册范围策略,如定义允许用户分配的令牌数或领域包含的令牌数或注册期间的随机OTP PIN或OTP PIN强度
– 支持审计范围,如定义允许那个管理严查看审计日志
– 支持导入和导出、启用或禁用策略

1.2.8 支持的事件处理程序

– 支持响应事件的电子邮件通知
– 支持定义已处理事件不会更改原始行为
– 支持通过简单的模块设置增强时间处理

1.2.9 支持的审计

– 详细的审计日志存储到SQL数据库
– 审计条目经过数据签名和检查以防止删除(防篡改)
– 审计日志会跟踪事件状态、事件触发者、涉及的令牌、请求的客户端、执行的privacyIDEA服务器以及其他详细信息

1.2.10 支持的机器和应用

– 支持定义客户端的计算机
– 支持定义应用程序的类型(SSH、PAM、LUKS),支持分配令牌到计算机的那些程序、支持脱机身份验证

1.2.11 支持的数据库

– SQLite
– MySQL
– PostgreSQL
– Oracle
– DB2
注:令牌秘钥数据库中使用AES加密

2 最佳实践

2.1 环境配置

2.1.1 系统环境信息

HostName = privacyidea.cmdschool.org
OS = CentOS 8.x-x86_64
IP Address = 10.168.0.105

2.1.2 完成系统的基本配置

如何完成CentOS 7.x的基本服务?

2.1.3 配置名称解析

echo '10.168.0.105 privacyidea privacyidea.cmdschool.org' >> /etc/hosts

2.2 部署DB

2.2.1 安装配置MariaDB

如何部署Oracle Linux 9.x MariaDB?

2.2.2 创建应用所需的库

echo 'create database privacyidea;' | mysql -u root -p
echo 'grant all privileges on privacyidea.* to "privacyidea"@"localhost" identified by "privacyideapwd";' | mysql -u root -p

配置完成可使用如下名称测试你的配置,

mysql -uprivacyidea -pprivacyideapwd

2.3 部署privacyIDEA

2.3.1 安装虚拟隔离配置工具

dnf install -y python3 python3-pip
pip install -y virtualenv

2.3.2 配置虚拟隔离环境

mkdir -p /opt/privacyidea
virtualenv /opt/privacyidea
cd /opt/privacyidea
source bin/activate

另外,如果你需要反向操作,取消该虚拟环境,请使用如下命令,

rm -rf /opt/privacyidea

2.3.3 安装pip工具

cd /opt/privacyidea
source bin/activate
pip install -i https://pypi.tuna.tsinghua.edu.cn/simpl --upgrade pip

2.3.4 安装依赖包

cd /opt/privacyidea
source bin/activate
pip install -i https://pypi.tuna.tsinghua.edu.cn/simpl privacyidea==3.11.4
pip install -i https://pypi.tuna.tsinghua.edu.cn/simpl -r lib/privacyidea/requirements.txt

2.3.5 确定连接MySQL包已安装

cd /opt/privacyidea
source bin/activate
pip list | grep PyMySQL

2.4 配置privacyIDEA

2.4.1 配置环境变量

vim /etc/profile.d/privacyidea.sh

增加如下定义,

export PRIVACYIDEA_HOME=/opt/privacyidea
export PATH=${PRIVACYIDEA_HOME}/bin:$PATH
export PRIVACYIDEA_CONFIGFILE=/etc/privacyidea/pi.cfg

然后,你需要执行以下变量导入环境变量,

source /etc/profile

根据环境变量的定义,我们需要创建如下文件夹,

mkdir -p /etc/privacyidea

2.4.2 创建并定义配置文件

touch /etc/privacyidea/pi.cfg
chmod 640 /etc/privacyidea/pi.cfg
vim /etc/privacyidea/pi.cfg

增加如下配置,

# The realm, where users are allowed to login as administrators
SUPERUSER_REALM = ['super', 'administrators']
# Your database
SQLALCHEMY_DATABASE_URI = 'mysql://privacyidea:privacyideapwd@localhost/privacyidea'
# This is used to encrypt the auth_token
SECRET_KEY = 't0p s3cr3t'
# This is used to encrypt the admin passwords
PI_PEPPER = "Never know..."
# This is used to encrypt the token data and token passwords
PI_ENCFILE = '/etc/privacyidea/enckey'
# This is used to sign the audit log
PI_AUDIT_KEY_PRIVATE = '/etc/privacyidea/private.pem'
PI_AUDIT_KEY_PUBLIC = '/etc/privacyidea/public.pem'
# PI_AUDIT_MODULE = 
# PI_AUDIT_SQL_URI = 
# Truncate Audit entries to fit into DB columns
PI_AUDIT_SQL_TRUNCATE = True
# PI_LOGFILE = 'var/log/privacyidea/privacyidea.log'
# PI_LOGLEVEL = 20
# PI_INIT_CHECK_HOOK = 'your.module.function'
# PI_CSS = '/location/of/theme.css'
# PI_UI_DEACTIVATED = True

根据环境变量的定义,我们需要创建如下文件夹,

mkdir -p /var/log/privacyidea

修改“PI_PEPPER”和“SECRET_KEY”的变量值,

PEPPER="$(tr -dc A-Za-z0-9_ </dev/urandom | head -c24)"
sed -i "s~Never know...~$PEPPER~g" /etc/privacyidea/pi.cfg
SECRET="$(tr -dc A-Za-z0-9_ </dev/urandom | head -c24)"
sed -i "s~t0p s3cr3t~$SECRET~g" /etc/privacyidea/pi.cfg

根据配置文件的需求,我们需用使用以下命令创建变量““PI_ENCFILE””所需的文件,

pi-manage create_enckey

可见如下提示,


             _                    _______  _______
   ___  ____(_)  _____ _______ __/  _/ _ \/ __/ _ |
  / _ \/ __/ / |/ / _ `/ __/ // // // // / _// __ |
 / .__/_/ /_/|___/\_,_/\__/\_, /___/____/___/_/ |_|
/_/                       /___/
                                            v3.11.4
    
DeprecationWarning: The command 'create_enckey' is deprecated.
Traceback (most recent call last):
  File "/opt/privacyidea/bin/pi-manage", line 7, in 
    sys.exit(cli())
  File "/opt/privacyidea/lib/python3.9/site-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
  File "/opt/privacyidea/lib/python3.9/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
  File "/opt/privacyidea/lib/python3.9/site-packages/click/core.py", line 1688, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/opt/privacyidea/lib/python3.9/site-packages/click/core.py", line 1434, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/opt/privacyidea/lib/python3.9/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
  File "/opt/privacyidea/lib/python3.9/site-packages/click/decorators.py", line 33, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/opt/privacyidea/lib/python3.9/site-packages/flask/cli.py", line 383, in decorator
    app = ctx.ensure_object(ScriptInfo).load_app()
  File "/opt/privacyidea/lib/python3.9/site-packages/flask/cli.py", line 328, in load_app
    app: Flask | None = self.create_app()
  File "/opt/privacyidea/lib/python3.9/site-packages/privacyidea/cli/__init__.py", line 32, in create_silent_app
    app = create_app(config_name="production", silent=True)
  File "/opt/privacyidea/lib/python3.9/site-packages/privacyidea/app.py", line 177, in create_app
    db.init_app(app)
  File "/opt/privacyidea/lib/python3.9/site-packages/flask_sqlalchemy/extension.py", line 329, in init_app
    engines[key] = self._make_engine(key, options, app)
  File "/opt/privacyidea/lib/python3.9/site-packages/flask_sqlalchemy/extension.py", line 617, in _make_engine
    return sa.engine_from_config(options, prefix="")
  File "/opt/privacyidea/lib64/python3.9/site-packages/sqlalchemy/engine/create.py", line 739, in engine_from_config
    return create_engine(url, **options)
  File "", line 2, in create_engine
  File "/opt/privacyidea/lib64/python3.9/site-packages/sqlalchemy/util/deprecations.py", line 375, in warned
    return fn(*args, **kwargs)
  File "/opt/privacyidea/lib64/python3.9/site-packages/sqlalchemy/engine/create.py", line 544, in create_engine
    dbapi = dialect_cls.dbapi(**dbapi_args)
  File "/opt/privacyidea/lib64/python3.9/site-packages/sqlalchemy/dialects/mysql/mysqldb.py", line 150, in dbapi
    return __import__("MySQLdb")
ModuleNotFoundError: No module named 'MySQLdb'

注意到“150”行的错误提示,即由于“Python 3.9”环境数据库连接模块名称变更所致,可通过如下命令修复此问题,

vim /opt/privacyidea/lib64/python3.9/site-packages/sqlalchemy/dialects/mysql/mysqldb.py

通过注解掉原来的数据库模块代码,并添加符合实际的数据库模块导入代码,详细修改如下,

        #return __import__("MySQLdb")
        return __import__("pymysql")

修复后,重新执行可见如下提示,

             _                    _______  _______
   ___  ____(_)  _____ _______ __/  _/ _ \/ __/ _ |
  / _ \/ __/ / |/ / _ `/ __/ // // // // / _// __ |
 / .__/_/ /_/|___/\_,_/\__/\_, /___/____/___/_/ |_|
/_/                       /___/
                                            v3.11.4
    
Encryption key written to /etc/privacyidea/enckey
The file permission of /etc/privacyidea/enckey was set to 400!
Please ensure, that it is owned by the right user.

根据配置文件的需求,我们需用使用以下命令创建变量“PI_AUDIT_KEY_PRIVATE”和“PI_AUDIT_KEY_PUBLIC”所需的文件,

pi-manage create_audit_keys

可见如下提示,


             _                    _______  _______
   ___  ____(_)  _____ _______ __/  _/ _ \/ __/ _ |
  / _ \/ __/ / |/ / _ `/ __/ // // // // / _// __ |
 / .__/_/ /_/|___/\_,_/\__/\_, /___/____/___/_/ |_|
/_/                       /___/
                                            v3.11.4
    
Signing keys written to /etc/privacyidea/private.pem and /etc/privacyidea/public.pem
The file permission of /etc/privacyidea/private.pem was set to 400!
Please ensure, that it is owned by the right user.

根据配置文件的需求,我们需要使用如下命令创建数据库结构,

pi-manage createdb

如果收到如下错误提示,


             _                    _______  _______
   ___  ____(_)  _____ _______ __/  _/ _ \/ __/ _ |
  / _ \/ __/ / |/ / _ `/ __/ // // // // / _// __ |
 / .__/_/ /_/|___/\_,_/\__/\_, /___/____/___/_/ |_|
/_/                       /___/
                                            v3.11.4
    
Using connect string 
Running online

另外便于以后更新,请使用如下命令标记当前数数据库的组织和结构版本(可选),

pi-manage db stamp head -d /opt/privacyidea/lib/privacyidea/migrations/

可见如下输出,


             _                    _______  _______
   ___  ____(_)  _____ _______ __/  _/ _ \/ __/ _ |
  / _ \/ __/ / |/ / _ `/ __/ // // // // / _// __ |
 / .__/_/ /_/|___/\_,_/\__/\_, /___/____/___/_/ |_|
/_/                       /___/
                                            v3.11.4
    
Running online

然后,你需要使用以下命令创建一个本地的管理员账号,以下创建“admin”

pi-manage admin add admin

可见如下设置向导,


             _                    _______  _______
   ___  ____(_)  _____ _______ __/  _/ _ \/ __/ _ |
  / _ \/ __/ / |/ / _ `/ __/ // // // // / _// __ |
 / .__/_/ /_/|___/\_,_/\__/\_, /___/____/___/_/ |_|
/_/                       /___/
                                            v3.11.4
    
Password: 
Repeat for confirmation: 
Admin admin was registered successfully.

然后,你可以使用如下命令查看运行参数,

pi-manage runserver --help

可见如下显示,


             _                    _______  _______
   ___  ____(_)  _____ _______ __/  _/ _ \/ __/ _ |
  / _ \/ __/ / |/ / _ `/ __/ // // // // / _// __ |
 / .__/_/ /_/|___/\_,_/\__/\_, /___/____/___/_/ |_|
/_/                       /___/
                                            v3.11.4
    
Usage: pi-manage runserver [OPTIONS]

  (Deprecated) Run a local development server.

  This server is for development purposes only. It does not provide the
  stability, security, or performance of production WSGI servers.

  The reloader and debugger are enabled by default with the '--debug' option.

Options:
  --debug / --no-debug            Set debug mode.
  -h, --host TEXT                 The interface to bind to.
  -p, --port INTEGER              The port to bind to.
  --cert PATH                     Specify a certificate file to use HTTPS.
  --key FILE                      The key file to use when specifying a
                                  certificate.
  --reload / --no-reload          Enable or disable the reloader. By default
                                  the reloader is active if debug is enabled.
  --debugger / --no-debugger      Enable or disable the debugger. By default
                                  the debugger is active if debug is enabled.
  --with-threads / --without-threads
                                  Enable or disable multithreading.
  --extra-files PATH              Extra files that trigger a reload on change.
                                  Multiple paths are separated by ':'.
  --exclude-patterns PATH         Files matching these fnmatch patterns will
                                  not trigger a reload on change. Multiple
                                  patterns are separated by ':'.
  --help                          Show this message and exit.

  This command is deprecated. Please use 'pi-manage run' instead.

然后,你可以使用如下命令手动运行服务,

pi-manage runserver -h 0.0.0.0 -p 80

可见如下显示,


             _                    _______  _______
   ___  ____(_)  _____ _______ __/  _/ _ \/ __/ _ |
  / _ \/ __/ / |/ / _ `/ __/ // // // // / _// __ |
 / .__/_/ /_/|___/\_,_/\__/\_, /___/____/___/_/ |_|
/_/                       /___/
                                            v3.11.4
    
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:80
 * Running on http://10.168.0.22:80
Press CTRL+C to quit

2.5 配置Apache WSGI

2.5.1 安装软件包

dnf install -y httpd mod_ssl python3-mod_wsgi

2.5.2 删除ssl.conf的默认虚拟服务器配置

sed -i '/^<VirtualHost/,/<\/VirtualHost>$/d' /etc/httpd/conf.d/ssl.conf

配置修改后,请使用如下命令验证配置的语法,

httpd -t

2.5.3 启动服务并配置自启动

systemctl start httpd.service
systemctl status httpd.service
systemctl enable httpd.service

2.5.4 配置SELinux(可选)

semanage fcontext -a -t httpd_sys_rw_content_t "/var/log/privacyidea(/.*)?"
restorecon -R /var/log/privacyidea
setsebool -P httpd_can_network_connect_db 1
setsebool -P httpd_can_connect_ldap 1

2.5.5 配置运行用户和权限

useradd -r -m privacyidea -d /opt/privacyIDEA
chown -R privacyidea:root /var/log/privacyidea
chown -R privacyidea:root /etc/privacyidea
privacyidea-fix-access-rights -f /etc/privacyidea/pi.cfg -u privacyidea

2.5.6 部署WSGI配置

cp /opt/privacyidea/etc/privacyidea/privacyideaapp.wsgi /etc/privacyidea/

2.5.7 配置虚拟服务器

vim /etc/httpd/conf.d/privacyidea.conf

增加如下配置,

TraceEnable off
ServerSignature Off
ServerTokens Prod
WSGIPythonHome /opt/privacyidea
WSGISocketPrefix /var/run/wsgi

<VirtualHost *:443>
    SSLEngine on
    SSLProtocol all -SSLv2
    SSLCipherSuite DEFAULT:!EXP:!SSLv2:!DES:!IDEA:!SEED:+3DES
    SSLCertificateFile /etc/httpd/privacyidea.cmdschool.org.crt
    SSLCertificateKeyFile /etc/httpd/privacyidea.cmdschool.org.key
    SSLCertificateChainFile /etc/httpd/TrustedRoot.crt

    # The daemon is running as user 'privacyidea'
    # This user should have access to the encKey database encryption file
    WSGIDaemonProcess privacyidea python-path=/etc/privacyidea:/opt/privacyidea/lib/python3.6/site-packages processes=1 threads=15 display-name=%{GROUP} user=privacyidea
    WSGIProcessGroup privacyidea
    WSGIPassAuthorization On
    WSGIScriptAlias / /etc/privacyidea/privacyideaapp.wsgi

    DocumentRoot /var/www/privacyidea.cmdschool.org
    ServerName privacyidea.cmdschool.org
</VirtualHost>

<VirtualHost *:80>
    DocumentRoot /var/www/privacyidea.cmdschool.org
    ServerName privacyidea.cmdschool.org

    RewriteEngine on
    RewriteCond %{SERVER_PORT} !^443$
    RewriteRule ^/?(.*)$ https://%{SERVER_NAME}/$1 [L,R]
</VirtualHost>

<Directory /var/www/privacyidea.cmdschool.org>
    Require all granted
</Directory>
<Directory /etc/privacyidea>
    Require all granted
</Directory>

根据上面的配置,我们需要使用如下命令启动重写模块,

grep "rewrite_module modules" /etc/httpd/conf.modules.d/00-base.conf

正常有如下返回,

LoadModule rewrite_module modules/mod_rewrite.so

配置应用的证书可到腾讯云申请,详细请查阅下文,此处不再详述,
https://cloud.tencent.com/product/ssl
创建配置文件后,我们使用如下命令验证配置,

httpd -t

如果遇到如下错误提示,

AH00112: Warning: DocumentRoot [/var/www/privacyidea.cmdschool.org] does not exist
AH00112: Warning: DocumentRoot [/var/www/privacyidea.cmdschool.org] does not exist
Syntax OK

确认配置有效后,你需要使用如下命令重载服务使配置生效,

systemctl reload httpd.service

根据配置的需求,你可能还需要执行如下命令开放业务所需的端口,

firewall-cmd --permanent --add-service=http --add-service=https
firewall-cmd --reload
firewall-cmd --list-all

2.5.8 配置名称解析

notepad \Windows\System32\drivers\etc\hosts

加入如下配置,

10.168.0.105 privacyidea.cmdschool.org

注:以上为临时配置,生产环境请使用DNS配置

2.5.9 浏览器测试

https://privacyidea.cmdschool.org

注:以上账号和密码由之前步骤的“pi-manage admin add admin”命令创建

参阅文档
===================

软件版本
———-
https://pypi.org/project/privacyIDEA/

安装方法参考
——–

如何基于RHEL 8.x部署PrivacyIDEA?

没有评论

发表回复

privacyIDEA
如何加载privacyIDEA非本地的passwd文件用户源?

1 前言 一个问题,一篇文章,一出故事。 虽然privacyIDEA允许导入本地的“/etc/pas …

privacyIDEA
如何修改代码自定义PrivacyIDEA的OTP邮件模板?

1 前言 一个问题,一篇文章,一出故事。 笔者因为需要实现SFTP服务的2FA然后需要使用邮件发送验 …

privacyIDEA
如何创建PrivacyIDEA的邮件2FA Token?

1 前言 一个问题,一篇文章,一出故事。 笔者由于需要使用邮件获取一次性密码用于2FA认证,于是整理 …