管理的服务器、交换机、VM 数量越来越多,种类也越来越多,时常需要同时打开多个不同的工具来访问不同的目标。比方说 RDP 连着 Windows 的机器、 SSH 连着 Linux 的服务器、 Telnet 连着交换机……
不同的工具还有着不同的连接配置管理方式,没办法统一管理。我也用过 SecureCRT,虽然他很强大,并且在 Windows 上和 Mac 上都很好用,但是他只能连接字符界面。
0x00 Guacamole 简介
Apache Guacamole
是由 Apache 基金会孵化的项目,一个无客户端远程桌面网关(Clientless Remote Desktop Gateway),支持常见的远程桌面协议:
- VNC
- RDP
- SSH
- Telnet
除了以上的协议支持之外,从 1.1.0
开始,增加了连接 Kubernetes
Pod 的支持,连接的 Pod 需要开启 TTy
支持,之后我会详细介绍一下用法。本文着重介绍 Guacamole
的安装和基本使用。
同时, Guacamole 还支持连接分组、窗口多开、剪贴板共享等功能,可以方便的在同一个平台上管理不同协议的终端设备。
服务架构
首先了解一下 Guacamole
的架构
Guacamole Server
主要由两部分组成,Guacamole
和 guacd
:
Guacamole
是一个 Web 服务,包含前端guacamole-website
和guacd
的客户端guacamole-client
。guacamole-client
会将用户和服务器连接配置信息存储在数据库中,支持 MySQL 和 PostgreSQL。guacd
是一个无状态的 daemon。guacamole-client
会将存储在数据库中的连接配置信息通过内部的协议传递给guacd
,由guacd
负责处理不同的远程连接协议,并和服务器建立连接。
0x01 安装
Guacamole
提供了多种不同的安装方式,可以编译源码,也可以下载预编译的 war 包。但同时官方也提供预装了插件的 Docker 镜像可以直接简单配置使用。
这里我提供三种不同的部署形态供选择,可以通过左边的目录直接跳到对应的部分。
我写作这篇文章时,Guacamole
的最新版本为 1.4.0
,接下来的安装流程都是基于这个版本的
数据库我选择的是 PostgreSQL。
使用 Docker 部署
Docker 引擎的安装我在这里不过多赘述,可以参照 Docker 官网的安装指南进行安装。
使用 docker-compose 来管理服务。
version: "3"
services:
initdb:
image: guacamole/guacamole:1.4.0
user: 0:0
volumes:
- initdb:/docker-entrypoint-initdb.d
command:
- sh
- -c
- /opt/guacamole/bin/initdb.sh --postgres > /docker-entrypoint-initdb.d/initdb.sql
db:
image: postgres:14
restart: always
depends_on:
- initdb
volumes:
- initdb:/docker-entrypoint-initdb.d
- ./db:/var/lib/postgresql/data/pgdata
environment:
- PGDATA=/var/lib/postgresql/data/pgdata
- POSTGRES_USER=guacamole
- POSTGRES_PASSWORD=guacamole
- POSTGRES_HOST_AUTH_METHOD=md5
- POSTGRES_INITDB_ARGS=--auth-host=md5
ports:
- 5432:5432
guacad:
image: guacamole/guacd:1.4.0
restart: always
ports:
- 4822:4822
guacamole:
image: guacamole/guacamole:1.4.0
restart: always
ports:
- 8080:8080
depends_on:
- db
- guacad
environment:
- GUACD_HOSTNAME=guacad
- POSTGRES_HOSTNAME=db
- POSTGRES_DATABASE=guacamole
- POSTGRES_USER=guacamole
- POSTGRES_PASSWORD=guacamole
volumes:
initdb: {}
部署在 Kubernetes 上
如果环境中有 Kubernetes 环境,也可以直接部署在 Kubernetes 上,统一管理。
首先创建数据库实例,利用 initContainer
来创建数据库初始化脚本。(注意修改一下数据库密码)
apiVersion: v1
kind: Service
metadata:
name: guacamole-db
spec:
selector:
app: guacamole
component: db
ports:
- port: 5432
targetPort: 5432
---
apiVersion: v1
kind: Secret
metadata:
name: guacamole-db
type: Opaque
data:
POSTGRES_PASSWORD: Z3VhY2Ftb2xlCg== # guacamole
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: guacamole-db
spec:
selector:
matchLabels:
app: guacamole
component: db
serviceName: guacamole-db
replicas: 1
template:
metadata:
labels:
app: guacamole
component: db
spec:
initContainers:
- name: database-init
image: guacamole/guacamole:1.4.0
volumeMounts:
- name: init
mountPath: /docker-entrypoint-initdb.d
command:
- sh
args:
- "-c"
- "/opt/guacamole/bin/initdb.sh --postgres > /docker-entrypoint-initdb.d/initdb.sql"
containers:
- name: postgres
image: postgres:14
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5432
name: db
volumeMounts:
- name: init
mountPath: /docker-entrypoint-initdb.d
- name: data
mountPath: /var/lib/postgresql/data
env:
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
- name: POSTGRES_USER
value: guacamole
- name: POSTGRES_HOST_AUTH_METHOD
value: md5
- name: POSTGRES_INITDB_ARGS
value: --auth-host=md5
envFrom:
- secretRef:
name: guacamole-db
resources:
limits:
cpu: 500m
memory: 500Mi
volumes:
- name: init
emptyDir: {}
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 5Gi
再部署 Guacamole 服务,这里我将 guacd
和 Guacamole
部署在一起。实践中可以将 guacd
和 Guacamole
分开部署,再在创建连接时指定不同的 guacd
服务。
---
apiVersion: v1
kind: Service
metadata:
name: guacamole
spec:
selector:
app: guacamole
compoent: app
ports:
- port: 8080
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: guacamole
spec:
selector:
matchLabels:
app: guacamole
compoent: app
template:
metadata:
labels:
app: guacamole
compoent: app
spec:
containers:
- name: guacd
image: guacamole/guacd:1.4.0
resources:
limits:
memory: "1Gi"
cpu: "500m"
ports:
- containerPort: 4822
- name: guacamole
image: guacamole/guacamole:1.4.0
imagePullPolicy: IfNotPresent
resources:
limits:
memory: "1Gi"
cpu: "500m"
ports:
- containerPort: 8080
name: web
env:
- name: GUACD_HOSTNAME
value: "127.0.0.1"
- name: POSTGRES_HOSTNAME
value: guacamole-db
- name: POSTGRES_DATABASE
value: guacamole
- name: POSTGRES_USER
value: guacamole
envFrom:
- secretRef:
name: guacamole-db
0x02 安全配置
LDAP 登录
例子中我使用 Windows 的域控作为 LDAP 服务。
Guacamole 的镜像中已经预装了 LDAP 认证插件,我们只需要简单的设置好参数就可以接入 LDAP 服务。
需要配置如下的环境变量:
LDAP_HOSTNAME=<ldap server hostname or ip>
LDAP_SEARCH_BIND_DN=<adminitrator dn>
LDAP_SEARCH_BIND_PASSWORD=<administrator password>
LDAP_USER_BASE_DN=<user base dn>
LDAP_USER_SEARCH_FILTER=<the filter to find user>
LDAP_USERNAME_ATTRIBUTE=<attribute name in person object bind to username>
LDAP_GROUP_BASE_DN=<user group base dn>
LDAP_GROUP_SEARCH_FILTER=<the filter to find group>
我的实例:
LDAP_HOSTNAME="c.lo"
LDAP_SEARCH_BIND_DN="CN=Administrator,CN=Users,DC=c,DC=lo"
LDAP_SEARCH_BIND_PASSWORD=PASSWORD
LDAP_USER_BASE_DN="OU=Home,DC=c,DC=lo"
LDAP_USER_SEARCH_FILTER="(objectClass=person)"
LDAP_USERNAME_ATTRIBUTE="name"
LDAP_GROUP_BASE_DN="OU=Home,DC=c,DC=lo"
LDAP_GROUP_SEARCH_FILTER="(objectClass=group)"
其他的参数定制可以参考 官方文档
需要注意,官方文档中的配置参数为蛇形命名,转化为环境变量需要全部转为大写并把 “-” 换成 ”_“。
config | env |
---|---|
ldap-port | LDAP_PORT |
ldap-encryption-method | LDAP_ENCRYPTION_METHOD |
ldap-max-search-results | LDAP_MAX_SEARCH_RESULTS |
ldap-search-bind-dn | LDAP_SEARCH_BIND_DN |
ldap-user-attributes | LDAP_USER_ATTRIBUTES |
ldap-search-bind-password | LDAP_SEARCH_BIND_PASSWORD |
ldap-username-attribute | LDAP_USERNAME_ATTRIBUTE |
ldap-member-attribute | LDAP_MEMBER_ATTRIBUTE |
ldap-user-search-filter | LDAP_USER_SEARCH_FILTER |
ldap-config-base-dn | LDAP_CONFIG_BASE_DN |
ldap-group-base-dn | LDAP_GROUP_BASE_DN |
ldap-group-search-filter | LDAP_GROUP_SEARCH_FILTER |
ldap-member-attribute-type | LDAP_MEMBER_ATTRIBUTE_TYPE |
ldap-group-name-attribute | LDAP_GROUP_NAME_ATTRIBUTE |
ldap-dereference-aliases | LDAP_DEREFERENCE_ALIASES |
ldap-follow-referrals | LDAP_FOLLOW_REFERRALS |
ldap-max-referral-hops | LDAP_MAX_REFERRAL_HOPS |
ldap-operation-timeout | LDAP_OPERATION_TIMEOUT |
Guacamole
的默认管理员用户名为 guacadmin
,可以在域中添加这个用户,让默认管理员用户也使用 LDAP 进行登录。
0x03 使用指南
访问 http://127.0.0.1:8080/guacamole
使用默认用户名 guacadmin
和默认用户名密码 guacadmin
进行登录。(如果使用 LDAP 登录默认用户,则输入 LDAP 中 guacadmin
用户的密码)
新建连接
点击用户名,再点击”设置“,可以进到后台管理
导航到”连接“,点击”新建连接“
SSH
填写”名称“,选择 SSH
协议
滚动屏幕到下面的参数,填入目标服务器的 IP、端口、用户名和密码或是私钥,再按需调整下方的参数
滚动屏幕到最下方,点击”保存“
RDP
填写”名称“,选择 RDP
协议
滚动屏幕到下面的参数,填入目标服务器的 IP、端口、用户名、密码,勾选”忽略服务器证书“,再按需调整下方的参数
滚动屏幕到最下方,点击 ”保存“
Telnet
填写”名称“,选择 Telnet
协议
滚动屏幕到下面的参数,填入目标服务器的 IP、端口、用户名、密码
关于正则表达式的填写我们先来看下面的分析,这个是我 Telnet 登录 HomeLab 的路由器的过程
先看橙色框中,可以看到当 Username
出现的时候,就需要键入用户名,同理,当看到 Password
的时候,就需要键入密码,所以我们将 Username
填入 ”用户名正则表达式“,Password
填入 ”密码正则表达式“,让 Guacamole
自动输入用户名密码
那 Guacamole
要怎么判断登录成功还是失败呢?这时候我们看到红框
当出现 Error
的时候,表示登录失败了,而出现了命令提示符 Edge-Router
时,表示登录成功了,所以我就在 ”登录成功正则表达式“ 中填入 Edge-Router
,在 ”登录失败正则表达式“ 中填入 Error
(以上是我的示例配置,请根据实际情况修改)
滚动屏幕到最下方,点击 ”保存“
Kubernetes
连接 Kubernetes 上的容器,需要容器开启 tty
选项,这里提供一个示例的 yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo
spec:
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- name: demo
image: alpine:latest
resources:
limits:
memory: "128Mi"
cpu: "500m"
stdin: true
tty: true
填写”名称“,选择 Kubernetes
协议
滚动屏幕到 ”参数“,填入 Kubernetes API Server 的地址和端口,勾选 ”使用SSL/TLS“ 和 ”忽略服务器证书“
在 ”证书颁发机构证书“ 中填入从 kube config 的 clusters
字段中提取到的 certificate-authority-data
,注意要先 base64
解码一下
在下面的认证方式中填入从 kube config 的 users
字段中提取到的 client-certificate-data
和 client-key-data
,同样需要进行 base64
解码
在”容器“信息中填入 Pod 的信息,以及要执行的命令,此处我打算执行 sh
命令来启动一个 Shell
滚动屏幕到最下面,点击”保存“
VNC
填写”名称“,选择 VNC
协议
滚动屏幕到 “参数”,填入正确的主机名,端口,用户名,密码
滚动屏幕到最下面,点击“保存”
连接服务器
新建完连接,点击右上角用户名,在下拉菜单中点击“主页”,即可回到一开始登录的页面
点击下面的链接,即可连接到目标,最近使用过的连接会以缩略图的形式显示在上面,点击他也可以连接到服务器
等待片刻,即可连上
如果想要断开连接,按下键盘上的 Ctrl+Alt+Shift
即可呼出边栏,在边栏的右上角点击用户名,在下拉菜单中点击断开连接即可
如果不是点击“断开连接”而是点击“首页”,将会把当前连接最小化并悬挂在右下角
如果想要关闭边栏,再次按下 Ctrl+Alt+Shift
,边栏就会隐藏
连接多个终端(v1.4.0 以上)
有这样一个场景,我需要同时操作多个终端,开多个网页窗口可以实现,但如果我想在不同的同时执行相同的命令,多个网页的方案就不可行了
Guacamole
在 1.4.0
引入了 Connection tiling and keyboard broadcasting
功能,允许在一个页面上同时连接多个终端,和广播键盘消息到所有的连接
按下 Ctrl+Alt+Shift
呼出边栏,点击左上角连接名称,在下拉菜单中,勾选想要同时连接的终端,即可连接多个终端
想要在多个连接中同时输入,需要按住 Shift
或者 Ctrl
点击要接收输入的连接的标题栏,变为蓝色表示已被选中
0x04 尾巴
本文仅是 Guacamole
的使用入门,是 Guacamole
功能的冰山一脚,Guacamole
还支持如接入 2FA 认证、OpenID 登录、SFTP、WoL 网络唤醒、屏幕录制等大量功能,每种不同的连接协议又都有许多可以定制的参数。
再次感谢开发者对社区做出的巨大贡献!