linux上各应用的权限认证使用pam机制(http://www.linux-pam.org/ https://github.com/linux-pam/linux-pam )。
这里需要一个支持otp验证的pam模块,可以直接用pam_script模块,通过编写脚本实现。
1. 安装pam_script
yum install pam_script或从https://github.com/jeroennijhof/pam_script编译安装。
这里直接yum安装,会安装下面几个文件:
[root@localhost root]# ll /lib64/security/pam_script.so
-rwxr-xr-x. 1 root root 15416 8月 23 2016 /lib64/security/pam_script.so
[root@localhost root]# ll /etc/pam_script*
-rwxr-xr-x. 1 root root 3836 2月 13 12:21 /etc/pam_script
lrwxrwxrwx. 1 root root 10 2月 10 14:59 /etc/pam_script_acct -> pam_script
lrwxrwxrwx. 1 root root 10 2月 10 14:59 /etc/pam_script_auth -> pam_script
lrwxrwxrwx. 1 root root 10 2月 10 14:59 /etc/pam_script_passwd -> pam_script
lrwxrwxrwx. 1 root root 10 2月 10 14:59 /etc/pam_script_ses_close -> pam_script
lrwxrwxrwx. 1 root root 10 2月 10 14:59 /etc/pam_script_ses_open -> pam_script
另外还会生成/etc/pam-script.d/目录。
2. 修改/etc/pam.d/sshd文件,最前面增加下面一行配置,表示使用pam_script认证,只要通过pam_script认证成功就通过,否则,则使用原来的方式验证。
auth sufficient pam_script.so
3. 参考pam_script的资料和源码可知,使用pam_script进行认证,会调用/etc/pam_script_auth,实际调用的是/etc/pam_script,进一步调用/etc/pam-script.d/*_auth。
/etc/pam-script.d/*_auth里能得到请求认证的用户名密码等,exit的值为1即为认证失败。
所以只需要在/etc/pam-script.d/下增加一个*_auth的文件,能以otp方式判断用户名密码是否正确,相应返回exit值即可。
文件:/etc/pam-script.d/totp_auth
#! /bin/sh
stamp=`/bin/date +'%Y-%m-%d %H:%M:%S'`
script=`basename $0`
LOGFILE=/tmp/pam-script.log
echo ============= >> $LOGFILE
echo $stamp $script $PAM_SERVICE $PAM_TYPE \
user=$PAM_USER ruser=$PAM_RUSER rhost=$PAM_RHOST \
tty=$PAM_TTY \
args=["$@"] \
>> $LOGFILE
echo check $PAM_USER >> $LOGFILE
#if [[ "x$PAM_AUTHTOK" == "123456" ]]; then
# exit 0
#fi
#exit 1
python3 /root/check_totp.py $PAM_USER $PAM_AUTHTOK
exit $?
这里进一步使用python脚本检查otp动态口令是否正确。
totp密钥配置在认证用户的个人目录下的.pam_totp.conf中,以一行一组k=v格式配置key、key_format、window、interval参数,文件所有者需要为认证用户,读写权限为0600。
文件:/root/check_totp.py
# -*- coding: utf-8 -*-
import time
import os,sys
import pwd,stat
from otp import *
# 日志
def logprint(*msg):
pass
#print(*msg)
# 读取配置,格式每行一组k=v
def get_config(path):
logprint('load config: ', path)
config = {}
with open(path, 'r') as f:
while True:
line = f.readline()
if not line:
break
if line.startswith('#'):
continue
idx = line.find("=")
if idx >= 0:
a = line[0:idx]
b = line[idx+1:]
config[a.strip()] = b.strip()
logprint('config: ', config)
return config
# 检查totp口令
def check(username, token):
if not username or not token:
return 1
#### 获取用户
uu = pwd.getpwnam(username)
if not uu:
logprint('用户没找到')
return 1
#### 获取路径
if not uu.pw_dir:
logprint('用户目录没找到')
return 1
path = uu.pw_dir + '/.pam_totp.conf'
### 获取totp配置文件信息,要求文件权限为0600,文件所属为当前认证用户
fst = os.stat(path)
mode = fst.st_mode
if not stat.S_ISREG(mode):
logprint('totp配置文件不是普通文件')
return 1
if fst.st_size <= 0 or fst.st_size > 1024 * 10:
logprint('totp配置文件为空或文件大小超过10KB')
return 1
if stat.S_IMODE(mode) != (0o600):
logprint('totp配置文件权限不是0600')
return 1
uid = fst.st_uid
if fst.st_uid != uu.pw_uid:
logprint('totp配置文件所有者不是当前认证用户')
return 1
#### 获取配置
config = get_config(path)
key = config.get('key')
key_format = config.get('key_format')
window = config.get('window')
interval = config.get('interval')
if not key:
logprint('otp参数项key为空')
return 1
if not key_format:
key_format = "string"
if not window:
window = 3
if not interval:
interval = 30
#### 验证totp
t = TOTP(None, int(window), int(interval))
t.setSecret(key, key_format)
if t.verify(token) != True:
logprint('totp检测失败')
return 1
else:
logprint('totp检查成功')
return 0
def main(argv):
#time.sleep(3)
if len(argv) < 3:
logprint('参数个数错误')
exit(1)
username = argv[1]
token = argv[2]
ret = check(username, token)
logprint('检测结果:', ret)
exit(ret)
if __name__ == "__main__":
main(sys.argv)
文件:/root/otp.py
# 参看https://gitee.com/superzlc/otp
文件:.pam_totp.conf
key=202102191234567890qwertyuiop
key_format=string
window=3
interval=30
4. 其他
totp动态口令,可以用微信小程序“动态口令”生成。
本文介绍了如何在Linux上通过PAM机制和pam_script模块结合OTP动态口令实现SSH登录验证。步骤包括安装pam_script、修改sshd配置、创建认证脚本以及使用Python脚本验证OTP口令。

456

被折叠的 条评论
为什么被折叠?



