Initial YakPanel commit
This commit is contained in:
134
script/BT-FirewallServices.py
Normal file
134
script/BT-FirewallServices.py
Normal file
@@ -0,0 +1,134 @@
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# yakpanel
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2014-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
|
||||
|
||||
|
||||
def run_cmd(cmd):
|
||||
try:
|
||||
subprocess.run(cmd, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
def load_iptables():
|
||||
"""
|
||||
恢复 iptables 规则
|
||||
"""
|
||||
if run_cmd("iptables -C INPUT -j IN_BT"):
|
||||
print("iptables existed")
|
||||
else:
|
||||
if run_cmd("iptables-restore --noflush < /www/server/panel/data/iptablesdata"):
|
||||
print("iptables restored")
|
||||
|
||||
|
||||
def load_ipset():
|
||||
"""
|
||||
恢复 ipset 规则
|
||||
"""
|
||||
if run_cmd("ipset restore < /www/server/panel/data/ipsetdata"):
|
||||
print("ipset restored")
|
||||
else:
|
||||
print("ipset existed")
|
||||
|
||||
|
||||
def save_iptables():
|
||||
"""
|
||||
保存 iptables 规则
|
||||
"""
|
||||
if run_cmd("iptables -C INPUT -j IN_BT"):
|
||||
if run_cmd(
|
||||
"iptables-save | grep -E 'IN_BT|OUT_BT|FORWARD_BT|^\*|^COMMIT' | sed 's/^-A INPUT/-I INPUT/; s/^-A OUTPUT/-I OUTPUT/; s/^-A PREROUTING/-I PREROUTING/' > /www/server/panel/data/iptablesdata"):
|
||||
print("iptables saved")
|
||||
|
||||
|
||||
def save_ipset():
|
||||
"""
|
||||
保存 ipset 规则
|
||||
"""
|
||||
if run_cmd("ipset save | grep -E '_bt_' > /www/server/panel/data/ipsetdata"):
|
||||
print("ipset saved")
|
||||
|
||||
|
||||
def dbus_listener():
|
||||
if not os.path.exists("/sbin/firewalld"):
|
||||
print("is not Firewalld")
|
||||
return
|
||||
|
||||
cmd = [
|
||||
"dbus-monitor",
|
||||
"--system",
|
||||
"type='signal',path='/org/fedoraproject/FirewallD1',interface='org.fedoraproject.FirewallD1',member='Reloaded'",
|
||||
"type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.fedoraproject.FirewallD1',arg1=''"
|
||||
]
|
||||
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
|
||||
while True:
|
||||
line = process.stdout.readline().strip()
|
||||
|
||||
if not line:
|
||||
break
|
||||
if "signal" in line:
|
||||
if "member=Reloaded" in line:
|
||||
print("firewalld reload...")
|
||||
load_iptables()
|
||||
elif "member=NameOwnerChanged" in line:
|
||||
print("firewalld restart...")
|
||||
threading.Timer(3, load_iptables).start()
|
||||
|
||||
|
||||
def main():
|
||||
import time
|
||||
if len(sys.argv) < 2:
|
||||
print("command:start|reload|stop|save")
|
||||
sys.exit(1)
|
||||
|
||||
command = sys.argv[1]
|
||||
if command == "start":
|
||||
load_ipset()
|
||||
load_iptables()
|
||||
listener_thread = threading.Thread(target=dbus_listener)
|
||||
listener_thread.daemon = True
|
||||
listener_thread.start()
|
||||
while True:
|
||||
time.sleep(1)
|
||||
elif command == "reload":
|
||||
save_ipset()
|
||||
save_iptables()
|
||||
load_ipset()
|
||||
load_iptables()
|
||||
elif command == "stop":
|
||||
save_ipset()
|
||||
save_iptables()
|
||||
elif command == "save":
|
||||
save_ipset()
|
||||
save_iptables()
|
||||
elif command == "saveiptables":
|
||||
save_iptables()
|
||||
elif command == "saveipset":
|
||||
save_ipset()
|
||||
elif command == "loadiptables":
|
||||
load_iptables()
|
||||
elif command == "loadipset":
|
||||
load_ipset()
|
||||
elif command == "reloadiptables":
|
||||
save_iptables()
|
||||
load_iptables()
|
||||
elif command == "reloadipset":
|
||||
save_ipset()
|
||||
load_ipset()
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
26
script/GetOS.sh
Normal file
26
script/GetOS.sh
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
if grep -Eqi "CentOS" /etc/issue || grep -Eq "CentOS" /etc/*-release; then
|
||||
OSNAME='CentOS'
|
||||
elif grep -Eqi "Red Hat Enterprise Linux Server" /etc/issue || grep -Eq "Red Hat Enterprise Linux Server" /etc/*-release; then
|
||||
OSNAME='RHEL'
|
||||
elif grep -Eqi "Aliyun" /etc/issue || grep -Eq "Aliyun" /etc/*-release; then
|
||||
OSNAME='Aliyun'
|
||||
elif grep -Eqi "Fedora" /etc/issue || grep -Eq "Fedora" /etc/*-release; then
|
||||
OSNAME='Fedora'
|
||||
elif grep -Eqi "Amazon Linux AMI" /etc/issue || grep -Eq "Amazon Linux AMI" /etc/*-release; then
|
||||
OSNAME='Amazon'
|
||||
elif grep -Eqi "Debian" /etc/issue || grep -Eq "Debian" /etc/*-release; then
|
||||
OSNAME='Debian'
|
||||
elif grep -Eqi "Ubuntu" /etc/issue || grep -Eq "Ubuntu" /etc/*-release; then
|
||||
OSNAME='Ubuntu'
|
||||
elif grep -Eqi "Raspbian" /etc/issue || grep -Eq "Raspbian" /etc/*-release; then
|
||||
OSNAME='Raspbian'
|
||||
elif grep -Eqi "Deepin" /etc/issue || grep -Eq "Deepin" /etc/*-release; then
|
||||
OSNAME='Deepin'
|
||||
else
|
||||
OSNAME='unknow'
|
||||
fi
|
||||
|
||||
echo "$OSNAME" > /www/server/panel/data/osname.pl
|
||||
BIN
script/Werkzeug-2.2.3-py3-none-any.whl
Normal file
BIN
script/Werkzeug-2.2.3-py3-none-any.whl
Normal file
Binary file not shown.
BIN
script/__pycache__/sync_time.cpython-314.pyc
Normal file
BIN
script/__pycache__/sync_time.cpython-314.pyc
Normal file
Binary file not shown.
943
script/auto_apply_ip_ssl.py
Normal file
943
script/auto_apply_ip_ssl.py
Normal file
@@ -0,0 +1,943 @@
|
||||
import base64
|
||||
import binascii
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import fcntl
|
||||
import re
|
||||
import shutil
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import datetime
|
||||
from pathlib import Path
|
||||
|
||||
import requests
|
||||
|
||||
APACHE_CONF_DIRS = [
|
||||
"/www/server/panel/vhost/apache"
|
||||
]
|
||||
|
||||
def is_ipv4(ip):
|
||||
'''
|
||||
@name 是否是IPV4地址
|
||||
@author hwliang
|
||||
@param ip<string> IP地址
|
||||
@return True/False
|
||||
'''
|
||||
# 验证基本格式
|
||||
if not re.match(r"^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$", ip):
|
||||
return False
|
||||
|
||||
# 验证每个段是否在合理范围
|
||||
try:
|
||||
socket.inet_pton(socket.AF_INET, ip)
|
||||
except AttributeError:
|
||||
try:
|
||||
socket.inet_aton(ip)
|
||||
except socket.error:
|
||||
return False
|
||||
except socket.error:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def is_ipv6(ip):
|
||||
'''
|
||||
@name 是否为IPv6地址
|
||||
@author hwliang
|
||||
@param ip<string> 地址
|
||||
@return True/False
|
||||
'''
|
||||
# 验证基本格式
|
||||
if not re.match(r"^[\w:]+$", ip):
|
||||
return False
|
||||
|
||||
# 验证IPv6地址
|
||||
try:
|
||||
socket.inet_pton(socket.AF_INET6, ip)
|
||||
except socket.error:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def check_ip(ip):
|
||||
return is_ipv4(ip) or is_ipv6(ip)
|
||||
|
||||
def find_apache_conf_files(keyword):
|
||||
"""查找 Apache 主配置和 vhost 文件"""
|
||||
files = set()
|
||||
for base in APACHE_CONF_DIRS:
|
||||
base_path = Path(base)
|
||||
if not base_path.exists():
|
||||
continue
|
||||
for f in base_path.rglob("*.conf"):
|
||||
with open(f, "r") as file:
|
||||
content = file.read()
|
||||
# 检查是否包含 ServerName 或 ServerAlias 指令
|
||||
if keyword in content:
|
||||
files.add(str(f))
|
||||
return list(files)
|
||||
|
||||
|
||||
def insert_location_into_vhost(file_path, keyword, verify_file):
|
||||
LOCATION_BLOCK = [
|
||||
" <Location /.well-known/acme-challenge/{}>\n".format(verify_file),
|
||||
" Require all granted\n",
|
||||
" Header set Content-Type \"text/plain\"\n",
|
||||
" </Location>\n",
|
||||
" Alias /.well-known/acme-challenge/{} /tmp/{}\n".format(verify_file, verify_file),
|
||||
]
|
||||
|
||||
path = Path(file_path)
|
||||
backup = path.with_suffix(path.suffix + ".bak")
|
||||
shutil.copy(path, backup)
|
||||
|
||||
with open(path, "r") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
new_lines = []
|
||||
in_vhost = False
|
||||
hit_vhost = False
|
||||
location_exists = False
|
||||
|
||||
for line in lines:
|
||||
stripped = line.strip()
|
||||
|
||||
if stripped.lower().startswith("<virtualhost"):
|
||||
in_vhost = True
|
||||
hit_vhost = False
|
||||
location_exists = False
|
||||
|
||||
if in_vhost:
|
||||
low = stripped.lower()
|
||||
if (low.startswith("servername") or low.startswith("serveralias")) and keyword in stripped:
|
||||
hit_vhost = True
|
||||
|
||||
if "<location /.well-known/acme-challenge/>" in low:
|
||||
location_exists = True
|
||||
|
||||
if stripped.lower() == "</virtualhost>":
|
||||
if hit_vhost and not location_exists:
|
||||
new_lines.extend(LOCATION_BLOCK)
|
||||
in_vhost = False
|
||||
|
||||
new_lines.append(line)
|
||||
|
||||
with open(path, "w") as f:
|
||||
f.writelines(new_lines)
|
||||
|
||||
return True
|
||||
|
||||
def find_nginx_files_by_servername(keyword):
|
||||
"""通过 nginx -T 找到包含 server_name 的配置文件"""
|
||||
result = subprocess.run(
|
||||
["nginx", "-T"],
|
||||
stderr=subprocess.STDOUT,
|
||||
stdout=subprocess.PIPE,
|
||||
text=True,
|
||||
check=True
|
||||
)
|
||||
|
||||
files = set()
|
||||
current_file = None
|
||||
|
||||
for line in result.stdout.splitlines():
|
||||
if line.startswith("# configuration file"):
|
||||
current_file = line.split()[-1].rstrip(":")
|
||||
if "server_name" in line and keyword in line:
|
||||
if current_file:
|
||||
files.add(current_file)
|
||||
|
||||
return list(files)
|
||||
|
||||
def insert_location_into_server(file_path, keyword, verify_file, verify_content):
|
||||
path = Path(file_path)
|
||||
backup = path.with_suffix(path.suffix + ".bak")
|
||||
|
||||
shutil.copy(path, backup)
|
||||
|
||||
with open(path, "r") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
new_lines = []
|
||||
brace_level = 0
|
||||
in_server = False
|
||||
hit_server = False
|
||||
location_exists = False
|
||||
|
||||
LOCATION_BLOCK = [
|
||||
" location = /.well-known/acme-challenge/{} {{\n".format(verify_file),
|
||||
" default_type text/plain;\n",
|
||||
" return 200 \"{}\";\n".format(verify_content),
|
||||
" }\n"
|
||||
]
|
||||
|
||||
for i, line in enumerate(lines):
|
||||
stripped = line.strip()
|
||||
|
||||
# server 开始
|
||||
if stripped.startswith("server"):
|
||||
in_server = True
|
||||
hit_server = False
|
||||
location_exists = False
|
||||
|
||||
if in_server:
|
||||
brace_level += line.count("{")
|
||||
brace_level -= line.count("}")
|
||||
|
||||
if "server_name" in line and keyword in line:
|
||||
hit_server = True
|
||||
|
||||
if "location /.well-known/acme-challenge/" in line:
|
||||
location_exists = True
|
||||
|
||||
# server 结束
|
||||
if brace_level == 0:
|
||||
if hit_server and not location_exists:
|
||||
new_lines.extend(LOCATION_BLOCK)
|
||||
in_server = False
|
||||
|
||||
new_lines.append(line)
|
||||
|
||||
with open(path, "w") as f:
|
||||
f.writelines(new_lines)
|
||||
|
||||
return True
|
||||
|
||||
class AutoApplyIPSSL:
|
||||
# 请求到ACME接口
|
||||
def __init__(self):
|
||||
self._wait_time = 5
|
||||
self._max_check_num = 15
|
||||
self._url = 'https://acme-v02.api.letsencrypt.org/directory'
|
||||
self._bits = 2048
|
||||
self._conf_file_v2 = '/www/server/panel/config/letsencrypt_v2.json'
|
||||
self._apis = None
|
||||
self._replay_nonce = None
|
||||
self._config = self.read_config()
|
||||
|
||||
|
||||
# 取接口目录
|
||||
def get_apis(self):
|
||||
if not self._apis:
|
||||
# 尝试从配置文件中获取
|
||||
api_index = "Production"
|
||||
if not 'apis' in self._config:
|
||||
self._config['apis'] = {}
|
||||
if api_index in self._config['apis']:
|
||||
if 'expires' in self._config['apis'][api_index] and 'directory' in self._config['apis'][api_index]:
|
||||
if time.time() < self._config['apis'][api_index]['expires']:
|
||||
self._apis = self._config['apis'][api_index]['directory']
|
||||
return self._apis
|
||||
|
||||
# 尝试从云端获取
|
||||
res = requests.get(self._url)
|
||||
if not res.status_code in [200, 201]:
|
||||
result = res.json()
|
||||
if "type" in result:
|
||||
if result['type'] == 'urn:acme:error:serverInternal':
|
||||
raise Exception('Service is closed for maintenance or internal error occurred, check <a href="https://letsencrypt.status.io/" target="_blank" class="btlink">https://letsencrypt.status.io/</a> .')
|
||||
raise Exception(res.content)
|
||||
s_body = res.json()
|
||||
self._apis = {}
|
||||
self._apis['newAccount'] = s_body['newAccount']
|
||||
self._apis['newNonce'] = s_body['newNonce']
|
||||
self._apis['newOrder'] = s_body['newOrder']
|
||||
self._apis['revokeCert'] = s_body['revokeCert']
|
||||
self._apis['keyChange'] = s_body['keyChange']
|
||||
|
||||
# 保存到配置文件
|
||||
self._config['apis'][api_index] = {}
|
||||
self._config['apis'][api_index]['directory'] = self._apis
|
||||
self._config['apis'][api_index]['expires'] = time.time() + \
|
||||
86400 # 24小时后过期
|
||||
self.save_config()
|
||||
return self._apis
|
||||
|
||||
def acme_request(self, url, payload):
|
||||
headers = {}
|
||||
payload = self.stringfy_items(payload)
|
||||
|
||||
if payload == "":
|
||||
payload64 = payload
|
||||
else:
|
||||
payload64 = self.calculate_safe_base64(json.dumps(payload))
|
||||
protected = self.get_acme_header(url)
|
||||
protected64 = self.calculate_safe_base64(json.dumps(protected))
|
||||
signature = self.sign_message(
|
||||
message="{0}.{1}".format(protected64, payload64)) # bytes
|
||||
signature64 = self.calculate_safe_base64(signature) # str
|
||||
data = json.dumps(
|
||||
{"protected": protected64, "payload": payload64,
|
||||
"signature": signature64}
|
||||
)
|
||||
headers.update({"Content-Type": "application/jose+json"})
|
||||
response = requests.post(url, data=data.encode("utf8"), headers=headers)
|
||||
# 更新随机数
|
||||
self.update_replay_nonce(response)
|
||||
return response
|
||||
|
||||
# 更新随机数
|
||||
def update_replay_nonce(self, res):
|
||||
replay_nonce = res.headers.get('Replay-Nonce')
|
||||
if replay_nonce:
|
||||
self._replay_nonce = replay_nonce
|
||||
|
||||
def stringfy_items(self, payload):
|
||||
if isinstance(payload, str):
|
||||
return payload
|
||||
|
||||
for k, v in payload.items():
|
||||
if isinstance(k, bytes):
|
||||
k = k.decode("utf-8")
|
||||
if isinstance(v, bytes):
|
||||
v = v.decode("utf-8")
|
||||
payload[k] = v
|
||||
return payload
|
||||
|
||||
# 转为无填充的Base64
|
||||
def calculate_safe_base64(self, un_encoded_data):
|
||||
if sys.version_info[0] == 3:
|
||||
if isinstance(un_encoded_data, str):
|
||||
un_encoded_data = un_encoded_data.encode("utf8")
|
||||
r = base64.urlsafe_b64encode(un_encoded_data).rstrip(b"=")
|
||||
return r.decode("utf8")
|
||||
|
||||
# 获请ACME请求头
|
||||
def get_acme_header(self, url):
|
||||
header = {"alg": "RS256", "nonce": self.get_nonce(), "url": url}
|
||||
if url in [self._apis['newAccount'], 'GET_THUMBPRINT']:
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
private_key = serialization.load_pem_private_key(
|
||||
self.get_account_key().encode(),
|
||||
password=None,
|
||||
backend=default_backend(),
|
||||
)
|
||||
public_key_public_numbers = private_key.public_key().public_numbers()
|
||||
|
||||
exponent = "{0:x}".format(public_key_public_numbers.e)
|
||||
exponent = "0{0}".format(exponent) if len(
|
||||
exponent) % 2 else exponent
|
||||
modulus = "{0:x}".format(public_key_public_numbers.n)
|
||||
jwk = {
|
||||
"kty": "RSA",
|
||||
"e": self.calculate_safe_base64(binascii.unhexlify(exponent)),
|
||||
"n": self.calculate_safe_base64(binascii.unhexlify(modulus)),
|
||||
}
|
||||
header["jwk"] = jwk
|
||||
else:
|
||||
header["kid"] = self.get_kid()
|
||||
return header
|
||||
|
||||
def get_nonce(self, force=False):
|
||||
# 如果没有保存上一次的随机数或force=True时则重新获取新的随机数
|
||||
if not self._replay_nonce or force:
|
||||
response = requests.get(
|
||||
self._apis['newNonce'],
|
||||
)
|
||||
self._replay_nonce = response.headers["Replay-Nonce"]
|
||||
return self._replay_nonce
|
||||
|
||||
def analysis_private_key(self, key_pem, password=None):
|
||||
"""
|
||||
解析私钥
|
||||
:param key_pem: 私钥内容
|
||||
:param password: 私钥密码
|
||||
:return: 私钥对象
|
||||
"""
|
||||
try:
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
private_key = serialization.load_pem_private_key(
|
||||
key_pem.encode(),
|
||||
password=password,
|
||||
backend=default_backend()
|
||||
)
|
||||
return private_key
|
||||
except:
|
||||
return None
|
||||
|
||||
def sign_message(self, message):
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.asymmetric import padding
|
||||
|
||||
pk = self.analysis_private_key(self.get_account_key())
|
||||
return pk.sign(message.encode("utf8"), padding.PKCS1v15(), hashes.SHA256())
|
||||
|
||||
# 获用户取密钥对
|
||||
def get_account_key(self):
|
||||
if not 'account' in self._config:
|
||||
self._config['account'] = {}
|
||||
k = "Production"
|
||||
if not k in self._config['account']:
|
||||
self._config['account'][k] = {}
|
||||
|
||||
if not 'key' in self._config['account'][k]:
|
||||
self._config['account'][k]['key'] = self.create_key()
|
||||
if type(self._config['account'][k]['key']) == bytes:
|
||||
self._config['account'][k]['key'] = self._config['account'][k]['key'].decode()
|
||||
self.save_config()
|
||||
return self._config['account'][k]['key']
|
||||
|
||||
def create_key(self, key_type='RSA'):
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa, ec, ed25519
|
||||
|
||||
if key_type == 'RSA':
|
||||
private_key = rsa.generate_private_key(
|
||||
public_exponent=65537,
|
||||
key_size=self._bits
|
||||
)
|
||||
elif key_type == 'EC':
|
||||
private_key = ec.generate_private_key(ec.SECP256R1())
|
||||
elif key_type == 'ED25519':
|
||||
private_key = ed25519.Ed25519PrivateKey.generate()
|
||||
else:
|
||||
raise ValueError(f"Unsupported key type: {key_type}")
|
||||
|
||||
private_key_pem = private_key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.PKCS8,
|
||||
encryption_algorithm=serialization.NoEncryption()
|
||||
)
|
||||
return private_key_pem
|
||||
|
||||
def get_kid(self, force=False):
|
||||
#如果配置文件中不存在kid或force = True时则重新注册新的acme帐户
|
||||
if not 'account' in self._config:
|
||||
self._config['account'] = {}
|
||||
k = "Production"
|
||||
if not k in self._config['account']:
|
||||
self._config['account'][k] = {}
|
||||
|
||||
if not 'kid' in self._config['account'][k]:
|
||||
self._config['account'][k]['kid'] = self.register()
|
||||
self.save_config()
|
||||
time.sleep(3)
|
||||
self._config = self.read_config()
|
||||
return self._config['account'][k]['kid']
|
||||
|
||||
# 读配置文件
|
||||
def read_config(self):
|
||||
if not os.path.exists(self._conf_file_v2):
|
||||
self._config = {'orders': {}, 'account': {}, 'apis': {}, 'email': None}
|
||||
self.save_config()
|
||||
return self._config
|
||||
with open(self._conf_file_v2, 'r') as f:
|
||||
fcntl.flock(f, fcntl.LOCK_SH) # 加锁
|
||||
tmp_config = f.read()
|
||||
fcntl.flock(f, fcntl.LOCK_UN) # 解锁
|
||||
f.close()
|
||||
if not tmp_config:
|
||||
return self._config
|
||||
try:
|
||||
self._config = json.loads(tmp_config)
|
||||
except:
|
||||
self.save_config()
|
||||
return self._config
|
||||
return self._config
|
||||
|
||||
# 写配置文件
|
||||
def save_config(self):
|
||||
fp = open(self._conf_file_v2, 'w+')
|
||||
fcntl.flock(fp, fcntl.LOCK_EX) # 加锁
|
||||
fp.write(json.dumps(self._config))
|
||||
fcntl.flock(fp, fcntl.LOCK_UN) # 解锁
|
||||
fp.close()
|
||||
return True
|
||||
|
||||
# 注册acme帐户
|
||||
def register(self, existing=False):
|
||||
if not 'email' in self._config:
|
||||
self._config['email'] = 'demo@yakpanel.com'
|
||||
if existing:
|
||||
payload = {"onlyReturnExisting": True}
|
||||
elif self._config['email']:
|
||||
payload = {
|
||||
"termsOfServiceAgreed": True,
|
||||
"contact": ["mailto:{0}".format(self._config['email'])],
|
||||
}
|
||||
else:
|
||||
payload = {"termsOfServiceAgreed": True}
|
||||
|
||||
res = self.acme_request(url=self._apis['newAccount'], payload=payload)
|
||||
|
||||
if res.status_code not in [201, 200, 409]:
|
||||
raise Exception("Failed to register ACME account: {}".format(res.json()))
|
||||
kid = res.headers["Location"]
|
||||
return kid
|
||||
|
||||
def create_csr(self, ips):
|
||||
from cryptography import x509
|
||||
from cryptography.x509.oid import NameOID
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
import ipaddress
|
||||
|
||||
|
||||
# 生成私钥
|
||||
pk = self.create_key()
|
||||
private_key = serialization.load_pem_private_key(pk, password=None)
|
||||
|
||||
# IP证书不需要CN
|
||||
csr_builder = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([]))
|
||||
# 添加 subjectAltName 扩展
|
||||
alt_names = [x509.IPAddress(ipaddress.ip_address(ip)) for ip in ips]
|
||||
|
||||
|
||||
csr_builder = csr_builder.add_extension(
|
||||
x509.SubjectAlternativeName(alt_names),
|
||||
critical=False
|
||||
)
|
||||
|
||||
# 签署 CSR
|
||||
csr = csr_builder.sign(private_key, hashes.SHA256())
|
||||
|
||||
# 返回 CSR (ASN1 格式)
|
||||
return csr.public_bytes(serialization.Encoding.DER), pk
|
||||
|
||||
def apply_ip_ssl(self, ips, email, webroot=None, mode=None, path=None):
|
||||
print("Starting to apply for Let's Encrypt IP SSL certificate...")
|
||||
print("Retrieving ACME API directory...")
|
||||
self.get_apis()
|
||||
self._config['email'] = email
|
||||
print("Creating order...")
|
||||
order_data = self.create_order(ips)
|
||||
if not order_data:
|
||||
raise Exception("Failed to create order!")
|
||||
print("Order created successfully")
|
||||
print("Performing domain verification...")
|
||||
try:
|
||||
self.get_and_set_authorizations(order_data, webroot, mode, ips)
|
||||
except Exception as e:
|
||||
raise Exception("Domain verification failed! {}".format(e))
|
||||
# 完成订单
|
||||
print("Creating CSR...")
|
||||
csr, private_key = self.create_csr(ips)
|
||||
print("Sending CSR and completing order...")
|
||||
res = self.acme_request(order_data['finalize'], payload={
|
||||
"csr": self.calculate_safe_base64(csr)
|
||||
})
|
||||
if res.status_code not in [200, 201]:
|
||||
raise Exception("Failed to complete order! {}".format(res.json()))
|
||||
# 获取证书
|
||||
print("Retrieving certificate...")
|
||||
cert_url = res.json().get('certificate')
|
||||
if not cert_url:
|
||||
raise Exception("Failed to retrieve certificate URL!")
|
||||
cert_res = self.acme_request(cert_url, payload="")
|
||||
if cert_res.status_code not in [200, 201]:
|
||||
raise Exception("Failed to retrieve certificate! {}".format(cert_res.json()))
|
||||
print("Certificate retrieved successfully!")
|
||||
cert_pem = cert_res.content.decode()
|
||||
# 保存证书和私钥
|
||||
if not path:
|
||||
path = "/www/server/panel/ssl"
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
cert_path = os.path.join(path, 'certificate.pem')
|
||||
key_path = os.path.join(path, 'privateKey.pem')
|
||||
with open(cert_path, 'w') as f:
|
||||
f.write(cert_pem)
|
||||
with open(key_path, 'w') as f:
|
||||
f.write(private_key.decode())
|
||||
return cert_path, key_path
|
||||
|
||||
def create_order(self, ips):
|
||||
identifiers = []
|
||||
for ip in ips:
|
||||
identifiers.append({"type": "ip", "value": ip})
|
||||
payload = {"identifiers": identifiers, "profile": "shortlived"}
|
||||
print("Create order, domain name list:{}".format(','.join(ips)))
|
||||
res = self.acme_request(self._apis['newOrder'], payload)
|
||||
if not res.status_code in [201,200]: # 如果创建失败
|
||||
print("Failed to create order, attempting to fix error...")
|
||||
e_body = res.json()
|
||||
if 'type' in e_body:
|
||||
# 如果随机数失效
|
||||
if e_body['type'].find('error:badNonce') != -1:
|
||||
print("Nonce invalid, retrieving new nonce and retrying...")
|
||||
self.get_nonce(force=True)
|
||||
res = self.acme_request(self._apis['newOrder'], payload)
|
||||
# 如果帐户失效
|
||||
if e_body['detail'].find('KeyID header contained an invalid account URL') != -1:
|
||||
print("Account invalid, re-registering account and retrying...")
|
||||
k = "Production"
|
||||
del(self._config['account'][k])
|
||||
self.get_kid()
|
||||
self.get_nonce(force=True)
|
||||
res = self.acme_request(self._apis['newOrder'], payload)
|
||||
if not res.status_code in [201,200]:
|
||||
print(res.json())
|
||||
# 2025/12/25 yakpanel
|
||||
raise Exception(str(res.json()))
|
||||
# return {}
|
||||
return res.json()
|
||||
|
||||
# UTC时间转时间戳
|
||||
def utc_to_time(self, utc_string):
|
||||
try:
|
||||
utc_string = utc_string.split('.')[0]
|
||||
utc_date = datetime.datetime.strptime(
|
||||
utc_string, "%Y-%m-%dT%H:%M:%S")
|
||||
# 按北京时间返回
|
||||
return int(time.mktime(utc_date.timetuple())) + (3600 * 8)
|
||||
except:
|
||||
return int(time.time() + 86400 * 7)
|
||||
|
||||
def get_keyauthorization(self, token):
|
||||
acme_header_jwk_json = json.dumps(
|
||||
self.get_acme_header("GET_THUMBPRINT")["jwk"], sort_keys=True, separators=(",", ":")
|
||||
)
|
||||
acme_thumbprint = self.calculate_safe_base64(
|
||||
hashlib.sha256(acme_header_jwk_json.encode("utf8")).digest()
|
||||
)
|
||||
acme_keyauthorization = "{0}.{1}".format(token, acme_thumbprint)
|
||||
base64_of_acme_keyauthorization = self.calculate_safe_base64(
|
||||
hashlib.sha256(acme_keyauthorization.encode("utf8")).digest()
|
||||
)
|
||||
|
||||
return acme_keyauthorization, base64_of_acme_keyauthorization
|
||||
|
||||
# 获取并设置验证信息
|
||||
def get_and_set_authorizations(self, order_data, webroot=None, mode=None, ips=None):
|
||||
import os
|
||||
|
||||
if 'authorizations' not in order_data:
|
||||
raise Exception("Abnormal order data, missing authorization information!")
|
||||
for auth_url in order_data['authorizations']:
|
||||
res = self.acme_request(auth_url, payload="")
|
||||
if not res.status_code in [200, 201]:
|
||||
raise Exception("Failed to get authorization information! {}".format(res.json()))
|
||||
s_body = res.json()
|
||||
if 'status' in s_body:
|
||||
if s_body['status'] in ['invalid']:
|
||||
raise Exception("Invalid order, current order status is verification failed!")
|
||||
if s_body['status'] in ['valid']: # 跳过无需验证的域名
|
||||
continue
|
||||
for challenge in s_body['challenges']:
|
||||
if challenge['type'] == "http-01":
|
||||
break
|
||||
if challenge['type'] != "http-01":
|
||||
raise Exception("http-01 verification method not found, cannot continue applying for certificate!")
|
||||
# 检查是否需要验证
|
||||
check_auth_data = self.check_auth_status(challenge['url'])
|
||||
if check_auth_data.json()['status'] == 'invalid':
|
||||
raise Exception('Domain verification failed, please try applying again!')
|
||||
if check_auth_data.json()['status'] == 'valid':
|
||||
continue
|
||||
|
||||
acme_keyauthorization, auth_value = self.get_keyauthorization(
|
||||
challenge['token'])
|
||||
print(challenge)
|
||||
|
||||
if mode:
|
||||
if mode == 'standalone':
|
||||
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
||||
import threading
|
||||
import os
|
||||
|
||||
class ACMERequestHandler(SimpleHTTPRequestHandler):
|
||||
def log_message(self, format, *args):
|
||||
# 屏蔽默认的请求日志输出
|
||||
return
|
||||
|
||||
def do_GET(self):
|
||||
if self.path == '/.well-known/acme-challenge/{}'.format(challenge['token']):
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text/plain')
|
||||
self.end_headers()
|
||||
self.wfile.write(acme_keyauthorization.encode())
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
|
||||
server_address = ('', 80)
|
||||
httpd = HTTPServer(server_address, ACMERequestHandler)
|
||||
|
||||
def start_server():
|
||||
httpd.serve_forever()
|
||||
|
||||
server_thread = threading.Thread(target=start_server)
|
||||
server_thread.daemon = True
|
||||
server_thread.start()
|
||||
|
||||
|
||||
# 2025/12/26 yakpanel
|
||||
time.sleep(2) # 等待服务器启动
|
||||
try:
|
||||
# ========================= 校验服务 ==================================
|
||||
server_started = False
|
||||
challenge_local = f"http://127.0.0.1/.well-known/acme-challenge/{challenge['token']}"
|
||||
for i in range(10): # 最大尝试时间5秒
|
||||
try:
|
||||
# 临时检查80 打印
|
||||
with socket.create_connection(("127.0.0.1", 80), timeout=0.2):
|
||||
print("Temporary server started on port 80.")
|
||||
|
||||
response = requests.get(challenge_local, timeout=0.5)
|
||||
if response.status_code == 200 and response.text == acme_keyauthorization:
|
||||
print("Temporary server started and responding correctly challenge token on port 80.")
|
||||
server_started = True
|
||||
break
|
||||
else:
|
||||
print("Temporary server response incorrect, retrying...")
|
||||
time.sleep(0.5)
|
||||
except Exception:
|
||||
time.sleep(0.5)
|
||||
|
||||
if not server_started:
|
||||
# raise Exception("Failed to start temporary HTTP server on port 80 in 5 seconds.")
|
||||
print("Failed to start temporary HTTP server on port 80 in 5 seconds.")
|
||||
|
||||
# ========================= 通知ACME服务器进行验证 ===============================
|
||||
self.acme_request(challenge['url'], payload={"keyAuthorization": "{0}".format(acme_keyauthorization)})
|
||||
self.check_auth_status(challenge['url'], [
|
||||
'valid', 'invalid'])
|
||||
finally:
|
||||
httpd.shutdown()
|
||||
server_thread.join()
|
||||
elif mode == 'nginx':
|
||||
tmp_path = '/www/server/panel/vhost/nginx/tmp_apply_ip_ssl.conf'
|
||||
files = find_nginx_files_by_servername(ips[0])
|
||||
if not files:
|
||||
print("No related Nginx configuration files found, attempting to create temporary configuration file...")
|
||||
if not os.path.exists('/www/server/panel/vhost/nginx'):
|
||||
raise Exception("No Nginx configuration files found, and Nginx configuration directory does not exist!")
|
||||
# 如果没有找到相关配置文件,则创建一个临时配置文件
|
||||
with open(tmp_path, 'w') as f:
|
||||
f.write("""server
|
||||
{{
|
||||
listen 80;
|
||||
server_name {0};
|
||||
location /.well-known/acme-challenge/{1} {{
|
||||
default_type text/plain;
|
||||
return 200 "{2}";
|
||||
}}
|
||||
}}
|
||||
""".format(ips[0], challenge['token'], acme_keyauthorization))
|
||||
try:
|
||||
for file in files:
|
||||
print("Modifying Nginx configuration file: {}".format(file))
|
||||
insert_location_into_server(file, ips[0], verify_file=challenge['token'], verify_content=acme_keyauthorization)
|
||||
# 重新加载Nginx配置
|
||||
subprocess.run(["nginx", "-t"], check=True)
|
||||
subprocess.run(["nginx", "-s", "reload"], check=True)
|
||||
|
||||
# 通知ACME服务器进行验证
|
||||
self.acme_request(challenge['url'],
|
||||
payload={"keyAuthorization": "{0}".format(acme_keyauthorization)})
|
||||
self.check_auth_status(challenge['url'], [
|
||||
'valid', 'invalid'])
|
||||
finally:
|
||||
for file in files:
|
||||
print("Restoring Nginx configuration file: {}".format(file))
|
||||
# 恢复备份文件
|
||||
backup_file = file + ".bak"
|
||||
if os.path.exists(backup_file):
|
||||
shutil.move(backup_file, file)
|
||||
if not files:
|
||||
print("Deleting temporary Nginx configuration file...")
|
||||
# 删除临时配置文件
|
||||
os.remove(tmp_path)
|
||||
# 重新加载Nginx配置
|
||||
subprocess.run(["nginx", "-t"], check=True)
|
||||
subprocess.run(["nginx", "-s", "reload"], check=True)
|
||||
|
||||
elif mode == 'apache':
|
||||
tmp_path = '/www/server/panel/vhost/apache/tmp_apply_ip_ssl.conf'
|
||||
files = find_apache_conf_files(ips[0])
|
||||
if not files:
|
||||
print("No related Apache configuration files found, attempting to create temporary configuration file...")
|
||||
if not os.path.exists('/www/server/panel/vhost/apache'):
|
||||
raise Exception("No Apache configuration files found, and Apache configuration directory does not exist!")
|
||||
# 如果没有找到相关配置文件,则创建一个临时配置文件
|
||||
with open(tmp_path, 'w') as f:
|
||||
f.write("""<VirtualHost *:80>
|
||||
ServerName {0}
|
||||
<Location /.well-known/acme-challenge/{1}>
|
||||
Require all granted
|
||||
Header set Content-Type "text/plain"
|
||||
</Location>
|
||||
Alias /.well-known/acme-challenge/{1} /tmp/{1}
|
||||
</VirtualHost>
|
||||
""".format(ips[0], challenge['token']))
|
||||
try:
|
||||
for file in files:
|
||||
print("Modifying Apache configuration file: {}".format(file))
|
||||
insert_location_into_vhost(file, ips[0], verify_file=challenge['token'])
|
||||
# 写入验证文件
|
||||
with open('/tmp/{}'.format(challenge['token']), 'w') as f:
|
||||
f.write(acme_keyauthorization)
|
||||
# 重新加载Apache配置
|
||||
subprocess.run(["/etc/init.d/httpd", "reload"], check=True)
|
||||
|
||||
# 通知ACME服务器进行验证
|
||||
self.acme_request(challenge['url'],
|
||||
payload={"keyAuthorization": "{0}".format(acme_keyauthorization)})
|
||||
self.check_auth_status(challenge['url'], [
|
||||
'valid', 'invalid'])
|
||||
finally:
|
||||
for file in files:
|
||||
print("Restoring Apache configuration file: {}".format(file))
|
||||
# 恢复备份文件
|
||||
backup_file = file + ".bak"
|
||||
if os.path.exists(backup_file):
|
||||
shutil.move(backup_file, file)
|
||||
if not files:
|
||||
print("Deleting temporary Apache configuration file...")
|
||||
# 删除临时配置文件
|
||||
os.remove(tmp_path)
|
||||
# 重新加载Apache配置
|
||||
subprocess.run(["systemctl", "restart", "httpd"], check=True)
|
||||
else:
|
||||
# 使用webroot方式验证
|
||||
challenge_path = os.path.join(
|
||||
webroot, '.well-known', 'acme-challenge')
|
||||
if not os.path.exists(challenge_path):
|
||||
os.makedirs(challenge_path)
|
||||
file_path = os.path.join(challenge_path, challenge['token'])
|
||||
with open(file_path, 'w') as f:
|
||||
f.write(acme_keyauthorization)
|
||||
|
||||
try:
|
||||
# 通知ACME服务器进行验证
|
||||
self.acme_request(challenge['url'], payload={"keyAuthorization": "{0}".format(acme_keyauthorization)})
|
||||
self.check_auth_status(challenge['url'], [
|
||||
'valid', 'invalid'])
|
||||
finally:
|
||||
os.remove(file_path)
|
||||
|
||||
# 检查验证状态
|
||||
def check_auth_status(self, url, desired_status=None):
|
||||
desired_status = desired_status or ["pending", "valid", "invalid"]
|
||||
number_of_checks = 0
|
||||
authorization_status = "pending"
|
||||
while True:
|
||||
print("|- {} checking verification result...".format(number_of_checks + 1))
|
||||
if desired_status == ['valid', 'invalid']:
|
||||
time.sleep(self._wait_time)
|
||||
check_authorization_status_response = self.acme_request(url, "")
|
||||
a_auth = check_authorization_status_response.json()
|
||||
if not isinstance(a_auth, dict):
|
||||
continue
|
||||
authorization_status = a_auth["status"]
|
||||
number_of_checks += 1
|
||||
if authorization_status in desired_status:
|
||||
if authorization_status == "invalid":
|
||||
try:
|
||||
if 'error' in a_auth['challenges'][0]:
|
||||
ret_title = a_auth['challenges'][0]['error']['detail']
|
||||
elif 'error' in a_auth['challenges'][1]:
|
||||
ret_title = a_auth['challenges'][1]['error']['detail']
|
||||
elif 'error' in a_auth['challenges'][2]:
|
||||
ret_title = a_auth['challenges'][2]['error']['detail']
|
||||
else:
|
||||
ret_title = str(a_auth)
|
||||
except:
|
||||
ret_title = str(a_auth)
|
||||
raise StopIteration(
|
||||
"{0} >>>> {1}".format(
|
||||
ret_title,
|
||||
json.dumps(a_auth)
|
||||
)
|
||||
)
|
||||
break
|
||||
|
||||
if number_of_checks == self._max_check_num:
|
||||
raise StopIteration(
|
||||
"Error: Verification attempted {0} times. Maximum verification attempts: {1}. Verification interval: {2} seconds.".format(
|
||||
number_of_checks,
|
||||
self._max_check_num,
|
||||
self._wait_time
|
||||
)
|
||||
)
|
||||
print("|-Verification result: {}".format(authorization_status))
|
||||
return check_authorization_status_response
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
# 解析命令行参数
|
||||
parser = argparse.ArgumentParser(description='Auto-apply IP SSL certificate script')
|
||||
parser.add_argument('-ips', type=str, required=True, help='IP addresses to apply SSL certificate for', dest='ips')
|
||||
parser.add_argument('-email', type=str, required=False, help='Email for SSL certificate application', dest='email')
|
||||
parser.add_argument('-w', type=str, help='Website root directory', dest='webroot')
|
||||
parser.add_argument('--standalone', help='Apply certificate using standalone mode', dest='standalone', action='store_true')
|
||||
parser.add_argument('--nginx', help='Apply certificate using nginx mode', dest='nginx', action='store_true')
|
||||
parser.add_argument('--apache', help='Apply certificate using apache mode', dest='apache', action='store_true')
|
||||
parser.add_argument('-path', type=str, help='Certificate save path', dest='path')
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.standalone and not args.webroot and not args.nginx and not args.apache:
|
||||
print("No verification mode detected, will attempt to auto-select verification mode!")
|
||||
# 自动选择验证模式
|
||||
# 判断80端口是否被占用
|
||||
use_80 = False
|
||||
result = subprocess.run(
|
||||
["lsof", "-i:80"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True
|
||||
)
|
||||
if result.stdout:
|
||||
result = subprocess.run(
|
||||
["netstat", "-lntup", "|", "grep", "80"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True
|
||||
)
|
||||
if result.stdout:
|
||||
use_80 = True
|
||||
if use_80:
|
||||
print("It is detected that port 80 is occupied, try to use Nginx or Apache mode to verify...")
|
||||
# 检查是否安装Nginx
|
||||
if os.path.exists('/www/server/nginx/sbin/nginx'):
|
||||
args.nginx = True
|
||||
print("Selected Nginx mode for verification...")
|
||||
elif os.path.exists('/www/server/apache/bin/httpd'):
|
||||
args.apache = True
|
||||
print("Selected Apache mode for verification...")
|
||||
else:
|
||||
print("[ERROR] Nginx or Apache installation not detected, cannot use Nginx or Apache mode for verification! Please release port 80 and try again.")
|
||||
exit(1)
|
||||
else:
|
||||
args.standalone = True
|
||||
print("Port 80 not occupied, selected standalone mode for verification...")
|
||||
|
||||
if not args.email:
|
||||
# 使用默认邮箱
|
||||
email = "demo@yakpanel.com"
|
||||
else:
|
||||
email = args.email
|
||||
|
||||
ips = args.ips.split(',')
|
||||
# 先只支持单个IP申请
|
||||
if len(ips) > 1 and not args.standalone:
|
||||
print("[ERROR] Multiple IP SSL certificate application not supported in non-standalone mode!")
|
||||
exit(1)
|
||||
# 先只支持IPv4
|
||||
if not is_ipv4(ips[0]):
|
||||
print("[ERROR] Only IPv4 addresses are supported for SSL certificate application at this time!")
|
||||
exit(1)
|
||||
auto_ssl = AutoApplyIPSSL()
|
||||
mode = None
|
||||
if args.standalone:
|
||||
mode = 'standalone'
|
||||
elif args.nginx:
|
||||
mode = 'nginx'
|
||||
elif args.apache:
|
||||
mode = 'apache'
|
||||
try:
|
||||
cert_path, key_path = auto_ssl.apply_ip_ssl(ips, email, webroot=args.webroot, mode=mode, path=args.path)
|
||||
except Exception as e:
|
||||
# 2025/12/25 yakpanel
|
||||
print(f"[ERROR] Certificate application failed! Error details: {e}", file=sys.stderr)
|
||||
exit(1)
|
||||
exit(0)
|
||||
|
||||
|
||||
195
script/backup
Normal file
195
script/backup
Normal file
@@ -0,0 +1,195 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
#备份网站
|
||||
Backup_Site()
|
||||
{
|
||||
#准备必要信息
|
||||
startTime=`date +%s`
|
||||
myDate=`date +"%Y%m%d_%H%M%S"`
|
||||
zipName=${1}_${myDate}.zip
|
||||
backupDir=${backup_path}/site
|
||||
fileName=$backupDir/$zipName
|
||||
tmp=`$Sql_Exec -e "SELECT id,path FROM bt_sites WHERE name='$1'"`;
|
||||
pid=`echo $tmp|awk '{print $3}'`
|
||||
sitePath=`echo $tmp|awk '{print $4}'`
|
||||
|
||||
if [ "$pid" == '' ];then
|
||||
endDate=`date +"%Y-%m-%d %H:%M:%S"`
|
||||
log="网站[$1]不存在!"
|
||||
echo "★[$endDate] $log"
|
||||
echo '----------------------------------------------------------------------------'
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ ! -d $backupDir ];then
|
||||
mkdir -p $backupDir
|
||||
fi
|
||||
|
||||
#取目录名称
|
||||
oldIFS=$IFS
|
||||
IFS=/
|
||||
nameArr=($sitePath)
|
||||
pathName=${nameArr[@]: -1};
|
||||
IFS=$oldIFS
|
||||
|
||||
#压缩
|
||||
cd $sitePath
|
||||
cd ..
|
||||
zip -r $fileName $pathName > /dev/null
|
||||
|
||||
if [ ! -f $fileName ];then
|
||||
endDate=`date +"%Y-%m-%d %H:%M:%S"`
|
||||
log="网站[$1]备份失败!"
|
||||
echo "★[$endDate] $log"
|
||||
echo '----------------------------------------------------------------------------'
|
||||
exit
|
||||
fi
|
||||
|
||||
#记录日志
|
||||
endDate=`date +"%Y-%m-%d %H:%M:%S"`
|
||||
endTime=`date +%s`
|
||||
((outTime=($endTime-$startTime)))
|
||||
|
||||
log="网站[$1]备份成功,用时[$outTime]秒"
|
||||
$Sql_Exec -e "INSERT INTO bt_backup(type,name,pid,filename,addtime) VALUES(0,'$zipName',$pid,'$fileName','$endDate')"
|
||||
$Sql_Exec -e "INSERT INTO bt_logs(type,log,addtime) VALUES('Cron','$log','$endDate')"
|
||||
echo "★[$endDate] $log"
|
||||
echo "|---保留最新的[$2]份备份"
|
||||
echo "|---文件名:$fileName"
|
||||
|
||||
#清理多余备份
|
||||
tmp=`$Sql_Exec -e "SELECT COUNT(*) FROM bt_backup WHERE type=0 AND pid=$pid"`;
|
||||
count=`echo $tmp|awk '{print $2}'`
|
||||
((sum=($count-$2)))
|
||||
if [ $sum -gt 0 ];then
|
||||
str=`$Sql_Exec -e "SELECT filename FROM bt_backup WHERE type=0 AND pid=$pid ORDER BY id ASC LIMIT $sum"`
|
||||
str=`echo $str|sed "s#filename##"|sed "s# ##"`
|
||||
oldIFS=$IFS
|
||||
IFS=' '
|
||||
arr=($str)
|
||||
for s in ${arr[@]}
|
||||
do
|
||||
if [ -f $s ];then
|
||||
rm -f $s
|
||||
fi
|
||||
echo "|---已清理过期备份文件:$s"
|
||||
$Sql_Exec -e "DELETE FROM bt_backup WHERE filename='$s'" > /dev/null
|
||||
done
|
||||
IFS=$oldIFS
|
||||
fi
|
||||
|
||||
echo '----------------------------------------------------------------------------'
|
||||
}
|
||||
|
||||
#备份数据库
|
||||
Backup_Database()
|
||||
{
|
||||
#准备必要信息
|
||||
startTime=`date +%s`
|
||||
mysqlRoot=`echo $select |grep mysql_root|awk '{print $3}'`
|
||||
myDate=`date +"%Y%m%d_%H%M%S"`
|
||||
sqlName=${1}_${myDate}.sql
|
||||
zipName=${1}_${myDate}.zip
|
||||
backupDir=${backup_path}/database
|
||||
tmp=`$Sql_Exec -e "SELECT id FROM bt_databases WHERE name='$1'"`;
|
||||
pid=`echo $tmp|awk '{print $2}'`
|
||||
|
||||
if [ "$pid" == '' ];then
|
||||
endDate=`date +"%Y-%m-%d %H:%M:%S"`
|
||||
log="数据库[$1]不存在!"
|
||||
echo "★[$endDate] $log"
|
||||
echo '----------------------------------------------------------------------------'
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ ! -d $backupDir ];then
|
||||
mkdir -p $backupDir
|
||||
fi
|
||||
|
||||
|
||||
#sed -i "s###"
|
||||
isWrite=`cat /etc/my.cnf|grep 'user=root'`
|
||||
if [ "${isWrite}" == '' ];then
|
||||
echo -e "\n[mysqldump]\nuser=root\npassword=$mysqlRoot" >> /etc/my.cnf
|
||||
else
|
||||
sed -i "s#password=.*#password=$mysqlRoot#" /etc/my.cnf
|
||||
fi
|
||||
|
||||
#导出数据库
|
||||
cd $backupDir
|
||||
/www/server/mysql/bin/mysqldump -uroot -p$mysqlRoot -R $1 > $sqlName
|
||||
|
||||
sed -i "/\[mysqldump\]/d" /etc/my.cnf
|
||||
sed -i "/user=root/d" /etc/my.cnf
|
||||
sed -i "/password=.*/d" /etc/my.cnf
|
||||
sed -i "s/quick/\[mysqldump\]\nquick/" /etc/my.cnf
|
||||
|
||||
|
||||
if [ ! -f $sqlName ];then
|
||||
endDate=`date +"%Y-%m-%d %H:%M:%S"`
|
||||
log="数据库[$1]备份失败!"
|
||||
echo "★[$endDate] $log"
|
||||
echo '----------------------------------------------------------------------------'
|
||||
exit
|
||||
fi
|
||||
|
||||
#压缩
|
||||
zip -r $zipName $sqlName > /dev/null
|
||||
rm -f $sqlName
|
||||
|
||||
#记录日志
|
||||
endDate=`date +"%Y-%m-%d %H:%M:%S"`
|
||||
endTime=`date +%s`
|
||||
((outTime=($endTime-$startTime)))
|
||||
fileName=$backupDir/$zipName
|
||||
log="数据库[$1]备份成功,用时[$outTime]秒"
|
||||
$Sql_Exec -e "INSERT INTO bt_backup(type,name,pid,filename,addtime) VALUES(1,'$zipName',$pid,'$fileName','$endDate')" > /dev/null
|
||||
$Sql_Exec -e "INSERT INTO bt_logs(type,log,addtime) VALUES('Cron','$log','$endDate')" > /dev/null
|
||||
echo "★[$endDate] $log"
|
||||
echo "|---保留最新的[$2]份备份"
|
||||
echo "|---文件名:$fileName"
|
||||
|
||||
#清理多余备份
|
||||
tmp=`$Sql_Exec -e "SELECT COUNT(*) FROM bt_backup WHERE type=1 AND pid=$pid"`;
|
||||
count=`echo $tmp|awk '{print $2}'`
|
||||
((sum=($count-$2)))
|
||||
if [ $sum -gt 0 ];then
|
||||
str=`$Sql_Exec -e "SELECT filename FROM bt_backup WHERE type=1 AND pid=$pid ORDER BY id ASC LIMIT $sum"`
|
||||
str=`echo $str|sed "s#filename##"|sed "s# ##"`
|
||||
oldIFS=$IFS
|
||||
IFS=' '
|
||||
arr=($str)
|
||||
for s in ${arr[@]}
|
||||
do
|
||||
if [ -f $s ];then
|
||||
rm -f $s
|
||||
fi
|
||||
echo "|---已清理过期备份文件:$s"
|
||||
$Sql_Exec -e "DELETE FROM bt_backup WHERE filename='$s'" > /dev/null
|
||||
done
|
||||
IFS=$oldIFS
|
||||
fi
|
||||
echo '----------------------------------------------------------------------------'
|
||||
}
|
||||
|
||||
|
||||
#准备基础信息
|
||||
action=$1
|
||||
name=$2
|
||||
num=$3
|
||||
dbPwd=`cat /www/wwwroot/default/conf/sql.config.php |grep 'DB_PWD'|awk '{print $3}'|sed "s#'##g"|sed "s#,##g"`
|
||||
dbName='bt_default'
|
||||
Sql_Exec="/www/server/mysql/bin/mysql -u$dbName -p$dbPwd --default-character-set=utf8 -D $dbName"
|
||||
select=`$Sql_Exec -e "SELECT mysql_root,backup_path FROM bt_config WHERE id=1"`
|
||||
backup_path=`echo $select |grep backup_path|awk '{print $4}'`
|
||||
|
||||
case "${action}" in
|
||||
'site')
|
||||
Backup_Site $name $num
|
||||
;;
|
||||
'database')
|
||||
Backup_Database $name $num
|
||||
;;
|
||||
esac
|
||||
66
script/backup.py
Normal file
66
script/backup.py
Normal file
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/python
|
||||
#coding: utf-8
|
||||
#-----------------------------
|
||||
# YakPanel
|
||||
# 网站备份工具
|
||||
#-----------------------------
|
||||
|
||||
import sys,os
|
||||
os.chdir('/www/server/panel')
|
||||
if "./" not in sys.path: sys.path.insert(0, "./")
|
||||
if "class/" not in sys.path: sys.path.insert(0, "class/")
|
||||
if "class_v2/" not in sys.path: sys.path.insert(0, "class_v2/")
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
reload(sys) # noqa
|
||||
sys.setdefaultencoding('utf-8') # noqa
|
||||
import public,db,time
|
||||
from public.hook_import import hook_import
|
||||
hook_import()
|
||||
import class_v2.panel_backup_v2 as panelBackup
|
||||
# import panelBackup as panelBackup
|
||||
|
||||
class backupTools(panelBackup.backup):
|
||||
def backupSite(self, name, count, echo_id=None):
|
||||
self.backup_site(name, save=count, echo_id=echo_id)
|
||||
|
||||
def backupDatabase(self, name, count, echo_id=None):
|
||||
self.backup_database(name, save=count, echo_id=echo_id)
|
||||
|
||||
#备份指定目录
|
||||
def backupPath(self, path, count, echo_id=None):
|
||||
self.backup_path(path, save=count, echo_id=echo_id)
|
||||
|
||||
def backupSiteAll(self, save, echo_id=None):
|
||||
self.backup_site_all(save, echo_id=echo_id)
|
||||
|
||||
def backupDatabaseAll(self, save, echo_id=None):
|
||||
self.backup_database_all(save, echo_id=echo_id)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
backup = backupTools()
|
||||
type = sys.argv[1]
|
||||
echo_id = None
|
||||
if len(sys.argv) > 4: echo_id = sys.argv[4]
|
||||
if type == 'site':
|
||||
if sys.argv[2] == 'ALL':
|
||||
backup.backupSiteAll(sys.argv[3], echo_id)
|
||||
elif ',' in sys.argv[2]:
|
||||
for i in sys.argv[2].split(','):
|
||||
backup.backupSite(i, sys.argv[3], echo_id)
|
||||
else:
|
||||
backup = backupTools(cron_info = {'echo':sys.argv[4]})
|
||||
backup.backupSite(sys.argv[2], sys.argv[3], echo_id)
|
||||
elif type == 'path':
|
||||
backup = backupTools(cron_info = {'echo':sys.argv[4]})
|
||||
backup.backupPath(sys.argv[2], sys.argv[3], echo_id)
|
||||
elif type == 'database':
|
||||
if sys.argv[2] == 'ALL':
|
||||
backup.backupDatabaseAll(sys.argv[3], echo_id)
|
||||
elif ',' in sys.argv[2]:
|
||||
for i in sys.argv[2].split(','):
|
||||
backup.backupDatabase(i, sys.argv[3], echo_id)
|
||||
else:
|
||||
backup = backupTools(cron_info = {'echo':sys.argv[4]})
|
||||
backup.backupDatabase(sys.argv[2], sys.argv[3], echo_id)
|
||||
39
script/breaking_through_check.py
Normal file
39
script/breaking_through_check.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# YakPanel
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2017 YakPanel(www.yakpanel.com) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
# Author: hezhihong <hezhihong@yakpanel.com>
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
# ------------------------------
|
||||
# 防爆破检测脚本
|
||||
#------------------------------
|
||||
|
||||
import os,sys
|
||||
panel_path = '/www/server/panel'
|
||||
if not os.name in ['nt']:
|
||||
os.chdir(panel_path)
|
||||
if not 'class/' in sys.path:
|
||||
sys.path.insert(0, 'class/')
|
||||
if not 'class_v2/' in sys.path:
|
||||
sys.path.insert(0, 'class_v2/')
|
||||
sys.path.insert(0, '.')
|
||||
|
||||
import public
|
||||
|
||||
# import breaking_through
|
||||
# breakingObject = breaking_through.main()
|
||||
|
||||
|
||||
# 调用处理方法
|
||||
# result = run_object(pdata)
|
||||
import public.PluginLoader as plugin_loader
|
||||
mod_file = '{}/class_v2/breaking_through.py'.format(public.get_panel_path())
|
||||
plugin_class = plugin_loader.get_module(mod_file)
|
||||
class_string='main'
|
||||
plugin_object = getattr(plugin_class,class_string)()
|
||||
def_name='cron_method'
|
||||
getattr(plugin_object,def_name)()
|
||||
# public.print_log(result)
|
||||
BIN
script/bt-ipfilter
Normal file
BIN
script/bt-ipfilter
Normal file
Binary file not shown.
102
script/btpyprojectenv.sh
Normal file
102
script/btpyprojectenv.sh
Normal file
@@ -0,0 +1,102 @@
|
||||
_BT_PYTHON_NAME_MAP_FILE="/www/server/panel/data/python_project_name2env.txt"
|
||||
|
||||
ACTIVATE_NAME="${1}"
|
||||
if [ -z "${ACTIVATE_NAME}" ]; then
|
||||
echo "Usage: \" source py-project-env <project_name> \" to activate virtual environment"
|
||||
echo "Usage: \" bt_env_deactivate \" command to exit virtual environment and return to previous environment"
|
||||
echo "(Only supported on Linux servers)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_bt_map_safe_get() {
|
||||
awk -F: -v key="$1" '$1 == key {print $2; exit}' "${_BT_PYTHON_NAME_MAP_FILE}" 2>/dev/null
|
||||
}
|
||||
|
||||
if [ -z "${_BT_PROJECT_ENV}" ]; then
|
||||
_BT_PROJECT_ENV="$(_bt_map_safe_get ${ACTIVATE_NAME})"
|
||||
else
|
||||
ACTIVATE_NAME=$(basename "${_BT_PROJECT_ENV}")
|
||||
fi
|
||||
|
||||
if [ ! -d "${_BT_PROJECT_ENV}" ]; then
|
||||
echo "Virtual environment for project: ${ACTIVATE_NAME} does not exist"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# This file must be used with "source bin/activate" *from bash*
|
||||
# You cannot run it directly
|
||||
|
||||
bt_env_deactivate () {
|
||||
# reset old environment variables
|
||||
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
|
||||
PATH="${_OLD_VIRTUAL_PATH:-}"
|
||||
export PATH
|
||||
unset _OLD_VIRTUAL_PATH
|
||||
fi
|
||||
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
|
||||
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
|
||||
export PYTHONHOME
|
||||
unset _OLD_VIRTUAL_PYTHONHOME
|
||||
fi
|
||||
|
||||
# This should detect bash and zsh, which have a hash command that must
|
||||
# be called to get it to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
if [ -n "${BASH:-}" ] || [ -n "${ZSH_VERSION:-}" ] ; then
|
||||
hash -r 2> /dev/null
|
||||
fi
|
||||
|
||||
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
|
||||
PS1="${_OLD_VIRTUAL_PS1:-}"
|
||||
export PS1
|
||||
unset _OLD_VIRTUAL_PS1
|
||||
fi
|
||||
|
||||
unset VIRTUAL_ENV
|
||||
unset VIRTUAL_ENV_PROMPT
|
||||
if [ ! "${1:-}" = "nondestructive" ] ; then
|
||||
# Self destruct!
|
||||
unset -f bt_env_deactivate
|
||||
fi
|
||||
}
|
||||
|
||||
# unset irrelevant variables
|
||||
bt_env_deactivate nondestructive
|
||||
|
||||
_OLD_VIRTUAL_PATH="$PATH"
|
||||
|
||||
if [ -d "${_BT_PROJECT_ENV}/bin" ] ; then
|
||||
VIRTUAL_ENV="${_BT_PROJECT_ENV}"
|
||||
PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||
else
|
||||
VIRTUAL_ENV="${_BT_PROJECT_ENV}"
|
||||
PATH="$VIRTUAL_ENV:$PATH"
|
||||
fi
|
||||
|
||||
# use the path as-is
|
||||
export VIRTUAL_ENV
|
||||
|
||||
export PATH
|
||||
|
||||
# unset PYTHONHOME if set
|
||||
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
|
||||
# could use `if (set -u; : $PYTHONHOME) ;` in bash
|
||||
if [ -n "${PYTHONHOME:-}" ] ; then
|
||||
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
|
||||
unset PYTHONHOME
|
||||
fi
|
||||
|
||||
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
|
||||
_OLD_VIRTUAL_PS1="${PS1:-}"
|
||||
PS1="(${ACTIVATE_NAME}) ${PS1:-}"
|
||||
export PS1
|
||||
VIRTUAL_ENV_PROMPT="(${ACTIVATE_NAME}) "
|
||||
export VIRTUAL_ENV_PROMPT
|
||||
fi
|
||||
|
||||
# This should detect bash and zsh, which have a hash command that must
|
||||
# be called to get it to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
if [ -n "${BASH:-}" ] || [ -n "${ZSH_VERSION:-}" ] ; then
|
||||
hash -r 2> /dev/null
|
||||
fi
|
||||
40
script/check_msg.py
Normal file
40
script/check_msg.py
Normal file
@@ -0,0 +1,40 @@
|
||||
#coding: utf-8
|
||||
import sys,os,time
|
||||
os.chdir('/www/server/panel/')
|
||||
sys.path.insert(0,"class/")
|
||||
import public
|
||||
import http_requests
|
||||
http_requests.DEFAULT_TYPE = 'src'
|
||||
os.environ['BT_TASK'] = '1'
|
||||
|
||||
def main():
|
||||
import panelMessage
|
||||
import re
|
||||
msgObj = panelMessage.panelMessage()
|
||||
data = msgObj.get_messages()
|
||||
for x in data:
|
||||
if x['level'] in ['danger', 'error'] and not x['send'] and x['retry_num'] < 5:
|
||||
msg = '服务器IP【{}】: {}'.format(
|
||||
public.GetLocalIp(), re.sub(r',?<a\s*.+</a>', '', x['msg']))
|
||||
is_send = False
|
||||
|
||||
ret = public.return_is_send_info()
|
||||
for key in ret:
|
||||
if ret[key]:
|
||||
ret = public.send_body_words(key, 'YakPanel 消息提醒', msg)
|
||||
if ret:
|
||||
is_send = True
|
||||
pdata = {}
|
||||
if is_send:
|
||||
pdata['send'] = 1
|
||||
pdata['retry_num'] = 0
|
||||
else:
|
||||
pdata['send'] = 0
|
||||
pdata['retry_num'] = x['retry_num'] + 1
|
||||
|
||||
msgObj.set_send_status(x['id'], pdata)
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
82
script/createSsl.py
Normal file
82
script/createSsl.py
Normal file
@@ -0,0 +1,82 @@
|
||||
#自签证书脚本 hezhihong
|
||||
import sys,os
|
||||
os.chdir('/www/server/panel/')
|
||||
sys.path.insert(0,"class/")
|
||||
sys.path.insert(0,"class_v2/")
|
||||
import public,json
|
||||
class CreateSSLMain:
|
||||
cert_file="/www/server/vhost_virtual/data/cert/vhost.crt"
|
||||
key_file="/www/server/vhost_virtual/data/cert/vhost.key"
|
||||
def get_ipaddress(self):
|
||||
'''
|
||||
@name 获取本机IP地址
|
||||
@author hwliang<2020-11-24>
|
||||
@return list
|
||||
'''
|
||||
ipa_tmp = public.ExecShell("ip a |grep inet|grep -v inet6|grep -v 127.0.0.1|awk '{print $2}'|sed 's#/[0-9]*##g'")[0].strip()
|
||||
iplist = ipa_tmp.split('\n')
|
||||
return iplist
|
||||
|
||||
def get_host_all(self):
|
||||
local_ip = ['127.0.0.1','::1','localhost']
|
||||
ip_list = []
|
||||
bind_ip = self.get_ipaddress()
|
||||
|
||||
for ip in bind_ip:
|
||||
ip = ip.strip()
|
||||
if ip in local_ip: continue
|
||||
if ip in ip_list: continue
|
||||
ip_list.append(ip)
|
||||
net_ip = public.httpGet("https://ifconfig.me/ip")
|
||||
|
||||
if net_ip:
|
||||
net_ip = net_ip.strip()
|
||||
if not net_ip in ip_list:
|
||||
ip_list.append(net_ip)
|
||||
if len(ip_list) > 1:
|
||||
ip_list = [ip_list[-1],ip_list[0]]
|
||||
return ip_list
|
||||
|
||||
#自签证书
|
||||
def CreateSSL(self):
|
||||
if os.path.exists(self.cert_file) and os.path.exists(self.key_file): return True
|
||||
import base64
|
||||
userInfo = public.get_user_info()
|
||||
|
||||
if not userInfo:
|
||||
userInfo['uid'] = 0
|
||||
userInfo['access_key'] = 'B' * 32
|
||||
|
||||
if 'access_key' not in userInfo or not userInfo['access_key']:
|
||||
userInfo['access_key'] = 'B' * 32
|
||||
|
||||
domains = self.get_host_all()
|
||||
pdata = {
|
||||
"action":"get_domain_cert",
|
||||
"company":"yakpanel.com",
|
||||
"domain":','.join(domains),
|
||||
"uid":userInfo['uid'],
|
||||
"access_key":userInfo['access_key'],
|
||||
"panel":1
|
||||
}
|
||||
cert_api = 'https://api.yakpanel.com/yakpanel_cert'
|
||||
result = json.loads(public.httpPost(cert_api,{'data': json.dumps(pdata)}))
|
||||
if 'status' in result:
|
||||
if result['status']:
|
||||
public.writeFile(self.cert_file,result['cert'])
|
||||
public.writeFile(self.key_file,result['key'])
|
||||
_cert_dir = '/www/server/vhost_virtual/data/cert'
|
||||
for _rpfx in (f'{_cert_dir}/yakpanel_root.pfx', f'{_cert_dir}/baota_root.pfx'):
|
||||
if os.path.exists(_rpfx):
|
||||
os.remove(_rpfx)
|
||||
public.writeFile(f'{_cert_dir}/yakpanel_root.pfx', base64.b64decode(result['pfx']), 'wb+')
|
||||
public.writeFile('/www/server/vhost_virtual/data/cert/root_password.pl',result['password'])
|
||||
print('1')
|
||||
return True
|
||||
print('0')
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
ssl = CreateSSLMain()
|
||||
ssl.CreateSSL()
|
||||
31
script/cron_scaning.py
Normal file
31
script/cron_scaning.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import os,sys
|
||||
panel_path = '/www/server/panel'
|
||||
if not os.name in ['nt']:
|
||||
os.chdir(panel_path)
|
||||
if not 'class/' in sys.path:
|
||||
sys.path.insert(0, 'class/')
|
||||
if not 'class_v2/' in sys.path:
|
||||
sys.path.insert(0, 'class_v2/')
|
||||
sys.path.insert(0, '.')
|
||||
import public
|
||||
from mod.base.push_mod import system
|
||||
|
||||
class main:
|
||||
|
||||
|
||||
def run(self):
|
||||
msg_list = []
|
||||
from panel_site_v2 import panelSite
|
||||
site_obj = panelSite()
|
||||
res = site_obj.get_Scan(None)
|
||||
if int(res['loophole_num']):
|
||||
msg_list.append('Scan the website {} and find {} vulnerabilities'.format(res['site_num'], res['loophole_num']))
|
||||
else:
|
||||
msg_list.append('Scan the website [{}], status is [Security]'.format(res['site_num']))
|
||||
return {"msg_list": msg_list}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main = main()
|
||||
msg = main.run()
|
||||
system.push_by_task_keyword("vulnerability_scanning", "vulnerability_scanning", push_data=msg)
|
||||
42
script/crontab_task_exec.py
Normal file
42
script/crontab_task_exec.py
Normal file
@@ -0,0 +1,42 @@
|
||||
#coding: utf-8
|
||||
#-------------------------------------------------------------------
|
||||
# yakpanel
|
||||
#-------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 YakPanel (https://www.yakpanel.com) All rights reserved.
|
||||
#-------------------------------------------------------------------
|
||||
# Author: hwliang <hwl@yakpanel.com>
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
#------------------------------
|
||||
# 任务编排调用脚本
|
||||
#------------------------------
|
||||
import sys,os
|
||||
os.chdir('/www/server/panel')
|
||||
sys.path.insert(0,'class/')
|
||||
sys.path.insert(0,'class_v2/')
|
||||
# import PluginLoader
|
||||
import public
|
||||
args = public.dict_obj()
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print('ERROR: Task ID not found.')
|
||||
sys.exit()
|
||||
args.trigger_id = int(sys.argv[1])
|
||||
args.model_index = 'crontab_v2'
|
||||
# res = PluginLoader.module_run('trigger','test_trigger',args)
|
||||
|
||||
# if not res['status']:
|
||||
# print(res['msg'])
|
||||
# sys.exit()
|
||||
|
||||
|
||||
import public.PluginLoader as plugin_loader
|
||||
mod_file = '{}/class_v2/crontabModelV2/triggerModel.py'.format(public.get_panel_path())
|
||||
plugin_class = plugin_loader.get_module(mod_file)
|
||||
class_string='main'
|
||||
plugin_object = getattr(plugin_class,class_string)()
|
||||
def_name='test_trigger'
|
||||
res=getattr(plugin_object,def_name)(args)
|
||||
if res['status'] != 0 and 'message' in res:
|
||||
print(res['message'])
|
||||
sys.exit()
|
||||
95
script/dk_log_split.py
Normal file
95
script/dk_log_split.py
Normal file
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8
|
||||
# -----------------------------
|
||||
# docker container log cutting script
|
||||
# -----------------------------
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import datetime
|
||||
|
||||
os.chdir("/www/server/panel")
|
||||
sys.path.append('class/')
|
||||
import public
|
||||
|
||||
|
||||
class DkLogSpilt:
|
||||
task_list = []
|
||||
|
||||
def __init__(self):
|
||||
if not public.M('sqlite_master').db('docker_log_split').where('type=? AND name=?', ('table', 'docker_log_split')).count():
|
||||
self.task_list = []
|
||||
else:
|
||||
self.task_list = public.M('docker_log_split').select()
|
||||
|
||||
def run(self):
|
||||
if not self.task_list:
|
||||
print('No docker log cutting task')
|
||||
for task in self.task_list:
|
||||
try:
|
||||
if task['split_type'] == 'day':
|
||||
self.day_split(task)
|
||||
elif task['split_type'] == 'size':
|
||||
self.size_split(task)
|
||||
except:
|
||||
print('{} Failed to cut log!'.format(task['name']))
|
||||
|
||||
def day_split(self, task):
|
||||
now_time = int(time.time())
|
||||
exec_time = int(self.get_timestamp_of_hour_minute(task['split_hour'], task['split_minute']))
|
||||
if now_time <= exec_time <= now_time + 300:
|
||||
print("{} container starts log cutting".format(task['name']))
|
||||
split_path = '/var/lib/docker/containers/history_logs/{}/'.format(task['pid'])
|
||||
if not os.path.exists(split_path):
|
||||
os.makedirs(split_path)
|
||||
os.rename(task['log_path'], split_path + task['pid'] + "-json.log" + '_' + str(int(time.time())))
|
||||
public.writeFile(task['log_path'], '')
|
||||
print("{} log has been cut to:{}".format(task['name'],split_path + task['pid'] + "-json.log" + '_' + str(int(time.time()))))
|
||||
self.check_save(task)
|
||||
else:
|
||||
print('{}container log has not reached the cutting time'.format(task['name']))
|
||||
|
||||
|
||||
def size_split(self, task):
|
||||
if not os.path.exists(task['log_path']):
|
||||
print('Log file does not exist')
|
||||
return
|
||||
if os.path.getsize(task['log_path']) >= task['split_size']:
|
||||
print("{} container starts log cutting".format(task['name']))
|
||||
split_path = '/var/lib/docker/containers/history_logs/{}/'.format(task['pid'])
|
||||
if not os.path.exists(split_path):
|
||||
os.makedirs(split_path)
|
||||
os.rename(task['log_path'], split_path + task['pid'] + "-json.log" + '_' + str(int(time.time())))
|
||||
public.writeFile(task['log_path'], '')
|
||||
print("{} log has been cut to:{}".format(task['name'],split_path + task['pid'] + "-json.log" + '_' + str(int(time.time()))))
|
||||
self.check_save(task)
|
||||
else:
|
||||
print('{} container log has not reached cutting size'.format(task['name']))
|
||||
|
||||
def check_save(self, task):
|
||||
split_path = '/var/lib/docker/containers/history_logs/{}/'.format(task['pid'])
|
||||
file_count = len(os.listdir(split_path))
|
||||
if file_count > task['save']:
|
||||
file_list = os.listdir(split_path)
|
||||
file_list.sort()
|
||||
for i in range(file_count - task['save']):
|
||||
os.remove(split_path + file_list[i])
|
||||
print('Delete log files:{}'.format(split_path + file_list[i]))
|
||||
print('The latest {} logs have been retained'.format(task['save']))
|
||||
|
||||
def get_timestamp_of_hour_minute(self, hour, minute):
|
||||
"""获取当天指定时刻的时间戳。
|
||||
Args:
|
||||
hour: 小时。
|
||||
minute: 分钟。
|
||||
Returns:
|
||||
时间戳。
|
||||
"""
|
||||
current_time = datetime.datetime.now()
|
||||
timestamp = current_time.replace(hour=hour, minute=minute, second=0, microsecond=0)
|
||||
return int(timestamp.timestamp())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
dk = DkLogSpilt()
|
||||
dk.run()
|
||||
254
script/free_total_update.py
Normal file
254
script/free_total_update.py
Normal file
@@ -0,0 +1,254 @@
|
||||
# encoding utf-8
|
||||
import subprocess
|
||||
import requests
|
||||
import os
|
||||
import logging
|
||||
import tempfile
|
||||
|
||||
|
||||
class UpdateSiteTotal:
|
||||
# 类级别的常量定义,集中管理固定配置
|
||||
DEFAULT_USER_AGENT = "Yak-Panel/10.0"
|
||||
INSTALL_SCRIPT_TIMEOUT = 600 # 安装脚本超时时间(秒)
|
||||
VERSION_CHECK_TIMEOUT = 10 # 版本检查超时时间(秒)
|
||||
SCRIPT_DOWNLOAD_TIMEOUT = 15 # 脚本下载超时时间(秒)
|
||||
|
||||
site_total_path = '/www/server/site_total'
|
||||
download_url = 'https://node.yakpanel.com/site_total/'
|
||||
version_url = download_url + 'version.txt'
|
||||
install_sh = download_url + 'install.sh'
|
||||
site_total_bin = os.path.join(site_total_path, 'site_total')
|
||||
|
||||
def __init__(self):
|
||||
"""初始化更新工具
|
||||
"""
|
||||
|
||||
# 初始化日志配置
|
||||
self._init_logger()
|
||||
|
||||
def _init_logger(self):
|
||||
"""初始化日志配置(封装为独立方法,便于维护)"""
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
# ------------------------------
|
||||
# 版本相关方法
|
||||
# ------------------------------
|
||||
def get_current_version(self):
|
||||
"""获取当前安装的版本号"""
|
||||
if not self.is_installed():
|
||||
self._log_warning(f"{self.site_total_bin} is not detected, so the current version cannot be obtained")
|
||||
return None
|
||||
|
||||
try:
|
||||
# 执行版本命令
|
||||
result = subprocess.run(
|
||||
[self.site_total_bin, 'version'],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True,
|
||||
check=True,
|
||||
timeout=self.VERSION_CHECK_TIMEOUT
|
||||
)
|
||||
except (subprocess.CalledProcessError, subprocess.TimeoutExpired, Exception) as e:
|
||||
return self._handle_version_cmd_error(e)
|
||||
|
||||
# 解析版本号(调用辅助方法)
|
||||
return self._parse_version_output(result)
|
||||
|
||||
def _parse_version_output(self, result):
|
||||
"""解析版本命令的输出结果,提取版本号"""
|
||||
# 合并stdout和stderr,避免版本信息输出到错误流
|
||||
output_lines = result.stdout.splitlines() + result.stderr.splitlines()
|
||||
|
||||
for line in output_lines:
|
||||
if "Version:" in line:
|
||||
# 提取版本号并格式化(确保x.y格式)
|
||||
raw_version = line.split(":")[-1].strip()
|
||||
return self._format_version(raw_version)
|
||||
|
||||
self._log_warning(f"The version number cannot be parsed from the output: {output_lines}")
|
||||
return None
|
||||
|
||||
def _format_version(self, raw_version):
|
||||
"""将原始版本号格式化为x.y的字符串格式"""
|
||||
try:
|
||||
float_version = float(raw_version)
|
||||
return f"{float_version:.1f}"
|
||||
except ValueError:
|
||||
self._log_error(f"The version number format is abnormal. It's the original value: {raw_version}")
|
||||
return None
|
||||
|
||||
def _handle_version_cmd_error(self, error):
|
||||
"""处理版本命令执行中的异常"""
|
||||
if isinstance(error, subprocess.CalledProcessError):
|
||||
self._log_error(f"The version command failed to execute: {error.stderr.strip()}")
|
||||
elif isinstance(error, subprocess.TimeoutExpired):
|
||||
self._log_error("The version command has timed out")
|
||||
else:
|
||||
self._log_error(f"An error occurred when obtaining the version number: {str(error)}")
|
||||
return None
|
||||
|
||||
def get_latest_version(self):
|
||||
"""获取最新版本号(返回字符串格式)"""
|
||||
try:
|
||||
response = requests.get(
|
||||
self.version_url,
|
||||
timeout=self.VERSION_CHECK_TIMEOUT,
|
||||
headers={"User-Agent": self.DEFAULT_USER_AGENT}
|
||||
)
|
||||
response.raise_for_status()
|
||||
latest_version = response.text.strip()
|
||||
|
||||
if not latest_version:
|
||||
self._log_warning("The latest version number obtained is empty")
|
||||
return None
|
||||
return latest_version
|
||||
except requests.RequestException as e:
|
||||
self._log_error(f"Failed to get the latest version: {str(e)}")
|
||||
return None
|
||||
|
||||
def check_update_available(self):
|
||||
"""检查是否有可用更新(基于float格式版本号比较)"""
|
||||
current_version_str = self.get_current_version()
|
||||
latest_version_str = self.get_latest_version()
|
||||
|
||||
# 检查版本号获取结果
|
||||
if not current_version_str:
|
||||
return False, "The current version cannot be obtained"
|
||||
if not latest_version_str:
|
||||
return False, "The latest version cannot be obtained"
|
||||
|
||||
# 版本号比较
|
||||
try:
|
||||
current_version = float(current_version_str)
|
||||
latest_version = float(latest_version_str)
|
||||
except ValueError as e:
|
||||
error_msg = f"Version number format error (it should be x.y): {str(e)}"
|
||||
self._log_error(f"{error_msg}(Current: {current_version_str}, Latest: {latest_version_str})")
|
||||
return False, error_msg
|
||||
|
||||
if latest_version > current_version:
|
||||
return True, f"Current version: {current_version_str}, The latest version: {latest_version_str}"
|
||||
else:
|
||||
return False, f"Current version: {current_version_str}, The latest version: {latest_version_str}"
|
||||
|
||||
# ------------------------------
|
||||
# 安装/更新相关方法
|
||||
# ------------------------------
|
||||
def is_installed(self):
|
||||
"""检查site_total是否已安装且可执行"""
|
||||
return os.path.exists(self.site_total_bin) and os.access(self.site_total_bin, os.X_OK)
|
||||
|
||||
def install_or_update(self):
|
||||
"""执行安装或更新操作"""
|
||||
# 下载安装脚本
|
||||
install_script = self._download_install_script()
|
||||
if not install_script:
|
||||
return False, "The download and installation script failed"
|
||||
|
||||
# 执行安装脚本
|
||||
return self._execute_install_script(install_script)
|
||||
|
||||
def _download_install_script(self):
|
||||
"""下载安装脚本"""
|
||||
try:
|
||||
response = requests.get(
|
||||
self.install_sh,
|
||||
timeout=self.SCRIPT_DOWNLOAD_TIMEOUT,
|
||||
headers={"User-Agent": self.DEFAULT_USER_AGENT}
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.text
|
||||
except requests.RequestException as e:
|
||||
self._log_error(f"The download and installation script failed: {str(e)}")
|
||||
return None
|
||||
|
||||
def _execute_install_script(self, script_content):
|
||||
"""执行安装脚本"""
|
||||
temp_script = None
|
||||
try:
|
||||
# 创建临时脚本文件
|
||||
temp_script = self._create_temp_script(script_content)
|
||||
|
||||
# 执行脚本
|
||||
result = subprocess.run(
|
||||
["bash", temp_script],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True,
|
||||
timeout=self.INSTALL_SCRIPT_TIMEOUT
|
||||
)
|
||||
|
||||
# 检查执行结果
|
||||
if result.returncode != 0:
|
||||
error_msg = (f"The installation script failed to execute (return code: {result.returncode})\n"
|
||||
f"Error output: {result.stderr.strip()[:500]}")
|
||||
self._log_error(error_msg)
|
||||
return False, error_msg
|
||||
|
||||
if not self.is_installed():
|
||||
return False, "The installation is complete but the executable file was not found"
|
||||
|
||||
return True, "Installation/update successful"
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
return False, "The installation script has timed out"
|
||||
except Exception as e:
|
||||
return False, f"An error occurred during the installation process: {str(e)}"
|
||||
finally:
|
||||
# 清理临时文件
|
||||
self._cleanup_temp_script(temp_script)
|
||||
|
||||
def _create_temp_script(self, content):
|
||||
"""创建临时脚本文件(辅助方法,封装文件操作)"""
|
||||
with tempfile.NamedTemporaryFile(
|
||||
mode='w',
|
||||
suffix='.sh',
|
||||
delete=False,
|
||||
dir='/tmp'
|
||||
) as f:
|
||||
f.write(content)
|
||||
return f.name
|
||||
|
||||
def _cleanup_temp_script(self, script_path):
|
||||
"""清理临时脚本文件(辅助方法,确保资源释放)"""
|
||||
if script_path and os.path.exists(script_path):
|
||||
try:
|
||||
os.remove(script_path)
|
||||
except OSError as e:
|
||||
self._log_warning(f"Temporary files cannot be deleted {script_path}: {str(e)}")
|
||||
|
||||
# ------------------------------
|
||||
# 主逻辑与工具方法
|
||||
# ------------------------------
|
||||
def update_if_needed(self):
|
||||
"""有更新时执行更新,未安装时执行安装(主逻辑入口)"""
|
||||
if not self.is_installed():
|
||||
self._log_info("Installation not detected. Start the installation")
|
||||
return self.install_or_update()
|
||||
|
||||
has_update, msg = self.check_update_available()
|
||||
if not has_update:
|
||||
return False, f"No update required: {msg}"
|
||||
|
||||
self._log_info(f"Update detected: {msg},Start implementing the update")
|
||||
return self.install_or_update()
|
||||
|
||||
# 日志工具方法(封装日志调用,便于统一管理)
|
||||
def _log_info(self, msg):
|
||||
logging.info(msg)
|
||||
|
||||
def _log_warning(self, msg):
|
||||
logging.warning(msg)
|
||||
|
||||
def _log_error(self, msg):
|
||||
logging.error(msg)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
updater = UpdateSiteTotal()
|
||||
success, message = updater.update_if_needed()
|
||||
logging.info(f"Operation result: {'Success' if success else 'Failure'} - {message}")
|
||||
26
script/ftp.sh
Normal file
26
script/ftp.sh
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
install_tmp='/tmp/bt_install.pl'
|
||||
download_Url=https://www.yakpanel.com
|
||||
Install_Ftp()
|
||||
{
|
||||
echo 'Installing script files...' > $install_tmp
|
||||
wget -O /www/server/panel/script/backup_ftp.py $download_Url/install/lib/script/backup_ftp.py -T 5
|
||||
|
||||
echo 'The installation is complete' > $install_tmp
|
||||
}
|
||||
|
||||
Uninstall_Ftp()
|
||||
{
|
||||
rm -f /www/server/panel/script/backup_ftp.py
|
||||
echo 'Uninstall complete' > $install_tmp
|
||||
}
|
||||
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_Ftp
|
||||
else
|
||||
Uninstall_Ftp
|
||||
fi
|
||||
25
script/ftplogs_cut.py
Normal file
25
script/ftplogs_cut.py
Normal file
@@ -0,0 +1,25 @@
|
||||
import os, sys
|
||||
|
||||
os.chdir('/www/server/panel')
|
||||
sys.path.append('class/')
|
||||
import public
|
||||
|
||||
ftp_backup_path = public.get_backup_path() + '/pure-ftpd/'
|
||||
ftp_log_file = '/var/log/pure-ftpd.log'
|
||||
if not os.path.exists(ftp_backup_path):
|
||||
public.ExecShell('mkdir -p {}'.format(ftp_backup_path))
|
||||
|
||||
from datetime import date, timedelta
|
||||
|
||||
yesterday = (date.today() + timedelta(days=-1)).strftime("%Y-%m-%d")
|
||||
ftp_file = ftp_backup_path + yesterday + '_pure-ftpd.log'
|
||||
conf = ''
|
||||
if os.path.isfile(ftp_file):
|
||||
old_conf = public.readFile(ftp_file)
|
||||
conf += old_conf
|
||||
conf += '\n'
|
||||
new_conf = public.readFile(ftp_log_file)
|
||||
if new_conf: conf += new_conf
|
||||
public.writeFile(ftp_file, conf)
|
||||
public.writeFile(ftp_log_file, '')
|
||||
print('|pure-ftpd log cut completed')
|
||||
159
script/init_firewall.sh
Normal file
159
script/init_firewall.sh
Normal file
@@ -0,0 +1,159 @@
|
||||
#!/bin/bash
|
||||
create_chain() {
|
||||
# 创建iptables链
|
||||
# params:表名 链名
|
||||
local table=$1
|
||||
local chain=$2
|
||||
if ! iptables -t "$table" -n -L "$chain" > /dev/null 2>&1; then
|
||||
iptables -t "$table" -N "$chain"
|
||||
echo "Created chain $chain in table $table"
|
||||
else
|
||||
echo "Chain $chain already exists in table $table"
|
||||
fi
|
||||
}
|
||||
|
||||
insert_input_output_rules() {
|
||||
# 在指定的表的链中插入子链
|
||||
# params:"表名:目标链名:需要插入的链"
|
||||
local rules=("$@")
|
||||
for rule in "${rules[@]}"; do
|
||||
IFS=':' read -r table chain target <<< "$rule"
|
||||
if ! iptables -t "$table" -C "$chain" -j "$target" > /dev/null 2>&1; then
|
||||
iptables -t "$table" -I "$chain" 1 -j "$target"
|
||||
echo "Inserted $target to $chain in table $table"
|
||||
else
|
||||
echo "$target already in $chain in table $table"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
add_jump_rules() {
|
||||
# 在指定的表的链中添加跳转规则
|
||||
# params:表名 目标链名 需要跳转的链
|
||||
local table=$1
|
||||
local target_chain=$2
|
||||
shift 2
|
||||
local chains=("$@")
|
||||
for chain in "${chains[@]}"; do
|
||||
if ! iptables -t "$table" -C "$target_chain" -j "$chain" > /dev/null 2>&1; then
|
||||
iptables -t "$table" -A "$target_chain" -j "$chain"
|
||||
echo "Added $chain to $target_chain in table $table"
|
||||
else
|
||||
echo "$chain already in $target_chain in table $table"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
create_ipset() {
|
||||
local ipset_name=$1
|
||||
if ! ipset list "$ipset_name" > /dev/null 2>&1; then
|
||||
ipset create "$ipset_name" hash:net maxelem 100000 timeout 0
|
||||
echo "Created ipset $ipset_name"
|
||||
else
|
||||
echo "ipset $ipset_name already exists"
|
||||
fi
|
||||
}
|
||||
|
||||
add_ipset_rules() {
|
||||
local rules=("$@")
|
||||
for rule in "${rules[@]}"; do
|
||||
IFS=':' read -r chain action direction ipset_name <<< "$rule"
|
||||
if ! iptables -C "$chain" -m set --match-set "$ipset_name" "$direction" -j "$action" > /dev/null 2>&1; then
|
||||
iptables -I "$chain" 1 -m set --match-set "$ipset_name" "$direction" -j "$action"
|
||||
echo "Added $action rule for $ipset_name ($direction) in $chain"
|
||||
else
|
||||
echo "$action rule for $ipset_name ($direction) already in $chain"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# 函数:创建systemd服务
|
||||
create_systemd_service() {
|
||||
local exec_path="/www/server/panel/pyenv/bin/python3 /www/server/panel/script/BT-FirewallServices.py"
|
||||
local service_file="/etc/systemd/system/BT-FirewallServices.service"
|
||||
if [ ! -f "$service_file" ]; then
|
||||
/www/server/panel/pyenv/bin/python3 -c "import os,sys; os.chdir('/www/server/panel/'); sys.path.insert(0, 'class/'); sys.path.insert(0, '/www/server/panel/'); import public; public.stop_syssafe();"
|
||||
cat << EOF > "$service_file"
|
||||
[Unit]
|
||||
Description=Firewall and System Event Listener Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=$exec_path start
|
||||
ExecReload=$exec_path reload
|
||||
ExecStop=$exec_path stop
|
||||
User=root
|
||||
Type=simple
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl daemon-reload
|
||||
systemctl enable BT-FirewallServices.service
|
||||
${exec_path} save
|
||||
systemctl start BT-FirewallServices.service
|
||||
echo "Systemd service created and started"
|
||||
/www/server/panel/pyenv/bin/python3 -c "import os,sys; os.chdir('/www/server/panel/'); sys.path.insert(0, 'class/'); sys.path.insert(0, '/www/server/panel/'); import public; public.start_syssafe();"
|
||||
else
|
||||
echo "Systemd service already exists"
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
# 所有需要创建接管的子链
|
||||
local chains=(
|
||||
"filter:IN_BT"
|
||||
"filter:IN_BT_log"
|
||||
"filter:IN_BT_user_ip"
|
||||
"filter:IN_BT_ip"
|
||||
"filter:IN_BT_user_port"
|
||||
"filter:OUT_BT"
|
||||
"filter:OUT_BT_user_ip"
|
||||
"filter:OUT_BT_user_port"
|
||||
"filter:IN_BT_Country"
|
||||
"nat:FORWARD_BT"
|
||||
)
|
||||
for chain in "${chains[@]}"; do
|
||||
IFS=':' read -r table chain_name <<< "$chain"
|
||||
create_chain "$table" "$chain_name"
|
||||
done
|
||||
|
||||
# 插入接管的子链
|
||||
local rules=(
|
||||
"filter:INPUT:IN_BT"
|
||||
"filter:IN_BT:IN_BT_log"
|
||||
"filter:IN_BT:IN_BT_user_ip"
|
||||
"filter:IN_BT:IN_BT_ip"
|
||||
"filter:IN_BT:IN_BT_user_port"
|
||||
"filter:IN_BT_ip:IN_BT_Country"
|
||||
"filter:OUTPUT:OUT_BT"
|
||||
"filter:OUT_BT:OUT_BT_user_ip"
|
||||
"filter:OUT_BT:OUT_BT_user_port"
|
||||
"nat:PREROUTING:FORWARD_BT"
|
||||
)
|
||||
insert_input_output_rules "${rules[@]}"
|
||||
|
||||
# ipset集合
|
||||
local ipsets=(
|
||||
"in_bt_user_accept_ipset"
|
||||
"in_bt_user_drop_ipset"
|
||||
"out_bt_user_accept_ipset"
|
||||
"out_bt_user_drop_ipset"
|
||||
)
|
||||
for ipset_name in "${ipsets[@]}"; do
|
||||
create_ipset "$ipset_name"
|
||||
done
|
||||
|
||||
local ipset_rules=(
|
||||
"IN_BT_user_ip:ACCEPT:src:in_bt_user_accept_ipset"
|
||||
"IN_BT_user_ip:DROP:src:in_bt_user_drop_ipset"
|
||||
"OUT_BT_user_ip:ACCEPT:dst:out_bt_user_accept_ipset"
|
||||
"OUT_BT_user_ip:DROP:dst:out_bt_user_drop_ipset"
|
||||
)
|
||||
add_ipset_rules "${ipset_rules[@]}"
|
||||
create_systemd_service
|
||||
systemctl reload BT-FirewallServices
|
||||
echo "yakpanel firewall init finish..."
|
||||
}
|
||||
|
||||
main
|
||||
235
script/install.sh
Normal file
235
script/install.sh
Normal file
@@ -0,0 +1,235 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
echo "
|
||||
+----------------------------------------------------------------------
|
||||
| Bt-WebPanel 3.0 FOR CentOS beta
|
||||
+----------------------------------------------------------------------
|
||||
| Copyright (c) 2015-2017 YakPanel(https://www.yakpanel.com) All rights reserved.
|
||||
+----------------------------------------------------------------------
|
||||
| Python2.6/2.7 successful the http://SERVER_IP:8888 is WebPanel
|
||||
+----------------------------------------------------------------------
|
||||
"
|
||||
|
||||
download_Url='https://www.yakpanel.com'
|
||||
setup_patn=/www
|
||||
|
||||
while [ "$go" != 'y' ] && [ "$go" != 'n' ]
|
||||
do
|
||||
read -p "Now do you want to install Yak-Panel to the $setup_patn directory?(y/n): " go;
|
||||
done
|
||||
|
||||
if [ "$go" == 'n' ];then
|
||||
exit;
|
||||
fi
|
||||
|
||||
yum -y install ntp
|
||||
\cp -a -r /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||
echo 'Synchronizing system time..'
|
||||
ntpdate 0.asia.pool.ntp.org
|
||||
hwclock -w
|
||||
|
||||
startTime=`date +%s`
|
||||
|
||||
rm -f /var/run/yum.pid
|
||||
paces="wget python-devel python-imaging zip unzip openssl openssl-devel gcc libxml2 libxml2-dev libxslt* zlib zlib-devel libjpeg-devel libpng-devel libwebp libwebp-devel freetype freetype-devel lsof pcre pcre-devel vixie-cron crontabs"
|
||||
yum -y install $paces
|
||||
|
||||
if [ ! -f '/usr/bin/mysql_config' ];then
|
||||
yum install mysql-devel -y
|
||||
fi
|
||||
|
||||
tmp=`python -V 2>&1|awk '{print $2}'`
|
||||
pVersion=${tmp:0:3}
|
||||
|
||||
if [ ! -f "/usr/lib/python${pVersion}/site-packages/setuptools-33.1.1-py${pVersion}.egg" ];then
|
||||
wget $download_Url/install/src/setuptools-33.1.1.zip -T 10
|
||||
unzip setuptools-33.1.1.zip
|
||||
rm -f setuptools-33.1.1.zip
|
||||
cd setuptools-33.1.1
|
||||
python setup.py install
|
||||
cd ..
|
||||
rm -rf setuptools-33.1.1
|
||||
fi
|
||||
|
||||
if [ ! -f "/usr/lib64/python${pVersion}/site-packages/Pillow-3.2.0-py${pVersion}-linux-x86_64.egg" ] && [ ! -f "/usr/lib/python${pVersion}/site-packages/Pillow-3.2.0-py${pVersion}-linux-x86_64.egg" ];then
|
||||
wget $download_Url/install/src/Pillow-3.2.0.zip -T 10
|
||||
unzip Pillow-3.2.0.zip
|
||||
rm -f Pillow-3.2.0.zip
|
||||
cd Pillow-3.2.0
|
||||
python setup.py install
|
||||
cd ..
|
||||
rm -rf Pillow-3.2.0
|
||||
fi
|
||||
|
||||
if [ ! -d "/usr/lib/python${pVersion}/site-packages/psutil-5.1.3-py${pVersion}-linux-x86_64.egg" ] && [ ! -d "/usr/lib64/python${pVersion}/site-packages/psutil-5.1.3-py${pVersion}-linux-x86_64.egg" ];then
|
||||
wget $download_Url/install/src/psutil-5.1.3.tar.gz -T 10
|
||||
tar xvf psutil-5.1.3.tar.gz
|
||||
rm -f psutil-5.1.3.tar.gz
|
||||
cd psutil-5.1.3
|
||||
python setup.py install
|
||||
cd ..
|
||||
rm -rf psutil-5.1.3
|
||||
fi
|
||||
|
||||
if [ ! -f "/usr/lib64/python${pVersion}/site-packages/MySQL_python-1.2.5-py${pVersion}-linux-x86_64.egg" ] && [ ! -f "/usr/lib/python${pVersion}/site-packages/MySQL_python-1.2.5-py${pVersion}-linux-x86_64.egg" ];then
|
||||
wget $download_Url/install/src/MySQL-python-1.2.5.zip -T 10
|
||||
unzip MySQL-python-1.2.5.zip
|
||||
rm -f MySQL-python-1.2.5.zip
|
||||
cd MySQL-python-1.2.5
|
||||
python setup.py install
|
||||
cd ..
|
||||
rm -rf MySQL-python-1.2.5
|
||||
fi
|
||||
|
||||
if [ ! -f "/usr/lib/python${pVersion}/site-packages/chardet-2.3.0-py${pVersion}.egg" ];then
|
||||
wget $download_Url/install/src/chardet-2.3.0.tar.gz -T 10
|
||||
tar xvf chardet-2.3.0.tar.gz
|
||||
rm -f chardet-2.3.0.tar.gz
|
||||
cd chardet-2.3.0
|
||||
python setup.py install
|
||||
cd ..
|
||||
rm -rf chardet-2.3.0
|
||||
fi
|
||||
if [ ! -f "/usr/lib/python${pVersion}/site-packages/web.py-0.38-py${pVersion}.egg-info" ];then
|
||||
wget $download_Url/install/src/web.py-0.38.tar.gz -T 10
|
||||
tar xvf web.py-0.38.tar.gz
|
||||
rm -f web.py-0.38.tar.gz
|
||||
cd web.py-0.38
|
||||
python setup.py install
|
||||
cd ..
|
||||
rm -rf web.py-0.38
|
||||
fi
|
||||
|
||||
|
||||
mkdir -p $setup_patn/server/panel/logs
|
||||
wget https://dl.eff.org/certbot-auto --no-check-certificate -O $setup_patn/server/panel/certbot-auto
|
||||
chmod +x $setup_patn/server/panel/certbot-auto
|
||||
isCron=`cat /var/spool/cron/root|grep certbot.log`
|
||||
if [ "${isCron}" == "" ];then
|
||||
echo "30 2 * * * $setup_patn/server/panel/certbot-auto renew >> $setup_patn/server/panel/logs/certbot.log" >> /var/spool/cron/root
|
||||
chown 600 /var/spool/cron/root
|
||||
fi
|
||||
|
||||
if [ -f '/etc/init.d/bt' ];then
|
||||
service bt stop
|
||||
fi
|
||||
|
||||
mkdir -p /www/server
|
||||
mkdir -p /www/wwwroot
|
||||
mkdir -p /www/wwwlogs
|
||||
mkdir -p /www/backup/database
|
||||
mkdir -p /www/backup/site
|
||||
|
||||
|
||||
wget -O panel.zip $download_Url/install/src/panel.zip -T 10
|
||||
wget -O /etc/init.d/bt $download_Url/install/src/bt.init -T 10
|
||||
if [ -f "$setup_patn/server/panel/data/default.db" ];then
|
||||
if [ -d "/$setup_patn/server/panel/old_data" ];then
|
||||
rm -rf /$setup_patn/server/panel/old_data
|
||||
fi
|
||||
mv $setup_patn/server/panel/data /$setup_patn/server/panel/old_data
|
||||
fi
|
||||
|
||||
unzip -o panel.zip -d $setup_patn/server/ > /dev/null
|
||||
|
||||
if [ -d "$setup_patn/server/panel/old_data" ];then
|
||||
if [ -d "/$setup_patn/server/panel/data" ];then
|
||||
rm -rf /$setup_patn/server/panel/data
|
||||
fi
|
||||
mv /$setup_patn/server/panel/old_data $setup_patn/server/panel/data
|
||||
fi
|
||||
|
||||
|
||||
rm -f panel.zip
|
||||
|
||||
rm -f $setup_patn/server/panel/class/*.pyc
|
||||
rm -f $setup_patn/server/panel/*.pyc
|
||||
python -m compileall $setup_patn/server/panel
|
||||
rm -f $setup_patn/server/panel/class/*.py
|
||||
rm -f $setup_patn/server/panel/*.py
|
||||
|
||||
chmod +x /etc/init.d/bt
|
||||
chkconfig --add bt
|
||||
chkconfig --level 2345 bt on
|
||||
echo '8888' > $setup_patn/server/panel/data/port.pl
|
||||
chmod -R 600 $setup_patn/server/panel
|
||||
chmod +x $setup_patn/server/panel/certbot-auto
|
||||
chmod -R +x $setup_patn/server/panel/script
|
||||
service bt start
|
||||
password=`cat /dev/urandom | head -n 16 | md5sum | head -c 8`
|
||||
cd $setup_patn/server/panel/
|
||||
python tools.pyc panel $password
|
||||
cd ~
|
||||
echo "$password" > $setup_patn/server/panel/default.pl
|
||||
chmod 600 $setup_patn/server/panel/default.pl
|
||||
|
||||
if [ -f "/etc/init.d/iptables" ];then
|
||||
iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 20 -j ACCEPT
|
||||
iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 21 -j ACCEPT
|
||||
iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
|
||||
iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
|
||||
iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 8888 -j ACCEPT
|
||||
iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 30000:40000 -j ACCEPT
|
||||
service iptables save
|
||||
|
||||
iptables_status=`service iptables status | grep 'not running'`
|
||||
if [ "${iptables_status}" == '' ];then
|
||||
service iptables restart
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${isVersion}" == '' ];then
|
||||
if [ ! -f "/etc/init.d/iptables" ];then
|
||||
yum install firewalld -y
|
||||
systemctl enable firewalld
|
||||
systemctl start firewalld
|
||||
firewall-cmd --permanent --zone=public --add-port=20/tcp
|
||||
firewall-cmd --permanent --zone=public --add-port=21/tcp
|
||||
firewall-cmd --permanent --zone=public --add-port=22/tcp
|
||||
firewall-cmd --permanent --zone=public --add-port=80/tcp
|
||||
firewall-cmd --permanent --zone=public --add-port=8888/tcp
|
||||
firewall-cmd --permanent --zone=public --add-port=30000-40000/tcp
|
||||
firewall-cmd --reload
|
||||
fi
|
||||
fi
|
||||
|
||||
yum -y install epel-release
|
||||
country=`curl -sS --connect-timeout 10 -m 60 http://ip.vpser.net/country`
|
||||
if [ "${country}" = "CN" ]; then
|
||||
mkdir ~/.pip
|
||||
cat > ~/.pip/pip.conf <<EOF
|
||||
[global]
|
||||
index-url = https://pypi.doubanio.com/simple/
|
||||
|
||||
[install]
|
||||
trusted-host=pypi.doubanio.com
|
||||
EOF
|
||||
fi
|
||||
nohup $setup_patn/server/panel/certbot-auto -n > /tmp/certbot-auto.log 2>&1 &
|
||||
|
||||
address=""
|
||||
n=0
|
||||
while [ "$address" == '' ]
|
||||
do
|
||||
address=`curl -s http://city.ip138.com/ip2city.asp|grep -Eo '([0-9]+\.){3}[0-9]+'`
|
||||
let n++
|
||||
sleep 0.1
|
||||
if [ $n -gt 5 ];then
|
||||
address="SERVER_IP"
|
||||
fi
|
||||
done
|
||||
|
||||
curl http://www.yakpanel.com/Api/SetupCount?type=Linux
|
||||
|
||||
echo "====================================="
|
||||
echo -e "\033[32mThe install successful!\033[0m"
|
||||
echo -e "====================================="
|
||||
echo -e "Yak-Panel: http://$address:8888"
|
||||
echo -e "username: admin"
|
||||
echo -e "password: $password"
|
||||
echo -e "====================================="
|
||||
endTime=`date +%s`
|
||||
((outTime=($endTime-$startTime)/60))
|
||||
echo -e "Time consuming:\033[32m $outTime \033[0mMinute!"
|
||||
rm -f install.sh
|
||||
20
script/install_pycountry.sh
Normal file
20
script/install_pycountry.sh
Normal file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
mypip="/www/server/panel/pyenv/bin/pip3"
|
||||
pycountry_path="/www/server/panel/script/pycountry-24.6.1-py3-none-any.whl"
|
||||
|
||||
change_pip_package_list=$( $mypip list | grep -E "pycountry" )
|
||||
|
||||
pycountry_v=$(echo "$change_pip_package_list" | grep pycountry)
|
||||
if [ "$pycountry_v" = "" ];then
|
||||
echo "Update pycountry"
|
||||
$mypip install $pycountry_path
|
||||
rm -f $pycountry_path
|
||||
|
||||
pycountry_v_2=$($mypip list |grep pycountry)
|
||||
if [ "$pycountry_v_2" = "" ];then
|
||||
$mypip install pycountry
|
||||
fi
|
||||
fi
|
||||
20
script/install_pyroute2.sh
Normal file
20
script/install_pyroute2.sh
Normal file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
mypip="/www/server/panel/pyenv/bin/pip3"
|
||||
pyroute2_path="/www/server/panel/script/pyroute2-0.7.12-py3-none-any.whl"
|
||||
|
||||
change_pip_package_list=$( $mypip list | grep -E "pyroute2" )
|
||||
|
||||
pyroute2_v=$(echo "$change_pip_package_list" | grep pyroute2)
|
||||
if [ "$pyroute2_v" = "" ];then
|
||||
echo "Update pyroute2"
|
||||
$mypip install $pyroute2_path
|
||||
rm -f $pyroute2_path
|
||||
|
||||
pyroute2_v_2=$($mypip list |grep pyroute2)
|
||||
if [ "$pyroute2_v_2" = "" ];then
|
||||
$mypip install pyroute2
|
||||
fi
|
||||
fi
|
||||
55
script/install_python.sh
Normal file
55
script/install_python.sh
Normal file
@@ -0,0 +1,55 @@
|
||||
#!/bin/bash
|
||||
pyversion=${1}
|
||||
py_path=/www/server/pyporject_evn/versions
|
||||
py_cache=/www/server/pyporject_evn/versions/cached
|
||||
cpuCore=$(cat /proc/cpuinfo |grep "physical id"|sort |uniq|wc -l)
|
||||
|
||||
mkdir -p ${py_path}
|
||||
download_Url='https://node.yakpanel.com'
|
||||
|
||||
install_python() {
|
||||
\cp ${py_cache}/Python-${pyversion}.tar.xz /tmp/Python-${pyversion}.tar.xz
|
||||
cd /tmp/ && xz -d /tmp/Python-${pyversion}.tar.xz && tar -xvf /tmp/Python-${pyversion}.tar
|
||||
cd /tmp/Python-${pyversion} || exit
|
||||
if [ ${pyversion:0:1} -ge 2 ]; then
|
||||
openssl111check=$(openssl version | grep 1.1.1)
|
||||
if [ -z "${openssl111check}" ]; then
|
||||
Install_Openssl111
|
||||
WITH_SSL="--with-openssl=/usr/local/openssl111"
|
||||
else
|
||||
WITH_SSL=""
|
||||
fi
|
||||
cd /tmp/Python-${pyversion} || exit
|
||||
./configure --prefix=${py_path}/${pyversion} ${WITH_SSL} -with-openssl-rpath=auto
|
||||
make -j${cpuCore}
|
||||
make install
|
||||
rm -rf /tmp/Python-*
|
||||
else
|
||||
./configure --prefix=${py_path}/${pyversion}
|
||||
make -j${cpuCore}
|
||||
make install
|
||||
rm -rf /tmp/Python-*
|
||||
fi
|
||||
}
|
||||
|
||||
Install_Openssl111() {
|
||||
opensslCheck=$(/usr/local/openssl111/bin/openssl version | grep 1.1.1)
|
||||
if [ -z "${opensslCheck}" ]; then
|
||||
opensslVersion="1.1.1o"
|
||||
cd /tmp/
|
||||
wget ${download_Url}/src/openssl-${opensslVersion}.tar.gz
|
||||
tar -zxf openssl-${opensslVersion}.tar.gz
|
||||
rm -f openssl-${opensslVersion}.tar.gz
|
||||
cd openssl-${opensslVersion} || exit
|
||||
./config --prefix=/usr/local/openssl111 zlib-dynamic
|
||||
make -j${cpuCore}
|
||||
make install
|
||||
echo "/usr/local/openssl111/lib" >>/etc/ld.so.conf.d/openssl111.conf
|
||||
ldconfig
|
||||
ldconfig /lib64
|
||||
cd ..
|
||||
rm -rf openssl-${opensslVersion}
|
||||
fi
|
||||
}
|
||||
|
||||
install_python
|
||||
63
script/log_task_analyzer.py
Normal file
63
script/log_task_analyzer.py
Normal file
@@ -0,0 +1,63 @@
|
||||
import datetime
|
||||
import sys
|
||||
import os
|
||||
os.chdir('/www/server/panel/')
|
||||
sys.path.insert(0, "class/")
|
||||
import public
|
||||
|
||||
# 检查是否提供了日志文件路径作为命令行参数
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python log_task_analyzer.py <log_file_path>")
|
||||
sys.exit(1)
|
||||
|
||||
# 从命令行参数中获取日志文件路径
|
||||
log_file = sys.argv[1]
|
||||
|
||||
# 记录任务执行状态的文件路径
|
||||
task_log = "/tmp/task_log.log"
|
||||
|
||||
# 反向读取日志文件
|
||||
try:
|
||||
with open(log_file, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
lines = f.readlines()[::-1]
|
||||
except FileNotFoundError:
|
||||
print("Log file {} not found.".format(log_file))
|
||||
sys.exit(1)
|
||||
|
||||
# 初始化变量
|
||||
found_successful = False
|
||||
found_error_before_next_successful = False
|
||||
|
||||
# 要查找的错误模式
|
||||
error_patterns = ["错误", "失败", "command not found", "Unknown error", "不存在", "请先到软件商店安装日志清理工具", "failed to run command", "No such file or directory", "not supported or disabled in libcurl"]
|
||||
|
||||
# 迭代检查日志内容
|
||||
for line in lines:
|
||||
if "Successful" in line:
|
||||
if found_successful:
|
||||
# 如果找到第二个 "Successful",停止搜索
|
||||
break
|
||||
else:
|
||||
# 找到第一个 "Successful"
|
||||
found_successful = True
|
||||
elif any(error_pattern in line for error_pattern in error_patterns) and found_successful:
|
||||
# 如果在找到 "Successful" 后发现任何错误模式,则标记错误
|
||||
found_error_before_next_successful = True
|
||||
break
|
||||
|
||||
# 根据检查结果记录日志
|
||||
with open(task_log, 'a') as f:
|
||||
current_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
# 提取 echo 值作为日志文件名的前缀
|
||||
echo_value = log_file.split('/')[-1].split('.')[0]
|
||||
crontab_data_list = public.M('crontab').where('echo=?', (echo_value,)).select()
|
||||
|
||||
if found_successful and found_error_before_next_successful:
|
||||
if crontab_data_list:
|
||||
public.M('crontab').where('echo=?', (echo_value,)).setField('result', 0)
|
||||
|
||||
# f.write("{0} - 最后一次定时任务执行发现错误,执行失败\n".format(current_time))
|
||||
else:
|
||||
if crontab_data_list:
|
||||
public.M('crontab').where('echo=?', (echo_value,)).setField('result', 1)
|
||||
# f.write("{0} - 最后一次定时任务执行成功\n".format(current_time))
|
||||
253
script/logsBackup
Normal file
253
script/logsBackup
Normal file
@@ -0,0 +1,253 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8
|
||||
# -----------------------------
|
||||
# 宝塔Linux面板网站日志切割脚本
|
||||
# -----------------------------
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import time
|
||||
|
||||
os.chdir("/www/server/panel")
|
||||
sys.path.append('class/')
|
||||
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
import public
|
||||
|
||||
print('==================================================================')
|
||||
print('★[' + time.strftime("%Y/%m/%d %H:%M:%S") + '] Cutting log')
|
||||
print('==================================================================')
|
||||
print('|--Currently keeping the latest copies of [' + sys.argv[2] + ']')
|
||||
|
||||
logsPath = '/www/wwwlogs/'
|
||||
is_gzip = not os.path.exists('/www/server/panel/data/log_not_gzip.pl')
|
||||
is_nginx = False
|
||||
if os.path.exists('/www/server/nginx/logs/nginx.pid'): is_nginx = True
|
||||
px = '.log'
|
||||
if not is_nginx: px = '-access_log'
|
||||
|
||||
def build_errlog(sitename):
|
||||
if is_nginx:
|
||||
log = sitename + '.error.log'
|
||||
elif os.path.exists('/usr/local/lsws/bin/lswsctrl'):
|
||||
log = sitename + '_ols.error_log'
|
||||
else:
|
||||
log = sitename + '-error_log'
|
||||
return log
|
||||
|
||||
def clean_backlog(logname,num):
|
||||
logs=sorted(glob.glob(logname+"_*"))
|
||||
count=len(logs)
|
||||
num=count - num
|
||||
|
||||
for i in range(count):
|
||||
if i>num: break;
|
||||
os.remove(logs[i])
|
||||
print('|---The extra log ['+logs[i]+'] has been deleted!')
|
||||
|
||||
def split_logs(oldFileName,num,site_name):
|
||||
global logsPath
|
||||
errlog_name = build_errlog(site_name)
|
||||
old_errlog = logsPath + errlog_name
|
||||
|
||||
if not os.path.exists(oldFileName):
|
||||
print('|---'+oldFileName+'file does not exist!')
|
||||
return
|
||||
|
||||
clean_backlog(oldFileName,num)
|
||||
clean_backlog(old_errlog,num)
|
||||
|
||||
newFileName=oldFileName+'_'+time.strftime("%Y-%m-%d_%H%M%S")+'.log'
|
||||
shutil.move(oldFileName,newFileName)
|
||||
new_errlog = old_errlog+'_'+time.strftime("%Y-%m-%d_%H%M%S")+'.log'
|
||||
shutil.move(old_errlog, new_errlog)
|
||||
if not os.path.exists('/www/server/panel/data/log_not_gzip.pl'):
|
||||
os.system("gzip %s" % newFileName)
|
||||
os.system("gzip %s" % new_errlog)
|
||||
print('|---The log has been cut to:' + newFileName + '.gz')
|
||||
print('|---The log has been cut to:' + new_errlog + '.gz')
|
||||
else:
|
||||
print('|---The log has been cut to:'+newFileName+'.log')
|
||||
print('|---The log has been cut to:' + new_errlog + '.log')
|
||||
|
||||
|
||||
|
||||
def split_log(siteName, num, log_cut_path):
|
||||
global logsPath, is_gzip
|
||||
res = public.M('sites').where('name=?', (siteName,)).select()[0]['project_type'].lower()
|
||||
if res == 'php':
|
||||
res = ''
|
||||
else:
|
||||
res = res + '_'
|
||||
# print(res)
|
||||
directory = '/etc/init.d/'
|
||||
files = os.listdir(directory)
|
||||
file_list = list(files)
|
||||
print('|-Processing website: {}'.format(siteName))
|
||||
if 'nginx' in file_list:
|
||||
config_path = '/www/server/panel/vhost/nginx/{}.conf'.format(res + siteName)
|
||||
config = public.readFile(config_path)
|
||||
if not config:
|
||||
print('|-Processing website: logs for {} site not detected'.format(siteName))
|
||||
return
|
||||
tmp, _ = nginx_get_log_path(config, is_error_log=False)
|
||||
if tmp is not None:
|
||||
logsPath = tmp
|
||||
elif 'httpd' in file_list:
|
||||
config_path = '/www/server/panel/vhost/apache/{}.conf'.format(res + siteName)
|
||||
config = public.readFile(config_path)
|
||||
if not config:
|
||||
print('|-Processing website: logs for [{}] site not detected'.format(siteName))
|
||||
return
|
||||
tmp, _ = apache_get_log_path(config, is_error_log=False)
|
||||
if tmp is not None:
|
||||
logsPath = tmp
|
||||
|
||||
# print('|-正在处理网站: {}'.format(siteName))
|
||||
# print(logsPath)
|
||||
log_path = os.path.join(log_cut_path, 'history_backups', siteName)
|
||||
log_path = log_path.replace('history_backups/history_backups/', 'history_backups/')
|
||||
if not os.path.exists(log_path): os.makedirs(log_path, 384)
|
||||
logs = sorted(glob.glob(log_path + '/' + siteName + "_access_*"))
|
||||
old_logs = sorted(glob.glob(logsPath + '/' + siteName + "*log_*.log"))
|
||||
count = len(logs)
|
||||
remove_num = count - num
|
||||
# print('|-当前日志数量: {}, 删除:{}'.format(count,remove_num))
|
||||
old_list = old_logs[:remove_num+1]
|
||||
for i in old_list:
|
||||
if os.path.exists(i):
|
||||
os.remove(old_logs[i])
|
||||
print('|---The extra log [' + i + '] has been deleted!')
|
||||
remove_num = remove_num - len(old_list)
|
||||
for i in logs[:remove_num+1]:
|
||||
if os.path.exists(i):
|
||||
os.remove(i)
|
||||
print('|---The extra log [' + i + '] has been deleted!')
|
||||
err_file = i.replace('access', 'error')
|
||||
if os.path.exists(err_file):
|
||||
os.remove(err_file)
|
||||
print('|---The extra log [' + err_file + '] has been deleted!')
|
||||
|
||||
his_date = time.strftime("%Y-%m-%d_%H%M%S")
|
||||
ngx_access_log = os.path.join(logsPath, siteName + '.log')
|
||||
ngx_error_log = os.path.join(logsPath, siteName + '.error.log')
|
||||
if os.path.exists(ngx_access_log):
|
||||
history_log_file = log_path + '/' + siteName + '_access_' + his_date + '.log'
|
||||
shutil.move(ngx_access_log, history_log_file)
|
||||
if is_gzip:
|
||||
os.system('gzip {}'.format(history_log_file))
|
||||
print('|---Cut logs to:' + history_log_file + '.gz')
|
||||
else:
|
||||
print('|---Cut logs to:' + history_log_file + '.gz')
|
||||
if os.path.exists(ngx_error_log):
|
||||
history_log_file = log_path + '/' + siteName + '_error_' + his_date + '.log'
|
||||
shutil.move(ngx_error_log, history_log_file)
|
||||
if is_gzip:
|
||||
os.system('gzip {}'.format(history_log_file))
|
||||
print('|---Cut logs to:' + history_log_file + '.gz')
|
||||
else:
|
||||
print('|---Cut logs to:' + history_log_file + '.gz')
|
||||
|
||||
httpd_access_log = os.path.join(logsPath, siteName + '-access_log')
|
||||
httpd_error_log = os.path.join(logsPath, siteName + '-error_log')
|
||||
if os.path.exists(httpd_access_log):
|
||||
history_log_file = log_path + '/' + siteName + '_apache_access_' + his_date + '.log'
|
||||
if not os.path.exists(history_log_file):
|
||||
shutil.move(httpd_access_log, history_log_file)
|
||||
if is_gzip:
|
||||
os.system('gzip {}'.format(history_log_file))
|
||||
print('|---Cut logs to:' + history_log_file + '.gz')
|
||||
else:
|
||||
print('|---Cut logs to:' + history_log_file + '.gz')
|
||||
if os.path.exists(httpd_error_log):
|
||||
history_log_file = log_path + '/' + siteName + '_apache_error_' + his_date + '.log'
|
||||
if not os.path.exists(history_log_file):
|
||||
shutil.move(httpd_error_log, history_log_file)
|
||||
if is_gzip:
|
||||
os.system('gzip {}'.format(history_log_file))
|
||||
print('|---Cut logs to:' + history_log_file + '.gz')
|
||||
else:
|
||||
print('|---Cut logs to:' + history_log_file + '.gz')
|
||||
|
||||
ols_access_log = os.path.join(logsPath, siteName + '_ols.access_log')
|
||||
ols_error_log = os.path.join(logsPath, siteName + '_ols.error_log')
|
||||
if os.path.exists(ols_access_log):
|
||||
history_log_file = log_path + '/' + siteName + '_ols_access_' + his_date + '.log'
|
||||
if not os.path.exists(history_log_file):
|
||||
shutil.move(ols_access_log, history_log_file)
|
||||
if is_gzip:
|
||||
os.system('gzip {}'.format(history_log_file))
|
||||
print('|---Cut logs to:' + history_log_file + '.gz')
|
||||
else:
|
||||
print('|---Cut logs to:' + history_log_file + '.gz')
|
||||
if os.path.exists(ols_error_log):
|
||||
history_log_file = log_path + '/' + siteName + '_ols_error_' + his_date + '.log'
|
||||
if not os.path.exists(history_log_file):
|
||||
shutil.move(ols_error_log, history_log_file)
|
||||
if is_gzip:
|
||||
os.system('gzip {}'.format(history_log_file))
|
||||
print('|---Cut logs to:' + history_log_file + '.gz')
|
||||
else:
|
||||
print('|---Cut logs to:' + history_log_file + '.gz')
|
||||
|
||||
|
||||
def split_all(save,log_cut_path):
|
||||
sites = public.M('sites').field('name').select()
|
||||
for site in sites:
|
||||
split_log(site['name'], save,log_cut_path)
|
||||
|
||||
|
||||
def nginx_get_log_path(nginx_config: str, is_error_log: bool = False):
|
||||
if is_error_log:
|
||||
re_data = re.findall(r"error_log +(/(\S+/?)+) ?(.*?);", nginx_config)
|
||||
else:
|
||||
re_data = re.findall(r"access_log +(/(\S+/?)+) ?(.*?);", nginx_config)
|
||||
if re_data is None:
|
||||
return None, None
|
||||
for i in re_data:
|
||||
file_path = i[0].strip(";")
|
||||
if file_path != "/dev/null":
|
||||
return os.path.dirname(file_path), os.path.basename(file_path)
|
||||
return None, None
|
||||
|
||||
|
||||
def apache_get_log_path(apache_config: str, is_error_log: bool = False):
|
||||
if is_error_log:
|
||||
re_data = re.findall(r'''ErrorLog +['"]?(/(\S+/?)+)['"]? ?(.*?)\n''', apache_config)
|
||||
else:
|
||||
re_data = re.findall(r'''CustomLog +['"]?(/(\S+/?)+)['"]? ?(.*?)\n''', apache_config)
|
||||
if re_data is None:
|
||||
return None
|
||||
for i in re_data:
|
||||
file_path = i[0].strip('"').strip("'")
|
||||
if file_path != "/dev/null":
|
||||
return os.path.dirname(file_path), os.path.basename(file_path)
|
||||
return None, None
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
num = int(sys.argv[2])
|
||||
if len(sys.argv) > 3:
|
||||
log_cut_path = sys.argv[3]
|
||||
else:
|
||||
log_cut_path = '/www/wwwlogs/'
|
||||
if sys.argv[1].find('ALL') == 0:
|
||||
split_all(num, log_cut_path)
|
||||
else:
|
||||
siteName = sys.argv[1]
|
||||
try:
|
||||
public.M('sites').where('name=?', (siteName,)).select()[0]['project_type'].lower()
|
||||
split_log(sys.argv[1], num, log_cut_path)
|
||||
except:
|
||||
siteName = sys.argv[1]
|
||||
if siteName[-4:] == '.log':
|
||||
siteName = siteName[:-4]
|
||||
elif siteName[-11:] == '-access_log':
|
||||
siteName = siteName.replace("-access_log",'')
|
||||
else:
|
||||
siteName = siteName.replace("_ols.access_log", '')
|
||||
oldFileName = logsPath+sys.argv[1]
|
||||
split_logs(oldFileName,num,siteName)
|
||||
public.serviceReload()
|
||||
92
script/logsBackup.py
Normal file
92
script/logsBackup.py
Normal file
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/python
|
||||
#coding: utf-8
|
||||
#-----------------------------
|
||||
#YakPanel
|
||||
# 网站日志切割脚本
|
||||
#-----------------------------
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
import glob
|
||||
os.chdir("/www/server/panel")
|
||||
sys.path.append('class/')
|
||||
import public
|
||||
print ('==================================================================')
|
||||
print( '★['+time.strftime("%Y/%m/%d %H:%M:%S")+'],Cutting log')
|
||||
print ('==================================================================')
|
||||
print ('|--Currently retaining the latest ['+sys.argv[2]+'] copies')
|
||||
logsPath = '/www/wwwlogs/'
|
||||
is_nginx = False
|
||||
if os.path.exists('/www/server/nginx/logs/nginx.pid'): is_nginx = True
|
||||
px = '.log'
|
||||
if not is_nginx: px = '-access_log'
|
||||
|
||||
def build_errlog(sitename):
|
||||
if is_nginx:
|
||||
log = sitename + '.error.log'
|
||||
elif os.path.exists('/usr/local/lsws/bin/lswsctl'):
|
||||
log = sitename + '-error_log'
|
||||
else:
|
||||
log = sitename + '_ols.error_log'
|
||||
return log
|
||||
|
||||
def clean_backlog(logname,num):
|
||||
logs=sorted(glob.glob(logname+"_*"))
|
||||
count=len(logs)
|
||||
num=count - num
|
||||
|
||||
for i in range(count):
|
||||
if i>num: break;
|
||||
os.remove(logs[i])
|
||||
print('|---The extra log ['+logs[i]+'] has been deleted!')
|
||||
|
||||
def split_logs(oldFileName,num,site_name):
|
||||
global logsPath
|
||||
errlog_name = build_errlog(site_name)
|
||||
old_errlog = logsPath + errlog_name
|
||||
if not os.path.exists(oldFileName):
|
||||
print('|---'+oldFileName+'file does not exist!')
|
||||
return
|
||||
|
||||
clean_backlog(oldFileName,num)
|
||||
clean_backlog(old_errlog,num)
|
||||
|
||||
newFileName=oldFileName+'_'+time.strftime("%Y-%m-%d_%H%M%S")+'.log'
|
||||
shutil.move(oldFileName,newFileName)
|
||||
new_errlog = build_errlog(site_name)+'_'+time.strftime("%Y-%m-%d_%H%M%S")+'.log'
|
||||
shutil.move(old_errlog, newFileName)
|
||||
if not os.path.exists('/www/server/panel/data/log_not_gzip.pl'):
|
||||
os.system("gzip %s" % newFileName)
|
||||
os.system("gzip %s" % new_errlog)
|
||||
print('|---The log has been cut to:' + newFileName + '.gz')
|
||||
else:
|
||||
print('|---The log has been cut to:'+newFileName+'.log')
|
||||
|
||||
def split_all(save):
|
||||
sites = public.M('sites').field('name').select()
|
||||
for site in sites:
|
||||
oldFileName = logsPath + site['name'] + px
|
||||
split_logs(oldFileName,save,site['name'])
|
||||
|
||||
if __name__ == '__main__':
|
||||
num = int(sys.argv[2])
|
||||
if sys.argv[1].find('ALL') == 0:
|
||||
split_all(num)
|
||||
else:
|
||||
siteName = sys.argv[1]
|
||||
if siteName[-4:] == '.log':
|
||||
siteName = siteName[:-4]
|
||||
elif siteName[-11:] == '-access_log':
|
||||
siteName = siteName.replace("-access_log",'')
|
||||
else:
|
||||
siteName = siteName.replace("_ols.access_log", '')
|
||||
oldFileName = logsPath+sys.argv[1]
|
||||
split_logs(oldFileName,num,siteName)
|
||||
|
||||
if is_nginx:
|
||||
os.system("kill -USR1 `cat /www/server/nginx/logs/nginx.pid`")
|
||||
elif os.path.exists('/usr/local/lsws/bin/lswsctl'):
|
||||
os.system('/usr/local/lsws/bin/lswsctl restart')
|
||||
else:
|
||||
os.system('/etc/init.d/httpd reload')
|
||||
10
script/mail_task.py
Normal file
10
script/mail_task.py
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
#coding: utf-8
|
||||
import os,sys
|
||||
sys.path.insert(0,"/www/server/panel/class/")
|
||||
try:
|
||||
import send_to_user
|
||||
msg=send_to_user.send_to_user()
|
||||
msg.main()
|
||||
except:
|
||||
pass
|
||||
234
script/modify_ports_multiple_services.py
Normal file
234
script/modify_ports_multiple_services.py
Normal file
@@ -0,0 +1,234 @@
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class")
|
||||
|
||||
import public
|
||||
|
||||
|
||||
# ols 修改多服务配置文件
|
||||
def ols_update_config(status, is_restart=True):
|
||||
"""
|
||||
端口关系:
|
||||
8188:80
|
||||
8190:443
|
||||
"""
|
||||
listen_dir = os.path.join(public.get_panel_path(), 'vhost', 'openlitespeed')
|
||||
listen_main = os.path.join(listen_dir, 'listen', '80.conf') # 主监听
|
||||
listen_ssl = os.path.join(listen_dir, 'listen', '443.conf')
|
||||
|
||||
phpmyadmin = [
|
||||
os.path.join(listen_dir, 'listen', '887.conf'),
|
||||
os.path.join(listen_dir, 'listen', '888.conf'),
|
||||
os.path.join(listen_dir, 'phpmyadmin.conf'),
|
||||
os.path.join(listen_dir, 'detail', 'phpmyadmin.conf')
|
||||
]
|
||||
pattern = '*:80'
|
||||
pattern_ANY = '[ANY]:80'
|
||||
pattern_ssl = '*:443'
|
||||
pattern_ssl_ANY = '[ANY]:443'
|
||||
|
||||
if status == 'enable':
|
||||
if os.path.exists(listen_main):
|
||||
content = public.readFile(listen_main)
|
||||
content = content.replace(pattern, '*:8188')
|
||||
content = content.replace(pattern_ANY, '[ANY]:8188')
|
||||
public.writeFile(listen_main, content)
|
||||
|
||||
if os.path.exists(listen_ssl):
|
||||
content = public.readFile(listen_ssl)
|
||||
content = content.replace(pattern_ssl, '*:8190')
|
||||
content = content.replace(pattern_ssl_ANY, '[ANY]:8190')
|
||||
public.writeFile(listen_ssl, content)
|
||||
|
||||
# 取消监听phpmyadmin
|
||||
for path in phpmyadmin:
|
||||
if os.path.exists(path):
|
||||
shutil.move(path, path + '.bar')
|
||||
|
||||
elif status == 'disable':
|
||||
pattern = '*:8188'
|
||||
pattern_ANY = '[ANY]:8188'
|
||||
pattern_ssl = '*:8190'
|
||||
pattern_ssl_ANY = '[ANY]:8190'
|
||||
|
||||
# 恢复服务
|
||||
if os.path.exists(listen_main):
|
||||
content = public.readFile(listen_main)
|
||||
content = content.replace(pattern, '*:80')
|
||||
content = content.replace(pattern_ANY, '[ANY]:80')
|
||||
public.writeFile(listen_main, content)
|
||||
|
||||
if os.path.exists(listen_ssl):
|
||||
content = public.readFile(listen_ssl)
|
||||
content = content.replace(pattern_ssl, '*:443')
|
||||
content = content.replace(pattern_ssl_ANY, '[ANY]:443')
|
||||
public.writeFile(listen_ssl, content)
|
||||
|
||||
for path in phpmyadmin:
|
||||
if os.path.exists(path + '.bar'):
|
||||
shutil.move(path + '.bar', path)
|
||||
|
||||
# 处理用户添加的端口恢复
|
||||
listen_custom_dir = os.path.join(listen_dir, 'listen')
|
||||
if os.path.exists(listen_custom_dir):
|
||||
for filename in os.listdir(listen_custom_dir):
|
||||
file = filename.split('.')[0]
|
||||
if file not in ['80', '443', '887', '888']:
|
||||
content = public.readFile(os.path.join(listen_custom_dir, filename))
|
||||
content = content.replace(pattern, '*:' + file)
|
||||
content = content.replace(pattern_ANY, '*:' + file)
|
||||
public.writeFile(os.path.join(listen_custom_dir, filename), content)
|
||||
|
||||
return True, "The ols configuration modification was successful!"
|
||||
|
||||
|
||||
# apache 修改多服务配置文件
|
||||
def apache_update_config(status, is_restart=True) -> tuple[bool, str]:
|
||||
"""
|
||||
端口关系:
|
||||
8288:80
|
||||
8289:888
|
||||
8290:443
|
||||
"""
|
||||
main_config = '/www/server/apache/conf/httpd.conf' # 主配置文件
|
||||
httpd_vhosts = '/www/server/apache/conf/extra/httpd-vhosts.conf'
|
||||
httpd_ssl = '/www/server/apache/conf/extra/httpd-ssl.conf'
|
||||
phpadmin = os.path.join(public.get_panel_path(), 'vhost', 'apache', 'phpmyadmin.conf')
|
||||
apache_adminer = f"/www/server/panel/vhost/apache/0.adminer.conf"
|
||||
ols_adminer = f"/www/server/panel/vhost/openlitespeed/0.adminer.conf"
|
||||
bar_list = [phpadmin, ols_adminer, apache_adminer]
|
||||
|
||||
port_80 = '80'
|
||||
new_port_80 = '8288'
|
||||
port_888 = '888'
|
||||
new_port_888 = '8289'
|
||||
port_443 = '443'
|
||||
new_port_443 = '8290'
|
||||
|
||||
if status == 'disable':
|
||||
port_80 = '8288'
|
||||
new_port_80 = '80'
|
||||
port_888 = '8289'
|
||||
new_port_888 = '888'
|
||||
port_443 = '8290'
|
||||
new_port_443 = '443'
|
||||
|
||||
# 恢复配置文件
|
||||
for bar in bar_list:
|
||||
if os.path.exists(bar + '.bar'):
|
||||
shutil.move(bar + '.bar', bar)
|
||||
else:
|
||||
# 使配置文件无效
|
||||
for bar in bar_list:
|
||||
if os.path.exists(bar):
|
||||
shutil.move(bar, bar + '.bar')
|
||||
|
||||
# 修改虚拟主机端口配置,匹配所有网站配置文件
|
||||
site_path_list = get_apache_site_conf()
|
||||
for path in site_path_list:
|
||||
if os.path.exists(path):
|
||||
content = public.readFile(path)
|
||||
content = content.replace(f'*:{port_80}', f'*:{new_port_80}')
|
||||
content = content.replace(f'*:{port_443}', f'*:{new_port_443}')
|
||||
content = content.replace(f'[::]:{port_80}', f'[::]:{new_port_80}')
|
||||
content = content.replace(f'[::]:{port_443}', f'[::]:{new_port_443}')
|
||||
public.writeFile(path, content)
|
||||
|
||||
# 处理node
|
||||
site_name = public.M('sites').where('project_type = ?', 'Node').field('name').select()
|
||||
for name in site_name:
|
||||
check_node_project(name, status)
|
||||
|
||||
if os.path.exists(main_config):
|
||||
content = public.readFile(main_config)
|
||||
content = content.replace(f'Listen {port_80}', f'Listen {new_port_80}')
|
||||
content = content.replace(f'Listen {port_443}', f'Listen {new_port_443}')
|
||||
content = content.replace(f'ServerName 0.0.0.0:{port_80}', f'ServerName 0.0.0.0:{new_port_80}')
|
||||
public.writeFile(main_config, content)
|
||||
|
||||
if os.path.exists(httpd_vhosts):
|
||||
content = public.readFile(httpd_vhosts)
|
||||
content = content.replace(f'Listen {port_888}', f'Listen {new_port_888}')
|
||||
content = content.replace(f'*:{port_888}', f'*:{new_port_888}')
|
||||
content = content.replace(f'*:{port_80}', f'*:{new_port_80}')
|
||||
content = content.replace(f'[::]:{port_888}', f'[::]:{new_port_888}')
|
||||
content = content.replace(f'[::]:{port_80}', f'[::]:{new_port_80}')
|
||||
public.writeFile(httpd_vhosts, content)
|
||||
|
||||
if os.path.exists(httpd_ssl):
|
||||
content = public.readFile(httpd_ssl)
|
||||
content = content.replace(f'{port_443}', f'{new_port_443}')
|
||||
public.writeFile(httpd_ssl, content)
|
||||
|
||||
return True, ' '
|
||||
|
||||
# 检测node项目,多服务下默认走nginx
|
||||
def check_node_project(site_name, is_ = 'enable'):
|
||||
conf = os.path.join(public.get_panel_path(),'vhost', 'apache', f'node_{site_name}.conf')
|
||||
|
||||
# 使多服务下apache文件不生效
|
||||
if is_ == 'enable':
|
||||
if os.path.exists(conf):
|
||||
shutil.move(conf, conf + '.barduo')
|
||||
else:
|
||||
if os.path.exists(conf + '.barduo'):
|
||||
shutil.move(conf + '.barduo', conf )
|
||||
return True
|
||||
|
||||
# 获取apache所有站点配置文件
|
||||
def get_apache_site_conf():
|
||||
pata =os.path.join(public.get_panel_path() , 'vhost' ,'apache')
|
||||
if not os.path.exists(pata):
|
||||
return []
|
||||
|
||||
conf_files = []
|
||||
for entry in os.listdir(pata):
|
||||
full_path = os.path.join(pata, entry)
|
||||
|
||||
if os.path.isfile(full_path) and entry.endswith('.conf'):
|
||||
conf_files.append(full_path)
|
||||
|
||||
return conf_files
|
||||
|
||||
def multi_service_check_repair():
|
||||
try:
|
||||
# 尝试重新修改配置
|
||||
ols_update_config('enable')
|
||||
apache_update_config('enable')
|
||||
|
||||
# 重载服务
|
||||
public.kill_process_strictly('litespeed', True) # 去残留
|
||||
setup_path = public.get_setup_path()
|
||||
services = [
|
||||
(
|
||||
f"{setup_path}/nginx/sbin/nginx",
|
||||
"/etc/init.d/nginx reload",
|
||||
"pkill -9 nginx && sleep 1 && /etc/init.d/nginx start"
|
||||
),
|
||||
(
|
||||
f"{setup_path}/apache/bin/apachectl",
|
||||
"/etc/init.d/httpd reload",
|
||||
None
|
||||
),
|
||||
(
|
||||
"/usr/local/lsws/bin/lswsctrl",
|
||||
"/usr/local/lsws/bin/lswsctrl restart",
|
||||
None
|
||||
)
|
||||
]
|
||||
|
||||
for path, cmd, err_cmd in services:
|
||||
if os.path.exists(path):
|
||||
result = public.ExecShell(cmd)
|
||||
if "nginx" in path and result[1].find("nginx.pid") != -1:
|
||||
public.ExecShell(err_cmd)
|
||||
|
||||
public.print_log("The modification of multiple service ports has been completed")
|
||||
except Exception as e:
|
||||
public.print_log(str(e))
|
||||
|
||||
if __name__ == '__main__':
|
||||
multi_service_check_repair()
|
||||
9
script/mysql_quota.py
Normal file
9
script/mysql_quota.py
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/www/server/panel/pyenv/bin/python
|
||||
#coding: utf-8
|
||||
import os,sys
|
||||
os.chdir("/www/server/panel")
|
||||
sys.path.insert(0,"class/")
|
||||
sys.path.insert(0,"/www/server/panel/")
|
||||
from projectModel.quotaModel import main
|
||||
p = main()
|
||||
p.mysql_quota_check()
|
||||
58
script/node_command_executor.py
Normal file
58
script/node_command_executor.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# coding: utf-8
|
||||
|
||||
import sys
|
||||
import os
|
||||
import traceback
|
||||
import argparse
|
||||
|
||||
os.chdir('/www/server/panel/')
|
||||
sys.path.insert(0, "/www/server/panel/class/")
|
||||
sys.path.insert(0, "/www/server/panel/")
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class")
|
||||
import public
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--task_type", type=str, required=True, help="Task type")
|
||||
parser.add_argument("--task_id", type=int, required=True, help="Task ID")
|
||||
parser.add_argument("--exclude_nodes", type=str, help="Exclude nodes")
|
||||
args = parser.parse_args()
|
||||
|
||||
task_type, task_id = args.task_type, args.task_id
|
||||
ex_ids = []
|
||||
if args.exclude_nodes:
|
||||
ex_ids = [int(i) for i in args.exclude_nodes.split(",")]
|
||||
|
||||
|
||||
pid_file = "{}/logs/executor_log/{}_{}_0.pid".format(public.get_panel_path(), task_type, task_id)
|
||||
with open(pid_file, "w") as f:
|
||||
f.write(str(os.getpid()))
|
||||
|
||||
def cmd_task(main_id: int, log_id: int = 0):
|
||||
from mod.project.node.task_flow.command_task import CMDTask
|
||||
_task = CMDTask(main_id, log_id, print)
|
||||
_task.start()
|
||||
|
||||
def file_task(main_id: int, exclude_nodes: list, log_id: int = 0):
|
||||
from mod.project.node.task_flow.file_task import SelfFiletransferTask
|
||||
_task = SelfFiletransferTask(main_id, exclude_nodes, log_id)
|
||||
_task.start()
|
||||
|
||||
def flow_task(main_id: int):
|
||||
from mod.project.node.task_flow.flow import FlowTask
|
||||
_task = FlowTask(main_id)
|
||||
_task.start()
|
||||
|
||||
try:
|
||||
if task_type == "command":
|
||||
cmd_task(task_id)
|
||||
elif task_type == "file":
|
||||
file_task(task_id, ex_ids)
|
||||
elif task_type == "flow":
|
||||
flow_task(task_id)
|
||||
|
||||
except:
|
||||
traceback.print_exc()
|
||||
with open('/tmp/node_flow_task.log', 'w') as f:
|
||||
f.write(traceback.format_exc())
|
||||
os.remove(pid_file)
|
||||
17
script/node_file_transfers.py
Normal file
17
script/node_file_transfers.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# coding: utf-8
|
||||
|
||||
import sys
|
||||
import os
|
||||
import traceback
|
||||
|
||||
os.chdir('/www/server/panel/')
|
||||
sys.path.insert(0, "/www/server/panel/class/")
|
||||
sys.path.insert(0, "/www/server/panel/")
|
||||
|
||||
try:
|
||||
from mod.project.node.filetransfer import run_file_transfer_task
|
||||
run_file_transfer_task(int(sys.argv[1]))
|
||||
except:
|
||||
traceback.print_exc()
|
||||
with open('/tmp/node_file_transfer.pl', 'w') as f:
|
||||
f.write(traceback.format_exc())
|
||||
20
script/node_monitor.py
Normal file
20
script/node_monitor.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# coding: utf-8
|
||||
|
||||
import sys
|
||||
import os
|
||||
import traceback
|
||||
import time
|
||||
|
||||
os.chdir('/www/server/panel/')
|
||||
sys.path.insert(0, "/www/server/panel/class/")
|
||||
sys.path.insert(0, "/www/server/panel/")
|
||||
|
||||
try:
|
||||
from mod.project.node.nodeutil import monitor_all_node_status
|
||||
|
||||
monitor_all_node_status()
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
with open('/tmp/node_monitor.pl', 'w') as f:
|
||||
f.write(str(int(time.time())))
|
||||
f.write("{}".format(traceback.format_exc()))
|
||||
35
script/nodejs-service.py
Normal file
35
script/nodejs-service.py
Normal file
@@ -0,0 +1,35 @@
|
||||
#!/www/server/panel/pyenv/bin/python
|
||||
#coding: utf-8
|
||||
import os,sys
|
||||
os.chdir("/www/server/panel")
|
||||
sys.path.insert(0,"class/")
|
||||
|
||||
from projectModel.nodejsModel import main
|
||||
import public
|
||||
p = main()
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: nodejs-service [project_name] [start|stop|restart]")
|
||||
sys.exit()
|
||||
get = public.dict_obj()
|
||||
get.project_name = sys.argv[1].strip()
|
||||
action = sys.argv[2].strip()
|
||||
if action not in ['start','stop','restart','status']:
|
||||
print("Usage: nodejs-service [project_name] [start|stop|restart]")
|
||||
sys.exit()
|
||||
|
||||
if action == 'start':
|
||||
res = p.start_project(get)
|
||||
elif action == 'stop':
|
||||
res = p.stop_project(get)
|
||||
elif action == 'restart':
|
||||
res = p.restart_project(get)
|
||||
elif action == 'status':
|
||||
res = p.get(get)
|
||||
|
||||
if res['status']:
|
||||
print("\033[1;32mSUCCESS: " + res['data'] + "\033[0m")
|
||||
else:
|
||||
print("\033[1;31mERROR: " + res['error_msg'] + "\033[0m")
|
||||
|
||||
|
||||
43
script/open_malicious_ip.sh
Normal file
43
script/open_malicious_ip.sh
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
configure_logging() {
|
||||
sudo tee /etc/rsyslog.d/ip-daily-log.conf <<EOF
|
||||
:msg, contains, "DAILY-IP: " -/var/log/IP-DAILY-LOG.log
|
||||
& stop
|
||||
EOF
|
||||
|
||||
sudo tee /etc/logrotate.d/ip-daily-log <<EOF
|
||||
/var/log/IP-DAILY-LOG.log {
|
||||
daily
|
||||
rotate 3 # 仅保留3天历史
|
||||
missingok
|
||||
nocompress # 无需压缩
|
||||
notifempty
|
||||
sharedscripts
|
||||
postrotate
|
||||
systemctl reload rsyslog >/dev/null 2>&1
|
||||
endscript
|
||||
}
|
||||
EOF
|
||||
sudo systemctl restart rsyslog
|
||||
}
|
||||
|
||||
|
||||
sudo iptables -C IN_BT_log -j IN_BT_log_DAILY > /dev/null 2>&1
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "rules 'iptables -I IN_BT_log -j IN_BT_log_DAILY' Exiting..."
|
||||
exit 0
|
||||
else
|
||||
echo "add rule..."
|
||||
sudo iptables -N IN_BT_log_DAILY
|
||||
sudo iptables -I IN_BT_log -j IN_BT_log_DAILY
|
||||
sudo iptables -A IN_BT_log_DAILY -m recent --name DAILY_IPS --rcheck --seconds 86400 -j RETURN
|
||||
sudo iptables -A IN_BT_log_DAILY -m recent --name DAILY_IPS --set -j LOG --log-prefix "DAILY-IP: " --log-level 4
|
||||
|
||||
ipset create in_bt_malicious_ipset hash:ip maxelem 1000000 timeout 0;
|
||||
iptables -A IN_BT_ip -m set --match-set in_bt_malicious_ipset src -j DROP
|
||||
systemctl reload BT-FirewallServices
|
||||
configure_logging
|
||||
echo "add rule finish..."
|
||||
fi
|
||||
14
script/panel_ssl_task.py
Normal file
14
script/panel_ssl_task.py
Normal file
@@ -0,0 +1,14 @@
|
||||
#coding: utf-8
|
||||
import os,sys,time
|
||||
os.chdir('/www/server/panel/')
|
||||
sys.path.insert(0,"class/")
|
||||
import setPanelLets,public
|
||||
from json import loads
|
||||
lets_info = public.readFile("/www/server/panel/ssl/lets.info")
|
||||
if lets_info:
|
||||
lets_info = loads(lets_info)
|
||||
res = setPanelLets.setPanelLets().check_cert_update(lets_info['domain'])
|
||||
if res['status'] and res['msg'] == '1':
|
||||
strTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time())))
|
||||
public.writeFile("/tmp/panelSSL.pl","{} Panel certificate updated successfully\n".format(strTime),"a+")
|
||||
public.writeFile('/www/server/panel/data/reload.pl',"1")
|
||||
114
script/polkit_upgrade.py
Normal file
114
script/polkit_upgrade.py
Normal file
@@ -0,0 +1,114 @@
|
||||
#coding: utf-8
|
||||
# +-------------------------------------------------------------------
|
||||
# | YakPanel
|
||||
# +-------------------------------------------------------------------
|
||||
# | Copyright (c) 2015-2099 YakPanel(www.yakpanel.com) All rights reserved.
|
||||
# +-------------------------------------------------------------------
|
||||
# | Author: hwliang <hwl@yakpanel.com>
|
||||
# +-------------------------------------------------------------------
|
||||
|
||||
#--------------------------------
|
||||
# 修复polkit提权漏洞(CVE-2021-4034)
|
||||
#--------------------------------
|
||||
|
||||
import os,sys
|
||||
os.chdir("/www/server/panel")
|
||||
sys.path.insert(0,'class/')
|
||||
import public
|
||||
upgrade_log_file = '/www/server/panel/logs/upgrade_polkit.log'
|
||||
log_msg = "A polkit (CVE-2021-4034) privilege escalation vulnerability has been detected in the system and has been fixed for you!"
|
||||
|
||||
|
||||
def write_log(msg):
|
||||
global upgrade_log_file
|
||||
public.writeFile(upgrade_log_file,"[{}] - {}".format(public.format_date(),msg),'a+')
|
||||
|
||||
def is_yum():
|
||||
if os.path.exists('/usr/bin/yum'):
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_dnf():
|
||||
if os.path.exists('/usr/bin/dnf'):
|
||||
return True
|
||||
return False
|
||||
|
||||
def is_apt():
|
||||
if os.path.exists('/usr/bin/apt'):
|
||||
return True
|
||||
return False
|
||||
|
||||
def upgrade_by_yum():
|
||||
global upgrade_log_file,log_msg
|
||||
res = public.ExecShell("rpm -q polkit")[0]
|
||||
if res.startswith('polkit-'):
|
||||
os.system("yum -y update polkit &> {}".format(upgrade_log_file))
|
||||
res2 = public.ExecShell("rpm -q polkit")[0]
|
||||
if res == res2:
|
||||
write_log("Repair failed, please execute the command manually: yum -y update polkit")
|
||||
return False
|
||||
public.WriteLog('Vulnerability Repair',log_msg)
|
||||
return True
|
||||
return False
|
||||
|
||||
def upgrade_by_dnf():
|
||||
global upgrade_log_file,log_msg
|
||||
res = public.ExecShell("rpm -q polkit")[0]
|
||||
if res.startswith('polkit-'):
|
||||
os.system("dnf -y update polkit &> {}".format(upgrade_log_file))
|
||||
res2 = public.ExecShell("rpm -q polkit")[0]
|
||||
if res == res2:
|
||||
write_log("Repair failed, please execute the command manually: dnf -y update polkit")
|
||||
return False
|
||||
public.WriteLog('Vulnerability Repair',log_msg)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def upgrade_by_apt():
|
||||
global upgrade_log_file,log_msg
|
||||
res = public.ExecShell("dpkg -l policykit-1|grep policykit-1|awk '{print $3}'")[0]
|
||||
if res.startswith('0.105'):
|
||||
os.system("apt-get -y install policykit-1 &> {}".format(upgrade_log_file))
|
||||
res2 = public.ExecShell("dpkg -l policykit-1|grep policykit-1|awk '{print $3}'")[0]
|
||||
if res == res2:
|
||||
write_log("Repair failed, please execute the command manually: apt-get -y install policykit-1")
|
||||
return False
|
||||
public.WriteLog('Vulnerability Repair',log_msg)
|
||||
return True
|
||||
return False
|
||||
|
||||
def check():
|
||||
tip_file = '/www/server/panel/data/upgrade_polkit.pl'
|
||||
if os.path.exists(tip_file):
|
||||
return
|
||||
write_log("Fixing the privilege escalation vulnerability of polkit (CVE-2021-4034)...")
|
||||
if is_yum():
|
||||
upgrade_by_yum()
|
||||
elif is_dnf():
|
||||
upgrade_by_dnf()
|
||||
elif is_apt():
|
||||
upgrade_by_apt()
|
||||
else:
|
||||
return
|
||||
|
||||
public.writeFile(tip_file,'True')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
tip_file = '/www/server/panel/data/upgrade_polkit_run.pl'
|
||||
if os.path.exists(tip_file):
|
||||
print("The program is running, exit!")
|
||||
sys.exit(1)
|
||||
|
||||
public.writeFile(tip_file,'True')
|
||||
try:
|
||||
check()
|
||||
except:
|
||||
pass
|
||||
finally:
|
||||
if os.path.exists(tip_file): os.remove(tip_file)
|
||||
|
||||
|
||||
|
||||
|
||||
325
script/process_network_total.py
Normal file
325
script/process_network_total.py
Normal file
@@ -0,0 +1,325 @@
|
||||
#coding: utf-8
|
||||
#-------------------------------------------------------------------
|
||||
# YakPanel
|
||||
#-------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 YakPanel(www.yakpanel.com) All rights reserved.
|
||||
#-------------------------------------------------------------------
|
||||
# Author: hwliang<hwl@yakpanel.com>
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
import sys
|
||||
import time
|
||||
import os
|
||||
import struct
|
||||
import base64
|
||||
base64.b64encode
|
||||
|
||||
os.chdir('/www/server/panel')
|
||||
if 'class/' in sys.path: sys.path.insert(0,"class/")
|
||||
import copy
|
||||
# try:
|
||||
# import pcap
|
||||
# except ImportError:
|
||||
# # 标记只安装一次
|
||||
# tip_file = '/www/server/panel/install/tip.json'
|
||||
# if not os.path.exists(tip_file):
|
||||
# if os.path.exists('/usr/bin/apt'):
|
||||
# os.system("apt install libpcap-dev -y")
|
||||
# elif os.path.exists('/usr/bin/dnf'):
|
||||
# red_file = '/etc/redhat-release'
|
||||
# if os.path.exists(red_file):
|
||||
# f = open(red_file,'r')
|
||||
# red_body = f.read()
|
||||
# f.close()
|
||||
# if red_body.find('CentOS Linux release 8.') != -1:
|
||||
# rpm_file = '/root/libpcap-1.9.1.rpm'
|
||||
# down_url = "wget -O {} https://node.yakpanel.com/src/libpcap-devel-1.9.1-5.el8.x86_64.rpm --no-check-certificate -T 10".format(rpm_file)
|
||||
# if os.path.exists(rpm_file):
|
||||
# os.system(down_url)
|
||||
# os.system("rpm -ivh {}".format(rpm_file))
|
||||
# if os.path.exists(rpm_file): os.remove(rpm_file)
|
||||
# else:
|
||||
# os.system("dnf install libpcap-devel -y")
|
||||
# else:
|
||||
# os.system("dnf install libpcap-devel -y")
|
||||
# elif os.path.exists('/usr/bin/yum'):
|
||||
# os.system("yum install libpcap-devel -y")
|
||||
# # os.system("btpip install pypcap")
|
||||
# # 写入标记文件
|
||||
# os.system("echo True > {}".format(tip_file))
|
||||
# # try:
|
||||
# # import pcap
|
||||
# # except ImportError:
|
||||
# # print("3 pypcap module install failed. 3")
|
||||
# # sys.exit()
|
||||
|
||||
|
||||
class process_network_total:
|
||||
__pid_file = 'logs/process_network_total.pid'
|
||||
__inode_list = {}
|
||||
__net_process_list = {}
|
||||
__net_process_size = {}
|
||||
__last_stat = 0
|
||||
__last_write_time = 0
|
||||
__end_time = 0
|
||||
|
||||
# def start(self,timeout = 0):
|
||||
# '''
|
||||
# @name 启动进程网络监控
|
||||
# @author hwliang<2021-09-13>
|
||||
# @param timeout<int> 结束时间(秒),0表示持久运行,默认为0
|
||||
# @return void
|
||||
# '''
|
||||
# stime = time.time()
|
||||
# self.__end_time = timeout + stime
|
||||
# self.__last_stat = stime
|
||||
# try:
|
||||
# p = pcap.pcap() # 监听所有网卡
|
||||
# p.setfilter('tcp') # 只监听TCP数据包
|
||||
# for p_time,p_data in p:
|
||||
# self.handle_packet(p_data)
|
||||
# # 过期停止监听
|
||||
# if timeout > 0:
|
||||
# if p_time > self.__end_time:
|
||||
# self.rm_pid_file()
|
||||
# break
|
||||
# except:
|
||||
# self.rm_pid_file()
|
||||
|
||||
def handle_packet(self, pcap_data):
|
||||
'''
|
||||
@name 处理pcap数据包
|
||||
@author hwliang<2021-09-12>
|
||||
@param pcap_data<bytes> pcap数据包
|
||||
@return void
|
||||
'''
|
||||
# 获取IP协议头
|
||||
ip_header = pcap_data[14:34]
|
||||
# 解析src/dst地址
|
||||
src_ip = ip_header[12:16]
|
||||
dst_ip = ip_header[16:20]
|
||||
# 解析sport/dport端口
|
||||
src_port = pcap_data[34:36]
|
||||
dst_port = pcap_data[36:38]
|
||||
|
||||
src = src_ip + b':' + src_port
|
||||
dst = dst_ip + b':' + dst_port
|
||||
# 计算数据包长度
|
||||
pack_size = len(pcap_data)
|
||||
# 统计进程流量
|
||||
self.total_net_process(dst,src,pack_size)
|
||||
|
||||
def total_net_process(self,dst,src,pack_size):
|
||||
'''
|
||||
@name 统计进程流量
|
||||
@author hwliang<2021-09-13>
|
||||
@param dst<bytes> 目标地址
|
||||
@param src<bytes> 源地址
|
||||
@param pack_size<int> 数据包长度
|
||||
@return void
|
||||
'''
|
||||
self.get_tcp_stat()
|
||||
direction = None
|
||||
mtime = time.time()
|
||||
if dst in self.__net_process_list:
|
||||
pid = self.__net_process_list[dst]
|
||||
direction = 'down'
|
||||
elif src in self.__net_process_list:
|
||||
pid = self.__net_process_list[src]
|
||||
direction = 'up'
|
||||
else:
|
||||
if mtime - self.__last_stat > 3:
|
||||
self.__last_stat = mtime
|
||||
self.get_tcp_stat(True)
|
||||
if dst in self.__net_process_list:
|
||||
pid = self.__net_process_list[dst]
|
||||
direction = 'down'
|
||||
elif src in self.__net_process_list:
|
||||
pid = self.__net_process_list[src]
|
||||
direction = 'up'
|
||||
|
||||
if not direction: return False
|
||||
if not pid: return False
|
||||
if not pid in self.__net_process_size:
|
||||
self.__net_process_size[pid] = {}
|
||||
self.__net_process_size[pid]['down'] = 0
|
||||
self.__net_process_size[pid]['up'] = 0
|
||||
self.__net_process_size[pid]['up_package'] = 0
|
||||
self.__net_process_size[pid]['down_package'] = 0
|
||||
|
||||
self.__net_process_size[pid][direction] += pack_size
|
||||
self.__net_process_size[pid][direction + '_package'] += 1
|
||||
|
||||
# 写入到文件
|
||||
if mtime - self.__last_write_time > 1:
|
||||
self.__last_write_time = mtime
|
||||
self.write_net_process()
|
||||
|
||||
def write_net_process(self):
|
||||
'''
|
||||
@name 写入进程流量
|
||||
@author hwliang<2021-09-13>
|
||||
@return void
|
||||
'''
|
||||
w_file = '/dev/shm/bt_net_process'
|
||||
process_size = copy.deepcopy(self.__net_process_size)
|
||||
net_process = []
|
||||
for pid in process_size.keys():
|
||||
net_process.append(str(pid) + " " + str(process_size[pid]['down']) + " " + str(process_size[pid]['up']) + " " + str(process_size[pid]['down_package']) + " " + str(process_size[pid]['up_package']))
|
||||
|
||||
f = open(w_file,'w+',encoding='utf-8')
|
||||
f.write('\n'.join(net_process))
|
||||
f.close()
|
||||
|
||||
def hex_to_ip(self, hex_ip):
|
||||
'''
|
||||
@name 将16进制的IP地址转换为字符串IP地址
|
||||
@author hwliang<2021-09-13>
|
||||
@param hex_ip<string> 16进制的IP地址:16进程端口
|
||||
@return tuple(ip<str>,port<int>) IP地址,端口
|
||||
'''
|
||||
hex_ip,hex_port = hex_ip.split(':')
|
||||
ip = '.'.join([str(int(hex_ip[i:i+2], 16)) for i in range(0, len(hex_ip), 2)][::-1])
|
||||
port = int(hex_port, 16)
|
||||
return ip,port
|
||||
|
||||
def get_tcp_stat(self,force = False):
|
||||
'''
|
||||
@name 获取当前TCP连接状态表
|
||||
@author hwliang<2021-09-13>
|
||||
@param force<bool> 是否强制刷新
|
||||
@return dict
|
||||
'''
|
||||
if not force and self.__net_process_list: return self.__net_process_list
|
||||
self.__net_process_list = {}
|
||||
tcp_stat_file = '/proc/net/tcp'
|
||||
tcp_stat = open(tcp_stat_file, 'rb')
|
||||
tcp_stat_list = tcp_stat.read().decode('utf-8').split('\n')
|
||||
tcp_stat.close()
|
||||
tcp_stat_list = tcp_stat_list[1:]
|
||||
if force: self.get_process_inodes(force)
|
||||
for i in tcp_stat_list:
|
||||
tcp_tmp = i.split()
|
||||
if len(tcp_tmp) < 10: continue
|
||||
inode = tcp_tmp[9]
|
||||
if inode == '0': continue
|
||||
local_ip,local_port = self.hex_to_ip(tcp_tmp[1])
|
||||
if local_ip == '127.0.0.1': continue
|
||||
remote_ip,remote_port = self.hex_to_ip(tcp_tmp[2])
|
||||
if local_ip == remote_ip: continue
|
||||
if remote_ip == '0.0.0.0': continue
|
||||
|
||||
pid = self.inode_to_pid(inode,force)
|
||||
if not pid: continue
|
||||
|
||||
key = self.get_ip_pack(local_ip) + b':' + self.get_port_pack(local_port)
|
||||
self.__net_process_list[key] = pid
|
||||
return self.__net_process_list
|
||||
|
||||
|
||||
def get_port_pack(self,port):
|
||||
'''
|
||||
@name 将端口转换为字节流
|
||||
@author hwliang<2021-09-13>
|
||||
@param port<int> 端口
|
||||
@return bytes
|
||||
'''
|
||||
return struct.pack('H',int(port))[::-1]
|
||||
|
||||
def get_ip_pack(self,ip):
|
||||
'''
|
||||
@name 将IP地址转换为字节流
|
||||
@author hwliang<2021-09-13>
|
||||
@param ip<str> IP地址
|
||||
@return bytes
|
||||
'''
|
||||
ip_arr = ip.split('.')
|
||||
ip_pack = b''
|
||||
for i in ip_arr:
|
||||
ip_pack += struct.pack('B',int(i))
|
||||
return ip_pack
|
||||
|
||||
def inode_to_pid(self,inode,force = False):
|
||||
'''
|
||||
@name 将inode转换为进程ID
|
||||
@author hwliang<2021-09-13>
|
||||
@param inode<string> inode
|
||||
@param force<bool> 是否强制刷新
|
||||
@return int
|
||||
'''
|
||||
inode_list = self.get_process_inodes()
|
||||
if inode in inode_list:
|
||||
return inode_list[inode]
|
||||
return None
|
||||
|
||||
def get_process_inodes(self,force = False):
|
||||
'''
|
||||
@name 获取进程inode列表
|
||||
@author hwliang<2021-09-13>
|
||||
@param force<bool> 是否强制刷新
|
||||
@return dict
|
||||
'''
|
||||
if not force and self.__inode_list: return self.__inode_list
|
||||
proc_path = '/proc'
|
||||
inode_list = {}
|
||||
for pid in os.listdir(proc_path):
|
||||
try:
|
||||
if not pid.isdigit(): continue
|
||||
inode_path = proc_path + '/' + pid + '/fd'
|
||||
for fd in os.listdir(inode_path):
|
||||
try:
|
||||
fd_file = inode_path + '/' + fd
|
||||
fd_link = os.readlink(fd_file)
|
||||
if fd_link.startswith('socket:['):
|
||||
inode = fd_link[8:-1]
|
||||
inode_list[inode] = pid
|
||||
except:
|
||||
continue
|
||||
except:
|
||||
continue
|
||||
self.__inode_list = inode_list
|
||||
return inode_list
|
||||
|
||||
def get_process_name(self,pid):
|
||||
'''
|
||||
@name 获取进程名称
|
||||
@author hwliang<2021-09-13>
|
||||
@param pid<str> 进程ID
|
||||
@return str
|
||||
'''
|
||||
pid_path = '/proc/' + pid + '/comm'
|
||||
if not os.path.exists(pid_path): return ''
|
||||
pid_file = open(pid_path, 'rb')
|
||||
pid_name = pid_file.read().decode('utf-8').strip()
|
||||
pid_file.close()
|
||||
return pid_name
|
||||
|
||||
|
||||
def write_pid(self):
|
||||
'''
|
||||
@name 写入进程ID到PID文件
|
||||
@author hwliang<2021-09-13>
|
||||
@return void
|
||||
'''
|
||||
self_pid = os.getpid()
|
||||
pid_file = open(self.__pid_file,'w')
|
||||
pid_file.write(str(self_pid))
|
||||
pid_file.close()
|
||||
|
||||
def rm_pid_file(self):
|
||||
'''
|
||||
@name 删除进程pid文件
|
||||
@author hwliang<2021-09-13>
|
||||
@return void
|
||||
'''
|
||||
if os.path.exists(self.__pid_file):
|
||||
os.remove(self.__pid_file)
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) > 1:
|
||||
timeout = int(sys.argv[-1])
|
||||
else:
|
||||
timeout = 0
|
||||
p = process_network_total()
|
||||
p.write_pid()
|
||||
# p.start(timeout)
|
||||
82
script/project_daemon.py
Normal file
82
script/project_daemon.py
Normal file
@@ -0,0 +1,82 @@
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
panel_path = '/www/server/panel'
|
||||
os.chdir(panel_path)
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class")
|
||||
if "/www/server/panel" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel")
|
||||
|
||||
import public
|
||||
|
||||
|
||||
def PythonDaemons():
|
||||
"""
|
||||
@name Python 项目守护进程
|
||||
@author baozi@yakpanel.com
|
||||
@time 2023-10-21
|
||||
"""
|
||||
if public.M('sites').where('project_type=?', ('Python',)).count() >= 1:
|
||||
project_info = public.M('sites').where('project_type=?', ('Python',)).select()
|
||||
for i in project_info:
|
||||
try:
|
||||
from projectModelV2 import pythonModel
|
||||
# sites 表中的 project_config
|
||||
i['project_config'] = json.loads(i['project_config'])
|
||||
# auto_run, 自启,守护
|
||||
if 'auto_run' in i['project_config'] and i['project_config']['auto_run'] in ["1", 1, True, "true"]:
|
||||
python_obj = pythonModel.main()
|
||||
# 项目的运行状态是否为运行中
|
||||
if python_obj.get_project_run_state(project_name=i['name']):
|
||||
continue
|
||||
# 项目是否被用户停止了, 则不再自动启动
|
||||
if python_obj.is_stop_by_user(i["id"]):
|
||||
continue
|
||||
get = public.dict_obj()
|
||||
get.name = i['name']
|
||||
# StartProject
|
||||
python_obj.StartProject(get)
|
||||
public.WriteLog('Project Daemon', 'Python project [{}] auto start!'.format(i['name']))
|
||||
except Exception:
|
||||
import traceback
|
||||
print("Python Daemon Error: {}".format(traceback.format_exc()))
|
||||
continue
|
||||
|
||||
|
||||
def NodeDaemons():
|
||||
if public.M('sites').where('project_type=?', ('Node',)).count() >= 1:
|
||||
project_info = public.M('sites').where('project_type=?', ('Node',)).select()
|
||||
from projectModelV2 import nodejsModel
|
||||
for i in project_info:
|
||||
try:
|
||||
i['project_config'] = json.loads(i['project_config'])
|
||||
if 'is_power_on' in i['project_config'] and i['project_config']['is_power_on'] in ["1", 1, True, "true"]: # node 默认为is_power_on
|
||||
node_obj = nodejsModel.main()
|
||||
get = public.dict_obj()
|
||||
get.project_name = i['name']
|
||||
if node_obj._get_project_run_state(get):
|
||||
continue
|
||||
# StartProject
|
||||
node_obj.start_project(get)
|
||||
public.WriteLog('Project Daemon', 'Node.js project [{}] auto start!'.format(i['name']))
|
||||
except Exception:
|
||||
import traceback
|
||||
print("Node.js Daemon Error: {}".format(traceback.format_exc()))
|
||||
continue
|
||||
|
||||
|
||||
def GoDaemons():
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
# go, java, python, nodejs...
|
||||
GoDaemons()
|
||||
NodeDaemons()
|
||||
PythonDaemons()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
43
script/push_msg.py
Normal file
43
script/push_msg.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# coding: utf-8
|
||||
import json
|
||||
import sys, os, time
|
||||
|
||||
os.chdir('/www/server/panel/')
|
||||
sys.path.insert(0, "class/")
|
||||
sys.path.insert(0, "/www/server/panel/")
|
||||
import public
|
||||
import http_requests
|
||||
|
||||
http_requests.DEFAULT_TYPE = 'src'
|
||||
os.environ['BT_TASK'] = '1'
|
||||
|
||||
def main():
|
||||
try:
|
||||
import panelPush
|
||||
push = panelPush.panelPush()
|
||||
push.start()
|
||||
|
||||
# msg bind check
|
||||
flag = False
|
||||
sender = os.path.join(public.get_panel_path(), "data/mod_push_data/sender.json")
|
||||
if os.path.exists(sender):
|
||||
sender_info = public.readFile(sender)
|
||||
try:
|
||||
sender_info = json.loads(sender_info)
|
||||
except:
|
||||
pass
|
||||
if sender_info and isinstance(sender_info, list):
|
||||
for send in sender_info:
|
||||
if send.get("data") != {}:
|
||||
flag = True # has bind alarm
|
||||
break
|
||||
if flag:
|
||||
from mod.base.push_mod import PushSystem
|
||||
PushSystem().run()
|
||||
|
||||
except Exception as e:
|
||||
pass
|
||||
os.system("echo no,{},{} > /tmp/push.pl".format(time.time(), e))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
40
script/py_project_env.py
Normal file
40
script/py_project_env.py
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/www/sererv/panel/penv/bin/python3
|
||||
# coding: utf-8
|
||||
# -----------------------------
|
||||
# yakpanel Python项目准备脚本
|
||||
# -----------------------------
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
|
||||
os.chdir("/www/server/panel")
|
||||
if "/www/server/panel" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel")
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class")
|
||||
if "/www/server/panel/class_v2" not in sys.path:
|
||||
sys.path.insert(0, "/www/server/panel/class_v2")
|
||||
|
||||
|
||||
import public
|
||||
from projectModelV2.pythonModel import main as pythonMod
|
||||
|
||||
|
||||
def main(pj_id: int):
|
||||
project_info = public.M('sites').where('id=? ', (pj_id,)).find()
|
||||
if not isinstance(project_info, dict):
|
||||
print("project not found!")
|
||||
values = json.loads(project_info["project_config"])
|
||||
pythonMod().simple_prep_env(values)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) > 1:
|
||||
project_id = sys.argv[1]
|
||||
try:
|
||||
project_id = int(project_id)
|
||||
except:
|
||||
exit(1)
|
||||
else:
|
||||
main(project_id)
|
||||
BIN
script/pycountry-24.6.1-py3-none-any.whl
Normal file
BIN
script/pycountry-24.6.1-py3-none-any.whl
Normal file
Binary file not shown.
BIN
script/pyroute2-0.7.12-py3-none-any.whl
Normal file
BIN
script/pyroute2-0.7.12-py3-none-any.whl
Normal file
Binary file not shown.
100
script/rememory.sh
Normal file
100
script/rememory.sh
Normal file
@@ -0,0 +1,100 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
#+------------------------------------
|
||||
#+ 宝塔释放内存脚本
|
||||
#+------------------------------------
|
||||
|
||||
endDate=`date +"%Y-%m-%d %H:%M:%S"`
|
||||
log="Free up memory!"
|
||||
echo "★[$endDate] $log"
|
||||
echo '----------------------------------------------------------------------------'
|
||||
|
||||
if [ -f "/etc/init.d/php-fpm-52" ];then
|
||||
/etc/init.d/php-fpm-52 reload
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/php-fpm-53" ];then
|
||||
/etc/init.d/php-fpm-53 reload
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/php-fpm-54" ];then
|
||||
/etc/init.d/php-fpm-54 reload
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/php-fpm-55" ];then
|
||||
/etc/init.d/php-fpm-55 reload
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/php-fpm-56" ];then
|
||||
/etc/init.d/php-fpm-56 reload
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/php-fpm-70" ];then
|
||||
/etc/init.d/php-fpm-70 reload
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/php-fpm-71" ];then
|
||||
/etc/init.d/php-fpm-71 reload
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/php-fpm-72" ];then
|
||||
/etc/init.d/php-fpm-72 reload
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/php-fpm-73" ];then
|
||||
/etc/init.d/php-fpm-73 reload
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/php-fpm-74" ];then
|
||||
/etc/init.d/php-fpm-74 reload
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/php-fpm-80" ];then
|
||||
/etc/init.d/php-fpm-80 reload
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/php-fpm-81" ];then
|
||||
/etc/init.d/php-fpm-81 reload
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/php-fpm-82" ];then
|
||||
/etc/init.d/php-fpm-82 reload
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/php-fpm-83" ];then
|
||||
/etc/init.d/php-fpm-83 reload
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/php-fpm-84" ];then
|
||||
/etc/init.d/php-fpm-84 reload
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/php-fpm-85" ];then
|
||||
/etc/init.d/php-fpm-85 reload
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/mysqld" ];then
|
||||
/etc/init.d/mysqld reload
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/nginx" ];then
|
||||
/etc/init.d/nginx reload
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/httpd" ];then
|
||||
/etc/init.d/httpd graceful
|
||||
fi
|
||||
|
||||
if [ -f "/etc/init.d/pure-ftpd" ];then
|
||||
pkill -9 pure-ftpd
|
||||
sleep 0.3
|
||||
/etc/init.d/pure-ftpd start 2>/dev/null
|
||||
fi
|
||||
|
||||
sync
|
||||
sleep 2
|
||||
sync
|
||||
echo 3 > /proc/sys/vm/drop_caches
|
||||
|
||||
echo '----------------------------------------------------------------------------'
|
||||
63
script/restart_project.py
Normal file
63
script/restart_project.py
Normal file
@@ -0,0 +1,63 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# -----------------------------
|
||||
# Website Project Restart Script
|
||||
# -----------------------------
|
||||
# author: YakPanel
|
||||
|
||||
import os
|
||||
import sys
|
||||
from importlib import import_module
|
||||
from typing import Optional, Any
|
||||
|
||||
if "/www/server/panel" not in sys.path:
|
||||
sys.path.insert(0, '/www/server/panel')
|
||||
if "/www/server/panel/class" not in sys.path:
|
||||
sys.path.insert(0, '/www/server/panel/class')
|
||||
if "/www/server/panel/class_v2" not in sys.path:
|
||||
sys.path.insert(0, '/www/server/panel/class_v2')
|
||||
|
||||
os.chdir('/www/server/panel')
|
||||
|
||||
import public
|
||||
|
||||
|
||||
def get_action_model_obj(model_name: str) -> Optional[Any]:
|
||||
try:
|
||||
if model_name in "java" and os.path.exists("/www/server/panel/mod/project/java/projectMod.py"):
|
||||
model = import_module("mod.project.java.projectMod")
|
||||
else:
|
||||
model = import_module("projectModelV2." + model_name + "Model")
|
||||
except:
|
||||
return None
|
||||
|
||||
if not hasattr(model, "main"):
|
||||
return None
|
||||
main_class = getattr(model, "main")
|
||||
if not callable(main_class):
|
||||
return None
|
||||
return main_class()
|
||||
|
||||
|
||||
def restart_project_based_on_model(model_name: str, project_name: str):
|
||||
try:
|
||||
print(public.lang(f"Starting to restart {model_name} project [{project_name}]..."))
|
||||
model_obj = get_action_model_obj(model_name)
|
||||
if not model_obj:
|
||||
print(public.lang(f"Failed to load operation class for model {model_name}."))
|
||||
return
|
||||
res = model_obj.restart_project(public.to_dict_obj({
|
||||
"project_name": project_name
|
||||
}))
|
||||
if res['status'] != 0:
|
||||
print(public.lang(f"Failed to restart project [{project_name}]. {res.get('msg', '')}"))
|
||||
return
|
||||
print(public.lang("Project [{}] Restarted Successfully!").format(project_name))
|
||||
except Exception as e:
|
||||
print(public.get_error_info())
|
||||
print("Error: " + str(e))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: python restart_project.py <model_name> <project_name>")
|
||||
restart_project_based_on_model(sys.argv[1], sys.argv[2])
|
||||
816
script/restart_services.py
Normal file
816
script/restart_services.py
Normal file
@@ -0,0 +1,816 @@
|
||||
# coding: utf-8
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from functools import wraps, partial
|
||||
from typing import Optional, Dict, Callable
|
||||
|
||||
import fcntl
|
||||
|
||||
os.chdir("/www/server/panel")
|
||||
sys.path.insert(0, "class/")
|
||||
sys.path.insert(0, "class_v2/")
|
||||
|
||||
SETUP_PATH = "/www/server"
|
||||
DATA_PATH = os.path.join(SETUP_PATH, "panel/data")
|
||||
PLUGINS_PATH = os.path.join(SETUP_PATH, "panel/plugin")
|
||||
|
||||
DAEMON_SERVICE = os.path.join(DATA_PATH, "daemon_service.pl")
|
||||
DAEMON_SERVICE_LOCK = os.path.join(DATA_PATH, "daemon_service_lock.pl")
|
||||
DAEMON_RESTART_RECORD = os.path.join(DATA_PATH, "daemon_restart_record.pl")
|
||||
MANUAL_FLAG = os.path.join(SETUP_PATH, "panel/data/mod_push_data", "manual_flag.pl")
|
||||
|
||||
|
||||
def read_file(filename: str):
|
||||
fp = None
|
||||
try:
|
||||
fp = open(filename, "rb")
|
||||
f_body_bytes: bytes = fp.read()
|
||||
f_body = f_body_bytes.decode("utf-8", errors='ignore')
|
||||
fp.close()
|
||||
return f_body
|
||||
except Exception:
|
||||
return False
|
||||
finally:
|
||||
if fp and not fp.closed:
|
||||
fp.close()
|
||||
|
||||
|
||||
def run_command(cmd, timeout=5) -> str:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
timeout=timeout,
|
||||
check=False
|
||||
)
|
||||
output = result.stdout.strip()
|
||||
return output if output else ""
|
||||
except subprocess.TimeoutExpired as t:
|
||||
write_logs(f"Command execution timed out: {cmd}, error: {t}")
|
||||
return ""
|
||||
except Exception as e:
|
||||
write_logs(f"Command execution failed: {cmd}, error: {e}")
|
||||
return ""
|
||||
|
||||
|
||||
def service_shop_name(services_name: str) -> str:
|
||||
# 对应商店名字
|
||||
shop_name = {
|
||||
"postfix": "mail_sys",
|
||||
"pgsql": "pgsql_manager",
|
||||
"pure-ftpd": "pureftpd",
|
||||
|
||||
"php-fpm-52": "php-5.2",
|
||||
"php-fpm-53": "php-5.3",
|
||||
"php-fpm-54": "php-5.4",
|
||||
"php-fpm-55": "php-5.5",
|
||||
"php-fpm-56": "php-5.6",
|
||||
"php-fpm-70": "php-7.0",
|
||||
"php-fpm-71": "php-7.1",
|
||||
"php-fpm-72": "php-7.2",
|
||||
"php-fpm-73": "php-7.3",
|
||||
"php-fpm-74": "php-7.4",
|
||||
"php-fpm-80": "php-8.0",
|
||||
"php-fpm-81": "php-8.1",
|
||||
"php-fpm-82": "php-8.2",
|
||||
"php-fpm-83": "php-8.3",
|
||||
"php-fpm-84": "php-8.4",
|
||||
"php-fpm-85": "php-8.5",
|
||||
}
|
||||
return shop_name.get(services_name, services_name)
|
||||
|
||||
|
||||
def pretty_title(services_name: str) -> str:
|
||||
title = {
|
||||
"apache": "Apache",
|
||||
"nginx": "Nginx",
|
||||
"openlitespeed": "OpenLiteSpeed",
|
||||
"redis": "Redis",
|
||||
"mysql": "MySQL/MariaDB",
|
||||
"mongodb": "MongoDB",
|
||||
"pgsql": "PostgreSQL",
|
||||
"pure-ftpd": "Pure-FTPd",
|
||||
"memcached": "Memcached",
|
||||
"ssh": "SSH",
|
||||
"postfix": "Postfix",
|
||||
"pdns": "PowerDNS",
|
||||
|
||||
# PHP-FPM
|
||||
"php-fpm-52": "PHP 5.2 FPM",
|
||||
"php-fpm-53": "PHP 5.3 FPM",
|
||||
"php-fpm-54": "PHP 5.4 FPM",
|
||||
"php-fpm-55": "PHP 5.5 FPM",
|
||||
"php-fpm-56": "PHP 5.6 FPM",
|
||||
"php-fpm-70": "PHP 7.0 FPM",
|
||||
"php-fpm-71": "PHP 7.1 FPM",
|
||||
"php-fpm-72": "PHP 7.2 FPM",
|
||||
"php-fpm-73": "PHP 7.3 FPM",
|
||||
"php-fpm-74": "PHP 7.4 FPM",
|
||||
"php-fpm-80": "PHP 8.0 FPM",
|
||||
"php-fpm-81": "PHP 8.1 FPM",
|
||||
"php-fpm-82": "PHP 8.2 FPM",
|
||||
"php-fpm-83": "PHP 8.3 FPM",
|
||||
"php-fpm-84": "PHP 8.4 FPM",
|
||||
"php-fpm-85": "PHP 8.5 FPM",
|
||||
|
||||
# plugins
|
||||
"btwaf": "YakPanel WAF",
|
||||
"fail2ban": "Fail2Ban",
|
||||
}
|
||||
return title.get(services_name, services_name)
|
||||
|
||||
|
||||
# =======================================================
|
||||
def ssh_ver() -> str:
|
||||
version_info = run_command(["ssh", "-V"])
|
||||
if version_info and "OpenSSH" in version_info:
|
||||
return version_info.split(",")[0]
|
||||
return ""
|
||||
|
||||
|
||||
def postfix_ver() -> str:
|
||||
# mail_version = x.x.x
|
||||
output = run_command(["/usr/sbin/postconf", "mail_version"])
|
||||
if output and "=" in output:
|
||||
return output.split("=")[-1].strip()
|
||||
return ""
|
||||
|
||||
|
||||
def pgsql_pid() -> str:
|
||||
pid_str = run_command(["pgrep", "-o", "postgres"])
|
||||
if pid_str and pid_str.isdigit():
|
||||
return pid_str
|
||||
return ""
|
||||
|
||||
|
||||
def pgsql_ver() -> str:
|
||||
# psql (PostgreSQL) 18.0
|
||||
output = run_command([f"{SETUP_PATH}/pgsql/bin/psql", "--version"])
|
||||
if output:
|
||||
from re import search
|
||||
match = search(r"(\d+\.\d+(\.\d+)?)", output)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
org_file = f"{SETUP_PATH}/pgsql/data/PG_VERSION",
|
||||
if os.path.exists(str(org_file)):
|
||||
try:
|
||||
with open(str(org_file), "r") as f:
|
||||
version = f.read().strip()
|
||||
return version
|
||||
except:
|
||||
return ""
|
||||
return ""
|
||||
|
||||
|
||||
def pdns_pid() -> str:
|
||||
pid_str = run_command(["pgrep", "-x", "pdns_server"])
|
||||
if pid_str and pid_str.isdigit():
|
||||
return pid_str
|
||||
return ""
|
||||
|
||||
|
||||
def pdns_ver() -> str:
|
||||
# PowerDNS Authoritative Server x.x.x
|
||||
output = run_command(["pdns_server", "--version"])
|
||||
if not output:
|
||||
return ""
|
||||
from re import search
|
||||
match = search(r"(\d+\.\d+\.\d+)", output)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
if "PowerDNS" in output:
|
||||
return output.split()[-1]
|
||||
return ""
|
||||
|
||||
|
||||
def waf_pid() -> str:
|
||||
pid_str = run_command(["pgrep", "-x", "BT-WAF"])
|
||||
if pid_str and pid_str.isdigit():
|
||||
return pid_str
|
||||
return ""
|
||||
|
||||
|
||||
def pluign_ver(plugin_name: str) -> str:
|
||||
info = f"{PLUGINS_PATH}/{plugin_name}/info.json"
|
||||
if not os.path.exists(info):
|
||||
return ""
|
||||
try:
|
||||
with open(info, "r") as f:
|
||||
json_data = json.load(f)
|
||||
ret = json_data.get("versions", "")
|
||||
return ret
|
||||
except:
|
||||
pass
|
||||
return ""
|
||||
|
||||
|
||||
SERVICES_MAP = {
|
||||
# ========================= core base =============================
|
||||
"panel": (
|
||||
"Yak-Panel", f"{SETUP_PATH}/panel/logs/panel.pid", f"{SETUP_PATH}/panel/init.sh",
|
||||
f"{SETUP_PATH}/nginx/version.pl",
|
||||
),
|
||||
"apache": (
|
||||
"httpd", f"{SETUP_PATH}/apache/logs/httpd.pid", "/etc/init.d/httpd",
|
||||
f"{SETUP_PATH}/apache/version.pl",
|
||||
),
|
||||
"nginx": (
|
||||
"nginx", f"{SETUP_PATH}/nginx/logs/nginx.pid", "/etc/init.d/nginx",
|
||||
f"{SETUP_PATH}/nginx/version.pl",
|
||||
),
|
||||
"openlitespeed": (
|
||||
"litespeed", "/tmp/lshttpd/lshttpd.pid", "/usr/local/lsws/bin/lswsctrl",
|
||||
"/usr/local/lsws/VERSION",
|
||||
),
|
||||
"redis": (
|
||||
"redis-server", f"{SETUP_PATH}/redis/redis.pid", "/etc/init.d/redis",
|
||||
f"{SETUP_PATH}/redis/version.pl",
|
||||
),
|
||||
"mysql": (
|
||||
"mysqld", "/tmp/mysql.sock", "/etc/init.d/mysqld",
|
||||
f"{SETUP_PATH}/mysql/version.pl",
|
||||
),
|
||||
"mongodb": (
|
||||
"mongod", f"{SETUP_PATH}/mongodb/log/configsvr.pid", "/etc/init.d/mongodb",
|
||||
f"{SETUP_PATH}/mongodb/version.pl",
|
||||
),
|
||||
"pgsql": (
|
||||
"postgres", pgsql_pid, "/etc/init.d/pgsql",
|
||||
pgsql_ver,
|
||||
),
|
||||
"pure-ftpd": (
|
||||
"pure-ftpd", "/var/run/pure-ftpd.pid", "/etc/init.d/pure-ftpd",
|
||||
f"{SETUP_PATH}/pure-ftpd/version.pl",
|
||||
),
|
||||
"memcached": (
|
||||
"memcached", "/var/run/memcached.pid", "/etc/init.d/memcached",
|
||||
"/usr/local/memcached/version_check.pl",
|
||||
),
|
||||
"ssh": (
|
||||
"sshd", "/var/run/sshd.pid", "/etc/init.d/ssh",
|
||||
ssh_ver,
|
||||
),
|
||||
"postfix": (
|
||||
"master", "/var/spool/postfix/pid/master.pid", "/etc/init.d/postfix",
|
||||
postfix_ver,
|
||||
),
|
||||
"pdns": (
|
||||
# "pdns_server", pdns_pid, "/usr/sbin/pdns_server",
|
||||
"pdns_server", pdns_pid, "/www/server/panel/class_v2/ssl_dnsV2/aadns.pl",
|
||||
pdns_ver,
|
||||
),
|
||||
|
||||
# ======================== PHP-FPM ============================
|
||||
"php-fpm-52": ("php-fpm", f"{SETUP_PATH}/php/52/var/run/php-fpm.pid", "/etc/init.d/php-fpm-52",
|
||||
f"{SETUP_PATH}/php/52/version.pl"),
|
||||
"php-fpm-53": ("php-fpm", f"{SETUP_PATH}/php/53/var/run/php-fpm.pid", "/etc/init.d/php-fpm-53",
|
||||
f"{SETUP_PATH}/php/53/version.pl"),
|
||||
"php-fpm-54": ("php-fpm", f"{SETUP_PATH}/php/54/var/run/php-fpm.pid", "/etc/init.d/php-fpm-54",
|
||||
f"{SETUP_PATH}/php/54/version.pl"),
|
||||
"php-fpm-55": ("php-fpm", f"{SETUP_PATH}/php/55/var/run/php-fpm.pid", "/etc/init.d/php-fpm-55",
|
||||
f"{SETUP_PATH}/php/55/version.pl"),
|
||||
"php-fpm-56": ("php-fpm", f"{SETUP_PATH}/php/56/var/run/php-fpm.pid", "/etc/init.d/php-fpm-56",
|
||||
f"{SETUP_PATH}/php/56/version.pl"),
|
||||
"php-fpm-70": ("php-fpm", f"{SETUP_PATH}/php/70/var/run/php-fpm.pid", "/etc/init.d/php-fpm-70",
|
||||
f"{SETUP_PATH}/php/70/version.pl"),
|
||||
"php-fpm-71": ("php-fpm", f"{SETUP_PATH}/php/71/var/run/php-fpm.pid", "/etc/init.d/php-fpm-71",
|
||||
f"{SETUP_PATH}/php/71/version.pl"),
|
||||
"php-fpm-72": ("php-fpm", f"{SETUP_PATH}/php/72/var/run/php-fpm.pid", "/etc/init.d/php-fpm-72",
|
||||
f"{SETUP_PATH}/php/72/version.pl"),
|
||||
"php-fpm-73": ("php-fpm", f"{SETUP_PATH}/php/73/var/run/php-fpm.pid", "/etc/init.d/php-fpm-73",
|
||||
f"{SETUP_PATH}/php/73/version.pl"),
|
||||
"php-fpm-74": ("php-fpm", f"{SETUP_PATH}/php/74/var/run/php-fpm.pid", "/etc/init.d/php-fpm-74",
|
||||
f"{SETUP_PATH}/php/74/version.pl"),
|
||||
"php-fpm-80": ("php-fpm", f"{SETUP_PATH}/php/80/var/run/php-fpm.pid", "/etc/init.d/php-fpm-80",
|
||||
f"{SETUP_PATH}/php/80/version.pl"),
|
||||
"php-fpm-81": ("php-fpm", f"{SETUP_PATH}/php/81/var/run/php-fpm.pid", "/etc/init.d/php-fpm-81",
|
||||
f"{SETUP_PATH}/php/81/version.pl"),
|
||||
"php-fpm-82": ("php-fpm", f"{SETUP_PATH}/php/82/var/run/php-fpm.pid", "/etc/init.d/php-fpm-82",
|
||||
f"{SETUP_PATH}/php/82/version.pl"),
|
||||
"php-fpm-83": ("php-fpm", f"{SETUP_PATH}/php/83/var/run/php-fpm.pid", "/etc/init.d/php-fpm-83",
|
||||
f"{SETUP_PATH}/php/83/version.pl"),
|
||||
"php-fpm-84": ("php-fpm", f"{SETUP_PATH}/php/84/var/run/php-fpm.pid", "/etc/init.d/php-fpm-84",
|
||||
f"{SETUP_PATH}/php/84/version_check.pl"),
|
||||
"php-fpm-85": ("php-fpm", f"{SETUP_PATH}/php/85/var/run/php-fpm.pid", "/etc/init.d/php-fpm-85",
|
||||
f"{SETUP_PATH}/php/85/version_check.pl"),
|
||||
|
||||
# ======================== plugins ============================
|
||||
# nginx : btwaf
|
||||
# apache: btwaf_httpd
|
||||
"btwaf": (
|
||||
# "BT-WAF", f"{PLUGINS_PATH}/btwaf/BT-WAF.pid", f"/etc/init.d/btwaf",
|
||||
"BT-WAF", waf_pid, f"/etc/init.d/btwaf",
|
||||
partial(pluign_ver, "btwaf"),
|
||||
),
|
||||
"fail2ban": (
|
||||
"fail2ban-server", f"{PLUGINS_PATH}/fail2ban/fail2ban.pid", "/etc/init.d/fail2ban",
|
||||
partial(pluign_ver, "fail2ban"),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
# 日志
|
||||
def write_logs(msg: str, logger_name: str = "Service Daemon"):
|
||||
try:
|
||||
from public import M
|
||||
t = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
M("logs").add("uid,username,type,log,addtime", (1, "system", logger_name, msg, t))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
# 手动干预
|
||||
def manual_flag(server_name: str = None, open_: str = None) -> Optional[dict]:
|
||||
if not server_name: # only read
|
||||
return DaemonManager.manual_safe_read()
|
||||
# 人为干预
|
||||
if open_ in ["start", "restart"]: # 激活服务检查
|
||||
return DaemonManager.active_daemon(server_name)
|
||||
elif open_ == "stop": # 跳过服务检查
|
||||
return DaemonManager.skip_daemon(server_name)
|
||||
return DaemonManager.manual_safe_read()
|
||||
|
||||
|
||||
# 管理服务助手
|
||||
class ServicesHelper:
|
||||
def __init__(self, nick_name: str = None):
|
||||
self.nick_name = nick_name
|
||||
self._serviced = None
|
||||
self._pid_source = None
|
||||
self._bash = None
|
||||
self._ver_source = None
|
||||
self._pid_cache = None
|
||||
self._ver_cache = None
|
||||
self._install_cache = None
|
||||
self._info_inited = False
|
||||
|
||||
def __check_pid_process(self, pid: int) -> bool:
|
||||
try:
|
||||
# 是否活跃
|
||||
with open(f"/proc/{pid}/stat", "r") as f:
|
||||
if f.read().split()[2] == "Z":
|
||||
return False # 僵尸进程
|
||||
# 进程是否名字匹配
|
||||
with open(f"/proc/{pid}/comm", "r") as f:
|
||||
proc_name = f.read().strip()
|
||||
|
||||
# 特殊处理 mysql
|
||||
if self.nick_name != "mysql":
|
||||
return proc_name == self._serviced or proc_name == self.nick_name
|
||||
else:
|
||||
return proc_name in ["mysqld", "mariadbd"]
|
||||
except (FileNotFoundError, IndexError):
|
||||
return False
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def _init_info(self) -> None:
|
||||
if self._info_inited:
|
||||
return
|
||||
if not self.nick_name or not isinstance(self.nick_name, str):
|
||||
self._info_inited = True
|
||||
return
|
||||
|
||||
map_info = SERVICES_MAP.get(self.nick_name)
|
||||
if map_info:
|
||||
self._serviced, self._pid_source, self._bash, self._ver_source = map_info
|
||||
|
||||
self._info_inited = True
|
||||
|
||||
@property
|
||||
def pid(self):
|
||||
"""返回pid文件路径或pid值"""
|
||||
if self._pid_cache is None:
|
||||
self._init_info()
|
||||
if isinstance(self._pid_source, Callable):
|
||||
try:
|
||||
pid_val = self._pid_source()
|
||||
self._pid_cache = int(pid_val) if pid_val and pid_val.isdigit() else pid_val
|
||||
except Exception:
|
||||
self._pid_cache = self._pid_source
|
||||
else:
|
||||
self._pid_cache = self._pid_source
|
||||
return self._pid_cache
|
||||
|
||||
@property
|
||||
def version(self) -> str:
|
||||
"""返回服务版本号"""
|
||||
if self._ver_cache is None:
|
||||
self._init_info()
|
||||
if isinstance(self._ver_source, Callable):
|
||||
try:
|
||||
self._ver_cache = self._ver_source()
|
||||
except:
|
||||
self._ver_cache = ""
|
||||
elif isinstance(self._ver_source, str) and os.path.exists(self._ver_source):
|
||||
try:
|
||||
with open(self._ver_source, "r") as f:
|
||||
self._ver_cache = f.read().strip()
|
||||
except:
|
||||
self._ver_cache = ""
|
||||
else:
|
||||
self._ver_cache = ""
|
||||
|
||||
return str(self._ver_cache).strip()
|
||||
|
||||
@property
|
||||
def is_install(self) -> bool:
|
||||
"""判断是否安装"""
|
||||
if self._install_cache is not None:
|
||||
return self._install_cache
|
||||
|
||||
self._init_info()
|
||||
self._install_cache = False
|
||||
# waf特殊处理
|
||||
if self.nick_name == "btwaf":
|
||||
if os.path.exists(f"{PLUGINS_PATH}/btwaf"):
|
||||
self._install_cache = True
|
||||
return self._install_cache
|
||||
# postfix特殊处理
|
||||
if self.nick_name == "postfix":
|
||||
if os.path.exists(f"{PLUGINS_PATH}/mail_sys"):
|
||||
self._install_cache = True
|
||||
return self._install_cache
|
||||
|
||||
if any([
|
||||
self._serviced and os.path.exists(f"/etc/init.d/{self._serviced}"),
|
||||
self.nick_name and os.path.exists(f"/etc/init.d/{self.nick_name}"),
|
||||
self._bash and os.path.exists(self._bash),
|
||||
]):
|
||||
self._install_cache = True
|
||||
|
||||
return self._install_cache
|
||||
|
||||
@property
|
||||
def shop_name(self) -> str:
|
||||
return service_shop_name(self.nick_name)
|
||||
|
||||
@property
|
||||
def pretty_title(self) -> str:
|
||||
return pretty_title(self.nick_name)
|
||||
|
||||
@property
|
||||
def is_running(self) -> bool:
|
||||
"""
|
||||
判断进程是否存活
|
||||
pid: 进程pid文件 str 或 int
|
||||
serviced: 进程服务名称
|
||||
nick_name: 进程别名
|
||||
"""
|
||||
if not self.is_install:
|
||||
return False
|
||||
|
||||
if not self.pid:
|
||||
return False
|
||||
|
||||
if isinstance(self.pid, int):
|
||||
return self.__check_pid_process(self.pid)
|
||||
|
||||
if isinstance(self.pid, str):
|
||||
if not os.path.exists(self.pid):
|
||||
return False
|
||||
if self.pid.endswith(".pid"):
|
||||
try:
|
||||
with open(self.pid, "r") as f:
|
||||
temp_pid = int(f.read().strip())
|
||||
return self.__check_pid_process(temp_pid)
|
||||
except (ValueError, FileNotFoundError):
|
||||
return False
|
||||
except Exception:
|
||||
return False
|
||||
elif self.pid.endswith(".sock"):
|
||||
try:
|
||||
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
|
||||
s.settimeout(0.1)
|
||||
s.connect(self.pid)
|
||||
return True
|
||||
except (socket.timeout, ConnectionRefusedError, FileNotFoundError):
|
||||
return False
|
||||
except Exception:
|
||||
return False
|
||||
# not str and not int
|
||||
return False
|
||||
|
||||
def script(self, act: str, logger_name: str = "Service Daemon") -> None:
|
||||
if not self.is_install or act not in ["start", "stop", "restart", "status"]:
|
||||
return
|
||||
try:
|
||||
# "try to {act} [{self.nick_name}]..."
|
||||
self._init_info()
|
||||
bash_path = self._bash if self._bash else f"/etc/init.d/{self._serviced}"
|
||||
|
||||
# if isinstance(self.pid, str) and (
|
||||
# self.pid.endswith(".sock") or self.pid.endswith(".pid")
|
||||
# ):
|
||||
# try:
|
||||
# os.remove(self.pid)
|
||||
# except:
|
||||
# pass
|
||||
|
||||
if self.nick_name == "panel":
|
||||
if not os.path.exists(f"{SETUP_PATH}/panel/init.sh"):
|
||||
from public import get_url
|
||||
os.system(f"curl -k {get_url()}/install/update_7.x_en.sh|bash &")
|
||||
cmd = ["bash", bash_path, act]
|
||||
|
||||
elif self.nick_name == "pdns":
|
||||
cmd = ["systemctl", act, "pdns"]
|
||||
else:
|
||||
cmd = [bash_path, act]
|
||||
|
||||
subprocess.Popen(
|
||||
cmd,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
start_new_session=True
|
||||
)
|
||||
write_logs(f"Service [ {self.nick_name} ] {act}", logger_name)
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
write_logs(f"Failed to {act} {self.nick_name}, error: {e}", logger_name)
|
||||
|
||||
|
||||
# 守护服务管理
|
||||
class DaemonManager:
|
||||
_ensured = False
|
||||
|
||||
@classmethod
|
||||
def __ensure(cls):
|
||||
if cls._ensured:
|
||||
return
|
||||
if not os.path.exists(DAEMON_SERVICE_LOCK):
|
||||
with open(DAEMON_SERVICE_LOCK, "w") as _:
|
||||
pass
|
||||
if not os.path.exists(DAEMON_RESTART_RECORD):
|
||||
with open(DAEMON_RESTART_RECORD, "w") as fm:
|
||||
fm.write(json.dumps({}))
|
||||
os.makedirs(os.path.dirname(MANUAL_FLAG), exist_ok=True)
|
||||
if not os.path.exists(MANUAL_FLAG):
|
||||
with open(MANUAL_FLAG, "w") as fm:
|
||||
fm.write(json.dumps({}))
|
||||
if not os.path.exists(DAEMON_SERVICE):
|
||||
with open(DAEMON_SERVICE, "w") as fm:
|
||||
fm.write(json.dumps([]))
|
||||
cls._ensured = True
|
||||
|
||||
@staticmethod
|
||||
def read_lock(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
DaemonManager.__ensure()
|
||||
with open(DAEMON_SERVICE_LOCK, "r") as lock_file:
|
||||
fcntl.flock(lock_file, fcntl.LOCK_SH)
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
finally:
|
||||
fcntl.flock(lock_file, fcntl.LOCK_UN)
|
||||
|
||||
return wrapper
|
||||
|
||||
@staticmethod
|
||||
def write_lock(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
DaemonManager.__ensure()
|
||||
with open(DAEMON_SERVICE_LOCK, "r+") as lock_file:
|
||||
fcntl.flock(lock_file, fcntl.LOCK_EX)
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
finally:
|
||||
fcntl.flock(lock_file, fcntl.LOCK_UN)
|
||||
|
||||
return wrapper
|
||||
|
||||
@staticmethod
|
||||
@write_lock
|
||||
def operate_daemon(service_name: str, flag: int = 0) -> list:
|
||||
"""
|
||||
flag: 0 add, 1 del
|
||||
"""
|
||||
with open(DAEMON_SERVICE, "r+") as f:
|
||||
try:
|
||||
service = json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
service = []
|
||||
if flag == 0:
|
||||
service.append(service_name)
|
||||
elif flag == 1:
|
||||
service = [x for x in service if x != service_name]
|
||||
service = list(set(service))
|
||||
f.seek(0)
|
||||
# noinspection PyTypeChecker
|
||||
json.dump(service, f)
|
||||
f.truncate()
|
||||
return service
|
||||
|
||||
@staticmethod
|
||||
@write_lock
|
||||
def operate_manual_flag(service_name: str, flag: int = 0) -> dict:
|
||||
"""
|
||||
flag: 0 normal, 1 manual closed
|
||||
"""
|
||||
with open(MANUAL_FLAG, "r+") as f:
|
||||
try:
|
||||
service = json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
service = {}
|
||||
service[service_name] = flag
|
||||
f.seek(0)
|
||||
# noinspection PyTypeChecker
|
||||
json.dump(service, f)
|
||||
f.truncate()
|
||||
return service
|
||||
|
||||
@staticmethod
|
||||
def remove_daemon(service_name: str) -> list:
|
||||
"""移除守护进程服务"""
|
||||
return DaemonManager.operate_daemon(service_name, 1)
|
||||
|
||||
@staticmethod
|
||||
def add_daemon(service_name: str) -> list:
|
||||
"""添加守护进程服务"""
|
||||
return DaemonManager.operate_daemon(service_name, 0)
|
||||
|
||||
@staticmethod
|
||||
def skip_daemon(service_name: str) -> dict:
|
||||
"""跳过服务检查"""
|
||||
return DaemonManager.operate_manual_flag(service_name, 1)
|
||||
|
||||
@staticmethod
|
||||
def active_daemon(service_name: str) -> dict:
|
||||
"""激活服务检查"""
|
||||
return DaemonManager.operate_manual_flag(service_name, 0)
|
||||
|
||||
@staticmethod
|
||||
@read_lock
|
||||
def safe_read():
|
||||
"""服务守护进程服务列表"""
|
||||
try:
|
||||
res = read_file(DAEMON_SERVICE)
|
||||
return json.loads(res) if res else []
|
||||
except:
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
@read_lock
|
||||
def manual_safe_read():
|
||||
"""手动干预服务字典, 0: 需要干预, 1: 被手动关闭的"""
|
||||
try:
|
||||
manual = read_file(MANUAL_FLAG)
|
||||
return json.loads(manual) if manual else {}
|
||||
except:
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def update_restart_record(service_name: str, max_count: int) -> bool:
|
||||
try:
|
||||
with open(DAEMON_RESTART_RECORD, "r+") as f:
|
||||
fcntl.flock(f, fcntl.LOCK_EX)
|
||||
try:
|
||||
record = json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
record = {}
|
||||
|
||||
count = record.get(service_name, 0)
|
||||
if count >= max_count:
|
||||
return True
|
||||
|
||||
record[service_name] = count + 1
|
||||
f.seek(0)
|
||||
json.dump(record, f)
|
||||
f.truncate()
|
||||
return False
|
||||
except Exception:
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def safe_read_restart_record() -> Dict[str, int]:
|
||||
try:
|
||||
with open(DAEMON_RESTART_RECORD, "r") as f:
|
||||
fcntl.flock(f, fcntl.LOCK_SH)
|
||||
record = json.load(f)
|
||||
except Exception:
|
||||
record = {}
|
||||
return record
|
||||
|
||||
|
||||
# 服务守护
|
||||
class RestartServices:
|
||||
COUNT = 30
|
||||
|
||||
@staticmethod
|
||||
def __keep_flag_right(manual_info: dict) -> None:
|
||||
try:
|
||||
with open(MANUAL_FLAG, "r+") as f:
|
||||
try:
|
||||
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
|
||||
f.seek(0)
|
||||
# noinspection PyTypeChecker
|
||||
json.dump(manual_info, f)
|
||||
f.truncate()
|
||||
except:
|
||||
print("Error writing manual flag file")
|
||||
finally:
|
||||
fcntl.flock(f.fileno(), fcntl.LOCK_UN)
|
||||
except Exception as e:
|
||||
print("Error keep_flag_right:", e)
|
||||
|
||||
def _overhead(self, nick_name) -> bool:
|
||||
return DaemonManager.update_restart_record(
|
||||
nick_name, self.COUNT
|
||||
)
|
||||
|
||||
@DaemonManager.read_lock
|
||||
def main(self):
|
||||
manaul = read_file(MANUAL_FLAG)
|
||||
services = read_file(DAEMON_SERVICE)
|
||||
try:
|
||||
manual_info = json.loads(manaul) if manaul else {}
|
||||
check_list = json.loads(services) if services else []
|
||||
check_list = ["panel"] + check_list # panel 强制守护
|
||||
except Exception:
|
||||
manual_info = {}
|
||||
check_list = ["panel"]
|
||||
|
||||
record = DaemonManager.safe_read_restart_record()
|
||||
for service in [
|
||||
x for x in list(set(check_list)) if record.get(x, 0) < self.COUNT
|
||||
]:
|
||||
obj = ServicesHelper(service)
|
||||
if not obj.is_install:
|
||||
continue
|
||||
|
||||
if not obj.is_running:
|
||||
if int(manual_info.get(obj.nick_name, 0)) == 1:
|
||||
# service closed maually, skip
|
||||
continue
|
||||
# if obj.nick_name != "panel":
|
||||
# write_logs(f"Service [ {obj.nick_name} ] is Not Running, Try to start it...")
|
||||
|
||||
if not self._overhead(obj.nick_name):
|
||||
obj.script("restart")
|
||||
|
||||
if obj.is_running and manual_info.get(obj.nick_name) == 1:
|
||||
# service is running, fix the wrong flag
|
||||
manual_info[obj.nick_name] = 0
|
||||
# under lock file read lock
|
||||
self.__keep_flag_right(manual_info)
|
||||
|
||||
|
||||
def first_time_installed(data: dict) -> None:
|
||||
"""
|
||||
首次安装服务启动守护进程服务
|
||||
"""
|
||||
# todo 虽然支持, 但是守护目前不干预, 前端没开放
|
||||
exculde = [
|
||||
"pgsql", "fail2ban", "btwaf", "ssh", "pdns", "php-fpm", "memcached"
|
||||
]
|
||||
if not data:
|
||||
return
|
||||
try:
|
||||
for service in SERVICES_MAP.keys(): # support service
|
||||
if service in exculde:
|
||||
continue
|
||||
if "php-fpm" in service:
|
||||
continue
|
||||
pl_name = f"{DATA_PATH}/first_installed_flag_{service}.pl"
|
||||
if data.get(service): # panel installed
|
||||
setup = data[service].get("setup", False)
|
||||
if setup is False and os.path.exists(pl_name):
|
||||
os.remove(pl_name)
|
||||
elif setup is True and not os.path.exists(pl_name):
|
||||
DaemonManager.add_daemon(service)
|
||||
with open(pl_name, "w") as f:
|
||||
f.write("1")
|
||||
else:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) == 2:
|
||||
service_name = sys.argv[1]
|
||||
if service_name in SERVICES_MAP:
|
||||
helper = ServicesHelper(service_name)
|
||||
print(f"Attempting to restart service: {service_name}")
|
||||
helper.script("restart")
|
||||
print(f"'{service_name}' restart.")
|
||||
write_logs(f"Service [ {service_name} ] restart ...")
|
||||
else:
|
||||
print(f"Error: Service '{service_name}' not found.")
|
||||
print("Available services are:")
|
||||
for key in SERVICES_MAP.keys():
|
||||
print(f" - {key}")
|
||||
elif len(sys.argv) == 1:
|
||||
pass
|
||||
else:
|
||||
print("Usage: python restart_services.py [service_name]")
|
||||
171
script/run_log_split.py
Normal file
171
script/run_log_split.py
Normal file
@@ -0,0 +1,171 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8
|
||||
# -----------------------------
|
||||
# Website run log split script
|
||||
# -----------------------------
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import glob
|
||||
|
||||
os.chdir("/www/server/panel")
|
||||
if '/www/server/panel' not in sys.path:
|
||||
sys.path.insert(0,'/www/server/panel')
|
||||
if '/www/server/panel/class' not in sys.path:
|
||||
sys.path.insert(0,'class/')
|
||||
if '/www/server/panel/class_v2' not in sys.path:
|
||||
sys.path.insert(0,'class_v2/')
|
||||
|
||||
import public, json
|
||||
|
||||
try:
|
||||
from projectModelV2.pythonModel import main as pythonMod
|
||||
from projectModelV2.nodejsModel import main as nodejsMod
|
||||
|
||||
mods = {
|
||||
"python": pythonMod(),
|
||||
"node": nodejsMod(),
|
||||
}
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
print("****** project split log task error ******")
|
||||
|
||||
print('==================================================================')
|
||||
print('★[' + time.strftime("%Y/%m/%d %H:%M:%S") + '] split log task start ★')
|
||||
print('==================================================================')
|
||||
|
||||
|
||||
class LogSplit:
|
||||
__slots__ = ("stype", "log_size", "limit", "_time", "compress", "exclude_sites")
|
||||
|
||||
@classmethod
|
||||
def build_log_split(cls, name):
|
||||
logsplit = cls()
|
||||
path = '{}/data/run_log_split.conf'.format(public.get_panel_path())
|
||||
data = {}
|
||||
if os.path.exists(path):
|
||||
try:
|
||||
data = json.loads(public.readFile(path))
|
||||
except:
|
||||
public.ExecShell("rm -f {}".format(path))
|
||||
return "file not found"
|
||||
_clean(data)
|
||||
public.writeFile(path, json.dumps(data))
|
||||
target = data.get(name)
|
||||
if not target :
|
||||
return "file not found"
|
||||
else:
|
||||
for i in cls.__slots__:
|
||||
if i in target:
|
||||
setattr(logsplit, i, target[i])
|
||||
logsplit._show()
|
||||
return logsplit
|
||||
|
||||
def __init__(self, split_type: str = "day", limit: int = 180, log_size: int = 1024, compress: bool = False) -> None:
|
||||
self.stype = split_type
|
||||
self.log_size = log_size
|
||||
self.limit = limit
|
||||
self._time = time.strftime("%Y-%m-%d_%H%M%S")
|
||||
self.compress = compress
|
||||
self.exclude_sites = []
|
||||
|
||||
def _show(self):
|
||||
if self.stype == "day":
|
||||
print('|---Split method: Split 1 copy per day')
|
||||
else:
|
||||
print('|---Split method: Split by file size, split when file exceeds {}'.format(public.to_size(self.log_size)))
|
||||
print('|---Currently keeping the latest [{}] copies'.format(self.limit))
|
||||
|
||||
def _to_zip(self, file_path):
|
||||
os.system('gzip {}'.format(file_path))
|
||||
|
||||
def _del_surplus_log(self, history_log_path, log_prefix):
|
||||
if not os.path.exists(history_log_path):
|
||||
os.makedirs(history_log_path, mode=0o755)
|
||||
logs = sorted(glob.glob(history_log_path + '/' + log_prefix + "*_log.*"))
|
||||
|
||||
count = len(logs)
|
||||
if count >= self.limit:
|
||||
for i in logs[:count - self.limit + 1]:
|
||||
if os.path.exists(i):
|
||||
os.remove(i)
|
||||
print('|---Surplus log [' + i + '] has been deleted!')
|
||||
|
||||
def __call__(self, pjanme: str, sfile: str, log_prefix: str):
|
||||
base_path, filename = sfile.rsplit("/", 1)
|
||||
history_log_path = '{}/{}-history_logs'.format(base_path, pjanme)
|
||||
|
||||
if self.stype == 'size' and os.path.getsize(sfile) < self.log_size:
|
||||
print('|---File size has not exceeded [{}], skipping!'.format(public.to_size(self.log_size)))
|
||||
return
|
||||
|
||||
self._del_surplus_log(history_log_path, log_prefix)
|
||||
|
||||
if os.path.exists(sfile):
|
||||
history_log_file = history_log_path + '/' + log_prefix + '_' + self._time + '_log.log'
|
||||
if not os.path.exists(history_log_file):
|
||||
with open(history_log_file, 'wb') as hf, open(sfile, 'r+b') as lf:
|
||||
while True:
|
||||
chunk_data = lf.read(1024*100)
|
||||
if not chunk_data:
|
||||
break
|
||||
hf.write(chunk_data)
|
||||
lf.seek(0)
|
||||
lf.truncate()
|
||||
if self.compress:
|
||||
self._to_zip(history_log_file)
|
||||
|
||||
print('|---Log has been split to: ' + history_log_file + (".gz" if self.compress else ""))
|
||||
else:
|
||||
print('|---Target log file {} for project {} is missing, please note'.format(sfile, pjanme))
|
||||
|
||||
|
||||
|
||||
def main(name):
|
||||
logsplit = LogSplit.build_log_split(name)
|
||||
if logsplit=="file not found":
|
||||
print(
|
||||
"****** Detected panel project log split task configuration is empty,"
|
||||
" please reset project log split task ******"
|
||||
)
|
||||
return
|
||||
if not logsplit:
|
||||
print("****** Panel project log split task configuration file is missing ******")
|
||||
return
|
||||
project = public.M('sites').where("project_type <> ? and name = ?", ("PHP", name)).find()
|
||||
project['project_config'] = json.loads(project['project_config'])
|
||||
for_split_func = getattr(mods.get(project["project_type"].lower()), "for_split")
|
||||
if callable(for_split_func):
|
||||
print('|---Starting to operate on {} project [{}] logs'.format(project["project_type"], project["name"]))
|
||||
try:
|
||||
for_split_func(logsplit, project)
|
||||
print('|---Completed log split task for {} project [{}]'.format(project["project_type"], project["name"]))
|
||||
except:
|
||||
import traceback
|
||||
print(traceback.format_exc())
|
||||
print('|---Log split task error for {} project [{}]'.format(project["project_type"], project["name"]))
|
||||
else:
|
||||
print("****** Panel project log split task error ******")
|
||||
print('================= All log split tasks completed ==================')
|
||||
|
||||
|
||||
def _clean(data):
|
||||
res = public.M('crontab').field('name').select()
|
||||
del_config = []
|
||||
for i in data.keys():
|
||||
for j in res:
|
||||
if j["name"].find(i) != -1 and j["name"].find("log split"):
|
||||
break
|
||||
else:
|
||||
del_config.append(i)
|
||||
|
||||
for i in del_config:
|
||||
del data[i]
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) == 2:
|
||||
name = sys.argv[1].strip()
|
||||
main(name)
|
||||
else:
|
||||
print("****** Panel project log split task configuration parameter error ******")
|
||||
60
script/run_script.py
Normal file
60
script/run_script.py
Normal file
@@ -0,0 +1,60 @@
|
||||
#coding: utf-8
|
||||
#-------------------------------------------------------------------
|
||||
# YakPanel
|
||||
#-------------------------------------------------------------------
|
||||
# Copyright (c) 2015-2099 YakPanel(www.yakpanel.com) All rights reserved.
|
||||
#-------------------------------------------------------------------
|
||||
# Author: hwliang <hwl@yakpanel.com>
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
#------------------------------
|
||||
# 项目开机自启调用脚本
|
||||
#------------------------------
|
||||
import os,sys
|
||||
panel_path = '/www/server/panel'
|
||||
os.chdir(panel_path)
|
||||
if not 'class/' in sys.path: sys.path.insert(0,'class/')
|
||||
import public,time,psutil
|
||||
|
||||
|
||||
def project_model_auto_run():
|
||||
'''
|
||||
@name 项目模型自启调用
|
||||
@author hwliang<2021-08-09>
|
||||
@return bool
|
||||
'''
|
||||
project_model_path = '{}/projectModel'.format(public.get_class_path())
|
||||
if not os.path.exists(project_model_path): return False
|
||||
for mod_name in os.listdir(project_model_path):
|
||||
if mod_name in ['base.py','__init__.py']: continue
|
||||
mod_file = "{}/{}".format(project_model_path,mod_name)
|
||||
if not os.path.exists(mod_file): continue
|
||||
if not os.path.isfile(mod_file): continue
|
||||
|
||||
tmp_mod = public.get_script_object(mod_file)
|
||||
if not hasattr(tmp_mod,'main'): continue
|
||||
|
||||
run_object = getattr(tmp_mod.main(),'auto_run',None)
|
||||
if run_object: run_object()
|
||||
|
||||
|
||||
def start():
|
||||
run_tips = '/dev/shm/bt_auto_run.pl'
|
||||
boot_time = psutil.boot_time()
|
||||
stime = time.time()
|
||||
if os.path.exists(run_tips):
|
||||
last_time = int(public.readFile(run_tips))
|
||||
if boot_time < last_time: return False
|
||||
if stime - 3600 > boot_time: return False
|
||||
|
||||
# --------------------- 调用自启动程序 ---------------------
|
||||
|
||||
project_model_auto_run()
|
||||
|
||||
# --------------------- 结束调用 ---------------------
|
||||
|
||||
public.writeFile(run_tips,str(int(stime)))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
start()
|
||||
26
script/scan_log.py
Normal file
26
script/scan_log.py
Normal file
@@ -0,0 +1,26 @@
|
||||
#coding: utf-8
|
||||
import os,sys,time
|
||||
os.chdir('/www/server/panel/')
|
||||
sys.path.insert(0,"class/")
|
||||
import public
|
||||
import log_analysis
|
||||
la = log_analysis.log_analysis()
|
||||
site_infos = public.M('sites').field('name').select()
|
||||
if not site_infos:
|
||||
exit()
|
||||
get = public.to_dict_obj({})
|
||||
for i in site_infos:
|
||||
if public.get_webserver() == 'nginx':
|
||||
log_file = '{}.log'
|
||||
elif public.get_webserver() == 'apache':
|
||||
log_file = '{}-access_log'
|
||||
else:
|
||||
log_file = '{}_ols.access_log'
|
||||
log_file = log_file.format(i['name'])
|
||||
get.path = "/www/wwwlogs/{}".format(log_file)
|
||||
get.action = "log_analysis"
|
||||
print('==================================================================')
|
||||
print('|-Analyzing [{}] website logs...'.format(i['name']))
|
||||
la.log_analysis(get)
|
||||
print('|-Analysis of website logs completed')
|
||||
print('==================================================================')
|
||||
45
script/second_task.py
Normal file
45
script/second_task.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import time
|
||||
import os, sys
|
||||
panelPath = '/www/server/panel/'
|
||||
os.chdir(panelPath)
|
||||
needed_paths=['/www/server/panel', '/www/server/panel/class','/www/server/panel/class_v2']
|
||||
for path in needed_paths:
|
||||
if path not in sys.path:
|
||||
sys.path.insert(0,path)
|
||||
import public
|
||||
|
||||
def task(echo):
|
||||
execstr = public.GetConfigValue('setup_path') + '/cron/' + echo
|
||||
public.ExecShell('chmod +x ' + execstr)
|
||||
public.ExecShell('nohup ' + execstr + ' start >> ' + execstr + '.log 2>&1 &')
|
||||
|
||||
|
||||
def run_task(echo, interval):
|
||||
timestamp_file = '{}/data/{}.timestamp'.format(panelPath,echo)
|
||||
start_time = time.time()
|
||||
|
||||
while time.time() - start_time <= 60:
|
||||
try:
|
||||
with open(timestamp_file, 'r') as file:
|
||||
last_executed = float(file.read().strip())
|
||||
except (FileNotFoundError, ValueError):
|
||||
last_executed = 0
|
||||
|
||||
current_time = time.time()
|
||||
if current_time - last_executed >= interval:
|
||||
# print("任务开始执行时间: {}".format(time.strftime('%H:%M:%S')))
|
||||
task(echo)
|
||||
with open(timestamp_file, 'w') as file:
|
||||
file.write(str(time.time()))
|
||||
# 计算需要等待的时间,避免过频繁的检查
|
||||
time_to_wait = interval - (current_time - last_executed)
|
||||
time.sleep(max(0, time_to_wait))
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) != 3:
|
||||
sys.exit(1)
|
||||
|
||||
interval = int(sys.argv[1])
|
||||
echo = sys.argv[2]
|
||||
|
||||
run_task(echo, interval)
|
||||
18
script/site_task.py
Normal file
18
script/site_task.py
Normal file
@@ -0,0 +1,18 @@
|
||||
#coding: utf-8
|
||||
import os,sys,time
|
||||
os.chdir('/www/server/panel/')
|
||||
sys.path.insert(0,"class/")
|
||||
import public
|
||||
oldEdate = public.readFile('data/edate.pl')
|
||||
if not oldEdate: oldEdate = '0000-00-00'
|
||||
mEdate = time.strftime('%Y-%m-%d',time.localtime())
|
||||
edateSites = public.M('sites').where('edate>? AND edate<? AND (status=? OR status=?)',('0000-00-00',mEdate,1,u'正在运行')).field('id,name').select()
|
||||
import panelSite
|
||||
siteObject = panelSite.panelSite()
|
||||
for site in edateSites:
|
||||
get = public.dict_obj()
|
||||
get.id = site['id']
|
||||
get.name = site['name']
|
||||
siteObject.SiteStop(get)
|
||||
oldEdate = mEdate
|
||||
public.writeFile('data/edate.pl',mEdate)
|
||||
89
script/sync_time.py
Normal file
89
script/sync_time.py
Normal file
@@ -0,0 +1,89 @@
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import requests
|
||||
except:
|
||||
os.system("btpip install requests")
|
||||
import requests
|
||||
try:
|
||||
import ntplib
|
||||
except:
|
||||
os.system("btpip install ntplib")
|
||||
import ntplib
|
||||
from datetime import datetime
|
||||
|
||||
try:
|
||||
import pytz
|
||||
except:
|
||||
os.system("btpip install pytz")
|
||||
import pytz
|
||||
|
||||
|
||||
def sync_server_time(server, zone):
|
||||
try:
|
||||
print("Getting time from {}...".format(server))
|
||||
client = ntplib.NTPClient()
|
||||
response = client.request(server, version=3)
|
||||
timestamp = response.tx_time
|
||||
tz = pytz.timezone(zone)
|
||||
time_zone = datetime.fromtimestamp(timestamp, tz)
|
||||
local_time = datetime.now()
|
||||
offset = timestamp - time.time()
|
||||
print("Local time:", local_time)
|
||||
print("Server time:", time_zone)
|
||||
print("Time offset:", offset, "seconds")
|
||||
import os
|
||||
print("Syncing time...")
|
||||
os.system('date -s "{}"'.format(time_zone))
|
||||
return True
|
||||
except Exception as e:
|
||||
print("Failed to retrieve time from {}!".format(server))
|
||||
# print(traceback.format_exc())
|
||||
return False
|
||||
|
||||
|
||||
server_list = ['cn.pool.ntp.org', '0.pool.ntp.org', '2.pool.ntp.org']
|
||||
|
||||
if __name__ == '__main__':
|
||||
area = sys.argv[1].split('/')[0]
|
||||
zone = sys.argv[1].split('/')[1]
|
||||
print("Current time zone setting:{}".format(sys.argv[1]))
|
||||
if not zone:
|
||||
exit()
|
||||
os.system('rm -f /etc/localtime')
|
||||
os.system("ln -s '/usr/share/zoneinfo/" + area + "/" + zone + "' '/etc/localtime'")
|
||||
flag = 0
|
||||
for server in server_list:
|
||||
if sync_server_time(server, sys.argv[1]):
|
||||
flag = 1
|
||||
print("|-Synchronized time successfully!")
|
||||
break
|
||||
if flag == 0:
|
||||
try:
|
||||
port = '7800'
|
||||
port_pl = '/www/server/panel/data/port.pl'
|
||||
if os.path.exists(port_pl):
|
||||
with open(port_pl) as _pf:
|
||||
port = (_pf.read() or '').strip() or '7800'
|
||||
local_url = 'http://127.0.0.1:{}/api/index/get_time'.format(port)
|
||||
print("Getting time from {}...".format(local_url))
|
||||
r = requests.get(local_url, timeout=8)
|
||||
timestamp = int(r.text)
|
||||
tz = pytz.timezone(sys.argv[1])
|
||||
time_zone = datetime.fromtimestamp(timestamp, tz)
|
||||
local_time = datetime.now()
|
||||
offset = timestamp - time.time()
|
||||
print("Local time:", local_time)
|
||||
print("Server time:", time_zone)
|
||||
print("Time offset:", offset, "秒")
|
||||
print("Syncing time...")
|
||||
os.system(f"date -s '{time_zone}'")
|
||||
flag = 1
|
||||
print("|-Synchronized time successfully!")
|
||||
except:
|
||||
print(traceback.format_exc())
|
||||
if flag == 0:
|
||||
print("|-Synchronization time error!")
|
||||
378
script/upgrade_firewall.py
Normal file
378
script/upgrade_firewall.py
Normal file
@@ -0,0 +1,378 @@
|
||||
# coding: utf-8
|
||||
# -------------------------------------------------------------------
|
||||
# yakpanel
|
||||
# -------------------------------------------------------------------
|
||||
# Copyright (c) 2014-2099 yakpanel(http://www.yakpanel.com) All rights reserved.
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
os.chdir('/www/server/panel/')
|
||||
sys.path.insert(0, "class/")
|
||||
sys.path.insert(0, "class_v2/")
|
||||
sys.path.insert(0, "/www/server/panel/")
|
||||
import public
|
||||
|
||||
__isFirewalld = False
|
||||
__isUfw = False
|
||||
|
||||
if os.path.exists('/usr/sbin/firewalld') and os.path.exists('/usr/bin/yum'):
|
||||
__isFirewalld = True
|
||||
if os.path.exists('/usr/sbin/ufw') and os.path.exists('/usr/bin/apt-get'):
|
||||
__isUfw = True
|
||||
|
||||
|
||||
def check_ipset_exist(ipset_name):
|
||||
cmd = "ipset list {}|grep Name".format(ipset_name)
|
||||
res, err = public.ExecShell(cmd)
|
||||
if err != "":
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def firewalld_process_zone_file(zone_path, rule_dict, zone_name):
|
||||
"""
|
||||
处理zone文件(public.xml或trusted.xml),删除匹配的规则。
|
||||
|
||||
参数:
|
||||
zone_path (str): zone文件路径
|
||||
rule_dict (dict): 规则字典
|
||||
zone_name (str): Zone名称('public'或'trusted')
|
||||
"""
|
||||
if not os.path.exists(zone_path):
|
||||
return
|
||||
import xml.etree.ElementTree as ET
|
||||
tree = ET.parse(zone_path)
|
||||
root = tree.getroot()
|
||||
# 处理<rule>标签
|
||||
for rule in root.findall('rule'):
|
||||
source = rule.find('source')
|
||||
if source is not None:
|
||||
ip = source.get('address')
|
||||
action = None
|
||||
if rule.find('accept') is not None:
|
||||
action = 'ACCEPT'
|
||||
elif rule.find('drop') is not None:
|
||||
action = 'DROP'
|
||||
if (ip in rule_dict and
|
||||
rule_dict[ip]['Chain'].upper() == 'INPUT' and
|
||||
rule_dict[ip]['Zone'] == zone_name and
|
||||
rule_dict[ip]['Strategy'].upper() == action.upper()):
|
||||
root.remove(rule)
|
||||
if zone_name == 'trusted':
|
||||
for source in root.findall('source'):
|
||||
ip = source.get('address')
|
||||
if (ip in rule_dict and
|
||||
rule_dict[ip]['Chain'].upper() == 'INPUT' and
|
||||
rule_dict[ip]['Zone'] == 'trusted' and
|
||||
rule_dict[ip]['Strategy'].upper() == 'ACCEPT'):
|
||||
root.remove(source)
|
||||
tree.write(zone_path)
|
||||
|
||||
|
||||
def firewalld_process_direct_file(direct_path, rule_dict):
|
||||
"""
|
||||
处理direct.xml文件,删除匹配的OUTPUT规则。
|
||||
参数:
|
||||
direct_path (str): direct.xml文件路径
|
||||
rule_dict (dict): 规则字典
|
||||
"""
|
||||
if not os.path.exists(direct_path):
|
||||
return
|
||||
import xml.etree.ElementTree as ET
|
||||
tree = ET.parse(direct_path)
|
||||
root = tree.getroot()
|
||||
for rule in root.findall('rule'):
|
||||
if rule.get('chain') == 'OUTPUT':
|
||||
rule_text = rule.text.strip()
|
||||
if rule_text.startswith('-d '):
|
||||
parts = rule_text.split()
|
||||
ip = parts[1]
|
||||
action = parts[-1]
|
||||
if (ip in rule_dict and
|
||||
rule_dict[ip]['Chain'].upper() == 'OUTPUT' and
|
||||
rule_dict[ip]['Strategy'].upper() == action.upper()):
|
||||
root.remove(rule)
|
||||
tree.write(direct_path)
|
||||
|
||||
|
||||
def firewalld_batch_remove_ip_rule(rule_dict):
|
||||
direct_path = '/etc/firewalld/direct.xml'
|
||||
public_path = '/etc/firewalld/zones/public.xml'
|
||||
trusted_path = '/etc/firewalld/zones/trusted.xml'
|
||||
import shutil
|
||||
|
||||
if os.path.exists(public_path):
|
||||
shutil.copyfile(public_path, public_path + '.bak')
|
||||
firewalld_process_zone_file(public_path, rule_dict, 'public')
|
||||
if os.path.exists(trusted_path):
|
||||
shutil.copyfile(trusted_path, trusted_path + '.bak')
|
||||
firewalld_process_zone_file(trusted_path, rule_dict, 'trusted')
|
||||
if os.path.exists(direct_path):
|
||||
shutil.copyfile(direct_path, direct_path + '.bak')
|
||||
firewalld_process_direct_file(direct_path, rule_dict)
|
||||
|
||||
|
||||
def ufw_batch_remove_ip_rule(rule_dict):
|
||||
"""
|
||||
UFW批量删除规则
|
||||
"""
|
||||
ufw_config = '/etc/ufw/user.rules'
|
||||
import shutil
|
||||
shutil.copy(ufw_config, ufw_config + ".bak" + public.format_date())
|
||||
|
||||
lines = public.readFile(ufw_config)
|
||||
lines = lines.splitlines()
|
||||
new_lines = []
|
||||
i = 0
|
||||
|
||||
while i < len(lines):
|
||||
line = lines[i]
|
||||
|
||||
if line.startswith('### tuple ###'):
|
||||
parts = line.split()
|
||||
action = parts[3] # deny 或 allow
|
||||
direction = parts[-1] # in 或 out
|
||||
ip = parts[-2] # IP 地址
|
||||
|
||||
if ip == '0.0.0.0/0':
|
||||
new_lines.extend(lines[i:i + 3]) # 保留这三行
|
||||
i += 3
|
||||
continue
|
||||
|
||||
chain = 'INPUT' if direction == 'in' else 'OUTPUT' if direction == 'out' else None
|
||||
strategy = 'DROP' if action == 'deny' else 'ACCEPT' if action == 'allow' else None
|
||||
if chain and strategy and all([
|
||||
ip in rule_dict,
|
||||
chain.upper() == rule_dict[ip]['Chain'].upper(),
|
||||
strategy.upper() == rule_dict[ip]['Strategy'].upper(),
|
||||
]):
|
||||
# 如果匹配,跳过这三行(即删除)
|
||||
i += 3
|
||||
else:
|
||||
# 如果不匹配,保留这三行
|
||||
new_lines.extend(lines[i:i + 3])
|
||||
i += 3
|
||||
else:
|
||||
# 如果不是规则的开始行,直接保留
|
||||
new_lines.append(lines[i])
|
||||
i += 1
|
||||
with open(ufw_config, 'w') as file:
|
||||
file.writelines(line if line.endswith('\n') else line + '\n' for line in new_lines)
|
||||
|
||||
|
||||
# 9.5.0-9.6.0
|
||||
def upgrade_iprule(commodel):
|
||||
list_address = commodel.firewall.list_address()
|
||||
if len(list_address) == 0:
|
||||
return
|
||||
|
||||
rule_dict = {}
|
||||
print("-* IP rule data being migrated...")
|
||||
for item in list_address:
|
||||
print("Migration IP rules:{}".format(item))
|
||||
commodel.iptables.set_chain_rich_ip(item, 'add', item['Chain'])
|
||||
rule_dict[item['Address']] = {
|
||||
"Address": item['Address'],
|
||||
"Family": item['Family'],
|
||||
"Strategy": item['Strategy'],
|
||||
"Zone": item.get('Zone'),
|
||||
"Chain": item['Chain']
|
||||
}
|
||||
|
||||
if __isFirewalld:
|
||||
firewalld_batch_remove_ip_rule(rule_dict)
|
||||
elif __isUfw:
|
||||
ufw_batch_remove_ip_rule(rule_dict)
|
||||
else:
|
||||
return
|
||||
commodel.firewall.reload()
|
||||
public.ExecShell("systemctl reload BT-FirewallServices")
|
||||
print("-* IP rule data migration completed...")
|
||||
|
||||
|
||||
# 9.5.0-9.6.0
|
||||
def upgrade_countrys(commodel):
|
||||
country_list = public.M('firewall_country').select()
|
||||
if len(country_list) == 0:
|
||||
return
|
||||
|
||||
from safeModelV2.firewallModel import main as firewallModel
|
||||
firewallmodel = firewallModel()
|
||||
|
||||
print("-* Area rule data being migrated...")
|
||||
for country in country_list:
|
||||
ports = country['ports']
|
||||
brief = country['brief']
|
||||
types = country['types']
|
||||
|
||||
print("Rules for relocation areas:{}".format(brief))
|
||||
if __isUfw or not __isFirewalld:
|
||||
if not ports:
|
||||
public.ExecShell('iptables -D INPUT -m set --match-set ' + brief + ' src -j ' + types.upper())
|
||||
else:
|
||||
public.ExecShell(
|
||||
'iptables -D INPUT -m set --match-set ' + brief + ' src -p tcp --destination-port ' + ports + ' -j ' + types.upper()
|
||||
)
|
||||
else:
|
||||
if not ports:
|
||||
o, e = public.ExecShell(
|
||||
"firewall-cmd --permanent --direct --remove-rule ipv4 filter INPUT 0 -m set --match-set {} src -j {}".format(
|
||||
brief, types.upper()))
|
||||
if e != '':
|
||||
public.ExecShell(
|
||||
'firewall-cmd --permanent --remove-rich-rule=\'rule source ipset="{}" {}\''.format(brief,
|
||||
types.upper()))
|
||||
else:
|
||||
o, e = public.ExecShell(
|
||||
'firewall-cmd --permanent --direct --remove-rule ipv4 filter INPUT 0 -m set --match-set {} src -p tcp --dport {} -j {}'.format(
|
||||
brief, ports, types.upper()))
|
||||
if e != '':
|
||||
public.ExecShell(
|
||||
'firewall-cmd --permanent --remove-rich-rule=\'rule source ipset="' + brief + '" port port="' + ports + '" protocol=tcp ' + types.upper() + '\'')
|
||||
|
||||
commodel.firewall.reload()
|
||||
for country in country_list:
|
||||
ports = country['ports']
|
||||
brief = country['brief']
|
||||
types = country['types']
|
||||
|
||||
print("Rules for relocation areas:{}".format(brief))
|
||||
o, e = public.ExecShell("ipset destroy " + brief)
|
||||
if e != '':
|
||||
public.ExecShell("firewall-cmd --permanent --delete-ipset=" + brief)
|
||||
|
||||
tmp_file = "/tmp/firewall_{}.txt".format(brief)
|
||||
|
||||
if os.path.exists(tmp_file): # bt 9.5.0之后
|
||||
command = '''grep -q "in_bt_country" {filename} || awk '{{print "add in_bt_country_" $2, $3}}' {filename} > {filename}.tmp && mv {filename}.tmp {filename}'''.format(
|
||||
filename=tmp_file
|
||||
)
|
||||
public.ExecShell(command)
|
||||
|
||||
_ipset = "in_bt_country_" + brief
|
||||
public.ExecShell(
|
||||
'ipset create {} hash:net maxelem 1000000; ipset restore -f {}'.format(_ipset, tmp_file)
|
||||
)
|
||||
|
||||
if ports:
|
||||
public.ExecShell(
|
||||
'iptables -I IN_BT_Country -m set --match-set {} src -p tcp --destination-port {} -j {}'.format(
|
||||
_ipset,
|
||||
ports,
|
||||
types.upper())
|
||||
)
|
||||
else:
|
||||
public.ExecShell(
|
||||
'iptables -I IN_BT_Country -m set --match-set {} src -j {}'.format(_ipset, types.upper())
|
||||
)
|
||||
public.ExecShell("systemctl reload BT-FirewallServices")
|
||||
|
||||
else: # bt 9.5.0之前
|
||||
public.M("firewall_country").where("id=?", (country['id'],)).delete()
|
||||
get_tmp = public.dict_obj()
|
||||
get_tmp.country = country['country']
|
||||
get_tmp.types = country['types']
|
||||
get_tmp.ports = country['ports']
|
||||
get_tmp.choose = "all"
|
||||
get_tmp.is_update = True
|
||||
res = firewallmodel.create_countrys(get_tmp)
|
||||
if res['status'] is False:
|
||||
print(f"[ {brief} ] Area rule migrated fail : {res['message']}")
|
||||
|
||||
print("-* Area rule data migration completed...")
|
||||
|
||||
|
||||
# 9.5.0-9.6.0
|
||||
def upgrade_malicious_ip():
|
||||
pl_path = "/www/server/panel/config/firewalld_malicious_ip.pl"
|
||||
if not os.path.exists(pl_path):
|
||||
return
|
||||
|
||||
if not check_ipset_exist("malicious_ipset"):
|
||||
return
|
||||
|
||||
print("-* Migration of malicious IP blocking data in progress...")
|
||||
# 移除旧规则
|
||||
if __isUfw or not __isFirewalld:
|
||||
public.ExecShell('rm -rf /var/log/FIREWALL-ACCESS-LOG*')
|
||||
public.ExecShell('rm -rf /etc/rsyslog.d/firewall-access-log.conf')
|
||||
public.ExecShell('rm -rf /etc/logrotate.d/firewall-access-log')
|
||||
public.ExecShell(
|
||||
'iptables -D INPUT -m conntrack --ctstate NEW -j LOG --log-prefix "FIREWALL-ACCESS: " --log-level 4')
|
||||
public.ExecShell('iptables -D INPUT -j IP-DAILY-LOG')
|
||||
public.ExecShell('iptables -D IP-DAILY-LOG -m recent --name DAILY_IPS --rcheck --seconds 86400 -j RETURN')
|
||||
public.ExecShell(
|
||||
'iptables -D IP-DAILY-LOG -m recent --name DAILY_IPS --set -j LOG --log-prefix "DAILY-IP: " --log-level 4')
|
||||
public.ExecShell('iptables -D IP-DAILY-LOG -j RETURN')
|
||||
public.ExecShell('iptables -X IP-DAILY-LOG')
|
||||
public.ExecShell("iptables -D INPUT -m set --match-set malicious_ipset src -j DROP")
|
||||
else:
|
||||
public.ExecShell(
|
||||
"firewall-cmd --permanent --direct --remove-rule ipv4 filter INPUT 1 -m conntrack --ctstate NEW -j LOG --log-prefix 'FIREWALL-ACCESS: ' --log-level 4")
|
||||
public.ExecShell("firewall-cmd --permanent --direct --remove-rule ipv4 filter INPUT 2 -j IP-DAILY-LOG")
|
||||
public.ExecShell(
|
||||
"firewall-cmd --permanent --direct --remove-rule ipv4 filter IP-DAILY-LOG 0 -m recent --name DAILY_IPS --rcheck --seconds 86400 -j RETURN")
|
||||
public.ExecShell(
|
||||
"firewall-cmd --permanent --direct --remove-rule ipv4 filter IP-DAILY-LOG 1 -m recent --name DAILY_IPS --set -j LOG --log-prefix 'DAILY-IP: ' --log-level 4")
|
||||
public.ExecShell("firewall-cmd --permanent --direct --remove-rule ipv4 filter IP-DAILY-LOG 2 -j RETURN")
|
||||
public.ExecShell("firewall-cmd --permanent --direct --remove-chain ipv4 filter IP-DAILY-LOG")
|
||||
public.ExecShell(
|
||||
"firewall-cmd --permanent --direct --remove-rule ipv4 filter INPUT 0 -m set --match-set malicious_ipset src -j DROP")
|
||||
commodel.firewall.reload()
|
||||
public.ExecShell("ipset destroy malicious_ipset")
|
||||
|
||||
read = public.readFile(pl_path)
|
||||
if read.strip() == "open":
|
||||
tmp_file = "/tmp/firewall_malicious_ip.txt"
|
||||
command = '''grep -q "in_bt" {filename} || awk '{{print "add in_bt_" $2, $3 ,"timeout", 86400}}' {filename} > {filename}.tmp && mv {filename}.tmp {filename}'''.format(
|
||||
filename=tmp_file)
|
||||
public.ExecShell(command)
|
||||
public.ExecShell("sh /www/server/panel/script/open_malicious_ip.sh")
|
||||
public.ExecShell("ipset restore -f {}".format(tmp_file))
|
||||
public.ExecShell("systemctl reload BT-FirewallServices")
|
||||
print("-* Malicious IP blocking data migration completed...")
|
||||
|
||||
|
||||
def upgrade_port_forward(commodel):
|
||||
"""
|
||||
升级端口转发规则
|
||||
"""
|
||||
port_forward_list = commodel.firewall.list_port_forward()
|
||||
print("-* Migrating port forwarding rule data...")
|
||||
for port_forward in port_forward_list:
|
||||
print("Migrating port forwarding rules:{}".format(port_forward))
|
||||
info = {
|
||||
"Family": "ipv4",
|
||||
"Protocol": port_forward["Protocol"],
|
||||
"S_Address": port_forward['S_Address'],
|
||||
"S_Port": port_forward['S_Port'],
|
||||
"T_Address": port_forward['T_Address'],
|
||||
"T_Port": port_forward['T_Port'],
|
||||
}
|
||||
commodel.firewall.port_forward(info, "remove")
|
||||
commodel.iptables.port_forward(info, "add")
|
||||
public.ExecShell("systemctl reload BT-FirewallServices")
|
||||
commodel.firewall.reload()
|
||||
print("-* Port forwarding rule data migration complete...")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
from firewallModelV2.comModel import main as comModel
|
||||
import time
|
||||
|
||||
commodel = comModel()
|
||||
upgrade_iprule(commodel)
|
||||
upgrade_countrys(commodel)
|
||||
upgrade_malicious_ip()
|
||||
upgrade_port_forward(commodel)
|
||||
print("yakpanel: FireWall Migrate Service Finish...")
|
||||
except Exception as e:
|
||||
import traceback
|
||||
|
||||
print("-" * 50)
|
||||
print(traceback.format_exc())
|
||||
print("-" * 50)
|
||||
print(f"Error: FireWall Migrate Error: {e}")
|
||||
15
script/upgrade_flask.sh
Normal file
15
script/upgrade_flask.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
is_flask1=$(/www/server/panel/pyenv/bin/pip3 list|grep 'Flask'|grep ' 1.')
|
||||
if [ "${is_flask1}" = "" ];then
|
||||
exit;
|
||||
fi
|
||||
|
||||
/www/server/panel/pyenv/bin/pip3 install flask -U
|
||||
/www/server/panel/pyenv/bin/pip3 install flask-sock
|
||||
/www/server/panel/pyenv/bin/pip3 install simple-websocket==0.10.0 -I
|
||||
|
||||
rm -f /www/server/panel/script/upgrade_flask.sh
|
||||
bash /www/server/panel/init.sh reload
|
||||
20
script/upgrade_gevent.sh
Normal file
20
script/upgrade_gevent.sh
Normal file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
check_gevent_version(){
|
||||
is_gevent140=$(/www/server/panel/pyenv/bin/pip3 list|grep 'gevent'|grep ' 1.4.0')
|
||||
}
|
||||
|
||||
check_gevent_version
|
||||
if [ "${is_gevent140}" = "" ];then
|
||||
exit;
|
||||
fi
|
||||
|
||||
/www/server/panel/pyenv/bin/pip3 install gevent -U
|
||||
|
||||
check_gevent_version
|
||||
if [ "${is_gevent140}" = "" ];then
|
||||
rm -f /www/server/panel/script/upgrade_gevent.sh
|
||||
bash /www/server/panel/init.sh reload
|
||||
fi
|
||||
19
script/upgrade_telegram.sh
Normal file
19
script/upgrade_telegram.sh
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
echo 'skip telegram'
|
||||
#
|
||||
#is_flask1=$(/www/server/panel/pyenv/bin/pip3 list|grep 'telegram'|grep '0.0.1')
|
||||
#if [ "${is_flask1}" != "" ];then
|
||||
# echo "Y" | /www/server/panel/pyenv/bin/pip3 uninstall telegram
|
||||
#fi
|
||||
#
|
||||
#is_flask2=$(/www/server/panel/pyenv/bin/pip3 list|grep 'python-telegram-bot'|grep '20.3' )
|
||||
#if [ "${is_flask2}" = "" ];then
|
||||
# /www/server/panel/pyenv/bin/pip3 install python-telegram-bot==20.3 -I
|
||||
#else
|
||||
# exit;
|
||||
#fi
|
||||
#
|
||||
#rm -f /www/server/panel/script/upgrade_telegram.sh
|
||||
#bash /www/server/panel/init.sh reload
|
||||
34
script/vhost_virtual.py
Normal file
34
script/vhost_virtual.py
Normal file
@@ -0,0 +1,34 @@
|
||||
#coding: utf-8
|
||||
#多用户放行端口脚本
|
||||
import sys,os
|
||||
os.chdir('/www/server/panel/')
|
||||
sys.path.insert(0,"class/")
|
||||
sys.path.insert(0,"class_v2/")
|
||||
import public
|
||||
import yaml
|
||||
public.print_log("vhost_virtual.py")
|
||||
default_yaml = '{}/vhost_virtual/manifest/config/default.yaml'.format(public.GetConfigValue('setup_path'))
|
||||
not_accept_port_file = '{}/vhost_virtual/config/not_accept_port.pl'.format(public.GetConfigValue('setup_path'))
|
||||
import firewalls
|
||||
get = public.dict_obj()
|
||||
get.ps = "vhost virtual service"
|
||||
if not os.path.exists(not_accept_port_file):
|
||||
with open(default_yaml, 'r') as file:
|
||||
data=yaml.safe_load(file)
|
||||
try:
|
||||
if data["server"].get("address"):
|
||||
http_port=data["server"]["address"]
|
||||
#如果存在:用:分割端口,并取第二个端口和去除空格
|
||||
if ":" in http_port:
|
||||
get.port=http_port.split(":")[1].strip()
|
||||
if get.port !="" and public.M('firewall').where("port=?",(get.port,)).count()<1::
|
||||
firewalls.firewalls().AddAcceptPort(get)
|
||||
if data["server"].get("httpsAddr"):
|
||||
https_port = data["server"]["httpsAddr"]
|
||||
#如果存在:用:分割端口,并取第二个端口和去除空格
|
||||
if ":" in https_port:
|
||||
get.port=https_port.split(":")[1].strip()
|
||||
if get.port !="" and public.M('firewall').where("port=?",(get.port,)).count()<1::
|
||||
firewalls.firewalls().AddAcceptPort(get)
|
||||
except Exception as e:
|
||||
public.print_log("e111--------------:{}".format(e))
|
||||
8
script/warning_list.py
Normal file
8
script/warning_list.py
Normal file
@@ -0,0 +1,8 @@
|
||||
#coding: utf-8
|
||||
import os,sys
|
||||
os.chdir('/www/server/panel')
|
||||
sys.path.insert(0,'class/')
|
||||
import panelWarning,public,json
|
||||
args = public.dict_obj()
|
||||
result = panelWarning.panelWarning().get_list(args)
|
||||
print(json.dumps(result))
|
||||
239
script/webserver-ctl.sh
Normal file
239
script/webserver-ctl.sh
Normal file
@@ -0,0 +1,239 @@
|
||||
#!/bin/bash
|
||||
action=$1
|
||||
panel_path="/www/server/panel" # 面板路径
|
||||
webserver_bin="$panel_path/webserver/sbin/webserver" # nginx二进制文件
|
||||
webserver_conf="$panel_path/webserver/conf/webserver.conf" # nginx配置文件
|
||||
webserver_pid="$panel_path/webserver/logs/webserver.pid" # nginx pid文件
|
||||
|
||||
|
||||
PID=0
|
||||
|
||||
|
||||
|
||||
get_pid() {
|
||||
if [ ! -f "$webserver_pid" ]; then
|
||||
PID=0
|
||||
else
|
||||
PID=$(cat $webserver_pid)
|
||||
if [ "$PID" == "" ]; then
|
||||
PID=0
|
||||
else
|
||||
PID=$(ps aux | grep "$PID" | grep -v grep | awk '{print $2}')
|
||||
PID_11=$(ps aux | grep "$webserver_bin" | grep -v grep | awk '{print $2}')
|
||||
if [ "$PID" != "$PID_11" ]; then
|
||||
PID=0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
PID=$(ps aux | grep "$webserver_bin" | grep -v grep | awk '{print $2}')
|
||||
if [ -z "$PID" ]; then
|
||||
PID=0
|
||||
else
|
||||
PID="$PID"
|
||||
fi
|
||||
|
||||
if [ -z "$PID" ]; then
|
||||
PID=0
|
||||
fi
|
||||
}
|
||||
|
||||
validate_server_files() {
|
||||
if [ ! -f "$webserver_conf" ]; then
|
||||
echo "YakPanel web server configuration not found: $webserver_conf"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$webserver_bin" ]; then
|
||||
echo "YakPanel web server binary not found: $webserver_bin"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
start() {
|
||||
validate_server_files
|
||||
get_pid
|
||||
|
||||
if [ $PID -gt 0 ]; then
|
||||
echo "YakPanel web server is already running with PID ($PID)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -n "Starting YakPanel web server..."
|
||||
if [ -f "$webserver_pid" ]; then
|
||||
rm -f $webserver_pid
|
||||
fi
|
||||
chmod 700 $webserver_bin
|
||||
$webserver_bin -c $webserver_conf
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to start YakPanel web server"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo " Started"
|
||||
}
|
||||
|
||||
stop() {
|
||||
validate_server_files
|
||||
get_pid
|
||||
if [ $PID -eq 0 ]; then
|
||||
echo "YakPanel web server is not running"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -n "Stopping YakPanel web server..."
|
||||
$webserver_bin -c $webserver_conf -s stop
|
||||
|
||||
pids=$(lsof -c webserver|grep LISTEN|awk '{print $2}'|sort -u)
|
||||
for pid in $pids; do
|
||||
kill -9 $pid
|
||||
done
|
||||
|
||||
pids=$(ps aux | grep "$webserver_bin" | grep -v grep | awk '{print $2}')
|
||||
for pid in $pids; do
|
||||
kill -9 $pid
|
||||
child_process=1
|
||||
done
|
||||
if [ "$child_process" = 1 ]; then
|
||||
if [ -f /www/server/panel/data/port.pl ]; then
|
||||
kill -9 $(lsof -t -i:$(cat /www/server/panel/data/port.pl) -sTCP:LISTEN)
|
||||
fi
|
||||
fi
|
||||
|
||||
echo " Stopped"
|
||||
}
|
||||
|
||||
restart() {
|
||||
validate_server_files
|
||||
get_pid
|
||||
echo -n "Restarting YakPanel web server..."
|
||||
if [ $PID -eq 0 ]; then
|
||||
$webserver_bin -c $webserver_conf
|
||||
else
|
||||
$webserver_bin -c $webserver_conf -s reopen
|
||||
fi
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to restart YakPanel web server"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo " Restarted"
|
||||
}
|
||||
|
||||
status() {
|
||||
validate_server_files
|
||||
get_pid
|
||||
if [ $PID -eq 0 ]; then
|
||||
echo "YakPanel web server is not running"
|
||||
else
|
||||
cmdline=/proc/$PID/cmdline
|
||||
if [ ! -f $cmdline ]; then
|
||||
echo "YakPanel web server is not running"
|
||||
rm -f $webserver_pid
|
||||
exit 1
|
||||
fi
|
||||
echo "YakPanel web server is running with PID ($PID)"
|
||||
fi
|
||||
}
|
||||
|
||||
reload() {
|
||||
validate_server_files
|
||||
get_pid
|
||||
if [ $PID -eq 0 ]; then
|
||||
echo "YakPanel web server is not running"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -n "Reloading YakPanel web server..."
|
||||
$webserver_bin -c $webserver_conf -s reload
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to reload YakPanel web server"
|
||||
exit 1
|
||||
fi
|
||||
echo " Reloaded"
|
||||
}
|
||||
|
||||
configtest() {
|
||||
validate_server_files
|
||||
# 检查配置文件正确性,检查程序自动输出检查结果
|
||||
$webserver_bin -c $webserver_conf -t
|
||||
|
||||
}
|
||||
|
||||
download() {
|
||||
tip_file=$panel_path/data/download.pl
|
||||
if [ -f $tip_file ]; then
|
||||
echo "YakPanel web server binary has been downloaded"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 标记已下载
|
||||
echo "1" > $tip_file
|
||||
|
||||
# 获取machine
|
||||
machine=$(uname -m)
|
||||
zip_file=$panel_path/data/webserver-$machine.zip
|
||||
wget -O $zip_file https://node.yakpanel.com/webserver/webserver-$machine.zip
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to download YakPanel web server binary"
|
||||
rm -f $zip_file
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 验证文件hash
|
||||
hash256=$(sha256sum $zip_file | awk '{print $1}')
|
||||
cloud_hash256=$(wget -q -O - https://node.yakpanel.com/webserver/webserver-$machine.txt)
|
||||
if [ "$hash256" != "$cloud_hash256" ]; then
|
||||
echo "Failed to verify YakPanel web server binary"
|
||||
rm -f $zip_file
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 解压文件
|
||||
unzip -o $zip_file -d $panel_path/
|
||||
if [ ! -f $webserver_bin ]; then
|
||||
echo "Failed to extract YakPanel web server binary"
|
||||
rm -f $zip_file
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 删除临时文件
|
||||
rm -f $zip_file
|
||||
# 设置权限
|
||||
chmod 700 $webserver_bin
|
||||
echo "YakPanel web server binary has been downloaded"
|
||||
bash /www/server/panel/init.sh reload
|
||||
}
|
||||
|
||||
|
||||
case "$action" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
restart)
|
||||
restart
|
||||
;;
|
||||
status)
|
||||
status
|
||||
;;
|
||||
reload)
|
||||
reload
|
||||
;;
|
||||
configtest)
|
||||
configtest
|
||||
;;
|
||||
test)
|
||||
configtest
|
||||
;;
|
||||
download)
|
||||
download
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|status|reload|configtest|test|download}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user