Skip to content

Add cross-account AssumeRole support for Route 53 DNS validation.#749

Open
VA6DAH wants to merge 1 commit into
webprofusion:developmentfrom
VA6DAH:feature/route53-assume-role
Open

Add cross-account AssumeRole support for Route 53 DNS validation.#749
VA6DAH wants to merge 1 commit into
webprofusion:developmentfrom
VA6DAH:feature/route53-assume-role

Conversation

@VA6DAH

@VA6DAH VA6DAH commented Jun 28, 2026

Copy link
Copy Markdown

Summary

Adds optional STS AssumeRole support to the Amazon Route 53 DNS-01 provider so Certify can update hosted zones in a different AWS account than the IAM user whose access keys are stored in credentials.

Assume-role settings live in stored credentials (access keys, role ARN, session name, optional external ID). Certificate configuration keeps only zone-specific fields (hosted zone ID, propagation delay).

Includes UI/model improvements so Route 53 appears correctly in the credential provider list, assume-role fields show/hide based on a checkbox, and other DNS providers are not regressed.

Why this matters (especially in AWS Organizations)

In many AWS Organizations, DNS is centralized in a shared/network account while applications and certificate workloads run in member accounts. Route 53 hosted zones often cannot (and should not) be managed from the same account that runs the app server.

Without AssumeRole, operators typically must either:

  • Create long-lived IAM users in the DNS account and copy keys onto every Certify host, or
  • Grant overly broad Route 53 permissions to identities in workload accounts that do not own the zones.

AssumeRole is the standard AWS pattern for this:

Concern How AssumeRole helps
Account separation Workload account holds a caller identity; DNS account holds a role trusted for sts:AssumeRole only.
Least privilege Caller: sts:AssumeRole on one role. Target role: scoped route53:ChangeResourceRecordSets on specific hosted zones.
Org governance Aligns with centralized DNS, SCPs, and landing-zone designs where member accounts should not administer org-wide DNS directly.
Credential hygiene No need to duplicate DNS-account IAM users across many servers; rotate/trust at the role boundary.
Cross-account safety Optional External ID supports confused-deputy protection in the role trust policy.

What changed

Route 53 provider

  • When Assume cross-account role is enabled, calls sts:AssumeRole with stored access keys, then creates the Route 53 client with temporary session credentials.
  • Supports Role ARN, session name (default CertifyTheWeb), and optional External ID.
  • Reads assume-role settings from credentials first, with fallback to certificate parameters for backward compatibility.
  • Adds AWSSDK.SecurityToken (pinned with aligned AWSSDK.Core / AWSSDK.Route53 versions).

AWS Route 53 Credentials

  • Credentials: access key, secret key, assume-role toggle and related fields.

UI / model

  • ProviderParameter.VisibleWhenParameterKey/Value — conditional field visibility (assume-role sub-fields in Edit Credential).
  • ProviderParameterCredentialFilter — reliable detection of DNS providers/fields that support stored credentials (API JSON omits default IsCredential=true).
  • ProviderParameterVisibility — shared show/hide helper.
  • EditCredential — assume-role validation on save; fix provider list refresh when empty.
  • ChallengeConfigItem — certificate UI shows non-credential parameters only; visibility refresh guarded to avoid MSDNS dropdown freeze.

Tests

  • ProviderParameterCredentialFilterTests — Cloudflare, Route 53, DnsMadeEasy, MSDNS parameter classification.

Screenshots

2026-06-27 18_24_04-Certify Certificate Manager  Community Edition  7 0 22 0

Test plan

  • Route 53 plugin loads without errors
  • Route 53 appears in credential provider dropdown
  • Credential test succeeds with cross-account AssumeRole (caller → role in DNS account)
  • Assume-role fields hidden when off; shown when on
  • Saving credential validates Role ARN when assume-role enabled
  • Certificate challenge UI shows zone ID / propagation delay only
  • MSDNS certificate config no longer freezes when selected (dropdown rebind loop fixed)
  • Same-account Route 53 (no assume role)
  • Other DNS-01 DNS API providers to ensure no usability bugs.

