安全的docker-api的使用(java)
通过docker-api来执行docker相关的操作。
配置
可以在docker启动文件docker.service中加入如下
vi /lib/systemd/system/docker.service
在下面ExecStart 后面添加 -H tcp://0.0.0.0:2375
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H unix://var/run/docker.sock -H tcp://0.0.0.0:2375
但是这样直接开放api,不安全,因此就需要指定证书。
修改 ExecStart的值如下:
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock -D --tlsverify --tlscert=/etc/docker/certs.d/server-cert-docker.pem --tlskey=/etc/docker/certs.d/server-key-docker.pem --tlscacert=/etc/docker/certs.d/ca-docker.pem
客户端在访问docker-api的时候就需要提供证书。
脚本
下面是自动生成docker-api证书的脚本。
利用脚本自动生成,这样非常便捷,脚本(auto_gen_docker_tls_certs.sh)如下:
#!/bin/bash # # ------------------------------------------------------------- # 自动创建 Docker TLS 证书 # ------------------------------------------------------------- # 以下是配置信息 # --[BEGIN]------------------------------ CODE="docker" IP="192.168.31.199" PASSWORD="123456" COUNTRY="CN" STATE="HUNAN" CITY="CHANGSHA" ORGANIZATION="thyc" ORGANIZATIONAL_UNIT="Dev" COMMON_NAME="$IP" EMAIL="an23gn@163.com" # --[END]-- # Generate CA key openssl genrsa -aes256 -passout "pass:$PASSWORD" -out "ca-key-$CODE.pem" 4096 # Generate CA openssl req -new -x509 -days 365 -key "ca-key-$CODE.pem" -sha256 -out "ca-$CODE.pem" -passin "pass:$PASSWORD" -subj "/C=$COUNTRY/ST=$STATE/L=$CITY/O=$ORGANIZATION/OU=$ORGANIZATIONAL_UNIT/CN=$COMMON_NAME/emailAddress=$EMAIL" # Generate Server key openssl genrsa -out "server-key-$CODE.pem" 4096 # Generate Server Certs. openssl req -subj "/CN=$COMMON_NAME" -sha256 -new -key "server-key-$CODE.pem" -out server.csr echo "subjectAltName = IP:$IP,IP:127.0.0.1" >> extfile.cnf echo "extendedKeyUsage = serverAuth" >> extfile.cnf openssl x509 -req -days 365 -sha256 -in server.csr -passin "pass:$PASSWORD" -CA "ca-$CODE.pem" -CAkey "ca-key-$CODE.pem" -CAcreateserial -out "server-cert-$CODE.pem" -extfile extfile.cnf # Generate Client Certs. rm -f extfile.cnf openssl genrsa -out "key-$CODE.pem" 4096 openssl req -subj '/CN=client' -new -key "key-$CODE.pem" -out client.csr echo extendedKeyUsage = clientAuth >> extfile.cnf openssl x509 -req -days 365 -sha256 -in client.csr -passin "pass:$PASSWORD" -CA "ca-$CODE.pem" -CAkey "ca-key-$CODE.pem" -CAcreateserial -out "cert-$CODE.pem" -extfile extfile.cnf rm -vf client.csr server.csr chmod -v 0400 "ca-key-$CODE.pem" "key-$CODE.pem" "server-key-$CODE.pem" chmod -v 0444 "ca-$CODE.pem" "server-cert-$CODE.pem" "cert-$CODE.pem" # 打包客户端证书 mkdir -p "tls-client-certs-$CODE" cp -f "ca-$CODE.pem" "cert-$CODE.pem" "key-$CODE.pem" "tls-client-certs-$CODE/" cd "tls-client-certs-$CODE" tar zcf "tls-client-certs-$CODE.tar.gz" * mv "tls-client-certs-$CODE.tar.gz" ../ cd .. rm -rf "tls-client-certs-$CODE" # 拷贝服务端证书 mkdir -p /etc/docker/certs.d cp "ca-$CODE.pem" "server-cert-$CODE.pem" "server-key-$CODE.pem" /etc/docker/certs.d/
对脚本中的变量进行修改后运行,自动会创建好tls证书,服务器的证书在/etc/docker/certs.d/目录下:
客户端的证书在运行脚本的目录下,同时还自动打好了一个.tar.gz的包,很方便。
重启docker
systemctl daemon-reload && systemctl restart docker
以java的docker-api的访问为例子:
依赖
<dependency> <groupId>com.github.docker-java</groupId> <artifactId>docker-java</artifactId> <!-- use latest version https://github.com/docker-java/docker-java/releases --> <version>3.1.5</version> </dependency>
实体类
import lombok.Data; import lombok.experimental.Accessors; /** * @author wzm * @version 1.0.0 * @date 2019/7/25 10:06 **/ @Data @Accessors(chain = true) public class DockerClientDTO { /** * 私钥和证书文件路径 */ private String certAndKeyFilePath; /** * 主机ip */ private String host; /** * 端口 */ private String port; /** * 注册用户名 */ private String registryUsername; /** * 注册密码 */ private String registryPassword; /** * 注册邮箱 */ private String registryEmail; public DockerClientDTO(String host, String port, String certAndKeyFilePath) { this.host = host; this.port = port; this.certAndKeyFilePath = certAndKeyFilePath; } }
将使用脚本生成好的客户端证书tls-client-certs-docker.tar.gz解压到“D:/docker/tls”目录下
java代码
import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.DockerCmdExecFactory; import com.github.dockerjava.api.model.Container; import com.github.dockerjava.api.model.Image; import com.github.dockerjava.api.model.Info; import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientBuilder; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.jaxrs.JerseyDockerCmdExecFactory; import net.sf.json.JSONArray; import org.springframework.util.StringUtils; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.List; /** * docker-api操作工具类 * * @author wzm * @version 1.0.0 * @date 2019/7/16 11:16 **/ public class DockerOperationUtils { // D:/docker/tls // "tcp://192.168.2.133:2375" // "docker" // "123456" // "an23gn@163.com" private static DockerClient dockerClient; /** * 获取DOCKER客户端 * * @param dockerClientDTO docker客户端连接信息 * @return DockerClient */ public static DockerClient getDockerClient(DockerClientDTO dockerClientDTO) { // 进行安全认证 DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder() // 服务器ip .withDockerHost("tcp://" + dockerClientDTO.getHost() + ":" + dockerClientDTO.getPort()) .withDockerTlsVerify(true) // 证书的本地位置 .withDockerCertPath(dockerClientDTO.getCertAndKeyFilePath()) // 私钥的本地位置 //.withDockerConfig(dockerClientDTO.getCertAndKeyFilePath()) // API版本 可通过在服务器 docker version 命令查看 .withApiVersion("1.31") // 默认 .withRegistryUrl("https://index.docker.io/v1/") // 默认 .withRegistryUsername(dockerClientDTO.getRegistryUsername()) // 默认 .withRegistryPassword(dockerClientDTO.getRegistryPassword()) // 默认 .withRegistryEmail(dockerClientDTO.getRegistryEmail()) .build(); // docker命令执行工厂 DockerCmdExecFactory dockerCmdExecFactory = new JerseyDockerCmdExecFactory() .withReadTimeout(60000) .withConnectTimeout(60000) .withMaxTotalConnections(100) .withMaxPerRouteConnections(10); dockerClient = DockerClientBuilder.getInstance(config).withDockerCmdExecFactory(dockerCmdExecFactory).build(); return dockerClient; } public static void main(String[] args) { dockerClient = DockerOperationUtils.getDockerClient(new DockerClientDTO("192.168.2.134", "2375", "D:/docker/tls")); Info info = queryClientInfo(dockerClient); System.out.println(info); } /** * 连接信息 * * @param dockerClient DOCKER客户端 * @return Object */ public static Info queryClientInfo(DockerClient dockerClient) { return dockerClient.infoCmd().exec(); } /** * 查看镜像 * * @param dockerClient DOCKER客户端 * @return Object */ public static List<Image> queryImagesList(DockerClient dockerClient) { return dockerClient.listImagesCmd().exec(); } /** * 停止容器 * * @param dockerClient DOCKER客户端 * @param container 容器ID * @return Object */ public static Object stopContainer(DockerClient dockerClient, String container) { return dockerClient.stopContainerCmd(container).exec(); } /** * 删除镜像 * * @param dockerClient DOCKER客户端 * @param imagesID 镜像ID * @return Object */ public static Object removeImages(DockerClient dockerClient, String imagesID) { return dockerClient.removeImageCmd(imagesID).exec(); } /** * 删除容器 * * @param dockerClient DOCKER客户端 * @param container 容器ID * @return Object */ public static Object removeContainer(DockerClient dockerClient, String container) { return dockerClient.removeContainerCmd(container).exec(); } /** * 创建容器 * * @param dockerClient DOCKER客户端 * @param imagesID 镜像ID * @return Object */ public static Object createContainer(DockerClient dockerClient, String imagesID) { return dockerClient.createContainerCmd(imagesID).exec(); } /** * 创建一个镜像 * * @param dockerClient DOCKER客户端 * @return Object * @throws FileNotFoundException 找不到文件 */ public static Object createImages(DockerClient dockerClient) throws FileNotFoundException { //仓库地址 String repository = ""; //镜像文件流 InputStream imageStream = new FileInputStream(""); return dockerClient.createImageCmd(repository, imageStream).exec(); } /** * 容器列表(运行中的) * * @param dockerClient DOCKER客户端 * @return Object */ public static List<Container> listContainersCmd(DockerClient dockerClient) { return dockerClient.listContainersCmd().exec(); } public static List<String> getContainerNameList(List<Container> containerList) { List<String> containerNameList = new ArrayList<>(); for (Container container : containerList) { String containerName = container.getNames()[0].replace("/", ""); containerNameList.add(containerName); } return containerNameList; } /** * 启动容器 * * @param dockerClient DOCKER客户端 * @param containerID 容器ID */ public static Object startContainerCmd(DockerClient dockerClient, String containerID) { return dockerClient.startContainerCmd(containerID).exec(); } /** * 重启容器 * * @param dockerClient 客户端 * @param containerID 容器id * @return java.lang.Object * @author wzm * @date 2019/9/28 15:30 */ public static Object restartContainerCmd(DockerClient dockerClient, String containerID) { return dockerClient.restartContainerCmd(containerID).exec(); } /** * 从本地上传资源到容器 * * @param dockerClient 客户端 * @param containerID 容器id * @param resource 本地资源路径 * @param remote 服务器资源路径 * @return Object */ public static Object copyArchiveToContainerCmd(DockerClient dockerClient, String containerID, String resource, String remote) { return dockerClient.copyArchiveToContainerCmd(containerID).withHostResource(resource).withRemotePath(remote).exec(); } /** * 从容器下载资源到本地 * * @param dockerClient 客户端 * @param containerID 容器id * @param local 本地路径 * @param remote 远程容器路径 * @return Object */ public static Object copyArchiveFromContainerCmd(DockerClient dockerClient, String containerID, String local, String remote) { try { // String path = "F:\\tmp\\wealth.rar" // remote="/tmp/wealth.rar" InputStream input = dockerClient .copyArchiveFromContainerCmd(containerID, remote) .exec(); int index; byte[] bytes = new byte[1024]; FileOutputStream downloadFile = new FileOutputStream(local); while ((index = input.read(bytes)) != -1) { downloadFile.write(bytes, 0, index); downloadFile.flush(); } input.close(); downloadFile.close(); return true; } catch (Exception e) { throw new RuntimeException(e.getMessage()); } } /** * 根据容器名获取容器ID * * @param dockerClient 容器客户端 * @param containerName 容器名 * @return java.lang.String * @author wzm * @date 2019/9/28 15:38 */ public static String getContainerIdByName(DockerClient dockerClient, String containerName) { try { String containerId = ""; Object object = DockerOperationUtils.listContainersCmd(dockerClient); JSONArray jsonArray = JSONArray.fromObject(object); for (int i = 0; i < jsonArray.size(); i++) { String name = jsonArray.getJSONObject(i).getString("names"); name = name.replace("[\"/", "").replace("\"]", ""); if (!StringUtils.isEmpty(name) && name.equals(containerName)) { containerId = jsonArray.getJSONObject(i).getString("id"); } } return containerId; } catch (Exception e) { throw new RuntimeException(e.getMessage()); } } }