OpenStack 给nova组件 vnc 配置https(SSL)自验证签名证书

先说解决办法以及结果

解决办法:Nginx 转发
结果:只转发 生成vnc 的console 地址,拼凑出https,能在Https web应用即可,比如把console url (控制台地址)iframe 进自己的页面中。
说明:如果你也是这种方式,只用一个控制台地址,放在https的web里,截至目前为至(2022年7月20日),网上大部分配置Open Stack内部组件通信的Https SSL证书的方式,我都试过了,结论就是一个,自签名证书验证失败。
如果你有自己的正规机构颁发的证书,其实也不用像你百度出来的教程,官网文档也有 不过藏得比较深,
地址:官方文档
官方是 在给horizon 配置https的过程中 配置了nova 的https 以供 console的使用。
此文章可以转发,但必须标注出处和作者,否则追究其责任

前言

openstack中默认的各个组件之间通信都是用的http,包括horizon都是,这就对一些web开发上出现一些问题。我这边开发spring项目在外网,https 是再正常不过的事,所以就开始想办法给openstack配置上 SSL,但是在生产过程中,用到的签名都是自己生成的,这就埋下了隐患。

环境

Ubuntu 20.04.4 LTS openstack yoga版本 全是新的,
嘎嘎新的系统,
嘎嘎新的Open Stack版本,
(2022年7月20日),
唉,也就是这个原因网上的教程都不多,资源有限,让我这个初学者真头大

解决过程

方法 1

给open stack 配上ssl

方法 2

只给open stack nova配上ssl

方法 3

nginx 代理转发

方法1

先说说方法1,我安装的是all-in-one ,我就一台机子,最小配置, keystone,glance,nova,placement,neutron 还有cinder,后来cinder被我停了,暂时不用,这要是给各个组件配置太麻烦了,网上也有几个教程

https://blog.csdn.net/zhongbeida_xue/article/details/101017148 这个是19年的
https://blog.csdn.net/weixin_42159480/article/details/118364310 这个是21年的但是train版本,现在官网已经过去6个版本了。。
有关这方面的都挺老的,也有借鉴意义
但是这个太费功夫,我就只想要vnc console url 带上https就行

方法2

只给nova配置 也不是不行,毕竟只有一个组件,但是查了查资料还是我太天真,因为版本问题,好多的配置文件中的配置项都变了,最后我还是从官网查到的,就是上边我提到的地址:官方文档
在给horizon 配置https的过程中 配置了nova 的https 以供 console的使用。
所以我就尝试了一下,但是emmmmmmm
跟着官网走,我先是把horizon配成了https,暂时没有问题,至于我为啥要配置,纯粹是为了跟着文档走
接下来配置nova 配置完文档上的东西,重启服务后,查看日志出现了错误,
日志在/var/log/nova/nova-api.log

  • 1、cert_file 说我在配置文件没有这个“cert_file ”配置项 但是官网给的配置项是 cert 和key
    所以我就把新的加上了,也把以前老的配置项加上了 ssl_cert_file ssl_key_file
  • 2、文件名称不对
2022-07-18 16:29:48.030 17517 ERROR oslo_service.service RuntimeError: Unable to find cert_file : /root/ssl/signing_cert.pem

但我的确是配置了证书而且不是他提示的 “signing_cert.pem” 我配置的server.crt
然后我从网上查到过教程来说,改了两个问题:

  1. 更改证书 chown -R nova:nova “证书”,好让nova能用,
  2. 其次我在/etc/nova/文件夹下 创建了ssl文件夹赋予同样的用户组和用户,并拷贝了证书文件过去,这下重启后总算是不报错了。

但出现了如下问题:

问题

自签名证书问题

现在新版本的open stack用的都是python3 包括一些包,用的都是新的,所以出现的第一个问题就是自己签发的证书被python拦截下来。导致验证不通过。

ssl.SSLError:[SSL:CERTIFICATE_VERIFY_FAILED]

在这个过程中我的证书是crt 也有配置pem的 可能报错不一样但是结论是一样的 ,python这边好像放行不了这种证书,网上的解决办法一般是 在发送请求时候加上 verify=false
比如

requests.get('https://www.baidu.com', verify=False)

但是没用啊,我总不能去改open stack python源码吧,多了去,我咋改。何况我是干Java的小白

所以至此为止,如果你有真正的证书我估计应该已经可以了,但是如果是生产过程中用的自己生成的证书,那估计不行。
所以我用了我最不想用的方法 Nginx

方法3 Nginx

