Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multisite with restricted domain updates #27

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 183 additions & 0 deletions .configure.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
!/bin/bash
echo "### ownDynDNS configuration script"

# set variables
scriptversion="1.5"

wwwuserd="www-data"
wwwgroupd="www-data"

defaultenvfile=".env.dist"

dir=$(pwd)
while [ ! -f $dir/$defaultenvfile ]
do
echo "current directory does not contain ${defaultenvfile} !"
read -p "enter directory where ownDynDNS is located: " dir
done

# source .env.dist
source $dir/$defaultenvfile

envfile="${dir}/.env"

log1="$logFile"
log2="/var/log/dnsupdater/log.json"


### main script
#echo "found ${defaultenvfile}. using current directory"

read -p "enter a custom username for dns updates [random]: " user
user=${user:-$(tr -dc A-Za-z0-9 </dev/urandom | head -c 16)}
#echo "using username: ${user}"

read -s -p "enter a custom password for dns updates [random]: " pass
pass=${pass:-$(tr -dc A-Za-z0-9 </dev/urandom | head -c 16)}
echo ""
#echo "using password: ${pass}"

read -s -p "enter your netcup DNS API Key: " apikey
echo ""
#echo "using api key: ${apikey}"

read -s -p "enter your netcup API Password: " apipass
echo ""
#echo "using api password: ${apipass}"

read -p "enter your netcup customer ID: " custid
#echo "using customer id: ${custid}"

read -p "do you wish to enable debugging? [y/N]: " debug
if [[ ${debug,,::1} == "y" ]]
then
#echo "enabling debugging"
debug="true"
else
#echo "disabling debugging"
debug="false"
fi

read -p "do you want to enable logging? [Y/n]: " log
if [[ ${log,,::1} != "n" ]]
then
#echo "enabling logging"
log="true"
else
#echo "disabling logging"
log="false"
fi

echo "the logfile is created in this directory by default. your ip history is thereby publically available."
echo "select where the logfile should be created if enabled:"
echo "[1] default: ${log1}"
echo "[2] private: ${log2}"
echo "[3] custom location"

read -p "select from the choices above [1]: " choice
case $choice in
2)
logfile=${log2}
;;
3)
read -p "enter logfile location: " logfile
logfile=${logfile:-$log1}
;;
*)
logfile=${log1}
;;
esac

echo "the logfile needs to be writable by the webserver if logging is enabled."
read -p "which user does the webserver run as? [${wwwuserd}]: " wwwuser
wwwuser=${wwwuser:-$wwwuserd}

read -p "which group does the webserver run as? [${wwwgroupd}]: " wwwgroup
wwwgroup=${wwwgroup:-$wwwgroupd}

mkdir -p $(dirname $logfile) && touch $logfile || echo "### could not create logfile!"
chown $wwwuser:$wwwgroup $logfile
chmod 0640 $logfile
#echo "logfile will be created at: ${logfile}"



### Apache htaccess file config
echo "if you are using apache it is recommended to enable the .htaccess file to prevent unauthorized access to the .env file and any logfile."
echo "select if you want to enable the .htaccess file:"
echo "[1] no .htaccess file. (e.g. using nginx)"
echo "[2] block access to .env file only (default log location accessible)"
echo "[3] block access to .env file and log file"

read -p "select from the choices above [1]: " choice
case $choice in
2)
cat > $htaccess << EOM
<FilesMatch "\.env$">
Order allow,deny
Deny from all
</FilesMatch>
EOM
rm .htaccess.example
;;
3)
mv .htaccess{.example,}
;;
*)
rm .htaccess.example
;;
esac

### nginx htaccess equivalent message
echo "if you are using nginx please read the docs about how to disable access to certain files and folders.\nyou might add a location block to the beginning of your site config as follows:"
echo -e " location ~* (env|log|json) {\n deny all;\n return 404;\n }"

read -p "do you wish to enable result return? [y/N]: " returnip
if [[ ${returnip,,::1} == "y" ]]
then
#echo "enabling return ip"
returnip="true"
else
#echo "disabling return ip"
returnip="false"
fi

read -p "do you want to allow creation of new entries on the fly? [y/N]: " allowcreate
if [[ ${allowcreate,,::1} == "y" ]]
then
#echo "enabling return ip"
allowcreate="true"
else
#echo "disabling return ip"
allowcreate="false"
fi


### create the .env file
if [ -f $envfile ]
then
echo "${envfile} already exists!"
read -p "overwrite? [y/N]: " overwrite
if [[ ! ${overwrite,,::1} == y ]]
then
echo "script cancelled. exiting"
exit 1
fi
fi

#echo "creating .env file"
touch $envfile
echo "# file created at $(date)" >$envfile
echo "# by configuration script version ${scriptversion}" >> $envfile
echo "username=\"${user}\"" >> $envfile
echo "password=\"${pass}\"" >> $envfile
echo "apiKey=\"${apikey}\"" >> $envfile
echo "apiPassword=\"${apipass}\"" >> $envfile
echo "customerId=\"${custid}\"" >> $envfile
echo "debug=${debug}" >> $envfile
echo "log=${log}" >> $envfile
echo "logFile=${logfile}" >> $envfile
echo "returnIp=${returnip}" >> $envfile
echo "allowCreate=${allowcreate}" >> $envfile

