Skip to content

Commit 5c61ce9

Browse files
authored
Merge pull request #392 from DefangLabs/edw-cert-gen-ns-check
Query the NS servers of the domain until insync for cert gen DNS check
2 parents dc53ca5 + 971bae9 commit 5c61ce9

File tree

1 file changed

+78
-12
lines changed

1 file changed

+78
-12
lines changed

src/pkg/cli/cert.go

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,36 +38,35 @@ func GenerateLetsEncryptCert(ctx context.Context, client cliClient.Client) error
3838
}
3939
}
4040
if cnt == 0 {
41-
term.Infof("No services found need to generate Let's Encrypt cert")
41+
term.Infof(" * No services found need to generate Let's Encrypt cert")
4242
}
4343

4444
return nil
4545
}
4646

4747
func generateCert(ctx context.Context, domain, albDns string) {
48-
term.Infof("Triggering Let's Encrypt cert generation for %v", domain)
48+
term.Infof(" * Triggering Let's Encrypt cert generation for %v", domain)
4949
if err := waitForCNAME(ctx, domain, albDns); err != nil {
5050
term.Errorf("Error waiting for CNAME: %v", err)
5151
return
5252
}
5353

54-
term.Infof("%v DNS is properly configured!", domain)
54+
term.Infof(" * %v DNS is properly configured!", domain)
5555
if err := checkTLSCert(ctx, domain); err == nil {
56-
term.Infof("TLS cert for %v is already ready", domain)
56+
term.Infof(" * TLS cert for %v is already ready", domain)
5757
return
5858
}
59-
term.Infof("Triggering cert generation for %v", domain)
59+
term.Infof(" * Triggering cert generation for %v", domain)
6060
triggerCertGeneration(ctx, domain)
6161

62-
term.Infof("Waiting for TLS cert to be online for %v", domain)
62+
term.Infof(" * Waiting for TLS cert to be online for %v", domain)
6363
if err := waitForTLS(ctx, domain); err != nil {
6464
term.Errorf("Error waiting for TLS to be online: %v", err)
65-
// FIXME: The message below is only valid for BYOC, need to update when playground ACME cert support is added
66-
term.Errorf("Please check for error messages from `/aws/lambda/acme-lambda` log group in cloudwatch for more details")
65+
// FIXME: Add more info on how to debug, possibly provided by the server side to avoid client type detection here
6766
return
6867
}
6968

70-
term.Infof("TLS cert for %v is ready", domain)
69+
fmt.Printf("TLS cert for %v is ready\n", domain)
7170
}
7271

7372
func triggerCertGeneration(ctx context.Context, domain string) {
@@ -141,12 +140,13 @@ func waitForCNAME(ctx context.Context, domain, albDns string) error {
141140
case <-ctx.Done():
142141
return ctx.Err()
143142
case <-ticker.C:
144-
cname, err := resolver.LookupCNAME(ctx, domain)
143+
cname, err := waitForCNAMEInSync(ctx, domain)
145144
cname = strings.TrimSuffix(cname, ".")
146145
if err != nil || strings.ToLower(cname) != strings.ToLower(albDns) {
147146
if !msgShown {
148-
term.Infof("Please setup CNAME record for %v to point to ALB %v, waiting for CNAME record setup and DNS propagation", domain, strings.ToLower(albDns))
149-
term.Infof("Note: DNS propagation may take a while, we will proceed as soon as the CNAME record is ready, checking...")
147+
term.Infof(" * Please setup CNAME record for %v", domain)
148+
fmt.Printf(" %v CNAME %v\n", domain, strings.ToLower(albDns))
149+
term.Infof(" * Waiting for CNAME record setup and DNS propagation...")
150150
msgShown = true
151151
}
152152
if doSpinner {
@@ -185,3 +185,69 @@ func getWithRetries(ctx context.Context, url string, tries int) error {
185185
}
186186
return errors.Join(errs...)
187187
}
188+
189+
func waitForCNAMEInSync(ctx context.Context, domain string) (string, error) {
190+
ns, err := getNSServers(ctx, domain)
191+
if err != nil {
192+
return "", err
193+
}
194+
195+
ticker := time.NewTicker(1 * time.Second)
196+
defer ticker.Stop()
197+
for {
198+
select {
199+
case <-ticker.C:
200+
cnames := make(map[string]bool)
201+
var cname string
202+
var err error
203+
for _, n := range ns {
204+
cname, err = resolverAt(n).LookupCNAME(context.Background(), domain)
205+
if err != nil {
206+
term.Debugf(" - Error looking up CNAME for %v at %v: %v", domain, n, err)
207+
}
208+
cnames[cname] = true
209+
}
210+
if len(cnames) > 1 {
211+
continue
212+
}
213+
return cname, err
214+
case <-ctx.Done():
215+
return "", ctx.Err()
216+
}
217+
}
218+
}
219+
220+
func getNSServers(ctx context.Context, domain string) ([]string, error) {
221+
d := domain
222+
var ns []*net.NS
223+
for {
224+
var err error
225+
ns, err = resolver.LookupNS(ctx, d)
226+
var ne *net.DNSError
227+
if errors.As(err, &ne) {
228+
if strings.Count(d, ".") <= 1 {
229+
return nil, fmt.Errorf("No DNS server found")
230+
}
231+
d = d[strings.Index(domain, ".")+1:]
232+
continue
233+
} else if err != nil {
234+
return nil, fmt.Errorf("Failed to find NS server for %v at %v: %v", domain, d, err)
235+
}
236+
break
237+
}
238+
servers := make([]string, len(ns))
239+
for i, n := range ns {
240+
servers[i] = n.Host
241+
}
242+
return servers, nil
243+
}
244+
245+
func resolverAt(nsServer string) *net.Resolver {
246+
return &net.Resolver{
247+
PreferGo: true,
248+
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
249+
d := net.Dialer{}
250+
return d.DialContext(ctx, network, nsServer+":53")
251+
},
252+
}
253+
}

0 commit comments

Comments
 (0)