Skip to content

Comments

Fix 22203#200

Open
wy65701436 wants to merge 25 commits intomainfrom
fix-22203
Open

Fix 22203#200
wy65701436 wants to merge 25 commits intomainfrom
fix-22203

Conversation

@wy65701436
Copy link
Owner

Thank you for contributing to Harbor!

Comprehensive Summary of your change

Issue being fixed

Fixes #(issue)

Please indicate you've done the following:

  • Well Written Title and Summary of the PR
  • Label the PR as needed. "release-note/ignore-for-release, release-note/new-feature, release-note/update, release-note/enhancement, release-note/community, release-note/breaking-change, release-note/docs, release-note/infra, release-note/deprecation"
  • Accepted the DCO. Commits without the DCO will delay acceptance.
  • Made sure tests are passing and test coverage is added if needed.
  • Considered the docs impact and opened a new docs issue or PR with docs changes if needed in website repository.

@wy65701436 wy65701436 requested a review from Copilot November 10, 2025 11:11
return NewTransport(
WithCustomCACert(cfg.CACertificate),
)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: CA Cert Silently Overrides Insecure Flag

When a custom CA certificate is provided, GetHTTPTransport creates a new transport with only the custom CA configured, ignoring the Insecure flag. This means if both WithInsecure(true) and WithCACert(cert) are passed, the insecure setting is silently dropped, potentially causing TLS verification to fail when the user expects it to be skipped.

Fix in Cursor Fix in Web

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds CA certificate support for registry endpoints in Harbor, allowing users to provide custom PEM-encoded CA certificates to verify TLS connections to registries using self-signed or private CA certificates.

  • Added CACertificate field to registry models throughout the codebase (API, database, UI)
  • Implemented CA certificate validation and custom transport configuration
  • Updated all registry adapter implementations to support custom CA certificates

Reviewed Changes

