Добавил named и функцию синхронизации конфига

This commit is contained in:
kirillius 2025-01-10 02:35:38 +03:00
parent 7f7106386a
commit d43ad8168e
32 changed files with 498 additions and 47 deletions

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

16
.idea/deployment.xml Normal file
View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PublishConfigData" autoUpload="Always" serverName="1" remoteFilesAllowedToDisappearOnAutoupload="false" confirmBeforeUploading="false">
<option name="confirmBeforeUploading" value="false" />
<serverData>
<paths name="1">
<serverdata>
<mappings>
<mapping deploy="/" local="$PROJECT_DIR$" web="/" />
</mappings>
</serverdata>
</paths>
</serverData>
<option name="myAutoUpload" value="ALWAYS" />
</component>
</project>

37
.idea/misc.xml Normal file
View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectInspectionProfilesVisibleTreeState">
<entry key="Project Default">
<profile-state>
<expanded-state>
<State>
<id>Error handlingJava</id>
</State>
<State>
<id>Java</id>
</State>
<State>
<id>Probable bugsJava</id>
</State>
<State>
<id>RESTful Web Service (JAX-RS)</id>
</State>
<State>
<id>Spring</id>
</State>
<State>
<id>Spring BootSpring</id>
</State>
</expanded-state>
<selected-state>
<State>
<id>User defined</id>
</State>
</selected-state>
</profile-state>
</entry>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/protected-resources-list.iml" filepath="$PROJECT_DIR$/.idea/protected-resources-list.iml" />
</modules>
</component>
</project>

4
.idea/php.xml Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PhpProjectSharedConfiguration" php_language_level="8.0" />
</project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/sshConfigs.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SshConfigs">
<configs>
<sshConfig authType="PASSWORD" host="172.16.100.40" id="1f4a1f9f-da20-402f-aacd-0cb94b211004" port="22" nameFormat="DESCRIPTIVE" username="root" useOpenSSHConfig="false" />
</configs>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

14
.idea/webServers.xml Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WebServers">
<option name="servers">
<webServer id="a4ede633-44cd-40df-a8db-a1d5f363083d" name="1">
<fileTransfer rootFolder="/opt/protected-resources-list" accessType="SFTP" host="172.16.100.40" port="22" sshConfigId="1f4a1f9f-da20-402f-aacd-0cb94b211004" sshConfig="root@172.16.100.40:22 password">
<advancedOptions>
<advancedOptions dataProtectionLevel="Private" keepAliveTimeout="0" passiveMode="true" shareSSLContext="true" />
</advancedOptions>
</fileTransfer>
</webServer>
</option>
</component>
</project>

View File

