前言

  Jumpserver 作为国内流行的开源堡垒机,很多公司都在尝试使用,同时 Jumpserver 为了契合众多公司的用户认证,也提供了 LDAP 的用户认证方式,作为 Jumpserver 的用户,大家可能知道了 Jumpserver 的 LDAP 认证方式,仅是作为 登录Jumpserver Web UI、登录 Jumpserver 终端(COCO) 的用户认证,进入 Jumpserver 终端(COCO)后,再而跳到目标主机,却需要使用Jumpserver 创建的系统用户,也就是 登录Jumpserver 和 Jumpserver登录目标主机 是需要两个完全没有关系的用户,对于很多基于LDAP用户登录主机的场景,Jumpserver 这种双用户认证概念显得有点鸡肋,既然接入了 LDAP, 我们希望做到 登录Jumpserver 和 Jumpserver跳转主机都使用 LDAP 完成认证登录,带着这一想法,便开始了对 Jumpserver 终端核心 COCO 进行了部分修改。

COCO前后对比

注:LDAP 用户登录Jumpserver coco、选择登录主机后,直接使用登录coco 的用户进行登录主机,取消了选择系统用户的步骤。

详细流程

注1:用户名密码登录 Jumpserver 时,COCO 处理线程存储用户名密码,用于SSH 连接目标主机;

注2:公钥登录 Jumpserver 时,COCO 处理线程存储用户名和空密码,SSH 连接目标主机时,根据用户名从COCO本地查找密码,有则使用,无则提示输出密码;

注3:SSH 连接认证失败可尝试输入密码尝试三次,认证成功则向本地存储最近一次连接成功的加密密码。

代码实现

修改 coco/models.py,添加 password 参数

 class Request:
def __init__(self, addr):
self.type = []
self.meta = {"width": 80, "height": 24}
self.user = None
self.password = '' # @ 周旺
self.addr = addr
self.remote_ip = self.addr[0]
self.change_size_event = threading.Event()
self.date_start = datetime.datetime.now() class Client:
def __init__(self, chan, request):
self.chan = chan
self.request = request
self.user = request.user
self.password = request.password # @ 周旺
self.addr = request.addr

修改 coco/interface.py, 赋值 request.password 

 class SSHInterface(paramiko.ServerInterface):
def validate_auth(self, username, password="", public_key=""):
info = app_service.authenticate(
username, password=password, public_key=public_key,
remote_addr=self.request.remote_ip
)
user = info.get('user', None)
if user:
self.request.user = user
self.request.password = password # request password 赋值 @ 周旺
self.info = info seed = info.get('seed', None)
token = info.get('token', None)
if seed and not token:
self.otp_auth = True return user

修改 coco/interactive.py 

 class InteractiveServer:
def display_search_result(self):
sort_by = current_app.config["ASSET_LIST_SORT_BY"]
self.search_result = sort_assets(self.search_result, sort_by)
fake_data = [_("ID"), _("Hostname"), _("IP"), _("LoginAs")]
id_length = max(len(str(len(self.search_result))), 4)
hostname_length = item_max_length(self.search_result, 15,
key=lambda x: x.hostname)
sysuser_length = item_max_length(self.search_result,
key=lambda x: x.system_users_name_list)
size_list = [id_length, hostname_length, 16, sysuser_length]
header_without_comment = format_with_zh(size_list, *fake_data)
comment_length = max(
self.request.meta["width"] -
size_of_str_with_zh(header_without_comment) - 1,
2
)
size_list.append(comment_length)
fake_data.append(_("Comment"))
self.client.send(wr(title(format_with_zh(size_list, *fake_data))))
for index, asset in enumerate(self.search_result, 1):
# data = [ # 注释主机显示列表 @ 周旺
# index, asset.hostname, asset.ip,
# asset.system_users_name_list, asset.comment
# ] data = [ # 主机显示列表 @ 周旺
index, asset.hostname, asset.ip,
self.client.user.username, asset.comment
]

self.client.send(wr(format_with_zh(size_list, *data)))
self.client.send(wr(_("总共: {} 匹配: {}").format(
len(self.assets), len(self.search_result)), before=1)
) def proxy(self, asset):
# system_user = self.choose_system_user(asset.system_users_granted) # 注释 @ 周旺
# if system_user is None:
# self.client.send(_("没有系统用户"))
# return
system_user = self.client.user # 修改系统用户为登录用户 @ 周旺 注: 仍保持system_user 变量名,后面所有 system_user 皆是登录用户
password = self.client.password # 密码 @ 周旺 --> by client -> by request

forwarder = ProxyServer(self.client)
forwarder.proxy(asset, system_user, password) # password @ 周旺

修改 coco/proxy.py

 class ProxyServer:
