Jax's Blog

Happy Coding,Happy Life


  • Home

  • About

  • Tags

  • Archives

记一次docker容器中被植入kdevtmpfsi挖矿程序

Posted on 2020-10-11 | | Visitors:

某天,晴,准时下班与朋友小聚,酒足饭饱后正商量着第二场活动,这时手机短信响起,随手拿起一看。卧槽,提示服务器蠕虫病毒。遂与友人say sorry。回家打开电脑开始杀毒并排查原因。

0POxdb

首先打开阿里云看下日志。这里还是要点赞一下阿里云的云盾,服务器防护与预警属实做的很牛逼。日志提示服务器被植入挖矿程序,并且告知所在进程

image-20201029210354849

查看到是在容器内植入,好在没有植入我的宿主机。当前主机挂载了好几个容器服务。并且宿主机有做端口限制,应该是跑不出去。

逐步排查原因:

执行top可看到挖矿程序疯狂占用CPU。

image-20201029211045157

ps查看进程运行目录,发现tmp中不存在这个文件,猜想可能是docker容器中的进程。

PrvH89

执行 docker stats 查看容器运行情况。能够发现cpu占用高的容器。

f74cMY

执行 docker top 容器id 查看容器进程详细信息

PHUCaI

为什么会被植入?

  • 网上说的redis漏洞。

    该镜像没有集成redis。PASS

  • 镜像本身是否存在病毒?

    很多镜像可能会集成第三方的插件,或者jar包。那么排查镜像源问题,可使用命令 docker inspect <镜像id> 查看镜像元数据进行排查。我镜像都是自己打包上传,并且本地重新下载镜像并未发现任何异常。大概率PASS

  • 远程执行漏洞?

    那就很有可能是存在这个问题了。该docker容器开放了9999(用于xxl-job分布式事务),8085用于spring-boot服务(大概率可以排除)。那就应该是XXL-JOB的锅了。搜索得知,XXL-JOB远程执行漏洞导致kdevtmpfsi挖矿病毒:

    https://help.aliyun.com/noticelist/articleid/1060736995.html?spm=5176.2020520154.sas.52.e96d1e430g70fo

最终解决

1、9999端口加上IP限制,只允许xxljob-admin源服务器ip地址调用。

2、xxl-job增加授权验证,配置 xxl.job.accessToken 防止未授权访问漏洞。

如何单模块打包到自己的私有仓

Posted on 2020-07-11 | | Visitors:

如何不使用Nexus搭建属于自己团队的一个maven或者Gradle私有仓。这里推荐使用阿里云新出的产品:云效。云效有个产品叫做制品库,我们可以利用制品库快速搭建自己的maven私有仓库。

云效:制品库

注册阿里云云效平台账号:https://devops.aliyun.com/。进入制品库,发现已经拥有发布以及快照两个仓。点击进去会有文档告知如何配置maven环境,推荐方式1直接覆盖settings文件即可

Idea设置使用自定义settings文件

如何单模块打包

首先弄懂maven的snapshot快照仓库和release发布仓库,所以打包也分为release包跟snapshot快照包。snapshot快照仓库用于保存开发过程中的不稳定版本,release正式仓库则是用来保存稳定的发行版本。
定义一个组件/模块为快照版本,只需要在pom文件中在该模块的版本号后加上-SNAPSHOT即可(注意这里必须是大写),如下:

1
2
3
4
<groupId>com.rednet</groupId>
<artifactId>m1</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>jar</packaging>

如果是快照版本,那么在mvn deploy时会自动发布到快照版本库中,而使用快照版本的模块,在不更改版本号的情况下,直接编译打包时,maven会自动从镜像服务器上下载最新的快照版本。但是release不改版本号情况会出现如下错误

Failed to transfer file https://packages.aliyun.com/maven/repository/2019159-release-3dosVm/com/rednet/framework/spring-boot-starter-sms/1.0.2/spring-boot-starter-sms-1.0.2.jar with status code 409

IDEA无法更新最新的snapshot快照包,如何解决?IDEA->设置-》maven-》勾选aways update snapshots。

在模块中直接运行以下代码即可推送jar包到自己的阿里云私有仓
`
$ mvn clean deploy -DskipTests

`
也可以在IDEA中点击maven-》Lifecycle-》deploy进行发布。

Openjdk1.8字体缺失导致获取图片验证码出错

Posted on 2020-06-11 | | Visitors:

问题

1
2
3
4
5
6
7
8
9
10
Caused by: java.lang.NullPointerException: null
at sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264) ~[na:1.8.0_151]
at sun.awt.FontConfiguration.readFontConfigFile(FontConfiguration.java:219) ~[na:1.8.0_151]
at sun.awt.FontConfiguration.init(FontConfiguration.java:107) ~[na:1.8.0_151]
at sun.awt.X11FontManager.createFontConfiguration(X11FontManager.java:774) ~[na:1.8.0_151]
at sun.font.SunFontManager$2.run(SunFontManager.java:431) ~[na:1.8.0_151]
at java.security.AccessController.doPrivileged(Native Method) ~[na:1.8.0_151]
at sun.font.SunFontManager.<init>(SunFontManager.java:376) ~[na:1.8.0_151]
at sun.awt.FcFontManager.<init>(FcFontManager.java:35) ~[na:1.8.0_151]
at sun.awt.X11FontManager.<init>(X11FontManager.java:57) ~[na:1.8.0_151]

原因

Openjdk1.8字体缺失导致

解决方案

只需要在项目的dockerFile部署文件里面添加下面这一条即可:

RUN apk add –update ttf-dejavu fontconfig

补充

但是加了之后项目构建会变得很慢。这之后需要切换使用国内镜像,具体写法如下:

