You've already forked ddns-updater
Compare commits
7 Commits
v1.4.0
...
ignore-ip-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6f9ce7aea | ||
|
|
26a3cf2a18 | ||
|
|
bad00230a9 | ||
|
|
29c3312ab1 | ||
|
|
ecd2776a50 | ||
|
|
51958d719e | ||
|
|
10a5a12b86 |
6
.github/workflows/quality-checks.yml
vendored
6
.github/workflows/quality-checks.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.21'
|
||||
cache: false
|
||||
- uses: actions/checkout@v3
|
||||
- name: golangci-lint
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
- name: Prepare go environment
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.21.5'
|
||||
cache: false
|
||||
- name: Install dep scanner
|
||||
run: |
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
- name: Prepare go environment
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.21'
|
||||
cache: false
|
||||
- name: Run tests
|
||||
run: go test --cover -coverprofile coverage.out -covermode count -v ./...
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
FROM golang:1.20 as build
|
||||
FROM golang:1.21 as build
|
||||
|
||||
# Copy project sources
|
||||
COPY . /opt/project/
|
||||
WORKDIR /opt/project
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates=20210119
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates=20230311
|
||||
|
||||
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /cloudflare-ddns-updater
|
||||
|
||||
|
||||
28
README.md
28
README.md
@@ -18,9 +18,24 @@ Before run, you need configure this environment variables.
|
||||
- `ON_CHANGE_COMMENT` - (optional) in the event that the ip address of the dns record changes, this comment will be added to the record
|
||||
- `CHECK_INTERVAL_SECONDS` - (optional) how often will the ip address of the records be checked (default: `300`)
|
||||
- `PUBLIC_IP_RESOLVER` - (optional) public ip address resolver. (default: `ifconfig.me`) Available: `ifconfig.me`, `v4.ident.me`, `1.1.1.1`
|
||||
- `NOTIFIERS` - (optional) setting the notifier in case of an update of the dns record. Multiple entries are separated by commas. (default none). Example: `webhook@http://localhost/cloudflare-notification`
|
||||
- Available
|
||||
- `webhook` - Call defined webhook. Example: `webhook@http://localhost/cloudflare-notification`
|
||||
- `NOTIFIERS` - (optional) setting the notifier in case of an update of the dns record. Multiple entries are separated by commas. (default none). Example: `webhook@http://localhost/cloudflare-notification`
|
||||
|
||||
### Notifications
|
||||
|
||||
Currently, only webhook notification is available. Webhook sends a POST request to the specified endpoint in json format.
|
||||
|
||||
Request body example:
|
||||
```json
|
||||
{
|
||||
"old_ip": "xxx.xxx.xxx.xxx",
|
||||
"new_ip": "xxx.xxx.xxx.xxx",
|
||||
"checked_at": "2023-05-04T17:39:42.942850354+02:00",
|
||||
"resolver_tag": "ifconfig.me",
|
||||
"domain": "my.domain.com"
|
||||
}
|
||||
```
|
||||
|
||||
Other notification methods will be implemented later (check future plans section).
|
||||
|
||||
### Building from source
|
||||
|
||||
@@ -52,6 +67,7 @@ services:
|
||||
environment:
|
||||
- CLOUDFLARE_DNS_TO_CHECK=my.testdomain.com,your.testdomain.com
|
||||
- CLOUDFLARE_API_KEY=your_cloudflare_api_key
|
||||
- NOTIFIERS=webhook@http://localhost/cloudflare-updated-notification
|
||||
- CLOUDFLARE_ZONE=testdomain.com
|
||||
- ON_CHANGE_COMMENT="automatically updated"
|
||||
- CHECK_INTERVAL_SECONDS=300
|
||||
@@ -62,6 +78,12 @@ services:
|
||||
docker run -e CLOUDFLARE_DNS_TO_CHECK=my.testdomain.com,your.testdomain.com -e CLOUDFLARE_API_KEY=your_cloudflare_api_key -e CLOUDFLARE_ZONE=testdomain.com -e ON_CHANGE_COMMENT="automatically updated" -e CHECK_INTERVAL_SECONDS=300 mkelcik/cloudflare-ddns-update:latest
|
||||
```
|
||||
|
||||
### Future plans
|
||||
|
||||
- prometheus metrics
|
||||
- mqtt and rabbitmq notifiers
|
||||
- IPv6 support
|
||||
|
||||
### Contributing
|
||||
|
||||
Feel free to contribute and pls report bugs. Thanks
|
||||
|
||||
13
go.mod
13
go.mod
@@ -1,14 +1,15 @@
|
||||
module github.com/mkelcik/cloudflare-ddns-update
|
||||
|
||||
go 1.20
|
||||
go 1.21
|
||||
|
||||
require github.com/cloudflare/cloudflare-go v0.66.0
|
||||
require github.com/cloudflare/cloudflare-go v0.83.0
|
||||
|
||||
require (
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.2 // indirect
|
||||
golang.org/x/net v0.9.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
)
|
||||
|
||||
13
go.sum
13
go.sum
@@ -1,8 +1,12 @@
|
||||
github.com/cloudflare/cloudflare-go v0.66.0 h1:B74IvVGQ4UFYJnqQSK/9GbR+Y1HwNxqqdN2Bmg0dckg=
|
||||
github.com/cloudflare/cloudflare-go v0.66.0/go.mod h1:tA44hjU9FfycofKT+lWWMHb/dEq1pRbiVPGuJo1WzLQ=
|
||||
github.com/cloudflare/cloudflare-go v0.83.0 h1:aq85Hbr5W6KfXZV7v3lx6fhBkiu0FYqY+3+xzG14mdY=
|
||||
github.com/cloudflare/cloudflare-go v0.83.0/go.mod h1:5pkAzpoWJYI5NekLZoRryQAcghYDhdbUxdcal1f7lu4=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
@@ -13,6 +17,8 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj
|
||||
github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
@@ -21,10 +27,17 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
||||
@@ -18,6 +18,7 @@ const (
|
||||
envKeyOnChangeComment = "ON_CHANGE_COMMENT"
|
||||
envKeyCheckIntervalSeconds = "CHECK_INTERVAL_SECONDS"
|
||||
envKeyNotifiers = "NOTIFIERS"
|
||||
envKeyIgnoredIpChange = "IGNORED_IP_CHANGE"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
@@ -28,6 +29,7 @@ type Config struct {
|
||||
OnChangeComment string
|
||||
Notifiers []string
|
||||
CheckInterval time.Duration
|
||||
IgnoredIpChange []string
|
||||
}
|
||||
|
||||
func (c Config) Validate() error {
|
||||
@@ -61,5 +63,6 @@ func NewConfig() Config {
|
||||
OnChangeComment: os.Getenv(envKeyOnChangeComment),
|
||||
Notifiers: parseCommaDelimited(os.Getenv(envKeyNotifiers)),
|
||||
CheckInterval: time.Duration(checkInterval) * time.Second,
|
||||
IgnoredIpChange: parseCommaDelimited(os.Getenv(envKeyIgnoredIpChange)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package internal
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func parseCommaDelimited(data string) []string {
|
||||
out := make([]string, 0, strings.Count(data, ",")+1)
|
||||
|
||||
26
internal/ignore_list.go
Normal file
26
internal/ignore_list.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"net"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
func checkAddress(address, pattern string) bool {
|
||||
pattern = "^" + pattern + "$"
|
||||
re := regexp.MustCompile(pattern)
|
||||
return re.MatchString(address)
|
||||
}
|
||||
|
||||
func IgnoredIpChange(ip net.IP, ignored []string) bool {
|
||||
if len(ignored) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, i := range ignored {
|
||||
if checkAddress(ip.String(), i) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
75
internal/ignore_list_test.go
Normal file
75
internal/ignore_list_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package internal
|
||||
|
||||
import "testing"
|
||||
|
||||
func Test_checkAddress(t *testing.T) {
|
||||
type args struct {
|
||||
address string
|
||||
pattern string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
args: args{
|
||||
address: "192.168.0.1",
|
||||
pattern: "",
|
||||
},
|
||||
want: false,
|
||||
}, {
|
||||
name: "true match 192.*",
|
||||
args: args{
|
||||
address: "192.168.0.1",
|
||||
pattern: "192.*",
|
||||
},
|
||||
want: true,
|
||||
}, {
|
||||
name: "false match 193.*",
|
||||
args: args{
|
||||
address: "192.168.0.1",
|
||||
pattern: "193.*",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "true match 192.168.0.1",
|
||||
args: args{
|
||||
address: "192.168.0.1",
|
||||
pattern: "192.168.0.1",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "false not match 192.168.0.2",
|
||||
args: args{
|
||||
address: "192.168.0.1",
|
||||
pattern: "192.168.0.2",
|
||||
},
|
||||
want: false,
|
||||
}, {
|
||||
name: "true match 192.168.0.*",
|
||||
args: args{
|
||||
address: "192.168.0.10",
|
||||
pattern: "192.168.0.*",
|
||||
},
|
||||
want: true,
|
||||
}, {
|
||||
name: "false match 192.168.0.*",
|
||||
args: args{
|
||||
address: "192.168.1.10",
|
||||
pattern: "192.168.0.*",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := checkAddress(tt.args.address, tt.args.pattern); got != tt.want {
|
||||
t.Errorf("checkAddress() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
8
main.go
8
main.go
@@ -64,6 +64,12 @@ func main() {
|
||||
}
|
||||
log.Printf("Current public ip `%s` (resolver: %s)", currentPublicIP, resolverTag)
|
||||
|
||||
// check if ip is not in ignore list
|
||||
if internal.IgnoredIpChange(currentPublicIP, config.IgnoredIpChange) {
|
||||
log.Printf("Ignored ip change `%s`, skipping.", currentPublicIP)
|
||||
return
|
||||
}
|
||||
|
||||
dns, err := allDNSRecords(ctx, api, cloudflare.ZoneIdentifier(zoneID))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -83,7 +89,7 @@ func main() {
|
||||
}
|
||||
|
||||
if config.OnChangeComment != "" {
|
||||
update.Comment = config.OnChangeComment
|
||||
update.Comment = &config.OnChangeComment
|
||||
}
|
||||
|
||||
if _, err := api.UpdateDNSRecord(ctx, cloudflare.ZoneIdentifier(zoneID), update); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user