1
0
mirror of https://github.com/cmur2/dyndnsd.git synced 2025-08-08 08:33:56 +02:00

Compare commits

..

1 Commits

Author SHA1 Message Date
cn
3f150ad065 docs: add link to newer version 2020-07-28 17:33:27 +02:00
19 changed files with 53 additions and 196 deletions

View File

@@ -1,9 +0,0 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

View File

@@ -1,26 +0,0 @@
---
name: cd
on:
push:
tags:
- 'v*.*.*'
jobs:
release-dockerimage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Extract dyndnsd version from tag name
run: |
echo ::set-env name=DYNDNSD_VERSION::${GITHUB_REF#refs/*/v}
# https://github.com/marketplace/actions/build-and-push-docker-images
- name: Build and push Docker image for dyndnsd ${{ env.DYNDNSD_VERSION }}
uses: docker/build-push-action@v1
with:
username: cmur2
password: ${{ secrets.DOCKER_TOKEN }}
repository: cmur2/dyndnsd
path: docker
build_args: DYNDNSD_VERSION=${{ env.DYNDNSD_VERSION }}
tag_with_ref: true

View File

@@ -1,43 +0,0 @@
---
name: vulnscan
on:
schedule:
- cron: '7 4 * * 4' # weekly on thursday morning
workflow_dispatch:
jobs:
scan-released-dockerimages:
runs-on: ubuntu-latest
env:
TRIVY_LIGHT: 'true'
TRIVY_IGNORE_UNFIXED: 'true'
TRIVY_REMOVED_PKGS: 'true'
steps:
- name: Install Trivy
run: |
mkdir -p $GITHUB_WORKSPACE/bin
echo "::add-path::$GITHUB_WORKSPACE/bin"
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/master/contrib/install.sh | sh -s -- -b $GITHUB_WORKSPACE/bin
- name: Download Trivy DB
run: |
trivy image --download-db-only
- name: Scan vulnerabilities using Trivy
run: |
trivy --version
ALL_IMAGES="$(curl -s https://hub.docker.com/v2/repositories/cmur2/dyndnsd/tags?page_size=1000 | jq -r '.results[].name | "cmur2/dyndnsd:" + .' | grep -e 'cmur2/dyndnsd:v' | sort -r)"
EXIT_CODE=0
set -e
for major_version in $(seq 1 10); do
for image in $ALL_IMAGES; do
if [[ "$image" = cmur2/dyndnsd:v$major_version.* ]]; then
echo -n "\nScanning newest patch release $image of major v$major_version...\n"
if ! trivy image --skip-update --exit-code 1 "$image"; then
EXIT_CODE=1
fi
break
fi
done
done
exit "$EXIT_CODE"

View File

@@ -1,6 +1,5 @@
AllCops: AllCops:
TargetRubyVersion: '2.5' TargetRubyVersion: '2.3'
NewCops: enable
Layout/EmptyLineAfterGuardClause: Layout/EmptyLineAfterGuardClause:
Enabled: false Enabled: false

View File

@@ -5,6 +5,8 @@ rvm:
- 2.7 - 2.7
- 2.6 - 2.6
- 2.5 - 2.5
- 2.4
- 2.3
script: script:
- bundle exec rake travis - bundle exec rake travis

View File

@@ -1,17 +1,5 @@
# Changelog # Changelog
## 3.1.0 (August 19, 2020)
IMPROVEMENTS:
- Add officially maintained [Docker image for dyndnsd](https://hub.docker.com/r/cmur2/dyndnsd)
## 3.0.0 (July 29, 2020)
IMPROVEMENTS:
- Drop EOL Ruby 2.4 and lower support, now minimum version supported is Ruby 2.5
## 2.3.1 (July 27, 2020) ## 2.3.1 (July 27, 2020)
IMPROVEMENTS: IMPROVEMENTS:

View File

@@ -1,9 +1,10 @@
# dyndnsd.rb # dyndnsd.rb
[![Build Status](https://travis-ci.org/cmur2/dyndnsd.svg?branch=master)](https://travis-ci.org/cmur2/dyndnsd) [![Dependencies](https://badges.depfu.com/badges/4f25da8493f7a29f652ac892fbf9227b/overview.svg)](https://depfu.com/github/cmur2/dyndnsd) [![Build Status](https://travis-ci.org/cmur2/dyndnsd.svg?branch=dyndnsd-2.x)](https://travis-ci.org/cmur2/dyndnsd) [![Dependencies](https://badges.depfu.com/badges/4f25da8493f7a29f652ac892fbf9227b/overview.svg)](https://depfu.com/github/cmur2/dyndnsd)
A small, lightweight and extensible DynDNS server written with Ruby and Rack. A small, lightweight and extensible DynDNS server written with Ruby and Rack.
**Note:** a newer version of dyndnsd.rb is available on [branch master](https://github.com/cmur2/dyndnsd), see also the [changelog](https://github.com/cmur2/dyndnsd/blob/master/CHANGELOG.md).
## Description ## Description
@@ -64,42 +65,7 @@ users:
Run dyndnsd.rb by: Run dyndnsd.rb by:
```bash dyndnsd /path/to/config.yaml
dyndnsd /path/to/config.yml
```
### Docker image
There is an officially maintained [Docker image for dyndnsd](https://hub.docker.com/r/cmur2/dyndnsd) available at Dockerhub. The goal is to have a minimal secured image available (currently based on Alpine) that works well for the `zone_transfer_server` updater use case.
Users can make extensions by deriving from the official Docker image or building their own.
The Docker image consumes the same configuration file in YAML format as the gem, inside the container it needs to be mounted/available as `/etc/dyndnsd/config.yml`. the following YAML should be used as a base and extended with user's settings:
```yaml
host: "0.0.0.0"
port: 8080
# omit the logfile: option so logging to STDOUT will happen automatically
db: "/var/lib/dyndnsd/db.json"
# User's settings for updater and permissions follow here!
```
more ports might be needed depending on if DNS zone transfer is needed
Run the Docker image exposing the DynDNS-API on host port 8080 via:
```bash
docker run -d --name dyndnsd \
-p 8080:8080 \
-v /host/path/to/dyndnsd/config.yml:/etc/dyndnsd/config.yml \
-v /host/ptherpath/to/dyndnsd/datadir:/var/lib/dyndnsd \
cmur2/dyndnsd:vX.Y.Z
```
*Note*: You may need to expose more then just port 8080 e.g. if you use the `zone_transfer_server` which can be done by appending additional `-p 5353:5353` flags to the `docker run` command.
## Using dyndnsd.rb with any nameserver via DNS zone transfers (AXFR) ## Using dyndnsd.rb with any nameserver via DNS zone transfers (AXFR)
@@ -222,11 +188,9 @@ If you want to provide an additional IPv6 address as myip6 parameter, the myip p
Use a webserver as a proxy to handle SSL and/or multiple listen addresses and ports. DynDNS.com provides HTTP on port 80 and 8245 and HTTPS on port 443. Use a webserver as a proxy to handle SSL and/or multiple listen addresses and ports. DynDNS.com provides HTTP on port 80 and 8245 and HTTPS on port 443.
### Startup ### Init scripts
There is a [Dockerfile](docs/Dockerfile) that can be used to build a Docker image for running dyndnsd.rb. The [Debian 6 init.d script](init.d/debian-6-dyndnsd) assumes that dyndnsd.rb is installed into the system ruby (no RVM support) and the config.yaml is at /opt/dyndnsd/config.yaml. Modify to your needs.
The [Debian 6 init.d script](docs/debian-6-init-dyndnsd) assumes that dyndnsd.rb is installed into the system ruby (no RVM support) and the config.yaml is at /opt/dyndnsd/config.yaml. Modify to your needs.
### Monitoring ### Monitoring

View File

@@ -21,4 +21,4 @@ end
task default: [:rubocop, :spec, 'bundle:audit'] task default: [:rubocop, :spec, 'bundle:audit']
task travis: [:default, :'solargraph:init', :'solargraph:tc'] task travis: [:default, :'solargraph:tc']

View File

@@ -1,15 +0,0 @@
FROM alpine:3.12
EXPOSE 5353 8080
ARG DYNDNSD_VERSION=3.0.0
RUN apk --no-cache add openssl ca-certificates && \
apk --no-cache add ruby ruby-etc ruby-io-console ruby-json ruby-webrick && \
apk --no-cache add --virtual .build-deps ruby-dev build-base tzdata && \
gem install --no-document dyndnsd -v ${DYNDNSD_VERSION} && \
# set timezone to Berlin
cp /usr/share/zoneinfo/Europe/Berlin /etc/localtime && \
apk del .build-deps
ENTRYPOINT ["dyndnsd", "/etc/dyndnsd/config.yml"]

View File

@@ -25,10 +25,10 @@ Gem::Specification.new do |s|
s.executables = ['dyndnsd'] s.executables = ['dyndnsd']
s.extra_rdoc_files = Dir['README.md', 'CHANGELOG.md', 'LICENSE'] s.extra_rdoc_files = Dir['README.md', 'CHANGELOG.md', 'LICENSE']
s.required_ruby_version = '>= 2.5' s.required_ruby_version = '>= 2.3'
s.add_runtime_dependency 'async-dns', '~> 1.2.0' s.add_runtime_dependency 'async-dns', '~> 1.2.0'
s.add_runtime_dependency 'jaeger-client', '~> 1.0.0' s.add_runtime_dependency 'jaeger-client', '~> 0.10.0'
s.add_runtime_dependency 'metriks' s.add_runtime_dependency 'metriks'
s.add_runtime_dependency 'opentracing', '~> 0.5.0' s.add_runtime_dependency 'opentracing', '~> 0.5.0'
s.add_runtime_dependency 'rack', '~> 2.0' s.add_runtime_dependency 'rack', '~> 2.0'
@@ -39,6 +39,6 @@ Gem::Specification.new do |s|
s.add_development_dependency 'rack-test' s.add_development_dependency 'rack-test'
s.add_development_dependency 'rake' s.add_development_dependency 'rake'
s.add_development_dependency 'rspec' s.add_development_dependency 'rspec'
s.add_development_dependency 'rubocop', '~> 0.89.0' s.add_development_dependency 'rubocop', '~> 0.81.0'
s.add_development_dependency 'solargraph' s.add_development_dependency 'solargraph'
end end

0
docs/debian-6-init-dyndnsd → init.d/debian-6-dyndnsd Executable file → Normal file
View File

View File

@@ -1,6 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'date'
require 'etc' require 'etc'
require 'logger' require 'logger'
require 'ipaddr' require 'ipaddr'
@@ -81,7 +80,7 @@ module Dyndnsd
end end
# @param env [Hash{String => String}] # @param env [Hash{String => String}]
# @return [Array{Integer,Hash{String => String},Array<String>}] # @return [Array{Integer,Hash{String => String},Array{String}}]
def call(env) def call(env)
return [422, {'X-DynDNS-Response' => 'method_forbidden'}, []] if env['REQUEST_METHOD'] != 'GET' return [422, {'X-DynDNS-Response' => 'method_forbidden'}, []] if env['REQUEST_METHOD'] != 'GET'
return [422, {'X-DynDNS-Response' => 'not_found'}, []] if env['PATH_INFO'] != '/nic/update' return [422, {'X-DynDNS-Response' => 'not_found'}, []] if env['PATH_INFO'] != '/nic/update'
@@ -135,7 +134,7 @@ module Dyndnsd
private private
# @param params [Hash{String => String}] # @param params [Hash{String => String}]
# @return [Array<String>] # @return [Array{String}]
def extract_v4_and_v6_address(params) def extract_v4_and_v6_address(params)
return [] if !(params['myip']) return [] if !(params['myip'])
begin begin
@@ -149,7 +148,7 @@ module Dyndnsd
# @param env [Hash{String => String}] # @param env [Hash{String => String}]
# @param params [Hash{String => String}] # @param params [Hash{String => String}]
# @return [Array<String>] # @return [Array{String}]
def extract_myips(env, params) def extract_myips(env, params)
# require presence of myip parameter as valid IPAddr (v4) and valid myip6 # require presence of myip parameter as valid IPAddr (v4) and valid myip6
return extract_v4_and_v6_address(params) if params.key?('myip6') return extract_v4_and_v6_address(params) if params.key?('myip6')
@@ -165,8 +164,8 @@ module Dyndnsd
end end
# @param hostnames [String] # @param hostnames [String]
# @param myips [Array<String>] # @param myips [Array{String}]
# @return [Array<Symbol>] # @return [Array{Symbol}]
def process_changes(hostnames, myips) def process_changes(hostnames, myips)
changes = [] changes = []
Helper.span('process_changes') do |span| Helper.span('process_changes') do |span|
@@ -201,7 +200,7 @@ module Dyndnsd
end end
# @param env [Hash{String => String}] # @param env [Hash{String => String}]
# @return [Array{Integer,Hash{String => String},Array<String>}] # @return [Array{Integer,Hash{String => String},Array{String}}]
def handle_dyndns_request(env) def handle_dyndns_request(env)
params = Rack::Utils.parse_query(env['QUERY_STRING']) params = Rack::Utils.parse_query(env['QUERY_STRING'])
@@ -246,7 +245,7 @@ module Dyndnsd
if config['logfile'] if config['logfile']
Dyndnsd.logger = Logger.new(config['logfile']) Dyndnsd.logger = Logger.new(config['logfile'])
else else
Dyndnsd.logger = Logger.new($stdout) Dyndnsd.logger = Logger.new(STDOUT)
end end
Dyndnsd.logger.progname = 'dyndnsd' Dyndnsd.logger.progname = 'dyndnsd'

View File

@@ -27,7 +27,7 @@ module Dyndnsd
ips.each do |ip| ips.each do |ip|
ip = IPAddr.new(ip).native ip = IPAddr.new(ip).native
type = ip.ipv6? ? 'AAAA' : 'A' type = ip.ipv6? ? 'AAAA' : 'A'
name = hostname.chomp(".#{@domain}") name = hostname.chomp('.' + @domain)
out << "#{name} IN #{type} #{ip}" out << "#{name} IN #{type} #{ip}"
end end
end end

View File

@@ -9,7 +9,7 @@ module Dyndnsd
end end
# @param env [Hash{String => String}] # @param env [Hash{String => String}]
# @return [Array{Integer,Hash{String => String},Array<String>}] # @return [Array{Integer,Hash{String => String},Array{String}}]
def call(env) def call(env)
@app.call(env).tap do |status_code, headers, body| @app.call(env).tap do |status_code, headers, body|
if headers.key?('X-DynDNS-Response') if headers.key?('X-DynDNS-Response')
@@ -24,32 +24,30 @@ module Dyndnsd
# @param status_code [Integer] # @param status_code [Integer]
# @param headers [Hash{String => String}] # @param headers [Hash{String => String}]
# @param body [Array<String>] # @param body [Array{String}]
# @return [Array{Integer,Hash{String => String},Array<String>}] # @return [Array{Integer,Hash{String => String},Array{String}}]
def decorate_dyndnsd_response(status_code, headers, body) def decorate_dyndnsd_response(status_code, headers, body)
case status_code if status_code == 200
when 200
[200, {'Content-Type' => 'text/plain'}, [get_success_body(body[0], body[1])]] [200, {'Content-Type' => 'text/plain'}, [get_success_body(body[0], body[1])]]
when 422 elsif status_code == 422
error_response_map[headers['X-DynDNS-Response']] error_response_map[headers['X-DynDNS-Response']]
end end
end end
# @param status_code [Integer] # @param status_code [Integer]
# @param headers [Hash{String => String}] # @param headers [Hash{String => String}]
# @param _body [Array<String>] # @param _body [Array{String}]
# @return [Array{Integer,Hash{String => String},Array<String>}] # @return [Array{Integer,Hash{String => String},Array{String}}]
def decorate_other_response(status_code, headers, _body) def decorate_other_response(status_code, headers, _body)
case status_code if status_code == 400
when 400
[status_code, headers, ['Bad Request']] [status_code, headers, ['Bad Request']]
when 401 elsif status_code == 401
[status_code, headers, ['badauth']] [status_code, headers, ['badauth']]
end end
end end
# @param changes [Array<Symbol>] # @param changes [Array{Symbol}]
# @param myips [Array<String>] # @param myips [Array{String}]
# @return [String] # @return [String]
def get_success_body(changes, myips) def get_success_body(changes, myips)
changes.map { |change| "#{change} #{myips.join(' ')}" }.join("\n") changes.map { |change| "#{change} #{myips.join(' ')}" }.join("\n")

View File

@@ -9,7 +9,7 @@ module Dyndnsd
end end
# @param env [Hash{String => String}] # @param env [Hash{String => String}]
# @return [Array{Integer,Hash{String => String},Array<String>}] # @return [Array{Integer,Hash{String => String},Array{String}}]
def call(env) def call(env)
@app.call(env).tap do |status_code, headers, body| @app.call(env).tap do |status_code, headers, body|
if headers.key?('X-DynDNS-Response') if headers.key?('X-DynDNS-Response')
@@ -24,32 +24,30 @@ module Dyndnsd
# @param status_code [Integer] # @param status_code [Integer]
# @param headers [Hash{String => String}] # @param headers [Hash{String => String}]
# @param body [Array<String>] # @param body [Array{String}]
# @return [Array{Integer,Hash{String => String},Array<String>}] # @return [Array{Integer,Hash{String => String},Array{String}}]
def decorate_dyndnsd_response(status_code, headers, body) def decorate_dyndnsd_response(status_code, headers, body)
case status_code if status_code == 200
when 200
[200, {'Content-Type' => 'text/plain'}, [get_success_body(body[0], body[1])]] [200, {'Content-Type' => 'text/plain'}, [get_success_body(body[0], body[1])]]
when 422 elsif status_code == 422
error_response_map[headers['X-DynDNS-Response']] error_response_map[headers['X-DynDNS-Response']]
end end
end end
# @param status_code [Integer] # @param status_code [Integer]
# @param headers [Hash{String => String}] # @param headers [Hash{String => String}]
# @param _body [Array<String>] # @param _body [Array{String}]
# @return [Array{Integer,Hash{String => String},Array<String>}] # @return [Array{Integer,Hash{String => String},Array{String}}]
def decorate_other_response(status_code, headers, _body) def decorate_other_response(status_code, headers, _body)
case status_code if status_code == 400
when 400
[status_code, headers, ['Bad Request']] [status_code, headers, ['Bad Request']]
when 401 elsif status_code == 401
[status_code, headers, ['Unauthorized']] [status_code, headers, ['Unauthorized']]
end end
end end
# @param changes [Array<Symbol>] # @param changes [Array{Symbol}]
# @param myips [Array<String>] # @param myips [Array{String}]
# @return [String] # @return [String]
def get_success_body(changes, myips) def get_success_body(changes, myips)
changes.map { |change| change == :good ? "Changed to #{myips.join(' ')}" : "No change needed for #{myips.join(' ')}" }.join("\n") changes.map { |change| change == :good ? "Changed to #{myips.join(' ')}" : "No change needed for #{myips.join(' ')}" }.join("\n")

View File

@@ -28,9 +28,11 @@ module Dyndnsd
sleep @interval sleep @interval
Thread.new do Thread.new do
write begin
rescue StandardError => e write
@on_error[e] rescue nil rescue StandardError => e
@on_error[e] rescue nil
end
end end
end end
end end
@@ -94,8 +96,8 @@ module Dyndnsd
# @param file [String] # @param file [String]
# @param base_name [String] # @param base_name [String]
# @param metric [Object] # @param metric [Object]
# @param keys [Array<Symbol>] # @param keys [Array{Symbol}]
# @param snapshot_keys [Array<Symbol>] # @param snapshot_keys [Array{Symbol}]
# @return [void] # @return [void]
def write_metric(file, base_name, metric, keys, snapshot_keys = []) def write_metric(file, base_name, metric, keys, snapshot_keys = [])
time = Time.now.to_i time = Time.now.to_i

View File

@@ -85,7 +85,7 @@ module Dyndnsd
# converts into suitable parameter form for Async::DNS::Resolver or Async::DNS::Server # converts into suitable parameter form for Async::DNS::Resolver or Async::DNS::Server
# #
# @param endpoint_list [Array<String>] # @param endpoint_list [Array{String}]
# @return [Array{Array{Object}}] # @return [Array{Array{Object}}]
def self.parse_endpoints(endpoint_list) def self.parse_endpoints(endpoint_list)
endpoint_list.map { |addr_string| addr_string.split('@') } endpoint_list.map { |addr_string| addr_string.split('@') }
@@ -139,7 +139,7 @@ module Dyndnsd
# @param name [String] # @param name [String]
# @param resource_class [Resolv::DNS::Resource] # @param resource_class [Resolv::DNS::Resource]
# Since solargraph cannot parse this: param transaction [Async::DNS::Transaction] # @param transaction [Async::DNS::Transaction]
# @return [void] # @return [void]
def process(name, resource_class, transaction) def process(name, resource_class, transaction)
if name != @domain || resource_class != Resolv::DNS::Resource::Generic::Type252_Class1 if name != @domain || resource_class != Resolv::DNS::Resource::Generic::Type252_Class1

View File

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

View File

@@ -6,7 +6,7 @@ describe Dyndnsd::Daemon do
include Rack::Test::Methods include Rack::Test::Methods
def app def app
Dyndnsd.logger = Logger.new($stdout) Dyndnsd.logger = Logger.new(STDOUT)
Dyndnsd.logger.level = Logger::UNKNOWN Dyndnsd.logger.level = Logger::UNKNOWN
config = { config = {