Skip to content

Commit aa948e0

Browse files
committed
Test login to registry as well
1 parent c294236 commit aa948e0

File tree

2 files changed

+77
-6
lines changed

2 files changed

+77
-6
lines changed

dev-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ pytest-cov
55
pytest>=7
66
pyyaml
77
requests_mock
8+
bcrypt

tests/norun/test_registry.py

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
import json
12
import os
23
import secrets
34
import shutil
45
import socket
56
import subprocess
7+
from tempfile import TemporaryDirectory
8+
from base64 import b64encode
69
import time
10+
import bcrypt
711
from pathlib import Path
812

913
import pytest
@@ -16,8 +20,9 @@
1620

1721

1822
@pytest.fixture(scope="session")
19-
def dind(registry, host_ip):
23+
def dind(registry):
2024
port = get_free_port()
25+
registry_host, _, _ = registry
2126

2227
# docker daemon will generate certs here, that we can then use to connect to it.
2328
# put it in current dir than in /tmp because on macos, current dir is likely to
@@ -42,7 +47,7 @@ def dind(registry, host_ip):
4247
"--host",
4348
"0.0.0.0:2376",
4449
"--insecure-registry",
45-
registry,
50+
registry_host,
4651
]
4752
proc = subprocess.Popen(cmd)
4853
time.sleep(5)
@@ -80,11 +85,27 @@ def host_ip():
8085
@pytest.fixture(scope="session")
8186
def registry(host_ip):
8287
port = get_free_port()
88+
username = "user"
89+
password = secrets.token_hex(16)
90+
bcrypted_pw = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt(rounds=12)).decode("utf-8")
91+
92+
# We put our password here, and mount it into the container.
93+
# put it in current dir than in /tmp because on macos, current dir is likely to
94+
# shared with docker VM so it can be mounted, unlike /tmp
95+
htpasswd_dir = HERE / f"tmp-certs-{secrets.token_hex(8)}"
96+
htpasswd_dir.mkdir()
97+
(htpasswd_dir / "htpasswd.conf").write_text(f"{username}:{bcrypted_pw}")
98+
8399
# Explicitly pull the image first so it runs on time
84100
registry_image = "registry:3.0.0-rc.3"
85101
subprocess.check_call(["docker", "pull", registry_image])
86102

87-
cmd = ["docker", "run", "--rm", "-p", f"{port}:5000", registry_image]
103+
cmd = ["docker", "run", "--rm",
104+
"-e", "REGISTRY_AUTH=htpasswd",
105+
"-e", "REGISTRY_AUTH_HTPASSWD_REALM=basic",
106+
"-e", "REGISTRY_AUTH_HTPASSWD_PATH=/opt/htpasswd/htpasswd.conf",
107+
"--mount", f"type=bind,src={htpasswd_dir},dst=/opt/htpasswd",
108+
"-p", f"{port}:5000", registry_image]
88109
proc = subprocess.Popen(cmd)
89110
health_url = f"http://{host_ip}:{port}/v2"
90111
# Wait for the registry to actually come up
@@ -101,14 +122,18 @@ def registry(host_ip):
101122
raise TimeoutError("Test registry did not come up in time")
102123

103124
try:
104-
yield f"{host_ip}:{port}"
125+
yield f"{host_ip}:{port}", username, password
105126
finally:
106127
proc.terminate()
107128
proc.wait()
108129

109130

110-
def test_registry(registry, dind):
111-
image_name = f"{registry}/{secrets.token_hex(8)}:latest"
131+
def test_registry_explicit_creds(registry, dind):
132+
"""
133+
Test that we can push to registry when given explicit credentials
134+
"""
135+
registry_host, username, password = registry
136+
image_name = f"{registry_host}/{secrets.token_hex(8)}:latest"
112137
r2d = make_r2d(["--image", image_name, "--push", "--no-run", str(HERE)])
113138

114139
docker_host, cert_dir = dind
@@ -119,10 +144,55 @@ def test_registry(registry, dind):
119144
os.environ["DOCKER_HOST"] = docker_host
120145
os.environ["DOCKER_CERT_PATH"] = str(cert_dir / "client")
121146
os.environ["DOCKER_TLS_VERIFY"] = "1"
147+
os.environ["CONTAINER_ENGINE_REGISTRY_CREDENTIALS"] = json.dumps({
148+
"registry": f"http://{registry_host}",
149+
"username": username,
150+
"password": password
151+
})
122152
r2d.start()
123153

154+
124155
proc = subprocess.run(["docker", "manifest", "inspect", "--insecure", image_name])
125156
assert proc.returncode == 0
157+
158+
# Validate that we didn't leak our registry creds into existing docker config
159+
docker_config_path = Path(os.environ.get("DOCKER_CONFIG", "~/.docker/config.json")).expanduser()
160+
if docker_config_path.exists():
161+
# Just check that our randomly generated password is not in this file
162+
# Can this cause a conflict? Sure, if there's a different randomly generated password in here
163+
# that matches our own randomly generated password. But if you're that unlucky, take cover from the asteroid.
164+
assert password not in docker_config_path.read_text()
165+
finally:
166+
os.environ.clear()
167+
os.environ.update(old_environ)
168+
169+
170+
def test_registry_no_explicit_creds(registry, dind):
171+
"""
172+
Test that we can push to registry *without* explicit credentials but reading from a DOCKER_CONFIG
173+
"""
174+
registry_host, username, password = registry
175+
image_name = f"{registry_host}/{secrets.token_hex(8)}:latest"
176+
r2d = make_r2d(["--image", image_name, "--push", "--no-run", str(HERE)])
177+
178+
docker_host, cert_dir = dind
179+
180+
old_environ = os.environ.copy()
181+
182+
try:
183+
os.environ["DOCKER_HOST"] = docker_host
184+
os.environ["DOCKER_CERT_PATH"] = str(cert_dir / "client")
185+
os.environ["DOCKER_TLS_VERIFY"] = "1"
186+
with TemporaryDirectory() as d:
187+
(Path(d) / "config.json").write_text(json.dumps(
188+
({"auths":{f"http://{registry_host}":{"auth":b64encode(f"{username}:{password}".encode()).decode()}}})
189+
))
190+
os.environ["DOCKER_CONFIG"] = d
191+
r2d.start()
192+
193+
194+
proc = subprocess.run(["docker", "manifest", "inspect", "--insecure", image_name])
195+
assert proc.returncode == 0
126196
finally:
127197
os.environ.clear()
128198
os.environ.update(old_environ)

0 commit comments

Comments
 (0)