运维开发之堡垒机(Fortress Machine for Operation and Development)

news2024/9/21 7:21:26

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。

推荐:Linux运维老纪的首页,持续学习,不断总结,共同进步,活到老学到老
导航剑指大厂系列:全面总结 运维核心技术:系统基础、数据库、网路技术、系统安全、自动化运维、容器技术、监控工具、脚本编程、云服务等。
常用运维工具系列:常用的运维开发工具, zabbix、nagios、docker、k8s、puppet、ansible等
数据库系列:详细总结了常用数据库 mysql、Redis、MongoDB、oracle 技术点,以及工作中遇到的 mysql 问题等
懒人运维系列:总结好用的命令,解放双手不香吗?能用一个命令完成绝不用两个操作
数据结构与算法系列:总结数据结构和算法,不同类型针对性训练,提升编程思维,剑指大厂
非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。💝💝💝 ✨✨ 欢迎订阅本专栏 ✨✨

开发运维之堡垒机

技能目标:

- 了解堡垒机特点及架构
- 理解堡垒机应用场景
- 掌握使用 Python 编写堡垒机的方法

8.1 案例分析

8.1.1 案例概述

网络安全是互联网企业重要关注的一个问题,随着信息化步伐加快而变得越来越重要,
对网络安全的认知和防范也要重视起来。创鑫公司的 IT 系统出现问题,当地的 IT 运维人员
无法解决时,就会向总部发起求助。而此时,总部的技术工程师根本无法获悉最原始的问题,
因为原来的问题在经过分部的运维工程师的操作后,已经面目全非,还可能引入了新的问题,
整个过程没有记录,没有管控,找不到解决问题的线索。所以总部工程师迫切希望知道,从
一开始问题的表象,到分支机构的运维人员的运维操作,都是什么情况。数据实际是存储在
服务器上的,因此,保障服务器安全性是最后一道关口。本章节将从服务器入口增加一道防
御手段 -- 堡垒机。

8.1.2 案例前置知识点

1.什么是堡垒机

运维堡垒机的理念起源于跳板机。在早期,有一定运维规模的公司为了对运维人员的远
程登录进行集中管理,会单独在一台服务器上部署跳板机。运维人员在维护服务器时,先登
录到这台跳板机上,然后再从这台服务器再登录到目标服务器上进行维护。

2.堡垒机特点

任何操作都有记录,有依据可追溯;
根据系统账户区分工作角色,人员流动不影响;
约束执行命令,避免人为失误。

3. 堡垒机主要作用

(1)权限管理

当公司服务器越来越多后,操作这些服务器的人肯定就不只是一个运维人员,同时也可
能包括多个开发人员。目前,不少公司现状是多个运维共用一个 root 账户和密码,这就放
大了运维权限和泄露风险。如果某个运维心情不好随时能把业务停止,甚至删除数据,很难
追查谁干的。
(2)审计管理
审计是把用户的所有操作记录下来,以备日后追查问题提供依据。
针对上面问题,使用堡垒机很简单就可以实现:
即使多个运维人员共享一个 root 账户,但依然能分辨谁再哪台服务器上操作了哪些命
令,因为每个人登录堡垒机的账户是独有的。
对登录的堡垒机账户以基准做记录。
4.堡垒机架构
如图 8.1 所示,堡垒机在服务器前面增加一层。
8.1 堡垒机架构
堡垒机的主要作用是权限控制和用户行为审计,例如一个住宅小区,小区里的房屋就是
不同的业务服务器,外部人员要想访问业主,就必须经过小区门口安保人员的授权,他们就
像堡垒机一样;而进入小区的人的所有行为和足迹都会被摄像头监控和记录下来。一旦发生
犯罪事件,小区安保人员就可以通过这些监控记录追踪到人。
要想使用堡垒机也要满足以下条件:
回收所有人员已有的访问权限,做到除了堡垒机管理员,没有人知道业务服务器的登录
权限。
网络上限制所有人员只能通过堡垒机才能访问业务服务器。
服务器创建共享账户。