Copilot reviewed 44 out of 44 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/common/http/transport.go Added CA certificate validation and custom transport configuration with WithCustomCACert and ValidateCACertificate functions
src/common/http/transport_test.go Added comprehensive tests for CA certificate validation and transport configuration
src/pkg/registry/client.go Added NewClientWithCACert function and updated NewClientWithAuthorizer to support CA certificates
src/pkg/registry/auth/authorizer.go Updated NewAuthorizer to accept optional CA certificate parameter
src/pkg/reg/model/registry.go Added CACertificate field to Registry model
src/pkg/reg/dao/model.go Added ca_certificate column to database model
src/pkg/reg/manager.go Updated model conversion functions to include CA certificate field
src/pkg/reg/util/util.go Updated GetHTTPTransport and Ping to support CA certificates
src/server/v2.0/handler/registry.go Added CA certificate validation in CreateRegistry, UpdateRegistry, and PingRegistry endpoints
src/server/v2.0/handler/replication.go Updated convertRegistry to include CA certificate in response
src/portal/src/app/shared/services/interface.ts Added ca_certificate field to Endpoint and PingEndpoint interfaces
src/portal/src/app/base/left-side-nav/registries/create-edit-endpoint/create-edit-endpoint.component.ts Added CA certificate to payload in ping operation
src/portal/src/app/base/left-side-nav/registries/create-edit-endpoint/create-edit-endpoint.component.html Added CA certificate textarea field with tooltip, disabled when insecure mode is enabled
src/portal/src/i18n/lang/*.json Added translations for CA certificate labels, tooltips, and helper text in 10 languages
make/migrations/postgresql/0180_2.15.0_schema.up.sql Added database migration to add ca_certificate column to registry table
api/v2.0/swagger.yaml Updated API schema to include ca_certificate field in registry-related definitions
src/pkg/reg/adapter/* Updated all registry adapter implementations (AWS ECR, Azure ACR, AliACR, GitHub CR, GitLab, Harbor, Huawei, JFrog, Quay, Tencent CR, Volcengine CR, Docker Hub, DTR) to pass CA certificate when creating HTTP transports
src/controller/proxy/local.go Updated local proxy helper to pass empty CA certificate string to maintain backward compatibility

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 131 to 132
subjects := httpTransport.TLSClientConfig.RootCAs.Subjects()
assert.Empty(t, subjects, "RootCAs should be empty for invalid cert")
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as above: RootCAs.Subjects() is deprecated as of Go 1.18. Consider using an alternative method to verify the certificate pool is empty.

Copilot uses AI. Check for mistakes.
Comment on lines 142 to 148
// Verify signature for self-signed certificates (root CAs)
//if cert.Subject.String() == cert.Issuer.String() {
// if err := cert.CheckSignature(cert.SignatureAlgorithm, cert.RawTBSCertificate, cert.Signature); err != nil {
// log.Errorf("CA certificate signature verification failed: %v", err)
// continue
// }
//}
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commented-out code for certificate signature verification should either be removed or uncommented with a clear explanation. Keeping commented code in production can lead to confusion about intended behavior and creates maintenance debt.

Suggested change
// Verify signature for self-signed certificates (root CAs)
//if cert.Subject.String() == cert.Issuer.String() {
// if err := cert.CheckSignature(cert.SignatureAlgorithm, cert.RawTBSCertificate, cert.Signature); err != nil {
// log.Errorf("CA certificate signature verification failed: %v", err)
// continue
// }
//}
// Verify signature for self-signed certificates (root CAs).
// This ensures that only valid root CA certificates are trusted.
if cert.Subject.String() == cert.Issuer.String() {
if err := cert.CheckSignature(cert.SignatureAlgorithm, cert.RawTBSCertificate, cert.Signature); err != nil {
log.Errorf("CA certificate signature verification failed: %v", err)
continue
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +214 to +218
if cfg.CACertificate != "" {
return NewTransport(
WithCustomCACert(cfg.CACertificate),
)
}
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a CA certificate is provided in GetHTTPTransport, the insecure flag is not applied to the transport. This means that if both WithInsecure(true) and WithCACert(caCert) are passed, InsecureSkipVerify will not be set on the TLSClientConfig, which may not match user expectations. Consider either:

  1. Setting InsecureSkipVerify when insecure=true even with a custom CA, or
  2. Documenting this behavior clearly in the function comment

Currently, the priority documentation mentions CA cert takes precedence, but doesn't explicitly state that the insecure flag is ignored when a CA cert is provided.

Copilot uses AI. Check for mistakes.
Comment on lines 103 to 110
if !strings.HasPrefix(caCert, "-----BEGIN CERTIFICATE-----") {
return errors.New("invalid CA certificate: must start with -----BEGIN CERTIFICATE-----")
}

if !strings.HasSuffix(caCert, "-----END CERTIFICATE-----") {
return errors.New("invalid CA certificate: must end with -----END CERTIFICATE-----")
}

Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validation only checks for the presence of "-----BEGIN CERTIFICATE-----" and "-----END CERTIFICATE-----" markers, but doesn't validate that they appear in the correct order or that there's actual content between them. A certificate like "-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----" would pass these checks but fail later. Consider using pem.Decode to check proper formatting before attempting to parse.

Suggested change
if !strings.HasPrefix(caCert, "-----BEGIN CERTIFICATE-----") {
return errors.New("invalid CA certificate: must start with -----BEGIN CERTIFICATE-----")
}
if !strings.HasSuffix(caCert, "-----END CERTIFICATE-----") {
return errors.New("invalid CA certificate: must end with -----END CERTIFICATE-----")
}
block, _ := pem.Decode([]byte(caCert))
if block == nil || block.Type != "CERTIFICATE" || len(block.Bytes) == 0 {
return errors.New("invalid CA certificate: not a valid PEM encoded CERTIFICATE block")
}

Copilot uses AI. Check for mistakes.
Comment on lines +37 to +41
if len(caCert) > 0 && caCert[0] != "" {
transport = commonhttp.GetHTTPTransport(
commonhttp.WithInsecure(insecure),
commonhttp.WithCACert(caCert[0]),
)
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When insecure=true and a CA certificate is provided together in NewAuthorizer, the transport will have a custom CA but won't have InsecureSkipVerify set (due to the priority in GetHTTPTransport). This could lead to unexpected behavior where certificate validation still occurs despite the insecure flag being true. Consider handling this case explicitly or documenting the interaction between these two parameters.

Suggested change
if len(caCert) > 0 && caCert[0] != "" {
transport = commonhttp.GetHTTPTransport(
commonhttp.WithInsecure(insecure),
commonhttp.WithCACert(caCert[0]),
)
// If both insecure and caCert are provided, ensure InsecureSkipVerify is set
if len(caCert) > 0 && caCert[0] != "" {
if insecure {
// Both insecure and CA cert provided: set both options
transport = commonhttp.GetHTTPTransport(
commonhttp.WithInsecure(true),
commonhttp.WithCACert(caCert[0]),
)
} else {
transport = commonhttp.GetHTTPTransport(
commonhttp.WithCACert(caCert[0]),
)
}

Copilot uses AI. Check for mistakes.
Comment on lines 148 to 149
// Custom CA certificate takes precedence
transport = commonhttp.GetHTTPTransport(commonhttp.WithCACert(caCert))
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When caCert != "", the function creates a new transport with only the CA certificate option, ignoring the insecure parameter. If insecure=true is passed along with a CA certificate, the InsecureSkipVerify flag won't be set on the TLSClientConfig. This is inconsistent with the behavior in NewAuthorizer where both options are passed to GetHTTPTransport. Consider applying the insecure flag when creating the transport:

if caCert != "" {
    transport = commonhttp.GetHTTPTransport(
        commonhttp.WithInsecure(insecure),
        commonhttp.WithCACert(caCert))
}
Suggested change
// Custom CA certificate takes precedence
transport = commonhttp.GetHTTPTransport(commonhttp.WithCACert(caCert))
// Apply both insecure and custom CA certificate options
transport = commonhttp.GetHTTPTransport(
commonhttp.WithInsecure(insecure),
commonhttp.WithCACert(caCert))

Copilot uses AI. Check for mistakes.
Comment on lines 106 to 107
subjects := tr.TLSClientConfig.RootCAs.Subjects()
assert.Empty(t, subjects, "RootCAs should be empty for invalid cert")
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test verifies that RootCAs is not nil and empty for invalid certificates, but there's a potential issue: RootCAs.Subjects() is deprecated as of Go 1.18. Consider using len(tr.TLSClientConfig.RootCAs.Subjects()) == 0 or checking in a different way that doesn't rely on the deprecated Subjects() method to ensure forward compatibility.

Copilot uses AI. Check for mistakes.
@wy65701436 wy65701436 force-pushed the fix-22203 branch 2 times, most recently from 6937fa7 to 51580ce Compare November 11, 2025 07:18
chlins and others added 19 commits November 19, 2025 18:06
…2581)

fix: resolve 'docker-compose: command not found' error

Add fallback to support Docker Compose V2 plugin format when
standalone docker-compose binary is not available.

Signed-off-by: coldenchen <coldenchen@tencent.com>
Co-authored-by: coldenchen <coldenchen@tencent.com>
Signed-off-by: chlins <chlins.zhang@gmail.com>
…goharbor#22587)

removed unused lang translation

Signed-off-by: carsontham <carsontham@outlook.com>
Co-authored-by: carsontham <carsontham@outlook.com>
Signed-off-by: stonezdj <stone.zhang@broadcom.com>
… in /src (goharbor#22600)

chore(deps): bump github.com/go-openapi/runtime in /src

Bumps [github.com/go-openapi/runtime](https://github.com/go-openapi/runtime) from 0.28.0 to 0.29.2.
- [Release notes](https://github.com/go-openapi/runtime/releases)
- [Commits](go-openapi/runtime@v0.28.0...v0.29.2)

---
updated-dependencies:
- dependency-name: github.com/go-openapi/runtime
  dependency-version: 0.29.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: miner <miner.yang@broadcom.com>
…oharbor#22579)

Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.43.0 to 0.45.0.
- [Commits](golang/crypto@v0.43.0...v0.45.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.45.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…or#22617)

- Fix broken placeholder syntax in 'SCAN_BY' value
- Update various UI strings for better readability
- Translate previously untranslated keys (MANDATORY, OVERRIDE, etc.)

Signed-off-by: g-yunjh <yunjh1126@naver.com>
Signed-off-by: stonezdj <stone.zhang@broadcom.com>
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
fix(ui): remove unwanted scrollbar from tag-retention

Signed-off-by: bupd <bupdprasanth@gmail.com>
… 5.1.1 (goharbor#22599)

chore(deps): bump aws-actions/configure-aws-credentials

Bumps [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) from 5.0.0 to 5.1.1.
- [Release notes](https://github.com/aws-actions/configure-aws-credentials/releases)
- [Changelog](https://github.com/aws-actions/configure-aws-credentials/blob/main/CHANGELOG.md)
- [Commits](aws-actions/configure-aws-credentials@v5.0.0...v5.1.1)

---
updated-dependencies:
- dependency-name: aws-actions/configure-aws-credentials
  dependency-version: 5.1.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* feat(cosign): support Cosign v3 bundle format

Signed-off-by: Aloui-Ikram <ikram@container-registry.com>

* fix: format code with gofmt

Signed-off-by: Aloui-Ikram <ikram@container-registry.com>

* refactor: rename variable to mediaTypeCosignArtifactType

Signed-off-by: Aloui-Ikram <ikram@container-registry.com>

---------

Signed-off-by: Aloui-Ikram <ikram@container-registry.com>
Co-authored-by: Aloui-Ikram <ikram@container-registry.com>
Co-authored-by: miner <miner.yang@broadcom.com>
…oharbor#22623)

Bumps [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) from 0.34.1 to 0.34.2.
- [Commits](kubernetes/apimachinery@v0.34.1...v0.34.2)

---
updated-dependencies:
- dependency-name: k8s.io/apimachinery
  dependency-version: 0.34.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: bupd <bupdprasanth@gmail.com>
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](actions/upload-artifact@v5...v6)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Remove the unused function MostMatchSorter, it should not be implemented in golang, should be implement in the db query.
Remove the unused function onBoardCommonUserGroup()
fixes goharbor#22573

Signed-off-by: stonezdj <stonezdj@gmail.com>
refine apitest

Signed-off-by: my036811 <miner.yang@broadcom.com>
@github-actions
Copy link

This PR is being marked stale due to a period of inactivty. If this PR is still relevant, please comment or remove the stale label. Otherwise, this PR will close in 30 days.

@github-actions github-actions bot added the Stale label Jan 10, 2026
wang yan added 4 commits January 11, 2026 23:37
fixes goharbor#22203

Allowing Harbor to trust self-signed or private CA certificates for individual registry endpoints without modifying the system-level trust store.

1. Database schema changes, API updates with PEM validation.
2. HTTP transport layer modifications across all the registry adapters.
3. UI field to fill in the certificate.

The feature is backward compatible - existing installations using system-level CA trust will continue to work without any changes.

Signed-off-by: wang yan <yan-yw.wang@broadcom.com>
Signed-off-by: wang yan <yan-yw.wang@broadcom.com>
Signed-off-by: wang yan <yan-yw.wang@broadcom.com>
Signed-off-by: wang yan <yan-yw.wang@broadcom.com>
Signed-off-by: wang yan <yan-yw.wang@broadcom.com>
if err != nil {
// if push chunk error, we should query the upload progress for new location and end range.
newLocation, newEnd, err1 := c.getUploadStatus(location)
newLocation, newEnd, err1 := c.getUploadStatus(url)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong URL passed to upload status query

High Severity

The getUploadStatus call incorrectly passes url instead of location. The url variable contains query parameters added by buildChunkBlobUploadURL (including digest for the last chunk), while getUploadStatus expects the base upload location URL without query parameters. This causes the upload status query to fail or return incorrect results when a chunk upload fails, breaking error recovery for chunked blob uploads.

Fix in Cursor Fix in Web

Signed-off-by: wang yan <yan-yw.wang@broadcom.com>
@github-actions github-actions bot removed the Stale label Jan 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants