Skip to content

Commit

Permalink
feat: Add setting for editing only allowed zones (#29)
Browse files Browse the repository at this point in the history
* feat: Add setting for editing only allowed zones

* docs: Update README to underscore the need for fully qualified names

* fix: Fix automated tests

---------

Co-authored-by: Zachary Seguin <[email protected]>
  • Loading branch information
msiebuhr and zachomedia authored Mar 16, 2023
1 parent ccc7a6a commit b138036
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 1 deletion.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ clean:
go clean -testcache

verify:
TEST_ASSET_ETCD=_out/kubebuilder/bin/etcd TEST_ASSET_KUBE_APISERVER=_out/kubebuilder/bin/kube-apiserver TEST_ASSET_KUBECTL=_out/kubebuilder/bin/kubectl TEST_DNS_SERVER="127.0.0.1:53" TEST_ZONE_NAME=example.ca. HTTP_PROXY="127.0.0.1:3128" HTTPS_PROXY="127.0.0.1:3128" NO_PROXY="proxy.golang.org" go test -v -run "TestIsAllowedZones"
TEST_ASSET_ETCD=_out/kubebuilder/bin/etcd TEST_ASSET_KUBE_APISERVER=_out/kubebuilder/bin/kube-apiserver TEST_ASSET_KUBECTL=_out/kubebuilder/bin/kubectl TEST_DNS_SERVER="127.0.0.1:53" TEST_ZONE_NAME=example.ca. go test -v -run "^TestNoProxy.*"
TEST_ASSET_ETCD=_out/kubebuilder/bin/etcd TEST_ASSET_KUBE_APISERVER=_out/kubebuilder/bin/kube-apiserver TEST_ASSET_KUBECTL=_out/kubebuilder/bin/kubectl TEST_DNS_SERVER="127.0.0.1:53" TEST_ZONE_NAME=example.ca. HTTP_PROXY="127.0.0.1:3128" HTTPS_PROXY="127.0.0.1:3128" go test -v -run "^TestProxy.*"
TEST_ASSET_ETCD=_out/kubebuilder/bin/etcd TEST_ASSET_KUBE_APISERVER=_out/kubebuilder/bin/kube-apiserver TEST_ASSET_KUBECTL=_out/kubebuilder/bin/kubectl TEST_DNS_SERVER="127.0.0.1:53" TEST_ZONE_NAME=example.ca. HTTP_PROXY="127.0.0.1:3128" HTTPS_PROXY="127.0.0.1:3128" NO_PROXY="proxy.golang.org" go test -v -run "^TestProxy.*"

test: verify

Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ spec:
# Timeout for requests to the PDNS api server
# (in seconds)
timeout: 30

# If the server is only allowed to edit certain zones; the
# default is an empty list, allowing everything.
# *IMPORTANT*: Remember the trailing dot to make the zone-name
# fully qualified.
allowed-zones:
- example.com.
- example.org.
- example.net.
```
And then you can issue a cert:
Expand Down
4 changes: 4 additions & 0 deletions docker-compose.test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,9 @@ services:
- '3128:3128'
volumes:
- ./testdata/pdns/docker/squid/squid.conf:/etc/squid/squid.conf
ulimits:
nofile:
soft: 65536
hard: 65536
volumes:
data: {}
24 changes: 24 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"
"net/http"
"os"
"strings"
"time"

"golang.org/x/exp/maps"
Expand Down Expand Up @@ -106,6 +107,25 @@ type powerDNSProviderConfig struct {
//
// +optional
Timeout int `json:"timeout"`

// AllowedZones is the list of zones that may be edited. If the list is
// empty, all zones are permitted.
AllowedZones []string `json:"allowed-zones"`
}

// IsAllowedZone checks if the webhook is allowed to edit the given zone, per
// AllowedZones setting. All zones allowed if AllowedZones is empty (the default setting)
func (cfg powerDNSProviderConfig) IsAllowedZone(zone string) bool {
if len(cfg.AllowedZones) == 0 {
return true
}

for _, allowed := range cfg.AllowedZones {
if zone == allowed || strings.HasSuffix(zone, "."+allowed) {
return true
}
}
return false
}

// Name is used as the name for this DNS solver when referencing it on the ACME
Expand Down Expand Up @@ -133,6 +153,10 @@ func (c *powerDNSProviderSolver) Present(ch *v1alpha1.ChallengeRequest) error {
return fmt.Errorf("failed initializing powerdns provider: %v", err)
}

if !cfg.IsAllowedZone(ch.ResolvedZone) {
return fmt.Errorf("zone %s may not be edited per config (allowed zones are %v)", ch.ResolvedZone, cfg.AllowedZones)
}

records, err := c.getExistingRecords(ctx, provider, ch.ResolvedZone, ch.ResolvedFQDN)
if err != nil {
return fmt.Errorf("failed loading existing records for %s in domain %s: %v", ch.ResolvedFQDN, ch.ResolvedZone, err)
Expand Down
25 changes: 25 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,28 @@ func getEnv(key, fallback string) string {
}
return fallback
}

func TestIsAllowedZones(t *testing.T) {
cfg := powerDNSProviderConfig{
AllowedZones: []string{"example.com.", "example.org."},
}

tests := []struct {
zone string
matched bool
}{
{"foo.example.com.", true},
{"foo.example.net.", false},
{"example.com.", true},
{"notexample.com.", false},
}

for _, tt := range tests {
t.Run(tt.zone, func(t *testing.T) {
match := cfg.IsAllowedZone(tt.zone)
if match != tt.matched {
t.Errorf("Unexpected IsAllowedZone(%s) = %t, expected %t", tt.zone, match, tt.matched)
}
})
}
}

0 comments on commit b138036

Please sign in to comment.