Skip to content

Commit

Permalink
feat!: rename IP4/6_POLICY to IP4/6_PROVIDER (#167)
Browse files Browse the repository at this point in the history
* feat!: rename IP4/6_POLICY to IP4/6_PROVIDER

* ci: boost coverage rate

* ci: make linter happy
  • Loading branch information
favonia committed May 14, 2022
1 parent 718d43a commit 1dcd4e4
Show file tree
Hide file tree
Showing 26 changed files with 554 additions and 339 deletions.
28 changes: 13 additions & 15 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ spec:
labels:
app: cloudflare-ddns
spec:
restartPolicy: Always
restartolicy: Always
containers:
- name: cloudflare-ddns
image: favonia/cloudflare-ddns:latest
Expand All @@ -215,8 +215,8 @@ spec:
runAsUser: 1000
runAsGroup: 1000
env:
- name: "IP6_POLICY"
value: "unmanaged"
- name: "IP6_PROVIDER"
value: "none"
- name: "PROXIED"
value: "true"
- name: "CF_API_TOKEN"
Expand All @@ -238,11 +238,11 @@ Kubernetes comes with built-in support to drop superuser privileges. The updater
</details>

<details>
<summary>📡 Use <code>IP6_POLICY: "unmanaged"</code> to disable IPv6 management.</summary>
<summary>📡 Use <code>IP6_PROVIDER: "none"</code> to disable IPv6 management.</summary>

The support of IPv6 in Kubernetes has been improving, but a working setup still takes effort. Since Kubernetes 1.21+, the [IPv4/IPv6 dual stack](https://kubernetes.io/docs/concepts/services-networking/dual-stack/) is enabled by default, but a setup which allows IPv6 egress traffic (_e.g.,_ to reach Cloudflare servers to detect public IPv6 addresses) still requires deep understanding of Kubernetes and is beyond this simple guide. The popular tool [minicube](https://minikube.sigs.k8s.io/), which implements a simple local Kubernetes cluster, unfortunately [does not support IPv6 yet.](https://minikube.sigs.k8s.io/docs/faq/#does-minikube-support-ipv6) Until there is an easy way to enable IPv6 in Kubernetes, the template here will have IPv6 disabled.

If you manage to enable IPv6, congratulations. Feel free to remove `IP6_POLICY: "unmanaged"` to detect and update both `A` and `AAAA` records. There is almost no danger in enabling IPv6 even when the IPv6 setup is not working. In the worst case, the updater will remove all `AAAA` records associated with the domains in `DOMAINS` and `IP6_DOMAINS` because those records will appear to be “stale.” The deleted records will be recreated once the updater correctly detects the IPv6 addresses.
If you manage to enable IPv6, congratulations. Feel free to remove `IP6_PROVIDER: "none"` to detect and update both `A` and `AAAA` records. There is almost no danger in enabling IPv6 even when the IPv6 setup is not working. In the worst case, the updater will remove all `AAAA` records associated with the domains in `DOMAINS` and `IP6_DOMAINS` because those records will appear to be “stale.” The deleted records will be recreated once the updater correctly detects the IPv6 addresses.
</details>

<details>
Expand Down Expand Up @@ -292,12 +292,12 @@ In most cases, `CF_ACCOUNT_ID` is not needed.
| ---- | ------------ | ------- | --------- | ------------- |
| `DOMAINS` | Comma-separated fully qualified domain names or wildcard domain names | The domains this tool should manage | (See below) | N/A
| `IP4_DOMAINS` | Comma-separated fully qualified domain names or wildcard domain names | The domains this tool should manage for `A` records | (See below) | N/A
| `IP4_POLICY` | `cloudflare.doh`, `cloudflare.trace`, `ipify`, `local`, and `unmanaged` | How to detect IPv4 addresses. (See below) | No | `cloudflare.trace`
| `IP4_PROVIDER` | `cloudflare.doh`, `cloudflare.trace`, `ipify`, `local`, and `none` | How to detect IPv4 addresses. (See below) | No | `cloudflare.trace`
| `IP6_DOMAINS` | Comma-separated fully qualified domain names or wildcard domain names | The domains this tool should manage for `AAAA` records | (See below) | N/A
| `IP6_POLICY` | `cloudflare.doh`, `cloudflare.trace`, `ipify`, `local`, and `unmanaged` | How to detect IPv6 addresses. (See below) | No | `cloudflare.trace`
| `IP6_PROVIDER` | `cloudflare.doh`, `cloudflare.trace`, `ipify`, `local`, and `none` | How to detect IPv6 addresses. (See below) | No | `cloudflare.trace`

> <details>
> <summary>📜 Available policies for <code>IP4_POLICY</code> and <code>IP6_POLICY</code></summary>
> <summary>📜 Available providers for <code>IP4_PROVIDER</code> and <code>IP6_PROVIDER</code></summary>
>
> - `cloudflare.doh`\
> Get the public IP address by querying `whoami.cloudflare.` against [Cloudflare via DNS-over-HTTPS](https://developers.cloudflare.com/1.1.1.1/dns-over-https) and update DNS records accordingly.
Expand All @@ -307,12 +307,10 @@ In most cases, `CF_ACCOUNT_ID` is not needed.
> Get the public IP address via [ipify’s public API](https://www.ipify.org/) and update DNS records accordingly.
> - `local`\
> Get the address via local network interfaces and update DNS records accordingly. When multiple local network interfaces or in general multiple IP addresses are present, the tool will use the address that would have been used for outbound UDP connections to Cloudflare servers. ⚠️ You need access to the host network (such as `network_mode: host` in Docker Compose or `hostNetwork: true` in Kubernetes) for this policy, for otherwise the tool will detect the addresses inside the [bridge network in Docker](https://docs.docker.com/network/bridge/) or the [default namespaces in Kubernetes](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/) instead of those in the host network.
> - `unmanaged`\
> - `none`\
> Stop the DNS updating completely. Existing DNS records will not be removed.
> - `cloudflare`\
> Deprecated; currently an alias of `cloudflare.trace`.
>
> The option `IP4_POLICY` is governing IPv4 addresses and `A`-type records, while the option `IP6_POLICY` is governing IPv6 addresses and `AAAA`-type records. The two options act independently of each other.
> The option `IP4_PROVIDER` is governing IPv4 addresses and `A`-type records, while the option `IP6_PROVIDER` is governing IPv6 addresses and `AAAA`-type records. The two options act independently of each other.
> </details>
> <details>
Expand Down Expand Up @@ -380,10 +378,10 @@ If you are using Kubernetes, run `kubectl replace -f cloudflare-ddns.yaml` after
| `API_KEY_FILE=file` | ✔️ | Use `CF_API_TOKEN_FILE=file` |
| `ZONE=example.org` and `SUBDOMAIN=sub` | ✔️ | Use `DOMAINS=sub.example.org` directly |
| `PROXIED=true` | ✔️ | Same |
| `RRTYPE=A` | ✔️ | Both IPv4 and IPv6 are enabled by default; use `IP6_POLICY=unmanaged` to disable IPv6 |
| `RRTYPE=AAAA` | ✔️ | Both IPv4 and IPv6 are enabled by default; use `IP4_POLICY=unmanaged` to disable IPv4 |
| `RRTYPE=A` | ✔️ | Both IPv4 and IPv6 are enabled by default; use `IP6_PROVIDER=none` to disable IPv6 |
| `RRTYPE=AAAA` | ✔️ | Both IPv4 and IPv6 are enabled by default; use `IP4_PROVIDER=none` to disable IPv4 |
| `DELETE_ON_STOP=true` | ✔️ | Same |
| `INTERFACE=iface` | ✔️ | Not required for `local` policies; we can handle multiple network interfaces |
| `INTERFACE=iface` | ✔️ | Not required for `local` providers; we can handle multiple network interfaces |
| `CUSTOM_LOOKUP_CMD=cmd` || _There is not even a shell in the minimum Docker image._ |
| `DNS_SERVER=server` || _Only the Cloudflare server is supported._ |

Expand Down
54 changes: 27 additions & 27 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ import (

"github.com/favonia/cloudflare-ddns/internal/api"
"github.com/favonia/cloudflare-ddns/internal/cron"
"github.com/favonia/cloudflare-ddns/internal/detector"
"github.com/favonia/cloudflare-ddns/internal/file"
"github.com/favonia/cloudflare-ddns/internal/ipnet"
"github.com/favonia/cloudflare-ddns/internal/monitor"
"github.com/favonia/cloudflare-ddns/internal/pp"
"github.com/favonia/cloudflare-ddns/internal/provider"
)

type Config struct {
Auth api.Auth
Policy map[ipnet.Type]detector.Policy
Provider map[ipnet.Type]provider.Provider
Domains map[ipnet.Type][]api.Domain
UpdateCron cron.Schedule
UpdateOnStart bool
Expand All @@ -31,9 +31,9 @@ type Config struct {
func Default() *Config {
return &Config{
Auth: nil,
Policy: map[ipnet.Type]detector.Policy{
ipnet.IP4: detector.NewCloudflareTrace(),
ipnet.IP6: detector.NewCloudflareTrace(),
Provider: map[ipnet.Type]provider.Provider{
ipnet.IP4: provider.NewCloudflareTrace(),
ipnet.IP6: provider.NewCloudflareTrace(),
},
Domains: map[ipnet.Type][]api.Domain{
ipnet.IP4: nil,
Expand Down Expand Up @@ -147,18 +147,18 @@ func ReadDomainMap(ppfmt pp.PP, field *map[ipnet.Type][]api.Domain) bool {
return true
}

func ReadPolicyMap(ppfmt pp.PP, field *map[ipnet.Type]detector.Policy) bool {
ip4Policy := (*field)[ipnet.IP4]
ip6Policy := (*field)[ipnet.IP6]
func ReadProviderMap(ppfmt pp.PP, field *map[ipnet.Type]provider.Provider) bool {
ip4Provider := (*field)[ipnet.IP4]
ip6Provider := (*field)[ipnet.IP6]

if !ReadPolicy(ppfmt, "IP4_POLICY", &ip4Policy) ||
!ReadPolicy(ppfmt, "IP6_POLICY", &ip6Policy) {
if !ReadProvider(ppfmt, "IP4_PROVIDER", "IP4_POLICY", &ip4Provider) ||
!ReadProvider(ppfmt, "IP6_PROVIDER", "IP6_POLICY", &ip6Provider) {
return false
}

*field = map[ipnet.Type]detector.Policy{
ipnet.IP4: ip4Policy,
ipnet.IP6: ip6Policy,
*field = map[ipnet.Type]provider.Provider{
ipnet.IP4: ip4Provider,
ipnet.IP6: ip6Provider,
}
return true
}
Expand All @@ -174,12 +174,12 @@ func (c *Config) Print(ppfmt pp.PP) {
inner := ppfmt.IncIndent()

ppfmt.Infof(pp.EmojiConfig, "Policies:")
inner.Infof(pp.EmojiBullet, "IPv4 policy: %s", detector.Name(c.Policy[ipnet.IP4]))
if c.Policy[ipnet.IP4] != nil {
inner.Infof(pp.EmojiBullet, "IPv4 provider: %s", provider.Name(c.Provider[ipnet.IP4]))
if c.Provider[ipnet.IP4] != nil {
inner.Infof(pp.EmojiBullet, "IPv4 domains: %v", c.Domains[ipnet.IP4])
}
inner.Infof(pp.EmojiBullet, "IPv6 policy: %s", detector.Name(c.Policy[ipnet.IP6]))
if c.Policy[ipnet.IP6] != nil {
inner.Infof(pp.EmojiBullet, "IPv6 provider: %s", provider.Name(c.Provider[ipnet.IP6]))
if c.Provider[ipnet.IP6] != nil {
inner.Infof(pp.EmojiBullet, "IPv6 domains: %v", c.Domains[ipnet.IP6])
}

Expand Down Expand Up @@ -215,7 +215,7 @@ func (c *Config) ReadEnv(ppfmt pp.PP) bool { //nolint:cyclop
}

if !ReadAuth(ppfmt, &c.Auth) ||
!ReadPolicyMap(ppfmt, &c.Policy) ||
!ReadProviderMap(ppfmt, &c.Provider) ||
!ReadDomainMap(ppfmt, &c.Domains) ||
!ReadCron(ppfmt, "UPDATE_CRON", &c.UpdateCron) ||
!ReadBool(ppfmt, "UPDATE_ON_START", &c.UpdateOnStart) ||
Expand All @@ -241,11 +241,11 @@ func (c *Config) checkUselessDomains(ppfmt pp.PP) {
}

for ipNet, domains := range c.Domains {
if c.Policy[ipNet] == nil {
if c.Provider[ipNet] == nil {
for i := range domains {
if count[domains[i]] != len(c.Domains) {
ppfmt.Warningf(pp.EmojiUserWarning,
"Domain %q is ignored because it is only for %s but %s is unmanaged",
"Domain %q is ignored because it is only for %s but %s is disabled",
domains[i].Describe(), ipNet.Describe(), ipNet.Describe())
}
}
Expand All @@ -259,17 +259,17 @@ func (c *Config) NormalizeDomains(ppfmt pp.PP) bool {
return false
}

// change useless policies to unmanaged
// change useless policies to none
for ipNet, domains := range c.Domains {
if len(domains) == 0 && c.Policy[ipNet] != nil {
c.Policy[ipNet] = nil
ppfmt.Warningf(pp.EmojiUserWarning, "IP%d_POLICY was changed to %q because no domains were set for %s",
ipNet.Int(), detector.Name(c.Policy[ipNet]), ipNet.Describe())
if len(domains) == 0 && c.Provider[ipNet] != nil {
c.Provider[ipNet] = nil
ppfmt.Warningf(pp.EmojiUserWarning, "IP%d_PROVIDER was changed to %q because no domains were set for %s",
ipNet.Int(), provider.Name(c.Provider[ipNet]), ipNet.Describe())
}
}

if c.Policy[ipnet.IP4] == nil && c.Policy[ipnet.IP6] == nil {
ppfmt.Errorf(pp.EmojiUserError, "Both IPv4 and IPv6 are unmanaged")
if c.Provider[ipnet.IP4] == nil && c.Provider[ipnet.IP6] == nil {
ppfmt.Errorf(pp.EmojiUserError, "Both IPv4 and IPv6 are disabled")
return false
}

Expand Down
Loading

0 comments on commit 1dcd4e4

Please sign in to comment.