From d064863f9bb65cde863c8cb20fea50e0f139032c Mon Sep 17 00:00:00 2001 From: Robin Breathe Date: Mon, 16 Jan 2023 15:12:20 +0100 Subject: [PATCH] fix: allow override of artifact server bind address (#1560) * Prior to this change, the artifact server always binds to the detected "outbound IP", breaks functionality when that IP is unroutable. For example, Zscaler assigns the host a local CGNAT address, 100.64.0.1, which is unreachable from Docker Desktop. * Add the `--artifact-server-addr` flag to allow override of the address to which the artifact server binds, defaulting to the existing behaviour. Fixes: #1559 --- README.md | 3 ++- cmd/input.go | 1 + cmd/root.go | 6 ++++-- pkg/artifacts/server.go | 7 +++---- pkg/artifacts/server_test.go | 10 ++++++---- pkg/runner/run_context.go | 2 +- pkg/runner/runner.go | 1 + 7 files changed, 18 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index f8577ee..6423a54 100644 --- a/README.md +++ b/README.md @@ -169,8 +169,9 @@ It will save that information to `~/.actrc`, please refer to [Configuration](#co -a, --actor string user that triggered the event (default "nektos/act") --replace-ghe-action-with-github-com If you are using GitHub Enterprise Server and allow specified actions from GitHub (github.com), you can set actions on this. (e.g. --replace-ghe-action-with-github-com=github/super-linter) --replace-ghe-action-token-with-github-com If you are using replace-ghe-action-with-github-com and you want to use private actions on GitHub, you have to set personal access token + --artifact-server-addr string Defines the address to which the artifact server binds. (default "") --artifact-server-path string Defines the path where the artifact server stores uploads and retrieves downloads from. If not specified the artifact server will not start. - --artifact-server-port string Defines the port where the artifact server listens (will only bind to localhost). (default "34567") + --artifact-server-port string Defines the port where the artifact server listens. (default "34567") -b, --bind bind working directory to container, rather than copy --container-architecture string Architecture which should be used to run containers, e.g.: linux/amd64. If not specified, will use host default architecture. Requires Docker server API Version 1.41+. Ignored on earlier Docker server platforms. --container-cap-add stringArray kernel capabilities to add to the workflow containers (e.g. --container-cap-add SYS_PTRACE) diff --git a/cmd/input.go b/cmd/input.go index 1caeccb..37655a5 100644 --- a/cmd/input.go +++ b/cmd/input.go @@ -40,6 +40,7 @@ type Input struct { containerCapDrop []string autoRemove bool artifactServerPath string + artifactServerAddr string artifactServerPort string jsonLogger bool noSkipCheckout bool diff --git a/cmd/root.go b/cmd/root.go index 1d153ab..78f3a72 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -82,7 +82,8 @@ func Execute(ctx context.Context, version string) { rootCmd.PersistentFlags().StringVarP(&input.containerOptions, "container-options", "", "", "Custom docker container options for the job container without an options property in the job definition") rootCmd.PersistentFlags().StringVarP(&input.githubInstance, "github-instance", "", "github.com", "GitHub instance to use. Don't use this if you are not using GitHub Enterprise Server.") rootCmd.PersistentFlags().StringVarP(&input.artifactServerPath, "artifact-server-path", "", "", "Defines the path where the artifact server stores uploads and retrieves downloads from. If not specified the artifact server will not start.") - rootCmd.PersistentFlags().StringVarP(&input.artifactServerPort, "artifact-server-port", "", "34567", "Defines the port where the artifact server listens (will only bind to localhost).") + rootCmd.PersistentFlags().StringVarP(&input.artifactServerAddr, "artifact-server-addr", "", common.GetOutboundIP().String(), "Defines the address to which the artifact server binds.") + rootCmd.PersistentFlags().StringVarP(&input.artifactServerPort, "artifact-server-port", "", "34567", "Defines the port where the artifact server listens.") rootCmd.PersistentFlags().BoolVarP(&input.noSkipCheckout, "no-skip-checkout", "", false, "Do not skip actions/checkout") rootCmd.SetArgs(args()) @@ -482,6 +483,7 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str ContainerCapDrop: input.containerCapDrop, AutoRemove: input.autoRemove, ArtifactServerPath: input.artifactServerPath, + ArtifactServerAddr: input.artifactServerAddr, ArtifactServerPort: input.artifactServerPort, NoSkipCheckout: input.noSkipCheckout, RemoteName: input.remoteName, @@ -493,7 +495,7 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str return err } - cancel := artifacts.Serve(ctx, input.artifactServerPath, input.artifactServerPort) + cancel := artifacts.Serve(ctx, input.artifactServerPath, input.artifactServerAddr, input.artifactServerPort) ctx = common.WithDryrun(ctx, input.dryrun) if watch, err := cmd.Flags().GetBool("watch"); err != nil { diff --git a/pkg/artifacts/server.go b/pkg/artifacts/server.go index 06a7706..9b11457 100644 --- a/pkg/artifacts/server.go +++ b/pkg/artifacts/server.go @@ -262,7 +262,7 @@ func downloads(router *httprouter.Router, fsys fs.FS) { }) } -func Serve(ctx context.Context, artifactPath string, port string) context.CancelFunc { +func Serve(ctx context.Context, artifactPath string, addr string, port string) context.CancelFunc { serverContext, cancel := context.WithCancel(ctx) logger := common.Logger(serverContext) @@ -276,17 +276,16 @@ func Serve(ctx context.Context, artifactPath string, port string) context.Cancel fs := os.DirFS(artifactPath) uploads(router, MkdirFsImpl{artifactPath, fs}) downloads(router, fs) - ip := common.GetOutboundIP().String() server := &http.Server{ - Addr: fmt.Sprintf("%s:%s", ip, port), + Addr: fmt.Sprintf("%s:%s", addr, port), ReadHeaderTimeout: 2 * time.Second, Handler: router, } // run server go func() { - logger.Infof("Start server on http://%s:%s", ip, port) + logger.Infof("Start server on http://%s:%s", addr, port) if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { logger.Fatal(err) } diff --git a/pkg/artifacts/server_test.go b/pkg/artifacts/server_test.go index f1c09a3..6f865dc 100644 --- a/pkg/artifacts/server_test.go +++ b/pkg/artifacts/server_test.go @@ -240,7 +240,8 @@ type TestJobFileInfo struct { containerArchitecture string } -var aritfactsPath = path.Join(os.TempDir(), "test-artifacts") +var artifactsPath = path.Join(os.TempDir(), "test-artifacts") +var artifactsAddr = "127.0.0.1" var artifactsPort = "12345" func TestArtifactFlow(t *testing.T) { @@ -250,7 +251,7 @@ func TestArtifactFlow(t *testing.T) { ctx := context.Background() - cancel := Serve(ctx, aritfactsPath, artifactsPort) + cancel := Serve(ctx, artifactsPath, artifactsAddr, artifactsPort) defer cancel() platforms := map[string]string{ @@ -271,7 +272,7 @@ func runTestJobFile(ctx context.Context, t *testing.T, tjfi TestJobFileInfo) { t.Run(tjfi.workflowPath, func(t *testing.T) { fmt.Printf("::group::%s\n", tjfi.workflowPath) - if err := os.RemoveAll(aritfactsPath); err != nil { + if err := os.RemoveAll(artifactsPath); err != nil { panic(err) } @@ -286,7 +287,8 @@ func runTestJobFile(ctx context.Context, t *testing.T, tjfi TestJobFileInfo) { ReuseContainers: false, ContainerArchitecture: tjfi.containerArchitecture, GitHubInstance: "github.com", - ArtifactServerPath: aritfactsPath, + ArtifactServerPath: artifactsPath, + ArtifactServerAddr: artifactsAddr, ArtifactServerPort: artifactsPort, } diff --git a/pkg/runner/run_context.go b/pkg/runner/run_context.go index c9c8ea1..fa2da05 100644 --- a/pkg/runner/run_context.go +++ b/pkg/runner/run_context.go @@ -751,7 +751,7 @@ func (rc *RunContext) withGithubEnv(ctx context.Context, github *model.GithubCon func setActionRuntimeVars(rc *RunContext, env map[string]string) { actionsRuntimeURL := os.Getenv("ACTIONS_RUNTIME_URL") if actionsRuntimeURL == "" { - actionsRuntimeURL = fmt.Sprintf("http://%s:%s/", common.GetOutboundIP().String(), rc.Config.ArtifactServerPort) + actionsRuntimeURL = fmt.Sprintf("http://%s:%s/", rc.Config.ArtifactServerAddr, rc.Config.ArtifactServerPort) } env["ACTIONS_RUNTIME_URL"] = actionsRuntimeURL diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 9eb225f..7e3f9b1 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -48,6 +48,7 @@ type Config struct { ContainerCapDrop []string // list of kernel capabilities to remove from the containers AutoRemove bool // controls if the container is automatically removed upon workflow completion ArtifactServerPath string // the path where the artifact server stores uploads + ArtifactServerAddr string // the address the artifact server binds to ArtifactServerPort string // the port the artifact server binds to NoSkipCheckout bool // do not skip actions/checkout RemoteName string // remote name in local git repo config