8.1.3 案例环境

1.本案例环境

本案例环境如表 8-1 所示。
8-1 创建并管理堡垒机案例环境
主机操作系统主机名/IP 地址主要软件及版本
堡垒机CentOS 7.3BLJ /192.168.0.10MySQLPython3.6
服务器CentOS 7.3Server1 /192.168.0.11/
服务器CentOS 7.3Server1 /192.168.0.12/

具体的拓扑如图 8.2 所示。

图 8.2 案例拓扑

2.案例需求

1 )所有的用户操作日志保留在 MySQL 数据库中。
2 )用户人员在维护过程中,首先连接到堡垒机,然后选择要访问的服务器(只能查
看自己权限内的服务器),不需要再输入目标机器的访问密码。
3 )每个用户对不同的服务器有不同的访问权限,例如:对 192.168.0.10 有普通用户
权限,对 192.168.0.11 sudo 权限,对 192.168.0.12 没有任何权限。
4 )管理员登录堡垒机,可查看其它账户操作记录。
5 )确保堡垒机管理员除外,其他用户只提供堡垒机登录跳转功能。

3.实现思路

1 )通过 paramiko 模块远程登录服务器交互执行命令,通过堡垒机权限检查。
2 )通过 sqlalchemy 模块操作 MySQL 数据库存放操作记录。
3 )利用 Python 数据类型处理数据。

8.2 案例实施

8.2.1 设计表结构

本案例需要在 MySQL 实例中创建 test 数据库, test 数据库中有创建 user 表(用户信
息表)、 host 表(主机信息表)和 cmd_log 表(工作日志表)。三张表没有主外键关系, user
表主要功能是保存授权用户对应的授权主机, host 表用来保存被管理主机 ip 对应的端口号,
cmd_log 表是用于保存授权用户在操作授权主机时所产生的记录比如操作命令、时间等。
8-2 user 表的结构,
序号字段名称字段说明类型长度备注
1id编号int自动编号,主键
2username用户名varchar50非空
3hosts授权的主机text
8-3 host
序号字段名称字段说明类型长度备注
1id编号自动编号,主键
2ipIP 地址varchar50非空

表 8-4 cmd_log

序号字段名称字段说明类型长度备注
1id编号int自动编号,主键
2login_user登录用户名varchar16feikong
3share_user目标主机共享账户varchar16非空
4
server_ip
目标主机 IP 地址
varchar50非空
5
shell_command
用户操作的 Shell 命令
varchar255非空
6
datetime
操作命令日期时间
varchar30非空

8.2.2 功能模块规划

实现本案例需求,需要规划 Python 目录结构:
├── bin
│ ├── __init__.py
│ ├── manager_menu.py
│ └── user_menu.py
├── config
│ ├── config.py
│ ├── __init__.py
└── modules
├── db_conn.py
├── __init__.py
├── interactive.py
├── manager_handle.py
├── ssh_login.py
└── table_init.py
在每个目录下有一个空的 __init__.py 文件,说明这个目录是一个包,可导入。 __init__.py
文件定义了包的属性和方法。其实它可以什么也不定义,它可以只是一个空文件,但是必须
存在。如果 __init__.py 不存在,这个目录就仅仅是一个目录,而不是一个包,它就不能被
导入或者包含其它的模块和嵌套包。
8-5 Python 工程目录结构中所包含的模块
目录
模块名
描述
描述
bin
manager_menu
管理菜单
bin
user_menu
用户菜单
config
confg
数据库配置文件
modules
table_init.py
数据库表创建与映射
modules
db_conn
数据库连接
modules
interactive
SSH 交互式
modules
ssh_login
SSH 登录
modules
manager_handle
管理菜单处理程序

8.2.3 功能实现

通常,开发项目先从依赖的关系写,例如 : 用到数据库,需要先将数据库相关逻辑弄清
楚,方便写功能时调用调试。
本案例编写模块顺序: config -> modules -> bin

