mirror of
https://github.com/hanzoai/deploy.git
synced 2026-10-07 08:44:50 +00:00
go.mod still declared `module github.com/hanzoai/deploy` after the repo was renamed. A Go module path is not cosmetic — it is the resolvable location. So `go get github.com/hanzoai/cd` could not work, and anything importing this module had to spell a name that no longer exists at that address. The build log of v3.6.0 is where it surfaced: the compiler was still emitting github.com/hanzoai/deploy/cmd/cd/commands. 3541 import paths across 690 .go, 19 .proto, 4 .yaml, 3 .sh, 2 .mod, Procfile and Makefile. Plus the 62 ghcr.io/hanzoai/deploy image references, moved to ghcr.io/hanzoai/cd to match hanzo.yml's build target — a distinct string from the module path, replaced separately so neither could clobber the other. go mod tidy clean, CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ./... clean. Co-authored-by: Hanzo Dev <[email protected]>
194 lines
6.3 KiB
Go
194 lines
6.3 KiB
Go
package reposerver_test
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/hanzoai/cd/reposerver"
|
|
reposervercache "github.com/hanzoai/cd/reposerver/cache"
|
|
"github.com/hanzoai/cd/reposerver/metrics"
|
|
"github.com/hanzoai/cd/reposerver/repository"
|
|
"github.com/hanzoai/cd/util/askpass"
|
|
cacheutil "github.com/hanzoai/cd/util/cache"
|
|
utilstls "github.com/hanzoai/cd/util/tls"
|
|
)
|
|
|
|
func TestNewServer_DisableTLS(t *testing.T) {
|
|
srv, err := newTestServer(t, "", true)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, srv)
|
|
|
|
assert.Nil(t, srv.GetTLSConfig(), "TLS config must be nil when --disable-tls is set")
|
|
assert.Nil(t, srv.GetHealthCheckClientCert(), "health-check client cert must be nil when TLS is disabled")
|
|
}
|
|
|
|
func TestNewServer_TLSOnly_NoMTLS(t *testing.T) {
|
|
srv, err := newTestServer(t, "", false)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, srv)
|
|
|
|
tlsCfg := srv.GetTLSConfig()
|
|
require.NotNil(t, tlsCfg, "TLS config must be non-nil when TLS is enabled")
|
|
|
|
assert.Nil(t, srv.GetHealthCheckClientCert(), "health-check client cert must be nil when clientCAPath is not set")
|
|
assert.NotEqual(t, tls.RequireAndVerifyClientCert, tlsCfg.ClientAuth, "ClientAuth must not be RequireAndVerifyClientCert when mTLS is not configured")
|
|
assert.Nil(t, tlsCfg.ClientCAs, "ClientCAs pool must be nil when no client CA path is provided")
|
|
}
|
|
|
|
func TestNewServer_MTLS_HealthCheckCertGenerated(t *testing.T) {
|
|
caPath := writeTempCACert(t)
|
|
|
|
srv, err := newTestServer(t, caPath, false)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, srv)
|
|
|
|
hcCert := srv.GetHealthCheckClientCert()
|
|
require.NotNil(t, hcCert, "an ephemeral health-check client cert must be generated when mTLS is enabled")
|
|
require.NotEmpty(t, hcCert.Certificate, "the health-check cert must contain at least one DER-encoded certificate")
|
|
}
|
|
|
|
func TestNewServer_MTLS_ServerRequiresClientCert(t *testing.T) {
|
|
caPath := writeTempCACert(t)
|
|
|
|
srv, err := newTestServer(t, caPath, false)
|
|
require.NoError(t, err)
|
|
|
|
tlsCfg := srv.GetTLSConfig()
|
|
require.NotNil(t, tlsCfg)
|
|
|
|
assert.Equal(t, tls.RequireAndVerifyClientCert, tlsCfg.ClientAuth, "server must enforce RequireAndVerifyClientCert when a client CA path is set")
|
|
assert.NotNil(t, tlsCfg.ClientCAs, "ClientCAs pool must be populated from the provided client CA path")
|
|
}
|
|
|
|
func TestNewServer_MTLS_HealthCheckCertRegisteredInClientCAs(t *testing.T) {
|
|
caPath := writeTempCACert(t)
|
|
|
|
srv, err := newTestServer(t, caPath, false)
|
|
require.NoError(t, err)
|
|
|
|
hcCert := srv.GetHealthCheckClientCert()
|
|
require.NotNil(t, hcCert)
|
|
|
|
tlsCfg := srv.GetTLSConfig()
|
|
require.NotNil(t, tlsCfg)
|
|
require.NotNil(t, tlsCfg.ClientCAs)
|
|
|
|
parsedHCCert, err := x509.ParseCertificate(hcCert.Certificate[0])
|
|
require.NoError(t, err)
|
|
|
|
_, err = parsedHCCert.Verify(x509.VerifyOptions{
|
|
Roots: tlsCfg.ClientCAs,
|
|
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
|
})
|
|
require.NoError(t, err, "the health-check cert must be verifiable against the server's ClientCAs pool: "+
|
|
"if this fails, the liveness probe will be rejected by its own server")
|
|
}
|
|
|
|
func TestNewServer_MTLS_HealthCheckCertProperties(t *testing.T) {
|
|
caPath := writeTempCACert(t)
|
|
|
|
srv, err := newTestServer(t, caPath, false)
|
|
require.NoError(t, err)
|
|
|
|
hcCert := srv.GetHealthCheckClientCert()
|
|
require.NotNil(t, hcCert)
|
|
|
|
parsed, err := x509.ParseCertificate(hcCert.Certificate[0])
|
|
require.NoError(t, err)
|
|
|
|
assert.True(t, parsed.IsCA, "health-check cert must be a CA cert so it can be added to the ClientCAs pool")
|
|
assert.Equal(t, parsed.Subject.String(), parsed.Issuer.String(), "health-check cert must be self-signed")
|
|
require.Len(t, parsed.ExtKeyUsage, 1, "health-check cert must carry exactly one extended key usage")
|
|
assert.Equal(t, x509.ExtKeyUsageClientAuth, parsed.ExtKeyUsage[0], "health-check cert must carry ExtKeyUsageClientAuth, not ExtKeyUsageServerAuth")
|
|
}
|
|
|
|
func TestNewServer_MTLS_EachStartupGeneratesDistinctCert(t *testing.T) {
|
|
caPath1 := writeTempCACert(t)
|
|
caPath2 := writeTempCACert(t)
|
|
|
|
srv1, err := newTestServer(t, caPath1, false)
|
|
require.NoError(t, err)
|
|
|
|
srv2, err := newTestServer(t, caPath2, false)
|
|
require.NoError(t, err)
|
|
|
|
cert1 := srv1.GetHealthCheckClientCert()
|
|
cert2 := srv2.GetHealthCheckClientCert()
|
|
|
|
require.NotNil(t, cert1)
|
|
require.NotNil(t, cert2)
|
|
|
|
assert.NotEqual(t, cert1.Certificate[0], cert2.Certificate[0], "each server instance must generate its own unique ephemeral health-check cert")
|
|
}
|
|
|
|
func TestNewServer_MTLS_InvalidCAPath(t *testing.T) {
|
|
srv, err := newTestServer(t, "/nonexistent/path/ca.crt", false)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, srv)
|
|
|
|
tlsCfg := srv.GetTLSConfig()
|
|
require.NotNil(t, tlsCfg, "TLS config must still be non-nil (server TLS is active, just no mTLS)")
|
|
assert.Nil(t, tlsCfg.ClientCAs, "ClientCAs must be nil when the CA file does not exist")
|
|
assert.NotEqual(t, tls.RequireAndVerifyClientCert, tlsCfg.ClientAuth, "ClientAuth must not require client certs when CA file is absent")
|
|
assert.Nil(t, srv.GetHealthCheckClientCert(), "no health-check cert should be generated when mTLS is skipped")
|
|
}
|
|
|
|
func TestNewServer_MTLS_InvalidCACertContent(t *testing.T) {
|
|
invalidCAPath := filepath.Join(t.TempDir(), "bad-ca.crt")
|
|
require.NoError(t, os.WriteFile(invalidCAPath, []byte("not a valid certificate"), 0o600))
|
|
|
|
_, err := newTestServer(t, invalidCAPath, false)
|
|
assert.ErrorContains(t, err, "invalid cert data")
|
|
}
|
|
|
|
func newTestServer(t *testing.T, clientCAPath string, disableTLS bool) (*reposerver.ArgoCDRepoServer, error) {
|
|
t.Helper()
|
|
|
|
metricsServer := metrics.NewMetricsServer()
|
|
|
|
cache := reposervercache.NewCache(
|
|
cacheutil.NewCache(cacheutil.NewInMemoryCache(1*time.Hour)),
|
|
1*time.Minute,
|
|
1*time.Minute,
|
|
10*time.Second,
|
|
)
|
|
|
|
askPassServer := askpass.NewServer(filepath.Join(t.TempDir(), "cd-askpass.sock"))
|
|
|
|
return reposerver.NewServer(
|
|
metricsServer,
|
|
cache,
|
|
nil,
|
|
repository.RepoServerInitConstants{},
|
|
askPassServer,
|
|
clientCAPath,
|
|
disableTLS,
|
|
)
|
|
}
|
|
|
|
func writeTempCACert(t *testing.T) string {
|
|
t.Helper()
|
|
|
|
cert, err := utilstls.GenerateX509KeyPair(utilstls.CertOptions{
|
|
Hosts: []string{"localhost"},
|
|
Organization: "Argo CD Test CA",
|
|
IsCA: true,
|
|
ECDSACurve: "P256",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
certPEM, _ := utilstls.EncodeX509KeyPair(*cert)
|
|
|
|
path := filepath.Join(t.TempDir(), "ca.crt")
|
|
require.NoError(t, os.WriteFile(path, certPEM, 0o600))
|
|
|
|
return path
|
|
}
|