Repository: pantsbuild/pants

Issue:
docker-image does not seem to pass ssh authentication
**Is your feature request related to a problem? Please describe.**
The docker-image target doesn't allow the docker `--ssh` argument (often `--ssh default`) which allows SSH forwarding through `mount=type=ssh` commands (see https://docs.docker.com/develop/develop-images/build_enhancements/#using-ssh-to-access-private-data-in-builds).  This means that docker images which pull from private repositories cannot execute correctly when run from `pants` even when the `SSH_AUTH_SOCK` env var is set correctly.

**Describe the solution you'd like**
Please enable the `ssh` argument for docker within the `docker_image` target, ie

```
docker_image(
  dependencies=[...],
  ssh="default"
)
```

**Describe alternatives you've considered**
While `pants` itself can use private repositories in PEX builds, and those PEX builds can be used within docker, that solution still doesn't quite cover all situations.  While adding the above argument does complicate the name space, it would be immensely useful!




Discussion:
Given the various forms the `--ssh` option can take on the command line, I'm thinking if this is best reflected with multiple fields or a single more generic/less typed field.

```python
# One generic field
docker_image(
  # Still be a list, as --ssh may be provided multiple times..
  ssh=["default", "<id>=<socket>", "<key>", ...],
)

# Dedicated fields, for stricter format
docker_image(
  ssh_default=True,
  ssh_sockets={
    "<id>": "<socket>",
    ...
  },
  ssh_keys=["<key>", ...],
)
```

cc @Eric-Arellano wdyt?

Thanks for opening @vputz! @kaos , given how niche this feature is, I'd bias towards encapsulating it to a single field.

But taking a step back, why can't you use `extra_build_args` here to set `--ssh`? 
Because the `extra_build_args=["ARG=VALUE"]` translates to `--build-arg ARG=VALUE` ... which is perhaps a bit unfortunate, in terms of naming...
I can see the benefit with a single field for `ssh`. +1 for that.

Resolving patch:
diff --git a/src/python/pants/backend/docker/target_types.py b/src/python/pants/backend/docker/target_types.py
--- a/src/python/pants/backend/docker/target_types.py
+++ b/src/python/pants/backend/docker/target_types.py
@@ -8,7 +8,7 @@
 import shlex
 from abc import ABC, abstractmethod
 from textwrap import dedent
-from typing import ClassVar, Iterator
+from typing import ClassVar, Iterator, cast
 
 from typing_extensions import final
 
@@ -206,6 +206,25 @@ def option_values(self) -> Iterator[str]:
             yield f"id={secret},src={os.path.normpath(full_path)}"
 
 
+class DockerBuildSSHOptionField(DockerBuildOptionFieldMixin, StringSequenceField):
+    alias = "ssh"
+    default = ()
+    help = (
+        "SSH agent socket or keys to expose to the build (only if BuildKit enabled) "
+        "(format: default|<id>[=<socket>|<key>[,<key>]])\n\n"
+        "The exposed agent and/or keys can then be used in your `Dockerfile` by mounting them in "
+        "your `RUN` instructions:\n\n"
+        "    RUN --mount=type=ssh ...\n\n"
+        "See [Docker documentation](https://docs.docker.com/develop/develop-images"
+        "/build_enhancements/#using-ssh-to-access-private-data-in-builds) for more information."
+    )
+
+    docker_build_option = "--ssh"
+
+    def option_values(self) -> Iterator[str]:
+        yield from cast("tuple[str]", self.value)
+
+
 class DockerImageTarget(Target):
     alias = "docker_image"
     core_fields = (
@@ -218,6 +237,7 @@ class DockerImageTarget(Target):
         DockerRegistriesField,
         DockerRepositoryField,
         DockerBuildSecretsOptionField,
+        DockerBuildSSHOptionField,
         DockerSkipPushField,
         RestartableField,
     )


Test patch:
diff --git a/src/python/pants/backend/docker/goals/package_image_test.py b/src/python/pants/backend/docker/goals/package_image_test.py
--- a/src/python/pants/backend/docker/goals/package_image_test.py
+++ b/src/python/pants/backend/docker/goals/package_image_test.py
@@ -604,6 +604,39 @@ def check_docker_proc(process: Process):
     )
 
 
+def test_docker_build_ssh_option(rule_runner: RuleRunner) -> None:
+    rule_runner.write_files(
+        {
+            "docker/test/BUILD": dedent(
+                """\
+                docker_image(
+                  name="img1",
+                  ssh=["default"],
+                )
+                """
+            ),
+        }
+    )
+
+    def check_docker_proc(process: Process):
+        assert process.argv == (
+            "/dummy/docker",
+            "build",
+            "--ssh=default",
+            "-t",
+            "img1:latest",
+            "-f",
+            "docker/test/Dockerfile",
+            ".",
+        )
+
+    assert_build(
+        rule_runner,
+        Address("docker/test", target_name="img1"),
+        process_assertions=check_docker_proc,
+    )
+
+
 @pytest.mark.parametrize(
     "copy_sources, build_context_files, expect_logged, fail_log_contains",
     [