1. 数据库配置模块

[root@BLJ baoleiji]# vim config/config.py
database='test'
ip='127.0.0.1'
port=3306
user='root'
password='123456'
engine_param = 'mysql+pymysql://%s:%s@%s:%d/%s?charset=utf8' %(user, password, ip, port,
database)

2. 数据库连接模块

[root@BLJ baoleiji]# vim modules/db_conn.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from modules.table_init import *
from config.config import engine_param
engine=create_engine(engine_param)
# 创建会话
Session = sessionmaker(bind=engine)
Session = sessionmaker()
Session.configure(bind=engine)
session = Session()

3. 创建表并映射模块

[root@BLJ baoleiji]# vim modules/table_init.py
#!/usr/local/bin/python3.6
import os,sys
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Text
from config import config
# 创建对象的基类
Base = declarative_base()
# 定义 User 对象
class User(Base):
__tablename__ = 'user'
id = Column(Integer, autoincrement=True, primary_key=True)
username = Column(String(50))
hosts = Column(Text, default="{'sudo': [], 'no-sudo': []}")
class Host(Base):
__tablename__ = 'host'
id = Column(Integer, autoincrement=True, primary_key=True)
ip = Column(String(50))
port = Column(Integer, default=22)
class Cmd_log(Base):
__tablename__ = 'cmd_log'
id = Column(Integer, autoincrement=True, primary_key=True)
login_user = Column(String(16))
share_user = Column(String(16))
server_ip = Column(String(50))
shell_command = Column(String(255))
datetime = Column(String(30))
if __name__ == "__main__":
engine=create_engine(config.engine_param)
Base.metadata.create_all(engine) # 创建表结构

4. SSH 交互式处理和登录模块

