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:
parent
616d7fcaeb
commit
710a3ac94c
5 changed files with 61 additions and 4 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue