背景
由于原来使用的 Nacos 版本(1.1.4)存在安全漏洞,需要进行升级修复。经过查询后,决定将版本升级到2.2.4。
Nacos 服务共有三个:
- 192.168.2.190:8848
- 192.168.2.191:8848
- 192.168.2.192:8848
步骤
- 服务端升级(不开鉴权)
- 客户端升级
- 服务端开启鉴权
说明:服务端升级到 2.2.4 版本,如果不需要开启鉴权,则客户端可以不升级,因为配置中心兼容支持 Nacos1.0 起的所有版本客户端,服务发现兼容 Nacos1.2 起所有版本客户端。
具体参考:https://nacos.io/zh-cn/docs/v2/upgrading/2.0.0-compatibility.html(FAQ)
一、服务端升级
1. 修改 Nginx 代理配置(nginx.conf)
说明:在1.1.4版本时,我们对 Nacos 服务端的协议进行了升级(即 http -> https),方式是通过 Nginx 进行代理转发,从 8848 转发到 8849 端口。而在 2.2.4 版本的 Nacos 中,新增了 gRPC 的通信方式,需要新增端口,因此这里在 nginx.conf 需要新增一些端口转发配置。
具体参考:https://nacos.io/zh-cn/docs/v2/upgrading/2.0.0-compatibility.html(新版本部署)
# 具体路径以实际为准,这里仅供参考
# 若不知道配置文件位置,可通过 history 或 find ./ -name "nginx.conf" 2> /dev/null 命令查找
# 备份配置文件
cp /home/appuser/nginx/conf/nginx.conf /home/appuser/nginx/conf/nginx.conf_20240114_bak
# 修改配置文件
vim /home/appuser/nginx/conf/nginx.conf
# 在 http{} 同层级下添加以下内容
stream {
upstream nacos-tcp {
# IP 与所在服务器需对应
server 192.168.2.190:9849;
}
server {
listen 9848;
proxy_pass nacos-tcp;
}
}
# 检查配置修改是否正确
/home/appuser/nginx/sbin/nginx -t
# 重新加载配置文件
/home/appuser/nginx/sbin/nginx -s reload
2. 执行SQL
Nacos 2.x 版本相比 Nacos 1.x 版本,数据结构有所变更,以下是相应 SQL 脚本:
注意:SQL 脚本需在新版本启动前执行,否则将启动报错
ALTER TABLE nacos_config.config_info ADD encrypted_data_key TEXT NOT NULL COMMENT '秘钥';
ALTER TABLE nacos_config.config_info_beta ADD encrypted_data_key TEXT NOT NULL COMMENT '秘钥';
ALTER TABLE nacos_config.his_config_info ADD encrypted_data_key TEXT NOT NULL COMMENT '秘钥';
CREATE TABLE nacos_config.`permissions` (
`role` varchar(50) NOT NULL,
`resource` varchar(255) NOT NULL,
`action` varchar(8) NOT NULL,
UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE
);
3. 修改配置文件
建议直接在本地修改好相应的配置文件之后,再压缩上传到服务器,避免配置文件修改不一致出现问题。
- application.properties
### 服务端端口号 server.port=8849 ### 使用数据库类型 spring.datasource.platform=mysql db.num=1 db.url.0=jdbc:mysql://192.168.2.193:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC db.user.0=root db.password.0=root ### 是否开启鉴权(这里暂时不开启,因为客户端是1.1.4,开启后将会导致服务注册不了) nacos.core.auth.enabled=false ### 开启旧版本服务之间的鉴权方式,升级完成后关闭 nacos.core.auth.enable.userAgentAuthWhite=true ### 白名单授权,用于标识来自其他nacos服务器的请求。(根据需要自行修改,需要保证每台nacos服务器都是一样配置) nacos.core.auth.server.identity.key=identityRegexp nacos.core.auth.server.identity.value=identityRegexp ### 用于生成accessToken的密钥(根据需要自行修改,需要保证每台nacos服务器都是一样配置) # 官方推荐将配置项设置为Base64编码的字符串,且原始密钥长度不得低于32字符 nacos.core.auth.plugin.nacos.token.secret.key=aWRlbnRpdHlpZGVudGl0eWlkZW50aXR5aWRlbnRpdHlpZGVudGl0eQ==
- cluster.conf
192.168.2.190:8848 192.168.2.191:8848 192.168.2.192:8848
4. 升级服务端(依次升级)
## 上传安装包(可以通过rz命令或sftp上传)
rz
## 解压缩
unzip nacos-2.2.4.zip -d /home/appuser
## 停止旧服务
sh /home/appuser/nacos-1.1.4/bin/shutdown.sh
## 启动新服务
sh /home/appuser/nacos-2.2.4/bin/startup.sh
## 查看启动日志
tail -f /home/appuser/nacos-2.2.4/logs/start.out -n 10
## 若观察到 mybatis-plus 图标,并没有报错,即启动成功
## 登录nacos页面(https://192.168.0.190:8848/nacos)查看是否正常
二、客户端升级
据我了解,客户端升级有两种方式:
- 第一种:直接升级 Spring Boot、Spring Cloud、Spring Cloud Alibaba 版本
- 第二种:升级 Nacos Client 版本,并重写源码
由于我们项目比较大 + 时间紧,第一种升级方式的风险比较大,故选择第二种方式。
1. 添加依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-common</artifactId>
<version>2.2.4</version>
</dependency>
2. 添加配置
bootstrap.yml
spring:
cloud:
nacos:
discovery:
username: nacos
password: nacos
config:
username: nacos
password: nacos
contextPath: /nacos
3. 重写 Nacos 客户端源码
重写方式:在项目的 java 目录下新建需重写类所在的包路径,然后复制源码到该路径下并进行修改,比如:NacosDiscoveryProperties 类所在包路径为 com.alibaba.cloud.nacos,则在 java 目录下新建该包,然后复制源码
-
NacosDiscoveryProperties.java
public class NacosDiscoveryProperties { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Properties getNacosProperties() { properties.put(USERNAME, Objects.toString(username, "")); properties.put(PASSWORD, Objects.toString(password, "")); } }
-
NacosConfigProperties.java
public class NacosConfigProperties { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Properties getConfigServiceProperties() { properties.put(USERNAME, Objects.toString(username, "")); properties.put(PASSWORD, Objects.toString(password, "")); } }
由于我们所用的 Nacos 使用的是 https 协议,所以还需要重写 ServerHttpAgent 和 NacosRestTemplate 以支持 SSL。
-
ServerHttpAgent.java
public class ServerHttpAgent implements HttpAgent { public static final String HTTP = "http://"; public static final String HTTPS = "https://"; private static final boolean isSSL = ObjectUtils.isEmpty(System.getProperty("nacos.ssl.enable")) || Boolean.parseBoolean(System.getProperty("nacos.ssl.enable")); static { if (isSSL) { try { TrustManager[] managers = new TrustManager[1]; managers[0] = new TrustAllManager(); SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, managers, null); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier((urlHostName, session) -> true); } catch (Exception e) { LOGGER.error("trust all https certificates fail", e); } } } public static class TrustAllManager implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s) { } @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s) { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } } private String getUrl(String serverAddr, String relativePath, boolean isSSL) { String contextPath = serverListMgr.getContentPath().startsWith("/") ? serverListMgr.getContentPath() : "/" + serverListMgr.getContentPath(); String url = StringUtils.removeEnd(serverAddr, "/") + "/" + StringUtils.removeStart(contextPath, "/") + relativePath; if (isSSL && url.startsWith(HTTP)) { return HTTPS + StringUtils.removeStart(url, HTTP); } else { return url; } } }
-
NacosRestTemplate.java
public class NacosRestTemplate extends AbstractNacosRestTemplate { private static final Logger LOGGER = LogUtils.logger(NacosRestTemplate.class); private static final boolean isSSL = ObjectUtils.isEmpty(System.getProperty("nacos.ssl.enable")) || Boolean.parseBoolean(System.getProperty("nacos.ssl.enable")); static { if (isSSL) { try { TrustManager[] managers = new TrustManager[1]; managers[0] = new ServerHttpAgent.TrustAllManager(); SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, managers, null); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); HttpsURLConnection.setDefaultHostnameVerifier((urlHostName, session) -> true); } catch (Exception e) { LOGGER.error("trust all https certificates fail", e); } } } public static class TrustAllManager implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s) { } @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s) { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } } }
三、服务端开启鉴权
# 编辑配置文件
vim /home/appuser/nacos-2.2.4/conf/application.conf
# 找到以下行,将 false 修改为 true 即可,无需重启
nacos.core.auth.enabled=false
开启之后观察服务日志与 Nacos 日志是否正常