Skip to content

Commit 9c0bd92

Browse files
committed
update webguacamole
1 parent 8d39c3b commit 9c0bd92

27 files changed

+383
-130
lines changed

Dockerfile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
FROM python:3.7.2
1+
FROM python:3.7.9
22
RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak
33
COPY ./sources.list /etc/apt
44
ADD . /devops
55
WORKDIR /devops
66
# RUN apt-get update && apt-get install python3-dev default-libmysqlclient-dev sshpass -y
7-
RUN cd /devops && pip install -i https://mirrors.aliyun.com/pypi/simple -r requirements.txt
8-
RUN cd /devops && echo_supervisord_conf > /etc/supervisord.conf && cat deamon.ini >> /etc/supervisord.conf && \
7+
RUN cd /devops && /usr/local/bin/pip install -i https://mirrors.aliyun.com/pypi/simple -r requirements.txt
8+
RUN cd /devops && echo_supervisord_conf > /etc/supervisord.conf && cat supervisord.conf >> /etc/supervisord.conf && \
99
sed -i 's/nodaemon=false/nodaemon=true/g' /etc/supervisord.conf
1010
RUN dpkg -i sshpass_1.06-1_amd64.deb
1111
RUN echo 'Asia/Shanghai' > /etc/timezone

README.md

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# devops
2-
基于 python 3.7.2 + django 2.2.16 + channels 2.2.0 + celery 4.3.0 + ansible 2.9.2 + AdminLTE-3.0.0 实现的运维 devops 管理系统。具体见 `screenshots` 文件夹中的效果预览图。
2+
基于 python 3.7.9 + django 2.2.16 + channels 2.4.0 + celery 4.4.7 + ansible 2.9.14 + AdminLTE-3.0.0 实现的运维 devops 管理系统。具体见 `screenshots` 文件夹中的效果预览图。
33
本人为运维工程师,非专业开发,项目各个功能模块都是现学现用,可能有的地方暂时没有考虑合理和性能的问题。
44

55

@@ -35,7 +35,7 @@
3535

3636
# 部署安装
3737

38-
环境:Centos 7.5,python 3.7.2,docker 1.13.1,项目目录为 `/home/workspace/devops`
38+
环境:Centos 7.5,python 3.7.9,docker 1.13.1,项目目录为 `/home/workspace/devops`
3939

4040
**1. 安装依赖**
4141
```bash
@@ -46,28 +46,33 @@ yum install -y gcc sshpass python3-devel mysql-devel
4646
- python3-devel 与 mysql-devel 为 mysqlclient 库的依赖
4747
- 不建议使用 pymysql 代替 mysqlclient ,因为 pymysql 为纯 python 编写的库,性能较低
4848

49-
**2. 安装 redis(docker 方式)**
49+
**2. 安装 mysql(docker 方式)**
5050
```bash
51-
docker run --name redis-server -p 6379:6379 -d redis:latest
51+
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 mysql:5.7.31
52+
```
53+
54+
**3. 安装 redis(docker 方式)**
55+
```bash
56+
docker run --name redis-server -p 6379:6379 -d redis:6.0.8
5257
```
5358
- channels、缓存、celery以及 session 支持所需,必须
5459

55-
**3. 安装 guacd(docker 方式)**
60+
**4. 安装 guacd(docker 方式)**
5661
```bash
57-
docker run --name guacd -e GUACD_LOG_LEVEL=info -v /home/workspace/devops/media/guacd:/fs -p 4822:4822 -d guacamole/guacd
62+
docker run --name guacd -e GUACD_LOG_LEVEL=info -v /home/workspace/devops/media/guacd:/fs -p 4822:4822 -d guacamole/guacd:1.1.0
5863
```
5964
- rdp 与 vnc 连接支持所需,非必须
6065
- rdp 必须设置为`允许运行任意版本远程桌面的计算机连接(较不安全)(L)`才能连接,也就说目前暂不支持 nla 登陆方式
6166
- `-v /home/workspace/devops/media/guacd:/fs` 挂载磁盘,用于远程挂载文件系统实现上传和下载文件
6267

63-
**4. 安装 python 依赖库**
68+
**5. 安装 python 依赖库**
6469
```bash
6570
# 安装相关库
6671
pip3 install -i https://mirrors.aliyun.com/pypi/simple -r requirements.txt
6772
```
6873
- -i 指定阿里源,速度飞起,我大 TC 威武,局域网玩得贼 6
6974

70-
**5. 修改 devops/settings.py 配置**
75+
**6. 修改 devops/settings.py 配置**
7176

7277
相关配置均有注释,根据实际情况修改。默认数据库使用的是 sqlite3,如果需要使用 mysql,方法如下:
7378

@@ -93,7 +98,7 @@ DATABASES = {
9398
```
9499
- 相关数据库(不需创建表)与账号必须事先在 mysql 数据库中创建好并授权。
95100

96-
**6. 迁移数据库**
101+
**7. 迁移数据库**
97102
```bash
98103
sh delete_makemigrations.sh
99104
rm -f db.sqlite3
@@ -102,15 +107,15 @@ python3 manage.py makemigrations
102107
python3 manage.py migrate
103108
```
104109

105-
**7. 初始化数据**
110+
**8. 初始化数据**
106111
```bash
107112
python3 manage.py loaddata initial_data.json
108113
python3 init.py
109114
```
110115
- initial_data.json 为权限数据
111116
- init.py 创建超级管理员 admin 以及部分测试数据,可根据实际情况修改
112117

113-
**8. 启动相关服务**
118+
**9. 启动相关服务**
114119
```bash
115120
rm -rf logs/*
116121
export PYTHONOPTIMIZE=1 # 解决 celery 不允许创建子进程的问题
@@ -127,7 +132,7 @@ nohup gunicorn -c gunicorn.cfg devops.wsgi:application > logs/gunicorn.log 2>&1
127132
- celery_beat 定时任务处理进程,读取 `devops/settings.py` 中设置的 `CELERY_BEAT_SCHEDULE` 定时任务,详见 v1.8.8 升级日志
128133
- 需要停止时 kill 相应的进程,然后删除 logs 目录下所有的 pid 文件即可
129134

130-
**9. 配置 nginx 前端代理**
135+
**10. 配置 nginx 前端代理**
131136
```
132137
yum install -y nginx
133138
```
@@ -272,7 +277,7 @@ systemctl start nginx
272277
```
273278
- 启动前建议先检查下配置是否正确:`nginx -t`
274279

275-
**10. 访问首页:http://127.0.0.1**
280+
**11. 访问首页:http://127.0.0.1**
276281

277282
初始超级管理员:
278283
> 账号: admin 密码:123456
@@ -295,6 +300,26 @@ systemctl start nginx
295300

296301
# 升级日志
297302

303+
### ver2.2.0
304+
升级 webguacamole,支持设置 AD 域账号以及设置验证方式(any、rdp、tls、nla、nla-ext);
305+
306+
升级部分依赖:
307+
- celery 4.3.0 到 4.4.7;
308+
- channel 2.2.0 到 2.4.0;
309+
- channel-redis 2.4.0 到 3.1.0;
310+
- daphne 2.3.0 到 2.5.0;
311+
- pyasn1 0.4.3 到 0.4.8;
312+
- cryptography 2.7 到 2.8;
313+
- gunicorn 20.0.3 到 20.0.4;
314+
- supervisor 4.1.0 到 4.2.1;
315+
- redis 3.3.11 到 3.5.3;
316+
- django-redis 4.10.0 到 4.12.1;
317+
- requests==2.22.0 到 2.24.0;
318+
- jsonpickle 1.2 到 1.4.1;
319+
- gssapi==1.6.2 到 1.6.9;
320+
- django-cachalot 2.3.1 到 2.3.2;
321+
- ansible 2.9.2 到 2.9.14;
322+
298323
### ver2.1.1
299324
修正 greenlet 与 gunicorn 的兼容性问题;
300325