def proxy(self, asset, system_user, password=''): # 添加 password 参数 @ 周旺
#self.get_system_user_auth(system_user) # 注释 @ 周旺 if not password: # 添加46-74行 @ 周旺
with open('/opt/pwd/%s.pwd' % system_user.username, 'ab+') as pwd: # 查找本地缓存密码
pwd.seek(0)
try:
password = base64.b64decode(pwd.read().strip()).decode().strip()
# password = pwd.read().strip()
except:
password = '' if not password:
prompt = "{}@{} password: ".format(system_user.username, asset.ip)
password = net_input(self.client, prompt=prompt, sensitive=True) for n in range(4):
self.connecting = True
self.send_connecting_message(asset, system_user)
self.server = self.get_server_conn(asset, system_user, password)
if self.server:
with open('/opt/pwd/%s.pwd' % system_user.username, 'wb') as pwd: # 保存最后一次的正确密码
pwd.write(base64.b64encode(password.encode(encoding='utf-8')))
#pwd.write(password)
break if n < 3:
prompt = "{}@{} password({}/3): ".format(system_user.username, asset.ip, n+1)
password = net_input(self.client, prompt=prompt, sensitive=True)
else:
return False # self.send_connecting_message(asset, system_user) # 注释 @ 周旺
# self.server = self.get_server_conn(asset, system_user, password)
command_recorder = current_app.new_command_recorder()
replay_recorder = current_app.new_replay_recorder()
session = Session(
self.client, self.server,
command_recorder=command_recorder,
replay_recorder=replay_recorder,
)
current_app.add_session(session)
self.watch_win_size_change_async()
session.bridge()
self.stop_event.set()
self.end_watch_win_size_change()
current_app.remove_session(session) def get_server_conn(self, asset, system_user, password=''): # 添加 password 参数 @ 周旺
logger.info("Connect to {}".format(asset.hostname))
# if not self.validate_permission(asset, system_user): # 注释 @ 周旺
# self.client.send(warning('No permission'))
# return None
# if True:
# server = self.get_ssh_server_conn(asset, system_user)
# else:
# server = self.get_ssh_server_conn(asset, system_user)

server = self.get_ssh_server_conn(asset, system_user, password) # password @ 周旺
return server def get_ssh_server_conn(self, asset, system_user, password=''): # 添加 password 参数 @ 周旺
request = self.client.request
term = request.meta.get('term', 'xterm')
width = request.meta.get('width', 80)
height = request.meta.get('height', 24)
ssh = SSHConnection()
chan, sock, msg = ssh.get_channel(
asset, system_user, term=term, width=width, height=height, password=password) # password @ 周旺
if not chan:
self.client.send(warning(wr(msg, before=1, after=0)))
server = None
else:
server = Server(chan, sock, asset, system_user)
self.connecting = False
self.client.send(b'\r\n')
return server def send_connecting_message(self, asset, system_user):
def func():
delay = 0.0
self.client.send('Connecting to {}@{} {:.1f}'.format(
system_user.username, asset.ip, delay) # 修改为 用户名,ip地址 @ 周旺
)
while self.connecting and delay < TIMEOUT:
self.client.send('\x08\x08\x08{:.1f}'.format(delay).encode())
time.sleep(0.1)
delay += 0.1
thread = threading.Thread(target=func)
thread.start()

 修改 coco/connection.py

 class SSHConnection:
def get_ssh_client(self, asset, system_user, password=''): # 添加 password 参数 @ 周旺
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
sock = None # if not system_user.password and not system_user.private_key: # 注释 @ 周旺
# self.get_system_user_auth(system_user)
if asset.domain:
sock = self.get_proxy_sock_v2(asset)
try:
ssh.connect(
asset.ip, port=asset.port, username=system_user.username,
#password=system_user.password, pkey=system_user.private_key, # 注释 @ 周旺
password=password, # password @ 周旺

timeout=TIMEOUT, compress=True, auth_timeout=TIMEOUT,
look_for_keys=False, sock=sock
)
except (paramiko.AuthenticationException,
paramiko.BadAuthenticationType,
SSHException) as e:
# password_short = "None" # 注释 @ 周旺 注:感觉没啥用
# key_fingerprint = "None"
# if system_user.password:
# password_short = system_user.password[:5] + \
# (len(system_user.password) - 5) * '*'
# if system_user.private_key:
# key_fingerprint = get_private_key_fingerprint(
# system_user.private_key
# )
#
# logger.error("Connect {}@{}:{} auth failed, password: \
# {}, key: {}".format(
# system_user.username, asset.ip, asset.port,
# password_short, key_fingerprint,
# ))

return None, None, str(e)
except (socket.error, TimeoutError) as e:
return None, None, str(e)
return ssh, sock, None def get_channel(self, asset, system_user, term="xterm", width=80, height=24, password=''): # password 参数 @ 周旺
ssh, sock, msg = self.get_ssh_client(asset, system_user, password) # password @ 周旺
if ssh:
chan = ssh.invoke_shell(term, width=width, height=height)
return chan, sock, None
else:
return None, sock, msg

效果展示

后记

  以上仅适用 jumpserver 终端命令行,没有涉及对jumpserver web 终端及SFTP的修改。

谢 jumpserver 团队:http://www.jumpserver.org/

解决 Jumpserver coco 使用登录用户(ldap)进行SSH连接目标主机,忽略系统用户的更多相关文章

  1. Linux root用户不能通过SSH连接的问题

    http://jingyan.baidu.com/article/fd8044fad48fc95031137a85.html 最近在虚拟机安装Ubuntu之后,通过普通ssh远程连接的时候明明输入了正 ...

  2. root用户无法通过ssh连接Linux系统

    ssh协议为了安全,有些版本默认禁止root用户的登陆 cd /etc/ssh 编辑sshd_config文件 cat sshd_config | grep PermitRootLogin Permi ...

  3. Aamazon Web Service EC2 Ubuntu 新建用户而且用ssh连接host

    本文參照 http://docs.aws.amazon.com/zh_cn/AWSEC2/latest/UserGuide/managing-users.html http://docs.aws.am ...

  4. jumpserver 用户,系统用户和管理用户 普通用户和特权用户 区别

    前言 现在很多公司都有在用Jumpserver跳板机 有很多人一直对jumpserver的各种用户还不是很了解 当你了解了这几个概念了之后,就能更好的灵活的运用到分配权限当中去. 下面我们一个一个的说 ...

  5. ansible不配ssh连接,用户密码登录

    ansible 不配ssh免密链接,直接用ssh用户密码连接,要先装sshpass. 否则会报错: sshpass安装 sshpass下载地址:http://sourceforge.net/proje ...

  6. mysql 系统用户最大文件打开数限制

    纸上得来终觉浅,绝知此事多宕机...记录一下自己很蠢的一次故障处理过程. 上周的时候,一个刚上线的系统又开始反映登不上了,因为最近这个系统也老是出现这个问题,开发也一直在找问题中,所以也没太在意.于是 ...

  7. linux远程登录(Telnet、SSH)

    系统:RHEL 5.5 64位,使用CentOS的yum源并作更新处理 参考书目<Linux兵书>/电子工业出版社/刘丽霞,细节之处稍有变动. 一.Telnet(远程登录推荐SSH) 1. ...

  8. vsftpd系统用户配置详解

    1.安装yum -y install pam pam-devel db4 de4-devel db4-uitls db4-tclyum -y install vsftpd 新建vsftpd系统用户:u ...

  9. ssh连接服务器失败解决记录

    故障:db2inst1用户无法通过ssh连接数据库服务器. 但是root用户可以连接,连接后su – db2inst1用户报错: su: cannot set user id: Resource te ...

随机推荐

  1. Freemarke

    本文介绍了freemarker的集成.FTL指令.内建函数.运算符等常用操作. 1.为什么要使用网页静态化技术 网页静态化解决方案在实际开发中运用比较多,例如新闻网站,门户网站中的新闻频道或者是文章类 ...

  2. matlab学习笔记8 基本绘图命令-初级二维绘图/交互式绘图

    一起来学matlab-matlab学习笔记8 基本绘图命令_5 初级二维绘图/交互式绘图 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考书籍 <matlab 程序设计与综合应用&g ...

  3. [LeetCode] 149. Max Points on a Line 共线点个数

    Given n points on a 2D plane, find the maximum number of points that lie on the same straight line. ...

  4. 使用consul实现分布式服务注册和发现--redis篇

    安装consul client consul 客户端检脚本 ====================================================================== ...

  5. 【视频开发】CximageMat 、CximagelplImage 以及 lplImageMat的转换、像素位深度

    1.传统的lplImage * -------> Mat格式 IplImage* img = cvLoadImage("greatwave.png", 1); Mat mtx ...

  6. Docker虚拟化

    1. Docker虚拟化特点 跟传统VM比较具有如下优点: 操作启动快 运行时的性能可以获取极大提升,管理操作(启动,停止,开始,重启等等) 都是以秒或毫秒为单位的. 轻量级虚拟化 你会拥有足够的“操 ...

  7. spring security实现记住我下次自动登录功能

    目录 spring security实现记住我下次自动登录功能 一.原理分析 二.实现方式 2.1 简单实现方式 2.2 数据库实现方式 三.区分是密码登录还是rememberme登录 spring ...

  8. Find the median(线段树+离散化)(2019牛客暑期多校训练营(第七场))

    题目出处:Find the median 示例: 输入: 53 1 4 1 5 92 7 1 8 2 9 输出:3 4 5 4 5 说明:L = [3, 2 ,4, 1, 7],R = [4, 8, ...

  9. xorm -Alias,Asc,Desc方法实例

    Alias(string)给Table设定一个别名 package main import ( "fmt" _ "github.com/go-sql-driver/mys ...

  10. 单例DCL模式

    单例模式可以保证系统中一个类只有一个实例.即一个类只有一个对象实例. 一般写法 public class DCLSingle { public static DCLSingle instance= n ...