[root@BLJ baoleiji]# vim modules/interactive.py
import os,sys
#BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
#sys.path.append(BASE_DIR)
import socket
import termios
import tty
from datetime import datetime
from modules.table_init import *
from modules.db_conn import session
def interactive_shell(channel, login_user, share_user, server_ip):
import select
oldtty = termios.tcgetattr(sys.stdin)
try:
tty.setraw(sys.stdin.fileno())
tty.setcbreak(sys.stdin.fileno())
channel.settimeout(0.0)
cmd = ''
tab_key = False
while True:
r, w, e = select.select([channel, sys.stdin], [], [])
if channel in r:
try:
x = channel.recv(1024).decode()
if tab_key:
if x not in ('\t','\r\n'):
cmd += x
tab_key = False
if len(x) == 0:
sys.stdout.write('\r\n*** EOF\r\n')
break
sys.stdout.write(x)
sys.stdout.flush()
except socket.timeout:
pass
if sys.stdin in r:
x = sys.stdin.read(1)
if '\r' != x:
cmd +=x # 输入字符不包含回车,则命令还未输入完成,包含回车且输入字
符长度大于 0 ,则记录日志
if '\r' == x and len(cmd) > 0:
add_log = Cmd_log(login_user=login_user,
share_user=share_user,
server_ip=server_ip,
shell_command=cmd,
datetime=datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
session.add(add_log)
session.commit()
cmd = ''
if '\t' == x:
tab_key = True
channel.send(x)
finally:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
[root@BLJ baoleiji]# vim modules/ssh_login.py
import os,sys
#BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
#sys.path.append(BASE_DIR)
import paramiko
from modules.interactive import interactive_shell
def ssh_login(login_user, share_user, server_ip, server_port, password):
# 建立 ssh 连接
ssh=paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(server_ip, server_port, share_user, password)
# 建立交互式 shell 连接
channel=ssh.invoke_shell()
# 建立交互式管道
interactive_shell(channel, login_user, share_user, server_ip)
# 关闭连接
channel.close()
ssh.close()
if __name__ == "__main__":
# 手动执行测试
login_user = "xiaoming"
share_user = "user2"
password = "123456"
server_ip = "192.168.0.11"
server_port = 22
ssh_login(login_user, share_user, server_ip, server_port, password)

5. 管理接口模块

[root@BLJ baoleiji]# vim bin/manager_menu.py
#!/usr/local/bin/python3.6
import os,sys,io
# 添加当前目录到 Python 模块搜索路径
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
# 改变标准输出的默认编码,解决中文编码错误 :UnicodeEncodeError
sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf-8')
from modules.manager_handle import *
choices = {
"1": " 添加用户名 ",
"2": " 授权用户名访问主机 ",
"3": " 添加主机 ",
"4": " 删除主机 ",
"5": " 查看日志 ",
"6": " 退出 "
}
while True:
print(" 菜单 :")
for k, v in choices.items():
print(k + ". " + v)
choice = input(" 请输入编号 : ")
choice = str(choice).strip()
if choice in choices.keys():
if choice == "1":
add_user()
elif choice == "2":
authorized()
elif choice == "3":
add_host()
elif choice == "4":
del_host()
elif choice == "5":
view_log()
elif choice == "6":
quit()
else:
print(" 编号错误,请重新输入 !")
continue
[root@BLJ baoleiji]# vim modules/manager_handle.py
#!/usr/local/bin/python3.6
#import os,sys
#BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
#sys.path.append(BASE_DIR)
import re
from modules.table_init import *
from modules.db_conn import session
def add_user():
username = input(" 请输入要添加的用户名 : ").strip()
user = User(username=username)
session.add(user)
session.commit()
print(" 添加成功 .")
def authorized():
n = 1
while True:
username = input(" 请输入要授权的用户名 : ").strip()
user = session.query(User).filter(User.username==username).first()
if user:
break
else:
if n == 3:
print(" 错误次数达到 3 , 退出 !")
return
print(" 用户名不存在 , 请重新输入 !")
n += 1
continue
n = 1
while True:
ip = input(" 请输入要授权的主机 IP 地址 : ").strip()
host = session.query(Host).filter(Host.ip==ip).first()
if host:
break
else:
if n == 3:
print(" 错误次数达到 3 , 退出 !")
return
print(" 主机 IP 地址不存在 , 请重新输入 !")
n += 1
continue
n = 1
while True:
sudo = input(" 需要 sudo root 权限 ?(Y/N): ").strip()
if sudo in ['y','Y', 'n', 'N']:
break
else:
if n == 3:
print(" 错误次数达到 3 , 退出 !")
return
print(" 输入错误 , 请重新输入 !")
n += 1
continue
hosts = eval(user.hosts)
if sudo in ['y', 'y']:
hosts['sudo'].append(ip)
elif sudo in ['n', 'N']:
hosts['no-sudo'].append(ip)
user = session.query(User).filter(User.username==username).update({"hosts": str(hosts)})
session.commit()
print(" 添加成功 .")
def add_host():
n = 1
while True:
ip = input(" 请输入要添加的 IP: ").strip()
ip_match = re.match('^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$', ip)
if ip_match:
host = Host(ip=ip)
session.add(host)
session.commit()
print(" 添加成功 .")
break
else:
if n == 3:
print(" 错误次数达到 3 , 退出 !")
break
print("IP 地址错误 , 请重新输入 !")
n +=1
continue
def del_host():
n = 1
while True:
ip = input(" 请输入要删除的 IP 地址 : ").strip()
host = session.query(Host).filter(Host.ip==ip).first()
if host:
session.delete(host)
session.commit()
print(" 删除成功 .")
break
else:
if n == 3:
print(" 错误次数达到 3 , 退出 !")
break
print("IP 地址不存在 , 请重新输入 !")
n +=1
continue
def view_log():
logs = session.query(Cmd_log).all()
if logs:
for i in logs:
print("login_user: %s | share_user: %s | server_ip: %s | shell_command: %s |
datetime: %s"
%(i.login_user, i.share_user, i.server_ip, i.shell_command, i.datetime))
else:
print(" 没有日志记录 .")
def quit():
sys.exit()

6. 用户接口模块

[root@BLJ baoleiji]# vim bin/user_menu.py
#!/usr/local/bin/python3.6
import os,sys,io
# 添加当前目录到 Python 模块搜索路径
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
# 改变标准输出的默认编码,解决中文编码错误 :UnicodeEncodeError
sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='utf-8')
from modules.table_init import *
from modules.db_conn import session
from modules.ssh_login import ssh_login
import getpass
login_user = getpass.getuser()
server_port = 22
share_user = {'sudo': 'admin', 'no-sudo': 'user', 'password': '123456'}
user_host = session.query(User).filter(User.username==login_user).first()
# 如果 root 登陆说明是管理员,则退出不执行
if login_user == "root":
sys.exit()
if not user_host:
print(" 数据库未记录该登录用户 , 请联系管理员 !")
sys.exit()
sudo = set(eval(user_host.hosts)['sudo'])
no_sudo = set(eval(user_host.hosts)['no-sudo'])
def display_menu():
print(" 主机列表 :")
if not sudo and not no_sudo:
print(" 没有授权的主机 !")
for i in sudo:
print(i + " sudo")
for i in no_sudo:
print(i)
n = 1
while True:
display_menu()
server_ip = input(str(" 请输入要登录的主机 IP 地址 : "))
if server_ip in sudo:
ssh_login(login_user,share_user['sudo'],server_ip,server_port,share_user['password'])
elif server_ip in no_sudo:
ssh_login(login_user,share_user['no-sudo'],server_ip,server_port,share_user['password'])
else:
if n == 3:
print(" 错误次数达到 3 , 退出 !")
os.system("exit")
print("IP 地址错误,请重新输入 !")
n +=1
Continue