apps/server/forms.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ class ChangeUserForm(forms.Form):
55
userid = forms.IntegerField(label="用户ID")
66
username = forms.CharField(label="用户名", max_length=128)
77
password = forms.CharField(label="密码", max_length=32)
8+
domain = forms.CharField(label="AD域", max_length=256, required=False)
89
memo = forms.CharField(label="备注", max_length=255, widget=forms.Textarea, required=False)
910
enabled = forms.BooleanField(label="登陆后是否su跳转超级用户", required=False)
1011
superusername = forms.CharField(label="超级用户", max_length=128, required=False)
@@ -15,6 +16,7 @@ class AddUserForm(forms.Form):
1516
name = forms.CharField(label="名称", max_length=128)
1617
username = forms.CharField(label="用户名", max_length=128)
1718
password = forms.CharField(label="密码", max_length=32)
19+
domain = forms.CharField(label="AD域", max_length=256, required=False)
1820
memo = forms.CharField(label="备注", max_length=255, widget=forms.Textarea, required=False)
1921
enabled = forms.BooleanField(label="登陆后是否su跳转超级用户", required=False)
2022
superusername = forms.CharField(label="超级用户", max_length=128, required=False)
@@ -27,6 +29,7 @@ class ChangeHostForm(forms.Form):
2729
ip = forms.GenericIPAddressField(label="主机IP")
2830
wip = forms.GenericIPAddressField(label="公网IP", required=False)
2931
protocol = forms.IntegerField(label='协议')
32+
security = forms.CharField(label="rdp验证方式", max_length=32, required=False)
3033
env = forms.IntegerField(label='环境')
3134
platform = forms.IntegerField(label='平台')
3235
port = forms.IntegerField(label='端口')
@@ -42,6 +45,7 @@ class AddHostForm(forms.Form):
4245
ip = forms.GenericIPAddressField(label="主机IP")
4346
wip = forms.GenericIPAddressField(label="公网IP", required=False)
4447
protocol = forms.IntegerField(label='协议')
48+
security = forms.CharField(label="rdp验证方式", max_length=32, required=False)
4549
env = forms.IntegerField(label='环境')
4650
platform = forms.IntegerField(label='平台')
4751
port = forms.IntegerField(label='端口')

apps/server/models.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class RemoteUser(models.Model):
2626
name = models.CharField(unique=True, max_length=128, verbose_name="名称")
2727
username = models.CharField(max_length=128, verbose_name="用户名")
2828
password = models.CharField(max_length=512, verbose_name="密码")
29+
domain = models.CharField(blank=True, null=True, max_length=256, verbose_name='AD域验证服务器')
2930
enabled = models.BooleanField(default=False, verbose_name='登陆后是否su跳转超级用户')
3031
superusername = models.CharField(max_length=128, blank=True, null=True, verbose_name="超级用户")
3132
superpassword = models.CharField(max_length=512, blank=True, null=True, verbose_name="超级密码")
@@ -78,6 +79,8 @@ class RemoteUserBindHost(models.Model):
7879
port = models.SmallIntegerField(default=22, verbose_name='端口')
7980
release = models.CharField(max_length=255, default='CentOS', verbose_name='系统/型号')
8081
platform = models.SmallIntegerField(default=1, choices=PLATFORM_CHOICES, verbose_name='平台')
82+
# rdp 验证方式,可选项:rdp,tls,nla,nla-ext
83+
security = models.CharField(blank=True, null=True, max_length=32, verbose_name='rdp验证方式')
8184
memo = models.TextField(blank=True, null=True, verbose_name='备注')
8285
# on_delete 当 RemoteUser 记录被删时,阻止其操作
8386
remote_user = models.ForeignKey('RemoteUser', blank=True, null=True, on_delete=models.PROTECT)

