2023-01-12 16:55:05 +01:00
|
|
|
package tlstunnel
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"os/exec"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/caddyserver/certmagic"
|
|
|
|
"github.com/libdns/libdns"
|
|
|
|
)
|
|
|
|
|
|
|
|
type commandDNSProvider struct {
|
|
|
|
Name string
|
|
|
|
Params []string
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ certmagic.ACMEDNSProvider = (*commandDNSProvider)(nil)
|
|
|
|
|
|
|
|
func (provider *commandDNSProvider) exec(ctx context.Context, subcmd string, subargs ...string) error {
|
|
|
|
var params []string
|
|
|
|
params = append(params, provider.Params...)
|
|
|
|
params = append(params, subcmd)
|
|
|
|
params = append(params, subargs...)
|
|
|
|
cmd := exec.CommandContext(ctx, provider.Name, params...)
|
|
|
|
|
|
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
|
|
details := ""
|
|
|
|
if len(out) > 0 {
|
|
|
|
details = ": " + string(out)
|
|
|
|
}
|
|
|
|
return fmt.Errorf("failed to run DNS hook %v (%w)%v", subcmd, err, details)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (provider *commandDNSProvider) processRecords(ctx context.Context, zone string, recs []libdns.Record, subcmd string) ([]libdns.Record, error) {
|
|
|
|
var (
|
|
|
|
done []libdns.Record
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
for _, rec := range recs {
|
|
|
|
var domain string
|
|
|
|
if domain, err = domainFromACMEChallengeRecord(zone, &rec); err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if err = provider.exec(ctx, subcmd, domain, "-", rec.Value); err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
done = append(done, rec)
|
|
|
|
}
|
|
|
|
return done, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (provider *commandDNSProvider) AppendRecords(ctx context.Context, zone string, recs []libdns.Record) ([]libdns.Record, error) {
|
|
|
|
return provider.processRecords(ctx, zone, recs, "deploy_challenge")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (provider *commandDNSProvider) DeleteRecords(ctx context.Context, zone string, recs []libdns.Record) ([]libdns.Record, error) {
|
|
|
|
return provider.processRecords(ctx, zone, recs, "clean_challenge")
|
|
|
|
}
|
|
|
|
|
|
|
|
func domainFromACMEChallengeRecord(zone string, rec *libdns.Record) (string, error) {
|
2023-01-26 19:14:08 +01:00
|
|
|
relZone := strings.TrimSuffix(zone, ".")
|
|
|
|
|
|
|
|
var domain string
|
|
|
|
if rec.Name == "_acme-challenge" {
|
|
|
|
// Root domain
|
|
|
|
domain = relZone
|
|
|
|
} else if strings.HasPrefix(rec.Name, "_acme-challenge.") {
|
|
|
|
// Subdomain
|
|
|
|
relName := strings.TrimPrefix(rec.Name, "_acme-challenge.")
|
|
|
|
domain = relName + "." + relZone
|
|
|
|
}
|
|
|
|
if rec.Type != "TXT" || domain == "" {
|
2023-01-12 16:55:05 +01:00
|
|
|
return "", fmt.Errorf("DNS record doesn't look like an ACME challenge: %v %v", rec.Type, rec.Name)
|
|
|
|
}
|
2023-01-26 19:14:08 +01:00
|
|
|
|
|
|
|
return domain, nil
|
2023-01-12 16:55:05 +01:00
|
|
|
}
|