mirror of
https://github.com/fernwerker/ownDynDNS.git
synced 2025-07-10 14:15:14 +02:00
Merge pull request #1 from NiiWiiCamo/testing-multisite
Multisite with domain restriction
This commit is contained in:
commit
a1feba58bd
7 changed files with 209 additions and 56 deletions
29
README.md
29
README.md
|
@ -10,21 +10,28 @@ Self-hosted dynamic DNS php script to update netcup DNS API from Router like AVM
|
||||||
## Usage
|
## Usage
|
||||||
### Installation
|
### Installation
|
||||||
* Copy all files to your webspace
|
* 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:
|
* 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
|
Parameter | Example | Explanation
|
||||||
* `apiKey` -> API key which is generated in netcup CCP
|
---: | :--- | :---
|
||||||
* `apiPassword` -> API password which is generated in netcup CCP
|
`username` | dnsupdater | The username for your Router to authenticate (so not everyone can update your DNS)
|
||||||
* `customerId` -> your netcup Customer ID
|
`password` | secretpleasechange | password for your Router
|
||||||
* `log` -> true|false enables logging
|
`apiKey` | 18neqwd91e21onei1p23841 | API key which is generated in netcup CCP
|
||||||
* `logFile` -> configures logfile location if enabled
|
`apiPassword` | 82jewqde9m30 | API password which is generated in netcup CCP
|
||||||
* `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.
|
`customerId` | 12345 | your netcup Customer ID
|
||||||
* `returnIp` -> true|false enables return of result if a record was changed
|
`log` | `true` / false | enables logging
|
||||||
* `allowCreate` -> true|false allows creation of entries if parameter `create=true` in URL
|
`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)
|
* 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. The script does not create any missing records.
|
* 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:
|
## URL possible uses:
|
||||||
|
|
67
examples/multisite.md
Normal file
67
examples/multisite.md
Normal file
|
@ -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
examples/mydomain/.env.dist
Normal file
12
examples/mydomain/.env.dist
Normal file
|
@ -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"
|
20
examples/mydomain/update.php
Executable file
20
examples/mydomain/update.php
Executable file
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
error_reporting(-1);
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
ini_set('html_errors', 0);
|
||||||
|
|
||||||
|
header('Content-Type: text/plain; charset=utf-8');
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../src/Soap.php';
|
||||||
|
require_once __DIR__ . '/../src/Config.php';
|
||||||
|
require_once __DIR__ . '/../src/Payload.php';
|
||||||
|
require_once __DIR__ . '/../src/Handler.php';
|
||||||
|
|
||||||
|
if (!file_exists('.env')) {
|
||||||
|
throw new RuntimeException('.env file missing');
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = parse_ini_file('.env', false, INI_SCANNER_TYPED);
|
||||||
|
|
||||||
|
(new netcup\DNS\API\Handler($config, $_REQUEST))->doRun();
|
|
@ -55,6 +55,21 @@ final class Config
|
||||||
*/
|
*/
|
||||||
private $allowCreate = false;
|
private $allowCreate = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $restrictDomain = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $domain;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $host;
|
||||||
|
|
||||||
|
|
||||||
public function __construct(array $config)
|
public function __construct(array $config)
|
||||||
{
|
{
|
||||||
|
@ -158,4 +173,58 @@ final class Config
|
||||||
{
|
{
|
||||||
return $this->allowCreate;
|
return $this->allowCreate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isRestrictDomain()
|
||||||
|
{
|
||||||
|
return $this->restrictDomain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDomain()
|
||||||
|
{
|
||||||
|
if (empty($this->host))
|
||||||
|
{
|
||||||
|
return $this->domain;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return $this->host . "." . $this->domain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getHost()
|
||||||
|
{
|
||||||
|
if (!empty($this->host))
|
||||||
|
{
|
||||||
|
return $this->host;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$domainParts = explode('.', $this->domain);
|
||||||
|
return $domainParts[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDomainName()
|
||||||
|
{
|
||||||
|
// hack if top level domain are used for dynDNS
|
||||||
|
if (1 === substr_count($this->domain, '.')) {
|
||||||
|
return $this->domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
$domainParts = explode('.', $this->domain);
|
||||||
|
array_shift($domainParts); // remove sub domain
|
||||||
|
return implode('.', $domainParts);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,6 @@ final class Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @return self
|
* @return self
|
||||||
*/
|
*/
|
||||||
public function doRun()
|
public function doRun()
|
||||||
|
@ -125,34 +124,36 @@ final class Handler
|
||||||
$this->doLog(sprintf('api login failed, message: %s', $loginHandle->longmessage));
|
$this->doLog(sprintf('api login failed, message: %s', $loginHandle->longmessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if domain is restricted in config, force use of config values for domain and host
|
||||||
|
if ($this->config->isRestrictDomain()) {
|
||||||
|
$this->doLog('domain is restricted by .env file');
|
||||||
|
$updateDomain = $this->config->getDomain();
|
||||||
|
$updateDomainName = $this->config->getDomainName();
|
||||||
|
$updateHost = $this->config->getHost();
|
||||||
|
$this->doLog(sprintf('ignoring received domain, using configured domain: %s', $updateDomain));
|
||||||
|
} else {
|
||||||
|
$updateDomain = $this->payload->getDomain();
|
||||||
|
$updateDomainName = $this->payload->getDomainName();
|
||||||
|
$updateHost = $this->payload->getHost();
|
||||||
|
}
|
||||||
|
|
||||||
$infoHandle = $dnsClient->infoDnsRecords(
|
$infoHandle = $dnsClient->infoDnsRecords(
|
||||||
$this->payload->getDomainName(),
|
$updateDomainName,
|
||||||
$this->config->getCustomerId(),
|
$this->config->getCustomerId(),
|
||||||
$this->config->getApiKey(),
|
$this->config->getApiKey(),
|
||||||
$loginHandle->responsedata->apisessionid,
|
$loginHandle->responsedata->apisessionid,
|
||||||
$clientRequestId
|
$clientRequestId
|
||||||
);
|
);
|
||||||
|
|
||||||
// test: create new entry if it does not exist
|
|
||||||
$createnewentry = true;
|
|
||||||
$exists = false;
|
$exists = false;
|
||||||
$testing = true;
|
|
||||||
|
|
||||||
$ipv4changes = false;
|
$ipv4changes = false;
|
||||||
$ipv6changes = false;
|
$ipv6changes = false;
|
||||||
$txtchanges = false;
|
$txtchanges = false;
|
||||||
|
|
||||||
|
|
||||||
// TODO: delete, testing
|
|
||||||
// echo "--- EXISTING ENTRIES BELOW ---", PHP_EOL;
|
|
||||||
// $teststring = print_r($infoHandle->responsedata->dnsrecords, true);
|
|
||||||
// echo $teststring, PHP_EOL;
|
|
||||||
|
|
||||||
foreach ($infoHandle->responsedata->dnsrecords as $key => $record) {
|
foreach ($infoHandle->responsedata->dnsrecords as $key => $record) {
|
||||||
$recordHostnameReal = (!in_array($record->hostname, $this->payload->getMatcher())) ? $record->hostname . '.' . $this->payload->getDomainName() : $this->payload->getDomainName();
|
$recordHostnameReal = (!in_array($record->hostname, $this->payload->getMatcher())) ? $record->hostname . '.' . $updateDomainName : $updateDomainName;
|
||||||
|
|
||||||
|
if ($recordHostnameReal === $updateDomain) {
|
||||||
if ($recordHostnameReal === $this->payload->getDomain()) {
|
|
||||||
|
|
||||||
// found matching entry, no need to create one
|
// found matching entry, no need to create one
|
||||||
$exists = true;
|
$exists = true;
|
||||||
|
@ -165,7 +166,7 @@ final class Handler
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
$record->destination = $this->payload->getIpv4();
|
$record->destination = $this->payload->getIpv4();
|
||||||
$this->doLog(sprintf('IPv4 for %s set to %s', $record->hostname . '.' . $this->payload->getDomainName(), $this->payload->getIpv4()));
|
$this->doLog(sprintf('IPv4 for %s set to %s', $record->hostname . '.' . $updateDomainName, $this->payload->getIpv4()));
|
||||||
$ipv4changes = true;
|
$ipv4changes = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +178,7 @@ final class Handler
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
$record->destination = $this->payload->getIpv6();
|
$record->destination = $this->payload->getIpv6();
|
||||||
$this->doLog(sprintf('IPv6 for %s set to %s', $record->hostname . '.' . $this->payload->getDomainName(), $this->payload->getIpv6()));
|
$this->doLog(sprintf('IPv6 for %s set to %s', $record->hostname . '.' . $updateDomainName, $this->payload->getIpv6()));
|
||||||
$ipv6changes = true;
|
$ipv6changes = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,16 +190,13 @@ final class Handler
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
$record->destination = $this->payload->getTxt();
|
$record->destination = $this->payload->getTxt();
|
||||||
$this->doLog(sprintf('TXT for %s set to %s', $record->hostname . '.' . $this->payload->getDomainName(), $this->payload->getTxt()));
|
$this->doLog(sprintf('TXT for %s set to %s', $record->hostname . '.' . $updateDomainName, $this->payload->getTxt()));
|
||||||
$txtchanges = true;
|
$txtchanges = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// echo "--- Exists ---", $exists, PHP_EOL;
|
// if entry does not exist and createnewentry is true:
|
||||||
// echo "--- Createnewentry ---", $createnewentry, PHP_EOL;
|
|
||||||
|
|
||||||
// TODO: if entry does not exist and createnewentry is true:
|
|
||||||
if ( !$exists && $this->payload->getCreate() && $this->config->isAllowCreate() )
|
if ( !$exists && $this->payload->getCreate() && $this->config->isAllowCreate() )
|
||||||
{
|
{
|
||||||
// init new record set containing empty array
|
// init new record set containing empty array
|
||||||
|
@ -208,18 +206,13 @@ final class Handler
|
||||||
foreach ($this->payload->getTypes() as $key => $type)
|
foreach ($this->payload->getTypes() as $key => $type)
|
||||||
{
|
{
|
||||||
$record = new Soap\Dnsrecord();
|
$record = new Soap\Dnsrecord();
|
||||||
|
|
||||||
// echo "getDomain: ", $this->payload->getDomain(), PHP_EOL;
|
|
||||||
// echo "getDomainName: ", $this->payload->getDomainName(), PHP_EOL;
|
|
||||||
// echo "getHost: ", $this->payload->getHost(), PHP_EOL;
|
|
||||||
|
|
||||||
$record->hostname = $this->payload->getHost();
|
$record->hostname = $updateHost;
|
||||||
$record->type = $type;
|
$record->type = $type;
|
||||||
$record->priority = "0"; // only for MX, can possibly be removed
|
$record->priority = "0"; // only for MX, can possibly be removed
|
||||||
|
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case 'A':
|
case 'A':
|
||||||
// echo "A record set: ", $this->payload->getIpv4(), PHP_EOL;
|
|
||||||
$record->destination = $this->payload->getIpv4();
|
$record->destination = $this->payload->getIpv4();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -231,20 +224,13 @@ final class Handler
|
||||||
$record->destination = $this->payload->getTxt();
|
$record->destination = $this->payload->getTxt();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// echo "destination: ", $record->destination, PHP_EOL;
|
|
||||||
// echo "--- NEW ENTRY BELOW ---", PHP_EOL;
|
|
||||||
// $teststring = print_r($record, true);
|
|
||||||
// echo $teststring, PHP_EOL;
|
|
||||||
|
|
||||||
array_push($newRecordSet->dnsrecords, $record); // push new record into array
|
array_push($newRecordSet->dnsrecords, $record); // push new record into array
|
||||||
}
|
}
|
||||||
|
|
||||||
// echo "--- newRecordSet ---", PHP_EOL;
|
|
||||||
// $teststring = print_r($newRecordSet, true);
|
|
||||||
// echo $teststring, PHP_EOL;
|
|
||||||
|
|
||||||
$dnsClient->updateDnsRecords(
|
$dnsClient->updateDnsRecords(
|
||||||
$this->payload->getDomainName(),
|
$updateDomainName,
|
||||||
$this->config->getCustomerId(),
|
$this->config->getCustomerId(),
|
||||||
$this->config->getApiKey(),
|
$this->config->getApiKey(),
|
||||||
$loginHandle->responsedata->apisessionid,
|
$loginHandle->responsedata->apisessionid,
|
||||||
|
@ -261,7 +247,7 @@ final class Handler
|
||||||
$recordSet->dnsrecords = $infoHandle->responsedata->dnsrecords;
|
$recordSet->dnsrecords = $infoHandle->responsedata->dnsrecords;
|
||||||
|
|
||||||
$dnsClient->updateDnsRecords(
|
$dnsClient->updateDnsRecords(
|
||||||
$this->payload->getDomainName(),
|
$updateDomainName,
|
||||||
$this->config->getCustomerId(),
|
$this->config->getCustomerId(),
|
||||||
$this->config->getApiKey(),
|
$this->config->getApiKey(),
|
||||||
$loginHandle->responsedata->apisessionid,
|
$loginHandle->responsedata->apisessionid,
|
||||||
|
|
|
@ -255,12 +255,4 @@ final class Payload
|
||||||
return $this->force;
|
return $this->force;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getType()
|
|
||||||
{
|
|
||||||
// TODO:
|
|
||||||
return "A";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue