Repository navigation
feat: add AWS IAM database authentication for MySQL and PostgreSQL - #396
michielvha wants to merge 11 commits into
Conversation
|
Any updates on this? is it waiting for #393 to be resolved? |
kkendzia
left a comment
There was a problem hiding this comment.
Thanks for this great contribution! LGTM. IMHO it's a well written addition to the provider. :)
|
Would you mind reviewing this PR when you have a moment? Thanks in advance! |
| } | ||
| // IAM auth requires TLS. Cert verification depends on the operator | ||
| // supplying the RDS CA (e.g. via trust-manager or a mounted secret). | ||
| sslMode = "require" |
There was a problem hiding this comment.
sample place where sslMode is overwritten
There was a problem hiding this comment.
I've corrected this in b8d6121 in all six cluster reconcilers and the shared namespaced provider. IAM now preserves require, verify-ca, and verify-full, and upgrades only an empty mode or disable to require. The Kubernetes default stays verify-full.
The regression tests cover mode preservation and both namespaced ProviderConfig kinds. I also checked against RDS: verify-full fails when the RDS CA is missing.
|
This MR overwrites Postgres |
| - **[trust-manager](https://cert-manager.io/docs/trust/trust-manager/)** - add the | ||
| [Amazon RDS CA bundle](https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem) | ||
| to the provider pod's trust store. Then set `tls: "true"` (MySQL) or | ||
| `sslMode: verify-full` (PostgreSQL) and the certificate verifies normally. |
There was a problem hiding this comment.
This is not accurate. Trust-manager does not change pod runtime trust store.
Trust-manager only publishes the bundle as a ConfigMap/Secret and pod still needs that file mounted where Go reads the system pool (/etc/ssl/certs/ca-certificates.crt). This makes the solution similar to mounting secret as below.
There was a problem hiding this comment.
I've rewritten this in b8d6121 to include the missing mount and runtime configuration. The example mounts a ConfigMap containing public roots and the RDS CA, sets SSL_CERT_FILE, and documents restarting the provider after updates. trust-manager is described as distributing the bundle to a ConfigMap or Secret; the pod still needs to consume it.
I tested the mount in kind against RDS for both engines. Verified connections worked with the CA, failed without it, and recovered when it was restored.
| - **Mounted secret** - mount the RDS CA and reference it through the provider's | ||
| existing custom TLS mechanism (`tls: custom` + `tlsConfig` for MySQL; | ||
| `sslMode: verify-full` with the CA for PostgreSQL). |
There was a problem hiding this comment.
MySQL has tlsConfig.caCert.secretRef available in ProviderConfig but noting equivalent for Postgres
There was a problem hiding this comment.
I've removed that claim from the docs. The PostgreSQL example now uses the mounted trust bundle through SSL_CERT_FILE with sslMode: verify-full. This works with the existing ProviderConfig API, so I haven't added a CA Secret selector. Would you prefer adding a CA Secret selector to the PostgreSQL ProviderConfig?
| // with AWS IAM authentication. If mode is unset or non-encrypting | ||
| // ("preferred"/"false") it returns "skip-verify" (encrypted but unverified); | ||
| // otherwise it returns mode unchanged so an operator-configured verifying mode | ||
| // ("true"/"custom", backed by the RDS CA) is preserved. |
There was a problem hiding this comment.
tls=custom is not backed by the RDS CA on its own. LoadConfig rejects a tlsConfig that has caCert without clientCert and clientKey, and IAM database auth has no client certificate. The mode this function can actually verify with is tls=true, which reads the pod trust store. An omitted tls field still becomes skip-verify, so the connection is encrypted and the server certificate is not checked.
There was a problem hiding this comment.
I've corrected the comment, docs, and examples in b8d6121. They now use tls: "true" with the mounted trust bundle and explain that the custom loader requires caCert, clientCert, and clientKey.
The loader and the omitted-field skip-verify fallback are unchanged. I checked tls: "true" against RDS: it connects with the RDS CA and rejects the connection without it.
|
The commit from today, What is still wrong is the MySQL fallback and one PostgreSQL claim in the docs. MySQL still turns a missing or weak
The PostgreSQL The note that the 15-minute lifetime is never an issue is too strong. The token is baked into the DSN at connect time. A reconcile that lasts longer than 15 minutes and then opens another connection uses the expired token. |
|
I've addressed the remaining points:
Tested in EKS with Pod Identity against RDS PostgreSQL and MySQL across all three ProviderConfig paths per engine. CA rejection, rotation, password compatibility and fresh signing after a 16-minute wait passed. |
|
PostgreSQL in this PR can only verify the server through one |
|
I've added
|
b75ef57 to
8ae3b03
Compare
Add pkg/clients/awsiam: generates short-lived RDS IAM auth tokens and injects them as the database password, with region resolution and a test seam (injectable TokenBuilder) so the logic is unit-tested without AWS credentials or network. Part of crossplane-contrib#347. Signed-off-by: michielvha <michielvh@outlook.com>
Add the AWSIAMAuth credential source value and an optional Region field to the MySQL and PostgreSQL ProviderConfig (and namespaced ClusterProviderConfig) credential types, for both cluster and namespaced API groups. Regenerate deepcopy and CRDs. Part of crossplane-contrib#347. Signed-off-by: michielvha <michielvh@outlook.com>
Add a cleartext parameter to mysql.New/DSN that appends allowCleartextPasswords=true to the DSN when set. AWS RDS IAM authentication needs this because the AWSAuthenticationPlugin uses MySQL's cleartext client plugin, which go-sql-driver refuses by default. This commit only plumbs the flag (all existing callers pass false, so behaviour is unchanged); the IAM reconciler changes that set it follow. Part of crossplane-contrib#347. Signed-off-by: michielvha <michielvh@outlook.com>
Add awsiam.Inject (load AWS config, resolve region, generate and inject the IAM token) and call it from each cluster reconciler's Connect() when the credentials source is AWSIAMAuth. For MySQL this also enables the cleartext flag and ensures TLS is on; for PostgreSQL it forces sslmode=require. Certificate verification relies on the operator supplying the RDS CA (e.g. via trust-manager or a mounted secret). Part of crossplane-contrib#347. Signed-off-by: michielvha <michielvh@outlook.com>
Inject the IAM token centrally in GetProviderConfig() for the namespaced MySQL and PostgreSQL providers when the credentials source is AWSIAMAuth. MySQL additionally surfaces a Cleartext flag (forwarded by its three reconcilers) and forces TLS on; PostgreSQL forces sslmode=require. The six namespaced PostgreSQL reconcilers need no change. Part of crossplane-contrib#347. Signed-off-by: michielvha <michielvh@outlook.com>
Add example AWSIAMAuth ProviderConfigs for cluster and namespaced MySQL and PostgreSQL, and docs/aws-iam-auth.md covering prerequisites, the operator-supplied RDS CA trust options (trust-manager / mounted secret), region resolution and the TLS behaviour. Part of crossplane-contrib#347. Signed-off-by: michielvha <michielvh@outlook.com>
Signed-off-by: michielvha <michielvh@outlook.com>
Signed-off-by: michielvha <michielvh@outlook.com>
Signed-off-by: michielvha <michielvh@outlook.com>
8ae3b03 to
bf4e6c2
Compare
fernandezcuesta
left a comment
There was a problem hiding this comment.
Thanks for sticking with this one. I built the branch and ran go test ./pkg/..., and both pass. I did not run the E2E suite. I read the IAM client, both database clients, the TLS registry and garbage collector, the namespaced providers and a sample of the cluster reconcilers.
Per-connection token signing through PasswordConnector looks right, and the sslMode handling now keeps verify-ca and verify-full. I left three inline comments I'd like addressed before merge. The rest are smaller.
Separately, the PR now includes the MySQL custom-TLS rework (optional client cert, renamed registrations), the PostgreSQL DSN rewrite and the ExecTx close fix. They look correct to me. I'd lean towards splitting them out, but I'll defer to the maintainers on that.
For reference, the provider binary grows by about 6% with the AWS SDK (72.9 MB to 77.1 MB default build, 51.0 MB to 54.0 MB stripped). I did not measure memory.
Signed-off-by: michielvha <michielvh@outlook.com>
Signed-off-by: michielvha <michielvh@outlook.com>
|
Thanks for the review. I've addressed all six comments, including the shared connection helpers, the MySQL registration identity and PostgreSQL no-CA fast path.
I measured memory too. Local idle RSS was 49.7 MiB upstream and 51.6 MiB on this branch. In EKS with 100 additional consumers sharing one MySQL ClusterProviderConfig, median provider RSS was 114.4 MiB before these fixes and 71.8 MiB after, across three runs per version. CPU was about 0.17 core in both versions for that workload. On scope, I'd suggest keeping the CA-only MySQL support and DSN handling here, since they're used by the IAM connection paths. I'm happy to split out the independent |
Description of your changes
Adds AWS IAM database authentication for Aurora/RDS MySQL and PostgreSQL. Select
AWSIAMAuthas the credentials source and provide a connection Secret withendpoint,portandusername. The provider signs a fresh token for every physical connection attempt, includingdatabase/sqlretries, using the AWS SDK's refreshable credentials. The signing region comes from the ProviderConfig, the connection Secret, or the SDK configuration, in that order.Both engines support a per-ProviderConfig CA bundle through
tlsConfig.caCert.secretRef. PostgreSQL accepts the selector withverify-caorverify-full; the examples useverify-full. Namespaced PostgreSQL ProviderConfigs read the CA from their own namespace, while cluster-scoped configurations specify the Secret namespace. CA Secret updates reload on reconciliation and apply to subsequent connections without a provider restart. MySQL's custom TLS loaders accept a CA alone; an optional client certificate and key must be supplied together.PostgreSQL IAM preserves
require,verify-caandverify-full, and upgradesdisabletorequire. The API default remainsverify-full. MySQL IAM defaults to verifiedtls=true; explicitskip-verifyremains available. Existing password-authentication TLS defaults are preserved.The docs and examples use CA Secret selectors. Creating IAM-enabled database users remains a separate bootstrap step.
Fixes #347
How has this code been tested
make reviewable: generation, module tidy, zero lint issues and the full unit suite passed.Temporary test resources were removed.
I have:
make reviewableagainst the final revision.