Como dijo @Connor McCarthy, mientras esperamos que Amazon presente una mejor solución para claves más permanentes, mientras tanto tendríamos que generar las claves en el servidor Jenkins de alguna manera.
Mi solución es tener un trabajo periódico que actualice las credenciales de Jenkins para ECR cada 12 horas automáticamente, utilizando la API Groovy. Esto se basa en esta respuesta muy detallada , aunque hice algunas cosas de manera diferente y tuve que modificar el script.
Pasos:
- Asegúrese de que su maestro Jenkins pueda acceder a la API de AWS requerida. En mi configuración, el maestro Jenkins se ejecuta en EC2 con un rol de IAM, por lo que solo tuve que agregar el permiso
ecr:GetAuthorizationToken
al rol de servidor. [ Actualización ] para conseguir cualquier empuja completan con éxito, también había necesidad de conceder estos permisos: ecr:InitiateLayerUpload, ecr:UploadLayerPart, ecr:CompleteLayerUpload, ecr:BatchCheckLayerAvailability, ecr:PutImage
. Amazon tiene una política incorporada que ofrece estas capacidades, llamada AmazonEC2ContainerRegistryPowerUser
.
- Asegúrese de que la AWS CLI esté instalada en el maestro. En mi configuración, con el maestro ejecutándose en un contenedor de Debian Docker, acabo de agregar este paso de compilación de shell al trabajo de generación de claves:
dpkg -l python-pip >/dev/null 2>&1 || sudo apt-get install python-pip -y; pip list 2>/dev/null | grep -q awscli || pip install awscli
- Instale el complemento Groovy que le permite ejecutar el script Groovy como parte del sistema Jenkins.
- En la pantalla de credenciales, busque su clave de AWS ECR, haga clic en "Avanzado" y registre su "ID". Para este ejemplo, voy a suponer que es "12345".
- Cree un nuevo trabajo, con un lanzamiento periódico de 12 horas, y agregue un paso de compilación del "script Groovy del sistema" con el siguiente script:
import jenkins.model.*
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl
def changePassword = { username, new_password ->
def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
com.cloudbees.plugins.credentials.common.StandardUsernameCredentials.class,
Jenkins.instance)
def c = creds.findResult { it.username == username ? it : null }
if ( c ) {
println "found credential ${c.id} for username ${c.username}"
def credentials_store = Jenkins.instance.getExtensionList(
'com.cloudbees.plugins.credentials.SystemCredentialsProvider'
)[0].getStore()
def result = credentials_store.updateCredentials(
com.cloudbees.plugins.credentials.domains.Domain.global(),
c,
new UsernamePasswordCredentialsImpl(c.scope, "12345", c.description, c.username, new_password))
if (result) {
println "password changed for ${username}"
} else {
println "failed to change password for ${username}"
}
} else {
println "could not find credential for ${username}"
}
}
println "calling AWS for docker login"
def prs = "/usr/local/bin/aws --region us-east-1 ecr get-login".execute()
prs.waitFor()
def logintext = prs.text
if (prs.exitValue()) {
println "Got error from aws cli"
throw new Exception()
} else {
def password = logintext.split(" ")[5]
println "Updating password"
changePassword('AWS', password)
}
Tenga en cuenta:
- el uso de la cadena codificada
"AWS"
como nombre de usuario para las credenciales de ECR: así es como funciona ECR, pero si tiene múltiples credenciales con el nombre de usuario "AWS", entonces necesitará actualizar el script para ubicar las credenciales basadas en campo de descripción o algo así.
- Debe usar la ID real de su clave ECR real en el script, porque la API para credenciales reemplaza el objeto de credenciales con un nuevo objeto en lugar de simplemente actualizarlo, y el enlace entre el paso de compilación de Docker y la clave es por la ID. Si usa el valor
null
para la ID (como en la respuesta que vinculé antes), se creará una nueva ID y se perderá la configuración de las credenciales en el paso de compilación del acoplador.
Y eso es todo: el script debería poder ejecutarse cada 12 horas y actualizar las credenciales de ECR, y podemos continuar usando los complementos de Docker.