Compare commits

..

No commits in common. "master" and "1.0.0" have entirely different histories.

30 changed files with 171 additions and 629 deletions

View file

@ -1,26 +0,0 @@
---
name: 'build go project'
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.20'
- name: Build
run: |
cd dyndns
go build -v ./...
- name: Test
run: |
cd dyndns
go test -v ./...

View file

@ -1,54 +0,0 @@
---
name: 'build images'
on:
push:
tags:
- '*'
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Prepare
id: prep
run: |
DOCKER_IMAGE=${{ secrets.DOCKER_USERNAME }}/${GITHUB_REPOSITORY#*/}
VERSION=${GITHUB_REF/refs\/tags\//}
SHORTREF=${GITHUB_SHA::8}
TAGS="${DOCKER_IMAGE}:${VERSION},${DOCKER_IMAGE}:latest"
# Set output parameters
echo ::set-output name=version::${VERSION}
echo ::set-output name=tags::${TAGS}
echo ::set-output name=docker_image::${DOCKER_IMAGE}
- name: Get the version
id: get_version
run: echo ::set-output name=version::${GITHUB_REF/refs\/tags\//}
- name: Set up QEMU
uses: docker/setup-qemu-action@master
with:
platforms: linux/amd64,linux/arm/v7,linux/arm64
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@master
- name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build
uses: docker/build-push-action@v2
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
file: ./deployment/Dockerfile
platforms: linux/amd64,linux/arm64,linux/arm
push: true
tags: ${{ steps.prep.outputs.tags }}

22
.travis.yml Normal file
View file

@ -0,0 +1,22 @@
language: go
env: GO111MODULE=on
go:
- 1.13.x
- 1.14.x
git:
depth: 1
before_script:
- cd dyndns
script:
- go test -v ./...
- go build ./...
notifications:
email:
on_success: never
on_failure: always

View file

@ -1,12 +1,14 @@
# Dynamic DNS Server for Docker with Web UI written in Go
![Travis build status](https://travis-ci.com/benjaminbear/docker-ddns-server.svg?branch=master)
![Docker build status](https://img.shields.io/docker/cloud/build/bbaerthlein/docker-ddns-server)
![Docker build automated](https://img.shields.io/docker/cloud/automated/bbaerthlein/docker-ddns-server)
![GitHub release (latest by date)](https://img.shields.io/github/v/release/benjaminbear/docker-ddns-server)
![Go version](https://img.shields.io/github/go-mod/go-version/benjaminbear/docker-ddns-server?filename=dyndns%2Fgo.mod)
![License](https://img.shields.io/github/license/benjaminbear/docker-ddns-server)
With docker-ddns-server you can set up your own dynamic DNS server. This project is inspired by https://github.com/dprandzioch/docker-ddns . In addition to the original version, you can setup and maintain your dyndns entries via simple web ui.
With docker-ddns-server you can setup your own dynamic DNS server. This project is inspired by https://github.com/dprandzioch/docker-ddns . In addition to the original version, you can setup and maintain your dyndns entries via simple web ui.
<p float="left">
<img src="https://raw.githubusercontent.com/benjaminbear/docker-ddns-server/master/img/addhost.png" width="285">
@ -28,7 +30,7 @@ docker run -it -d \
-p 53:53 \
-p 53:53/udp \
-v /somefolder:/var/cache/bind \
-v /someotherfolder:/root/database \
-v /someotherfolder:/root/dyndns/database \
-e DDNS_ADMIN_LOGIN=admin:123455546. \
-e DDNS_DOMAINS=dyndns.example.com \
-e DDNS_PARENT_NS=ns.example.com \
@ -51,7 +53,6 @@ If you want to embed this into a docker-compose.yml you have to double the dolla
```
echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g
```
If `DDNS_ADMIN_LOGIN` is not set, all /admin routes are without protection. (use case: auth proxy)
`DDNS_DOMAINS` are the domains of the webservice and the domain zones of your dyndns server (see DNS Setup) i.e. `dyndns.example.com,dyndns.example.org` (comma separated list)
@ -59,12 +60,6 @@ If `DDNS_ADMIN_LOGIN` is not set, all /admin routes are without protection. (use
`DDNS_DEFAULT_TTL` is the default TTL of your dyndns server.
`DDNS_CLEAR_LOG_INTERVAL` optional: clear log entries automatically in days (integer) e.g. `DDNS_CLEAR_LOG_INTERVAL:30`
`DDNS_ALLOW_WILDCARD` optional: allows all `*.subdomain.dyndns.example.com` to point to your ip (boolean) e.g. `true`
`DDNS_LOGOUT_URL` optional: allows a logout redirect to certain url by clicking the logout button (string) e.g. `https://example.com`
### DNS setup
If your parent domain is `example.com` and you want your dyndns domain to be `dyndns.example.com`,

View file

@ -1,16 +1,12 @@
FROM golang:1.20 as builder
FROM golang:latest as builder
ENV GO111MODULE=on
ENV GOPATH=/root/go
RUN mkdir -p /root/go/src
COPY dyndns /root/go/src/dyndns
WORKDIR /root/go/src/dyndns
# temp sqlite3 error fix
ENV CGO_CFLAGS "-g -O2 -Wno-return-local-addr"
RUN go mod tidy
RUN GOOS=linux go build -o /root/go/bin/dyndns && go test -v
RUN cd /root/go/src/dyndns && go mod download && GOOS=linux GOARCH=amd64 go build -o /root/go/bin/dyndns && go test -v
FROM debian:11-slim
FROM debian:buster-slim
RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
apt-get install -q -y bind9 dnsutils curl && \
@ -27,4 +23,4 @@ COPY dyndns/views /root/views
COPY dyndns/static /root/static
EXPOSE 53 8080
CMD ["sh", "-c", "/root/setup.sh ; service named start ; /root/dyndns"]
CMD ["sh", "-c", "/root/setup.sh ; service bind9 start ; /root/dyndns"]

View file

@ -14,4 +14,4 @@ services:
- "8080:8080"
volumes:
- ./bind-data:/var/cache/bind
- ./database:/root/database
- ./database:/root/dyndns/database

View file

@ -1,6 +1,6 @@
#!/bin/bash
#[ -z "$DDNS_ADMIN_LOGIN" ] && echo "DDNS_ADMIN_LOGIN not set" && exit 1;
[ -z "$DDNS_ADMIN_LOGIN" ] && echo "DDNS_ADMIN_LOGIN not set" && exit 1;
[ -z "$DDNS_DOMAINS" ] && echo "DDNS_DOMAINS not set" && exit 1;
[ -z "$DDNS_PARENT_NS" ] && echo "DDNS_PARENT_NS not set" && exit 1;
[ -z "$DDNS_DEFAULT_TTL" ] && echo "DDNS_DEFAULT_TTL not set" && exit 1;
@ -48,4 +48,4 @@ done
chown root:bind /var/cache/bind
chown bind:bind /var/cache/bind/*
chmod 770 /var/cache/bind
chmod 644 /var/cache/bind/*
chmod 644 /var/cache/bind/*

View file

@ -1,32 +1,12 @@
module github.com/benjaminbear/docker-ddns-server/dyndns
go 1.20
go 1.16
require (
github.com/foolin/goview v0.3.0
github.com/go-playground/validator/v10 v10.14.0
github.com/go-playground/validator/v10 v10.8.0
github.com/jinzhu/gorm v1.9.16
github.com/labstack/echo/v4 v4.10.2
github.com/labstack/gommon v0.4.0
github.com/tg123/go-htpasswd v1.2.1
)
require (
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-sqlite3 v1.14.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
golang.org/x/crypto v0.7.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
golang.org/x/time v0.3.0 // indirect
github.com/labstack/echo/v4 v4.4.0
github.com/labstack/gommon v0.3.0
github.com/tg123/go-htpasswd v1.0.0
)

View file

@ -1,135 +0,0 @@
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 h1:KeNholpO2xKjgaaSyd+DyQRrsQjhbSeS7qe4nEw8aQw=
github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/foolin/goview v0.3.0 h1:q5wKwXKEFb20dMRfYd59uj5qGCo7q4L9eVHHUjmMWrg=
github.com/foolin/goview v0.3.0/go.mod h1:OC1VHC4FfpWymhShj8L1Tc3qipFmrmm+luAEdTvkos4=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/labstack/echo/v4 v4.1.6/go.mod h1:kU/7PwzgNxZH4das4XNsSpBSOD09XIF5YEPzjpkGnGE=
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
github.com/labstack/gommon v0.2.9/go.mod h1:E8ZTmW9vw5az5/ZyHWCp0Lw4OH2ecsaBP1C/NKavGG4=
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tg123/go-htpasswd v1.2.1 h1:i4wfsX1KvvkyoMiHZzjS0VzbAPWfxzI8INcZAKtutoU=
github.com/tg123/go-htpasswd v1.2.1/go.mod h1:erHp1B86KXdwQf1X5ZrLb7erXZnWueEQezb2dql4q58=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190607181551-461777fb6f67/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190609082536-301114b31cce/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190608022120-eacb66d2a7c3/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -24,7 +24,6 @@ func (h *Handler) ListCNames(c echo.Context) (err error) {
return c.Render(http.StatusOK, "listcnames", echo.Map{
"cnames": cnames,
"title": h.Title,
})
}
@ -43,7 +42,6 @@ func (h *Handler) AddCName(c echo.Context) (err error) {
return c.Render(http.StatusOK, "addcname", echo.Map{
"config": h.Config,
"hosts": hosts,
"title": h.Title,
})
}
@ -79,7 +77,7 @@ func (h *Handler) CreateCName(c echo.Context) (err error) {
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
}
if err = nswrapper.UpdateRecord(cname.Hostname, fmt.Sprintf("%s.%s", cname.Target.Hostname, cname.Target.Domain), "CNAME", cname.Target.Domain, cname.Ttl, h.AllowWildcard); err != nil {
if err = nswrapper.UpdateRecord(cname.Hostname, fmt.Sprintf("%s.%s", cname.Target.Hostname, cname.Target.Domain), "CNAME", cname.Target.Domain, cname.Ttl); err != nil {
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
}
@ -114,7 +112,7 @@ func (h *Handler) DeleteCName(c echo.Context) (err error) {
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
}
if err = nswrapper.DeleteRecord(cname.Hostname, cname.Target.Domain, h.AllowWildcard); err != nil {
if err = nswrapper.DeleteRecord(cname.Hostname, cname.Target.Domain); err != nil {
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
}

View file

@ -2,12 +2,8 @@ package handler
import (
"fmt"
"github.com/labstack/gommon/log"
"os"
"strconv"
"strings"
"time"
"github.com/benjaminbear/docker-ddns-server/dyndns/model"
"github.com/go-playground/validator/v10"
@ -17,15 +13,10 @@ import (
)
type Handler struct {
DB *gorm.DB
AuthAdmin bool
Config Envs
Title string
DisableAdminAuth bool
LastClearedLogs time.Time
ClearInterval uint64
AllowWildcard bool
LogoutUrl string
DB *gorm.DB
AuthHost *model.Host
AuthAdmin bool
Config Envs
}
type Envs struct {
@ -48,33 +39,13 @@ type Error struct {
// Authenticate is the method the website admin user and the host update user have to authenticate against.
// To gather admin rights the username password combination must match with the credentials given by the env var.
func (h *Handler) AuthenticateUpdate(username, password string, c echo.Context) (bool, error) {
h.CheckClearInterval()
reqParameter := c.QueryParam("hostname")
reqArr := strings.SplitN(reqParameter, ".", 2)
if len(reqArr) != 2 {
log.Error("Error: Something wrong with the hostname parameter")
return false, nil
}
host := &model.Host{}
if err := h.DB.Where(&model.Host{UserName: username, Password: password, Hostname: reqArr[0], Domain: reqArr[1]}).First(host).Error; err != nil {
log.Error("Error: ", err)
return false, nil
}
if host.ID == 0 {
log.Error("hostname or user user credentials unknown")
return false, nil
}
c.Set("updateHost", host)
return true, nil
}
func (h *Handler) AuthenticateAdmin(username, password string, c echo.Context) (bool, error) {
func (h *Handler) Authenticate(username, password string, c echo.Context) (bool, error) {
h.AuthHost = nil
h.AuthAdmin = false
ok, err := h.authByEnv(username, password)
if err != nil {
log.Error("Error:", err)
fmt.Println("Error:", err)
return false, nil
}
@ -83,8 +54,17 @@ func (h *Handler) AuthenticateAdmin(username, password string, c echo.Context) (
return true, nil
}
return false, nil
host := &model.Host{}
if err := h.DB.Where(&model.Host{UserName: username, Password: password}).First(host).Error; err != nil {
fmt.Println("Error:", err)
return false, nil
}
h.AuthHost = host
return true, nil
}
func (h *Handler) authByEnv(username, password string) (bool, error) {
hashReader := strings.NewReader(h.Config.AdminLogin)
@ -103,55 +83,19 @@ func (h *Handler) authByEnv(username, password string) (bool, error) {
// ParseEnvs parses all needed environment variables:
// DDNS_ADMIN_LOGIN: The basic auth login string in htpasswd style.
// DDNS_DOMAINS: All domains that will be handled by the dyndns server.
func (h *Handler) ParseEnvs() (adminAuth bool, err error) {
log.Info("Read environment variables")
func (h *Handler) ParseEnvs() error {
h.Config = Envs{}
adminAuth = true
h.Config.AdminLogin = os.Getenv("DDNS_ADMIN_LOGIN")
if h.Config.AdminLogin == "" {
log.Info("No Auth! DDNS_ADMIN_LOGIN should be set")
adminAuth = false
h.AuthAdmin = true
h.DisableAdminAuth = true
}
var ok bool
h.Title, ok = os.LookupEnv("DDNS_TITLE")
if !ok {
h.Title = "TheBBCloud DynDNS"
}
allowWildcard, ok := os.LookupEnv("DDNS_ALLOW_WILDCARD")
if ok {
h.AllowWildcard, err = strconv.ParseBool(allowWildcard)
if err == nil {
log.Info("Wildcard allowed")
}
}
logoutUrl, ok := os.LookupEnv("DDNS_LOGOUT_URL")
if ok {
if len(logoutUrl) > 0 {
log.Info("Logout url set: ", logoutUrl)
h.LogoutUrl = logoutUrl
}
}
clearEnv := os.Getenv("DDNS_CLEAR_LOG_INTERVAL")
clearInterval, err := strconv.ParseUint(clearEnv, 10, 32)
if err != nil {
log.Info("No log clear interval found")
} else {
log.Info("log clear interval found: ", clearInterval, "days")
h.ClearInterval = clearInterval
if clearInterval > 0 {
h.LastClearedLogs = time.Now()
}
return fmt.Errorf("environment variable DDNS_ADMIN_LOGIN has to be set")
}
h.Config.Domains = strings.Split(os.Getenv("DDNS_DOMAINS"), ",")
if len(h.Config.Domains) < 1 {
return adminAuth, fmt.Errorf("environment variable DDNS_DOMAINS has to be set")
return fmt.Errorf("environment variable DDNS_DOMAINS has to be set")
}
return adminAuth, nil
return nil
}
// InitDB creates an empty database and creates all tables if there isn't already one, or opens the existing one.
@ -182,19 +126,3 @@ func (h *Handler) InitDB() (err error) {
return nil
}
// Check if a log cleaning is needed
func (h *Handler) CheckClearInterval() {
if !h.LastClearedLogs.IsZero() {
if !DateEqual(time.Now(), h.LastClearedLogs) {
go h.ClearLogs()
}
}
}
// compare two dates
func DateEqual(date1, date2 time.Time) bool {
y1, m1, d1 := date1.Date()
y2, m2, d2 := date2.Date()
return y1 == y2 && m1 == m2 && d1 == d2
}

View file

@ -2,7 +2,6 @@ package handler
import (
"fmt"
l "github.com/labstack/gommon/log"
"net"
"net/http"
"strconv"
@ -52,7 +51,6 @@ func (h *Handler) ListHosts(c echo.Context) (err error) {
return c.Render(http.StatusOK, "listhosts", echo.Map{
"hosts": hosts,
"title": h.Title,
})
}
@ -65,7 +63,6 @@ func (h *Handler) AddHost(c echo.Context) (err error) {
return c.Render(http.StatusOK, "edithost", echo.Map{
"addEdit": "add",
"config": h.Config,
"title": h.Title,
})
}
@ -89,7 +86,6 @@ func (h *Handler) EditHost(c echo.Context) (err error) {
"host": host,
"addEdit": "edit",
"config": h.Config,
"title": h.Title,
})
}
@ -113,7 +109,7 @@ func (h *Handler) CreateHost(c echo.Context) (err error) {
if err = h.checkUniqueHostname(host.Hostname, host.Domain); err != nil {
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
}
host.LastUpdate = time.Now()
if err = h.DB.Create(host).Error; err != nil {
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
}
@ -125,7 +121,7 @@ func (h *Handler) CreateHost(c echo.Context) (err error) {
return c.JSON(http.StatusBadRequest, &Error{fmt.Sprintf("ip %s is not a valid ip", host.Ip)})
}
if err = nswrapper.UpdateRecord(host.Hostname, host.Ip, ipType, host.Domain, host.Ttl, h.AllowWildcard); err != nil {
if err = nswrapper.UpdateRecord(host.Hostname, host.Ip, ipType, host.Domain, host.Ttl); err != nil {
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
}
}
@ -172,7 +168,7 @@ func (h *Handler) UpdateHost(c echo.Context) (err error) {
return c.JSON(http.StatusBadRequest, &Error{fmt.Sprintf("ip %s is not a valid ip", host.Ip)})
}
if err = nswrapper.UpdateRecord(host.Hostname, host.Ip, ipType, host.Domain, host.Ttl, h.AllowWildcard); err != nil {
if err = nswrapper.UpdateRecord(host.Hostname, host.Ip, ipType, host.Domain, host.Ttl); err != nil {
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
}
}
@ -216,7 +212,7 @@ func (h *Handler) DeleteHost(c echo.Context) (err error) {
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
}
if err = nswrapper.DeleteRecord(host.Hostname, host.Domain, h.AllowWildcard); err != nil {
if err = nswrapper.DeleteRecord(host.Hostname, host.Domain); err != nil {
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
}
@ -227,12 +223,11 @@ func (h *Handler) DeleteHost(c echo.Context) (err error) {
// Hostname, IP and senders IP are validated, a log entry is created
// and finally if everything is ok, the DNS Server will be updated
func (h *Handler) UpdateIP(c echo.Context) (err error) {
host, ok := c.Get("updateHost").(*model.Host)
if !ok {
if h.AuthHost == nil {
return c.String(http.StatusBadRequest, "badauth\n")
}
log := &model.Log{Status: false, Host: *host, TimeStamp: time.Now(), UserAgent: nswrapper.ShrinkUserAgent(c.Request().UserAgent())}
log := &model.Log{Status: false, Host: *h.AuthHost, TimeStamp: time.Now(), UserAgent: nswrapper.ShrinkUserAgent(c.Request().UserAgent())}
log.SentIP = c.QueryParam(("myip"))
// Get caller IP
@ -242,7 +237,7 @@ func (h *Handler) UpdateIP(c echo.Context) (err error) {
if err != nil {
log.Message = "Bad Request: Unable to get caller IP"
if err = h.CreateLogEntry(log); err != nil {
l.Error(err)
fmt.Println(err)
}
return c.String(http.StatusBadRequest, "badrequest\n")
@ -251,10 +246,10 @@ func (h *Handler) UpdateIP(c echo.Context) (err error) {
// Validate hostname
hostname := c.QueryParam("hostname")
if hostname == "" || hostname != host.Hostname+"."+host.Domain {
if hostname == "" || hostname != h.AuthHost.Hostname+"."+h.AuthHost.Domain {
log.Message = "Hostname or combination of authenticated user and hostname is invalid"
if err = h.CreateLogEntry(log); err != nil {
l.Error(err)
fmt.Println(err)
}
return c.String(http.StatusBadRequest, "notfqdn\n")
@ -268,7 +263,7 @@ func (h *Handler) UpdateIP(c echo.Context) (err error) {
if ipType == "" {
log.Message = "Bad Request: Sent IP is invalid"
if err = h.CreateLogEntry(log); err != nil {
l.Error(err)
fmt.Println(err)
}
return c.String(http.StatusBadRequest, "badrequest\n")
@ -276,12 +271,12 @@ func (h *Handler) UpdateIP(c echo.Context) (err error) {
}
// add/update DNS record
if err = nswrapper.UpdateRecord(log.Host.Hostname, log.SentIP, ipType, log.Host.Domain, log.Host.Ttl, h.AllowWildcard); err != nil {
if err = nswrapper.UpdateRecord(log.Host.Hostname, log.SentIP, ipType, log.Host.Domain, log.Host.Ttl); err != nil {
log.Message = fmt.Sprintf("DNS error: %v", err)
l.Error(log.Message)
if err = h.CreateLogEntry(log); err != nil {
l.Error(err)
fmt.Println(err)
}
return c.String(http.StatusBadRequest, "dnserr\n")
}
@ -290,7 +285,7 @@ func (h *Handler) UpdateIP(c echo.Context) (err error) {
log.Status = true
log.Message = "No errors occurred"
if err = h.CreateLogEntry(log); err != nil {
l.Error(err)
fmt.Println(err)
}
return c.String(http.StatusOK, "good\n")

View file

@ -1,10 +1,8 @@
package handler
import (
"log"
"net/http"
"strconv"
"time"
"github.com/benjaminbear/docker-ddns-server/dyndns/model"
"github.com/labstack/echo/v4"
@ -26,13 +24,12 @@ func (h *Handler) ShowLogs(c echo.Context) (err error) {
}
logs := new([]model.Log)
if err = h.DB.Preload("Host").Limit(30).Order("created_at desc").Find(logs).Error; err != nil {
if err = h.DB.Preload("Host").Limit(30).Find(logs).Error; err != nil {
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
}
return c.Render(http.StatusOK, "listlogs", echo.Map{
"logs": logs,
"title": h.Title,
"logs": logs,
})
}
@ -48,19 +45,11 @@ func (h *Handler) ShowHostLogs(c echo.Context) (err error) {
}
logs := new([]model.Log)
if err = h.DB.Preload("Host").Where(&model.Log{HostID: uint(id)}).Order("created_at desc").Limit(30).Find(logs).Error; err != nil {
if err = h.DB.Preload("Host").Where(&model.Log{HostID: uint(id)}).Limit(30).Find(logs).Error; err != nil {
return c.JSON(http.StatusBadRequest, &Error{err.Error()})
}
return c.Render(http.StatusOK, "listlogs", echo.Map{
"logs": logs,
"title": h.Title,
"logs": logs,
})
}
func (h *Handler) ClearLogs() {
var clearInterval = strconv.FormatUint(h.ClearInterval, 10) + " day"
h.DB.Exec("DELETE FROM LOGS WHERE created_at < datetime('now', '-" + clearInterval + "');REINDEX LOGS;")
h.LastClearedLogs = time.Now()
log.Print("logs cleared")
}

View file

@ -1,21 +1,18 @@
package main
import (
"net/http"
"github.com/benjaminbear/docker-ddns-server/dyndns/handler"
"github.com/foolin/goview"
"github.com/foolin/goview/supports/echoview-v4"
"github.com/go-playground/validator/v10"
_ "github.com/jinzhu/gorm/dialects/sqlite"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/labstack/gommon/log"
"html/template"
"net/http"
"time"
)
func main() {
// Set new instance
e := echo.New()
e.Logger.SetLevel(log.ERROR)
@ -23,17 +20,7 @@ func main() {
e.Use(middleware.Logger())
// Set Renderer
e.Renderer = echoview.New(goview.Config{
Root: "views",
Master: "layouts/master",
Extension: ".html",
Funcs: template.FuncMap{
"year": func() string {
return time.Now().Format("2006")
},
},
DisableCache: true,
})
e.Renderer = echoview.Default()
// Set Validator
e.Validator = &handler.CustomValidator{Validator: validator.New()}
@ -50,69 +37,38 @@ func main() {
}
defer h.DB.Close()
authAdmin, err := h.ParseEnvs()
if err != nil {
if err := h.ParseEnvs(); err != nil {
e.Logger.Fatal(err)
}
// UI Routes
groupPublic := e.Group("/")
groupPublic.GET("*", func(c echo.Context) error {
//redirect to admin
return c.Redirect(301, "./admin/")
})
groupAdmin := e.Group("/admin")
if authAdmin {
groupAdmin.Use(middleware.BasicAuth(h.AuthenticateAdmin))
}
e.Use(middleware.BasicAuth(h.Authenticate))
groupAdmin.GET("/", h.ListHosts)
groupAdmin.GET("/hosts/add", h.AddHost)
groupAdmin.GET("/hosts/edit/:id", h.EditHost)
groupAdmin.GET("/hosts", h.ListHosts)
groupAdmin.GET("/cnames/add", h.AddCName)
groupAdmin.GET("/cnames", h.ListCNames)
groupAdmin.GET("/logs", h.ShowLogs)
groupAdmin.GET("/logs/host/:id", h.ShowHostLogs)
// UI Routes
e.GET("/", func(c echo.Context) error {
//render with master
return c.Render(http.StatusOK, "listhosts", nil)
})
e.GET("/hosts/add", h.AddHost)
e.GET("/hosts/edit/:id", h.EditHost)
e.GET("/hosts", h.ListHosts)
e.GET("/cnames/add", h.AddCName)
e.GET("/cnames", h.ListCNames)
e.GET("/logs", h.ShowLogs)
e.GET("/logs/host/:id", h.ShowHostLogs)
// Rest Routes
groupAdmin.POST("/hosts/add", h.CreateHost)
groupAdmin.POST("/hosts/edit/:id", h.UpdateHost)
groupAdmin.GET("/hosts/delete/:id", h.DeleteHost)
//redirect to logout
groupAdmin.GET("/logout", func(c echo.Context) error {
// either custom url
if len(h.LogoutUrl) > 0 {
return c.Redirect(302, h.LogoutUrl)
}
// or standard url
return c.Redirect(302, "../")
})
groupAdmin.POST("/cnames/add", h.CreateCName)
groupAdmin.GET("/cnames/delete/:id", h.DeleteCName)
e.POST("/hosts/add", h.CreateHost)
e.POST("/hosts/edit/:id", h.UpdateHost)
e.GET("/hosts/delete/:id", h.DeleteHost)
e.POST("/cnames/add", h.CreateCName)
e.GET("/cnames/delete/:id", h.DeleteCName)
// dyndns compatible api
// (avoid breaking changes and create groups for each update endpoint)
updateRoute := e.Group("/update")
updateRoute.Use(middleware.BasicAuth(h.AuthenticateUpdate))
updateRoute.GET("", h.UpdateIP)
nicRoute := e.Group("/nic")
nicRoute.Use(middleware.BasicAuth(h.AuthenticateUpdate))
nicRoute.GET("/update", h.UpdateIP)
v2Route := e.Group("/v2")
v2Route.Use(middleware.BasicAuth(h.AuthenticateUpdate))
v2Route.GET("/update", h.UpdateIP)
v3Route := e.Group("/v3")
v3Route.Use(middleware.BasicAuth(h.AuthenticateUpdate))
v3Route.GET("/update", h.UpdateIP)
// health-check
e.GET("/ping", func(c echo.Context) error {
u := &handler.Error{
Message: "OK",
}
return c.JSON(http.StatusOK, u)
})
e.GET("/update", h.UpdateIP)
e.GET("/nic/update", h.UpdateIP)
e.GET("/v2/update", h.UpdateIP)
e.GET("/v3/update", h.UpdateIP)
// Start server
e.Logger.Fatal(e.Start(":8080"))

View file

@ -11,10 +11,10 @@ type Host struct {
gorm.Model
Hostname string `gorm:"unique_index:idx_host_domain;not null" form:"hostname" validate:"required,hostname"`
Domain string `gorm:"unique_index:idx_host_domain;not null" form:"domain" validate:"required,hostname"`
Ip string `form:"ip" validate:"omitempty,ipv4|ipv6"`
Ip string `form:"ip" validate:"omitempty,ipv4"`
Ttl int `form:"ttl" validate:"required,min=20,max=86400"`
LastUpdate time.Time `form:"lastupdate"`
UserName string `gorm:"unique" form:"username" validate:"min=3"`
UserName string `gorm:"unique" form:"username" validate:"min=8"`
Password string `form:"password" validate:"min=8"`
}

View file

@ -3,7 +3,7 @@ package nswrapper
import (
"bytes"
"errors"
"github.com/labstack/gommon/log"
"fmt"
"net"
"net/http"
"strings"
@ -25,7 +25,7 @@ func GetIPType(ipAddr string) string {
// GetCallerIP searches for the "real" IP senders has actually.
// If its a private address we won't use it.
func GetCallerIP(r *http.Request) (string, error) {
log.Info("request", r.Header)
fmt.Println("request", r.Header)
for _, h := range []string{"X-Real-Ip", "X-Forwarded-For"} {
addresses := strings.Split(r.Header.Get(h), ",")
// march from right to left until we get a public address

View file

@ -4,15 +4,14 @@ import (
"bufio"
"bytes"
"fmt"
"github.com/labstack/gommon/log"
"io/ioutil"
"os"
"os/exec"
)
// UpdateRecord builds a nsupdate file and updates a record by executing it with nsupdate.
func UpdateRecord(hostname string, target string, addrType string, zone string, ttl int, enableWildcard bool) error {
log.Info(fmt.Sprintf("%s record update request: %s -> %s", addrType, hostname, target))
func UpdateRecord(hostname string, target string, addrType string, zone string, ttl int) error {
fmt.Printf("%s record update request: %s -> %s\n", addrType, hostname, target)
f, err := ioutil.TempFile(os.TempDir(), "dyndns")
if err != nil {
@ -25,13 +24,7 @@ func UpdateRecord(hostname string, target string, addrType string, zone string,
w.WriteString(fmt.Sprintf("server %s\n", "localhost"))
w.WriteString(fmt.Sprintf("zone %s\n", zone))
w.WriteString(fmt.Sprintf("update delete %s.%s %s\n", hostname, zone, addrType))
if enableWildcard {
w.WriteString(fmt.Sprintf("update delete %s.%s %s\n", "*."+hostname, zone, addrType))
}
w.WriteString(fmt.Sprintf("update add %s.%s %v %s %s\n", hostname, zone, ttl, addrType, target))
if enableWildcard {
w.WriteString(fmt.Sprintf("update add %s.%s %v %s %s\n", "*."+hostname, zone, ttl, addrType, target))
}
w.WriteString("send\n")
w.Flush()
@ -55,7 +48,7 @@ func UpdateRecord(hostname string, target string, addrType string, zone string,
}
// DeleteRecord builds a nsupdate file and deletes a record by executing it with nsupdate.
func DeleteRecord(hostname string, zone string, enableWildcard bool) error {
func DeleteRecord(hostname string, zone string) error {
fmt.Printf("record delete request: %s\n", hostname)
f, err := ioutil.TempFile(os.TempDir(), "dyndns")
@ -69,9 +62,6 @@ func DeleteRecord(hostname string, zone string, enableWildcard bool) error {
w.WriteString(fmt.Sprintf("server %s\n", "localhost"))
w.WriteString(fmt.Sprintf("zone %s\n", zone))
w.WriteString(fmt.Sprintf("update delete %s.%s\n", hostname, zone))
if enableWildcard {
w.WriteString(fmt.Sprintf("update delete %s.%s\n", "*."+hostname, zone))
}
w.WriteString("send\n")
w.Flush()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1,18 +1,18 @@
$("button.addHost").click(function () {
location.href='/admin/hosts/add';
location.href='/hosts/add';
});
$("button.editHost").click(function () {
location.href='/admin/hosts/edit/' + $(this).attr('id');
location.href='/hosts/edit/' + $(this).attr('id');
});
$("button.deleteHost").click(function () {
$.ajax({
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
type: 'GET',
url: "/admin/hosts/delete/" + $(this).attr('id')
url: "/hosts/delete/" + $(this).attr('id')
}).done(function(data, textStatus, jqXHR) {
location.href="/admin/hosts";
location.href="/hosts";
}).fail(function(jqXHR, textStatus, errorThrown) {
alert("Error: " + $.parseJSON(jqXHR.responseText).message);
location.reload()
@ -20,7 +20,7 @@ $("button.deleteHost").click(function () {
});
$("button.showHostLog").click(function () {
location.href='/admin/logs/host/' + $(this).attr('id');
location.href='/logs/host/' + $(this).attr('id');
});
$("button.add, button.edit").click(function () {
@ -53,9 +53,9 @@ $("button.add, button.edit").click(function () {
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
data: $('#editHostForm').serialize(),
type: 'POST',
url: '/admin/'+type+'/'+action+id,
url: '/'+type+'/'+action+id,
}).done(function(data, textStatus, jqXHR) {
location.href="/admin/"+type;
location.href="/"+type;
}).fail(function(jqXHR, textStatus, errorThrown) {
alert("Error: " + $.parseJSON(jqXHR.responseText).message);
});
@ -64,7 +64,6 @@ $("button.add, button.edit").click(function () {
});
$("#logout").click(function (){
//document.execCommand("ClearAuthenticationCache");
try {
// This is for Firefox
$.ajax({
@ -74,7 +73,7 @@ $("#logout").click(function (){
password: 'reset',
// If the return is 401, refresh the page to request new details.
statusCode: { 401: function() {
// document.location = document.location;
document.location = document.location;
}
}
});
@ -84,23 +83,22 @@ $("#logout").click(function (){
if (!document.execCommand("ClearAuthenticationCache")) {
// exeCommand returns false if it didn't work (which happens in Chrome) so as a last
// resort refresh the page providing new, invalid details.
// document.location = location.protocol+"//reset:reset@" + document.location.hostname + document.location.pathname;
document.location = "http://reset:reset@" + document.location.hostname + document.location.pathname;
}
}
console.log("first logout")
});
$("button.addCName").click(function () {
location.href='/admin/cnames/add';
location.href='/cnames/add';
});
$("button.deleteCName").click(function () {
$.ajax({
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
type: 'GET',
url: "/admin/cnames/delete/" + $(this).attr('id')
url: "/cnames/delete/" + $(this).attr('id')
}).done(function(data, textStatus, jqXHR) {
location.href="/admin/cnames";
location.href="/cnames";
}).fail(function(jqXHR, textStatus, errorThrown) {
alert("Error: " + $.parseJSON(jqXHR.responseText).message);
location.reload()
@ -126,33 +124,15 @@ $("button.copyToClipboard").click(function () {
copyText.setSelectionRange(0, 99999);
document.execCommand("copy");
});
$("button.copyUrlToClipboard").click(function () {
let id = $(this).attr('id');
let hostname = document.getElementById('host-hostname_'+id).innerHTML
let domain = document.getElementById('host-domain_'+id).innerHTML
let username = document.getElementById('host-username_'+id).innerHTML
let password = document.getElementById('host-password_'+id).innerHTML
let out = location.protocol + '//' +username.trim()+':'+password.trim()+'@'+ domain
out +='/update?hostname='+hostname
let dummy = document.createElement("textarea");
document.body.appendChild(dummy);
dummy.value = out;
dummy.select();
document.execCommand("copy");
document.body.removeChild(dummy);
});
function randomHash() {
let chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
var passwordLength = 16;
var password = "";
for (var i = 0; i <= passwordLength; i++) {
var randomNumber = Math.floor(Math.random() * chars.length);
password += chars.substring(randomNumber, randomNumber +1);
let chars = "abcdefghijklmnopqrstuvwxyz!@#$%^&*()-+<>ABCDEFGHIJKLMNOP1234567890";
let pass = "";
for (let x = 0; x < 32; x++) {
let i = Math.floor(Math.random() * chars.length);
pass += chars.charAt(i);
}
return password;
return pass;
}
$("button.generateHash").click(function () {
@ -175,7 +155,7 @@ $(document).ready(function(){
}
});
urlPath = new URL(window.location.href).pathname.split("/")[2];
urlPath = new URL(window.location.href).pathname.split("/")[1];
if (urlPath === "") {
urlPath = "hosts"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -7,13 +7,13 @@
<meta name="author" content="">
<link rel="icon" href="/static/icons/favicon.ico">
<title>{{.title}}</title>
<title>TheBBCloud DynDNS</title>
<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="/static/css/bootstrap.min.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<!-- JQueryUI base CSS -->
<link rel="stylesheet" href="/static/css/jquery-ui.min.css"/>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.min.css" integrity="sha256-sEGfrwMkIjbgTBwGLVK38BG/XwIiNC/EAG9Rzsfda6A=" crossorigin="anonymous" />
<!-- Custom styles for this template -->
<link href="/static/css/narrow-jumbotron.css" rel="stylesheet">
@ -27,27 +27,27 @@
<nav>
<ul class="nav nav-pills float-right">
<li class="nav-item">
<a class="nav-link nav-hosts" href="/admin/hosts">Hosts</a>
<a class="nav-link nav-hosts" href="/hosts">Hosts</a>
</li>
<li class="nav-item">
<a class="nav-link nav-cnames" href="/admin/cnames">CNames</a>
<a class="nav-link nav-cnames" href="/cnames">CNames</a>
</li>
<li class="nav-item">
<a class="nav-link nav-logs" href="/admin/logs">Logs</a>
<a class="nav-link nav-logs" href="/logs">Logs</a>
</li>
<li class="nav-item">
<a class="nav-link nav-logout" href="/admin/logout" id="logout">Logout</a>
<a class="nav-link nav-logout" href="" id="logout">Logout</a>
</li>
</ul>
</nav>
<h3 class="text-muted">{{.title}}</h3>
<h3 class="text-muted">TheBBCloud DynDNS</h3>
</div>
<!-- Page Content -->
{{template "content" .}}
<footer class="footer">
<p>&copy; {{.title}} {{year}}</p>
<p>&copy; TheBBCloud 2021</p>
</footer>
</div> <!-- /container -->
@ -56,11 +56,10 @@
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<script src="/static/js/jquery-3.4.1.min.js"></script>
<!-- popper.js@1.16.0 -->
<script src="/static/js/popper.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<script src="/static/js/jquery-ui.min.js"></script>
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js" integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU=" crossorigin="anonymous"></script>
<script src="/static/js/actions-1.0.0.js"></script>
</body>
</html>

View file

@ -1,54 +1,27 @@
{{define "content"}}
<div class="container marketing">
<h3 class="text-center mb-4">DNS Host Entries</h3>
<table class="table table-striped text-center">
<thead>
<tr>
<th>Domain</th>
<th>Hostname</th>
<th>IP</th>
<th>TTL</th>
<th>LastUpdate</th>
<th>
<button class="addHost btn btn-primary">Add Host Entry</button>
</th>
</tr>
</thead>
<tbody>
{{range .hosts}}
<tr id="host_{{.ID}}">
<td id="host-domain_{{.ID}}">{{.Domain}}</td>
<td id="host-hostname_{{.ID}}">{{.Hostname}}.{{.Domain}}</td>
<td>{{.Ip}}</td>
<td>{{.Ttl}}</td>
<td>{{.LastUpdate.Format "01/02/2006 15:04 MEZ"}}</td>
<td>
<div style="display:none">
<div id="host-username_{{.ID}}">
{{.UserName}}
</div>
<div id="host-password_{{.ID}}" >
{{.Password}}
</div>
</div>
<div class="btn-group" id="host_{{.ID}}" >
<button id="{{.ID}}" class="editHost btn btn-outline-secondary btn-sm"><img
src="/static/icons/pencil.svg" alt="" width="16" height="16" title="Edit"></button>&nbsp;
<button
id="{{.ID}}" class="deleteHost btn btn-outline-secondary btn-sm"><img
src="/static/icons/trash.svg"
alt="" width="16" height="16"
title="Delete"></button> &nbsp;
<button id="{{.ID}}" class="showHostLog btn btn-outline-secondary btn-sm"><img
src="/static/icons/table.svg" alt="" width="16" height="16" title="Logs"></button> &nbsp;
<button id="{{.ID}}" class="copyUrlToClipboard btn btn-outline-secondary btn-sm"><img
src="/static/icons/clipboard.svg" alt="" width="16" height="16" title="Copy URL to clipboard"></button>
</div>
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
<div class="container marketing">
<h3 class="text-center mb-4">DNS Host Entries</h3>
<table class="table table-striped text-center">
<thead>
<tr>
<th>Hostname</th>
<th>IP</th>
<th>TTL</th>
<th>LastUpdate</th>
<th><button class="addHost btn btn-primary">Add Host Entry</button></th>
</tr>
</thead>
<tbody>
{{range .hosts}}
<tr>
<td>{{.Hostname}}.{{.Domain}}</td>
<td>{{.Ip}}</td>
<td>{{.Ttl}}</td>
<td>{{.LastUpdate.Format "01/02/2006 15:04 MEZ"}}</td>
<td><button id="{{.ID}}" class="editHost btn btn-outline-secondary btn-sm"><img src="/static/icons/pencil.svg" alt="" width="16" height="16" title="Edit"></button>&nbsp;<button id="{{.ID}}" class="deleteHost btn btn-outline-secondary btn-sm"><img src="/static/icons/trash.svg" alt="" width="16" height="16" title="Delete"></button> <button id="{{.ID}}" class="showHostLog btn btn-outline-secondary btn-sm"><img src="/static/icons/table.svg" alt="" width="16" height="16" title="Logs"></button></td>
</tr>
{{end}}
</tbody>
</table>
</div>
{{end}}