Bound native validation and pin offline GeoIP dependencies
This commit is contained in:
parent
99007b4518
commit
3d20330ee3
@ -12,11 +12,19 @@ final class NativeValidator
|
||||
public int $checks = 0;
|
||||
public array $rejected = [];
|
||||
private string $dir;
|
||||
private float $startedAt;
|
||||
|
||||
public function __construct(private string $singbox, private string $mihomo, private ?array $clashTemplate = null)
|
||||
public function __construct(private string $singbox, private string $mihomo, private ?array $clashTemplate = null, ?string $dataDirectory = null)
|
||||
{
|
||||
$this->startedAt = microtime(true);
|
||||
if ($dataDirectory !== null) {
|
||||
foreach (['Country.mmdb', 'geoip.metadb'] as $file) {
|
||||
if (!is_readable($dataDirectory.'/'.$file)) throw new RuntimeException('missing_offline_geoip');
|
||||
}
|
||||
}
|
||||
$this->dir = sys_get_temp_dir().'/freenode-check-'.bin2hex(random_bytes(8));
|
||||
mkdir($this->dir, 0700, true);
|
||||
if ($dataDirectory !== null) foreach (['Country.mmdb', 'geoip.metadb'] as $file) symlink($dataDirectory.'/'.$file, $this->dir.'/'.$file);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
@ -29,11 +37,13 @@ final class NativeValidator
|
||||
{
|
||||
$binary = $format === 'singbox' ? $this->singbox : $this->mihomo;
|
||||
if (!is_executable($binary)) throw new RuntimeException('native_validator_not_executable:'.$format);
|
||||
$remaining = 240 - (microtime(true) - $this->startedAt);
|
||||
if ($remaining <= 0) throw new RuntimeException('native_validation_batch_timeout');
|
||||
$file = $this->dir.'/'.bin2hex(random_bytes(6)).($format === 'clash' ? '.yaml' : '.json');
|
||||
file_put_contents($file, $content); chmod($file, 0600);
|
||||
$command = $format === 'singbox' ? [$binary, 'check', '-c', $file] : [$binary, '-t', '-d', $this->dir, '-f', $file];
|
||||
$process = new Process($command);
|
||||
$process->setTimeout(45);
|
||||
$process->setTimeout(min(45, $remaining));
|
||||
try {
|
||||
$process->run();
|
||||
$this->checks++;
|
||||
@ -65,8 +75,7 @@ final class NativeValidator
|
||||
$chunks = array_chunk($nodes, (int) ceil(count($nodes) / 2), true);
|
||||
$good = [];
|
||||
foreach ($chunks as $chunk) $good += $this->partition($format, $chunk, $render);
|
||||
// A final whole-file check also catches cross-node constraints.
|
||||
if (!$this->check($format, $render($good))) throw new RuntimeException('combined_config_invalid:'.$format);
|
||||
// Builder validates the final complete config once, including cross-node constraints.
|
||||
return $good;
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ final class Pipeline
|
||||
if (!$sources || !$sites) throw new RuntimeException('no_enabled_sources_or_sites');
|
||||
$sites[] = ['code' => '_admin', 'clients' => ['v2ray', 'clash', 'singbox']];
|
||||
$clashTemplate = \Symfony\Component\Yaml\Yaml::parseFile(public_path('freenode/template/clash.txt'));
|
||||
$validator = new NativeValidator(config('freenode.singbox_binary'), config('freenode.mihomo_binary'), $clashTemplate);
|
||||
$validator = new NativeValidator(config('freenode.singbox_binary'), config('freenode.mihomo_binary'), $clashTemplate, config('freenode.geoip_directory'));
|
||||
$result = (new Builder())->build($sources, $sites, $validator);
|
||||
return self::store()->stage($result, date('Y-m-d'), (float) config('freenode.minimum_ratio', 0.5));
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ return [
|
||||
'minimum_ratio' => (float) env('FREENODE_MINIMUM_RATIO', 0.5),
|
||||
'singbox_binary' => env('FREENODE_SINGBOX_BINARY', '/opt/master01-freenode/bin/sing-box'),
|
||||
'mihomo_binary' => env('FREENODE_MIHOMO_BINARY', '/opt/master01-freenode/bin/mihomo'),
|
||||
'geoip_directory' => env('FREENODE_GEOIP_DIRECTORY', '/opt/master01-freenode/data'),
|
||||
|
||||
'speed_min' => (int) env('freenode_speed_min', 0),
|
||||
'speed_max' => (int) env('freenode_speed_max', 0),
|
||||
|
||||
@ -12,6 +12,8 @@ import urllib.request
|
||||
ASSETS = [
|
||||
('sing-box', 'https://github.com/SagerNet/sing-box/releases/download/v1.14.3/sing-box-1.14.3-linux-amd64.tar.gz', 'e2bdf179c15a3652955dc44867e33ca0965c9c9b91b34267dcc5a5d639a5feee'),
|
||||
('mihomo', 'https://github.com/MetaCubeX/mihomo/releases/download/v1.19.32/mihomo-linux-amd64-v1-v1.19.32.gz', '306f81e723e60ce6b828899a6fe83e1d00e9ecefb2dc8d4d849312a5bc00efdc'),
|
||||
('Country.mmdb', 'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/country.mmdb', '64809d264be87ecfb7ac3ad19b7678893b6275038a7d4bf9eec72464a6c4f9fe'),
|
||||
('geoip.metadb', 'https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb', 'ec6701e428b7efe610fe40b187a69fb03324892a56792b033adc080d2b6c273e'),
|
||||
]
|
||||
|
||||
def main():
|
||||
@ -20,12 +22,19 @@ def main():
|
||||
root = Path('/opt/master01-freenode/bin')
|
||||
root.mkdir(mode=0o755, parents=True, exist_ok=True)
|
||||
for name, url, expected in ASSETS:
|
||||
if name in ('Country.mmdb', 'geoip.metadb'):
|
||||
root = Path('/opt/master01-freenode/data')
|
||||
root.mkdir(mode=0o755, parents=True, exist_ok=True)
|
||||
else:
|
||||
root = Path('/opt/master01-freenode/bin')
|
||||
request = urllib.request.Request(url, headers={'User-Agent': 'master01-freenode-validator'})
|
||||
with urllib.request.urlopen(request, timeout=60) as response:
|
||||
raw = response.read()
|
||||
if hashlib.sha256(raw).hexdigest() != expected:
|
||||
raise RuntimeError('Official archive checksum mismatch: ' + name)
|
||||
if name == 'sing-box':
|
||||
if name in ('Country.mmdb', 'geoip.metadb'):
|
||||
binary = raw
|
||||
elif name == 'sing-box':
|
||||
with tarfile.open(fileobj=io.BytesIO(raw), mode='r:gz') as archive:
|
||||
member = next(m for m in archive.getmembers() if m.isfile() and m.name.endswith('/sing-box'))
|
||||
binary = archive.extractfile(member).read()
|
||||
@ -33,7 +42,7 @@ def main():
|
||||
binary = gzip.decompress(raw)
|
||||
temp = root / ('.' + name + '.tmp')
|
||||
temp.write_bytes(binary)
|
||||
temp.chmod(0o755)
|
||||
temp.chmod(0o644 if name in ('Country.mmdb', 'geoip.metadb') else 0o755)
|
||||
os.replace(temp, root / name)
|
||||
print(name + ': archive verified, validator installed', flush=True)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user