如何将机密传递给 testContainers?

gst*_*low 5 kotlin docker spring-boot docker-compose testcontainers

我有以下用于本地开发的 docker-compose 文件:

version: '3.4'

networks:
  mynetwork:

services:
  samba:
    image: instantlinux/samba-dc:latest
    container_name: samba-dc
    cap_add:
      - CAP_SYS_ADMIN
    hostname: my.org
    environment:
      DOMAIN_ACTION: provision
      REALM: my.org
    volumes:
      - etc:/etc/samba
      - lib:/var/lib/samba
    ports:
      - "53:53"
      - "53:53/udp"
      - "88:88"
      - "88:88/udp"
      - "389:389"
    secrets:
      - samba-admin-password

volumes:
  etc:
  lib:

secrets:
  samba-admin-password:
    file: secrets.yaml
Run Code Online (Sandbox Code Playgroud)

现在我尝试使用 testContainers 来实现集成测试:

@Testcontainers
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
....


       init {
            try {
                val ldapContainer =
                    GenericContainer("instantlinux/samba-dc:latest")
                        .withEnv("DOMAIN_ACTION", "provision")
                        .withEnv("REALM", "my.company")
                        .withEnv("ADMIN_PASSWORD_SECRET", "samba-admin-password")
                        .withExposedPorts(53, 88, 389)                    
                ldapContainer.start()
                print("Containers has started")
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
Run Code Online (Sandbox Code Playgroud)

但是当我尝试运行它时,我收到一个错误:

Container startup failed for image instantlinux/samba-dc:latest
....
rg.testcontainers.containers.GenericContainer expected the predicate to return <true> but it returned <false> for input of <InspectContainerResponse(args=[], config=ContainerConfig(attachStderr=false, attachStdin=false, attachStdout=false, cmd=null, domainName=, entrypoint=[/usr/local/bin/entrypoint.sh], env=[DOMAIN_ACTION=provision, ADMIN_PASSWORD_SECRET=samba-admin-password, REALM=my.company, PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin, ALLOW_DNS_UPDATES=secure, BIND_INTERFACES_ONLY=yes, DOMAIN_LOGONS=yes, DOMAIN_MASTER=no, INTERFACES=lo eth0, LOG_LEVEL=1, MODEL=standard, NETBIOS_NAME=, SERVER_STRING=Samba Domain Controller, TZ=UTC, WINBIND_USE_DEFAULT_DOMAIN=yes, WORKGROUP=AD], exposedPorts=....
...
17:01:51.548 [Test worker] ERROR tc.instantlinux/samba-dc:latest -- Log output from the failed container:
Set timezone
Cannot read secret $ADMIN_PASSWORD_SECRET in /run/secrets
Run Code Online (Sandbox Code Playgroud)

看起来我必须以某种方式配置秘密,但我没有找到实现它的方法。

更新1

秘密文件如下所示:

kind: Secret
apiVersion: v1
metadata:
  name: samba-admin-password
data:
  ADMIN_PASSWORD_SECRET: superpassword
Run Code Online (Sandbox Code Playgroud)

更新2

根据 VonC 答案,我创建了示例:

@Testcontainers
@SpringBootTest(webEnvironment = RANDOM_PORT)
@ActiveProfiles("test")
class TestContainersBase {

    @Test
    fun test() {
        val mapper = ObjectMapper(YAMLFactory())
        val secretPathOnHost = "C:\\work\\MyApp\\docker\\secrets.yaml"
        val secretsFile = File(secretPathOnHost)

        val secretsData: Map<String, Any> = mapper.readValue(secretsFile, object: TypeReference<Map<String, Any>>(){})

        // Extract the secret from the parsed data
        val adminPassword = (secretsData["data"] as Map<*,*>?)!!["ADMIN_PASSWORD_SECRET"] as String?

        val secretPathInContainer = "/run/secrets/samba-admin-password";
        // Create and start the container
        val ldapContainer = GenericContainer("instantlinux/samba-dc:latest")
            .withEnv("DOMAIN_ACTION", "provision")
            .withEnv("REALM", "my.company")
            .withEnv("ADMIN_PASSWORD_SECRET", adminPassword) // Set the extracted secret as an environment variable
            .withExposedPorts(53, 88, 389)
            .withFileSystemBind(secretPathOnHost, secretPathInContainer, BindMode.READ_ONLY);

        ldapContainer.start()

        print("qwerty")
        
        Thread.sleep(100000000)

    }
}
Run Code Online (Sandbox Code Playgroud)

在应用程序日志中我看到:

2023-08-21T13:38:50.555+03:00  INFO 15136 --- [    Test worker] o.t.utility.ImageNameSubstitutor         : Image name substitution will be performed by: DefaultImageNameSubstitutor (composite of 'ConfigurationFileImageNameSubstitutor' and 'PrefixingImageNameSubstitutor')
2023-08-21T13:38:51.739+03:00  INFO 15136 --- [    Test worker] o.t.d.DockerClientProviderStrategy       : Loaded org.testcontainers.dockerclient.NpipeSocketClientProviderStrategy from ~/.testcontainers.properties, will try it first
2023-08-21T13:38:52.779+03:00  INFO 15136 --- [    Test worker] o.t.d.DockerClientProviderStrategy       : Found Docker environment with local Npipe socket (npipe:////./pipe/docker_engine)
2023-08-21T13:38:52.784+03:00  INFO 15136 --- [    Test worker] org.testcontainers.DockerClientFactory   : Docker host IP address is localhost
2023-08-21T13:38:52.814+03:00  INFO 15136 --- [    Test worker] org.testcontainers.DockerClientFactory   : Connected to docker: 
  Server Version: 20.10.21
  API Version: 1.41
  Operating System: Docker Desktop
  Total Memory: 38292 MB
2023-08-21T13:38:52.889+03:00  INFO 15136 --- [    Test worker] tc.testcontainers/ryuk:0.4.0             : Creating container for image: testcontainers/ryuk:0.4.0
2023-08-21T13:38:53.928+03:00  INFO 15136 --- [    Test worker] o.t.utility.RegistryAuthLocator          : Credential helper/store (docker-credential-desktop) does not have credentials for https://index.docker.io/v1/
2023-08-21T13:38:54.201+03:00  INFO 15136 --- [    Test worker] tc.testcontainers/ryuk:0.4.0             : Container testcontainers/ryuk:0.4.0 is starting: b4a10e2647f83d6fc404644fb09edabf930e987e2c5d138eb3d1b9414b1400ac
2023-08-21T13:38:55.320+03:00  INFO 15136 --- [    Test worker] tc.testcontainers/ryuk:0.4.0             : Container testcontainers/ryuk:0.4.0 started in PT2.488268S
2023-08-21T13:38:55.330+03:00  INFO 15136 --- [    Test worker] o.t.utility.RyukResourceReaper           : Ryuk started - will monitor and terminate Testcontainers containers on JVM exit
2023-08-21T13:38:55.330+03:00  INFO 15136 --- [    Test worker] org.testcontainers.DockerClientFactory   : Checking the system...
2023-08-21T13:38:55.332+03:00  INFO 15136 --- [    Test worker] org.testcontainers.DockerClientFactory   : ?? Docker server version should be at least 1.6.0
2023-08-21T13:38:55.334+03:00  INFO 15136 --- [    Test worker] tc.instantlinux/samba-dc:latest          : Creating container for image: instantlinux/samba-dc:latest
2023-08-21T13:38:56.834+03:00  INFO 15136 --- [    Test worker] tc.instantlinux/samba-dc:latest          : Container instantlinux/samba-dc:latest is starting: 496246f47398809c3a7327b0c73a9b7d7fbe6440865b1cad4c124849f6069acb
2023-08-21T13:39:07.361+03:00  WARN 15136 --- [ntainers-wait-0] .c.w.i.InternalCommandPortListeningCheck : An exception while executing the internal check: Container.ExecResult(exitCode=137, stdout=, stderr=/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
/bin/sh: /bin/bash: not found
)
2023-08-21T13:39:07.367+03:00  INFO 15136 --- [    Test worker] tc.instantlinux/samba-dc:latest          : Container instantlinux/samba-dc:latest started in PT12.0305603S
Run Code Online (Sandbox Code Playgroud)

在 Docker 桌面中:

在此输入图像描述

第一个容器(基于端口,我认为它是 Samba)记录:

2023-08-21 13:38:58 Set timezone
2023-08-21 13:38:59 INFO 2023-08-21 10:38:59,067 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #2108: Looking up IPv4 addresses
2023-08-21 13:38:59 INFO 2023-08-21 10:38:59,068 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #2125: Looking up IPv6 addresses
2023-08-21 13:38:59 WARNING 2023-08-21 10:38:59,068 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #2132: No IPv6 address will be assigned
2023-08-21 13:38:59 INFO 2023-08-21 10:38:59,721 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #2274: Setting up share.ldb
2023-08-21 13:38:59 INFO 2023-08-21 10:38:59,874 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #2278: Setting up secrets.ldb
2023-08-21 13:38:59 INFO 2023-08-21 10:38:59,936 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #2283: Setting up the registry
2023-08-21 13:39:00 INFO 2023-08-21 10:39:00,304 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #2286: Setting up the privileges database
2023-08-21 13:39:00 INFO 2023-08-21 10:39:00,466 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #2289: Setting up idmap db
2023-08-21 13:39:00 INFO 2023-08-21 10:39:00,555 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #2296: Setting up SAM db
2023-08-21 13:39:00 INFO 2023-08-21 10:39:00,573 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #880: Setting up sam.ldb partitions and settings
2023-08-21 13:39:00 INFO 2023-08-21 10:39:00,574 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #892: Setting up sam.ldb rootDSE
2023-08-21 13:39:00 INFO 2023-08-21 10:39:00,591 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #1305: Pre-loading the Samba 4 and AD schema
2023-08-21 13:39:00 Unable to determine the DomainSID, can not enforce uniqueness constraint on local domainSIDs
2023-08-21 13:39:00 
2023-08-21 13:39:00 INFO 2023-08-21 10:39:00,638 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #1383: Adding DomainDN: DC=my,DC=company
2023-08-21 13:39:00 INFO 2023-08-21 10:39:00,659 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #1415: Adding configuration container
2023-08-21 13:39:00 INFO 2023-08-21 10:39:00,678 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #1430: Setting up sam.ldb schema
2023-08-21 13:39:03 INFO 2023-08-21 10:39:03,229 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #1448: Setting up sam.ldb configuration data
2023-08-21 13:39:03 INFO 2023-08-21 10:39:03,356 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #1489: Setting up display specifiers
2023-08-21 13:39:05 INFO 2023-08-21 10:39:05,522 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #1497: Modifying display specifiers and extended rights
2023-08-21 13:39:05 INFO 2023-08-21 10:39:05,569 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #1504: Adding users container
2023-08-21 13:39:05 INFO 2023-08-21 10:39:05,570 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #1510: Modifying users container
2023-08-21 13:39:05 INFO 2023-08-21 10:39:05,571 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #1513: Adding computers container
2023-08-21 13:39:05 INFO 2023-08-21 10:39:05,572 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #1519: Modifying computers container
2023-08-21 13:39:05 INFO 2023-08-21 10:39:05,574 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #1523: Setting up sam.ldb data
2023-08-21 13:39:05 INFO 2023-08-21 10:39:05,728 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #1553: Setting up well known security principals
2023-08-21 13:39:05 INFO 2023-08-21 10:39:05,770 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #1567: Setting up sam.ldb users and groups
2023-08-21 13:39:05 INFO 2023-08-21 10:39:05,993 pid:18 /usr/lib/python3.10/site-packages/samba/provision/__init__.py #1575: Setting up self join
2023-08-21 13:39:06 Repacking database from v1 to v2 format (first record CN=Structural-Object-Class,CN=Schema,CN=Configuration,DC=my,DC=company)
2023-08-21 13:39:06 Repack: re-packed 10000 records so far
2023-08-21 13:39:06 Repacking database from v1 to v2 format (first record CN=nTDSSiteSettings-Display,CN=406,CN=DisplaySpecifiers,CN=Configuration,DC=my,DC=company)
2023-08-21 13:39:06 Repacking database from v1 to v2 format (first record CN=ObjectMoveTable,CN=FileLinks,CN=System,DC=my,DC=company)
2023-08-21 13:39:07 set_nt_acl_no_snum: fset_nt_acl returned NT_STATUS_ACCESS_DENIED.
2023-08-21 13:39:07 ERROR(runtime): uncaught exception - (3221225506, '{Access Denied} A process has requested access to an object but has not been granted those access rights.')
2023-08-21 13:39:07   File "/usr/lib/python3.10/site-packages/samba/netcmd/__init__.py", line 186, in _run
2023-08-21 13:39:07     return self.run(*args, **kwargs)
2023-08-21 13:39:07   File "/usr/lib/python3.10/site-packages/samba/netcmd/domain.py", line 493, in run
2023-08-21 13:39:07     result = provision(self.logger,
2023-08-21 13:39:07   File "/usr/lib/python3.10/site-packages/samba/provision/__init__.py", line 2325, in provision
2023-08-21 13:39:07     provision_fill(samdb, secrets_ldb, logger, names, paths,
2023-08-21 13:39:07   File "/usr/lib/python3.10/site-packages/samba/provision/__init__.py", line 1965, in provision_fill
2023-08-21 13:39:07     setsysvolacl(samdb, paths.netlogon, paths.sysvol, paths.root_uid,
2023-08-21 13:39:07   File "/usr/lib/python3.10/site-packages/samba/provision/__init__.py", line 1742, in setsysvolacl
2023-08-21 13:39:07     _setntacl(sysvol)
2023-08-21 13:39:07   File "/usr/lib/python3.10/site-packages/samba/provision/__init__.py", line 1736, in _setntacl
2023-08-21 13:39:07     return setntacl(
2023-08-21 13:39:07   File "/usr/lib/python3.10/site-packages/samba/ntacls.py", line 228, in setntacl
2023-08-21 13:39:07     smbd.set_nt_acl(
Run Code Online (Sandbox Code Playgroud)

码头工人

PS C:\work\myApp\docker> docker ps
CONTAINER ID   IMAGE                       COMMAND       CREATED          STATUS          PORTS                     NAMES
5541e9f96005   testcontainers/ryuk:0.4.0   "/bin/ryuk"   24 seconds ago   Up 23 seconds   0.0.0.0:64762->8080/tcp   testcontainers-ryuk-6d02415f-7042-4de8-bf0d-a1be71ea5172
PS C:\work\myApp\docker>
Run Code Online (Sandbox Code Playgroud)

Von*_*onC 3

Testcontainers似乎不直接支持 Docker Compose 的秘密。唯一的秘密概念是使用HashiCorp Vault 模块 .withSecretInVault()时。

在您的情况下,您可以尝试使用卷来模拟秘密:这是一种解决方法,将您的秘密绑定到容器中预期路径的卷。

val secretPathOnHost = "/path/to/your/secrets.yaml";
val secretPathInContainer = "/run/secrets/samba-admin-password";

val ldapContainer = GenericContainer("instantlinux/samba-dc:latest")
    .withEnv("DOMAIN_ACTION", "provision")
    .withEnv("REALM", "my.company")
    .withEnv("ADMIN_PASSWORD_SECRET", "samba-admin-password")
    .withExposedPorts(53, 88, 389)
    .withFileSystemBind(secretPathOnHost, secretPathInContainer, BindMode.READ_ONLY);

ldapContainer.start();
Run Code Online (Sandbox Code Playgroud)

请替换为主机上文件/path/to/your/secrets.yaml的绝对路径。secrets.yaml

注意:将机密模拟为卷意味着该机密可以作为主机系统上的纯文本文件使用,因此请确保正确管理其权限和访问。这可能适合本地开发和测试,但对于类似生产的环境可能并不理想。

并且......永远不要将秘密或秘密路径提交给源代码管理。


由于秘密文件是:

kind: Secret
apiVersion: v1
metadata:
  name: samba-admin-password
data:
  ADMIN_PASSWORD_SECRET: superpassword
Run Code Online (Sandbox Code Playgroud)

鉴于这种类似于 Kubernetes 密钥的格式,您需要在测试设置中解析该文件,然后将密钥设置为容器的环境变量。

使用testcontainersJackson 库进行 YAML 解析:

kind: Secret
apiVersion: v1
metadata:
  name: samba-admin-password
data:
  ADMIN_PASSWORD_SECRET: superpassword
Run Code Online (Sandbox Code Playgroud)

该方法涉及将机密读取到 Java 应用程序中,然后将其作为环境变量传递给容器。


从 OP 提供的示例来看,它看起来像是在 Spring Boot 应用程序中使用 Testcontainers 的 Samba 容器。容器似乎启动正确,您可以看到 Samba 的初始化日志。

主要问题是 samba 容器在启动后立即关闭(应用程序挂在线程睡眠中)。看起来根本原因可以在 samba 日志中找到:

2023-08-21 13:39:07 ERROR(runtime): uncaught exception - (3221225506, '{Access Denied} A process has requested access to an object but has not been granted those access rights.')

由于您使用的是 Docker,请记住 Samba 容器将有自己的用户系统。如果要安装卷,您可能需要调整用户或组 ID 以匹配您的主机系统。

在容器入口点中添加id -als -alrth /path/to/your/secrets.yaml命令来查看您在容器内的身份,以及如何查看已安装的文件系统。
检查已安装到容器中的任何卷的所有权和权限。容器内部的UID和GID可能与外部不同,从而导致权限问题。

如果您在 Docker 容器内运行 Samba,请确保您已提供所有必要的功能--cap-add(如果需要)。

检查您的 Samba 配置 ( smb.conf)。确保定义的共享和路径具有正确的权限。
另外,检查任何valid usersread list、 或write list指令并确保列出的用户具有适当的权限。

如果您运行的系统启用了 SELinux,这可能会导致权限问题。您可以暂时将 SELinux 设置为宽容模式,看看是否可以解决问题:

sudo setenforce 0
Run Code Online (Sandbox Code Playgroud)

如果这解决了问题,您将需要创建适当的 SELinux 策略或调整 Samba 相关文件和目录的上下文。