vnc连接高版本tls的定位于解决方法

一次tls握手失败导致的vnc不可用

前言

使用 guacd 连接高版本 vnc 服务遇见了失败

检验 vnc 服务器输出 https://pic.y1nhui.com/ssl_handshake/img_v3_02ii_0df075cc-c1d0-4545-9c8a-18f5b61a207g.jpg

可见tls握手失败。

这里我们优先质疑是 centos 的问题,因为它的本机openssl 版本为:1.0.2k https://pic.y1nhui.com/ssl_handshake/image.png

查看支持的加密套件库,甚至看不到tls1.3 ,而且还有 ssl3.0

实验中定位,实验中解决

笔者计划:

  • 升级 centos openssl 库 查看是否可以
  • 若不行,升级 guacd 编译机器重新编译guacd 验证是否可行

笔者先给出最终的方法,具体的解决过程将在后续逐渐给出:

  • 运行机器 openssl 肯定需要升级
  • Guacd 也需要在升级了openssl 的情况下编译
  • Guacd 的 vnc 服务依赖 libvncserver 实现,因而 libvnvserver 也需要在编译机器重新编译

同时需要考虑到安装包的问题,所以这里的 openssl 与 libvncserver 的升级方案,都需要做rpm包

所以我们最终需要的是

  • Openssl 新版本 rpm
  • 指向了 新 openssl 的 libvncserver 的rpm
  • 指向了 新libvncserver 的guacd

Openssl rpm 构建

笔者先是参考了一篇文章与一个github仓库:

CentOS7制作rpm包升级OpenSSL

Build latest OpenSSL binary for CentOS

构建过程为:

安装依赖

yum install rpm-build rpmlint rpmdevtools -y
yum install gcc gcc-c++ make perl perl-WWW-Curl -y

构建文件夹

mkdir -p /root/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}

移动包到目标下

cp openssl-1.1.1w.tar.gz ~/rpmbuild/SOURCES/

创建 spec

vi ~/rpmbuild/SPECS/openssl.spec 填写内容:

Summary: OpenSSL 1.1.1w for redhat
Name: openssl
Version: %{?version}%{!?version:1.1.1w}
Release: 1%{?dist}
Obsoletes: %{name} <= %{version}
Provides: %{name} = %{version}
URL: https://www.openssl.org/
License: GPLv2+

Source: https://www.openssl.org/source/%{name}-%{version}.tar.gz 
 
BuildRequires: make gcc perl perl-WWW-Curl
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
%global openssldir /usr/local/openssl1.1.1w
 
%description
OpenSSL RPM for version 1.1.1w on redhat
 
%package devel
Summary: Development files for programs which will use the openssl library
Group: Development/Libraries
Requires: %{name} = %{version}-%{release}
 
%description devel
OpenSSL RPM for version 1.1.1w on redhat (development package)
 
%prep
%setup -q
 
%build
./config --prefix=%{openssldir} --openssldir=%{openssldir}
make -j4
 
%install
[ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot}
%make_install
 
mkdir -p %{buildroot}%{_bindir}
mkdir -p %{buildroot}%{_libdir}
ln -sf %{openssldir}/lib/libssl.so.1.1 %{buildroot}%{_libdir}
ln -sf %{openssldir}/lib/libcrypto.so.1.1 %{buildroot}%{_libdir}
ln -sf %{openssldir}/bin/openssl %{buildroot}%{_bindir}
 
%clean
[ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot}
 
%files
%{openssldir}
%defattr(-,root,root)
/usr/bin/openssl
/usr/lib64/libcrypto.so.1.1
/usr/lib64/libssl.so.1.1
 
%files devel
%{openssldir}/include/*
%defattr(-,root,root)
 
%post -p /sbin/ldconfig
 
%postun -p /sbin/ldconfig

执行构建

rpmbuild -D "version 1.1.1w" -ba openssl.spec

这里可以看到,选择了将openssl 安装到了自定义文件夹下,而不是默认文件夹,这是因为直接替换的情况下,centos有很多依赖 1.0.2 libs 与 devel 的组件,会导致系统崩溃。

那么第一步,openssl 的 rpm 包就好了

这里做验证:

rpm -ivh –force –nodeps xxx.rpm 安装完成后查看,可见已经在运行 openssl 1.1.1w https://pic.y1nhui.com/ssl_handshake/image-2.png 并且执行 openssl ciphers -v 也可以看到已经支持了tls1.3 https://pic.y1nhui.com/ssl_handshake/image-3.png 但是这里运行,guacd 还是连接失败,从日志可以定位到仍然在使用 openssl 1.0.2 k,所以需要重新编译guacd

编译guacd

编译guacd 基本与原本的没有区别,只是对 configure 做了增加 改为了 ./configure –prefix=/custom/server
–with-init-dir=/custom/server
–disable-kubernetes –enable-shared
CPPFLAGS="-I/usr/local/openssl1.1.1w/include"
LDFLAGS="-L/usr/local/openssl1.1.1w/lib"
–with-openssl=/usr/local/openssl1.1.1w 但是这个版本还是不可用。 当时还没有考虑好c的编译依赖等情况,只是单程的在想,是不是 libvncserver 需要重新编译? 于是我也编译了libvncserver

Libvncserver

使用如下 cmake 进行配置,之后make&& make install cmake -DCMAKE_C_FLAGS="-I/usr/local/openssl1.1.1w/include"
-DCMAKE_CXX_FLAGS="-I/usr/local/openssl1.1.1w/include"
-DCMAKE_EXE_LINKER_FLAGS="-L/usr/local/openssl1.1.1w/lib"
-DCMAKE_SHARED_LINKER_FLAGS="-L/usr/local/openssl1.1.1w/lib"
-DOPENSSL_ROOT_DIR=/usr/local/openssl1.1.1w
-DOPENSSL_LIBRARIES=/usr/local/openssl1.1.1w/lib
-DOPENSSL_INCLUDE_DIR=/usr/local/openssl1.1.1w/include
-DCMAKE_INSTALL_PREFIX=/opt/libvnc
..

弯路子

然后再次进行了 guacd 编译。但是还是不行。这时想起了同事前说过的链接。于是去ldd 看了下 guacd https://pic.y1nhui.com/ssl_handshake/image-4.png 可以看到,引用了 libssl1.1 但是还有老版本的 10.而vnc 的链接居然没有。

到了这一步,笔者走了一个弯路子,认为是不是只要我将guacd对libssl 10.so的全部依赖组件都升级成1.1.1w的就可以了。 甚至开始去做,比如从 guacd 的编译日志中看到了

https://pic.y1nhui.com/ssl_handshake/image-5.png

于是开始去做 openldap 的升级。 当然,这些操作是不必要的,所以这里忽略不记了。

直到最后,在一个 StackOverflow Does Apache Guacamole support VNC Connection with GNUTLS? 中有人提及了一个论坛 或者说邮件

https://pic.y1nhui.com/ssl_handshake/image-6.png

Guacd 的 vnc 连接方式完全取决于 libvncclient 。 而libvncclient 组件是 libvncserver 的一部分。 所以又回到了之前的问题上面了,libvncserver 为什么没有启用 新的 openssl。 或者说,为什么 guacd 没有使用新的 libvncserver。

这里回到 guacd 没有 vnc 依赖的问题。在同事帮助下,得知 guacd 的这几个协议是动态加载的,所以主程序没有依赖是正常的,它们在 https://pic.y1nhui.com/ssl_handshake/image-7.png

image.png

可以看到,它引用的 vnc 不是我们重新编译的,而是旧的。那么这里破案了。

Guacd -> 老libvnc -> openssl 1.0.2k

所以我们将 guacd 重新指向新的libvnc就好了,这里学习 openssl 的方法,将libvnc 也安装到了一个独立文件夹

Libvncserver rpm

考虑到前文已经给出了一个rpm的全过程,所以这里只写spec

Name:           libvncserver
Version:        0.9.15
Release:        1%{?dist}
Summary:        A library for creating VNC servers and clients

License:        GPLv2+
URL:            https://github.com/LibVNC/libvncserver
Source0:        libvncserver-LibVNCServer-%{version}.tar.gz

BuildRequires:  gcc, make, cmake
Requires:       glibc

%description
LibVNCServer is a library that makes it easy to implement VNC server or client functionality.

%prep
%setup -q -n libvncserver-LibVNCServer-%{version}

%build
# 使用 CMake 构建,添加你的编译选项
mkdir -p build
cd build
cmake -DCMAKE_C_FLAGS="-I/usr/local/openssl1.1.1w/include" \
      -DCMAKE_CXX_FLAGS="-I/usr/local/openssl1.1.1w/include" \
      -DCMAKE_EXE_LINKER_FLAGS="-L/usr/local/openssl1.1.1w/lib" \
      -DCMAKE_SHARED_LINKER_FLAGS="-L/usr/local/openssl1.1.1w/lib" \
      -DOPENSSL_ROOT_DIR=/usr/local/openssl1.1.1w \
      -DOPENSSL_LIBRARIES=/usr/local/openssl1.1.1w/lib \
      -DOPENSSL_INCLUDE_DIR=/usr/local/openssl1.1.1w/include \
      -DCMAKE_INSTALL_PREFIX=/opt/libvnc \
      ..
make %{?_smp_mflags}

%install
# 安装到指定目录
rm -rf $RPM_BUILD_ROOT
cd build
make install DESTDIR=$RPM_BUILD_ROOT

%files
/opt/libvnc/include
/opt/libvnc/lib64

%changelog
* Tue Feb 18 2025 y1nhui <yinhui.zzy@gmail.com> - 0.9.15-1
- Initial RPM release

可以看到,我将libvnc 安装到了 /opt/libvnc 文件夹下

之后就是重新编译 guacd

新 guacd

./configure --prefix=/custom/server \
            --with-init-dir=/custom/server \
            --disable-kubernetes \
            --enable-shared \
            CPPFLAGS="-I/usr/local/openssl1.1.1w/include -I/opt/libvnc/include" \
            LDFLAGS="-L/usr/local/openssl1.1.1w/lib -L/opt/libvnc/lib64/ -Wl,-rpath=/opt/libvnc/lib64"

补充了 libvnc,可以看到 指向了

https://pic.y1nhui.com/ssl_handshake/image-9.png

这个版本的guacd 也确实可以连接了

https://pic.y1nhui.com/ssl_handshake/image-10.png

这里如果有眼尖的读者,或许可以看到日志中有一个告警。这是一个坑,后续会谈到

新的问题

看上去一切ok了,但是在实际测试中发现了新的问题。只是可以连上去了,一操作就要崩溃 同时有一个偶现日志:

https://pic.y1nhui.com/ssl_handshake/image-11.png

去除对 gcrypt 的依赖

这时候还是有些迷茫的,先一步步来吧。 上面我也提到了有个告警的坑

https://pic.y1nhui.com/ssl_handshake/image-12.png

Libgcrypt 告警。但是这是 gnutls 的加解密库,openssl 并不依赖它。 笔者就在想,是不是 guacd 有类似 https 连接的一种省略方法,前期握手的时候使用非对称加密,彻底连接后使用对称加密来减少性能开销,而这一步正好用了 libgcrypt

于是继续走弯路,想要升这里,不过幸好看到了 libvncserver 的官方issue

这里提到了 关闭 ssl,开启gcrypt 的方法,那笔者反过来,关闭 gnutls 与 gcrypt 开启tls就好了

所以改成了这样。

cmake -DCMAKE_C_FLAGS="-I/usr/local/openssl1.1.1w/include" \
      -DCMAKE_CXX_FLAGS="-I/usr/local/openssl1.1.1w/include" \
      -DCMAKE_EXE_LINKER_FLAGS="-L/usr/local/openssl1.1.1w/lib" \
      -DCMAKE_SHARED_LINKER_FLAGS="-L/usr/local/openssl1.1.1w/lib" \
      -DOPENSSL_ROOT_DIR=/usr/local/openssl1.1.1w \
      -DOPENSSL_LIBRARIES=/usr/local/openssl1.1.1w/lib \
      -DOPENSSL_INCLUDE_DIR=/usr/local/openssl1.1.1w/include \
      -DCMAKE_INSTALL_PREFIX=/opt/libvnc \
      -DWITH_GNUTLS=OFF \
      -DWITH_OPENSSL=ON \
      -DWITH_FFMPEG=OFF \
      -DWITH_GCRYPT=OFF \
      ..

到这一步 gcrypt 的依赖就去除了,直接将rpm 换上去看看。

回退 libvncserver 版本

但是问题仍然存在,这时候就想照猫画虎,用一下 gdb 调试,不过看到了一个issue,他也遇到了类似的崩溃问题,但是说 使用 -f 启用就一切ok,所以我也是试了试

/custom/server/sbin/guacd -p /var/run/guacd.pid -f

然后开始运维,还是会出错,但是看到了更详细的日志 https://pic.y1nhui.com/ssl_handshake/image-13.png

这一步笔者其实没有办法了,只是在收集的时候,发现其他环境的libvnserver 都是 0.9.14,只有这里,因为是我手动编译的,所以是更新的 0.9.15 所以我干脆重新替换了一下,使用0.9.14

然后好了。。。。

具体的问题到底是什么也不知道了,笔者只给 libvncserver 官方提了个issue,等待他们修复了