Publish Docker images using Spring Boot Plugin without credentials

1.5k Views Asked by At

I've got a Gradle project with Spring Boot plugin and am trying to publish the image built by the plugin: gradle bootBuildImage --publishImage

The problem is to publish "either token or username/password must be provided" and we can't do that since we have different authentication mechanism in different environments. For example on local machine we're using ecr-credentials-helper and the pipeline uses aws ecr get-login-token | docker login.

Is there any way to force the plugin to let the docker handles the authentication? (I'm assuming the plugin uses the docker daemon on the host).

Currently I wrote a task to generate a token file using aws ecr get-login-token and read the token file in bootBuildImage task. But I don't like this solution, due to security reasons.

2

There are 2 best solutions below

0
On

Here's a solution that replicates aws ecr get-login-password within Gradle, using the AWS Java SDK. Although you could instead invoke the CLI directly from Gradle, this makes the build script more fragile, as it then depends on having a certain version of the CLI installed. Particularly so since ecr login was a breaking change between v1 and v2 of the CLI.

This assumes you have your AWS credentials set up in some standard way that the default credentials provider in the SDK will locate.

import software.amazon.awssdk.services.ecr.EcrClient

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath platform("software.amazon.awssdk:bom:2.17.19")
        classpath "software.amazon.awssdk:ecr"
        classpath "software.amazon.awssdk:sts" // sts is required to use roleArn in aws profiles
    }
}

plugins {
    id "java"
    id "org.springframework.boot"
}

dependencies {
    implementation platform("org.springframework.boot:spring-boot-dependencies:2.5.3")
    implementation "org.springframework.boot:spring-boot-starter"
    // Rest of app dependencies
}

bootBuildImage {
    doFirst {
        String base64Token = EcrClient.create().getAuthorizationToken().authorizationData()[0].authorizationToken()
        String[] auth = new String( base64Token.decodeBase64() ).split(":", 2)

        docker {
            publishRegistry {
                username = auth[0]
                password = auth[1]
            }
        }
    }
}
0
On

The solution of Adrian doesn't work anymore with newer Gradle versions. I adapted his solution:

import org.springframework.boot.gradle.tasks.bundling.BootBuildImage
import software.amazon.awssdk.services.ecr.EcrClient
import software.amazon.awssdk.services.ecr.model.AuthorizationData
import java.util.Base64

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath(platform("software.amazon.awssdk:bom:2.20.121"))
        classpath("software.amazon.awssdk:ecr")
        classpath("software.amazon.awssdk:sts") // sts is required to use roleArn in aws profiles
    }
}

plugins {
    id("org.springframework.boot")
    kotlin("jvm")
    kotlin("plugin.spring")
}

tasks.withType<BootBuildImage> {

    val authData: Provider<AuthorizationData> = providers.provider {
        val ecrClient = EcrClient.builder().build()
        val authorizationData = ecrClient
                .getAuthorizationToken()
                .authorizationData()[0]
        return@provider authorizationData
    }

    val decodedEcrToken: Provider<String> = authData.map { String(Base64.getDecoder().decode(it.authorizationToken())) }
    val registryUsername: Provider<String> = decodedEcrToken.map { it.split(":")[0] }
    val registryPassword: Provider<String> = decodedEcrToken.map { it.split(":")[1] }

    docker {
        publishRegistry {
            username.set(registryUsername)
            password.set(registryPassword)
        }
    }
}