-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Awkward API to get token and value for http challenge #8
Comments
For what it's worth, I think you have everything you need in ChallengeDescription. Here is what I use in my deployment: struct ChallengeKey: Hashable {
var host: String
var path: String
}
extension ChallengeKey {
init(url urlString: String) throws {
guard
let url = URL(string: urlString),
let host = url.host()
else { throw CertificateRenewalError.invalidURL(urlString) }
self.host = host
self.path = url.path(percentEncoded: true)
}
}
actor CertificateManager {
// ...
var activeChallenges: [ChallengeKey : ChallengeDescription] = [:]
func refresh() async throws -> Date {
// ...
let pendingChallenges = try await acmeClient.orders.describePendingChallenges(from: certificateOrder, preferring: .http)
defer { activeChallenges = [:] }
activeChallenges = [:]
logger.notice("Awaiting verifications for order:")
for challenge in pendingChallenges {
guard case .http = challenge.type else {
logger.warning("Unknown type encountered: \(challenge.type)")
continue
}
logger.notice(" • The URL \(challenge.endpoint) needs to return \(challenge.value)")
activeChallenges[try ChallengeKey(url: challenge.endpoint)] = challenge
}
var failedChallenges = try await acmeClient.orders.validateChallenges(from: certificateOrder, preferring: .http)
for timeout in [5, 10, 10, 10, 30] {
guard !failedChallenges.isEmpty else { break }
logger.notice("\(failedChallenges.count) challenges remain, trying again in \(timeout) seconds.")
try await Task.sleep(for: .seconds(timeout))
failedChallenges = try await acmeClient.orders.validateChallenges(from: certificateOrder, preferring: .http)
}
guard failedChallenges.isEmpty else {
logger.error("Certificate order validations failed: \(failedChallenges)")
throw CertificateRenewalError.failedToValidate(failedChallenges)
}
logger.notice("Finished validating orders. Downloading certificates.")
let (privateKey, _, finalizedOrders) = try await acmeClient.orders.finalizeWithEcdsa(order: certificateOrder, domains: domains)
// ...
}
}
actor HTTPApplication {
// ...
@Sendable
func redirect(request: Request) async throws -> Response {
guard let host = request.headers[.host].first, validDomains.contains(host)
else { return request.redirect(to: "https://\(request.application.environment.certificateDomains[0])\(request.url)", redirectType: .temporary) }
// If we have active challenges to fulfill, go ahead and check if any requests match, and return the challenge value
if let activeChallenges = await certificateManager?.activeChallenges, !activeChallenges.isEmpty {
let key = ChallengeKey(host: host, path: request.url.path)
request.logger.notice("Checking challenge for \(key) in \(activeChallenges.keys)")
if let challenge = activeChallenges[key] {
request.logger.notice("Returning for \(key): \(challenge.value)")
return Response(status: .ok, body: .init(string: challenge.value))
}
}
// Otherwise redirect to HTTPS
return request.redirect(to: "https://\(host)\(request.url)", redirectType: .temporary)
}
} (Edit: I guess we can avoid the URL parsing, though this doesn't need to be done in a hot path, so the above works fine for me) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
tl;dr: Should we add the
token
to theChallengeDescription
type?First: Thank you for this package!
When I stumbled over the need to self-manage public certificates I couldn't believe my luck: There is a swift package for ACME!
A tiny little thing I think could be improved:
Currently, you can either call
acme.orders.getAuthorizations(from: order)
to get the "raw data", or use theacme.orders.describePendingChallenges(from: order, preferring: .http)
API to get the little value encoding dance for free.Getting the correct value for the challenges is clearly nicer, but on
ChallengeDescription
you can not access thetoken
directly.Based on how you'd implement "placing" the http value on you server, you'll now have to extract it out of the
endpoint
URL again.Additionally, we could also move the get me the value for this challenge code to some reusable place to decouple it from the
describePendingChallenges
API.The text was updated successfully, but these errors were encountered: