Merge pull request #17 from maltegrosse/master

Features
This commit is contained in:
benjaminbear
2022-05-26 17:34:18 +02:00
committed by GitHub
25 changed files with 565 additions and 134 deletions
+54
View File
@@ -0,0 +1,54 @@
---
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 }}
+7
View File
@@ -53,6 +53,7 @@ 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 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) `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)
@@ -60,6 +61,12 @@ echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g
`DDNS_DEFAULT_TTL` is the default TTL of your dyndns server. `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 ### DNS setup
If your parent domain is `example.com` and you want your dyndns domain to be `dyndns.example.com`, If your parent domain is `example.com` and you want your dyndns domain to be `dyndns.example.com`,
+6 -3
View File
@@ -4,9 +4,12 @@ ENV GO111MODULE=on
ENV GOPATH=/root/go ENV GOPATH=/root/go
RUN mkdir -p /root/go/src RUN mkdir -p /root/go/src
COPY dyndns /root/go/src/dyndns COPY dyndns /root/go/src/dyndns
RUN cd /root/go/src/dyndns && go mod download && GOOS=linux GOARCH=amd64 go build -o /root/go/bin/dyndns && go test -v WORKDIR /root/go/src/dyndns
RUN go mod download
RUN go mod verify
RUN GOOS=linux go build -o /root/go/bin/dyndns && go test -v
FROM debian:buster-slim FROM debian:bullseye-slim
RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
apt-get install -q -y bind9 dnsutils curl && \ apt-get install -q -y bind9 dnsutils curl && \
@@ -23,4 +26,4 @@ COPY dyndns/views /root/views
COPY dyndns/static /root/static COPY dyndns/static /root/static
EXPOSE 53 8080 EXPOSE 53 8080
CMD ["sh", "-c", "/root/setup.sh ; service bind9 start ; /root/dyndns"] CMD ["sh", "-c", "/root/setup.sh ; service named start ; /root/dyndns"]
+2 -2
View File
@@ -1,6 +1,6 @@
#!/bin/bash #!/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_DOMAINS" ] && echo "DDNS_DOMAINS not set" && exit 1;
[ -z "$DDNS_PARENT_NS" ] && echo "DDNS_PARENT_NS 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; [ -z "$DDNS_DEFAULT_TTL" ] && echo "DDNS_DEFAULT_TTL not set" && exit 1;
@@ -48,4 +48,4 @@ done
chown root:bind /var/cache/bind chown root:bind /var/cache/bind
chown bind:bind /var/cache/bind/* chown bind:bind /var/cache/bind/*
chmod 770 /var/cache/bind chmod 770 /var/cache/bind
chmod 644 /var/cache/bind/* chmod 644 /var/cache/bind/*
+131
View File
@@ -0,0 +1,131 @@
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/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 h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
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/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.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.8.0 h1:1kAa0fCrnpv+QYdkdcRzrRM7AyYs5o8+jZdJCz9xj6k=
github.com/go-playground/validator/v10 v10.8.0/go.mod h1:9JhgTzTaE31GZDpH/HSvHiRJrJ3iKAgqqH0Bl/Ocjdk=
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-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 h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
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.4.0 h1:rblX1cN6T4LvUW9ZKMPZ17uPl/Dc8igP7ZmjGHZoj4A=
github.com/labstack/echo/v4 v4.4.0/go.mod h1:PvmtTvhVqKDzDQy4d3bWzPjZLzom4iQbAZy2sgZ/qI8=
github.com/labstack/gommon v0.2.9/go.mod h1:E8ZTmW9vw5az5/ZyHWCp0Lw4OH2ecsaBP1C/NKavGG4=
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
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.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
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.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
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/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tg123/go-htpasswd v1.0.0 h1:Ze/pZsz73JiCwXIyJBPvNs75asKBgfodCf8iTEkgkXs=
github.com/tg123/go-htpasswd v1.0.0/go.mod h1:eQTgl67UrNKQvEPKrDLGBssjVwYQClFZjALVLhIv8C0=
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 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
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.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
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.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
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-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
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.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/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 h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+4 -2
View File
@@ -24,6 +24,7 @@ func (h *Handler) ListCNames(c echo.Context) (err error) {
return c.Render(http.StatusOK, "listcnames", echo.Map{ return c.Render(http.StatusOK, "listcnames", echo.Map{
"cnames": cnames, "cnames": cnames,
"title": h.Title,
}) })
} }
@@ -42,6 +43,7 @@ func (h *Handler) AddCName(c echo.Context) (err error) {
return c.Render(http.StatusOK, "addcname", echo.Map{ return c.Render(http.StatusOK, "addcname", echo.Map{
"config": h.Config, "config": h.Config,
"hosts": hosts, "hosts": hosts,
"title": h.Title,
}) })
} }
@@ -77,7 +79,7 @@ func (h *Handler) CreateCName(c echo.Context) (err error) {
return c.JSON(http.StatusBadRequest, &Error{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); err != nil { 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 {
return c.JSON(http.StatusBadRequest, &Error{err.Error()}) return c.JSON(http.StatusBadRequest, &Error{err.Error()})
} }
@@ -112,7 +114,7 @@ func (h *Handler) DeleteCName(c echo.Context) (err error) {
return c.JSON(http.StatusBadRequest, &Error{err.Error()}) return c.JSON(http.StatusBadRequest, &Error{err.Error()})
} }
if err = nswrapper.DeleteRecord(cname.Hostname, cname.Target.Domain); err != nil { if err = nswrapper.DeleteRecord(cname.Hostname, cname.Target.Domain, h.AllowWildcard); err != nil {
return c.JSON(http.StatusBadRequest, &Error{err.Error()}) return c.JSON(http.StatusBadRequest, &Error{err.Error()})
} }
+94 -22
View File
@@ -2,8 +2,12 @@ package handler
import ( import (
"fmt" "fmt"
"github.com/labstack/gommon/log"
"os" "os"
"strconv"
"strings" "strings"
"time"
"github.com/benjaminbear/docker-ddns-server/dyndns/model" "github.com/benjaminbear/docker-ddns-server/dyndns/model"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
@@ -13,10 +17,15 @@ import (
) )
type Handler struct { type Handler struct {
DB *gorm.DB DB *gorm.DB
AuthHost *model.Host AuthAdmin bool
AuthAdmin bool Config Envs
Config Envs Title string
DisableAdminAuth bool
LastClearedLogs time.Time
ClearInterval uint64
AllowWildcard bool
LogoutUrl string
} }
type Envs struct { type Envs struct {
@@ -39,13 +48,33 @@ type Error struct {
// Authenticate is the method the website admin user and the host update user have to authenticate against. // 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. // To gather admin rights the username password combination must match with the credentials given by the env var.
func (h *Handler) Authenticate(username, password string, c echo.Context) (bool, error) { func (h *Handler) AuthenticateUpdate(username, password string, c echo.Context) (bool, error) {
h.AuthHost = nil h.CheckClearInterval()
h.AuthAdmin = false 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) {
h.AuthAdmin = false
ok, err := h.authByEnv(username, password) ok, err := h.authByEnv(username, password)
if err != nil { if err != nil {
fmt.Println("Error:", err) log.Error("Error:", err)
return false, nil return false, nil
} }
@@ -54,17 +83,8 @@ func (h *Handler) Authenticate(username, password string, c echo.Context) (bool,
return true, nil return true, nil
} }
host := &model.Host{} return false, nil
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) { func (h *Handler) authByEnv(username, password string) (bool, error) {
hashReader := strings.NewReader(h.Config.AdminLogin) hashReader := strings.NewReader(h.Config.AdminLogin)
@@ -83,19 +103,55 @@ func (h *Handler) authByEnv(username, password string) (bool, error) {
// ParseEnvs parses all needed environment variables: // ParseEnvs parses all needed environment variables:
// DDNS_ADMIN_LOGIN: The basic auth login string in htpasswd style. // DDNS_ADMIN_LOGIN: The basic auth login string in htpasswd style.
// DDNS_DOMAINS: All domains that will be handled by the dyndns server. // DDNS_DOMAINS: All domains that will be handled by the dyndns server.
func (h *Handler) ParseEnvs() error { func (h *Handler) ParseEnvs() (adminAuth bool, err error) {
log.Info("Read environment variables")
h.Config = Envs{} h.Config = Envs{}
adminAuth = true
h.Config.AdminLogin = os.Getenv("DDNS_ADMIN_LOGIN") h.Config.AdminLogin = os.Getenv("DDNS_ADMIN_LOGIN")
if h.Config.AdminLogin == "" { if h.Config.AdminLogin == "" {
return fmt.Errorf("environment variable DDNS_ADMIN_LOGIN has to be set") 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()
}
} }
h.Config.Domains = strings.Split(os.Getenv("DDNS_DOMAINS"), ",") h.Config.Domains = strings.Split(os.Getenv("DDNS_DOMAINS"), ",")
if len(h.Config.Domains) < 1 { if len(h.Config.Domains) < 1 {
return fmt.Errorf("environment variable DDNS_DOMAINS has to be set") return adminAuth, fmt.Errorf("environment variable DDNS_DOMAINS has to be set")
} }
return nil return adminAuth, nil
} }
// InitDB creates an empty database and creates all tables if there isn't already one, or opens the existing one. // InitDB creates an empty database and creates all tables if there isn't already one, or opens the existing one.
@@ -126,3 +182,19 @@ func (h *Handler) InitDB() (err error) {
return nil 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
}
+19 -14
View File
@@ -2,6 +2,7 @@ package handler
import ( import (
"fmt" "fmt"
l "github.com/labstack/gommon/log"
"net" "net"
"net/http" "net/http"
"strconv" "strconv"
@@ -51,6 +52,7 @@ func (h *Handler) ListHosts(c echo.Context) (err error) {
return c.Render(http.StatusOK, "listhosts", echo.Map{ return c.Render(http.StatusOK, "listhosts", echo.Map{
"hosts": hosts, "hosts": hosts,
"title": h.Title,
}) })
} }
@@ -63,6 +65,7 @@ func (h *Handler) AddHost(c echo.Context) (err error) {
return c.Render(http.StatusOK, "edithost", echo.Map{ return c.Render(http.StatusOK, "edithost", echo.Map{
"addEdit": "add", "addEdit": "add",
"config": h.Config, "config": h.Config,
"title": h.Title,
}) })
} }
@@ -86,6 +89,7 @@ func (h *Handler) EditHost(c echo.Context) (err error) {
"host": host, "host": host,
"addEdit": "edit", "addEdit": "edit",
"config": h.Config, "config": h.Config,
"title": h.Title,
}) })
} }
@@ -109,7 +113,7 @@ func (h *Handler) CreateHost(c echo.Context) (err error) {
if err = h.checkUniqueHostname(host.Hostname, host.Domain); err != nil { if err = h.checkUniqueHostname(host.Hostname, host.Domain); err != nil {
return c.JSON(http.StatusBadRequest, &Error{err.Error()}) return c.JSON(http.StatusBadRequest, &Error{err.Error()})
} }
host.LastUpdate = time.Now()
if err = h.DB.Create(host).Error; err != nil { if err = h.DB.Create(host).Error; err != nil {
return c.JSON(http.StatusBadRequest, &Error{err.Error()}) return c.JSON(http.StatusBadRequest, &Error{err.Error()})
} }
@@ -121,7 +125,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)}) 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); err != nil { if err = nswrapper.UpdateRecord(host.Hostname, host.Ip, ipType, host.Domain, host.Ttl, h.AllowWildcard); err != nil {
return c.JSON(http.StatusBadRequest, &Error{err.Error()}) return c.JSON(http.StatusBadRequest, &Error{err.Error()})
} }
} }
@@ -168,7 +172,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)}) 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); err != nil { if err = nswrapper.UpdateRecord(host.Hostname, host.Ip, ipType, host.Domain, host.Ttl, h.AllowWildcard); err != nil {
return c.JSON(http.StatusBadRequest, &Error{err.Error()}) return c.JSON(http.StatusBadRequest, &Error{err.Error()})
} }
} }
@@ -212,7 +216,7 @@ func (h *Handler) DeleteHost(c echo.Context) (err error) {
return c.JSON(http.StatusBadRequest, &Error{err.Error()}) return c.JSON(http.StatusBadRequest, &Error{err.Error()})
} }
if err = nswrapper.DeleteRecord(host.Hostname, host.Domain); err != nil { if err = nswrapper.DeleteRecord(host.Hostname, host.Domain, h.AllowWildcard); err != nil {
return c.JSON(http.StatusBadRequest, &Error{err.Error()}) return c.JSON(http.StatusBadRequest, &Error{err.Error()})
} }
@@ -223,11 +227,12 @@ func (h *Handler) DeleteHost(c echo.Context) (err error) {
// Hostname, IP and senders IP are validated, a log entry is created // Hostname, IP and senders IP are validated, a log entry is created
// and finally if everything is ok, the DNS Server will be updated // and finally if everything is ok, the DNS Server will be updated
func (h *Handler) UpdateIP(c echo.Context) (err error) { func (h *Handler) UpdateIP(c echo.Context) (err error) {
if h.AuthHost == nil { host, ok := c.Get("updateHost").(*model.Host)
if !ok {
return c.String(http.StatusBadRequest, "badauth\n") return c.String(http.StatusBadRequest, "badauth\n")
} }
log := &model.Log{Status: false, Host: *h.AuthHost, TimeStamp: time.Now(), UserAgent: nswrapper.ShrinkUserAgent(c.Request().UserAgent())} log := &model.Log{Status: false, Host: *host, TimeStamp: time.Now(), UserAgent: nswrapper.ShrinkUserAgent(c.Request().UserAgent())}
log.SentIP = c.QueryParam(("myip")) log.SentIP = c.QueryParam(("myip"))
// Get caller IP // Get caller IP
@@ -237,7 +242,7 @@ func (h *Handler) UpdateIP(c echo.Context) (err error) {
if err != nil { if err != nil {
log.Message = "Bad Request: Unable to get caller IP" log.Message = "Bad Request: Unable to get caller IP"
if err = h.CreateLogEntry(log); err != nil { if err = h.CreateLogEntry(log); err != nil {
fmt.Println(err) l.Error(err)
} }
return c.String(http.StatusBadRequest, "badrequest\n") return c.String(http.StatusBadRequest, "badrequest\n")
@@ -246,10 +251,10 @@ func (h *Handler) UpdateIP(c echo.Context) (err error) {
// Validate hostname // Validate hostname
hostname := c.QueryParam("hostname") hostname := c.QueryParam("hostname")
if hostname == "" || hostname != h.AuthHost.Hostname+"."+h.AuthHost.Domain { if hostname == "" || hostname != host.Hostname+"."+host.Domain {
log.Message = "Hostname or combination of authenticated user and hostname is invalid" log.Message = "Hostname or combination of authenticated user and hostname is invalid"
if err = h.CreateLogEntry(log); err != nil { if err = h.CreateLogEntry(log); err != nil {
fmt.Println(err) l.Error(err)
} }
return c.String(http.StatusBadRequest, "notfqdn\n") return c.String(http.StatusBadRequest, "notfqdn\n")
@@ -263,7 +268,7 @@ func (h *Handler) UpdateIP(c echo.Context) (err error) {
if ipType == "" { if ipType == "" {
log.Message = "Bad Request: Sent IP is invalid" log.Message = "Bad Request: Sent IP is invalid"
if err = h.CreateLogEntry(log); err != nil { if err = h.CreateLogEntry(log); err != nil {
fmt.Println(err) l.Error(err)
} }
return c.String(http.StatusBadRequest, "badrequest\n") return c.String(http.StatusBadRequest, "badrequest\n")
@@ -271,12 +276,12 @@ func (h *Handler) UpdateIP(c echo.Context) (err error) {
} }
// add/update DNS record // add/update DNS record
if err = nswrapper.UpdateRecord(log.Host.Hostname, log.SentIP, ipType, log.Host.Domain, log.Host.Ttl); err != nil { if err = nswrapper.UpdateRecord(log.Host.Hostname, log.SentIP, ipType, log.Host.Domain, log.Host.Ttl, h.AllowWildcard); err != nil {
log.Message = fmt.Sprintf("DNS error: %v", err) log.Message = fmt.Sprintf("DNS error: %v", err)
l.Error(log.Message)
if err = h.CreateLogEntry(log); err != nil { if err = h.CreateLogEntry(log); err != nil {
fmt.Println(err) l.Error(err)
} }
return c.String(http.StatusBadRequest, "dnserr\n") return c.String(http.StatusBadRequest, "dnserr\n")
} }
@@ -285,7 +290,7 @@ func (h *Handler) UpdateIP(c echo.Context) (err error) {
log.Status = true log.Status = true
log.Message = "No errors occurred" log.Message = "No errors occurred"
if err = h.CreateLogEntry(log); err != nil { if err = h.CreateLogEntry(log); err != nil {
fmt.Println(err) l.Error(err)
} }
return c.String(http.StatusOK, "good\n") return c.String(http.StatusOK, "good\n")
+15 -4
View File
@@ -1,8 +1,10 @@
package handler package handler
import ( import (
"log"
"net/http" "net/http"
"strconv" "strconv"
"time"
"github.com/benjaminbear/docker-ddns-server/dyndns/model" "github.com/benjaminbear/docker-ddns-server/dyndns/model"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
@@ -24,12 +26,13 @@ func (h *Handler) ShowLogs(c echo.Context) (err error) {
} }
logs := new([]model.Log) logs := new([]model.Log)
if err = h.DB.Preload("Host").Limit(30).Find(logs).Error; err != nil { if err = h.DB.Preload("Host").Limit(30).Order("created_at desc").Find(logs).Error; err != nil {
return c.JSON(http.StatusBadRequest, &Error{err.Error()}) return c.JSON(http.StatusBadRequest, &Error{err.Error()})
} }
return c.Render(http.StatusOK, "listlogs", echo.Map{ return c.Render(http.StatusOK, "listlogs", echo.Map{
"logs": logs, "logs": logs,
"title": h.Title,
}) })
} }
@@ -45,11 +48,19 @@ func (h *Handler) ShowHostLogs(c echo.Context) (err error) {
} }
logs := new([]model.Log) logs := new([]model.Log)
if err = h.DB.Preload("Host").Where(&model.Log{HostID: uint(id)}).Limit(30).Find(logs).Error; err != nil { if err = h.DB.Preload("Host").Where(&model.Log{HostID: uint(id)}).Order("created_at desc").Limit(30).Find(logs).Error; err != nil {
return c.JSON(http.StatusBadRequest, &Error{err.Error()}) return c.JSON(http.StatusBadRequest, &Error{err.Error()})
} }
return c.Render(http.StatusOK, "listlogs", echo.Map{ return c.Render(http.StatusOK, "listlogs", echo.Map{
"logs": logs, "logs": logs,
"title": h.Title,
}) })
} }
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")
}
+69 -25
View File
@@ -1,18 +1,21 @@
package main package main
import ( import (
"net/http"
"github.com/benjaminbear/docker-ddns-server/dyndns/handler" "github.com/benjaminbear/docker-ddns-server/dyndns/handler"
"github.com/foolin/goview"
"github.com/foolin/goview/supports/echoview-v4" "github.com/foolin/goview/supports/echoview-v4"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
_ "github.com/jinzhu/gorm/dialects/sqlite" _ "github.com/jinzhu/gorm/dialects/sqlite"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware" "github.com/labstack/echo/v4/middleware"
"github.com/labstack/gommon/log" "github.com/labstack/gommon/log"
"html/template"
"net/http"
"time"
) )
func main() { func main() {
// Set new instance
e := echo.New() e := echo.New()
e.Logger.SetLevel(log.ERROR) e.Logger.SetLevel(log.ERROR)
@@ -20,7 +23,17 @@ func main() {
e.Use(middleware.Logger()) e.Use(middleware.Logger())
// Set Renderer // Set Renderer
e.Renderer = echoview.Default() 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,
})
// Set Validator // Set Validator
e.Validator = &handler.CustomValidator{Validator: validator.New()} e.Validator = &handler.CustomValidator{Validator: validator.New()}
@@ -37,38 +50,69 @@ func main() {
} }
defer h.DB.Close() defer h.DB.Close()
if err := h.ParseEnvs(); err != nil { authAdmin, err := h.ParseEnvs()
if err != nil {
e.Logger.Fatal(err) e.Logger.Fatal(err)
} }
e.Use(middleware.BasicAuth(h.Authenticate))
// UI Routes // UI Routes
e.GET("/", func(c echo.Context) error { groupPublic := e.Group("/")
//render with master groupPublic.GET("*", func(c echo.Context) error {
return c.Render(http.StatusOK, "listhosts", nil) //redirect to admin
return c.Redirect(301, "./admin/")
}) })
groupAdmin := e.Group("/admin")
if authAdmin {
groupAdmin.Use(middleware.BasicAuth(h.AuthenticateAdmin))
}
e.GET("/hosts/add", h.AddHost) groupAdmin.GET("/", h.ListHosts)
e.GET("/hosts/edit/:id", h.EditHost) groupAdmin.GET("/hosts/add", h.AddHost)
e.GET("/hosts", h.ListHosts) groupAdmin.GET("/hosts/edit/:id", h.EditHost)
e.GET("/cnames/add", h.AddCName) groupAdmin.GET("/hosts", h.ListHosts)
e.GET("/cnames", h.ListCNames) groupAdmin.GET("/cnames/add", h.AddCName)
e.GET("/logs", h.ShowLogs) groupAdmin.GET("/cnames", h.ListCNames)
e.GET("/logs/host/:id", h.ShowHostLogs) groupAdmin.GET("/logs", h.ShowLogs)
groupAdmin.GET("/logs/host/:id", h.ShowHostLogs)
// Rest Routes // Rest Routes
e.POST("/hosts/add", h.CreateHost) groupAdmin.POST("/hosts/add", h.CreateHost)
e.POST("/hosts/edit/:id", h.UpdateHost) groupAdmin.POST("/hosts/edit/:id", h.UpdateHost)
e.GET("/hosts/delete/:id", h.DeleteHost) groupAdmin.GET("/hosts/delete/:id", h.DeleteHost)
e.POST("/cnames/add", h.CreateCName) //redirect to logout
e.GET("/cnames/delete/:id", h.DeleteCName) 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)
// dyndns compatible api // dyndns compatible api
e.GET("/update", h.UpdateIP) // (avoid breaking changes and create groups for each update endpoint)
e.GET("/nic/update", h.UpdateIP) updateRoute := e.Group("/update")
e.GET("/v2/update", h.UpdateIP) updateRoute.Use(middleware.BasicAuth(h.AuthenticateUpdate))
e.GET("/v3/update", h.UpdateIP) updateRoute.GET("", h.UpdateIP)
nicRoute := e.Group("/nic")
nicRoute.Use(middleware.BasicAuth(h.AuthenticateUpdate))
updateRoute.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)
})
// Start server // Start server
e.Logger.Fatal(e.Start(":8080")) e.Logger.Fatal(e.Start(":8080"))
+2 -2
View File
@@ -3,7 +3,7 @@ package nswrapper
import ( import (
"bytes" "bytes"
"errors" "errors"
"fmt" "github.com/labstack/gommon/log"
"net" "net"
"net/http" "net/http"
"strings" "strings"
@@ -25,7 +25,7 @@ func GetIPType(ipAddr string) string {
// GetCallerIP searches for the "real" IP senders has actually. // GetCallerIP searches for the "real" IP senders has actually.
// If its a private address we won't use it. // If its a private address we won't use it.
func GetCallerIP(r *http.Request) (string, error) { func GetCallerIP(r *http.Request) (string, error) {
fmt.Println("request", r.Header) log.Info("request", r.Header)
for _, h := range []string{"X-Real-Ip", "X-Forwarded-For"} { for _, h := range []string{"X-Real-Ip", "X-Forwarded-For"} {
addresses := strings.Split(r.Header.Get(h), ",") addresses := strings.Split(r.Header.Get(h), ",")
// march from right to left until we get a public address // march from right to left until we get a public address
+13 -3
View File
@@ -4,14 +4,15 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"fmt" "fmt"
"github.com/labstack/gommon/log"
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
) )
// UpdateRecord builds a nsupdate file and updates a record by executing it with nsupdate. // 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) error { func UpdateRecord(hostname string, target string, addrType string, zone string, ttl int, enableWildcard bool) error {
fmt.Printf("%s record update request: %s -> %s\n", addrType, hostname, target) log.Info(fmt.Sprintf("%s record update request: %s -> %s", addrType, hostname, target))
f, err := ioutil.TempFile(os.TempDir(), "dyndns") f, err := ioutil.TempFile(os.TempDir(), "dyndns")
if err != nil { if err != nil {
@@ -24,7 +25,13 @@ func UpdateRecord(hostname string, target string, addrType string, zone string,
w.WriteString(fmt.Sprintf("server %s\n", "localhost")) w.WriteString(fmt.Sprintf("server %s\n", "localhost"))
w.WriteString(fmt.Sprintf("zone %s\n", zone)) w.WriteString(fmt.Sprintf("zone %s\n", zone))
w.WriteString(fmt.Sprintf("update delete %s.%s %s\n", hostname, zone, addrType)) 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)) 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.WriteString("send\n")
w.Flush() w.Flush()
@@ -48,7 +55,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. // DeleteRecord builds a nsupdate file and deletes a record by executing it with nsupdate.
func DeleteRecord(hostname string, zone string) error { func DeleteRecord(hostname string, zone string, enableWildcard bool) error {
fmt.Printf("record delete request: %s\n", hostname) fmt.Printf("record delete request: %s\n", hostname)
f, err := ioutil.TempFile(os.TempDir(), "dyndns") f, err := ioutil.TempFile(os.TempDir(), "dyndns")
@@ -62,6 +69,9 @@ func DeleteRecord(hostname string, zone string) error {
w.WriteString(fmt.Sprintf("server %s\n", "localhost")) w.WriteString(fmt.Sprintf("server %s\n", "localhost"))
w.WriteString(fmt.Sprintf("zone %s\n", zone)) w.WriteString(fmt.Sprintf("zone %s\n", zone))
w.WriteString(fmt.Sprintf("update delete %s.%s\n", hostname, 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.WriteString("send\n")
w.Flush() 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.

After

Width:  |  Height:  |  Size: 15 KiB

+39 -19
View File
@@ -1,18 +1,18 @@
$("button.addHost").click(function () { $("button.addHost").click(function () {
location.href='/hosts/add'; location.href='/admin/hosts/add';
}); });
$("button.editHost").click(function () { $("button.editHost").click(function () {
location.href='/hosts/edit/' + $(this).attr('id'); location.href='/admin/hosts/edit/' + $(this).attr('id');
}); });
$("button.deleteHost").click(function () { $("button.deleteHost").click(function () {
$.ajax({ $.ajax({
contentType: 'application/x-www-form-urlencoded; charset=UTF-8', contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
type: 'GET', type: 'GET',
url: "/hosts/delete/" + $(this).attr('id') url: "/admin/hosts/delete/" + $(this).attr('id')
}).done(function(data, textStatus, jqXHR) { }).done(function(data, textStatus, jqXHR) {
location.href="/hosts"; location.href="/admin/hosts";
}).fail(function(jqXHR, textStatus, errorThrown) { }).fail(function(jqXHR, textStatus, errorThrown) {
alert("Error: " + $.parseJSON(jqXHR.responseText).message); alert("Error: " + $.parseJSON(jqXHR.responseText).message);
location.reload() location.reload()
@@ -20,7 +20,7 @@ $("button.deleteHost").click(function () {
}); });
$("button.showHostLog").click(function () { $("button.showHostLog").click(function () {
location.href='/logs/host/' + $(this).attr('id'); location.href='/admin/logs/host/' + $(this).attr('id');
}); });
$("button.add, button.edit").click(function () { $("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', contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
data: $('#editHostForm').serialize(), data: $('#editHostForm').serialize(),
type: 'POST', type: 'POST',
url: '/'+type+'/'+action+id, url: '/admin/'+type+'/'+action+id,
}).done(function(data, textStatus, jqXHR) { }).done(function(data, textStatus, jqXHR) {
location.href="/"+type; location.href="/admin/"+type;
}).fail(function(jqXHR, textStatus, errorThrown) { }).fail(function(jqXHR, textStatus, errorThrown) {
alert("Error: " + $.parseJSON(jqXHR.responseText).message); alert("Error: " + $.parseJSON(jqXHR.responseText).message);
}); });
@@ -64,6 +64,7 @@ $("button.add, button.edit").click(function () {
}); });
$("#logout").click(function (){ $("#logout").click(function (){
//document.execCommand("ClearAuthenticationCache");
try { try {
// This is for Firefox // This is for Firefox
$.ajax({ $.ajax({
@@ -73,7 +74,7 @@ $("#logout").click(function (){
password: 'reset', password: 'reset',
// If the return is 401, refresh the page to request new details. // If the return is 401, refresh the page to request new details.
statusCode: { 401: function() { statusCode: { 401: function() {
document.location = document.location; // document.location = document.location;
} }
} }
}); });
@@ -83,22 +84,23 @@ $("#logout").click(function (){
if (!document.execCommand("ClearAuthenticationCache")) { if (!document.execCommand("ClearAuthenticationCache")) {
// exeCommand returns false if it didn't work (which happens in Chrome) so as a last // exeCommand returns false if it didn't work (which happens in Chrome) so as a last
// resort refresh the page providing new, invalid details. // resort refresh the page providing new, invalid details.
document.location = "http://reset:reset@" + document.location.hostname + document.location.pathname; // document.location = location.protocol+"//reset:reset@" + document.location.hostname + document.location.pathname;
} }
} }
console.log("first logout")
}); });
$("button.addCName").click(function () { $("button.addCName").click(function () {
location.href='/cnames/add'; location.href='/admin/cnames/add';
}); });
$("button.deleteCName").click(function () { $("button.deleteCName").click(function () {
$.ajax({ $.ajax({
contentType: 'application/x-www-form-urlencoded; charset=UTF-8', contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
type: 'GET', type: 'GET',
url: "/cnames/delete/" + $(this).attr('id') url: "/admin/cnames/delete/" + $(this).attr('id')
}).done(function(data, textStatus, jqXHR) { }).done(function(data, textStatus, jqXHR) {
location.href="/cnames"; location.href="/admin/cnames";
}).fail(function(jqXHR, textStatus, errorThrown) { }).fail(function(jqXHR, textStatus, errorThrown) {
alert("Error: " + $.parseJSON(jqXHR.responseText).message); alert("Error: " + $.parseJSON(jqXHR.responseText).message);
location.reload() location.reload()
@@ -124,15 +126,33 @@ $("button.copyToClipboard").click(function () {
copyText.setSelectionRange(0, 99999); copyText.setSelectionRange(0, 99999);
document.execCommand("copy"); 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() { function randomHash() {
let chars = "abcdefghijklmnopqrstuvwxyz!@#$%^&*()-+<>ABCDEFGHIJKLMNOP1234567890"; let chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
let pass = ""; var passwordLength = 16;
for (let x = 0; x < 32; x++) { var password = "";
let i = Math.floor(Math.random() * chars.length); for (var i = 0; i <= passwordLength; i++) {
pass += chars.charAt(i); var randomNumber = Math.floor(Math.random() * chars.length);
password += chars.substring(randomNumber, randomNumber +1);
} }
return pass;
return password;
} }
$("button.generateHash").click(function () { $("button.generateHash").click(function () {
@@ -155,7 +175,7 @@ $(document).ready(function(){
} }
}); });
urlPath = new URL(window.location.href).pathname.split("/")[1]; urlPath = new URL(window.location.href).pathname.split("/")[2];
if (urlPath === "") { if (urlPath === "") {
urlPath = "hosts" 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
+14 -13
View File
@@ -7,13 +7,13 @@
<meta name="author" content=""> <meta name="author" content="">
<link rel="icon" href="/static/icons/favicon.ico"> <link rel="icon" href="/static/icons/favicon.ico">
<title>TheBBCloud DynDNS</title> <title>{{.title}}</title>
<!-- Bootstrap core CSS --> <!-- Bootstrap core CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"> <link rel="stylesheet" href="/static/css/bootstrap.min.css">
<!-- JQueryUI base CSS --> <!-- JQueryUI base 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" /> <link rel="stylesheet" href="/static/css/jquery-ui.min.css"/>
<!-- Custom styles for this template --> <!-- Custom styles for this template -->
<link href="/static/css/narrow-jumbotron.css" rel="stylesheet"> <link href="/static/css/narrow-jumbotron.css" rel="stylesheet">
@@ -27,27 +27,27 @@
<nav> <nav>
<ul class="nav nav-pills float-right"> <ul class="nav nav-pills float-right">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link nav-hosts" href="/hosts">Hosts</a> <a class="nav-link nav-hosts" href="/admin/hosts">Hosts</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link nav-cnames" href="/cnames">CNames</a> <a class="nav-link nav-cnames" href="/admin/cnames">CNames</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link nav-logs" href="/logs">Logs</a> <a class="nav-link nav-logs" href="/admin/logs">Logs</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link nav-logout" href="" id="logout">Logout</a> <a class="nav-link nav-logout" href="/admin/logout" id="logout">Logout</a>
</li> </li>
</ul> </ul>
</nav> </nav>
<h3 class="text-muted">TheBBCloud DynDNS</h3> <h3 class="text-muted">{{.title}}</h3>
</div> </div>
<!-- Page Content --> <!-- Page Content -->
{{template "content" .}} {{template "content" .}}
<footer class="footer"> <footer class="footer">
<p>&copy; TheBBCloud 2021</p> <p>&copy; {{.title}} {{year}}</p>
</footer> </footer>
</div> <!-- /container --> </div> <!-- /container -->
@@ -56,10 +56,11 @@
================================================== --> ================================================== -->
<!-- Placed at the end of the document so the pages load faster --> <!-- Placed at the end of the document so the pages load faster -->
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug --> <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script> <script src="/static/js/jquery-3.4.1.min.js"></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> <!-- popper.js@1.16.0 -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script> <script src="/static/js/popper.min.js"></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/bootstrap.min.js"></script>
<script src="/static/js/jquery-ui.min.js"></script>
<script src="/static/js/actions-1.0.0.js"></script> <script src="/static/js/actions-1.0.0.js"></script>
</body> </body>
</html> </html>
+52 -25
View File
@@ -1,27 +1,54 @@
{{define "content"}} {{define "content"}}
<div class="container marketing"> <div class="container marketing">
<h3 class="text-center mb-4">DNS Host Entries</h3> <h3 class="text-center mb-4">DNS Host Entries</h3>
<table class="table table-striped text-center"> <table class="table table-striped text-center">
<thead> <thead>
<tr> <tr>
<th>Hostname</th> <th>Domain</th>
<th>IP</th> <th>Hostname</th>
<th>TTL</th> <th>IP</th>
<th>LastUpdate</th> <th>TTL</th>
<th><button class="addHost btn btn-primary">Add Host Entry</button></th> <th>LastUpdate</th>
</tr> <th>
</thead> <button class="addHost btn btn-primary">Add Host Entry</button>
<tbody> </th>
{{range .hosts}} </tr>
<tr> </thead>
<td>{{.Hostname}}.{{.Domain}}</td> <tbody>
<td>{{.Ip}}</td> {{range .hosts}}
<td>{{.Ttl}}</td> <tr id="host_{{.ID}}">
<td>{{.LastUpdate.Format "01/02/2006 15:04 MEZ"}}</td> <td id="host-domain_{{.ID}}">{{.Domain}}</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> <td id="host-hostname_{{.ID}}">{{.Hostname}}.{{.Domain}}</td>
</tr> <td>{{.Ip}}</td>
{{end}} <td>{{.Ttl}}</td>
</tbody> <td>{{.LastUpdate.Format "01/02/2006 15:04 MEZ"}}</td>
</table> <td>
</div> <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>
{{end}} {{end}}