From 511bdc08f3fb7245b00efcf5f2aad69400ce1065 Mon Sep 17 00:00:00 2001 From: Kekskurse Date: Sat, 10 Aug 2019 16:34:53 +0200 Subject: [PATCH] DynDns --- .gitignore | 2 + Dockerfile | 5 + app/Component/Config/File.php | 29 +++ app/Component/Config/IConfig.php | 7 + app/Console/Commands/SyncDomain.php | 40 ++++ app/Console/Kernel.php | 2 + app/Http/Controllers/DynDnsController.php | 34 +++ app/Jobs/Updater/Cloudflare.php | 97 ++++++++ app/Jobs/Updater/IUpdater.php | 7 + app/Providers/AppServiceProvider.php | 10 +- bootstrap/app.php | 2 +- build.sh | 2 + composer.json | 3 +- composer.lock | 279 +++++++++++++++++++++- html | 1 + kube.yml | 88 +++++++ routes/web.php | 2 + 17 files changed, 606 insertions(+), 4 deletions(-) create mode 100644 Dockerfile create mode 100644 app/Component/Config/File.php create mode 100644 app/Component/Config/IConfig.php create mode 100644 app/Console/Commands/SyncDomain.php create mode 100644 app/Http/Controllers/DynDnsController.php create mode 100644 app/Jobs/Updater/Cloudflare.php create mode 100644 app/Jobs/Updater/IUpdater.php create mode 100755 build.sh create mode 120000 html create mode 100644 kube.yml diff --git a/.gitignore b/.gitignore index 059a517..a371e21 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ Homestead.json Homestead.yaml .env +kube.yml +storage/pv diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f797246 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,5 @@ +FROM php:7.2-apache + +RUN a2enmod rewrite + +COPY ./ /var/www diff --git a/app/Component/Config/File.php b/app/Component/Config/File.php new file mode 100644 index 0000000..fca5e46 --- /dev/null +++ b/app/Component/Config/File.php @@ -0,0 +1,29 @@ +config = json_decode(file_get_contents(storage_path(getenv("DYNDNS_CONFIG_FILE"))), true); + + } + public function validateUser($username, $password, $domain) : bool + { + foreach ($this->config["accounts"] as $account) { + if($account["username"] == $username) { + if($account["password"] == $password) { + if($account["domain"] == $domain) { + return true; + } + } + } + } + return false; + } +} diff --git a/app/Component/Config/IConfig.php b/app/Component/Config/IConfig.php new file mode 100644 index 0000000..3856360 --- /dev/null +++ b/app/Component/Config/IConfig.php @@ -0,0 +1,7 @@ +ask("Domainname"); + $ip = $this->ask("New IP"); + + $job = app(IUpdater::class); + $job->config($domain, $ip); + dispatch($job); + + + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index ad6e311..2598b34 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -2,6 +2,7 @@ namespace App\Console; +use App\Console\Commands\SyncDomain; use Illuminate\Console\Scheduling\Schedule; use Laravel\Lumen\Console\Kernel as ConsoleKernel; @@ -13,6 +14,7 @@ class Kernel extends ConsoleKernel * @var array */ protected $commands = [ + SyncDomain::class // ]; diff --git a/app/Http/Controllers/DynDnsController.php b/app/Http/Controllers/DynDnsController.php new file mode 100644 index 0000000..2788815 --- /dev/null +++ b/app/Http/Controllers/DynDnsController.php @@ -0,0 +1,34 @@ +validate($request, [ + 'hostname' => 'required', + 'myip' => 'required|ipv4', + 'password' => 'required|', + 'username' => 'required|' + ]); + + $config = app(IConfig::class); + $updater = app(IUpdater::class); + + $userValide = $config->validateUser($request->input("username"), $request->input("password"), $request->input("hostname")); + + if(!$userValide) { + return new Response("Invalide Data", 400); + } + + $updater->config($request->input("hostname"), $request->input("myip")); + $this->dispatch($updater); + + return new Response("Update started", 201); + } +} diff --git a/app/Jobs/Updater/Cloudflare.php b/app/Jobs/Updater/Cloudflare.php new file mode 100644 index 0000000..596d280 --- /dev/null +++ b/app/Jobs/Updater/Cloudflare.php @@ -0,0 +1,97 @@ +domain = $domain; + $this->ip = $ip; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle(Client $client) + { + //Get Zone for Domain in config + $res = $client->get("https://api.cloudflare.com/client/v4/zones", [ + "query" => [ + "name" => getenv("CLOUDFLARE_DOMAIN") + ], + "headers" => [ + "X-Auth-Email" => getenv("CLOUDFLARE_MAIL"), + "X-Auth-Key" => getenv("CLOUDFLARE_API_KEY") + ] + ]); + + $zoneData = \GuzzleHttp\json_decode((string)$res->getBody(), true); + + if (count($zoneData["result"]) != 1) { + throw new \Exception("No or more than once Zone found"); + } + + //Check if record exists + $res = $client->get("https://api.cloudflare.com/client/v4/zones/".$zoneData["result"][0]["id"]."/dns_records", [ + "query" => [ + "name" => $this->domain, + "type" => "A", + + ], + "headers" => [ + "X-Auth-Email" => getenv("CLOUDFLARE_MAIL"), + "X-Auth-Key" => getenv("CLOUDFLARE_API_KEY") + ] + ]); + + $recordData = \GuzzleHttp\json_decode((string)$res->getBody(), true); + if (count($recordData["result"]) > 1) { + throw new \Exception("More than once Record found"); + } + + + //Update or Create Record + if (count($recordData["result"]) == 0) { + $res = $client->post("https://api.cloudflare.com/client/v4/zones/".$zoneData["result"][0]["id"]."/dns_records", [ + "json" => [ + "name" => $this->domain, + "type" => "A", + "content"=> $this->ip, + "ttl" => 120 + + ], + "headers" => [ + "X-Auth-Email" => getenv("CLOUDFLARE_MAIL"), + "X-Auth-Key" => getenv("CLOUDFLARE_API_KEY") + ] + ]); + } else { + $res = $client->put("https://api.cloudflare.com/client/v4/zones/".$zoneData["result"][0]["id"]."/dns_records/".$recordData["result"][0]["id"], [ + "json" => [ + "name" => $this->domain, + "type" => "A", + "content"=> $this->ip, + "ttl" => 120 + + ], + "headers" => [ + "X-Auth-Email" => getenv("CLOUDFLARE_MAIL"), + "X-Auth-Key" => getenv("CLOUDFLARE_API_KEY") + ] + ]); + } + } +} diff --git a/app/Jobs/Updater/IUpdater.php b/app/Jobs/Updater/IUpdater.php new file mode 100644 index 0000000..c646929 --- /dev/null +++ b/app/Jobs/Updater/IUpdater.php @@ -0,0 +1,7 @@ +app->singleton(IUpdater::class, function ($app) { + return new \App\Jobs\Updater\Cloudflare(); + }); + $this->app->singleton(IConfig::class, function ($app) { + return new File(); + }); } } diff --git a/bootstrap/app.php b/bootstrap/app.php index ecbeb61..72030cf 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -76,7 +76,7 @@ $app->singleton( | */ -// $app->register(App\Providers\AppServiceProvider::class); +$app->register(App\Providers\AppServiceProvider::class); // $app->register(App\Providers\AuthServiceProvider::class); // $app->register(App\Providers\EventServiceProvider::class); diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..154b5ba --- /dev/null +++ b/build.sh @@ -0,0 +1,2 @@ +docker build -t docker.keks.cloud/soeren/dyndns . +docker push docker.keks.cloud/soeren/dyndns:latest diff --git a/composer.json b/composer.json index 7352148..2b4fec5 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,8 @@ "type": "project", "require": { "php": ">=7.1.3", - "laravel/lumen-framework": "5.8.*" + "laravel/lumen-framework": "5.8.*", + "guzzlehttp/guzzle":"6.*" }, "require-dev": { "fzaninotto/faker": "^1.4", diff --git a/composer.lock b/composer.lock index c74a5d2..1045ca1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "458aaee8e3716a16a84a6d70f17188b9", + "content-hash": "12128f426372b3b95bac883d8e1c7fdc", "packages": [ { "name": "doctrine/inflector", @@ -245,6 +245,193 @@ ], "time": "2019-07-19T20:52:08+00:00" }, + { + "name": "guzzlehttp/guzzle", + "version": "6.3.3", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "shasum": "" + }, + "require": { + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.4", + "php": ">=5.5" + }, + "require-dev": { + "ext-curl": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", + "psr/log": "^1.0" + }, + "suggest": { + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "6.3-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2018-04-22T15:46:56+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "v1.3.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646", + "shasum": "" + }, + "require": { + "php": ">=5.5.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "time": "2016-12-20T10:07:11+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "239400de7a173fe9901b9ac7c06497751f00727a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a", + "reference": "239400de7a173fe9901b9ac7c06497751f00727a", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "ext-zlib": "*", + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" + }, + "suggest": { + "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "time": "2019-07-01T23:21:34+00:00" + }, { "name": "illuminate/auth", "version": "v5.8.30", @@ -1807,6 +1994,56 @@ ], "time": "2017-02-14T16:28:37+00:00" }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "time": "2016-08-06T14:39:51+00:00" + }, { "name": "psr/log", "version": "1.1.0", @@ -1902,6 +2139,46 @@ ], "time": "2017-10-23T01:57:42+00:00" }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "time": "2019-03-08T08:55:37+00:00" + }, { "name": "symfony/console", "version": "v4.3.3", diff --git a/html b/html new file mode 120000 index 0000000..d70ebaa --- /dev/null +++ b/html @@ -0,0 +1 @@ +public \ No newline at end of file diff --git a/kube.yml b/kube.yml new file mode 100644 index 0000000..df5ddb4 --- /dev/null +++ b/kube.yml @@ -0,0 +1,88 @@ +apiVersion: certmanager.k8s.io/v1alpha1 +kind: Certificate +metadata: + name: dns.app.keks.cloud + namespace: dyndns +spec: + secretName: dns-app-keks-cloud-tls + acme: + config: + - dns01: + provider: cf-dns + domains: + - 'dns.app.keks.cloud' + commonName: 'dns.app.keks.cloud' + dnsNames: + - dns.app.keks.cloud + issuerRef: + kind: ClusterIssuer + name: letsencrypt-prod +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: env-dns-web + namespace: dyndns +data: + DYNDNS_CONFIG: "file" + DYNDNS_CONFIG_FILE: "pv/config.json" + CLOUDFLARE_API_KEY: "b82e4168ec5b30b1a6cbda0679e8827af9481" + CLOUDFLARE_DOMAIN: "keks.cloud" + CLOUDFLARE_MAIL: "cloudflare@kekskurse.de" +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: webapi + namespace: dyndns +spec: + replicas: 1 + template: + metadata: + labels: + app: webapi + spec: + containers: + - name: web + image: docker.keks.cloud/soeren/dyndns:latest + ports: + - containerPort: 80 + envFrom: + - configMapRef: + name: env-dns-web + imagePullSecrets: + - name: docker-keks-cloud +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + field.cattle.io/targetWorkloadIds: '["deployment:dyndns:webapi"]' + name: ingress-dyndns + namespace: dyndns +spec: + ports: + - port: 80 + protocol: TCP + targetPort: 80 + type: ClusterIP +status: + loadBalancer: {} +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: webapi + namespace: dyndns +spec: + rules: + - host: dns.app.keks.cloud + http: + paths: + - backend: + serviceName: ingress-dyndns + servicePort: 80 + path: / + tls: + - secretName: dns-app-keks-cloud-tls + diff --git a/routes/web.php b/routes/web.php index 0f73f16..ddea404 100644 --- a/routes/web.php +++ b/routes/web.php @@ -14,3 +14,5 @@ $router->get('/', function () use ($router) { return $router->app->version(); }); + +$router->get("/dyndns", ["uses" => "DynDnsController@update"]);