mirror of
https://github.com/drone-plugins/drone-docker.git
synced 2026-07-16 16:21:18 +08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e44c053dcc | |||
| 48f8235649 |
@@ -123,8 +123,8 @@ store**, so base-image pulls fail with
|
|||||||
`x509: certificate signed by unknown authority` unless that CA is trusted.
|
`x509: certificate signed by unknown authority` unless that CA is trusted.
|
||||||
|
|
||||||
Set the `HARNESS_CA_PATH` environment variable to a PEM CA (bundle) file and the
|
Set the `HARNESS_CA_PATH` environment variable to a PEM CA (bundle) file and the
|
||||||
plugin installs it into the host trust store **before starting the Docker
|
plugin installs it into the host trust store **before any HTTPS** (registry
|
||||||
daemon**:
|
wrapper auth such as GCP STS, and before starting the Docker daemon):
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
@@ -146,18 +146,22 @@ Behavior:
|
|||||||
`update-ca-certificates` / `update-ca-trust`. It also appends the CA directly
|
`update-ca-certificates` / `update-ca-trust`. It also appends the CA directly
|
||||||
to the consolidated bundle (`/etc/ssl/certs/ca-certificates.crt` or the RHEL
|
to the consolidated bundle (`/etc/ssl/certs/ca-certificates.crt` or the RHEL
|
||||||
equivalent) so trust holds even on minimal images without a refresh tool.
|
equivalent) so trust holds even on minimal images without a refresh tool.
|
||||||
- **Windows** — imports the CA into the machine trust store via
|
- **Windows** — imports the CA into `LocalMachine\Root` via the Windows
|
||||||
`certutil -addstore -f Root`.
|
CryptoAPI (`crypt32.dll`). This works on Nano Server images that lack
|
||||||
|
`certutil` / PowerShell.
|
||||||
- **macOS / other** — logged no-op (not currently supported).
|
- **macOS / other** — logged no-op (not currently supported).
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
|
|
||||||
|
- Registry wrappers (`plugins/gcr`, `gar`, `acr`, `ecr`) call the same install
|
||||||
|
before their pre-docker HTTPS auth (e.g. `sts.googleapis.com` token exchange).
|
||||||
- It is **best-effort and idempotent**: when `HARNESS_CA_PATH` is unset, the file
|
- It is **best-effort and idempotent**: when `HARNESS_CA_PATH` is unset, the file
|
||||||
is missing, or the file is empty, the plugin logs and continues. Re-running the
|
is missing, or the file is empty, the plugin logs and continues. Re-running the
|
||||||
step will not duplicate the CA in the bundle.
|
step will not duplicate the CA in the bundle.
|
||||||
- The CA is trusted for the daemon's own registry TLS (base-image pulls, cache
|
- The CA is trusted for the daemon's own registry TLS (base-image pulls, cache
|
||||||
endpoints). To make the CA available to `RUN` steps inside the build, also pass
|
endpoints) and for Go TLS clients in the plugin process. To make the CA
|
||||||
it as needed via the Dockerfile / build args.
|
available to `RUN` steps inside the build, also pass it as needed via the
|
||||||
|
Dockerfile / build args.
|
||||||
- This is separate from the proxy build args
|
- This is separate from the proxy build args
|
||||||
(`http_proxy`/`https_proxy`/`no_proxy`), which the plugin already injects from
|
(`http_proxy`/`https_proxy`/`no_proxy`), which the plugin already injects from
|
||||||
the environment (including `HARNESS_`-prefixed variants).
|
the environment (including `HARNESS_`-prefixed variants).
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// decodePEMCertificates extracts DER-encoded CERTIFICATE blocks from a PEM
|
||||||
|
// (or PEM bundle). Non-certificate blocks are skipped. Returns an error when
|
||||||
|
// no certificate blocks are present.
|
||||||
|
func decodePEMCertificates(pemBytes []byte) ([][]byte, error) {
|
||||||
|
var ders [][]byte
|
||||||
|
rest := pemBytes
|
||||||
|
for {
|
||||||
|
var block *pem.Block
|
||||||
|
block, rest = pem.Decode(rest)
|
||||||
|
if block == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if block.Type != "CERTIFICATE" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ders = append(ders, block.Bytes)
|
||||||
|
}
|
||||||
|
if len(ders) == 0 {
|
||||||
|
return nil, fmt.Errorf("no CERTIFICATE PEM blocks found")
|
||||||
|
}
|
||||||
|
return ders, nil
|
||||||
|
}
|
||||||
+114
@@ -0,0 +1,114 @@
|
|||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Real self-signed CAs generated for these tests (CN=HarnessTestCA / HarnessTestCA2).
|
||||||
|
const harnessTestCAPEM = `-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDETCCAfmgAwIBAgIUUnMVT6JwuvsR8lPanhZxYJnqUPYwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwGDEWMBQGA1UEAwwNSGFybmVzc1Rlc3RDQTAeFw0yNjA3MTUwNzMxMDBaFw0z
|
||||||
|
NjA3MTIwNzMxMDBaMBgxFjAUBgNVBAMMDUhhcm5lc3NUZXN0Q0EwggEiMA0GCSqG
|
||||||
|
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDJOssgNxmGP8itq7IxObLY2igTK3/YeMIJ
|
||||||
|
9gvjWzUGe/RRrWqWJvfplKScNaIbyGaABYbDaGURnL3uGr5QQWEUbqvOMlEYaoNq
|
||||||
|
21TmVbEl6Sponc+4aFfDPP9CFAOn2tqyrZaU3wZIjSUWbGo2wwseWULzaQxZ8lZ6
|
||||||
|
a+kgjipymolfTZeHbJQAk/hwXVhbrz6xBbOhue/K/tFJxFBuawvwgXEi+2ywzXgy
|
||||||
|
fn80wYWdjeFEpFAtNKyjg+bu36DtHikwDS9XqZzgSZnGhDKEGh3cj0hd3/nhr59h
|
||||||
|
FCDm56XsBvWQVHmNmiuuxCn3svzSa9qdyk6tcIqZN8wKz1J7lFRfAgMBAAGjUzBR
|
||||||
|
MB0GA1UdDgQWBBSrLR7fG/bwSozrDipyfT5MtiuYjTAfBgNVHSMEGDAWgBSrLR7f
|
||||||
|
G/bwSozrDipyfT5MtiuYjTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
|
||||||
|
A4IBAQAq9+CxKqe2vhBTz+5i9NBpxzxFy3fZTKcakuB98hafnNw29D4VggjV7sgo
|
||||||
|
99En87gkTIPsiOsRa0YH3wl5aa5EUp/Dx0ZfXasjzS5BFSZKZDYcuLuuUEeK9N6j
|
||||||
|
UBxaWDxNRjENevkX8SWpbxyNkcDGZM2IfJvVA/g7h8ERZLjlu9iOTqb6jt3PDe4q
|
||||||
|
CNoMbU5L3md3gk8mfoEZAX/IIUmbw7hAy7zE9dEfbKHeXl3FrcXPL0WmLePcK9BF
|
||||||
|
8XEpsOUSoq4sMPdVxaPF7l+SKDbdsR4gKtwlEMLefiiLqk0k4HhygjzzRgdsMOc3
|
||||||
|
S8dL7zv0BrAFy//XsfmsVeVFVhRN
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
`
|
||||||
|
|
||||||
|
const harnessTestCA2PEM = `-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDEzCCAfugAwIBAgIUR5AfE7MTe0ucAk1JzlqvOJd3Z7AwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwGTEXMBUGA1UEAwwOSGFybmVzc1Rlc3RDQTIwHhcNMjYwNzE1MDczMTAwWhcN
|
||||||
|
MzYwNzEyMDczMTAwWjAZMRcwFQYDVQQDDA5IYXJuZXNzVGVzdENBMjCCASIwDQYJ
|
||||||
|
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAI2EDM2Ac+jwhP8OaWFFb+DlVwzKSkEI
|
||||||
|
LoaZ41RQGHZaCckTLtaCtIBaNQHLWqj8q6I0fIz+8mmaXuxRKDjnhGl3AFIJ2wql
|
||||||
|
Hbyywo2MQcu+6wfs2Ewd887NDb5wrNJs/gl1GYRilSAd8n53T5IJUHHA0IGxtq4g
|
||||||
|
9XwS6OTmfBdQ3ycEsi9Yexd5Oz79sLMDhBnt21nYWrEO8Kumgyfd7gfQUBcGc1nH
|
||||||
|
NhoBrZffLmo+cljGqPNEFyAlWEnsmiqZnYh/EOUXGdXxsZMvyMWt02kLe0BlGmJr
|
||||||
|
PrI8iJCMElj86UhKK2oIjWLMA6cviYSkw9jyiihhodSUChhmqOABoe8CAwEAAaNT
|
||||||
|
MFEwHQYDVR0OBBYEFKK9RP95NSP4xmxN+hojPXlxVVatMB8GA1UdIwQYMBaAFKK9
|
||||||
|
RP95NSP4xmxN+hojPXlxVVatMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
|
||||||
|
BQADggEBABlrORwXmhmO+eIdTHZMuad72JMEJeHxiRCGbwH6vAVb03YZAXLmIf31
|
||||||
|
sBKkUCDV+Vm0MaTYmYncK3eZZBn6teEWcx7+L9XlF7ckVKBqj9xkwFUdfVipCimD
|
||||||
|
cjBl/H+2PEY5Hzoy/uf2/sE1czrfHA2HeEIEacVyht9t0HTtqXQqrn44ijESkNtb
|
||||||
|
SqqaVYCcceS85ID2oJYYKQycMnFJd0VwSHlD+Lu4ZubXbrg+pzSqw6olTOwCE7Q2
|
||||||
|
LhLxQRB4f8Xw807aIuFK2mKbPs+4dmPkhClM3rbFzzldQJ4lKLnvZHcFtXJUXg2a
|
||||||
|
UzwP8JQn8xuz7gIZxMO3vGqTweP48yc=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
`
|
||||||
|
|
||||||
|
func TestDecodePEMCertificates_Single(t *testing.T) {
|
||||||
|
ders, err := decodePEMCertificates([]byte(harnessTestCAPEM))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("decodePEMCertificates: %v", err)
|
||||||
|
}
|
||||||
|
if len(ders) != 1 {
|
||||||
|
t.Fatalf("got %d certs, want 1", len(ders))
|
||||||
|
}
|
||||||
|
cert, err := x509.ParseCertificate(ders[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ParseCertificate: %v", err)
|
||||||
|
}
|
||||||
|
if cert.Subject.CommonName != "HarnessTestCA" {
|
||||||
|
t.Fatalf("CN = %q, want HarnessTestCA", cert.Subject.CommonName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodePEMCertificates_Bundle(t *testing.T) {
|
||||||
|
bundle := harnessTestCAPEM + "\n" + harnessTestCA2PEM
|
||||||
|
ders, err := decodePEMCertificates([]byte(bundle))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("decodePEMCertificates: %v", err)
|
||||||
|
}
|
||||||
|
if len(ders) != 2 {
|
||||||
|
t.Fatalf("got %d certs, want 2", len(ders))
|
||||||
|
}
|
||||||
|
for i, der := range ders {
|
||||||
|
if _, err := x509.ParseCertificate(der); err != nil {
|
||||||
|
t.Fatalf("cert %d: ParseCertificate: %v", i, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodePEMCertificates_SkipsNonCertificateBlocks(t *testing.T) {
|
||||||
|
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: []byte("not-a-key")})
|
||||||
|
mixed := append(append([]byte{}, keyPEM...), []byte(harnessTestCAPEM)...)
|
||||||
|
ders, err := decodePEMCertificates(mixed)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("decodePEMCertificates: %v", err)
|
||||||
|
}
|
||||||
|
if len(ders) != 1 {
|
||||||
|
t.Fatalf("got %d certs, want 1 (private key block skipped)", len(ders))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodePEMCertificates_Empty(t *testing.T) {
|
||||||
|
_, err := decodePEMCertificates([]byte("not a pem\n"))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error for input with no CERTIFICATE blocks")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodePEMCertificates_MatchesOpenSSLDER(t *testing.T) {
|
||||||
|
ders, err := decodePEMCertificates([]byte(harnessTestCAPEM))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
origBlock, _ := pem.Decode([]byte(harnessTestCAPEM))
|
||||||
|
if !bytes.Equal(origBlock.Bytes, ders[0]) {
|
||||||
|
t.Fatal("decoded DER does not match original PEM payload")
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-4
@@ -31,7 +31,7 @@ func TestTrustHarnessCA_Unset(t *testing.T) {
|
|||||||
os.Unsetenv("HARNESS_CA_PATH")
|
os.Unsetenv("HARNESS_CA_PATH")
|
||||||
_, calls := withStubbedInstall(t)
|
_, calls := withStubbedInstall(t)
|
||||||
|
|
||||||
trustHarnessCA()
|
TrustHarnessCA()
|
||||||
|
|
||||||
if *calls != 0 {
|
if *calls != 0 {
|
||||||
t.Fatalf("expected installer not to be called when HARNESS_CA_PATH is unset, got %d calls", *calls)
|
t.Fatalf("expected installer not to be called when HARNESS_CA_PATH is unset, got %d calls", *calls)
|
||||||
@@ -44,7 +44,7 @@ func TestTrustHarnessCA_MissingFile(t *testing.T) {
|
|||||||
defer os.Unsetenv("HARNESS_CA_PATH")
|
defer os.Unsetenv("HARNESS_CA_PATH")
|
||||||
_, calls := withStubbedInstall(t)
|
_, calls := withStubbedInstall(t)
|
||||||
|
|
||||||
trustHarnessCA()
|
TrustHarnessCA()
|
||||||
|
|
||||||
if *calls != 0 {
|
if *calls != 0 {
|
||||||
t.Fatalf("expected installer not to be called when CA file is missing, got %d calls", *calls)
|
t.Fatalf("expected installer not to be called when CA file is missing, got %d calls", *calls)
|
||||||
@@ -60,7 +60,7 @@ func TestTrustHarnessCA_EmptyFile(t *testing.T) {
|
|||||||
defer os.Unsetenv("HARNESS_CA_PATH")
|
defer os.Unsetenv("HARNESS_CA_PATH")
|
||||||
_, calls := withStubbedInstall(t)
|
_, calls := withStubbedInstall(t)
|
||||||
|
|
||||||
trustHarnessCA()
|
TrustHarnessCA()
|
||||||
|
|
||||||
if *calls != 0 {
|
if *calls != 0 {
|
||||||
t.Fatalf("expected installer not to be called for whitespace-only CA, got %d calls", *calls)
|
t.Fatalf("expected installer not to be called for whitespace-only CA, got %d calls", *calls)
|
||||||
@@ -76,7 +76,7 @@ func TestTrustHarnessCA_ValidFile(t *testing.T) {
|
|||||||
defer os.Unsetenv("HARNESS_CA_PATH")
|
defer os.Unsetenv("HARNESS_CA_PATH")
|
||||||
captured, calls := withStubbedInstall(t)
|
captured, calls := withStubbedInstall(t)
|
||||||
|
|
||||||
trustHarnessCA()
|
TrustHarnessCA()
|
||||||
|
|
||||||
if *calls != 1 {
|
if *calls != 1 {
|
||||||
t.Fatalf("expected installer to be called once for a valid CA, got %d calls", *calls)
|
t.Fatalf("expected installer to be called once for a valid CA, got %d calls", *calls)
|
||||||
|
|||||||
+59
-27
@@ -5,8 +5,9 @@ package docker
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"unsafe"
|
||||||
"os/exec"
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
// installHarnessCA imports the Harness egress-proxy CA into the Windows
|
// installHarnessCA imports the Harness egress-proxy CA into the Windows
|
||||||
@@ -14,38 +15,69 @@ import (
|
|||||||
// Docker daemon and Go's crypto/x509 consult). Best-effort: failures are logged,
|
// Docker daemon and Go's crypto/x509 consult). Best-effort: failures are logged,
|
||||||
// never fatal.
|
// never fatal.
|
||||||
//
|
//
|
||||||
// certutil needs a file path, so the (already-validated) CA bytes are written to
|
// Uses CryptoAPI (crypt32.dll) directly so it works on Nano Server images that
|
||||||
// a temp .crt first, then added with `certutil -addstore -f Root <file>`.
|
// lack certutil and PowerShell. CERT_STORE_ADD_REPLACE_EXISTING keeps the step
|
||||||
|
// idempotent across retries.
|
||||||
func installHarnessCA(caPEM []byte) {
|
func installHarnessCA(caPEM []byte) {
|
||||||
tmp, err := os.CreateTemp("", "harness-egress-ca-*.crt")
|
ders, err := decodePEMCertificates(caPEM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Could not create temp file for Harness CA: %s\n", err)
|
fmt.Printf("Could not decode Harness CA PEM: %s\n", err)
|
||||||
return
|
|
||||||
}
|
|
||||||
tmpPath := tmp.Name()
|
|
||||||
defer os.Remove(tmpPath)
|
|
||||||
|
|
||||||
if _, err := tmp.Write(caPEM); err != nil {
|
|
||||||
tmp.Close()
|
|
||||||
fmt.Printf("Could not write temp Harness CA: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tmp.Close()
|
|
||||||
|
|
||||||
if _, err := exec.LookPath("certutil"); err != nil {
|
|
||||||
fmt.Printf("certutil not found on PATH; cannot install Harness egress CA: %s\n", err)
|
|
||||||
fmt.Println("Base-image pulls through the egress proxy may fail with x509 errors.")
|
fmt.Println("Base-image pulls through the egress proxy may fail with x509 errors.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// -f overwrites an existing entry, keeping the step idempotent across retries.
|
storeName, err := windows.UTF16PtrFromString("ROOT")
|
||||||
cmd := exec.Command("certutil", "-addstore", "-f", "Root", tmpPath)
|
if err != nil {
|
||||||
cmd.Stdout = os.Stdout
|
fmt.Printf("Could not prepare Windows root store name: %s\n", err)
|
||||||
cmd.Stderr = os.Stderr
|
return
|
||||||
if err := cmd.Run(); err != nil {
|
}
|
||||||
fmt.Printf("Warning: certutil -addstore Root failed: %s\n", err)
|
|
||||||
|
store, err := windows.CertOpenStore(
|
||||||
|
windows.CERT_STORE_PROV_SYSTEM,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
windows.CERT_SYSTEM_STORE_LOCAL_MACHINE,
|
||||||
|
uintptr(unsafe.Pointer(storeName)),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Could not open LocalMachine\\Root store: %s\n", err)
|
||||||
fmt.Println("Base-image pulls through the egress proxy may fail with x509 errors.")
|
fmt.Println("Base-image pulls through the egress proxy may fail with x509 errors.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Println("Installed Harness egress CA into the Windows LocalMachine\\Root store.")
|
defer windows.CertCloseStore(store, 0)
|
||||||
|
|
||||||
|
for i, der := range ders {
|
||||||
|
if err := addCertToStore(store, der); err != nil {
|
||||||
|
fmt.Printf("Warning: failed to add Harness CA cert %d to LocalMachine\\Root: %s\n", i+1, err)
|
||||||
|
fmt.Println("Base-image pulls through the egress proxy may fail with x509 errors.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Installed %d Harness egress CA certificate(s) into the Windows LocalMachine\\Root store.\n", len(ders))
|
||||||
|
}
|
||||||
|
|
||||||
|
func addCertToStore(store windows.Handle, der []byte) error {
|
||||||
|
if len(der) == 0 {
|
||||||
|
return fmt.Errorf("empty DER certificate")
|
||||||
|
}
|
||||||
|
ctx, err := windows.CertCreateCertificateContext(
|
||||||
|
windows.X509_ASN_ENCODING|windows.PKCS_7_ASN_ENCODING,
|
||||||
|
&der[0],
|
||||||
|
uint32(len(der)),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("CertCreateCertificateContext: %w", err)
|
||||||
|
}
|
||||||
|
defer windows.CertFreeCertificateContext(ctx)
|
||||||
|
|
||||||
|
if err := windows.CertAddCertificateContextToStore(
|
||||||
|
store,
|
||||||
|
ctx,
|
||||||
|
windows.CERT_STORE_ADD_REPLACE_EXISTING,
|
||||||
|
nil,
|
||||||
|
); err != nil {
|
||||||
|
return fmt.Errorf("CertAddCertificateContextToStore: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ func main() {
|
|||||||
godotenv.Load(env)
|
godotenv.Load(env)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Must run before Azure / ACR HTTPS auth: this binary does auth before
|
||||||
|
// spawning drone-docker, which is where TrustHarnessCA also runs for the daemon.
|
||||||
|
docker.TrustHarnessCA()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
repo = getenv("PLUGIN_REPO")
|
repo = getenv("PLUGIN_REPO")
|
||||||
registry = getenv("PLUGIN_REGISTRY")
|
registry = getenv("PLUGIN_REGISTRY")
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ func main() {
|
|||||||
godotenv.Load(env)
|
godotenv.Load(env)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Install egress CA before any TLS in this process (including the Docker
|
||||||
|
// daemon started from Plugin.Exec).
|
||||||
|
docker.TrustHarnessCA()
|
||||||
|
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
app.Name = "docker plugin"
|
app.Name = "docker plugin"
|
||||||
app.Usage = "docker plugin"
|
app.Usage = "docker plugin"
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ func main() {
|
|||||||
godotenv.Load(env)
|
godotenv.Load(env)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Must run before AWS / ECR HTTPS auth: this binary does auth before
|
||||||
|
// spawning drone-docker, which is where TrustHarnessCA also runs for the daemon.
|
||||||
|
docker.TrustHarnessCA()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
repo = getenv("PLUGIN_REPO")
|
repo = getenv("PLUGIN_REPO")
|
||||||
registry = getenv("PLUGIN_REGISTRY")
|
registry = getenv("PLUGIN_REGISTRY")
|
||||||
|
|||||||
@@ -49,6 +49,10 @@ func loadConfig() Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Must run before STS / oauth HTTPS: this binary does auth before spawning
|
||||||
|
// drone-docker, which is where TrustHarnessCA also runs for the daemon.
|
||||||
|
docker.TrustHarnessCA()
|
||||||
|
|
||||||
idToken := getenv("PLUGIN_OIDC_TOKEN_ID")
|
idToken := getenv("PLUGIN_OIDC_TOKEN_ID")
|
||||||
projectId := getenv("PLUGIN_PROJECT_NUMBER")
|
projectId := getenv("PLUGIN_PROJECT_NUMBER")
|
||||||
poolId := getenv("PLUGIN_POOL_ID")
|
poolId := getenv("PLUGIN_POOL_ID")
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ func loadConfig() Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Must run before STS / oauth HTTPS: this binary does auth before spawning
|
||||||
|
// drone-docker, which is where TrustHarnessCA also runs for the daemon.
|
||||||
|
docker.TrustHarnessCA()
|
||||||
|
|
||||||
idToken := getenv("PLUGIN_OIDC_TOKEN_ID")
|
idToken := getenv("PLUGIN_OIDC_TOKEN_ID")
|
||||||
projectId := getenv("PLUGIN_PROJECT_NUMBER")
|
projectId := getenv("PLUGIN_PROJECT_NUMBER")
|
||||||
poolId := getenv("PLUGIN_POOL_ID")
|
poolId := getenv("PLUGIN_POOL_ID")
|
||||||
|
|||||||
@@ -132,14 +132,6 @@ type (
|
|||||||
|
|
||||||
// Exec executes the plugin step
|
// Exec executes the plugin step
|
||||||
func (p Plugin) Exec() error {
|
func (p Plugin) Exec() error {
|
||||||
// Trust the Harness egress-proxy CA (if HARNESS_CA_PATH is set) BEFORE the
|
|
||||||
// daemon starts. The Docker daemon and its embedded BuildKit verify registry
|
|
||||||
// TLS against the host system trust store; when traffic goes through the
|
|
||||||
// TLS-intercepting egress proxy, the presented cert is signed by the Harness
|
|
||||||
// CA and must be trusted here or base-image pulls fail with an x509
|
|
||||||
// "unknown authority" error.
|
|
||||||
trustHarnessCA()
|
|
||||||
|
|
||||||
// start the Docker daemon server
|
// start the Docker daemon server
|
||||||
if !p.Daemon.Disabled {
|
if !p.Daemon.Disabled {
|
||||||
p.startDaemon()
|
p.startDaemon()
|
||||||
@@ -583,17 +575,23 @@ func getSecretCmdArg(kvp string, file bool) (string, error) {
|
|||||||
return fmt.Sprintf("id=%s,env=%s", key, value), nil
|
return fmt.Sprintf("id=%s,env=%s", key, value), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// trustHarnessCA installs the Harness egress-proxy CA into the host trust store
|
// TrustHarnessCA installs the Harness egress-proxy CA into the host trust store
|
||||||
// so the Docker daemon / embedded BuildKit trust the TLS-intercepting proxy when
|
// so the Docker daemon / embedded BuildKit (and this process's Go TLS clients)
|
||||||
// pulling base images. It is driven by HARNESS_CA_PATH (path to a PEM CA bundle)
|
// trust the TLS-intercepting proxy. It is driven by HARNESS_CA_PATH (path to a
|
||||||
// and is a best-effort no-op when the var is unset, the file is missing, or the
|
// PEM CA bundle) and is a best-effort no-op when the var is unset, the file is
|
||||||
// platform isn't supported — build failures are surfaced later by docker itself.
|
// missing, or the platform isn't supported — build failures are surfaced later
|
||||||
|
// by docker itself.
|
||||||
|
//
|
||||||
|
// Call from each binary's main (after loading PLUGIN_ENV_FILE) before any HTTPS:
|
||||||
|
// Go caches SystemCertPool on first use, and registry wrappers (gcr/gar/acr/ecr)
|
||||||
|
// perform auth HTTPS before spawning drone-docker.
|
||||||
|
//
|
||||||
// The platform-specific installation is provided by installHarnessCA (see
|
// The platform-specific installation is provided by installHarnessCA (see
|
||||||
// ca_linux.go / ca_windows.go / ca_other.go); it is indirected through
|
// ca_linux.go / ca_windows.go / ca_other.go); it is indirected through
|
||||||
// installHarnessCAFn so tests can stub the actual system-trust write.
|
// installHarnessCAFn so tests can stub the actual system-trust write.
|
||||||
var installHarnessCAFn = installHarnessCA
|
var installHarnessCAFn = installHarnessCA
|
||||||
|
|
||||||
func trustHarnessCA() {
|
func TrustHarnessCA() {
|
||||||
caPath := os.Getenv("HARNESS_CA_PATH")
|
caPath := os.Getenv("HARNESS_CA_PATH")
|
||||||
if caPath == "" {
|
if caPath == "" {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ require (
|
|||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/urfave/cli v1.22.2
|
github.com/urfave/cli v1.22.2
|
||||||
golang.org/x/oauth2 v0.34.0
|
golang.org/x/oauth2 v0.34.0
|
||||||
|
golang.org/x/sys v0.39.0
|
||||||
google.golang.org/api v0.187.0
|
google.golang.org/api v0.187.0
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -61,7 +62,6 @@ require (
|
|||||||
go.opentelemetry.io/otel/trace v1.39.0 // indirect
|
go.opentelemetry.io/otel/trace v1.39.0 // indirect
|
||||||
golang.org/x/crypto v0.46.0 // indirect
|
golang.org/x/crypto v0.46.0 // indirect
|
||||||
golang.org/x/net v0.48.0 // indirect
|
golang.org/x/net v0.48.0 // indirect
|
||||||
golang.org/x/sys v0.39.0 // indirect
|
|
||||||
golang.org/x/text v0.32.0 // indirect
|
golang.org/x/text v0.32.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
||||||
google.golang.org/grpc v1.79.3 // indirect
|
google.golang.org/grpc v1.79.3 // indirect
|
||||||
|
|||||||
Reference in New Issue
Block a user