8.2.4 部署

部署之前需要先将程序放到 /opt 目录下。

1. 安装模块

[root@BLJ baoleiji]# pip3 install paramiko pymysql sqlalchemy

2. 配置数据库信息

[root@BLJ baoleiji]# vim config/config.py
database='test'
ip='127.0.0.1'
port=3306
user='root'
password='123456'
engine_param = 'mysql+pymysql://%s:%s@%s:%d/%s?charset=utf8' %(user, password,
ip, port, database)
把对应字段值修改为自己的数据库信息。

3. 初始化数据库表

下面是创建 test 数据库的命令:
[root@BLJ baoleiji]# mysql –uroot -p
mysql> create database test;
初始化数据库表:
[root@BLJ baoleiji]# python3 modules/table_init.py
执行完成后,进数据库核实下是否创建如下表:
[root@BLJ baoleiji]# mysql –uroot -p
mysql> use test;
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| cmd_log
|
| host
|
| user
|
+----------------+
3 rows in set (0.00 sec)

4. 被管理主机创建登录账号

Server1 Server2 主机中分别创建 admin user 账号,该账号用于跳板机 SSH
到被管理主机登录时的账号。下面以 Server1 为例:
[root@server1 ~]# useradd admin
[root@server1 ~]# passwd admin
[root@server1 ~]# useradd user
[root@server1 ~]# passwd user
密码均设置为 123456
这两个账号配置在程序里:
[root@BLJ baoleiji]# cat bin/user_menu.py
......
share_user = {'sudo': 'admin', no-sudo: 'user', 'password': '123456'}
......

5. 配置用户登录自动执行用户菜单程序

[root@BLJ baoleiji]# vim /etc/profile.d/baoleiji.sh
python3 /opt/baoleiji/bin/user_menu.py
注意:在 Linux 中,通过配置用户家目录下的 .bashrc 文件,实现自动执行脚本。

6. 将被管理主机 IP 添加到堡垒机数据库