1
2
3
4
5
# Install cURL
RUN echo -e "https://mirror.tuna.tsinghua.edu.cn/alpine/v3.4/main\n\
https://mirror.tuna.tsinghua.edu.cn/alpine/v3.4/community" > /etc/apk/repositories
ENV LANG en_US.UTF-8
RUN apk add --update ttf-dejavu fontconfig && rm -rf /var/cache/apk/*

frp开启https

Posted on 2019-09-27 | | Visitors:

frp的搭建

看另外一篇文章 : frp搭建内网穿透服务

首先开启服务端的https请求端口

1
2
3
4
5
6
7
[common]
# frp 服务所使用的端口
bind_port = 7000
# HTTPS 协议建议使用 443 端口
vhost_https_port = 443
# HTTP 协议建议使用 80 端口
vhost_http_port = 80
Read more »

详解分布式事务之 Seata-Client 原理及流程

Posted on 2019-06-11 | | Visitors:

前言

在分布式系统中,分布式事务是一个必须要解决的问题,目前使用较多的是最终一致性方案。自年初阿里开源了Fescar(四月初更名为Seata)后,该项目受到了极大的关注,目前已接近 8000 Star。Seata 以高性能和零侵入的特性为目标解决微服务领域的分布式事务难题,目前正处于快速迭代中,近期小目标是生产可用的 Mysql 版本。
基于 spring cloud + spring jpa + spring cloud alibaba fescar + mysql + seata 的结构,搭建一个分布式系统的 demo,通过 seata 的 debug 日志和源代码,从 client 端(RM、TM)的角度分析其工作流程及原理。(示例项目:https://github.com/fescar-group/fescar-samples/tree/master/springcloud-jpa-seata)
为了更好地理解,我们来熟悉一下相关概念:

  • XID:全局事务的唯一标识,由 ip:port:sequence 组成;
  • Transaction Coordinator (TC):事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚;
  • Transaction Manager (TM ):控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议;
  • Resource Manager (RM):控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚;
    提示:文中代码是基于 fescar-0.4.1 版本,由于项目刚更名为 seata 不久,其中一些包名、类名、jar包等名称还没统一更换过来,故下文中仍使用 fescar 进行表述。
    Read more »

使用nacos做注册中心导致log4j日志服务异常

Posted on 2019-04-11 | | Visitors:

问题起源

日志框架用的log4j,接入阿里云Alibaba Cloud Log Log4j Appender做日志采集,以及错误预警。某天发现线上无法正常采集日志数据,怀疑是dubbo日志包冲突影响导致。报错信息:

1
2
3
4
5
main DEBUG Using configurationFactory org.apache.logging.log4j.core.config.ConfigurationFactory$Factory@1153ee42

main DEBUG Closing JarURLInputStream sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@31fcad71

main WARN No Root logger was configured, creating default ERROR-level Root logger with Console appender

问题追溯

IDEA有个功能,可以查看maven项目依赖总览图。如图所示功能:
image
这样很方便就可以找到是哪个jar包包含的依赖有冲突。
去除了dubbo的log4j依赖,问题依然重现。再判断是否nacos导致的。查看nacos源码发现以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public Log4J2NacosLogging() {
String location = this.getLocation("classpath:nacos-log4j2.xml");
if(!StringUtils.isBlank(location)) {
this.locationList.add(location);
}

}

public void loadConfiguration() {
if(!this.locationList.isEmpty()) {
List configList = this.findConfig(this.getCurrentlySupportedConfigLocations());
if(configList != null) {
this.locationList.addAll(configList);
}

ArrayList configurations = new ArrayList();
LoggerContext loggerContext = (LoggerContext)LogManager.getContext(false);
Iterator compositeConfiguration = this.locationList.iterator();

while(compositeConfiguration.hasNext()) {
String location = (String)compositeConfiguration.next();

try {
Configuration e = this.loadConfiguration(loggerContext, location);
if(e instanceof AbstractConfiguration) {
configurations.add((AbstractConfiguration)e);
}
} catch (Exception var7) {
throw new IllegalStateException("Could not initialize Log4J2 Nacos logging from " + location, var7);
}
}

CompositeConfiguration compositeConfiguration1 = new CompositeConfiguration(configurations);
loggerContext.start(compositeConfiguration1);
}
}

nacos注册中心会查找nacos的日志配置文件,如果找不到怎么办呢,再下面代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private String[] getCurrentlySupportedConfigLocations() {
ArrayList supportedConfigLocations = new ArrayList();
if(ClassUtils.isPresent("com.fasterxml.jackson.dataformat.yaml.YAMLParser")) {
Collections.addAll(supportedConfigLocations, new String[]{"log4j2.yaml", "log4j2.yml", "log4j2-test.yaml", "log4j2-test.yml"});
}

if(ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper")) {
Collections.addAll(supportedConfigLocations, new String[]{"log4j2.json", "log4j2.jsn", "log4j2-test.json", "log4j2-test.jsn"});
}

supportedConfigLocations.add("log4j2.xml");
supportedConfigLocations.add("log4j2-test.xml");
return (String[])supportedConfigLocations.toArray(new String[supportedConfigLocations.size()]);
}

nacos会引用它默认的配置信息,导致之前项目的log4j配置失效。如何禁用nacos的默认配置,在它配置工厂类有写:

private boolean isDefaultConfigEnabled() {
        String property = System.getProperty("nacos.logging.default.config.enabled");
        return property == null || BooleanUtils.toBoolean(property);
}

我们只需要在系统变量中设置nacos.logging.default.config.enabled=false,然后把nacos的日志配置信息放到我们项目中的log4j文件中即可,在nacos-client源码中我们可以找到log4j以及logback的写法。

打造云上DevOps,基于阿里云codepipeline,docker做持续交付实践

Posted on 2019-03-12 | | Visitors:

背景

项目进行服务化改造之后,dubbo服务加上其他应用服务有上十个微服务需要构建,面临一个服务发布以及运维工作量剧增。业内比较常用的自动构建工具是Jenkins。看一下它的流程以及工具集:
image
可以看到,非常强大,同时也非常复杂。网上收集整理出来的几大问题。

  1. 架构陈旧,性能底下。(250个slave,1000个job)。
  2. 安全漏洞频发
  3. 运维成本大
  4. 缺乏完善的权限模型

最终选型落在阿里云的codepipeline,目前免费中,估计后期肯定会收费,阿里云一贯的作风,先免费后收费。

codepipeline介绍

阿里云CodePipeline是兼容Jenkins标准的、提供快速可靠的持续集成与持续交付服务。基于容器技术和阿里云基础服务架构,提供稳定和安全的代码/Docker编译构建,测试,扫描和部署的工具服务,并提供Pipeline As Code的编码级配置模式,满足应用程序和基础设施快速可靠的交付和更新。

开通CodePipeline

  1. 开通CodePipeline
    1. 登录阿里云控制如后,通过如下导航找到CodePipeline。
      cbedb20e3464587ff60fa781fd71ef8d.png
    2. 点击图中菜单按钮来到服务开通页面,点击开通服务按钮.
  2. 创建项目和运行项目
    1. 登录阿里云控制台后,通过菜单导航到CodePipeline后,出现如下界面。
    2. 点击新建按键,来到创建项目导航页面。输入项目名称Cloud_DevOps,并选择Java项目,点击下一步,
    3. 来到代码配置构建页面,在“仓库地址”中输入如下代码,这是一个普通的Java Web示例项目,可以通过Git Clone后查看其内容。

CodePipeline架构

image

CodePipeline部署流程

image

阿里云服务器专有网络环境下链接FTP失败

Posted on 2019-02-22 | | Visitors:

问题显示

服务器:阿里云ECS
网络环境:专有网络
FTP服务:vsftpd
连接异常:

1
2
3
4
5
6
状态: 	已登录<br>
状态: 读取目录列表...
状态: 服务器发回了不可路由的地址。被动模式失败。
命令: PORT 172,20,10,5,229,9
响应: 500 Illegal PORT command.
错误: 读取目录列表失败
## 问题追溯 原因:专有网络的ECS系统中,没有公网IP地址,是经NAT与互联网连接,且ECS创建过程中的默认安全组规则没有针对FTP的快捷选项。建议在Linux系统里使用sftp协议替换ftp使用。 。 ## 解决方案 1. 修改vsftpd配置
1
2
3
4
5
pasv_enable=YES  # 启用pasv模式
pasv_min_port=30000 # 设置pasv模式中的可用端口范围(开始)
pasv_max_port=30100 # 设置pasv模式中的可用端口范围(结束)
pasv_address=39.108.4.89 # 设置pasv模式中的外网IP
seccomp_sandbox=NO # 关闭 seccomp 功能
2. 在ECS实例安全组中,分别增加两条规则,允许相应的tcp端口访问(tcp 21端口,和 tcp 3000到30100端口) 3. 重启vsftpd服务。客户端传输设置改成主动模式,fileZilla被动模式设置该生:退回到主动模式。

dubbo服务在docker容器中进行服务编排

Posted on 2019-01-23 | | Visitors:

问题起源

最近在对现有业务系统进行Dubbo服务化重构,部署方式采用Docker部署,注册中心采用nacos。当部署完成之后会发现消费者服务能正常启动,服务在注册中心显示正常,但是进行服务调用的时候就会报错,对应的服务无响应信息。

1
com.alibaba.dubbo.remoting.RemotingException: message can not send, because channel is closed

问题追溯

报错信息显示comsumer地址是172.18.*.*,这是docker容器的ip地址,猜想应该是容器之间无法进行调用。把docker服务启动参数修改成宿主机的内网地址,依然还是报错。是否应该是服务端口号不一致?查看dubbo端口绑定源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* Register port and bind port for the provider, can be configured separately
* Configuration priority: environment variable -> java system properties -> port property in protocol config file
* -> protocol default port
*
* @param protocolConfig
* @param name
* @return
*/
private Integer findConfigedPorts(ProtocolConfig protocolConfig, String name, Map<String, String> map) {
Integer portToBind = null;

// parse bind port from environment
String port = getValueFromConfig(protocolConfig, Constants.DUBBO_PORT_TO_BIND);
portToBind = parsePort(port);

// if there's no bind port found from environment, keep looking up.
if (portToBind == null) {
portToBind = protocolConfig.getPort();
if (provider != null && (portToBind == null || portToBind == 0)) {
portToBind = provider.getPort();
}
final int defaultPort = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).getDefaultPort();
if (portToBind == null || portToBind == 0) {
portToBind = defaultPort;
}
if (portToBind == null || portToBind <= 0) {
portToBind = getRandomPort(name);
if (portToBind == null || portToBind < 0) {
portToBind = getAvailablePort(defaultPort);
putRandomPort(name, portToBind);
}
logger.warn("Use random available port(" + portToBind + ") for protocol " + name);
}
}

