This project implements a libdns provider for Websupport DNS.
Implements the libdns interfaces for Websupport's DNS API so you can manage TXT records for ACME DNS-01 and other use cases.
Important: This repo includes a Windows-friendly test app (main.go) that can:
- Create/delete TXT records via Websupport API
- Generate a self‑signed certificate for
libdns.example.comfor local testing - Simulate an ACME DNS‑01 workflow (create/verify/cleanup)
You may notice imports like github.com/libdns/websupport/websupport (double websupport).
This is because the provider implementation lives in the websupport/ subdirectory of the repository.
- The module path is
github.com/libdns/websupport(the repository root). - The package that implements the provider is in the
websupportsubfolder, so the full import path becomesgithub.com/libdns/websupport/websupport.
If you prefer a single-segment import (for example github.com/libdns/websupport), we can reorganize the repository so the provider package is at the repository root and move the test application into cmd/ (recommended). Let me know if you want me to do that.
- ACME DNS-01 Support: Solve DNS challenges for Let's Encrypt and other ACME providers
- Full libdns Interface: Implements
RecordAppender,RecordDeleter, andRecordGetterinterfaces - TXT Record Management: Create, retrieve, and delete DNS TXT records
- Basic Authentication: Secure API communication using Websupport API credentials
- Context Support: Full context cancellation support for timeouts and cancellations
To use this provider in your project, add it as a dependency:
go get github.com/libdns/websupportAlternatively, clone the repository:
git clone https://github.com/libdns/websupport.git
cd websupport
go mod downloadpackage main
import (
"context"
"github.com/libdns/libdns"
"github.com/libdns/websupport/websupport"
"time"
)
func main() {
// Create a provider with your Websupport credentials
// SECURITY: Do NOT hardcode real API keys. Prefer environment variables.
provider := &websupport.Provider{
APIKey: os.Getenv("WEBSUPPORT_API_KEY"),
APISecret: os.Getenv("WEBSUPPORT_API_SECRET"),
APIBase: "https://rest.websupport.sk/v2",
}
ctx := context.Background()
zone := "example.com"
// Create a TXT record for ACME challenge
records := []libdns.Record{
&libdns.TXT{
Name: "_acme-challenge",
Text: "challenge-value",
TTL: 120 * time.Second,
},
}
// Append records
created, err := provider.AppendRecords(ctx, zone, records)
if err != nil {
panic(err)
}
// ... use created records ...
// Delete records when done
deleted, err := provider.DeleteRecords(ctx, zone, created)
if err != nil {
panic(err)
}
}You can configure the provider using environment variables:
export WEBSUPPORT_API_KEY="your-api-key"
export WEBSUPPORT_API_SECRET="your-api-secret"
export WEBSUPPORT_SERVICE_ID="your-service-id" # Required: Numeric ID for your domain
export WEBSUPPORT_TEST_ZONE="example.com" # Your domain name (not subdomain)Important Notes:
WEBSUPPORT_TEST_ZONEis your root domain (e.g.,example.com), NOT a subdomainWEBSUPPORT_SERVICE_IDis required - this is the numeric ID for your domain- When creating records for subdomains like
test.example.com, useName: "test"in the record
Why is WEBSUPPORT_SERVICE_ID required?
The Websupport REST API v2 uses service-based endpoints (/v2/service/{id}/dns/record) rather than domain-based endpoints. The API does not provide a working endpoint to automatically discover service IDs from domain names, so you must provide it manually.
How to find your Service ID:
- Log in to Websupport Admin Panel
- Click on your domain from the services list
- Look at the URL in your browser address bar
- The service ID is the number at the end of the URL
Example: https://admin.websupport.sk/en/dashboard/service/1234567 → Service ID is 1234567
To test the provider with your credentials:
# Build the project
go build .
# Set environment variables
export WEBSUPPORT_API_KEY="your-api-key"
export WEBSUPPORT_API_SECRET="your-api-secret"
export WEBSUPPORT_SERVICE_ID="your-service-id"
export WEBSUPPORT_TEST_ZONE="your-domain.com"
# Run tests
./libdns-websupport testComplete test command example:
cd /path/to/websupport && \
go build . && \
export WEBSUPPORT_API_KEY="your-api-key" && \
export WEBSUPPORT_API_SECRET="your-api-secret" && \
export WEBSUPPORT_TEST_ZONE="example.com" && \
export WEBSUPPORT_SERVICE_ID="1234567" && \
./libdns-websupport testtype Provider struct {
APIKey string // Websupport API Key
APISecret string // Websupport API Secret
APIBase string // API Base URL (default: https://rest.websupport.sk/v2)
ServiceID string // Service ID for the domain (required)
HTTPClient *http.Client // Custom HTTP client (optional)
Timeout time.Duration // Request timeout (default: 30s)
}Creates DNS records in the zone. Used for adding ACME challenge records.
func (p *Provider) AppendRecords(ctx context.Context, zone string, recs []libdns.Record) ([]libdns.Record, error)- Parameters:
ctx: Context for cancellation and timeoutszone: Domain name (e.g., "example.com")recs: Records to create (typicallylibdns.TXTrecords)
- Returns: Created records with populated IDs and any errors
Removes DNS records from the zone by ID.
func (p *Provider) DeleteRecords(ctx context.Context, zone string, recs []libdns.Record) ([]libdns.Record, error)- Parameters:
ctx: Context for cancellation and timeoutszone: Domain namerecs: Records to delete (must have valid IDs from creation)
- Returns: Deleted records and any errors
Retrieves all DNS records from the zone.
func (p *Provider) GetRecords(ctx context.Context, zone string) ([]libdns.Record, error)- Parameters:
ctx: Context for cancellation and timeoutszone: Domain name
- Returns: All TXT records in the zone and any errors
package main
import (
"context"
"fmt"
"time"
"github.com/libdns/libdns"
"github.com/libdns/websupport/websupport"
)
func main() {
provider := &websupport.Provider{
APIKey: os.Getenv("WEBSUPPORT_API_KEY"),
APISecret: os.Getenv("WEBSUPPORT_API_SECRET"),
APIBase: "https://rest.websupport.sk/v2",
}
ctx := context.Background()
zone := "example.com"
// Step 1: Create challenge record
challengeRecord := &libdns.TXT{
Name: "_acme-challenge",
Text: "your-challenge-token",
TTL: 120 * time.Second,
}
created, err := provider.AppendRecords(ctx, zone, []libdns.Record{challengeRecord})
if err != nil {
fmt.Printf("Failed to create record: %v\n", err)
return
}
fmt.Printf("Created record: %+v\n", created[0])
// Step 2: Wait for DNS propagation
time.Sleep(5 * time.Second)
// Step 3: Verify record exists
records, err := provider.GetRecords(ctx, zone)
if err != nil {
fmt.Printf("Failed to get records: %v\n", err)
return
}
fmt.Printf("Found %d records\n", len(records))
// Step 4: Clean up
deleted, err := provider.DeleteRecords(ctx, zone, created)
if err != nil {
fmt.Printf("Failed to delete record: %v\n", err)
return
}
fmt.Printf("Deleted %d records\n", len(deleted))
}This provider can be integrated with Caddy's DNS plugin system via CertMagic:
{
"apps": {
"tls": {
"automation": {
"policies": [
{
"issuers": [
{
"module": "acme",
"challenges": {
"dns": {
"provider": {
"name": "websupport",
"api_key": "${WEBSUPPORT_API_KEY}",
"api_secret": "${WEBSUPPORT_API_SECRET}"
}
}
}
}
]
}
]
}
}
}
}The project includes a comprehensive test application that allows you to validate the DNS provider functionality and generate test certificates.
Environment variables used by the test app:
WEBSUPPORT_API_KEY— Your Websupport API key (required)WEBSUPPORT_API_SECRET— Your Websupport API secret (required)WEBSUPPORT_SERVICE_ID— Numeric service ID for your domain (required)WEBSUPPORT_TEST_ZONE— Your root domain (e.g.,example.com) - NOT a subdomainWEBSUPPORT_TEST_DOMAIN— FQDN for cert/tests (default:libdns.example.com)
Important: WEBSUPPORT_TEST_ZONE should be your root domain like example.com, not a subdomain like test.example.com.
The test application supports three commands:
Tests creating, retrieving, and deleting DNS records:
Linux/Mac:
export WEBSUPPORT_API_KEY="your-api-key"
export WEBSUPPORT_API_SECRET="your-api-secret"
export WEBSUPPORT_SERVICE_ID="1234567"
export WEBSUPPORT_TEST_ZONE="example.com"
./libdns-websupport testWindows:
$env:WEBSUPPORT_API_KEY = "your-api-key"
$env:WEBSUPPORT_API_SECRET = "your-api-secret"
$env:WEBSUPPORT_SERVICE_ID = "1234567"
$env:WEBSUPPORT_TEST_ZONE = "example.com"
.\libdns-websupport.exe testThis will:
- Create a test TXT record
- Retrieve all records from your zone
- Delete the test record
- Display success/failure information
Generates a self-signed certificate for local testing purposes. This is NOT a real Let's Encrypt certificate and will show security warnings in browsers.
Linux/Mac:
./libdns-websupport create-certWindows:
.\libdns-websupport.exe create-certThis will:
- Generate a 2048-bit RSA private key
- Create a self-signed certificate (NOT trusted by browsers, for testing only)
- Certificate is valid for 1 year
- Save certificate to:
~/.caddy/certificates/libdns.example.com.crt(Linux/Mac) orC:\Users\<YourUsername>\.caddy\certificates\libdns.example.com.crt(Windows) - Save private key to:
~/.caddy/certificates/libdns.example.com.key(Linux/Mac) orC:\Users\<YourUsername>\.caddy\certificates\libdns.example.com.key(Windows)
Important: This creates a self-signed certificate for testing purposes only. To get real, trusted SSL/TLS certificates, see the "Obtaining Real Let's Encrypt Certificates" section below.
Simulates a complete ACME DNS-01 challenge workflow WITHOUT contacting Let's Encrypt. This tests that the DNS provider can create and clean up challenge records correctly.
Linux/Mac:
export WEBSUPPORT_API_KEY="your-api-key"
export WEBSUPPORT_API_SECRET="your-api-secret"
export WEBSUPPORT_SERVICE_ID="1234567"
export WEBSUPPORT_TEST_ZONE="example.com"
./libdns-websupport acme-testWindows:
$env:WEBSUPPORT_API_KEY = "your-api-key"
$env:WEBSUPPORT_API_SECRET = "your-api-secret"
$env:WEBSUPPORT_SERVICE_ID = "1234567"
$env:WEBSUPPORT_TEST_ZONE = "example.com"
.\libdns-websupport.exe acme-testThis will:
- Create a DNS challenge record (
_acme-challengeTXT record) - Wait for DNS propagation
- Verify the record via public DNS lookup
- Retrieve records from the API
- Clean up the challenge record
Important: This command only simulates the ACME workflow for testing purposes. It does NOT contact Let's Encrypt and does NOT issue a real certificate. See the section below for obtaining real certificates.
None of the built-in test commands (test, create-cert, acme-test) obtain real Let's Encrypt certificates. They are only for testing the DNS provider functionality.
To obtain real, trusted SSL/TLS certificates from Let's Encrypt for your domain or subdomains, you need to use this provider with an ACME client.
This provider works with any ACME client that supports the libdns interface:
- Caddy - Automatic HTTPS server (easiest option)
- Traefik - Reverse proxy with automatic HTTPS
- Certbot - Official Let's Encrypt client
- acme.sh - Shell script ACME client
- Lego - Go-based ACME client
When obtaining certificates for subdomains like test.example.com:
- Set
WEBSUPPORT_TEST_ZONEto your root domain (e.g.,example.com) - The ACME challenge will create
_acme-challenge.test.example.comautomatically - The provider will create the TXT record with
Name: "_acme-challenge.test"in your root domain
Example for subdomain certificate:
export WEBSUPPORT_API_KEY="your-api-key"
export WEBSUPPORT_API_SECRET="your-api-secret"
export WEBSUPPORT_SERVICE_ID="1234567"
export WEBSUPPORT_TEST_ZONE="example.com" # Root domain, NOT subdomain
# In your ACME client configuration, request cert for:
# - test.example.com
# - *.example.com (wildcard)
# - example.com (root)Traefik can use this provider for automatic certificate generation. Example configuration:
certificatesResolvers:
letsencrypt:
acme:
email: [email protected]
storage: /acme.json
dnsChallenge:
provider: websupport
resolvers:
- "1.1.1.1:53"
- "8.8.8.8:53"
# Environment variables for Traefik:
# WEBSUPPORT_API_KEY=your-api-key
# WEBSUPPORT_API_SECRET=your-api-secret
# WEBSUPPORT_SERVICE_ID=1234567Caddy can automatically obtain certificates using this DNS provider. You'll need to build Caddy with the Websupport DNS module or use the libdns interface directly.
Linux/Mac:
git clone https://github.com/libdns/websupport.git
cd websupport
go build .Windows:
git clone https://github.com/libdns/websupport.git
cd websupport
go build ..\libdns-websupport.exe acme-test
---
## Testing (Linux)
The test app works the same on Linux. Replace PowerShell with bash and note that files are written to `~/.caddy/certificates`.
### Test Commands
Environment variables used by the test app (optional but recommended):
- `WEBSUPPORT_TEST_ZONE` — your zone (default: `example.com`)
- `WEBSUPPORT_TEST_DOMAIN` — FQDN for cert/tests (default: `libdns.example.com`)
#### 1. Basic DNS Operations Test
```bash
export WEBSUPPORT_API_KEY="your-api-key"
## Task Runners
### Linux/macOS (Makefile)
Common tasks:
```bash
# Build binary
make build
# Run locally
make run
# Run tests
make test
# Create self-signed certificate (writes to ~/.caddy/certificates)
make cert
# DNS operations test (requires API env vars)
make dns-test
# ACME simulation (requires API env vars)
make acme-test
Use the provided make.ps1 script:
export WEBSUPPORT_API_SECRET="your-api-secret"
./libdns-websupport test./libdns-websupport create-cert
ls -l ~/.caddy/certificates/libdns.example.com.*Expected files:
~/.caddy/certificates/libdns.example.com.crt~/.caddy/certificates/libdns.example.com.key
export WEBSUPPORT_API_KEY="your-api-key"
export WEBSUPPORT_API_SECRET="your-api-secret"
./libdns-websupport acme-testgit clone https://github.com/goozoon/libdns-websupport.git
cd libdns-websupport
go build .go run .libdns-websupport/
├── go.mod # Go module definition
├── go.sum # Go module checksums
├── main.go # Test application
├── readme.md # This file
└── websupport/
└── provider.go # libdns provider implementation
go build ./websupportgo run .go test ./...- Credentials: Never hardcode API credentials. Use environment variables or a secrets vault.
- HTTPS: All API calls use HTTPS for secure communication
- Basic Auth: Credentials are sent via HTTP Basic Authentication
- Rate Limiting: Be mindful of Websupport's API rate limits when managing records
- GitHub Safety: Before publishing, search the repo for your domain or secrets and remove any accidental commits.
- Local Testing: The
create-certcommand generates a self‑signed cert; do not use it in production.
This project is open source and available under the MIT License.
Contributions are welcome! Please feel free to submit pull requests or open issues for bugs and feature requests.
For issues, questions, or suggestions, please open an issue on GitHub.