[root@BLJ baoleiji]# python3 bin/manager_menu.py
菜单 :
1. 添加用户名
2. 授权用户名访问主机
3. 添加主机
4. 删除主机
5. 查看日志
6. 退出
请输入编号 : 3
请输入要添加的 IP: 192.168.0.11
添加成功 .
菜单 :
1. 添加用户名
2. 授权用户名访问主机
3. 添加主机
4. 删除主机
5. 查看日志
6. 退出
请输入编号 : 3
请输入要添加的 IP: 192.168.0.12
添加成功 .
8.2.5 测试
工作流程:
(1) 管理员为用户在服务器上创建系统账号与添加此账号到堡垒机数据库。
(2) 用户登录堡垒机,输入系统账户与密码,认证通过后显示可访问的主机列表。
(3) 用户输入主机 IP 自动登录。
(4) 用户操作记录到数据库。
下面是测试过程:
1. 管理员使用 root 账户登录到堡垒机为同事创建系统账号
[root@BLJ ~]# useradd zhangsan
[root@BLJ ~]# passwd zhangsan
[root@BLJ ~]# useradd lisi
[root@BLJ ~]# passwd lisi
2. 添加系统账号到堡垒机数据库
[root@BLJ baoleiji]# python3 bin/manager_menu.py
菜单 :
1. 添加用户名
2. 授权用户名访问主机
3. 添加主机
4. 删除主机
5. 查看日志
6. 退出
请输入编号 : 1
请输入要添加的用户名 : zhangsan
添加成功 .
菜单 :
1. 添加用户名
2. 授权用户名访问主机
3. 添加主机
4. 删除主机
5. 查看日志
6. 退出
请输入编号 : 1
请输入要添加的用户名 : lisi
添加成功 .
3. 授权用户名访问主机
[root@BLJ baoleiji]# python3 bin/manager_menu.py
菜单 :
1. 添加用户名
2. 授权用户名访问主机
3. 添加主机
4. 删除主机
5. 查看日志
6. 退出
请输入编号 : 2
请输入要授权的用户名 : zhangsan
请输入要授权的主机 IP 地址 : 192.168.0.11
需要 sudo root 权限 ?(Y/N): y
添加成功 .
菜单 :
1. 添加用户名
2. 授权用户名访问主机
3. 添加主机
4. 删除主机
5. 查看日志
6. 退出
请输入编号 : 2
请输入要授权的用户名 : zhangsan
请输入要授权的主机 IP 地址 : 192.168.0.12
需要 sudo root 权限 ?(Y/N): n
添加成功 .
菜单 :
1. 添加用户名
2. 授权用户名访问主机
3. 添加主机
4. 删除主机
5. 查看日志
6. 退出
请输入编号 : 2
请输入要授权的用户名 : lisi
请输入要授权的主机 IP 地址 : 192.168.0.12
需要 sudo root 权限 ?(Y/N): n
添加成功 .
说明:上述授权 zhangsan 用户对 192.168.0.11 有可 sudo root 权限,对 192.168.0.12
sudo 权限。授权 lisi 用户对 192.168.0.12 sudo 权限。
4. 用户登录测试
(1)使用 zhangsan 用户登录 Server1 服务器
[root@BLJ ~]# login as: zhangsan
Localhost login zhangsan
password:
主机列表:
192.168.0.11 sudo
192.168.0.12
请输入要登录的主机 IP 地址 : 192.168.0.11
[admin@localhost ~]$ ip addr
# 查看是否连接到了目标服务器
(2)使用 lisi 用户登录 Server2 服务器。
[root@BLJ ~]# login as: lisi
Localhost login lisi
password:
主机列表:
192.168.0.12
请输入要登录的主机 IP 地址 : 192.168.0.12
[user@localhost ~]$ ip addr
# 查看是否连接到了目标服务器
5. 管理员查看操作日志
[root@BLJ ~]# python3 /opt/baoleiji/bin/manager_menu.py
菜单 :
1. 添加用户名
2. 授权用户名访问主机
3. 添加主机
4. 删除主机
5. 查看日志
6. 退出
请输入编号 : 5
login_user: zhangsan | share_user: admin | server_ip: 192.168.0.11 | shell_command: echo
"zhangsan" | datetime: 2020-07-14 14:29:27
login_user: zhangsan | share_user: admin | server_ip: 192.168.0.11 | shell_command: ls | datetime:
2020-07-14 14:29:30
login_user: lisi | share_user: user | server_ip: 192.168.0.12 | shell_command: echo "lisi | datetime:
2020-07-14 14:29:52
login_user: lisi | share_user: user | server_ip: 192.168.0.12 | shell_command: ls | datetime: 2020-07-14 14:29:54
本项目实现了基本的堡垒机功能,相比生产环境的使用功能还有待完善,这里的目的是
让大家熟悉堡垒机项目开发流程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2151666.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

