山东大学软件学院项目实训-创新实训-山大软院网络攻防靶场实验平台(五)


前言:

前面的几天时间里,学习了 docker 的安装与使用,学会使用命令行控制 docker 容器的创建、删除等。然后学习了 springboot 框架的基本使用、了解其框架基本原理与运行机制。完成了这些准备后,接下来我主要面临的一个比较关键的技术难点是如何使用 springboot 连接远程服务器上 docker 服务,实现容器的动态创建与删除。如果实现了这个技术点,那么我们的靶场题目环境便可以打包成镜像,然后放到服务器中,通过动态的创建、删除容器,来实现题目环境的部署。



一、搭建测试环境

后端动态创建容器使用 springboot 框架搭建
在这里插入图片描述

模拟靶场环境使用 springboot 框架。
在这里插入图片描述

前端就简单使用 HTML 写一个测试页面即可。
在这里插入图片描述



二、编写测试代码-动态创建容器

1、编写配置文件 pom.xml 与 application.yaml

pom.xml 导入项目需要用到的相关依赖

<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>SDU_SpringBoot_font</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>SDU_SpringBoot_font</name>
    <description>SDU_SpringBoot_font</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!--自己添加的部分(from docker project)-->
        <!-- https://mvnrepository.com/artifact/com.github.docker-java/docker-java -->
        <dependency>
            <groupId>com.github.docker-java</groupId>
            <artifactId>docker-java</artifactId>
            <version>3.1.5</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.28</version>
        </dependency>
        <!--自己添加的部分(from docker project)-->

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <!--排除这个slf4j-log4j12-->
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>

            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engina</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- servlet依赖. -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>

        <!-- tomcat的支持.-->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <scope>provided</scope>
        </dependency>


    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yaml 对项目进行配置,配置一下 html 文件的匹配路径

spring:
  thymeleaf:
    prefix:
      classpath: /templates/

2、编写 indexController

用于 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-java</groupId>
            <artifactId>docker-java</artifactId>
            <version>3.1.5</version>
        </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);

3、编写 DockerClientUtils

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);
    }
*/
}

4、编写前端界面

前端界面仅仅作为测试使用,真正项目的前端界面由组内其他成员完成,所以这里简单的使用 HTML 编写测试界面。

  1. index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
</head>
<body>

<form name="" action="/yaoyao/user" method="post">
    <tr>
        <td>
            <input type="submit" name="tijiao" value="进入靶场容器环境"/>
        </td>
    </tr>

</form>

</body>
</html>

<br>
  1. success.html
<!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 "靶场环境-处于新建容器中";
    }
}



四、打包、部署、测试

1、服务器 docker 配置

环境:
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或者其他的操作。

如果是在本地搭建的虚拟机,则需要把防火墙关闭或者在防火墙中开启相应端口以供外部使用


2、腾讯云防火墙端口

开启 2275 端口
在这里插入图片描述

3、dockerfile 编写

FROM java:8

COPY *.jar /app.jar

CMD ["--server.port=8080"]

EXPOSE 8080

ENTRYPOINT ["java","-jar","/app.jar" ]

4、镜像生成

  1. 生成 jar 包
    在这里插入图片描述

在这里插入图片描述

  1. 把 jar 包和 dockerfile 文件均上传到服务器
    在这里插入图片描述

  2. 生成镜像

sudo docker build -t="ctf-front-test" .

在这里插入图片描述


5、运行测试

在这里插入图片描述

在这里插入图片描述




参考文章:
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

猜你喜欢

转载自blog.csdn.net/m0_47470899/article/details/123410185