Notes for reviewers

  • SDK pinning: AWSSDK.SecurityToken added alongside pinned Core/Route53 versions to avoid TypeLoadException at plugin load.
  • Directory.Build.props → 7.0.22.0: local version bump for screenshot builds — happy to revert in a follow-up commit if maintainers prefer a separate version commit.

Request Certificate Log

2026-06-27 18:30:11.036 -06:00 [INF] ---- Beginning Request [certify.hourielabs.click] ----
2026-06-27 18:30:11.038 -06:00 [INF] Certify/7.0.22.0 (Microsoft Windows 10.0.26200; Microsoft Windows NT 10.0.26200.0) 
2026-06-27 18:30:11.043 -06:00 [INF] Beginning certificate request process: certify.hourielabs.click using ACME provider Anvil
2026-06-27 18:30:11.045 -06:00 [INF] The selected Certificate Authority is: Let's Encrypt
2026-06-27 18:30:11.047 -06:00 [INF] Requested identifiers to include on certificate: certify.hourielabs.click
2026-06-27 18:30:11.952 -06:00 [INF] Created ACME Order: https://acme-staging-v02.api.letsencrypt.org/acme/order/306988084/42545475244
2026-06-27 18:30:13.235 -06:00 [INF] Got http-01 challenge https://acme-staging-v02.api.letsencrypt.org/acme/chall/306988084/2379558924/4_vihg
2026-06-27 18:30:13.303 -06:00 [INF] Got dns-01 challenge https://acme-staging-v02.api.letsencrypt.org/acme/chall/306988084/2379558924/l3yUiw
2026-06-27 18:30:13.876 -06:00 [INF] Preparing automated challenge responses for: certify.hourielabs.click
2026-06-27 18:30:13.968 -06:00 [INF] Route53 :: Initialized with assumed role arn:aws:iam::051259******:role/CertifyRoute53
2026-06-27 18:30:13.969 -06:00 [INF] DNS: Creating TXT Record '_acme-challenge.certify.hourielabs.click' with value 'sBwCF3BVb69w0XkEEyfw-8TF41VRkFTJl**********', [certify.hourielabs.click] in ZoneId '/hostedzone/Z0625566148**********' using API provider 'Amazon Route 53 DNS API'
2026-06-27 18:30:41.737 -06:00 [INF] DNS change completed.
2026-06-27 18:30:41.738 -06:00 [INF] DNS: Amazon Route 53 DNS API :: Dns Record Created/Updated: _acme-challenge.certify.hourielabs.click
2026-06-27 18:31:42.365 -06:00 [INF] Resuming certificate request using CA: Let's Encrypt
2026-06-27 18:31:42.366 -06:00 [INF] Attempting challenge response validation for: certify.hourielabs.click
2026-06-27 18:31:42.367 -06:00 [INF] [Progress] Checking automated challenge response for: certify.hourielabs.click
2026-06-27 18:31:42.368 -06:00 [INF] Submitting challenge for validation: certify.hourielabs.click 
2026-06-27 18:31:46.811 -06:00 [INF] [Progress] Validation completed: certify.hourielabs.click
2026-06-27 18:31:47.083 -06:00 [INF] Route53 :: Initialized with assumed role arn:aws:iam::051259******::role/CertifyRoute53
2026-06-27 18:31:47.084 -06:00 [INF] DNS: Deleting TXT Record '_acme-challenge.certify.hourielabs.click' :'sBwCF3BVb69w0XkEEyfw-8TF41VRkFTJl**********', [certify.hourielabs.click] in ZoneId '/hostedzone/Z0625566148**********' using API provider 'Amazon Route 53 DNS API'
2026-06-27 18:31:47.409 -06:00 [INF] Route53 :: Delete Record : Zone matched /hostedzone/Z0625566148********** /hostedzone/Z0625566148********** : Fetching TXT record set _acme-challenge.certify.hourielabs.click 
2026-06-27 18:31:47.498 -06:00 [INF] Route53 :: Delete Record : Fetched TXT record set OK _acme-challenge.certify.hourielabs.click. 
2026-06-27 18:32:10.104 -06:00 [INF] DNS change completed.
2026-06-27 18:32:10.105 -06:00 [INF] [Progress] Requesting certificate via Certificate Authority
2026-06-27 18:32:15.389 -06:00 [INF] [Progress] Completed certificate request.
2026-06-27 18:32:15.419 -06:00 [INF] [Progress] Performing automated certificate binding
2026-06-27 18:32:16.487 -06:00 [INF] CertificateStorage: Certificate Stored - Certificate stored OK
2026-06-27 18:32:16.488 -06:00 [INF] Completed certificate request and automated binding updates
2026-06-27 18:32:16.489 -06:00 [INF] [Progress] New certificate received and standard deployment performed OK.
2026-06-27 18:32:35.009 -06:00 [INF] Previewing Certificate Deployment: certify.hourielabs.click
2026-06-27 18:32:35.012 -06:00 [INF] CertificateStorage: Certificate Storage - Certificate will be stored in the computer certificate store [My]
2026-06-27 18:32:35.013 -06:00 [INF] [Preview Mode] Completed certificate request and automated binding updates
2026-06-27 18:54:06.914 -06:00 [INF] Previewing Certificate Deployment: certify.hourielabs.click
2026-06-27 18:54:06.917 -06:00 [INF] CertificateStorage: Certificate Storage - Certificate will be stored in the computer certificate store [My]
2026-06-27 18:54:06.918 -06:00 [INF] [Preview Mode] Completed certificate request and automated binding updates

