Add custom docker registry authentication (#665)

* Add custom docker registry authentication

Uses DOCKER_USERNAME and DOCKER_PASSWORD as secrets provided into
the act cli.

Closes #527

Co-authored-by: Björn Brauer <zaubernerd@zaubernerd.de>

* Add test to check if pull authentication is filled in

* Update debug message to be more descriptive

Co-authored-by: Ryan (hackercat) <me@hackerc.at>

Co-authored-by: Björn Brauer <zaubernerd@zaubernerd.de>
Co-authored-by: Ryan (hackercat) <me@hackerc.at>
This commit is contained in:
Markus Wolf 2021-05-05 18:37:17 +02:00 committed by GitHub
parent 616d7fcaeb
commit 710a3ac94c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 61 additions and 4 deletions

View file

@ -2,6 +2,8 @@ package container
import ( import (
"context" "context"
"encoding/base64"
"encoding/json"
"fmt" "fmt"
"strings" "strings"
@ -17,6 +19,8 @@ type NewDockerPullExecutorInput struct {
Image string Image string
ForcePull bool ForcePull bool
Platform string Platform string
Username string
Password string
} }
// NewDockerPullExecutor function to create a run executor for the container // NewDockerPullExecutor function to create a run executor for the container
@ -54,9 +58,13 @@ func NewDockerPullExecutor(input NewDockerPullExecutorInput) common.Executor {
return err return err
} }
reader, err := cli.ImagePull(ctx, imageRef, types.ImagePullOptions{ imagePullOptions, err := getImagePullOptions(ctx, input)
Platform: input.Platform, if err != nil {
}) return err
}
reader, err := cli.ImagePull(ctx, imageRef, imagePullOptions)
_ = logDockerResponse(logger, reader, err != nil) _ = logDockerResponse(logger, reader, err != nil)
if err != nil { if err != nil {
return err return err
@ -65,6 +73,30 @@ func NewDockerPullExecutor(input NewDockerPullExecutorInput) common.Executor {
} }
} }
func getImagePullOptions(ctx context.Context, input NewDockerPullExecutorInput) (types.ImagePullOptions, error) {
imagePullOptions := types.ImagePullOptions{
Platform: input.Platform,
}
if input.Username != "" && input.Password != "" {
logger := common.Logger(ctx)
logger.Debugf("using authentication for docker pull")
authConfig := types.AuthConfig{
Username: input.Username,
Password: input.Password,
}
encodedJSON, err := json.Marshal(authConfig)
if err != nil {
return imagePullOptions, err
}
imagePullOptions.RegistryAuth = base64.URLEncoding.EncodeToString(encodedJSON)
}
return imagePullOptions, nil
}
func cleanImage(image string) string { func cleanImage(image string) string {
imageParts := len(strings.Split(image, "/")) imageParts := len(strings.Split(image, "/"))
if imageParts == 1 { if imageParts == 1 {

View file

@ -1,10 +1,11 @@
package container package container
import ( import (
"context"
"testing" "testing"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert" "gotest.tools/v3/assert"
) )
func init() { func init() {
@ -27,3 +28,19 @@ func TestCleanImage(t *testing.T) {
assert.Equal(t, table.imageOut, imageOut) assert.Equal(t, table.imageOut, imageOut)
} }
} }
func TestGetImagePullOptions(t *testing.T) {
ctx := context.Background()
options, err := getImagePullOptions(ctx, NewDockerPullExecutorInput{})
assert.NilError(t, err, "Failed to create ImagePullOptions")
assert.Equal(t, options.RegistryAuth, "", "RegistryAuth should be empty if no username or password is set")
options, err = getImagePullOptions(ctx, NewDockerPullExecutorInput{
Image: "",
Username: "username",
Password: "password",
})
assert.NilError(t, err, "Failed to create ImagePullOptions")
assert.Equal(t, options.RegistryAuth, "eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwicGFzc3dvcmQiOiJwYXNzd29yZCJ9", "Username and Password should be provided")
}

View file

@ -37,6 +37,8 @@ import (
// NewContainerInput the input for the New function // NewContainerInput the input for the New function
type NewContainerInput struct { type NewContainerInput struct {
Image string Image string
Username string
Password string
Entrypoint []string Entrypoint []string
Cmd []string Cmd []string
WorkingDir string WorkingDir string
@ -126,6 +128,8 @@ func (cr *containerReference) Pull(forcePull bool) common.Executor {
Image: cr.input.Image, Image: cr.input.Image,
ForcePull: forcePull, ForcePull: forcePull,
Platform: cr.input.Platform, Platform: cr.input.Platform,
Username: cr.input.Username,
Password: cr.input.Password,
}) })
} }
func (cr *containerReference) Copy(destPath string, files ...*FileEntry) common.Executor { func (cr *containerReference) Copy(destPath string, files ...*FileEntry) common.Executor {

View file

@ -118,6 +118,8 @@ func (rc *RunContext) startJobContainer() common.Executor {
Entrypoint: []string{"/usr/bin/tail", "-f", "/dev/null"}, Entrypoint: []string{"/usr/bin/tail", "-f", "/dev/null"},
WorkingDir: rc.Config.ContainerWorkdir(), WorkingDir: rc.Config.ContainerWorkdir(),
Image: image, Image: image,
Username: rc.Config.Secrets["DOCKER_USERNAME"],
Password: rc.Config.Secrets["DOCKER_PASSWORD"],
Name: name, Name: name,
Env: envList, Env: envList,
Mounts: mounts, Mounts: mounts,

View file

@ -247,6 +247,8 @@ func (sc *StepContext) newStepContainer(ctx context.Context, image string, cmd [
Entrypoint: entrypoint, Entrypoint: entrypoint,
WorkingDir: rc.Config.ContainerWorkdir(), WorkingDir: rc.Config.ContainerWorkdir(),
Image: image, Image: image,
Username: rc.Config.Secrets["DOCKER_USERNAME"],
Password: rc.Config.Secrets["DOCKER_PASSWORD"],
Name: createContainerName(rc.jobContainerName(), step.ID), Name: createContainerName(rc.jobContainerName(), step.ID),
Env: envList, Env: envList,
Mounts: mounts, Mounts: mounts,