echo "created .env file at: ${envfile}"
4 changes: 3 additions & 1 deletion .env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ apiPassword="netcup DNS API Password"
customerId="netcup customer ID"
debug=true
log=true
logFile=log.json
logFile=log.json
returnIp=true
allowCreate=false
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.idea
.env
.ignore
102 changes: 94 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,80 @@ Self-hosted dynamic DNS php script to update netcup DNS API from Router like AVM
* Felix Kretschmer [@fernwerker](https://github.com/fernwerker)
* Philipp Tempel [@philipptempel](https://github.com/philipptempel)
* Branko Wilhelm [@b2un0](https://github.com/b2un0)
* Nils Blume [@niiwiicamo](https://github.com/niiwiicamo)

## Usage
### Installation
* Copy all files to your webspace
* If you want multiple endpoints that each can only update one domain look at the mydomain folder.<br>
The update URL would be https://`url`/mydomain/update.php?(...)
* create a copy of `.env.dist` as `.env` and configure:
* `username` -> The username for your Router to authenticate (so not everyone can update your DNS)
* `password` -> password for your Router
* `apiKey` -> API key which is generated in netcup CCP
* `apiPassword` -> API password which is generated in netcup CCP
* `customerId` -> your netcup Customer ID
* `debug` -> true|false enables debug mode and generates output of update.php (normal operation has no output)

* Create each host record in your netcup CCP (DNS settings) before using the script. The script does not create any missing records.

Parameter | Example | Explanation
---: | :--- | :---
`username` | dnsupdater | The username for your Router to authenticate (so not everyone can update your DNS)
`password` | secretpleasechange | password for your Router
`apiKey` | 18neqwd91e21onei1p23841 | API key which is generated in netcup CCP
`apiPassword` | 82jewqde9m30 | API password which is generated in netcup CCP
`customerId` | 12345 | your netcup Customer ID
`log` | `true` / false | enables logging
`logFile` | log.json | configures logfile location if enabled
`debug` | true / `false` | enables debug mode and generates more output from update.php (normal operation has no output). Needed to receive stack traces from errors.
`returnIp` | `true` / false | enables return of result if a record was changed
`allowCreate` | true/`false` | allows creation of entries if parameter `create=true` in URL
`restrictDomain` | true / `false` | allows admin to restrict the domain to update to a given value `domain` and/or `host`. See URL parameters for host parameter explanation

* alternatively you can use .configure.sh to create your .env file for you (if you are on a *NIX system)

* Create each host record in your netcup CCP (DNS settings) before using the script. <s>The script does not create any missing records.</s><br>
You can now set `allowCreate=true` in .env and pass `create=true` as URL parameter to create entries on the fly.


## URL possible uses:
### Required parameters in URL:

<b>user, password and domain</b> are <i> always needed</i>, as well as at least one of the following: <br>
<b>ipv4, ipv6, txt</b>


Parameter | Example | Explanation
---: | :--- | :---
user | dnsupdater | username to authenticate against this script as defined in .env file
password | secretpleasechange |password for that user as defined in .env file
domain | home.example.com | `case A)` If `host` is not specified: the FQDN for your host
domain | example.com | `case B)` If you want to update the @ or * record
domain | example.com | `case C)` If `host`is specified: only the domain part as registered at netcup "nas.home.example.com"
host | nas.home | `case C)` If your domain contains more than 3 levels "nas.home.example.com"
ipv4 | 1.2.3.4 | the ipv4 address to update an existing A record
ipv6 | fe80::12:34:56 | the ipv6 address to update an existing AAAA record
txt | acme-challenge-text | the content to update an existing TXT record
force | true | ignore checking if the record needs to be updated, just do it anyways. Default: `false`
mode | * | `case B)` If domain is your registered domain "example.com". Possible values: `*` or `both`. Default: `@`
create | true | create all entries if none exist. e.g. will not create A if AAAA exists. Needs `allowCreate=true` in .env



#### Example URL to update A record (IPv4) of home.example.com:
https://`dyndns.example.com`/update.php?user=`username`&password=`password`&domain=`home.example.com`&ipv4=`IPv4`

#### Example URL to force update AAAA record (IPv6) of example.com:
https://`dyndns.example.com`/update.php?user=`username`&password=`password`&domain=`example.com`&ipv6=`IPv6`&force=`true`

#### Example URL to update A and AAAA records of home.example.com:
https://`dyndns.example.com`/update.php?user=`username`&password=`password`&domain=`home.example.com`&ipv4=`IPv4`&ipv6=`IPv6`

#### Example URL to update TXT record _acme-challenge of home.example.com:
https://`dyndns.example.com`/update.php?user=`username`&password=`password`&domain=`_acme-challenge.example.com`&txt=`textcontent`

#### Example URL to update A record of nas.home.example.com:
https://`dyndns.example.com`/update.php?user=`username`&password=`password`&domain=`example.com`&host=`nas.home`&ipv4=`IPv4`

#### Example URL to update AAAA wildcard record of example.com:
https://`dyndns.example.com`/update.php?user=`username`&password=`password`&domain=`example.com`&mode=`*`

#### Example URL to create A and TXT records of new.example.com:
https://`dyndns.example.com`/update.php?user=`username`&password=`password`&domain=`new.example.com`&ipv4=`IPv4`&txt=`textcontent`&create=`true`


### AVM FRITZ!Box Settings
* Go to "Internet" -> "Freigaben" -> "DynDNS"
Expand All @@ -32,6 +93,31 @@ Self-hosted dynamic DNS php script to update netcup DNS API from Router like AVM
* Username: `<username as defined in .env file>`
* Password: `<password as definied in .env file>`

### Synology DSM Settings
* Go to "Control Panel" -> "External Access" -> "DDNS"
* Click on "Customize Provider" to create a profile for your own DDNS server
* Service Provider: This is the display name of your custom provider
* Update-URL: `https://<url of your webspace>/update.php?user=__USERNAME__&password=__PASSWORD__&ipv4=__MYIP__&domain=__HOSTNAME__`
* Attention: The variables are delimited by two underscores
* Currently Synology custom DDNS does not support IPv6, for whatever reason.
* Save your custom provider
* Click on "Add" to create a DDNS job
* Select your custom provider. Notice that an asterisk [*] has appeared in front of the name to signify that this is a custom provider.
* Hostname: `<host record that is supposed to be updated>`
* Username/Email: `<username as defined in .env file>`
* Password/Key: `<password as defined in .env file>`
* External Address (IPv4): probably "Auto", uses Synology service to find own external IP
* External Address (IPv6): doesn't matter, currently not supported by Synology

### pfSense Settings
* Go to "Services" -> "Dynamic DNS"
* Click on "Add" to create a DDNS profile
* Service Type: "Custom"
* Interface to monitor: `<select you WAN interface>`
* Interface to send update from: `<select your WAN interface>`
* Update URL: `https://<url of your webspace>/update.php?user=<user from .env>&password=<password from .env>&ipv4=%IP%&domain=<host record to update>`
* Leave all other fields empty / default

# run as cronjob on a **nix based device
* see [examples](./examples)

Expand Down
67 changes: 67 additions & 0 deletions examples/multisite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Multiple Endpoints with separate credentials
For advanced use you might want to have separate users that can each only update one domain entry.

In that case it might be beneficial to habe multiple endpoints, e.g. `https://dyndns.example.com/endpointN/update.php` where endpointN is any directory name you wish.

## Setting up multiple endpoints
The directory structure of your webroot might look like this:
<pre>
├── index.html
├── src
│ ├── Config.php
│ ├── Handler.php
│ ├── Payload.php
│ └── Soap.php
├── fritzbox # this is a subdomain
│ ├── .env
│ └── update.php
├── nas # this is another
│ ├── .env
│ └── update.php
├── examplenet # uses another netcup account
│ ├── .env
│ └── update.php
└── subdomain1 # and another subdomain
├── .env
└── update.php
</pre>

Here the update.php files are copied from the mydomain example directory. All .env files contain different user credentials and may even use different netcup credentials.

## Setting up domain restrictions per .env file
It is nice to have multiple sets of credentials, but if anyone can update any entry of any domain this defeats the purpose.

That is why you can enable domain restriction per .env file and thereby per set of user credentials.

In these cases you the domain you send in your url will be ignored in favour of the one configured in the .env file. <b>You still need to send a placeholder for validation purposes.</b>

Example .env file for fritzbox.example.com.<br>
Callable by: `https://dyndns.example.com/fritzbox/update.php?user=fritzbox&password=changeme&domain=placeholder&ipv4=1.2.3.4`
<pre>
username="fritzbox"
password="changemeplease"
apiKey="j1meo213em823jd2q9"
apiPassword="12345secret"
customerId="12345"
debug=false
log=true
logFile=/var/log/dnsupdater/fritzbox.json
restrictDomain=true
domain=fritzbox.example.com
</pre>

Example .env file for nas.home.example.com.<br>
Callable by: `https://dyndns.example.com/nas/update.php?user=nas&password=changeme&domain=placeholder&ipv4=1.2.3.4`
<pre>
username="nas"
password="changemeplease"
apiKey="j1meo213em823jd2q9"
apiPassword="12345secret"
customerId="12345"
debug=false
log=true
logFile=/var/log/dnsupdater/nas.json
restrictDomain=true
domain=example.com # for explicit use of third-level domain
host=nas.home # we use the optional host parameter
</pre>
12 changes: 12 additions & 0 deletions examples/mydomain/.env.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
username="only-mydomain"
password="changemeplease"
apiKey="netcup DNS API Key"
apiPassword="netcup DNS API Password"
customerId="netcup customer ID"
debug=false
log=true
logFile=mydomain.json
returnIp=true
allowCreate=false
restrictDomain=true
domain="mydomain.example.com"
Loading