Use STS AssumeRole so Certify can update Route 53 hosted zones in a different AWS account from stored IAM credentials. Assume-role settings (role ARN, session name, optional external ID) live in stored credentials; certificates keep zone ID and propagation delay only.

Includes credential UI with conditional field visibility, DNS provider credential detection fixes for API JSON deserialization, MSDNS dropdown freeze guard, SDK pinning for AWSSDK.SecurityToken, and unit tests for parameter classification.
@CLAassistant

CLAassistant commented Jun 28, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@webprofusion-chrisc

Copy link
Copy Markdown
Contributor

Thanks for your proposal, it's much appreciated.

A few things:

  • If your change is produced by an agent it's usually better to ask us via support for the feature to be considered because we can prompt it ourselves and save you the effort
  • Your change should only touch the AWS Route53 provider, every other part of the change is fixing stuff that's unrelated or working around perceived problems.
  • Consider if adopting the Posh-ACME Route53 provider might solve this problem: https://poshac.me/docs/latest/Plugins/Route53/ if so, we can support using that.
  • The core premise of the change seems to be preventing the need to store DNS credentials on each install. Our Certify Management Hub product solves this problem across all DNS providers with Managed Challenges and/or Certificate Subscriptions.

@VA6DAH

VA6DAH commented Jun 29, 2026

Copy link
Copy Markdown
Author

Hey Chris,

Yes. This was written with curser. My initial attempt only touched the route 53 provider but that the change caused the provider to disappear entirely from the list. The only time I got it to work without touching other components all the new fields appeared in the DNS-01 configuration on the certificate config instead of within the credential config.

  • Posh-ACME does work well and I could see this resolving my use case.
  • In some cases , line of sight to the Certify Hub isn't possible, or should still support temporary credentials.
  • One thing that would also be lovely to see is support for no credentials if the application is deployed directly on a EC2 instance.
  • I would be happy to either convert this into a feature request or a discussion. I am happy to test and assist documenting more complex AWS integrations.

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.

3 participants