@ -4,12 +4,9 @@
## networks
Каталог с файлами сервисов и их подсетей
## utils
Различные скрипты и утилиты
### Установка
```shell
apk add php php-session jq git
apk add php php-session php-curl jq git
cd /opt
git clone https://git.kirillius.ru/kirillius/protected-resources-list.git
cd /opt/protected-resources-list/

27
bin/sync-networks Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/php
<?php
require_once dirname(__DIR__) . "/loader.php";
require_once dirname(__DIR__) . "/plugins/netsync/Netsync.php";
try {
$rpc = new StaticRPC();
$instance = $rpc->getPlugins()["netsync"] ?? null;
if ($instance === null) {
throw new RuntimeException("Plugin is not enabled");
}
/**
* @var Netsync $instance
*/
$config = $instance->getRoutingConfig();
$outfile = $argv[1];
file_put_contents($outfile, implode("\n", $config));
} catch (Exception $e) {
echo "\nError:" . $e->getMessage() . "\n";
exit(1);
}
exit(0);

View File

@ -13,4 +13,5 @@ PORT=`jq -r .web.port $CFGFILE`
#TODO FIXME !!!!
killall php
cd $ROOT
php $ROOT/loader.php --init
php -S $HOST:$PORT $ROOT/server.php

View File

@ -2,7 +2,7 @@
interface IPluggable
{
const PROTECTED_NAMES = ["enable", "disable", "render", "init"];
public function init(PluginContext $context);
public function onServerStarted();
public function onInit(PluginContext $context);
public function onSync();
}

42
classes/Plugin.php Normal file
View File

@ -0,0 +1,42 @@
<?php
abstract class Plugin implements IPluggable
{
protected PluginContext $context;
protected array $config;
public function onServerStarted()
{
}
public function onSync()
{
}
public function onInit(PluginContext $context): void
{
$this->context = $context;
$this->checkConfig();
$this->config = $this->context->getConfig()[$context->getName()];
}
protected function checkConfig(): void
{
$config = $this->context->getConfig();
$defaults = $this->context->getMetadata()["config"];
$name = $this->context->getName();
if (!isset($config[$name])) {
$config[$name] = $defaults;
return;
}
foreach ($defaults as $key => $value) {
if (!isset($config[$name][$key])) {
$config[$name][$key] = $value;
}
}
}
}

View File

@ -5,14 +5,22 @@ class PluginContext
private RPC $RPC;
private Config $config;
private array $metadata;
private string $name;
public function getName(): string
{
return $this->name;
}
/**
* @param RPC $RPC
* @param Config $config
* @param array $metadata
* @param string $name
*/
public function __construct(RPC $RPC, Config $config, array $metadata)
public function __construct(RPC $RPC, Config $config, array $metadata, string $name)
{
$this->name = $name;
$this->RPC = $RPC;
$this->config = $config;
$this->metadata = $metadata;

View File

@ -14,7 +14,7 @@ class RPC
try {
$meta = $this->getPluginMetadata($plugin);
$inst = $this->loadPlugin($plugin, $meta["class"]);
$inst->init(new PluginContext($this, $this->config, $meta));
$inst->onInit(new PluginContext($this, $this->config, $meta, $plugin));
} catch (Error $e) {
continue;
}
@ -117,6 +117,12 @@ class RPC
return md5(sha1($what) . md5($what));
}
private function isInternalMethod($name)
{
$cls = new ReflectionClass(IPluggable::class);
return $cls->hasMethod($name);
}
public function __invoke($method, $args)
{
$parts = explode("::", $method);
@ -126,7 +132,7 @@ class RPC
$this->checkAuth();
$plugin = $this->plugins[$parts[0]];
$methodname = $parts[1];
if (in_array($methodname, IPluggable::PROTECTED_NAMES)) {
if ($this->isInternalMethod($methodname)) {
throw new RuntimeException("Unable to invoke internal methods");
}
return call_user_func([$plugin, $methodname], $args);

View File

@ -3,4 +3,13 @@
require_once __DIR__ . "/common.inc.php";
spl_autoload_register(function ($classname) {
require_once __DIR__ . "/classes/" . $classname . ".php";
});
});
if (isset($argv) and in_array("--init", $argv)) {
$rpc = new StaticRPC();
foreach ($rpc->getPlugins() as $plugin => $instance) {
/**
* @var IPluggable $instance
*/
$instance->onServerStarted();
}
}

View File

@ -1,7 +1,7 @@
{
"description": "habrahabr",
"domains": [
"habr.com"
"habr.com"
],
"networks": [
"178.248.237.68/32"

20
plugins/api/API.php Normal file
View File

@ -0,0 +1,20 @@
<?php
class API extends Plugin
{
public function onInit(PluginContext $context): void
{
parent::onInit($context);
if (!isset($this->config["key"])) {
$this->config["key"] = sha1(rand() . uniqid());
$this->context->getConfig()->save();
}
}
public function getKey(): string
{
return $this->config["key"];
}
}

View File

@ -0,0 +1,5 @@
{
"class": "API",
"config": {
}
}

7
plugins/api/plugin.js Normal file
View File

@ -0,0 +1,7 @@
import {App} from "/assets/App.js";
(async function () {
let key = await App.RPC.__invoke("api::getKey");
$("body").append("<span>API Key: " + key + "</span>")
})();

View File

@ -0,0 +1,57 @@
<?php
class BindPlugin extends Plugin
{
public function restart(): string|bool|null
{
$selectedDomains = [];
$networks = (new NetworkConfigReader())->getConfigs();
//add new routes
foreach ($this->context->getConfig()["networks"] as $key) {
if (isset($networks[$key])) {
foreach ($networks[$key]["domains"] as $domain) {
$selectedDomains[] = $domain;
}
}
}
$selectedDomains = array_unique($selectedDomains);
$data = [];
foreach ($selectedDomains as $domain) {
$data[] = $this->createForwardRecord($domain);
}
file_put_contents($this->config["file"], implode("\n", $data));
return shell_exec($this->config["restart_cmd"]);
}
private function createForwardRecord($domain)
{
$fwd = implode(";", $this->config["forwarders"]) . ";";
return <<<TXT
zone "{$domain}" IN {
type forward;
forward only;
forwarders{{$fwd}};
};
TXT;
}
public function onServerStarted()
{
$this->restart();
}
public function onSync()
{
$this->restart();
}
}

View File

@ -0,0 +1,8 @@
{
"class": "BindPlugin",
"config": {
"restart_cmd": "/etc/init.d/named restart",
"file": "/var/bind/forward.dns",
"forwarders": ["8.8.8.8", "1.1.1.1"]
}
}

21
plugins/named/plugin.js Normal file
View File

@ -0,0 +1,21 @@
import {App} from "/assets/App.js";
(async function () {
$("#buttons").append(`<button id="restart-bind">Restart named</button>`);
$("#restart-bind").click(function () {
const self = $(this);
self.prop("disabled", true);
(async function () {
try {
alert(await App.RPC.__invoke("named::restart"));
} finally {
setTimeout(() => {
self.prop("disabled", false);
}, 5000);
}
})();
});
})();

View File

@ -0,0 +1,38 @@
<?php
class Netsync extends Plugin
{
public function sync()
{
$host = $this->config["master"];
$key = $this->config["key"];
if (empty($key)) {
throw new RuntimeException("API key is empty");
}
$ch = curl_init($host);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
"jsonrpc" => "2.0",
"id" => "1",
"method" => "getNetworks",
"params" => []
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"X-Auth: " . md5($key),
"Content-type: application/json"
]);
$output = curl_exec($ch);
if (curl_error($ch)) {
return curl_error($ch);
}
curl_close($ch);
return $output;
}
}

View File

@ -0,0 +1,7 @@
{
"class": "Netsync",
"config": {
"master": "127.0.0.1:8001",
"key": ""
}
}

21
plugins/netsync/plugin.js Normal file
View File

@ -0,0 +1,21 @@
import {App} from "/assets/App.js";
(async function () {
$("#buttons").append(`<button id="sync">Force sync</button>`);
$("#sync").click(function () {
const self = $(this);
self.prop("disabled", true);
(async function () {
try {
alert(await App.RPC.__invoke("netsync::sync"));
} finally {
setTimeout(() => {
location.reload();
}, 5000);
}
})();
});
})();

View File

@ -1,29 +1,15 @@
#!/usr/bin/php
<?php
class Openvpn implements IPluggable
class Openvpn extends Plugin
{
private PluginContext $context;
public function restart()
{
//restart ovpn
return shell_exec($this->context->getConfig()["ovpn"]["restart_cmd"]);
return shell_exec($this->config["restart_cmd"]);
}
public function init(PluginContext $context): void
{
$this->context = $context;
$this->checkConfig();
}
private function checkConfig(): void
{
$config = $this->context->getConfig();
if (!isset($config["ovpn"])) {
$config["ovpn"] = $this->context->getMetadata()["config"];
}
}
public function getRoutingConfig(): array
{
@ -44,5 +30,15 @@ class Openvpn implements IPluggable
return $data;
}
public function onServerStarted()
{
$this->restart();
}
public function onSync()
{
$this->restart();
}
}

View File

@ -1,14 +1,14 @@
<?php
class QuaggaPlugin implements IPluggable
class QuaggaPlugin extends Plugin
{
private PluginContext $context;
const REM_PREFIX = "! routes from file ";
public function restart(): string
{
$configfile = $this->context->getConfig()["quagga"]["file"];
$configfile = $this->config["file"];
if (!file_exists($configfile)) {
throw new RuntimeException("Quagga config file not found");
@ -67,23 +67,16 @@ class QuaggaPlugin implements IPluggable
file_put_contents($configfile, implode("\n", $lines));
//restart zebra
return shell_exec($this->context->getConfig()["quagga"]["restart_cmd"]);
return shell_exec($this->config["restart_cmd"]);
}
public function init(PluginContext $context): void
public function onServerStarted()
{
$this->context = $context;
$this->checkConfig();
$this->restart();
}
private function checkConfig(): void
public function onSync()
{
$config = $this->context->getConfig();
if (!isset($config["quagga"])) {
$config["quagga"] = $this->context->getMetadata()["config"];
}
$this->restart();
}
}

View File

@ -1,6 +1,6 @@
<?php
class Updates implements IPluggable
class Updates extends Plugin
{
@ -20,8 +20,5 @@ class Updates implements IPluggable
return @shell_exec("git --no-pager pull --verbose 2>&1");
}
public function init(PluginContext $context)
{
}
}

12
sort.php Normal file
View File

@ -0,0 +1,12 @@
<?php
const FILE = "test.txt";
$data = file_get_contents(FILE);
$data = explode("\n", $data);
sort($data);
file_put_contents(FILE, implode("\n", $data));

69
test.txt Normal file
View File

@ -0,0 +1,69 @@
"108.138.0.0/15",
"108.156.0.0/14",
"111.13.0.0/8",
"116.129.226.0/24",
"118.193.97.0/24",
"119.147.182.0/24",
"120.232.0.0/16",
"120.52.0.0/16",
"13.0.0.0/8",
"130.176.0.0/16",
"143.204.0.0/16",
"144.220.0.0/16",
"15.128.0.0/9",
"18.0.0.0/8",
"180.163.57.0/24",
"204.246.0.0/16",
"205.251.0.0/16",
"216.137.32.0/19",
"3.0.0.0/8",
"34.128.0.0/9",
"35.0.0.0/8",
"36.103.232.0/24",
"43.218.56.0/24",
"44.0.0.0/8",
"47.129.0.0/16",
"52.0.0.0/8",
"54.0.0.0/8",
"58.254.138.0/24",
"64.252.0.0/16",
"65.8.0.0/16",
"65.9.0.0/16",
"70.132.0.0/18",
"71.152.0.0/17",
"99.0.0.0/8",