crossplane-contrib / crossplane-contrib/provider-sql

Add pluggable credential helper source (HTTP endpoint) for ProviderConfig

Open
#393 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
154
Forks
119
Avg merge
6d 17h
Merged PRs (30d)
8

Description

## Problem

Today, every `ProviderConfig` in provider-sql requires master database credentials to be materialized as a Kubernetes `Secret` (referenced via `spec.credentials.connectionSecretRef`). For control planes that broker access to upstream secret stores (AWS Secrets Manager, GCP Secret Manager, Vault, etc.), this means the master password ends up sitting in plaintext in a user-visible namespace, exposed to anyone with namespace-scoped read access — even though the upstream system already enforces stricter access controls.

There is no way today to instruct provider-sql to fetch credentials from somewhere other than a `Secret` in the cluster.

## Proposal

Add a new credential `Source` value that points provider-sql at a configurable HTTP endpoint. The operator hosts a small "credential helper" — typically a sidecar bound to localhost — that resolves credentials from whatever upstream backend they want (Secrets Manager, Vault, etc.) and returns them over HTTP. Provider-sql never sees the upstream credentials and they never land in a `Secret`.

### API shape

```yaml
apiVersion: postgresql.sql.crossplane.io/v1alpha1
kind: ProviderConfig
metadata:
name: default
spec:
credentials:
source: PostgreSQLConnectionHelper
connectionHelperConfig:
endpoint: http://localhost:8080/credentials
sslMode: require
```

Mirrored for `mysql.sql.crossplane.io` and `mssql.sql.crossplane.io` with `MySQLConnectionHelper` / `MSSQLConnectionHelper` to match the existing per-engine `Source` naming convention.

### Helper response contract

The helper returns JSON whose keys map directly to the existing `xpv1.ResourceCredentialsSecret*Key` constants the connection clients already consume:

```json
{
"endpoint": "db.example.internal",
"port": "5432",
"username": "master",
"password": "..."
}
```

This keeps `pkg/clients/{postgresql,mysql,mssql}.New()` unchanged — they continue to receive a flat `map[string][]byte` of credentials and don't need to know whether the source was a `Secret` or a helper.

### v1 scope (kept deliberately small)

- **Localhost sidecar only** — no auth on the helper endpoint in v1; security boundary is the pod network namespace
- **All three engines** (postgres, mysql, mssql), cluster + namespaced ProviderConfig variants
- **Additive only** — no changes to the existing `*ConnectionSecret` source; existing users unaffected
- **No new dependencies** — `net/http` + `encoding/json` from stdlib are sufficient

Auth on the helper endpoint (bearer token, mTLS) and non-localhost endpoints are deliberately out of scope for v1, but the API shape leaves room to add them compatibly later (e.g. optional fields on `ConnectionHelperConfig`).

## Implementation footprint

- API types: `apis/{namespaced,cluster}/{postgresql,mysql,mssql}/v1alpha1/provider_types.go` — new enum const + optional `ConnectionHelperConfig` field on `ProviderCredentials`
- Connectors: each `pkg/controller/.../reconciler.go` `Connect()` gains a `switch` on `pc.Spec.Credentials.Source` with a new branch that calls the HTTP helper client
- New file: `pkg/clients/credentialhelper/http.go` — HTTP GET + JSON decode → `map[string][]byte`
- Tests: `httptest.Server`-based fixtures in the existing reconciler test suites

No refactoring of existing code is required.

## Prior art / related work

- #347 — AWS IAM database authentication. The author identifies `ProviderCredentials.Source` as the natural extension point for alternative credential sources. **This proposal is complementary, not competing**: IAM auth solves "use AWS-issued tokens" while a credential helper solves "fetch from any backend, including AWS Secrets Manager today, before IAM auth lands". Happy to coordinate naming/structure with whoever picks up #347.
- #362 — `SecretKeyMapping` (merged) — recent precedent that `ProviderCredentials` is being actively evolved.
- crossplane/crossplane#7343 — broader ecosystem interest in dynamic credential references.

## Questions for maintainers before I open a PR

1. **PR granularity** — would you prefer one PR per engine (postgres first, mysql/mssql follow), or a single PR covering all three? The reconciler change is mechanical but replicated across ~24 files.
2. **Naming** — per-engine `*ConnectionHelper` constants matches the existing `*ConnectionSecret` convention, but a single shared `ConnectionHelper` value across all three engines is also defensible. Preference?
3. **Helper response shape** — is JSON-with-stringly-typed-fields acceptable, or would you prefer a versioned content-type (e.g. `application/vnd.crossplane.provider-sql.credentials.v1+json`) from day one?
4. **Coordination with #347** — is there an active implementer there I should sync with?

Happy to iterate on the design here before any code lands. If reception is positive I'll open a draft PR scoped per your preference above.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.