Self-Service-Password 部署

 

Self-Service-Password (自助密码)部署

 

目录

介绍.... 2

特征.... 2

安装.... 2

先决条件:.... 2

CentOS / RedHat下安装.... 2

服务器配置.... 3

Web服务配置.... 3

openladp组件配置.... 4

导出活动目录证书.... 4

下载企业内部CA证书.... 6

配置 ldap.conf 6

修改ldap.conf文件.... 7

活动目录模式配置.... 8

配置文件示例.... 9

 

 

 

 

 

 介绍

 LDAP 工具箱自助密码是面向最终用户的 Web 应用程序。 它允许他们在丢失密码时更改或重置密码。

 它适用于任何 LDAP 目录,包括 Active Directory

 特征

 l  标准密码更改

 l  通过问题重置、通过邮件发送的令牌、通过短信发送的令牌

 l  本地密码策略

 l  LDAP 高级用法:密码修改扩展作、密码策略控制

 l  SSH 密钥更改

 l  Active Directory Samba 模式

 l  Prehook/Posthook:可以在更改密码之前和之后启动脚本

 l  邮件通知

 l  REST API

 安装

 先决条件:

 l  Apache 或其他 Web 服务器

 l  PHP >=7.4

 l  php-curl haveibeenpwned api

 l  php 过滤器

 l  PHP-GD (验证码)

 l  php-ldap 格式

 l  php-mbstring (重置邮件)

 l  php-openssl (令牌加密,可能是内置的)

 l  Smarty 3 4

 CentOS / RedHat下安装

 1.      配置 yum 存储库:

vi /etc/yum.repos.d/ltb-project.repo

 

[ltb-project-noarch]

name=LTB project packages (noarch)

baseurl=https://ltb-project.org/rpm/$releasever/noarch

enabled=1

gpgcheck=1

gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-LTB-project

 2.      更新

yum update

 3.      导入存储库密钥

对于 EL7/EL8

pm --import https://ltb-project.org/documentation/_static/RPM-GPG-KEY-LTB-project

对于 EL9

rpm --import https://ltb-project.org/documentation/_static/RPM-GPG-KEY-LTB-PROJECT-SECURITY

 4.      安装

yum install self-service-password

 服务器配置

 Web服务配置

 在apache环境下服务器会生成默认配置文件在

/etc/httpd/conf.d/self-service-password.conf

 修改配置文件servername为对对应FQDN名即可

       <VirtualHost *:80>

    ServerName sd.x.h

 

    DocumentRoot /usr/share/service-desk/htdocs

    DirectoryIndex index.php

 

    <Directory /usr/share/service-desk/htdocs>

        AllowOverride None

        <IfVersion >= 2.3>

            Require all granted

        </IfVersion>

        <IfVersion < 2.3>

            Order Deny,Allow

            Allow from all

        </IfVersion>

    </Directory>

 

    LogLevel warn

    ErrorLog /var/log/httpd/sd_error.log

    CustomLog /var/log/httpd/sd_access.log combined

</VirtualHost>

 

 

 openladp组件配置

 导出活动目录证书

 1.      ‌打开证书管理器‌

按下 Win + R 输入 ‌certlm.msc‌,回车(以管理员身份运行)。

或者通过 ‌MMC控制台‌:

打开运行(Win + R),输入 mmc

点击 ‌文件 > 添加/删除管理单元‌,选择 ‌证书‌ > ‌计算机账户‌ > ‌本地计算机‌

 2.      ‌找到域控制器证书

导航到 ‌证书 > 个人 > 证书‌。

在右侧列表中找到域控制器的证书(通常用途为“客户端身份验证”或“服务器身份验证”)

 

 3.      证书

右键点击证书,选择 ‌所有任务 > 导出‌。

‌选择导出选项‌:‌仅导出公钥(CER格式)‌:选择不导出私钥,导出为 .cer 文件。导出文件格式为Base64编码

 

 4.      ‌完成出‌

选择保存路径和文件名,完成导出。

 

 下载企业内部CA证书

 1.      访问企业CA服务器的Web页面(通常为 http://CA服务器地址/certsrv)。

 2.      下载根证书和中间证书(格式一般为 .crt .cer)。选择Base64编码

 

 配置 ldap.conf

 1.      将上面下载的两个证书复制到/etc/openldap/certs目录

 

 修改ldap.conf文件

#

# LDAP Defaults

#

 

# See ldap.conf(5) for details

# This file should be world readable but not world writable.

 

#BASE   dc=example,dc=com

#URI    ldap://ldap.example.com ldap://ldap-master.example.com:666

 

#SIZELIMIT      12

#TIMELIMIT      15

#DEREF          never

 

# When no CA certificates are specified the Shared System Certificates

# are in use. In order to have these available along with the ones specified

# by TLS_CACERTDIR one has to include them explicitly:

#TLS_CACERT     /etc/pki/tls/cert.pem

 

# System-wide Crypto Policies provide up to date cipher suite which should

# be used unless one needs a finer grinded selection of ciphers. Hence, the

# PROFILE=SYSTEM value represents the default behavior which is in place

# when no explicit setting is used. (see openssl-ciphers(1) for more info)

#TLS_CIPHER_SUITE PROFILE=SYSTEM

 

# Turning this off breaks GSSAPI used with krb5 when rdns = false

SASL_NOCANON    on

TLS_CACERT      /etc/openldap/certs/dcca.cer

TLS_CACERTDIR   /etc/openldap/certs

TLS_REQCERT allow

 

 活动目录模式配置

 1.      必须在 LDAP 连接上使用 SSL,因为 AD 拒绝更改 password clear 连接

 2.      启用活动目录模式

$ad_mode = true;

 

 3.      调整搜索过滤器

$ldap_filter = "(&(objectClass=user)(sAMAccountName={login})(!(userAccountControl:1.2.840.113556.1.4.803:=2)))";

 

 

 配置文件示例

<?php

#==============================================================================

# LTB Self Service Password

#

# Copyright (C) 2024 Clement OUDOT

# Copyright (C) 2024 LTB-project.org

#

# This program is free software; you can redistribute it and/or

# modify it under the terms of the GNU General Public License

# as published by the Free Software Foundation; either version 2

# of the License, or (at your option) any later version.

#

# This program is distributed in the hope that it will be useful,

# but WITHOUT ANY WARRANTY; without even the implied warranty of

# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

# GNU General Public License for more details.

#

# GPL License: http://www.gnu.org/licenses/gpl.txt

#

#==============================================================================

 

#==============================================================================

# All the default values are kept here, you should not modify it but use

# config.inc.local.php file instead to override the settings from here.

#==============================================================================

 

#==============================================================================

# Configuration

#==============================================================================

 

# Debug mode

# true: log and display any errors or warnings (use this in configuration/testing)

# false: log only errors and do not display them (use this in production)

$debug = true;

 

# LDAP

$ldap_url = "ldaps://dc.x.h:636";

$ldap_starttls = false;

$ldap_binddn = "cn=sspadmin,ou=XAdmin,dc=x,dc=h";

$ldap_bindpw = 'password';

// for GSSAPI authentication, comment out ldap_bind* and uncomment ldap_krb5ccname lines

//$ldap_krb5ccname = "/path/to/krb5cc";

$ldap_base = "dc=x,dc=h";

$ldap_login_attribute = "sAMAccountName";

#$ldap_login_attribute = "cn";

$ldap_fullname_attribute = "cn";

#$ldap_filter = "(&(objectClass=person)($ldap_login_attribute={login}))";

$ldap_filter = "(&(objectClass=user)(sAMAccountName={login})(!(userAccountControl:1.2.840.113556.1.4.803:=2)))";

$ldap_scope = "sub"; # possible values: sub, one, base

$ldap_use_exop_passwd = false;

$ldap_use_ppolicy_control = false;

$ldap_network_timeout = 10;

$ldap_page_size = 0;

 

# Active Directory mode

# true: use unicodePwd as password field

# false: LDAPv3 standard behavior

$ad_mode = true;

$ad_options=[];

# Force account unlock when password is changed

$ad_options['force_unlock'] = true;

# Force user change password at next login

$ad_options['force_pwd_change'] = false;

# Allow user with expired password to change password

$ad_options['change_expired_password'] = true;

 

# Samba mode

# true: update sambaNTpassword and sambaPwdLastSet attributes too

# false: just update the password

$samba_mode = false;

$samba_options=[];

# Set password min/max age in Samba attributes

#$samba_options['min_age'] = 5;

#$samba_options['max_age'] = 45;

#$samba_options['expire_days'] = 90;

 

# Shadow options - require shadowAccount objectClass

$shadow_options=[];

# Update shadowLastChange

$shadow_options['update_shadowLastChange'] = false;

$shadow_options['update_shadowExpire'] = false;

 

# Default to -1, never expire

$shadow_options['shadow_expire_days'] = -1;

 

# Hash mechanism for password:

# SSHA, SSHA256, SSHA384, SSHA512

# SHA, SHA256, SHA384, SHA512

# SMD5

# MD5

# CRYPT

# ARGON2

# clear (the default)

# auto (will check the hash of current password)

# This option is not used with ad_mode = true

$hash = "clear";

$hash_options=[];

 

# Prefix to use for salt with CRYPT

$hash_options['crypt_salt_prefix'] = "$6$";

$hash_options['crypt_salt_length'] = "6";

 

# USE rate-limiting by IP and/or by user

$use_ratelimit = false;

# dir for json db's (system default tmpdir)

#$ratelimit_dbdir = '/tmp';

# block attempts for same login ?

$max_attempts_per_user = 2;

# block attempts for same IP ?

$max_attempts_per_ip = 2;

# how many time to refuse subsequent requests ?

$max_attempts_block_seconds = "60";

# Header to use for client IP (HTTP_X_FORWARDED_FOR ?)

$client_ip_header = 'REMOTE_ADDR';

# JSON file to filter by IP

#$ratelimit_filter_by_ip_jsonfile = "/usr/share/self-service-password/conf/rrl_filter_by_ip.json";

 

# Local password policy

# This is applied before directory password policy

# Minimal length

$pwd_min_length = 8;

# Maximal length

$pwd_max_length = 20;

# Minimal lower characters

$pwd_min_lower = 0;

# Minimal upper characters

$pwd_min_upper = 0;

# Minimal digit characters

$pwd_min_digit = 0;

# Minimal special characters

$pwd_min_special = 0;

# Definition of special characters

$pwd_special_chars = "^a-zA-Z0-9";

# Forbidden characters

#$pwd_forbidden_chars = "@%";

# Don't reuse the same password as currently

$pwd_no_reuse = true;

# Check that password is different than login

$pwd_diff_login = true;

# Check new passwords differs from old one - minimum characters count

$pwd_diff_last_min_chars = 0;

# Forbidden words which must not appear in the password

$pwd_forbidden_words = array();

# Forbidden ldap fields

# Respective values of the user's entry must not appear in the password

# example: $pwd_forbidden_ldap_fields = array('cn', 'givenName', 'sn', 'mail');

$pwd_forbidden_ldap_fields = array();

# Complexity: number of different class of character required

$pwd_complexity = 0;

# use pwnedpasswords api v2 to securely check if the password has been on a leak

$use_pwnedpasswords = false;

# show password entropy bar (require php zxcvbn module)

$pwd_display_entropy = false;

# enforce password entropy check

$pwd_check_entropy = false;

# minimum entropy level required (when $pwd_check_entropy enabled)

$pwd_min_entropy = 3;

# Show policy constraints message:

# always

# never

# onerror

$pwd_show_policy = "above";

# Position of password policy constraints message:

# above - the form

# below - the form

$pwd_show_policy_pos = "above";

 

# disallow use of the only special character as defined in `$pwd_special_chars` at the beginning and end

$pwd_no_special_at_ends = false;

 

# Who changes the password?

# Also applicable for question/answer save

# user: the user itself

# manager: the above binddn

$who_change_password = "user";

 

# Show extended error message returned by LDAP directory when password is refused

$show_extended_error = false;

 

## Standard change

# Use standard change form?

$use_change = true;

 

## SSH Key Change

# Allow changing of sshPublicKey?

$change_sshkey = false;

 

# What attribute should be changed by the changesshkey action?

$change_sshkey_attribute = "sshPublicKey";

 

# What objectClass is required for that attribute?

$change_sshkey_objectClass = "ldapPublicKey";

 

# Ensure the SSH Key submitted uses a type we trust

$ssh_valid_key_types = array('ssh-rsa', 'ssh-dss', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'ssh-ed25519');

 

# Who changes the sshPublicKey attribute?

# Also applicable for question/answer save

# user: the user itself

# manager: the above binddn

$who_change_sshkey = "user";

 

# Notify users anytime their sshPublicKey is changed

## Requires mail configuration below

$notify_on_sshkey_change = false;

 

## Questions/answers

# Use questions/answers?

$use_questions = true;

# Allow to register more than one answer?

$multiple_answers = false;

# Store many answers in a single string attribute

# (only used if $multiple_answers = true)

$multiple_answers_one_str = false;

 

# Answer attribute should be hidden to users!

$answer_objectClass = "extensibleObject";

$answer_attribute = "info";

 

# Crypt answers inside the directory

$crypt_answers = true;

 

# Extra questions (built-in questions are in lang/$lang.inc.php)

# Should the built-in questions be included?

$questions_use_default = true;

#$messages['questions']['ice'] = "What is your favorite ice cream flavor?";

 

# How many questions must be answered.

#  If = 1: legacy behavior

#  If > 1:

#    this many questions will be included in the page forms

#    this many questions must be set at a time

#    user must answer this many correctly to reset a password

#    $multiple_answers must be true

#    at least this many possible questions must be available (there are only 2 questions built-in)

$questions_count = 1;

 

# Should the user be able to select registered question(s) by entering only the login?

$question_populate_enable = false;

 

## Token

# Use tokens?

# true (default)

# false

$use_tokens = true;

# Crypt tokens?

# true (default)

# false

$crypt_tokens = true;

# Token lifetime in seconds

$token_lifetime = "3600";

 

## File cache parameters

 

# cache type: File or Redis

$cache_type = "File";

 

# cache namespace: cache entries are grouped in this directory

$cache_namespace = "sspCache";

 

# cache directory: cache entries would be created in this extra

# directory inside namespace

$cache_directory = null;

 

# default lifetime for all cached entry

# not really usefull for now as each cache entry has a defined expiration

# (see cache_token_expiration and cache_form_expiration)

$cache_default_lifetime = 0;

 

## Redis cache parameters

 

# cache type: File or Redis

#$cache_type = "Redis";

 

# Data Source Name (DSN) for accessing to Redis server

# See https://symfony.com/doc/current/components/cache/adapters/redis_adapter.html

#$cache_redis_url = "redis:user:password@?host[redis1:6379]&timeout=5&dbindex=0";

 

# cache namespace: cache entries are prefixed by this namespace

#$cache_namespace = "sspCache";

 

# default lifetime for all cached entries

# not really usefull for now as each cache entry has a defined expiration

# (see cache_token_expiration and cache_form_expiration)

#$cache_default_lifetime = 0;

 

## General cache parameters

 

# $cache_token_expiration: integer, duration in seconds of cached objects

# each time a token is involved

# (for example when sending a token by sms or by mail)

# it is recommended to set a value >= $token_lifetime

$cache_token_expiration = 3600;

# $cache_form_expiration: integer, duration in seconds of cached objects

# at some steps when a user has to validate a form

# (for example when validating the email address before we send the mail)

# it is recommended to set a value high enough for a user to fill a form

$cache_form_expiration = 120;

 

# Reset URL (mandatory)

$reset_url = "http://ssp.x.h/";

# If inside a virtual host

#$reset_url = ($_SERVER['HTTPS'] ? "https" : "http") . "://" . $_SERVER['SERVER_NAME'] . $_SERVER['SCRIPT_NAME'];

# If behind a reverse proxy with a virtual host

#$reset_url = $_SERVER['HTTP_X_FORWARDED_PROTO'] . "://" . $_SERVER['HTTP_X_FORWARDED_HOST'] . $_SERVER['SCRIPT_NAME'];

 

## Mail

# LDAP mail attribute

$mail_attributes = array( "mail", "gosaMailAlternateAddress", "proxyAddresses" );

#$mail_attributes = "mail";

# Get mail address directly from LDAP (only first mail entry)

# and hide mail input field

# default = false

$mail_address_use_ldap = true;

# Who the email should come from

$mail_from = "sspadmin@xhome.local";

$mail_from_name = "Self Service Password";

$mail_signature = "SSP";

# Notify users anytime their password is changed

$notify_on_change = true;

# PHPMailer configuration (see https://github.com/PHPMailer/PHPMailer)

$mail_sendmailpath = '/usr/sbin/sendmail';

$mail_protocol = 'smtp';

$mail_smtp_debug = 2;

$mail_debug_format = 'error_log';

$mail_smtp_host = 'Exch2019.x.h';

$mail_smtp_auth = false;

$mail_smtp_user = 'sspadmin@x.h';

$mail_smtp_pass = 'Password';

$mail_smtp_port = 10025;

$mail_smtp_timeout = 30;

$mail_smtp_keepalive = false;

$mail_smtp_secure = 'false';

$mail_smtp_autotls = false;

$mail_smtp_options = array('tls' => array(

            'verify_peer' => false,

            'verify_peer_name' => false,

            'allow_self_signed' => true

        ));

$mail_contenttype = 'text/plain';

$mail_wordwrap = 0;

$mail_charset = 'utf-8';

$mail_priority = 3;

 

## SMS

# Use sms

$use_sms = false;

# Get telephone number directly from LDAP (only first number entry)

# and hide telephone number input field

# default = false

$sms_use_ldap = false;

# SMS method (mail, api)

$sms_method = "mail";

# path to SMS library to use

# currently, 3 libraries are bundled:

# - lib/smsapi-signal-cli.inc.php

# - lib/smsapi-twilio.inc.php

# - lib/smsovh/smsapi-ovh.inc.php

# The last one needs php-ovh-sms dependency, do `composer update` in lib/smsovh

# you can also write your own library

$sms_api_lib = "";

# GSM number attribute

$sms_attributes = array( "mobile", "pager", "ipPhone", "homephone" );

# Partially hide number

$sms_partially_hide_number = true;

# Send SMS mail to address. {sms_attribute} will be replaced by real sms number

$smsmailto = "{sms_attribute}@service.provider.com";

# Subject when sending email to SMTP to SMS provider

$smsmail_subject = "Provider code";

# Message

$sms_message = "{smsresetmessage} {smstoken}";

# Remove non digit characters from GSM number

$sms_sanitize_number = false;

# Truncate GSM number

$sms_truncate_number = false;

$sms_truncate_number_length = 10;

# SMS token length

$sms_token_length = 6;

# Max attempts allowed for SMS token

$sms_max_attempts_token = 3;

 

# Encryption, decryption keyphrase, required if $use_tokens = true and $crypt_tokens = true, or $use_sms, or $crypt_answer

# Please change it to anything long, random and complicated, you do not have to remember it

# Changing it will also invalidate all previous tokens and SMS codes

$keyphrase = "xhwAdmin135";

 

# Use attributes update page

$use_attributes = false;

#$attribute_mail = "mail";

#$attribute_phone = "mobile";

#$who_change_attributes = "manager";

 

# Display help messages

$show_help = true;

 

# Default language

$lang = "en";

 

# List of authorized languages. If empty, all language are allowed.

# If not empty and the user's browser language setting is not in that list, language from $lang will be used.

$allowed_lang = array();

 

# Display menu on top

$show_menu = true;

 

# Logo

$logo = "images/ltb-logo.png";

 

# Background image

$background_image = "images/unsplash-space.jpeg";

 

# Path is relative to htdocs/html and the custom CSS file should be created in css/ directory. For example: "css/sample.css"

$custom_css = "";

$display_footer = true;

 

# Where to log password resets - Make sure apache has write permission

# By default, they are logged in Apache log

#$reset_request_log = "/var/log/self-service-password";

 

# Invalid characters in login

# Set at least "*()&|" to prevent LDAP injection

# If empty, only alphanumeric characters are accepted

$login_forbidden_chars = "*()&|";

 

## Captcha

$use_captcha = false;

$captcha_class = "InternalCaptcha";

 

#$captcha_class = "FriendlyCaptcha";

#$friendlycaptcha_apiurl  = "https://api.friendlycaptcha.com/api/v1/siteverify";

#$friendlycaptcha_sitekey = "secret";

#$friendlycaptcha_secret  = "secret";

 

#$captcha_class       = "ReCaptcha";

#$recaptcha_url       = "https://www.google.com/recaptcha/api/siteverify";

#$recaptcha_sitekey   = "sitekey";

#$recaptcha_secretkey = "secretkey";

#$recaptcha_minscore  = 0.5;

 

## Default action

# change

# sendtoken

# sendsms

# changecustompwdfield

$default_action = "change";

 

## default page of custom password field to display

#$default_custompwdindex = 0;

 

## Rest API

$use_restapi = false;

 

## Extra messages

# They can also be defined in lang/ files

#$messages['passwordchangedextramessage'] = NULL;

#$messages['changehelpextramessage'] = NULL;

 

# Audit

#$audit_log_file = "/var/log/self-service-password/audit.log";

 

## Pre Hook

# Launch a prehook script before changing password.

# Script should return with 0, to allow password change.

# Any other exit code would abort password modification

#$prehook = "/usr/share/self-service-password/prehook.sh";

# Display prehook error

#$display_prehook_error = true;

# Encode passwords sent to prehook script as base64. This will prevent alteration of the passwords if set to true.

# To read the actual password in the prehook script, use a base64_decode function/tool

#$prehook_password_encodebase64 = false;

# Ignore prehook error. This will allow to change password even if prehook script fails.

#$ignore_prehook_error = true;

 

## Post Hook

# Launch a posthook script after successful password change

#$posthook = "/usr/share/self-service-password/posthook.sh";

# Display posthook error

#$display_posthook_error = true;

# Encode passwords sent to posthook script as base64. This will prevent alteration of the passwords if set to true.

# To read the actual password in the posthook script, use a base64_decode function/tool

#$posthook_password_encodebase64 = false;

 

# Force setlocale if your default PHP configuration is not correct

#setlocale(LC_CTYPE, "en_US.UTF-8");

 

# Hide some messages to not disclose sensitive information

$obscure_usernotfound_sendtoken = true;

$obscure_notfound_sendsms = true;

 

# HTTP Header name that may hold a login to preset in forms

#$header_name_preset_login="Auth-User";

 

# The name of an HTTP Header that may hold a reference to an extra config file to include.

#$header_name_extra_config="SSP-Extra-Config";

 

# Cache directory

$smarty_compile_dir = "/var/cache/self-service-password/templates_c";

$smarty_cache_dir = "/var/cache/self-service-password/cache";

 

# Smarty debug mode - will popup debug information on web interface

# and add many smarty debug messages in error logs

$smarty_debug = false;

 

## Custom Password Fields

# Change Custom Password Fields

$change_custompwdfield = array();

 

# Allow to override current settings with local configuration

if (file_exists (__DIR__ . '/config.inc.local.php')) {

    require_once __DIR__ . '/config.inc.local.php';

}

 

# Smarty

if (!defined("SMARTY")) {

    define("SMARTY", "/usr/share/php/Smarty/Smarty.class.php");

}

 

# Set preset login from HTTP header $header_name_preset_login

$presetLogin = "";

if (isset($header_name_preset_login)) {

    $presetLoginKey = "HTTP_".strtoupper(str_replace('-','_',$header_name_preset_login));

    if (array_key_exists($presetLoginKey, $_SERVER)) {

        $presetLogin = preg_replace("/[^a-zA-Z0-9-_@\.]+/", "", filter_var($_SERVER[$presetLoginKey], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH));

    }

}

 

# Allow to override current settings with an extra configuration file, whose reference is passed in HTTP_HEADER $header_name_extra_config

if (isset($header_name_extra_config)) {

    $extraConfigKey = "HTTP_".strtoupper(str_replace('-','_',$header_name_extra_config));

    if (array_key_exists($extraConfigKey, $_SERVER)) {

        $extraConfig = preg_replace("/[^a-zA-Z0-9-_]+/", "", filter_var($_SERVER[$extraConfigKey], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH));

        if (strlen($extraConfig) > 0 && file_exists (__DIR__ . "/config.inc.".$extraConfig.".php")) {

            require_once  __DIR__ . "/config.inc.".$extraConfig.".php";

        }

    }

}

文章细节

文章编号:
10
添加日期:
2025-05-07 21:45:40

相关文章