Files
Charon/plugins/powerdns/main.go
GitHub Actions b86aa3921b feat(dns): add custom DNS provider plugin system
- Add plugin interface with lifecycle hooks (Init/Cleanup)
- Implement thread-safe provider registry
- Add plugin loader with SHA-256 signature verification
- Migrate 10 built-in providers to registry pattern
- Add multi-credential support to plugin interface
- Create plugin management UI with enable/disable controls
- Add dynamic credential fields based on provider metadata
- Include PowerDNS example plugin
- Add comprehensive user & developer documentation
- Fix frontend test hang (33min → 1.5min, 22x faster)

Platform: Linux/macOS only (Go plugin limitation)
Security: Signature verification, directory permission checks

Backend coverage: 85.1%
Frontend coverage: 85.31%

Closes: DNS Challenge Future Features - Phase 5
2026-01-07 02:54:01 +00:00

143 lines
3.5 KiB
Go

package main
import (
"fmt"
"net/http"
"runtime"
"time"
"github.com/Wikid82/charon/backend/pkg/dnsprovider"
)
// Plugin is the exported symbol that Charon looks for.
var Plugin dnsprovider.ProviderPlugin = &PowerDNSProvider{}
// PowerDNSProvider implements the ProviderPlugin interface for PowerDNS.
type PowerDNSProvider struct{}
func (p *PowerDNSProvider) Type() string {
return "powerdns"
}
func (p *PowerDNSProvider) Metadata() dnsprovider.ProviderMetadata {
return dnsprovider.ProviderMetadata{
Type: "powerdns",
Name: "PowerDNS",
Description: "PowerDNS Authoritative Server with HTTP API",
DocumentationURL: "https://doc.powerdns.com/authoritative/http-api/",
Author: "Charon Community",
Version: "1.0.0",
IsBuiltIn: false,
GoVersion: runtime.Version(),
InterfaceVersion: dnsprovider.InterfaceVersion,
}
}
func (p *PowerDNSProvider) Init() error {
return nil
}
func (p *PowerDNSProvider) Cleanup() error {
return nil
}
func (p *PowerDNSProvider) RequiredCredentialFields() []dnsprovider.CredentialFieldSpec {
return []dnsprovider.CredentialFieldSpec{
{
Name: "api_url",
Label: "API URL",
Type: "text",
Placeholder: "https://pdns.example.com:8081",
Hint: "PowerDNS HTTP API endpoint",
},
{
Name: "api_key",
Label: "API Key",
Type: "password",
Placeholder: "Your PowerDNS API key",
Hint: "X-API-Key header value",
},
}
}
func (p *PowerDNSProvider) OptionalCredentialFields() []dnsprovider.CredentialFieldSpec {
return []dnsprovider.CredentialFieldSpec{
{
Name: "server_id",
Label: "Server ID",
Type: "text",
Placeholder: "localhost",
Hint: "PowerDNS server ID (default: localhost)",
},
}
}
func (p *PowerDNSProvider) ValidateCredentials(creds map[string]string) error {
if creds["api_url"] == "" {
return fmt.Errorf("api_url is required")
}
if creds["api_key"] == "" {
return fmt.Errorf("api_key is required")
}
return nil
}
func (p *PowerDNSProvider) TestCredentials(creds map[string]string) error {
if err := p.ValidateCredentials(creds); err != nil {
return err
}
// Test API connectivity
serverID := creds["server_id"]
if serverID == "" {
serverID = "localhost"
}
url := fmt.Sprintf("%s/api/v1/servers/%s", creds["api_url"], serverID)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("X-API-Key", creds["api_key"])
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("API connection failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("API returned status %d", resp.StatusCode)
}
return nil
}
func (p *PowerDNSProvider) SupportsMultiCredential() bool {
return false
}
func (p *PowerDNSProvider) BuildCaddyConfig(creds map[string]string) map[string]any {
serverID := creds["server_id"]
if serverID == "" {
serverID = "localhost"
}
return map[string]any{
"name": "powerdns",
"api_url": creds["api_url"],
"api_key": creds["api_key"],
"server_id": serverID,
}
}
func (p *PowerDNSProvider) BuildCaddyConfigForZone(baseDomain string, creds map[string]string) map[string]any {
return p.BuildCaddyConfig(creds)
}
func (p *PowerDNSProvider) PropagationTimeout() time.Duration {
return 60 * time.Second
}
func (p *PowerDNSProvider) PollingInterval() time.Duration {
return 2 * time.Second
}
func main() {}