这个我作为java开发我是真的不会,工作时间短,我还没来得及接触(但这不接来了😒)
先安装吧,
我在另一台电脑,安装了nginx,ip是 192.168.27.58 (同样安装在open stack节点上也行,我试过了), 我就不多说了,centos 和 Ubuntu 也不太一样 , 还有down下来 make 现编译的,网上教程很多,反正我是小白,咱就是说,就是一个apt install nginx -y 爱咋的咋的,主要是配置
来看配置
先找一下nginx 在哪
在这里插入图片描述
配置文件在 etc/nginx/nginx.conf下
但是呢 ,我也不知道上来就按照网上的教程给他改了(虽然做了备份),结果就是,一堆错,格式不对,后来我才知道,原来网上那些直接配置server的都是这个文件的配置子文件
在这里插入图片描述

在上图中,这有个include 地址下 去新建符合格式的配置文件
我建了一个openstack-nginx.conf 文件
在做配置文件之前,如果你有心的话,应该去看看 那个控制台页面的结构,以方便理解写配置文件

vnc_auto.html

这个文件所在位置:/usr/share/novnc 全局查找一下就好
页面上,不用看引入的js,只看页面中js,
在< head > 中 位置如下
在这里插入图片描述
大致看一眼就能看来他想用websocket
往下找,
在这里插入图片描述
这个位置,是表明,这个页面会根据 https还是http来更换端口,
接着往下看
在这里插入图片描述

此处是重点,他在页面中拼接了一个 url地址供给rfb去调用后面websocket,同样根据是否是https链接,来选择是ws还是wss,也就是说 websocket的连接地址是在此处生成的。

红色框内的内容是我自己 修改源代码 加入进去的 ,为了方便查看

url += "/wss" + path;

其中这一句是用于nginx 转发websocket的ws://*** 用的
也就是说在我修改完源代码页面后,如果访问https 控制台地址,websoket的链接是:

wss:// * * * * */wss?token=* * * * * * * 

这种格式
而其中的/wss是我自己写的,你们写成别的也可以 比如/websocket 啥都行,但是要在nginx配置中对应,这个主要是nginx监听websocket 以转发用的

接下里看看具体nginx怎么配置的

map $http_upgrade $conn_upgrade {
      default upgrade;
      '' close;
    }
upstream nova 
 {    
  server 192.168.27.48:6080;   
}
 
server {    
    listen 80;    
    listen 443 ssl;
    server_name 192.168.27.58;       
    ssl on;
    ssl_certificate      /etc/nginx/server.crt;
    ssl_certificate_key  /etc/nginx/server.key; 
    ssl_ciphers          ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    ssl_session_cache    shared:SSL:10m; 
    ssl_session_timeout  10m; 

 location /  {          
      proxy_set_header Host $host;               
      proxy_set_header X-Forwarded-For $remote_addr;              
      proxy_redirect  off;               
      proxy_pass http://nova;       
   }       
  location /wss  {
      proxy_set_header Host $host;
      proxy_set_header X-Forwarded-For $remote_addr;
      proxy_set_header  X-Forwarded-Proto   http; #这个是重点!!!!是个坑后面会讲(记得删掉#后面的注释)
      proxy_redirect  off;
      proxy_pass http://nova;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connetion $conn_upgrade;

   }
  access_log off;
}


做一下说明
首先我这个配置文件时最后成功时的最终配置,基本一股脑的抄就行,个别地方自己做修改,证书位置、ip 、名字啥的
首先nginx所在的服务器(电脑)host配置上 控制节点 我这里是192.168.27.48 controller

192.168.27.48:6080 : 192.168.27.48 是我open stack服务器的nova节点地址(和controller在一起),端口号6080是console vnc的接口 这个是指定的改不了,
192.168.27.58 : 这是我nginx 的电脑的地址,直接监听80端口 转发到443上,如果你nginx安装在 openstack 服务器上,不能监听80端口,因为此端口被horizon占用着,切记。
主要说明的是:
1、location /
这个是配置转发 console url 地址的
2、location /wss
这个是另一大问题,就是当console url https:////vnc_auto.htm?path=
这个页面时候,他的js 会把websocket 从ws改成wss(上面已经说过),也是这个时候我才知道,他起了一个websocket服务器 用来画这个命令行界面,(日哦)
所以 这就得把ws也得交给nginx转发成wss。
至于说为啥是 “/wss” 我也在上文中解释过。

如果一切顺利的话(怎么可能),我们接下来做一些验证操作
首先保证nginx 启动正常,配置文件没有不合法的问题

  • 去访问下 https://192.168.27.58/vnc_auto.html(https://controller/vnc_auto.html) —这个58地址就是nginx那个服务器的地址,且监听的80端口,server_name 写的也是
  • 如果出现了以下页面就代表这nginx转发 控制台页面成功了
  • 在这里插入图片描述
    因为我们没有给path 后面的token嘛,所以失败也正常
    只是说 通过https的方式能访问的这个页面就可以了