// save bind port, used as url's key later
map.put(Constants.BIND_PORT_KEY, String.valueOf(portToBind));

// registry port, not used as bind port by default
String portToRegistryStr = getValueFromConfig(protocolConfig, Constants.DUBBO_PORT_TO_REGISTRY);
Integer portToRegistry = parsePort(portToRegistryStr);
if (portToRegistry == null) {
portToRegistry = portToBind;
}

return portToRegistry;
}

整个方法最核心的代码:

1
String port = getValueFromConfig(protocolConfig, Constants.DUBBO_PORT_TO_BIND);

通过读取系统环境变量:DUBBO_PORT_TO_BIND获取暴露端口。如何修改这个参数呢?想到了docker三剑客之docker-compose。现在多个服务要逐个启动,正好使用compose可以解决这个问题。

Read more »

spring boot starter for aliYunMQ

Posted on 2018-12-20 | | Visitors:

spring boot starter for aliYunMQ

项目介绍

消息队列 RocketMQ 是阿里巴巴自研消息产品,服务于整个集团已超过 13 年,经过阿里巴巴交易核心链路反复打磨与历年双十一购物狂欢节的严苛考验,是一个真正具备低延迟、高并发、高可用、高可靠,可支撑万亿级数据洪峰的分布式消息中间件。

本模块实现了阿里云MQ的以下几个功能。

  • 同步发送消息
  • 异步发送消息
  • 广播发送消息
  • 有序发送和消费消息
  • 发送延时消息
  • 消息tag支持
  • 自动序列化和反序列化消息体
  • 发送事务消息

如何集成

1、添加依赖
1
2
3
4
5
<dependency>
<groupId>com.shopping</groupId>
<artifactId>spring-boot-starter-aliyunmq</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>

