Skip to content

Replace Ruby bosh-nats-sync with Golang implementation - #2746

Draft
aramprice wants to merge 3 commits into
mainfrom
experiment-golang-bosh-nats-sync
Draft

Replace Ruby bosh-nats-sync with Golang implementation#2746
aramprice wants to merge 3 commits into
mainfrom
experiment-golang-bosh-nats-sync

Conversation

@aramprice

Copy link
Copy Markdown
Member

Summary

  • Replaces the Ruby bosh-nats-sync gem with a Go binary (src/bosh-nats-sync/) that mirrors all Ruby behavior including TLS verification preferences (uaa_ca_cert > director_ca_cert), HTTP peer verification, and startup reliability
  • Removes all Ruby runtime dependencies from the nats BOSH job and package (director-ruby-3.3, gem bundling, BUNDLE_GEMFILE/GEM_HOME env vars)
  • Adds GitHub Actions CI coverage for the new Go code (lint + test jobs in go.yml); removes the defunct nats_sync:parallel Ruby matrix entry from ruby.yml

Commits

  1. Golang bosh-nats-sync — full Go implementation with parity to the Ruby version:

    • Correct TLS CA selection (uaa_ca_cert preferred over director_ca_cert)
    • Proper Director API TLS peer verification (no InsecureSkipVerify)
    • isConnectionError retry logic covering deadline exceeded / eof (matching Ruby's Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNRESET)
    • Fatal startup behavior on failed NATS reload (matching Ruby)
    • Full Ginkgo/Gomega test suite
  2. Remove ruby-isms now that nats is golang — strips Ruby from jobs/nats/, packages/nats/, src/Gemfile, and CI:

    • packages/nats/packaging now builds the Go binary via go build
    • jobs/nats/ wrapper script calls Go binary directly (no Ruby runtime sourcing)
    • CI go.yml: adds lint (bosh-nats-sync) and test (bosh-nats-sync) jobs
    • CI ruby.yml: removes nats_sync:parallel matrix entry
    • Adds src/bosh-nats-sync/.golangci.yml

Test plan

  • go.yml / lint (bosh-nats-sync) passes
  • go.yml / test (bosh-nats-sync) passes
  • ruby.yml / unit_specs passes for all remaining sub-projects (common:parallel, monitor:parallel, release)
  • Release ERB template specs (spec/nats_templates_spec.rb) pass

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5a8e9b6c-9f8f-41ea-b53e-a40fe461e924

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch experiment-golang-bosh-nats-sync

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread .github/workflows/go.yml Fixed
Comment thread .github/workflows/go.yml Fixed

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as resolved.

@github-project-automation github-project-automation Bot moved this from Inbox to Waiting for Changes | Open for Contribution in Foundational Infrastructure Working Group Jun 19, 2026
@aramprice
aramprice force-pushed the experiment-golang-bosh-nats-sync branch 2 times, most recently from 7577681 to d923c47 Compare June 19, 2026 23:49
@aramprice
aramprice requested a review from Copilot June 19, 2026 23:52

This comment was marked as outdated.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 19, 2026
@github-project-automation github-project-automation Bot moved this from Waiting for Changes | Open for Contribution to Pending Merge | Prioritized in Foundational Infrastructure Working Group Jun 19, 2026
@Alphasite
Alphasite requested a review from Copilot June 19, 2026 23:59

This comment was marked as resolved.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 20, 2026
@aramprice
aramprice force-pushed the experiment-golang-bosh-nats-sync branch 4 times, most recently from 84f41f0 to 8d773dc Compare June 20, 2026 01:47
@colins colins self-assigned this Jun 23, 2026
Comment thread src/bosh-nats-sync/pkg/userssync/users_sync.go
@aramprice
aramprice force-pushed the experiment-golang-bosh-nats-sync branch 2 times, most recently from fddaf21 to 5980267 Compare June 25, 2026 20:36
@colins
colins force-pushed the experiment-golang-bosh-nats-sync branch from 118abae to 5b1d58a Compare June 26, 2026 18:31

This comment was marked as duplicate.

This comment was marked as duplicate.

aramprice added a commit that referenced this pull request Jul 9, 2026
…olled tls.Config

Address item 1 in PR review comment
#2746 (comment):
authprovider and userssync both built their director/UAA TLS client by hand
(manual tls.Config{MinVersion}, x509.NewCertPool(), AppendCertsFromPEM). CF
already ships code.cloudfoundry.org/tlsconfig as the standard helper for
exactly this, and the reviewer noted the hand-rolled config was actually
*less* hardened than the org-standard one (no MaxVersion, no cipher-suite
pinning).

Deliberately does NOT touch items 2/3 from the same comment (reusing
bosh-cli's director/uaa packages for the director API and UAA calls) per
explicit instruction to avoid a github.com/cloudfoundry/bosh-cli dependency;
that remains a footprint-vs-reuse tradeoff for a future PR, not something to
pull in incidentally here.

pkg/authprovider/auth_provider.go:
- buildHTTPClient now builds its *tls.Config via
  tlsconfig.Build(tlsconfig.WithExternalServiceDefaults()).Client(...),
  applying tlsconfig.WithAuthorityFromFile(caCertPath) only when CAFilePath()
  resolves to a file with non-empty content. WithExternalServiceDefaults()
  pins MinVersion/MaxVersion to TLS 1.2-1.3 and a curated Mozilla-Intermediate
  cipher-suite list, in place of the previous MinVersion-only config.
- Preserves existing behavior exactly: a configured-but-unreadable CA file is
  still a hard error, an empty file still falls back to the system trust
  store, and a malformed (but present/non-empty) cert is still a hard error.

pkg/userssync/users_sync.go:
- buildHTTPClient now uses the same tlsconfig.Build(...).Client(...) pattern
  for the director API client. Replaced directorCACertPool (manual
  x509.CertPool + AppendCertsFromPEM) with a small usableCACertContent(path)
  predicate that gates whether tlsconfig.WithAuthorityFromFile is applied.
- Preserves existing behavior: director_ca_cert missing/unreadable/empty
  falls back to the system trust store (no error, matching Ruby's
  usable_director_ca_cert?), while a configured-but-unparseable cert is a
  hard error rather than a silent fallback.

pkg/userssync/export_test.go:
- Replaced the removed DirectorCACertPool test hook with
  BuildDirectorTLSConfig, which builds the real *http.Client via
  buildHTTPClient and returns its *tls.Config, so tests assert against the
  actual TLS configuration (RootCAs, MinVersion) rather than an internal
  CertPool-returning helper that no longer exists.

pkg/userssync/users_sync_test.go:
- Rewrote the "directorCACertPool" spec block against BuildDirectorTLSConfig:
  same four cases (not configured / missing file / whitespace-only file /
  unparseable content) now assert on tlsCfg.RootCAs and the error message,
  plus a new case asserting MinVersion is pinned to TLS 1.2 via
  WithExternalServiceDefaults.

Dependency footprint: go.mod/go.sum/vendor pick up
code.cloudfoundry.org/tlsconfig v0.61.0 and a matching golang.org/x/sys
patch bump (v0.44.0 -> v0.46.0, already an existing indirect dependency).
tlsconfig's own test-only dependencies (certstrap, go.step.sm/crypto,
golang.org/x/crypto, etc.) appear in go.sum for module-graph completeness
but are not imported by any non-test file, so `go mod vendor` does not pull
them into vendor/ or the build.

Verified `go build`, `go vet`, and `go test -race ./...` all pass with both
the default module resolution and explicit `-mod=vendor` (matching how
packages/nats/packaging invokes go build); gofmt and golangci-lint are clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aramprice
aramprice force-pushed the experiment-golang-bosh-nats-sync branch from 35830f0 to 87e76cb Compare July 9, 2026 17:47
aramprice added a commit that referenced this pull request Jul 9, 2026
…olled tls.Config

Address item 1 in PR review comment
#2746 (comment):
authprovider and userssync both built their director/UAA TLS client by hand
(manual tls.Config{MinVersion}, x509.NewCertPool(), AppendCertsFromPEM). CF
already ships code.cloudfoundry.org/tlsconfig as the standard helper for
exactly this, and the reviewer noted the hand-rolled config was actually
*less* hardened than the org-standard one (no MaxVersion, no cipher-suite
pinning).

Deliberately does NOT touch items 2/3 from the same comment (reusing
bosh-cli's director/uaa packages for the director API and UAA calls) per
explicit instruction to avoid a github.com/cloudfoundry/bosh-cli dependency;
that remains a footprint-vs-reuse tradeoff for a future PR, not something to
pull in incidentally here.

pkg/authprovider/auth_provider.go:
- buildHTTPClient now builds its *tls.Config via
  tlsconfig.Build(tlsconfig.WithExternalServiceDefaults()).Client(...),
  applying tlsconfig.WithAuthorityFromFile(caCertPath) only when CAFilePath()
  resolves to a file with non-empty content. WithExternalServiceDefaults()
  pins MinVersion/MaxVersion to TLS 1.2-1.3 and a curated Mozilla-Intermediate
  cipher-suite list, in place of the previous MinVersion-only config.
- Preserves existing behavior exactly: a configured-but-unreadable CA file is
  still a hard error, an empty file still falls back to the system trust
  store, and a malformed (but present/non-empty) cert is still a hard error.

pkg/userssync/users_sync.go:
- buildHTTPClient now uses the same tlsconfig.Build(...).Client(...) pattern
  for the director API client. Replaced directorCACertPool (manual
  x509.CertPool + AppendCertsFromPEM) with a small usableCACertContent(path)
  predicate that gates whether tlsconfig.WithAuthorityFromFile is applied.
- Preserves existing behavior: director_ca_cert missing/unreadable/empty
  falls back to the system trust store (no error, matching Ruby's
  usable_director_ca_cert?), while a configured-but-unparseable cert is a
  hard error rather than a silent fallback.

pkg/userssync/export_test.go:
- Replaced the removed DirectorCACertPool test hook with
  BuildDirectorTLSConfig, which builds the real *http.Client via
  buildHTTPClient and returns its *tls.Config, so tests assert against the
  actual TLS configuration (RootCAs, MinVersion) rather than an internal
  CertPool-returning helper that no longer exists.

pkg/userssync/users_sync_test.go:
- Rewrote the "directorCACertPool" spec block against BuildDirectorTLSConfig:
  same four cases (not configured / missing file / whitespace-only file /
  unparseable content) now assert on tlsCfg.RootCAs and the error message,
  plus a new case asserting MinVersion is pinned to TLS 1.2 via
  WithExternalServiceDefaults.

Dependency footprint: go.mod/go.sum/vendor pick up
code.cloudfoundry.org/tlsconfig v0.61.0 and a matching golang.org/x/sys
patch bump (v0.44.0 -> v0.46.0, already an existing indirect dependency).
tlsconfig's own test-only dependencies (certstrap, go.step.sm/crypto,
golang.org/x/crypto, etc.) appear in go.sum for module-graph completeness
but are not imported by any non-test file, so `go mod vendor` does not pull
them into vendor/ or the build.

Verified `go build`, `go vet`, and `go test -race ./...` all pass with both
the default module resolution and explicit `-mod=vendor` (matching how
packages/nats/packaging invokes go build); gofmt and golangci-lint are clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aramprice
aramprice force-pushed the experiment-golang-bosh-nats-sync branch from 87e76cb to 1e15707 Compare July 9, 2026 22:46
aramprice added a commit that referenced this pull request Jul 13, 2026
…olled tls.Config

Address item 1 in PR review comment
#2746 (comment):
authprovider and userssync both built their director/UAA TLS client by hand
(manual tls.Config{MinVersion}, x509.NewCertPool(), AppendCertsFromPEM). CF
already ships code.cloudfoundry.org/tlsconfig as the standard helper for
exactly this, and the reviewer noted the hand-rolled config was actually
*less* hardened than the org-standard one (no MaxVersion, no cipher-suite
pinning).

Deliberately does NOT touch items 2/3 from the same comment (reusing
bosh-cli's director/uaa packages for the director API and UAA calls) per
explicit instruction to avoid a github.com/cloudfoundry/bosh-cli dependency;
that remains a footprint-vs-reuse tradeoff for a future PR, not something to
pull in incidentally here.

pkg/authprovider/auth_provider.go:
- buildHTTPClient now builds its *tls.Config via
  tlsconfig.Build(tlsconfig.WithExternalServiceDefaults()).Client(...),
  applying tlsconfig.WithAuthorityFromFile(caCertPath) only when CAFilePath()
  resolves to a file with non-empty content. WithExternalServiceDefaults()
  pins MinVersion/MaxVersion to TLS 1.2-1.3 and a curated Mozilla-Intermediate
  cipher-suite list, in place of the previous MinVersion-only config.
- Preserves existing behavior exactly: a configured-but-unreadable CA file is
  still a hard error, an empty file still falls back to the system trust
  store, and a malformed (but present/non-empty) cert is still a hard error.

pkg/userssync/users_sync.go:
- buildHTTPClient now uses the same tlsconfig.Build(...).Client(...) pattern
  for the director API client. Replaced directorCACertPool (manual
  x509.CertPool + AppendCertsFromPEM) with a small usableCACertContent(path)
  predicate that gates whether tlsconfig.WithAuthorityFromFile is applied.
- Preserves existing behavior: director_ca_cert missing/unreadable/empty
  falls back to the system trust store (no error, matching Ruby's
  usable_director_ca_cert?), while a configured-but-unparseable cert is a
  hard error rather than a silent fallback.

pkg/userssync/export_test.go:
- Replaced the removed DirectorCACertPool test hook with
  BuildDirectorTLSConfig, which builds the real *http.Client via
  buildHTTPClient and returns its *tls.Config, so tests assert against the
  actual TLS configuration (RootCAs, MinVersion) rather than an internal
  CertPool-returning helper that no longer exists.

pkg/userssync/users_sync_test.go:
- Rewrote the "directorCACertPool" spec block against BuildDirectorTLSConfig:
  same four cases (not configured / missing file / whitespace-only file /
  unparseable content) now assert on tlsCfg.RootCAs and the error message,
  plus a new case asserting MinVersion is pinned to TLS 1.2 via
  WithExternalServiceDefaults.

Dependency footprint: go.mod/go.sum/vendor pick up
code.cloudfoundry.org/tlsconfig v0.61.0 and a matching golang.org/x/sys
patch bump (v0.44.0 -> v0.46.0, already an existing indirect dependency).
tlsconfig's own test-only dependencies (certstrap, go.step.sm/crypto,
golang.org/x/crypto, etc.) appear in go.sum for module-graph completeness
but are not imported by any non-test file, so `go mod vendor` does not pull
them into vendor/ or the build.

Verified `go build`, `go vet`, and `go test -race ./...` all pass with both
the default module resolution and explicit `-mod=vendor` (matching how
packages/nats/packaging invokes go build); gofmt and golangci-lint are clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aramprice
aramprice force-pushed the experiment-golang-bosh-nats-sync branch from 1e15707 to e94d5d2 Compare July 13, 2026 16:30
aramprice added a commit that referenced this pull request Jul 13, 2026
…olled tls.Config

Address item 1 in PR review comment
#2746 (comment):
authprovider and userssync both built their director/UAA TLS client by hand
(manual tls.Config{MinVersion}, x509.NewCertPool(), AppendCertsFromPEM). CF
already ships code.cloudfoundry.org/tlsconfig as the standard helper for
exactly this, and the reviewer noted the hand-rolled config was actually
*less* hardened than the org-standard one (no MaxVersion, no cipher-suite
pinning).

Deliberately does NOT touch items 2/3 from the same comment (reusing
bosh-cli's director/uaa packages for the director API and UAA calls) per
explicit instruction to avoid a github.com/cloudfoundry/bosh-cli dependency;
that remains a footprint-vs-reuse tradeoff for a future PR, not something to
pull in incidentally here.

pkg/authprovider/auth_provider.go:
- buildHTTPClient now builds its *tls.Config via
  tlsconfig.Build(tlsconfig.WithExternalServiceDefaults()).Client(...),
  applying tlsconfig.WithAuthorityFromFile(caCertPath) only when CAFilePath()
  resolves to a file with non-empty content. WithExternalServiceDefaults()
  pins MinVersion/MaxVersion to TLS 1.2-1.3 and a curated Mozilla-Intermediate
  cipher-suite list, in place of the previous MinVersion-only config.
- Preserves existing behavior exactly: a configured-but-unreadable CA file is
  still a hard error, an empty file still falls back to the system trust
  store, and a malformed (but present/non-empty) cert is still a hard error.

pkg/userssync/users_sync.go:
- buildHTTPClient now uses the same tlsconfig.Build(...).Client(...) pattern
  for the director API client. Replaced directorCACertPool (manual
  x509.CertPool + AppendCertsFromPEM) with a small usableCACertContent(path)
  predicate that gates whether tlsconfig.WithAuthorityFromFile is applied.
- Preserves existing behavior: director_ca_cert missing/unreadable/empty
  falls back to the system trust store (no error, matching Ruby's
  usable_director_ca_cert?), while a configured-but-unparseable cert is a
  hard error rather than a silent fallback.

pkg/userssync/export_test.go:
- Replaced the removed DirectorCACertPool test hook with
  BuildDirectorTLSConfig, which builds the real *http.Client via
  buildHTTPClient and returns its *tls.Config, so tests assert against the
  actual TLS configuration (RootCAs, MinVersion) rather than an internal
  CertPool-returning helper that no longer exists.

pkg/userssync/users_sync_test.go:
- Rewrote the "directorCACertPool" spec block against BuildDirectorTLSConfig:
  same four cases (not configured / missing file / whitespace-only file /
  unparseable content) now assert on tlsCfg.RootCAs and the error message,
  plus a new case asserting MinVersion is pinned to TLS 1.2 via
  WithExternalServiceDefaults.

Dependency footprint: go.mod/go.sum/vendor pick up
code.cloudfoundry.org/tlsconfig v0.61.0 and a matching golang.org/x/sys
patch bump (v0.44.0 -> v0.46.0, already an existing indirect dependency).
tlsconfig's own test-only dependencies (certstrap, go.step.sm/crypto,
golang.org/x/crypto, etc.) appear in go.sum for module-graph completeness
but are not imported by any non-test file, so `go mod vendor` does not pull
them into vendor/ or the build.

Verified `go build`, `go vet`, and `go test -race ./...` all pass with both
the default module resolution and explicit `-mod=vendor` (matching how
packages/nats/packaging invokes go build); gofmt and golangci-lint are clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aramprice
aramprice force-pushed the experiment-golang-bosh-nats-sync branch from e94d5d2 to 7e3388f Compare July 13, 2026 23:49
aramprice added a commit that referenced this pull request Jul 14, 2026
…olled tls.Config

Address item 1 in PR review comment
#2746 (comment):
authprovider and userssync both built their director/UAA TLS client by hand
(manual tls.Config{MinVersion}, x509.NewCertPool(), AppendCertsFromPEM). CF
already ships code.cloudfoundry.org/tlsconfig as the standard helper for
exactly this, and the reviewer noted the hand-rolled config was actually
*less* hardened than the org-standard one (no MaxVersion, no cipher-suite
pinning).

Deliberately does NOT touch items 2/3 from the same comment (reusing
bosh-cli's director/uaa packages for the director API and UAA calls) per
explicit instruction to avoid a github.com/cloudfoundry/bosh-cli dependency;
that remains a footprint-vs-reuse tradeoff for a future PR, not something to
pull in incidentally here.

pkg/authprovider/auth_provider.go:
- buildHTTPClient now builds its *tls.Config via
  tlsconfig.Build(tlsconfig.WithExternalServiceDefaults()).Client(...),
  applying tlsconfig.WithAuthorityFromFile(caCertPath) only when CAFilePath()
  resolves to a file with non-empty content. WithExternalServiceDefaults()
  pins MinVersion/MaxVersion to TLS 1.2-1.3 and a curated Mozilla-Intermediate
  cipher-suite list, in place of the previous MinVersion-only config.
- Preserves existing behavior exactly: a configured-but-unreadable CA file is
  still a hard error, an empty file still falls back to the system trust
  store, and a malformed (but present/non-empty) cert is still a hard error.

pkg/userssync/users_sync.go:
- buildHTTPClient now uses the same tlsconfig.Build(...).Client(...) pattern
  for the director API client. Replaced directorCACertPool (manual
  x509.CertPool + AppendCertsFromPEM) with a small usableCACertContent(path)
  predicate that gates whether tlsconfig.WithAuthorityFromFile is applied.
- Preserves existing behavior: director_ca_cert missing/unreadable/empty
  falls back to the system trust store (no error, matching Ruby's
  usable_director_ca_cert?), while a configured-but-unparseable cert is a
  hard error rather than a silent fallback.

pkg/userssync/export_test.go:
- Replaced the removed DirectorCACertPool test hook with
  BuildDirectorTLSConfig, which builds the real *http.Client via
  buildHTTPClient and returns its *tls.Config, so tests assert against the
  actual TLS configuration (RootCAs, MinVersion) rather than an internal
  CertPool-returning helper that no longer exists.

pkg/userssync/users_sync_test.go:
- Rewrote the "directorCACertPool" spec block against BuildDirectorTLSConfig:
  same four cases (not configured / missing file / whitespace-only file /
  unparseable content) now assert on tlsCfg.RootCAs and the error message,
  plus a new case asserting MinVersion is pinned to TLS 1.2 via
  WithExternalServiceDefaults.

Dependency footprint: go.mod/go.sum/vendor pick up
code.cloudfoundry.org/tlsconfig v0.61.0 and a matching golang.org/x/sys
patch bump (v0.44.0 -> v0.46.0, already an existing indirect dependency).
tlsconfig's own test-only dependencies (certstrap, go.step.sm/crypto,
golang.org/x/crypto, etc.) appear in go.sum for module-graph completeness
but are not imported by any non-test file, so `go mod vendor` does not pull
them into vendor/ or the build.

Verified `go build`, `go vet`, and `go test -race ./...` all pass with both
the default module resolution and explicit `-mod=vendor` (matching how
packages/nats/packaging invokes go build); gofmt and golangci-lint are clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aramprice
aramprice force-pushed the experiment-golang-bosh-nats-sync branch from 7e3388f to 8befa9e Compare July 14, 2026 16:27
aramprice added a commit that referenced this pull request Jul 16, 2026
…olled tls.Config

Address item 1 in PR review comment
#2746 (comment):
authprovider and userssync both built their director/UAA TLS client by hand
(manual tls.Config{MinVersion}, x509.NewCertPool(), AppendCertsFromPEM). CF
already ships code.cloudfoundry.org/tlsconfig as the standard helper for
exactly this, and the reviewer noted the hand-rolled config was actually
*less* hardened than the org-standard one (no MaxVersion, no cipher-suite
pinning).

Deliberately does NOT touch items 2/3 from the same comment (reusing
bosh-cli's director/uaa packages for the director API and UAA calls) per
explicit instruction to avoid a github.com/cloudfoundry/bosh-cli dependency;
that remains a footprint-vs-reuse tradeoff for a future PR, not something to
pull in incidentally here.

pkg/authprovider/auth_provider.go:
- buildHTTPClient now builds its *tls.Config via
  tlsconfig.Build(tlsconfig.WithExternalServiceDefaults()).Client(...),
  applying tlsconfig.WithAuthorityFromFile(caCertPath) only when CAFilePath()
  resolves to a file with non-empty content. WithExternalServiceDefaults()
  pins MinVersion/MaxVersion to TLS 1.2-1.3 and a curated Mozilla-Intermediate
  cipher-suite list, in place of the previous MinVersion-only config.
- Preserves existing behavior exactly: a configured-but-unreadable CA file is
  still a hard error, an empty file still falls back to the system trust
  store, and a malformed (but present/non-empty) cert is still a hard error.

pkg/userssync/users_sync.go:
- buildHTTPClient now uses the same tlsconfig.Build(...).Client(...) pattern
  for the director API client. Replaced directorCACertPool (manual
  x509.CertPool + AppendCertsFromPEM) with a small usableCACertContent(path)
  predicate that gates whether tlsconfig.WithAuthorityFromFile is applied.
- Preserves existing behavior: director_ca_cert missing/unreadable/empty
  falls back to the system trust store (no error, matching Ruby's
  usable_director_ca_cert?), while a configured-but-unparseable cert is a
  hard error rather than a silent fallback.

pkg/userssync/export_test.go:
- Replaced the removed DirectorCACertPool test hook with
  BuildDirectorTLSConfig, which builds the real *http.Client via
  buildHTTPClient and returns its *tls.Config, so tests assert against the
  actual TLS configuration (RootCAs, MinVersion) rather than an internal
  CertPool-returning helper that no longer exists.

pkg/userssync/users_sync_test.go:
- Rewrote the "directorCACertPool" spec block against BuildDirectorTLSConfig:
  same four cases (not configured / missing file / whitespace-only file /
  unparseable content) now assert on tlsCfg.RootCAs and the error message,
  plus a new case asserting MinVersion is pinned to TLS 1.2 via
  WithExternalServiceDefaults.

Dependency footprint: go.mod/go.sum/vendor pick up
code.cloudfoundry.org/tlsconfig v0.61.0 and a matching golang.org/x/sys
patch bump (v0.44.0 -> v0.46.0, already an existing indirect dependency).
tlsconfig's own test-only dependencies (certstrap, go.step.sm/crypto,
golang.org/x/crypto, etc.) appear in go.sum for module-graph completeness
but are not imported by any non-test file, so `go mod vendor` does not pull
them into vendor/ or the build.

Verified `go build`, `go vet`, and `go test -race ./...` all pass with both
the default module resolution and explicit `-mod=vendor` (matching how
packages/nats/packaging invokes go build); gofmt and golangci-lint are clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aramprice
aramprice force-pushed the experiment-golang-bosh-nats-sync branch from 8befa9e to 9061c91 Compare July 16, 2026 21:58
aramprice added a commit that referenced this pull request Jul 17, 2026
…olled tls.Config

Address item 1 in PR review comment
#2746 (comment):
authprovider and userssync both built their director/UAA TLS client by hand
(manual tls.Config{MinVersion}, x509.NewCertPool(), AppendCertsFromPEM). CF
already ships code.cloudfoundry.org/tlsconfig as the standard helper for
exactly this, and the reviewer noted the hand-rolled config was actually
*less* hardened than the org-standard one (no MaxVersion, no cipher-suite
pinning).

Deliberately does NOT touch items 2/3 from the same comment (reusing
bosh-cli's director/uaa packages for the director API and UAA calls) per
explicit instruction to avoid a github.com/cloudfoundry/bosh-cli dependency;
that remains a footprint-vs-reuse tradeoff for a future PR, not something to
pull in incidentally here.

pkg/authprovider/auth_provider.go:
- buildHTTPClient now builds its *tls.Config via
  tlsconfig.Build(tlsconfig.WithExternalServiceDefaults()).Client(...),
  applying tlsconfig.WithAuthorityFromFile(caCertPath) only when CAFilePath()
  resolves to a file with non-empty content. WithExternalServiceDefaults()
  pins MinVersion/MaxVersion to TLS 1.2-1.3 and a curated Mozilla-Intermediate
  cipher-suite list, in place of the previous MinVersion-only config.
- Preserves existing behavior exactly: a configured-but-unreadable CA file is
  still a hard error, an empty file still falls back to the system trust
  store, and a malformed (but present/non-empty) cert is still a hard error.

pkg/userssync/users_sync.go:
- buildHTTPClient now uses the same tlsconfig.Build(...).Client(...) pattern
  for the director API client. Replaced directorCACertPool (manual
  x509.CertPool + AppendCertsFromPEM) with a small usableCACertContent(path)
  predicate that gates whether tlsconfig.WithAuthorityFromFile is applied.
- Preserves existing behavior: director_ca_cert missing/unreadable/empty
  falls back to the system trust store (no error, matching Ruby's
  usable_director_ca_cert?), while a configured-but-unparseable cert is a
  hard error rather than a silent fallback.

pkg/userssync/export_test.go:
- Replaced the removed DirectorCACertPool test hook with
  BuildDirectorTLSConfig, which builds the real *http.Client via
  buildHTTPClient and returns its *tls.Config, so tests assert against the
  actual TLS configuration (RootCAs, MinVersion) rather than an internal
  CertPool-returning helper that no longer exists.

pkg/userssync/users_sync_test.go:
- Rewrote the "directorCACertPool" spec block against BuildDirectorTLSConfig:
  same four cases (not configured / missing file / whitespace-only file /
  unparseable content) now assert on tlsCfg.RootCAs and the error message,
  plus a new case asserting MinVersion is pinned to TLS 1.2 via
  WithExternalServiceDefaults.

Dependency footprint: go.mod/go.sum/vendor pick up
code.cloudfoundry.org/tlsconfig v0.61.0 and a matching golang.org/x/sys
patch bump (v0.44.0 -> v0.46.0, already an existing indirect dependency).
tlsconfig's own test-only dependencies (certstrap, go.step.sm/crypto,
golang.org/x/crypto, etc.) appear in go.sum for module-graph completeness
but are not imported by any non-test file, so `go mod vendor` does not pull
them into vendor/ or the build.

Verified `go build`, `go vet`, and `go test -race ./...` all pass with both
the default module resolution and explicit `-mod=vendor` (matching how
packages/nats/packaging invokes go build); gofmt and golangci-lint are clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aramprice
aramprice force-pushed the experiment-golang-bosh-nats-sync branch from 9061c91 to cd58b16 Compare July 17, 2026 19:12
aramprice added a commit that referenced this pull request Jul 17, 2026
…olled tls.Config

Address item 1 in PR review comment
#2746 (comment):
authprovider and userssync both built their director/UAA TLS client by hand
(manual tls.Config{MinVersion}, x509.NewCertPool(), AppendCertsFromPEM). CF
already ships code.cloudfoundry.org/tlsconfig as the standard helper for
exactly this, and the reviewer noted the hand-rolled config was actually
*less* hardened than the org-standard one (no MaxVersion, no cipher-suite
pinning).

Deliberately does NOT touch items 2/3 from the same comment (reusing
bosh-cli's director/uaa packages for the director API and UAA calls) per
explicit instruction to avoid a github.com/cloudfoundry/bosh-cli dependency;
that remains a footprint-vs-reuse tradeoff for a future PR, not something to
pull in incidentally here.

pkg/authprovider/auth_provider.go:
- buildHTTPClient now builds its *tls.Config via
  tlsconfig.Build(tlsconfig.WithExternalServiceDefaults()).Client(...),
  applying tlsconfig.WithAuthorityFromFile(caCertPath) only when CAFilePath()
  resolves to a file with non-empty content. WithExternalServiceDefaults()
  pins MinVersion/MaxVersion to TLS 1.2-1.3 and a curated Mozilla-Intermediate
  cipher-suite list, in place of the previous MinVersion-only config.
- Preserves existing behavior exactly: a configured-but-unreadable CA file is
  still a hard error, an empty file still falls back to the system trust
  store, and a malformed (but present/non-empty) cert is still a hard error.

pkg/userssync/users_sync.go:
- buildHTTPClient now uses the same tlsconfig.Build(...).Client(...) pattern
  for the director API client. Replaced directorCACertPool (manual
  x509.CertPool + AppendCertsFromPEM) with a small usableCACertContent(path)
  predicate that gates whether tlsconfig.WithAuthorityFromFile is applied.
- Preserves existing behavior: director_ca_cert missing/unreadable/empty
  falls back to the system trust store (no error, matching Ruby's
  usable_director_ca_cert?), while a configured-but-unparseable cert is a
  hard error rather than a silent fallback.

pkg/userssync/export_test.go:
- Replaced the removed DirectorCACertPool test hook with
  BuildDirectorTLSConfig, which builds the real *http.Client via
  buildHTTPClient and returns its *tls.Config, so tests assert against the
  actual TLS configuration (RootCAs, MinVersion) rather than an internal
  CertPool-returning helper that no longer exists.

pkg/userssync/users_sync_test.go:
- Rewrote the "directorCACertPool" spec block against BuildDirectorTLSConfig:
  same four cases (not configured / missing file / whitespace-only file /
  unparseable content) now assert on tlsCfg.RootCAs and the error message,
  plus a new case asserting MinVersion is pinned to TLS 1.2 via
  WithExternalServiceDefaults.

Dependency footprint: go.mod/go.sum/vendor pick up
code.cloudfoundry.org/tlsconfig v0.61.0 and a matching golang.org/x/sys
patch bump (v0.44.0 -> v0.46.0, already an existing indirect dependency).
tlsconfig's own test-only dependencies (certstrap, go.step.sm/crypto,
golang.org/x/crypto, etc.) appear in go.sum for module-graph completeness
but are not imported by any non-test file, so `go mod vendor` does not pull
them into vendor/ or the build.

Verified `go build`, `go vet`, and `go test -race ./...` all pass with both
the default module resolution and explicit `-mod=vendor` (matching how
packages/nats/packaging invokes go build); gofmt and golangci-lint are clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aramprice
aramprice force-pushed the experiment-golang-bosh-nats-sync branch 5 times, most recently from 8619ba8 to b9a9ff5 Compare July 17, 2026 23:03
@aramprice
aramprice force-pushed the experiment-golang-bosh-nats-sync branch 2 times, most recently from 7d32916 to bbefe1e Compare August 7, 2026 19:16
@aramprice
aramprice force-pushed the experiment-golang-bosh-nats-sync branch from bbefe1e to dc10816 Compare August 19, 2026 21:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Waiting for Changes | Open for Contribution

Development

Successfully merging this pull request may close these issues.

5 participants