下一步:
我们从命令中启动一个虚拟机 然后查看一下console url 看看真实的情况

root@controller:~# openstack server list
/usr/lib/python3/dist-packages/secretstorage/dhcrypto.py:15: CryptographyDeprecationWarning: int_from_bytes is deprecated, use int.from_bytes instead
  from cryptography.utils import int_from_bytes
/usr/lib/python3/dist-packages/secretstorage/util.py:19: CryptographyDeprecationWarning: int_from_bytes is deprecated, use int.from_bytes instead
  from cryptography.utils import int_from_bytes
+--------------------------------------+------------+---------+------------------------+---------+-------------+
| ID                                   | Name       | Status  | Networks               | Image   | Flavor      |
+--------------------------------------+------------+---------+------------------------+---------+-------------+
| ae5ee76f-3dc0-4de3-8243-26ee47751e46 | vm2        | SHUTOFF | selfservice=10.0.0.162 | cirror  | m1.nano     |
| e7f6f376-5a50-4031-bfb5-e8ade9cdb6d7 | vm1        | SHUTOFF | selfservice=10.0.0.223 | cirror  | m1.nano     |
| afa53d9e-ed0c-423f-9df4-65012276ea30 | cirros4j 2 | SHUTOFF | networke4j=10.0.0.221  | ubuntu1 | ubuntu tiny |
+--------------------------------------+------------+---------+------------------------+---------+-------------+
启动vm1
root@controller:~# openstack server start vm1

ok 我选择启动了vm1

查看vm1的vnc url
root@controller:~# openstack console url show vm1
/usr/lib/python3/dist-packages/secretstorage/dhcrypto.py:15: CryptographyDeprecationWarning: int_from_bytes is deprecated, use int.from_bytes instead
  from cryptography.utils import int_from_bytes
/usr/lib/python3/dist-packages/secretstorage/util.py:19: CryptographyDeprecationWarning: int_from_bytes is deprecated, use int.from_bytes instead
  from cryptography.utils import int_from_bytes
+----------+-------------------------------------------------------------------------------------------+
| Field    | Value                                                                                     |
+----------+-------------------------------------------------------------------------------------------+
| protocol | vnc                                                                                       |
| type     | novnc                                                                                     |
| url      | http://controller:6080/vnc_auto.html?path=%3Ftoken%3D89fa5c04-a7bd-4ec5-980a-afb24cd75e5e |
+----------+-------------------------------------------------------------------------------------------+

此时我们发现 url地址还是http 的形式,因为毕竟,在open stack服务器上 各个组件我们都没有动过配置。
我们主要得到的是token 就是path后面的。
然后我们自己拼接上去 第一步测试的地址后面
https://192.168.27.58/vnc_auto.html?path=%3Ftoken%3D89fa5c04-a7bd-4ec5-980a-afb24cd75e5e
在这里插入图片描述
可以在控制台中看到 websoket 改造完的地址是 wss://192.168.27.58:443/wss?token=89fa5c04-a7bd-4ec5-980a-afb24cd75e5e 其中/wss被nginx监听转发到内部/ws
至此应该就成功了。
接下来说一些一些疑点问题

疑点-问题-错误

有可能 在上一步 获取真实的token,拼接完整的url地址 访问依然报错(比如我就是)
在这里插入图片描述
控制台说,依然连接不上,
此时我去查了一下日志nova 的novnc日志
截取部分如下

2022-07-19 17:20:24.267 43633 INFO nova.console.websocketproxy [-] 127.0.0.1 - - [19/Jul/2022 17:20:24] 127.0.0.1: Plain non-SSL (ws://) WebSocket connection
2022-07-19 17:20:24.269 43633 INFO nova.console.websocketproxy [-] 127.0.0.1 - - [19/Jul/2022 17:20:24] 127.0.0.1: Path: '/wss?token=2a4f2cf4-b49f-4ecd-a188-83224911b067'
2022-07-19 17:20:26.041 43633 INFO nova.console.websocketproxy [req-dcb5e7ba-0a27-42c1-bff2-20f2c326e5d0 - - - - -] handler exception: Origin header protocol does not match this host.

第一条说是准备一个 no-SSL的 websoket连接
第二条说是获得了path,
第三条是重点,说是 Origin header protocol does not match this host.
意思是:原始报头协议与此主机不匹配。

这就很迷了,我在nginx 都转发了真是的地址啊,等等!!真实地址?

此时我决定搜一搜 ’Origin header protocol does not match this host. ‘
一下找到了源码,发现这个报错的python文件叫做 websocketproxy.py
那我就找一下

root@controller:~# find / -name websocketproxy.py
/usr/lib/python3/dist-packages/websockify/websocketproxy.py
/usr/lib/python3/dist-packages/nova/console/websocketproxy.py
find: ‘/run/user/1000/doc’: 权限不够
find: ‘/run/user/1000/gvfs’: 权限不够

根据网上的提示,真正的文件是第二个 /usr/lib/python3/dist-packages/nova/console/websocketproxy.py
那我就进去看看
通过vim的查找,找到这句话

# Verify Origin   来验证下源
        expected_origin_hostname = self.headers.get('Host')
        if ':' in expected_origin_hostname:
            e = expected_origin_hostname
            if '[' in e and ']' in e:
                expected_origin_hostname = e.split(']')[0][1:]
            else:
                expected_origin_hostname = e.split(':')[0]
        expected_origin_hostnames = CONF.console.allowed_origins
        expected_origin_hostnames.append(expected_origin_hostname)
        origin_url = self.headers.get('Origin')
        # missing origin header indicates non-browser client which is OK
        # 即使没有这个也是ok的 可能是用于IOS 浏览器吧
        if origin_url is not None:
            origin = urlparse.urlparse(origin_url)
            origin_hostname = origin.hostname
            origin_scheme = origin.scheme
            # If the console connection was forwarded by a proxy (example:
            # haproxy), the original protocol could be contained in the
            # X-Forwarded-Proto header instead of the Origin header. Prefer the
            # forwarded protocol if it is present.
            #问题在这里 如果经过了代理服务器的转发 他回去拿X-Forwarded-Proto  当作此时的origin_scheme 由于我们转发了真实的协议
            forwarded_proto = self.headers.get('X-Forwarded-Proto')
            if forwarded_proto is not None:
                origin_scheme = forwarded_proto
            if origin_hostname == '' or origin_scheme == '':
                detail = _("Origin header not valid.")
                raise exception.ValidationError(detail=detail)
            if origin_hostname not in expected_origin_hostnames:
                detail = _("Origin header does not match this host.")
                raise exception.ValidationError(detail=detail)
            if not self.verify_origin_proto(connect_info, origin_scheme):
            # 报错位置在这里 因为此if判断不通过
                detail = _("Origin header protocol does not match this host.")
                raise exception.ValidationError(detail=detail)

我们可以看到 在最后一个if 判断不通过 导致的报错 “Origin header protocol does not match this host.” 我能看懂python 但我又不会,他到底是怎么判断的,所以最笨的方法
改一下源码输出点 判断条件看看(个人认为改源码是大忌,但是我只看 又不改他逻辑,所以问题不大)

if not self.verify_origin_proto(connect_info, origin_scheme):
                detail = _("Origin header protocol does not match this host."+"connect_info:"+str(connect_info)+",origin_scheme:"+str(origin_scheme))
                raise exception.ValidationError(detail=detail)

妥妥,重启下nova服务,重新走一遍再看输出
handler exception: Origin header protocol does not match this host.connect_info:ConsoleAuthToken(access_url_base=‘http://controller:6080/vnc_auto.html’,console_type=‘novnc’,created_at=2022-07-19T09:37:53Z,host=‘192.168.27.48’,id=96,instance_uuid=e7f6f376-5a50-4031-bfb5-e8ade9cdb6d7,internal_access_path=None,port=5900,token=‘6e34ceac-1d36-45a2-9c46-4fe74df6799b’,updated_at=None)origin_scheme:https
此时看到两个判断条件,我就看出来了, origin_scheme是https 由于nginx 之前我配置的是 (跟网上学的)
proxy_set_header X-Forwarded-Proto $scheme

转发真实的协议 但是我们nginx是做的 wss-》ws 内部用的还是 http connect_info下没一个https 字眼,所以再怎么傻我也知道,判断肯定是不通过,所以我就投机取巧
nginx配置改为
proxy_set_header X-Forwarded-Proto http

跳过你的判断不就行了

重启nginx
重新走openstack 获取vm1 控制台地址流程,浏览器拼接,访问,成功!!!!

后续

我的程序中用的是openstack4j api 在调整好一切后 拿到特定虚拟机的控制台地址
依然是http://controller:6080/vnc_auto.html?path=%3Ftoken%3D89fa5c04-a7bd-4ec5-980a-afb24cd75e5e
我只取path后面的字符串发给前端,前端在前面拼接上https://192.168.27.58/vnc_auto.html 然后放到iframe中去 ,剩下的基本就是前端同学的活了,来调用我接口就好。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值