十种果冻的做法

菠萝果冻 1.在菠萝的1/5处切开,切去顶做盖子用,用水果刀在四周划一圈使皮和果肉分离 2.注意底部切透了,用水果刀把菠萝肉挖出,菠萝肉切丁用盐水浸泡备用 3.把菠萝丁放入料理机中加入少许纯净水,打成菠萝汁备用 4.打好…

伊犁-linux root 密码忘记咋办

1 root 密码忘记了 或者reboot 重启之后在引导界面 按住 e 进入如下界面 然后按住ctrlx 进入这个界面 root 修改成功

为什么512G的固态硬盘,电脑显示只有476G可用?

硬盘的标称容量与操作系统显示的可用容量存在差异,这是由于硬盘制造商和操作系统在计算容量时采用不同的进制标准所致。硬盘制造商通常使用10进制来标注硬盘容量,即1GB等于1000MB,而操作系统则使用2进制,即1GB等于1024MB。因此&am…

无人机视角电力巡检资产检测与异常判别数据集

无人机视角电力巡检资产检测与异常判别,资产检测关注17类目标,共10000余张无人机图像,json方式标注,类别如下: 1.Spiral Damper - 螺旋阻尼器 2.Stockbridge Damper - 斯托克布里奇阻尼器 3.Glass Insulator - 玻璃绝缘…

双击就可以打开vue项目,而不用npm run dev

右键点击桌面或其他位置,选择“新建” -> “快捷方式”,在“对象的位置”处直接输入“npm run dev”,然后下一步 自定义一个快捷方式名称 完成后,桌面会创建一个快捷方式,右键快捷方式选择属性,可以看…

智慧火灾应急救援航拍检测数据集(无人机视角)

智慧火灾应急救援。 无人机,直升机等航拍视角下火灾应急救援检测数据集,数据分别标注了火,人,车辆这三个要素内容,29810张高清航拍影像,共31GB,适合森林防火,应急救援等方向的学术研…

免费音乐剪辑软件大揭秘:2024 大学生的音乐创作利器

对于音乐爱好者而言,如果你萌生了尝试音乐剪辑的念头,不妨先从探索一些免费工具开始。在此,我愿分享几款我个人体验过的、值得一试的音乐剪辑免费软件,希望能为你的音乐探索之旅增添乐趣与灵感。 1.福晰音频剪辑 链接直达>&g…

Java servlet《网吧机房管理系统浅析》

网吧机房管理系统在网吧运营中起着至关重要的作用。 对于用户而言,该系统提供了便捷的登录方式,通过用户名和密码可准确显示所在网吧机房号,便于快速定位。同时,合理的机房分配功能确保用户获得良好上网体验。遇到问题时&#xff…

Highcharts甘特图基本用法(highcharts-gantt.js)

参考官方文档: https://www.highcharts.com/docs/gantt/getting-started-gantt https://www.highcharts.com/demo/gantt/project-management https://www.hcharts.cn/demo/gantt 链接在下面按需引入 https://code.highcharts.com/gantt/highcharts-gantt.js htt…

【C语言】自定义类型——联合和枚举

