79 lines
2.9 KiB
Python
79 lines
2.9 KiB
Python
import hashlib
|
|
import json
|
|
import os
|
|
import shutil
|
|
import stat
|
|
import subprocess
|
|
import tempfile
|
|
|
|
BASE = '/www/backup/txiaw-mysql-fix-20260831'
|
|
STAGE = BASE + '/stage'
|
|
WEB = '/www/wwwroot/www.txiaw.com'
|
|
NGINX = '/www/server/panel/vhost/nginx'
|
|
BIN = '/www/server/nginx/sbin/nginx'
|
|
PHP = '/www/server/php/56/bin/php'
|
|
|
|
def run(args):
|
|
subprocess.run(args, check=True, timeout=20)
|
|
|
|
def atomic_copy(src, dst, metadata=None):
|
|
info = metadata or os.stat(src)
|
|
fd, tmp = tempfile.mkstemp(prefix='.txiaw-repair-', suffix='.tmp', dir=os.path.dirname(dst))
|
|
try:
|
|
with os.fdopen(fd, 'wb') as target, open(src, 'rb') as source:
|
|
shutil.copyfileobj(source, target)
|
|
target.flush()
|
|
os.fsync(target.fileno())
|
|
os.chmod(tmp, stat.S_IMODE(info.st_mode))
|
|
os.chown(tmp, info.st_uid, info.st_gid)
|
|
os.replace(tmp, dst)
|
|
finally:
|
|
if os.path.exists(tmp):
|
|
os.unlink(tmp)
|
|
|
|
manifest = json.load(open(STAGE + '/baseline.json'))
|
|
for item in manifest:
|
|
data = open(item['remote'], 'rb').read()
|
|
if hashlib.sha256(data).hexdigest() != item['sha256']:
|
|
raise RuntimeError('Source changed since inspection: ' + item['remote'])
|
|
shutil.copy2(item['remote'], BASE + '/originals/' + item['backup'])
|
|
os.chmod(BASE + '/originals/' + item['backup'], 0o600)
|
|
|
|
for name in ['HomeRequestProtection.php', 'index.php', 'test_home_guard.php']:
|
|
run([PHP, '-l', STAGE + '/' + name])
|
|
run([PHP, STAGE + '/test_home_guard.php'])
|
|
|
|
changes = [
|
|
('HomeRequestProtection.php', WEB + '/Lib/HomeRequestProtection.php', None),
|
|
('index.php', WEB + '/index.php', 'original-index.php'),
|
|
('txiaw-home-protection-http.inc', NGINX + '/txiaw-home-protection-http.inc', None),
|
|
('txiaw-home-protection-server.inc', NGINX + '/txiaw-home-protection-server.inc', None),
|
|
('www.txiaw.com.conf', NGINX + '/www.txiaw.com.conf', 'original-www.txiaw.com.conf'),
|
|
]
|
|
for source, target, backup in changes:
|
|
if backup is None and os.path.exists(target):
|
|
raise RuntimeError('Refusing to overwrite unexpected file: ' + target)
|
|
|
|
installed = []
|
|
metadata = {}
|
|
try:
|
|
for source, target, backup in changes:
|
|
metadata[target] = os.stat(target) if backup else None
|
|
if not backup:
|
|
os.chmod(STAGE + '/' + source, 0o644)
|
|
atomic_copy(STAGE + '/' + source, target, metadata[target])
|
|
installed.append((target, backup))
|
|
run([BIN, '-t'])
|
|
run([BIN, '-s', 'reload'])
|
|
except Exception:
|
|
for target, backup in reversed(installed):
|
|
if backup:
|
|
atomic_copy(BASE + '/originals/' + backup, target, metadata[target])
|
|
else:
|
|
os.unlink(target)
|
|
run([BIN, '-t'])
|
|
raise
|
|
|
|
json.dump({'files': [p for p, _ in installed], 'status': 'installed'}, open(BASE + '/guard-installed.json', 'w'), indent=2)
|
|
print('GUARD_DEPLOYED; PHP guard passed; nginx configuration passed and gracefully reloaded.', flush=True)
|