apps/server/views_api.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ def user_update(request):
2424
userid = changeuser_form.cleaned_data.get('userid')
2525
username = changeuser_form.cleaned_data.get('username')
2626
password = changeuser_form.cleaned_data.get('password')
27+
domain = changeuser_form.cleaned_data.get('domain', None)
2728
memo = changeuser_form.cleaned_data.get('memo')
2829
enabled = changeuser_form.cleaned_data.get('enabled')
2930
superusername = changeuser_form.cleaned_data.get('superusername', None)
@@ -36,6 +37,7 @@ def user_update(request):
3637
data = {
3738
'username': username,
3839
'password': encrypt(password),
40+
'domain': domain,
3941
'memo': memo,
4042
'enabled': enabled,
4143
'superusername': superusername,
@@ -65,6 +67,7 @@ def user_add(request):
6567
name = adduser_form.cleaned_data.get('name')
6668
username = adduser_form.cleaned_data.get('username')
6769
password = adduser_form.cleaned_data.get('password')
70+
domain = adduser_form.cleaned_data.get('domain', None)
6871
memo = adduser_form.cleaned_data.get('memo')
6972
enabled = adduser_form.cleaned_data.get('enabled')
7073
superusername = adduser_form.cleaned_data.get('superusername', None)
@@ -78,6 +81,7 @@ def user_add(request):
7881
'name': name,
7982
'username': username,
8083
'password': encrypt(password),
84+
'domain': domain,
8185
'memo': memo,
8286
'enabled': enabled,
8387
'superusername': superusername,
@@ -178,6 +182,7 @@ def host_update(request):
178182
ip = changehost_form.cleaned_data.get('ip')
179183
wip = changehost_form.cleaned_data.get('wip')
180184
protocol = changehost_form.cleaned_data.get('protocol')
185+
security = changehost_form.cleaned_data.get('security', None)
181186
env = changehost_form.cleaned_data.get('env')
182187
platform = changehost_form.cleaned_data.get('platform')
183188
port = changehost_form.cleaned_data.get('port')
@@ -190,6 +195,7 @@ def host_update(request):
190195
'ip': ip,
191196
'wip': wip,
192197
'protocol': protocol,
198+
'security': security,
193199
'env': env,
194200
'platform': platform,
195201
'port': port,
@@ -232,6 +238,7 @@ def host_add(request):
232238
ip = addhost_form.cleaned_data.get('ip')
233239
wip = addhost_form.cleaned_data.get('wip')
234240
protocol = addhost_form.cleaned_data.get('protocol')
241+
security = addhost_form.cleaned_data.get('security', None)
235242
env = addhost_form.cleaned_data.get('env')
236243
platform = addhost_form.cleaned_data.get('platform')
237244
port = addhost_form.cleaned_data.get('port')
@@ -245,6 +252,7 @@ def host_add(request):
245252
'ip': ip,
246253
'wip': wip,
247254
'protocol': protocol,
255+
'security': security,
248256
'env': env,
249257
'platform': platform,
250258
'port': port,
@@ -258,7 +266,7 @@ def host_add(request):
258266
data['remote_user'] = remoteuser
259267
remoteuserbindhost = RemoteUserBindHost.objects.create(**data)
260268
if not request.session['issuperuser'] or request.session['username'] != 'admin':
261-
user.remote_user_bind_hosts.add(remoteuserbindhost) # 非 admin 添加的主机分配给自己
269+
user.remote_user_bind_hosts.add(remoteuserbindhost) # 非 admin 添加的主机分配给自己
262270
event_log(user, 12, '主机 [{}] 添加成功'.format(remoteuserbindhost.hostname),
263271
request.META.get('REMOTE_ADDR', None), request.META.get('HTTP_USER_AGENT', None))
264272
hostinfo = dict()
@@ -292,7 +300,7 @@ def host_update_info(request):
292300
hostid = request.POST.get('id', None)
293301

294302
try:
295-
ids = [ int(x) for x in hostid.split(',')]
303+
ids = [int(x) for x in hostid.split(',')]
296304
except Exception:
297305
error_message = '不合法的请求参数!'
298306
return JsonResponse({"code": 401, "err": error_message})

apps/webguacamole/guacamoleclient.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ def connect(self, protocol, hostname, port, username, password, width, height, d
134134
**kwargs,
135135
)
136136
elif protocol == 'rdp':
137+
if 'security' not in kwargs.keys():
138+
kwargs['security'] = 'any'
137139
self.guacamoleclient.handshake(
138140
protocol=protocol,
139141
port=port,
@@ -143,7 +145,8 @@ def connect(self, protocol, hostname, port, username, password, width, height, d
143145
width=width,
144146
height=height,
145147
dpi=dpi,
146-
security='tls', # rdp,nla,tls,any
148+
# domain='SWAD.COM', # 域验证服务器
149+
# security='nla', # rdp,nla,nla-ext,tls,any
147150
ignore_cert="true",
148151
disable_audio="true",
149152
client_name="devops",

apps/webguacamole/websocket_layer.py

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -87,40 +87,43 @@ def connect(self):
8787

8888
self.guacamoleclient = Client(websocker=self)
8989
if 'webguacamole终端文件上传下载' not in self.session[settings.INIT_PERMISSION]['titles']: # 判断权限
90-
self.guacamoleclient.connect(
91-
protocol=self.remote_host.get_protocol_display(),
92-
hostname=self.remote_host.ip,
93-
port=self.remote_host.port,
94-
username=self.remote_host.remote_user.username,
95-
password=decrypt(self.remote_host.remote_user.password),
96-
width=self.width,
97-
height=self.height,
98-
dpi=self.dpi,
99-
enable_font_smoothing="true",
100-
)
90+
kwargs = {
91+
'protocol': self.remote_host.get_protocol_display(),
92+
'hostname': self.remote_host.ip,
93+
'port': self.remote_host.port,
94+
'username': self.remote_host.remote_user.username,
95+
'password': decrypt(self.remote_host.remote_user.password),
96+
'width': self.width,
97+
'height': self.height,
98+
'dpi': self.dpi,
99+
'enable_font_smoothing': "true",
100+
}
101+
if self.remote_host.remote_user.domain:
102+
kwargs['domain'] = self.remote_host.remote_user.domain
103+
if self.remote_host.security:
104+
kwargs['security'] = self.remote_host.security
105+
self.guacamoleclient.connect(**kwargs)
101106
else:
102-
self.guacamoleclient.connect(
103-
protocol=self.remote_host.get_protocol_display(),
104-
hostname=self.remote_host.ip,
105-
port=self.remote_host.port,
106-
username=self.remote_host.remote_user.username,
107-
password=decrypt(self.remote_host.remote_user.password),
108-
width=self.width,
109-
height=self.height,
110-
dpi=self.dpi,
111-
enable_drive="true",
112-
drive_name="filesystem",
113-
drive_path="/fs/{}".format(self.group),
114-
create_drive_path="true",
115-
116-
# enable_wallpaper="true",
117-
# enable_theming="true",
118-
enable_font_smoothing="true", # 字体平滑开启就可以了
119-
# enable_full_window_drag="true",
120-
# enable_desktop_composition="true",
121-
# enable_menu_animations="true",
122-
)
123-
107+
kwargs = {
108+
'protocol': self.remote_host.get_protocol_display(),
109+
'hostname': self.remote_host.ip,
110+
'port': self.remote_host.port,
111+
'username': self.remote_host.remote_user.username,
112+
'password': decrypt(self.remote_host.remote_user.password),
113+
'width': self.width,
114+
'height': self.height,
115+
'dpi': self.dpi,
116+
'enable_font_smoothing': "true",
117+
'enable_drive': "true",
118+
'drive_name': "filesystem",
119+
'drive_path': "/fs/{}".format(self.group),
120+
'create_drive_path': "true",
121+
}
122+
if self.remote_host.remote_user.domain:
123+
kwargs['domain'] = self.remote_host.remote_user.domain
124+
if self.remote_host.security:
125+
kwargs['security'] = self.remote_host.security
126+
self.guacamoleclient.connect(**kwargs)
124127
for i in self.scope['headers']:
125128
if i[0].decode('utf-8') == 'user-agent':
126129
self.user_agent = i[1].decode('utf-8')

apps/webssh/management/commands/sshd.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,17 @@
55

66

77
class Command(BaseCommand):
8-
# 生成SSH服务端,用于透明代理SSH
9-
help = u'生成SSH透明代理服务器,类似堡垒机功能,使网站支持CRT,Xshell等SSH终端'
8+
help = '生成 SSH/SFTP 透明代理服务器,用于支持 SecureCRT/xshell/putty/winscp 等 SSH/SFTP 终端'
109

1110
# def add_arguments(self, parser):
1211
# parser.add_argument('port', nargs='?', default='2222', type=int,
13-
# help=u'''
12+
# help='''
1413
# SSH监听端口,默认为2222
1514
# ''')
1615

1716
def handle(self, *args, **options):
1817
host = settings.PROXY_SSHD.get('listen_host', '0.0.0.0')
1918
port = settings.PROXY_SSHD.get('listen_port', 2222)
20-
cons = settings.PROXY_SSHD.get('cons', 250)
19+
cons = settings.PROXY_SSHD.get('cons', 100)
2120
ssh_server = SSHServer(host, port, cons)
2221
ssh_server.run()
23-

0 commit comments

Comments
 (0)