1
0
mirror of https://github.com/cmur2/dyndnsd.git synced 2025-12-14 13:48:10 +01:00

Compare commits

...

8 Commits

Author SHA1 Message Date
cn
627b1c4dc5 release: 3.12.0 2025-12-04 01:05:11 +01:00
cn
da79ef902d dyndnsd: add tests for regular expressions 2025-12-04 01:03:16 +01:00
cn
8589cf801f project: update changelog 2025-12-04 00:28:32 +01:00
Jesús Daniel Colmenares Oviedo
c743c778bc dyndnsd: add support for regular expressions 2025-12-04 00:26:34 +01:00
renovate[bot]
415635f373 ci: update actions/checkout action to v6 2025-11-27 09:57:52 +01:00
dependabot[bot]
694a3e4c21 gems: update rubocop-rspec requirement from ~> 3.7.0 to ~> 3.8.0
Updates the requirements on [rubocop-rspec](https://github.com/rubocop/rubocop-rspec) to permit the latest version.
- [Release notes](https://github.com/rubocop/rubocop-rspec/releases)
- [Changelog](https://github.com/rubocop/rubocop-rspec/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rubocop/rubocop-rspec/compare/v3.7.0...v3.8.0)

---
updated-dependencies:
- dependency-name: rubocop-rspec
  dependency-version: 3.8.0
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-24 12:51:52 +01:00
renovate[bot]
74aec5f873 docker: update alpine Docker tag to v3.22.2 2025-10-09 16:37:24 +02:00
cn
34f2c01543 ci: run vulnscan less often 2025-10-02 20:37:01 +02:00
11 changed files with 86 additions and 13 deletions

View File

@@ -11,7 +11,7 @@ jobs:
release-dockerimage: release-dockerimage:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Extract dyndnsd version from tag name - name: Extract dyndnsd version from tag name
run: | run: |

View File

@@ -23,7 +23,7 @@ jobs:
- '3.3' - '3.3'
- '3.4' - '3.4'
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Set up Ruby ${{ matrix.ruby-version }} - name: Set up Ruby ${{ matrix.ruby-version }}
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
with: with:
@@ -37,7 +37,7 @@ jobs:
actionlint: actionlint:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Check workflow files - name: Check workflow files
run: | run: |
echo "::add-matcher::.github/actionlint-matcher.json" echo "::add-matcher::.github/actionlint-matcher.json"
@@ -50,6 +50,6 @@ jobs:
image: ghcr.io/renovatebot/renovate image: ghcr.io/renovatebot/renovate
options: --user root options: --user root
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Check Renovate config - name: Check Renovate config
run: renovate-config-validator --strict run: renovate-config-validator --strict

View File

@@ -1,10 +1,9 @@
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
--- ---
name: vulnscan name: vulnscan
on: on:
schedule: schedule:
- cron: '7 4 * * 4' # weekly on thursday morning - cron: '7 4 1 * *' # monthly on first day's morning
workflow_dispatch: workflow_dispatch:
jobs: jobs:

View File

@@ -1,5 +1,11 @@
# Changelog # Changelog
## 3.12.0 (December 4th, 2025)
IMPROVEMENTS:
- regex instead of hosts list can be used for hostname ownership
## 3.11.0 (October 2nd, 2025) ## 3.11.0 (October 2nd, 2025)
IMPROVEMENTS: IMPROVEMENTS:

View File

@@ -307,6 +307,37 @@ users:
``` ```
### Matching with a regular expression
Instead of relying on `hosts`, you can use `regex` to employ a regular expression, which is very useful for avoiding having to repeatedly edit the configuration file to register a new host name.
```yaml
host: "0.0.0.0"
port: 5354
username: "dyndnsd"
group: "dyndnsd"
db: "/dyndnsd/db.json"
debug: false
domain: "dyn.dc-air.home.arpa"
updater:
name: "command_with_bind_zone"
params:
zone_file: "/nsd/zones/static/dyn.dc-air.home.arpa.zone"
command: "doas service nsd reload"
ttl: "5m"
dns: "ns.dc-air.home.arpa."
email_addr: "admin.example.org"
users:
myuser:
password: "superhypermegas3kurepassword1234"
regex: '^[a-z][0-9]\.dyn\.dc\-air\.home\.arpa$'
```
However, when using `regex`, `hosts` is simply ignored if defined, so you must choose one or the other. Recommendation: use `regex` for scripts or programs and `hosts` for regular users.
**Note**: Please note that when dyndnsd evaluates the regular expression, the `Regexp::EXTENDED` and `Regexp::IGNORECASE` options are used.
## License ## License
dyndnsd.rb is licensed under the Apache License, Version 2.0. See LICENSE for more information. dyndnsd.rb is licensed under the Apache License, Version 2.0. See LICENSE for more information.

View File

@@ -1,4 +1,4 @@
FROM alpine:3.22.1 FROM alpine:3.22.2
EXPOSE 5353 8080 EXPOSE 5353 8080

View File

@@ -1,4 +1,4 @@
FROM alpine:3.22.1 FROM alpine:3.22.2
EXPOSE 5353 8080 EXPOSE 5353 8080

View File

@@ -46,6 +46,6 @@ Gem::Specification.new do |s|
s.add_development_dependency 'rspec' s.add_development_dependency 'rspec'
s.add_development_dependency 'rubocop', '~> 1.81.1' s.add_development_dependency 'rubocop', '~> 1.81.1'
s.add_development_dependency 'rubocop-rake', '~> 0.7.1' s.add_development_dependency 'rubocop-rake', '~> 0.7.1'
s.add_development_dependency 'rubocop-rspec', '~> 3.7.0' s.add_development_dependency 'rubocop-rspec', '~> 3.8.0'
s.add_development_dependency 'solargraph', '~> 0.55.0' s.add_development_dependency 'solargraph', '~> 0.55.0'
end end

View File

@@ -218,9 +218,22 @@ module Dyndnsd
# we can trust this information since user was authorized by middleware # we can trust this information since user was authorized by middleware
user = env['REMOTE_USER'] user = env['REMOTE_USER']
# check for hostnames that the user does not own if @users[user].key?('regex')
forbidden_hostnames = hostnames - @users[user].fetch('hosts', []) pattern = @users[user].fetch('regex')
return [422, {'X-DynDNS-Response' => 'host_forbidden'}, []] if forbidden_hostnames.any? begin
regex = Regexp.new(pattern, Regexp::IGNORECASE | Regexp::EXTENDED)
rescue RegexpError => e
Dyndnsd.logger.warn "Invalid regex pattern '#{pattern}': #{e.message}"
return [422, {'X-DynDNS-Response' => 'host_forbidden'}, []]
end
# check for hostnames that match the regex
matches = hostnames.any? { |str| regex.match?(str) }
return [422, {'X-DynDNS-Response' => 'host_forbidden'}, []] if !matches
else
# check for hostnames that the user does not own
forbidden_hostnames = hostnames - @users[user].fetch('hosts', [])
return [422, {'X-DynDNS-Response' => 'host_forbidden'}, []] if forbidden_hostnames.any?
end
if params['offline'] == 'YES' if params['offline'] == 'YES'
myips = [] myips = []

View File

@@ -1,5 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
module Dyndnsd module Dyndnsd
VERSION = '3.11.0' VERSION = '3.12.0'
end end

View File

@@ -18,6 +18,10 @@ describe Dyndnsd::Daemon do
}, },
'test2' => { 'test2' => {
'password' => 'ihavenohosts' 'password' => 'ihavenohosts'
},
'test3' => {
'password' => 'superhypermegas3kurepassword1234',
'regex' => '^[a-z0-9]+-test3\.example\.org$'
} }
} }
} }
@@ -74,6 +78,22 @@ describe Dyndnsd::Daemon do
expect(last_response.body).to eq("good 2001:db8::1\ngood 2001:db8::1") expect(last_response.body).to eq("good 2001:db8::1\ngood 2001:db8::1")
end end
it 'supports regex matches for hostnames' do
authorize 'test3', 'superhypermegas3kurepassword1234'
get '/nic/update?hostname=abc123-test3.example.org&myip=1.2.3.4'
expect(last_response).to be_ok
expect(last_response.body).to eq('good 1.2.3.4')
get '/nic/update?hostname=foo-test3.example.org,bar-test3.example.org&myip=2001:db8::1'
expect(last_response).to be_ok
expect(last_response.body).to eq("good 2001:db8::1\ngood 2001:db8::1")
get '/nic/update?hostname=abc123.example.org'
expect(last_response).to be_ok
expect(last_response.body).to eq('nohost')
end
it 'rejects request if one hostname is invalid' do it 'rejects request if one hostname is invalid' do
authorize 'test', 'secret' authorize 'test', 'secret'
@@ -120,6 +140,10 @@ describe Dyndnsd::Daemon do
get '/nic/update?hostname=foo.example.org,notmyhost.example.org' get '/nic/update?hostname=foo.example.org,notmyhost.example.org'
expect(last_response).to be_ok expect(last_response).to be_ok
expect(last_response.body).to eq('nohost') expect(last_response.body).to eq('nohost')
get '/nic/update?hostname=abc123-test3.example.org'
expect(last_response).to be_ok
expect(last_response.body).to eq('nohost')
end end
it 'updates a host on IP change' do it 'updates a host on IP change' do