目录 一、联合体(共用体) (1)联合体类型的声明 (2)联合体类型的特点 (3)联合体和结构体的比较 (4)联合体大小的计算 (5)联合体的…

STM8L101低功耗的理解

一.通过降低时钟频率来降低功耗: 规格书如图 1.code 从flash启动,主频率是16Mhz时,功耗测量2.51ma左右,对应程序如下: 2.code从flash启动,主频率是2Mhz时(上电默认值),功…

Android 如何使用jdk命令给应用/APK重新签名。

写在前面:本篇文章介绍Windows命令行中,如何使用jdk命令给已经签过名的apk重新签名。 使用工具:jarsigner。jarsigner是jdk自带的工具,签名之前确保Windows电脑中已安装jdk。我的环境如下:   后续操作步骤如下&…

力扣(leetcode)每日一题 2332 坐上公交的最晚时间

题目: 给你一个下标从 0 开始长度为 n 的整数数组 buses ,其中 buses[i] 表示第 i 辆公交车的出发时间。同时给你一个下标从 0 开始长度为 m 的整数数组 passengers ,其中 passengers[j] 表示第 j 位乘客的到达时间。所有公交车出发的时间互…

(七)使用SoapUI工具调用WebAPI

1.调用一个无参数的GET请求 [HttpGet(Name "GetWeatherForecast")]public IEnumerable<WeatherForecast> Get(){return Enumerable.Range(1, 5).Select(index > new WeatherForecast{Date DateTime.Now.AddDays(index),TemperatureC Random.Shared.Next(…

QFramework v1.0 使用指南 更新篇:20240918. 新增 BindableList

20240918. 新增 BindableList BindableProperty 很好用&#xff0c;但是不支持 List 等集合。 而 Bindable List 功能群友呼吁了很久了。 应群友要求&#xff0c;笔者实现了 Bindable List。 基本使用方式如下: using System; using UnityEngine; using UnityEngine.UI;na…

最新版本TensorFlow训练模型TinyML部署到ESP32入门实操

最新版本TensorFlow训练模型TinyML入门实操 1.概述 这篇文章介绍微型嵌入式设备的机器学习TinyML&#xff0c;它们的特点就是将训练好的模型部署到单片机上运行。 2.TensorFlow深度学习原理 TensorFlow开源项目是由google研发的一个嵌入式机器学习工具&#xff0c;通过调用…

智慧安防监控EasyCVR视频汇聚管理平台如何修改视频流分辨率?

智慧安防监控EasyCVR视频管理平台能在复杂的网络环境中&#xff0c;将前端监控设备进行统一集中接入与汇聚管理。EasyCVR平台支持H.264/H.265视频压缩技术&#xff0c;可在4G/5G/WIFI/宽带等网络环境下&#xff0c;传输720P/1080P/2K/4K高清视频。视频流经平台处理后&#xff0…

高质量的翻译:应用程序可用性和成功的关键

在日益全球化的应用市场中&#xff0c;开发一款优秀的产品只是成功的一半。另一半&#xff1f;确保你的用户&#xff0c;无论他们在哪里或说什么语言&#xff0c;都能无缝理解和使用它。这就是高质量翻译的用武之地——不是事后的想法&#xff0c;而是应用程序可用性和最终成功…

如何写一个自动化Linux脚本去进行等保测试--引言

#我的师兄喜欢给我的休闲实习生活加活&#xff0c;说是让我在实习期间写一个自动化脚本去进行等保测试。呵呵哒。 怎么办呢&#xff0c;师兄的指令得完成&#xff0c;师兄说让我使用Python完成任务。 设想如下&#xff1a; 1、将Linux指令嵌入到python脚本中 2、调试跑通 …

C++11——function与bind

包装器 function包装器function的介绍function的使用function的使用场景function的意义 bind包装器bind的介绍bind的使用 function包装器 function的介绍 function是用来包装函数的&#xff0c;所以叫做包装器或者适配器&#xff0c;fuction的本质其实是一个类模板。 functio…