前面的几天时间里,学习了 docker 的安装与使用,学会使用命令行控制 docker 容器的创建、删除等。然后学习了 springboot 框架的基本使用、了解其框架基本原理与运行机制。完成了这些准备后,接下来我主要面临的一个比较关键的技术难点是如何使用 springboot 连接远程服务器上 docker 服务,实现容器的动态创建与删除。如果实现了这个技术点,那么我们的靶场题目环境便可以打包成镜像,然后放到服务器中,通过动态的创建、删除容器,来实现题目环境的部署。
pom.xml 导入项目需要用到的相关依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.6.4version>
<relativePath/>
parent>
<groupId>com.examplegroupId>
<artifactId>SDU_SpringBoot_fontartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>SDU_SpringBoot_fontname>
<description>SDU_SpringBoot_fontdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>com.github.docker-javagroupId>
<artifactId>docker-javaartifactId>
<version>3.1.5version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.28version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<exclusions>
<exclusion>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-enginaartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<scope>providedscope>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>jstlartifactId>
dependency>
<dependency>
<groupId>org.apache.tomcat.embedgroupId>
<artifactId>tomcat-embed-jasperartifactId>
<scope>providedscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
application.yaml 对项目进行配置,配置一下 html 文件的匹配路径
spring:
thymeleaf:
prefix:
classpath: /templates/
用于 index 页面的匹配
实现浏览器请求 “/” 或 “/index.html” 时,匹配到 index.html 页面。
package com.example.sdu_springboot_font.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Controller
public class indexController {
@RequestMapping(value={"/","/index.html"})
public String index(){
return "index";
}
}
接下来是比较重要的部分,即 java 远程操控服务器上的 docker 服务,实现容器的创建、删除等基本操作。在思考如何实现这个功能的时候,查看了大量的文章,在网上搜索了很多的相关博客,也简单看了一下几篇关于 docker 在 CTF 比赛中容器创建的相关应用的论文。经过尝试,最后使用 docker-java 成功实现该功能。
docker-java 简介:
docker-java 是一个开源的项目,发布在 GitHub 上,此项目提供了很多接口,可以用于连接远程服务器上的 docker 服务,并进行基本的操控。使用时导入 jar 包即可。相关文档及使用博客并不是很多,需要自己进行学习探索。
使用前需导入 jar 包,因为是 springboot 框架,集成了 maven ,所以直接在 pom.xml 导入依赖
<dependency>
<groupId>com.github.docker-javagroupId>
<artifactId>docker-javaartifactId>
<version>3.1.5version>
dependency>
导入相关依赖后,编写 newContainer 类,利用 docker-java 的相关接口,实现远程服务器的 docker 服务的连接,并进行动态的创建容器、删除容器等基本操作。
package com.example.sdu_springboot_font.controller;
import com.alibaba.fastjson.JSONObject;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.model.Info;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.example.sdu_springboot_font.service.*;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/yaoyao/")//接口注解
//接收index.HTML的请求,创建一个容器,然后把跳转到容器界面
public class newContainer {
@Autowired //与service层进行交互
//private LoginClservice loginClService;
//private DockerClientUtils dockerClientUtils;
public static int port=8080;
public static int containerName = 0;
@RequestMapping("user")
public ModelAndView getLoginCl() throws InterruptedException {
port+=1;
containerName+=1;
//boolean b;
//b=loginClService.Find(name, password);//调用service层的方法
DockerClientUtils dockerClientUtils = new DockerClientUtils();
//连接Docker服务器
DockerClient client = dockerClientUtils.connectDocker("tcp://82.157.124.157:2275");
//创建容器
CreateContainerResponse container = dockerClientUtils.createContainer(client, "ctf-test-"+containerName, "ctf-test",8080,port);
//启动容器
dockerClientUtils.startContainer(client, container.getId());
//Info info = client.infoCmd().exec();
//String infoStr = JSONObject.toJSONString(info);
//System.out.println(infoStr);
//return "success";
//Thread.sleep(1000);
return new ModelAndView("redirect:http://82.157.124.157:"+port);
}
}
上面代码实现的测试功能为:浏览器请求 “/yaoyao/user”,服务端接收到请求后,会连接到远程服务器(82.157.124.157)的 docker 服务(2275 端口,需要在服务器端提前配置好,否则会出现拒绝连接现象)
创建容器,镜像名为 “ctf-test”,容器名为:ctf-test-编号,映射端口为服务器IP:8080
CreateContainerResponse container = dockerClientUtils.createContainer(client, "ctf-test-"+containerName, "ctf-test",8080,port);
启动容器
dockerClientUtils.startContainer(client, container.getId());
然后重定向到新创建的容器界面
return new ModelAndView("redirect:http://82.157.124.157:"+port);
DockerClientUtils 为 service 层文件,主要实现 docker-java 接口调用,进一步实现容器的创建删除。
package com.example.sdu_springboot_font.service;
import java.util.List;
import com.github.dockerjava.api.model.ExposedPort;
import com.github.dockerjava.api.model.HostConfig;
import com.github.dockerjava.api.model.Ports;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSONObject;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.model.Info;
import com.github.dockerjava.core.DockerClientBuilder;
//import com.example.smalldemo.dao.*;
//import com.example.smalldemo.model.*;
@Service
public class DockerClientUtils {
/**
* 连接Docker服务器
* @return
*/
public DockerClient connectDocker(String dockerInstance){
DockerClient dockerClient = DockerClientBuilder.getInstance(dockerInstance).build();
dockerClient.infoCmd().exec();
return dockerClient;
}
/**
* 创建容器
* @param client
* @return
public CreateContainerResponse createContainers(DockerClient client, String containerName, String imageName){
CreateContainerResponse container = client.createContainerCmd(imageName)
.withName(containerName)
.exec();
return container;
}
*/
/**
* 创建容器
*
* @param client
* @param containerName
* @param imageName
* @param exposedTcpPort
* @param bindTcpPort
* @return
*/
public CreateContainerResponse createContainer(DockerClient client, String containerName, String imageName,
int exposedTcpPort, int bindTcpPort) {
ExposedPort exposedPort = ExposedPort.tcp(exposedTcpPort);
Ports portBindings = new Ports();
portBindings.bind(exposedPort, Ports.Binding.bindPort(bindTcpPort));
CreateContainerResponse container = client.createContainerCmd(imageName)
.withName(containerName)
.withHostConfig(HostConfig.newHostConfig().withPortBindings(portBindings))
.withExposedPorts(exposedPort).exec();
return container;
}
/**
* 启动容器
* @param client
* @param containerId
*/
public void startContainer(DockerClient client,String containerId){
client.startContainerCmd(containerId).exec();
}
/**
* 停止容器
* @param client
* @param containerId
*/
public void stopContainer(DockerClient client,String containerId){
client.stopContainerCmd(containerId).exec();
}
/**
* 删除容器
* @param client
* @param containerId
*/
public void removeContainer(DockerClient client,String containerId){
client.removeContainerCmd(containerId).exec();
}
/*
public static void main(String[] args) {
DockerClientUtils dockerClientUtils = new DockerClientUtils();
//连接Docker服务器
DockerClient client = dockerClientUtils.connectDocker("tcp://82.157.124.157:2275");
//创建容器
CreateContainerResponse container = dockerClientUtils.createContainers(client, "sny_hello1", "hello-world");
//启动容器
dockerClientUtils.startContainer(client, container.getId());
Info info = client.infoCmd().exec();
String infoStr = JSONObject.toJSONString(info);
System.out.println(infoStr);
}
*/
}
前端界面仅仅作为测试使用,真正项目的前端界面由组内其他成员完成,所以这里简单的使用 HTML 编写测试界面。
DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title heretitle>
head>
<body>
<form name="" action="/yaoyao/user" method="post">
<tr>
<td>
<input type="submit" name="tijiao" value="进入靶场容器环境"/>
td>
tr>
form>
body>
html>
<br>
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Language" content="zh-CN">
<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=gb2312">
<meta http-equiv="refresh" content="2;url=http://82.157.124.157:8081">
<title>创建容器成功title>
<p>已成功创建靶场容器,即将自动跳转到靶场p>
head>
<body>
body>
html>
这各部分主要是编写一个模拟的靶场环境,用于测试是否可以远程操控。
代码也很简单,相关的其他配置与上面一部分类似,这里便不再赘述。
package com.example.ctfzone.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class index {
@RequestMapping("/")
public String indexpage(){
return "靶场环境-处于新建容器中";
}
}
环境:
centos7
开始
想要在java中还是在其他方式访问 dockerAPI 都需要设置一个端口
运行以下命令:进入docker.service
vi /lib/systemd/system/docker.service
找到 Execstart=/usr/bin/dockerd
后加上 -H tcp://0.0.0.0:2275 -H unix://var/run/docker.sock
退出并且保存
运行以下命令:
systemctl daemon-reload
service docker restart //重启启动docker
systemctl stats docker //可以查看相关内容,看看2275是否已经设置好
运行:netstat -nlp |grep 2275
可以查看2275是否已经被监听
如果你是阿里的云服务器这种,应该需要在安全组中加入2275或者其他的操作。
如果是在本地搭建的虚拟机,则需要把防火墙关闭或者在防火墙中开启相应端口以供外部使用
FROM java:8
COPY *.jar /app.jar
CMD ["--server.port=8080"]
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar" ]
sudo docker build -t="ctf-front-test" .
参考文章:
https://www.cnblogs.com/lsgxeva/p/8746644.html
https://blog.csdn.net/qq_36956154/article/details/81335886
参考论文:
https://www.fx361.com/page/2021/0728/8631053.shtml
https://xueshu.baidu.com/usercenter/paper/show?paperid=1x280t10ut280mx06m080p10qj548641&site=xueshu_se
https://xueshu.baidu.com/usercenter/paper/show?paperid=1u7u0v70e02c0200j85w08h0m0022660&site=xueshu_se
https://cdmd.cnki.com.cn/article/cdmd-10013-1018097424.htm