spring-projects / spring-boot

Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.
https://spring.io/projects/spring-boot
Apache License 2.0
75.08k stars 40.67k forks source link

Image building does not validate that image registry host matches publish registry URL #29281

Open liyuan-rey opened 2 years ago

liyuan-rey commented 2 years ago

hello,

Envronment: System: Windows 10 Home x64 21H2 JDK: 11.0.13 Spring Boot: 2.6.2 Docker Desktop 4.3.2 (WSL 2 based)

We built a private docker repositories with Sonatype Nexus at http://172.2.3.5:9000/.

Try build image for a simple Spring REST WebApp.

build.gradle

plugins {
    id 'java'
    id 'org.springframework.boot' version '2.6.2'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
}

...

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

bootBuildImage {
    imageName = "mycompany/mygroup/myproject:1.0.1"
    publish = true
    docker {
        publishRegistry {
            url = "http://172.2.3.5:9000/"
            username = "user"
            password = "pass"
        }
    }
}

Run .\gradlew.bat bootBuildImage, got outputs like:

...
> Task :bootBuildImage
- Building image 'docker.io/mycompany/mygroup/myproject:1.0.1'

 > Pulling builder image 'docker.io/paketobuildpacks/builder:base' ......
...
 > Pulling run image 'docker.io/paketobuildpacks/run:base-cnb' ......
...
 > Running creator
...

- Successfully built image 'docker.io/mycompany/mygroup/myproject:1.0.1'

- > Pushing image 'docker.io/mycompany/mygroup/myproject:1.0.1' .......

...

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':bootBuildImage'.
> Error response received when pushing image: unauthorized: incorrect username or password

...

Notice that the red lines always output 'docker.io/' even if 'docker.publishRegistry.url' is specified, so I guess the task might try to push the image to ‘docker.io’ instead of '172.2.3.5:9000', and it fails.

Please let me know if there is any misunderstanding, thank you.

wilkinsona commented 2 years ago

You haven't provided a domain when specifying the image name so it defaults to docker.io. If you set imageName to 172.2.3.5:9000/mycompany/mygroup/myproject:1.0.1 it should work.

I wonder if we can make this easier. It'd be nice to not have to duplicate the domain in the image name and in the publish registry URL.

liyuan-rey commented 2 years ago

@wilkinsona Yes, it works when I add host:port to imageName. Thank you for your help! 😄

I'm not sure if there are additional jobs to track, so I leave this issue as "open". It can be closed if necessary.

scottfrederick commented 2 years ago

It'd be nice to not have to duplicate the domain in the image name and in the publish registry URL.

The docker.publishRegistry.url must contain the full URL to the registry, including the scheme part, whereas the image name just contains the registry host. I'm not sure how we'd keep from duplicating the registry host without assuming things about the rest of the URL.

Another option would be to do more validation to ensure that the registry part of the built image and the host part of the registry URL match before attempting to push an image. If additional tags are applied to the built image, and publishing is requested in the build configuration, it is also required that all image tags have the same registry part. We should probably validate that this is the case before publishing also.

scottfrederick commented 2 years ago

The documentation should also clarify that the value of docker.publishRegistry.url has no effect on where a built image gets pushed. It is optional, which is currently mentioned in the documentation.

31820 has been created to cover the documentation change.

mhalbritter commented 4 days ago

The docker.publishRegistry.url must contain the full URL to the registry, including the scheme part, whereas the image name just contains the registry host. I'm not sure how we'd keep from duplicating the registry host without assuming things about the rest of the URL.

If I read the code correctly, docker.publishRegistry.url sets org.springframework.boot.buildpack.platform.docker.configuration.DockerRegistryUserAuthentication#url, which is sent to the Docker Engine in the serveraddress field.

Looking at the docs, it says:

The serveraddress is a domain/IP without a protocol.

I find the docker.publishRegistry.url confusing: When do I need to set it? It appears to be working even when I don't set it, the credentials get sent to the registry.

When setting this to some bogus value, the image is pushed to the registry with the credentials from username and password nonetheless. I first thought this setting exist to select to which registry the credentials are sent, but it doesn't look like it.

scottfrederick commented 4 days ago

When setting this to some bogus value, the image is pushed to the registry with the credentials from username and password nonetheless.

What registry did you test with? It's been a while since I've looked at this, but IIRC the value is inspected by the registry server, and it's possible that different registries handle it differently. If that's the case, we might want to clarify that in the documentation.

mhalbritter commented 4 days ago

Hi Scott! I've tested with https://hub.docker.com/_/registry.

I've thought the Docker Engine inspect that header and then either pass the credentials to the registry or it doesn't if it doesn't match. But i haven't checked the source code.