因为我们有自己的maven私库,所以没有po到maven公有库里。配置代码库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<repositories>
<repository>
<id>shopping</id>
<url>http://maven.sucok.com/content/groups/public</url>
</repository>
<repository>
<id>sonatype-nexus-staging</id>
<name>Sonatype Nexus Staging</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>

2、添加配置

1
2
3
4
5
6
7
8
9
aliyun.mq.onsAddr=<地址>
aliyun.mq.accessKey=
aliyun.mq.secretKey=
aliyun.mq.TopicIdRblc=
#为false表示不引入producer,为true则producerId必须提供
aliyun.mq.producer.enabled=true
aliyun.mq.producer.producerId=
#为false表示不引入consumer,为true则consumerId必须提供
aliyun.mq.consumer.enabled=true

3、添加生产者

顺序消息

按照消息的发布顺序进行顺序消费(FIFO),支持全局顺序与分区顺序

1
2
3
4
5
@Autowired
OrderMessageTemplate orderMessageTemplate;

//发送
orderMessageTemplate(new MessageEvent("{topic}", "{tag}", msgBody));

普通消息,定时消息,延迟消息生产者

消息可在指定的时间点(如2019/01/01 15:00:00)或延迟时间(如30分钟后)进行投递

1
2
3
4
5
6
7
8
9
10
11
12
@Autowired
RocketMQTemplate rocketMQTemplate;

// 发送普通消息
rocketMQTemplate.send(new MessageEvent("{topic}", "{tag}", msgBody));
// 延时消息,单位毫秒(ms),在指定延迟时间(当前时间之后)进行投递,例如消息在 3 秒后投递
rocketMQTemplate.sendAsync(new MessageEvent("{topic}", "{tag}", msgBody),3000);
// 延时消息,指定时间进行投递。例如消息在1天之后投递
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.DATE, 1);
rocketMQTemplate.sendAsync(new MessageEvent("{topic}", "{tag}", msgBody), cal.getTime(););

事务消息

类似 X/Open XA 的分布事务功能,既可做到系统间的解耦,又能保证数据的最终一致性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Autowired
TransactionMessageTemplate messageTemplate;

// 发送事务消息
/**封装消息*/
MessageEvent event = new MessageEvent();
event.setTopic("base_sms");
event.setTag("Tag_user");

User user = new User();
user.setName("Paul");
user.setAdds("北京市 昌平区 龙锦苑东二区");
/**封装任意类型领域对象*/
event.setDomain(user);

transactionMessageTemplate.send(event,new TransactionExecuter() {
@Override
public TransactionStatus executer(MessageEvent messageEvent, Long hashValue, Object arg) {
String transactionId = TransactionDemo.createTransaction();
TransactionStatus status = TransactionDemo.checker();
return status;
}
),"参数对象,以本字符串示例,会传递给TransactionExecuter.executer");

4、添加订阅者

普通消息订阅者,实现AbstractMessageListener接口

  • topic:支持SpringEl风格,取配置文件中的值。(必填)
  • tag:标签。* 标识所有。 (必填)
  • consumerId:消费者Id。 (必填)
  • consumeMode:消费模式 有序(单线程)或者无序(多线程) (默认无序)
1
2
3
4
5
6
7
8
9
10
@RocketMQMessageListener(topic = "${aliyun.mq.TopicIdRblc}", tag = "giveCoupon", consumerId = "CID_shopping_giveCoupon", consumeMode = MessageExtConst.CONSUME_MODE_ORDERLY)
public class GiveCouponMessageListener extends AbstractMessageListener<MessageEvent> {
/**
* 消息处理
*/
@Override
public void handle(MessageEvent messageEvent) throws Exception {
//TODO 业务处理
}
}

顺序消息订阅者,实现AbstractMessageOrderListener

1
2
3
4
5
6
7
8
9
10
@RocketMQMessageListener(topic = "${aliyun.mq.TopicIdRblc}", tag = "giveCoupon", consumerId = "CID_shopping_giveCoupon", consumeMode = MessageExtConst.CONSUME_MODE_ORDERLY)
public class GiveCouponMessageListener extends AbstractMessageOrderListener <MessageEvent> {
/**
* 消息处理
*/
@Override
public void handle(MessageEvent messageEvent) throws Exception {
//TODO 业务处理
}
}

5、相关参考

  • 官网Demo地址:https://github.com/AliwareMQ/mq-demo
<123…6>
Jax

Jax

52 posts
1 categories
23 tags
GitHub E-Mail 摄影/图虫
© 2022 Jax
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4
湘ICP备12012411号-3