public synchronized static RegistryAuthLocator instance() { if (instance == null) { instance = new RegistryAuthLocator(); } return instance; }
public AuthConfig effectiveAuthConfig(String imageName) { // allow docker-java auth config to be used as a fallback AuthConfig fallbackAuthConfig; try { fallbackAuthConfig = delegate.effectiveAuthConfig(imageName); } catch (Exception e) { log.debug("Delegate call to effectiveAuthConfig failed with cause: '{}'. " + "Resolution of auth config will continue using RegistryAuthLocator.", e.getMessage()); fallbackAuthConfig = new AuthConfig(); } // try and obtain more accurate auth config using our resolution final DockerImageName parsed = new DockerImageName(imageName); final AuthConfig effectiveAuthConfig = RegistryAuthLocator.instance() .lookupAuthConfig(parsed, fallbackAuthConfig); log.debug("Effective auth config [{}]", toSafeString(effectiveAuthConfig)); return effectiveAuthConfig; }
final String registryName = effectiveRegistryName(dockerImageName); log.debug("registryName [{}] for dockerImageName [{}]", registryName, dockerImageName); final AuthConfig helperAuthConfig = authConfigUsingHelper(config, registryName); if (helperAuthConfig != null) { log.debug("found helper auth config [{}]", toSafeString(helperAuthConfig)); final AuthConfig storeAuthConfig = authConfigUsingStore(config, registryName); if (storeAuthConfig != null) { log.debug("found creds store auth config [{}]", toSafeString(storeAuthConfig)); final AuthConfig existingAuthConfig = findExistingAuthConfig(config, registryName); if (existingAuthConfig != null) { log.debug("found existing auth config [{}]", toSafeString(existingAuthConfig));
private String discoverCredentialsHelperNotFoundMessage(String credentialHelperName) { // will do fake call to given credential helper to find out with which message // it response when there are no credentials for given hostName // hostName should be valid, but most probably not existing // IF its not enough, then should probably run 'list' command first to be sure... final String notExistentFakeHostName = "https://not.a.real.registry/url"; String credentialsNotFoundMsg = null; try { runCredentialProgram(notExistentFakeHostName, credentialHelperName); // should not reach here log.warn("Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response", credentialHelperName); } catch(Exception e) { if (e instanceof InvalidResultException) { credentialsNotFoundMsg = extractCredentialProviderErrorMessage((InvalidResultException)e); } if (isBlank(credentialsNotFoundMsg)) { log.warn("Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response. Exception message: {}", credentialHelperName, e.getMessage()); } else { log.debug("Got credentials not found error message from docker credential helper - {}", credentialsNotFoundMsg); } } return credentialsNotFoundMsg; }
final String credentialProgramName = getCredentialProgramName(helperOrStoreName); final String data; data = runCredentialProgram(hostName, credentialProgramName); } catch (InvalidResultException e) { final String responseErrorMsg = extractCredentialProviderErrorMessage(e); String credentialsNotFoundMsg = getGenericCredentialsNotFoundMsg(credentialProgramName); if (credentialsNotFoundMsg != null && credentialsNotFoundMsg.equals(responseErrorMsg)) { log.info("Credentials not found for host ({}) when using credential helper/store ({})",
@Test public void lookupAuthConfigWithCredentialsNotFound() throws URISyntaxException { Map<String, String> notFoundMessagesReference = new HashMap<>(); final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-store.json", notFoundMessagesReference); DockerImageName dockerImageName = new DockerImageName("registry2.example.com/org/repo"); final AuthConfig authConfig = authLocator.lookupAuthConfig(dockerImageName, new AuthConfig()); assertNull("No username should have been obtained from a credential store", authConfig.getUsername()); assertNull("No secret should have been obtained from a credential store", authConfig.getPassword()); assertEquals("Should have one 'credentials not found' message discovered", 1, notFoundMessagesReference.size()); String discoveredMessage = notFoundMessagesReference.values().iterator().next(); assertEquals( "Not correct message discovered", "Fake credentials not found on credentials store 'https://not.a.real.registry/url'", discoveredMessage); }
@Test public void testThatAuthLocatorIsUsed() throws Exception { final DockerImageName expectedName = new DockerImageName(testImageNameWithTag); final AuthConfig authConfig = new AuthConfig() .withUsername("testuser") .withPassword("notasecret") .withRegistryAddress("http://" + testRegistryAddress); // Replace the RegistryAuthLocator singleton with our mock, for the duration of this test final RegistryAuthLocator mockAuthLocator = Mockito.mock(RegistryAuthLocator.class); RegistryAuthLocator.setInstance(mockAuthLocator); when(mockAuthLocator.lookupAuthConfig(eq(expectedName), any())) .thenReturn(authConfig); // a push will use the auth locator for authentication, although that isn't the goal of this test putImageInRegistry(); // actually start a container, which will require an authenticated pull try (final GenericContainer container = new GenericContainer<>(testImageNameWithTag) .withCommand("/bin/sh", "-c", "sleep 10")) { container.start(); assertTrue("container started following an authenticated pull", container.isRunning()); } }
@BeforeClass public static void setUp() { originalAuthLocatorSingleton = RegistryAuthLocator.instance(); client = DockerClientFactory.instance().client(); testRegistryAddress = authenticatedRegistry.getContainerIpAddress() + ":" + authenticatedRegistry.getFirstMappedPort(); testImageName = testRegistryAddress + "/alpine"; testImageNameWithTag = testImageName + ":latest"; }
private String getGenericCredentialsNotFoundMsg(String credentialHelperName) { if (!CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE.containsKey(credentialHelperName)) { String credentialsNotFoundMsg = discoverCredentialsHelperNotFoundMessage(credentialHelperName); if (!isBlank(credentialsNotFoundMsg)) { CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE.put(credentialHelperName, credentialsNotFoundMsg); } } return CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE.get(credentialHelperName); }
private AuthConfig findExistingAuthConfig(final JsonNode config, final String reposName) throws Exception { final Map.Entry<String, JsonNode> entry = findAuthNode(config, reposName); if (entry != null && entry.getValue() != null && entry.getValue().size() > 0) { final AuthConfig deserializedAuth = OBJECT_MAPPER .treeToValue(entry.getValue(), AuthConfig.class) .withRegistryAddress(entry.getKey()); if (isBlank(deserializedAuth.getUsername()) && isBlank(deserializedAuth.getPassword()) && !isBlank(deserializedAuth.getAuth())) { final String rawAuth = new String(Base64.getDecoder().decode(deserializedAuth.getAuth())); final String[] splitRawAuth = rawAuth.split(":"); if (splitRawAuth.length == 2) { deserializedAuth.withUsername(splitRawAuth[0]); deserializedAuth.withPassword(splitRawAuth[1]); } } return deserializedAuth; } return null; }
final String credentialProgramName = getCredentialProgramName(helperOrStoreName); final String data; data = runCredentialProgram(hostName, credentialProgramName); } catch (InvalidResultException e) { final String responseErrorMsg = extractCredentialProviderErrorMessage(e); String credentialsNotFoundMsg = getGenericCredentialsNotFoundMsg(credentialProgramName); if (credentialsNotFoundMsg != null && credentialsNotFoundMsg.equals(responseErrorMsg)) { log.info("Credentials not found for host ({}) when using credential helper/store ({})",
@Test public void lookupAuthConfigUsingHelper() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-with-helper.json"); final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo"), new AuthConfig()); assertEquals("Correct server URL is obtained from a credential store", "url", authConfig.getRegistryAddress()); assertEquals("Correct username is obtained from a credential store", "username", authConfig.getUsername()); assertEquals("Correct secret is obtained from a credential store", "secret", authConfig.getPassword()); }
private String discoverCredentialsHelperNotFoundMessage(String credentialHelperName) { // will do fake call to given credential helper to find out with which message // it response when there are no credentials for given hostName // hostName should be valid, but most probably not existing // IF its not enough, then should probably run 'list' command first to be sure... final String notExistentFakeHostName = "https://not.a.real.registry/url"; String credentialsNotFoundMsg = null; try { runCredentialProgram(notExistentFakeHostName, credentialHelperName); // should not reach here log.warn("Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response", credentialHelperName); } catch(Exception e) { if (e instanceof InvalidResultException) { credentialsNotFoundMsg = extractCredentialProviderErrorMessage((InvalidResultException)e); } if (isBlank(credentialsNotFoundMsg)) { log.warn("Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response. Exception message: {}", credentialHelperName, e.getMessage()); } else { log.debug("Got credentials not found error message from docker credential helper - {}", credentialsNotFoundMsg); } } return credentialsNotFoundMsg; }
private String getGenericCredentialsNotFoundMsg(String credentialHelperName) { if (!CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE.containsKey(credentialHelperName)) { String credentialsNotFoundMsg = discoverCredentialsHelperNotFoundMessage(credentialHelperName); if (!isBlank(credentialsNotFoundMsg)) { CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE.put(credentialHelperName, credentialsNotFoundMsg); } } return CREDENTIALS_HELPERS_NOT_FOUND_MESSAGE_CACHE.get(credentialHelperName); }
private AuthConfig findExistingAuthConfig(final JsonNode config, final String reposName) throws Exception { final Map.Entry<String, JsonNode> entry = findAuthNode(config, reposName); if (entry != null && entry.getValue() != null && entry.getValue().size() > 0) { final AuthConfig deserializedAuth = OBJECT_MAPPER .treeToValue(entry.getValue(), AuthConfig.class) .withRegistryAddress(entry.getKey()); if (isBlank(deserializedAuth.getUsername()) && isBlank(deserializedAuth.getPassword()) && !isBlank(deserializedAuth.getAuth())) { final String rawAuth = new String(Base64.getDecoder().decode(deserializedAuth.getAuth())); final String[] splitRawAuth = rawAuth.split(":"); if (splitRawAuth.length == 2) { deserializedAuth.withUsername(splitRawAuth[0]); deserializedAuth.withPassword(splitRawAuth[1]); } } return deserializedAuth; } return null; }
final String registryName = effectiveRegistryName(dockerImageName); log.debug("registryName [{}] for dockerImageName [{}]", registryName, dockerImageName); final AuthConfig helperAuthConfig = authConfigUsingHelper(config, registryName); if (helperAuthConfig != null) { log.debug("found helper auth config [{}]", toSafeString(helperAuthConfig)); final AuthConfig storeAuthConfig = authConfigUsingStore(config, registryName); if (storeAuthConfig != null) { log.debug("found creds store auth config [{}]", toSafeString(storeAuthConfig)); final AuthConfig existingAuthConfig = findExistingAuthConfig(config, registryName); if (existingAuthConfig != null) { log.debug("found existing auth config [{}]", toSafeString(existingAuthConfig));
public AuthConfig effectiveAuthConfig(String imageName) { // allow docker-java auth config to be used as a fallback AuthConfig fallbackAuthConfig; try { fallbackAuthConfig = delegate.effectiveAuthConfig(imageName); } catch (Exception e) { log.debug("Delegate call to effectiveAuthConfig failed with cause: \'{}\'. Resolution of auth config will continue using RegistryAuthLocator.", e.getMessage()); fallbackAuthConfig = new AuthConfig(); } // try and obtain more accurate auth config using our resolution final DockerImageName parsed = new DockerImageName(imageName); final AuthConfig effectiveAuthConfig = RegistryAuthLocator.instance().lookupAuthConfig(parsed, fallbackAuthConfig); log.debug("Effective auth config [{}]", toSafeString(effectiveAuthConfig)); return effectiveAuthConfig; }
@Test public void lookupNonEmptyAuthWithHelper() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-existing-auth-with-helper.json"); final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("registry.example.com/org/repo"), new AuthConfig()); assertEquals("Correct server URL is obtained from a credential helper", "url", authConfig.getRegistryAddress()); assertEquals("Correct username is obtained from a credential helper", "username", authConfig.getUsername()); assertEquals("Correct password is obtained from a credential helper", "secret", authConfig.getPassword()); }
@NotNull private RegistryAuthLocator createTestAuthLocator(String configName, Map<String, String> notFoundMessagesReference) throws URISyntaxException { final File configFile = new File(Resources.getResource("auth-config/" + configName).toURI()); String commandPathPrefix = configFile.getParentFile().getAbsolutePath() + "/"; String commandExtension = ""; if (SystemUtils.IS_OS_WINDOWS) { commandPathPrefix += "win/"; // need to provide executable extension otherwise won't run it // with real docker wincredential exe there is no problem commandExtension = ".bat"; } return new RegistryAuthLocator(configFile, commandPathPrefix, commandExtension, notFoundMessagesReference); } }
@Test public void lookupAuthConfigWithoutCredentials() throws URISyntaxException { final RegistryAuthLocator authLocator = createTestAuthLocator("config-empty.json"); final AuthConfig authConfig = authLocator.lookupAuthConfig(new DockerImageName("unauthenticated.registry.org/org/repo"), new AuthConfig()); assertEquals("Default docker registry URL is set on auth config", "https://index.docker.io/v1/", authConfig.getRegistryAddress()); assertNull("No username is set", authConfig.getUsername()); assertNull("No password is set", authConfig.getPassword()); }