36 Commits

Author SHA1 Message Date
Mr.Lin e9e49a8d87 Revise Fail2Ban SSH setup script for clarity and features
Refactor Fail2Ban SSH setup script with improved functionality and structure.
2026-05-26 19:42:25 +08:00
Mr.Lin 82375b31b6 Add SFTP user setup script for SSL certificate management
This script automates the setup of an SFTP user for managing SSL certificates, including creating necessary directories, setting permissions, and configuring SSH.
2026-05-21 21:46:35 +08:00
Mr.Lin e270f0f569 Add Fail2Ban installation and configuration script
This script installs and configures Fail2Ban on Debian 12/13, including old version detection, uninstallation, and whitelist migration. It also performs an automatic ban test while excluding the current public IP.
2026-05-21 21:43:08 +08:00
Mr.Lin 06a38db443 Update Font Awesome link and improve modal script 2026-05-13 16:52:40 +08:00
Mr.Lin 1985b75b96 Delete html/react-day-night-toggle-theme-animation/static/css directory 2026-04-10 20:20:12 +08:00
ldxw 5c8ec75c13 add domain and ip 2026-04-10 20:18:58 +08:00
Mr.Lin 58029b4ad9 Update README.md 2026-04-10 20:09:41 +08:00
Mr.Lin d6f0bccf8f Update README.md 2026-04-10 20:09:06 +08:00
Mr.Lin b7c59ad5db Rename v2ray-rules-需第三方GeoIP.json to v2ray-rules-GeoIP.json 2026-04-10 20:08:28 +08:00
Mr.Lin 76ac67e53a Update v2ray-rules-需第三方GeoIP.json 2026-04-10 20:06:54 +08:00
Mr.Lin 5d13f87340 Rename v2ray-rules.json to v2ray-rules-需第三方GeoIP.json 2026-04-10 19:51:28 +08:00
Mr.Lin 0dcfd7c3f7 Create README.md 2026-04-10 19:51:17 +08:00
Mr.Lin 4fe8c46b91 Rename rules/v2ray/russia-v2ray-rules-dat-rules.json to rules/v2ray/runetfreedom/v2ray-rules.json 2026-04-10 19:40:36 +08:00
Mr.Lin 16a3694d63 Create russia-v2ray-rules-dat-rules.json 2026-04-10 19:37:31 +08:00
Mr.Lin eec9114bf0 Update README.md 2026-03-13 15:24:05 +08:00
Mr.Lin b101b09a33 Add Cloudflare Access logo SVG file 2026-03-04 20:26:23 +08:00
Mr.Lin 7e4d9dee01 Update index.html 2025-08-27 07:44:56 +08:00
Mr.Lin f53d3e7a92 Create normalize-5.0.0.min.css 2025-08-26 19:55:14 +08:00
Mr.Lin 4738bc869d 白昼黑夜切换器
来自: https://www.fecoder.cn/code-fun/react-day-night-toggle-theme-animation
2025-08-26 19:54:43 +08:00
ldxw eea5278b94 add img 2025-08-21 23:02:19 +08:00
ldxw 656cb40077 add bg_main.jpg 2025-07-30 20:54:47 +08:00
ldxw 13ae6e9c09 add jquery-3.5.0.min.j
s'
2025-07-30 20:53:20 +08:00
ldxw 9f0a4f33e7 add 2025-07-30 18:40:23 +08:00
Mr.Lin a6a3bd5859 Create 1.js 2020-02-05 04:19:56 +08:00
Mr.Lin 40811d95b4 Update README.md 2020-02-01 13:19:24 +08:00
Mr.Lin 28d3368e59 Update README.md 2020-02-01 13:17:42 +08:00
Mr.Lin cc242fe773 Update README.md 2020-02-01 13:16:53 +08:00
ldxw 7a0ead0ac0 更新README.md文件 2 2020-02-01 13:14:39 +08:00
ldxw 25e67d1fcf 更新README.md文件 2020-02-01 13:14:09 +08:00
ldxw 71c44e2db8 更新README.md文件 2020-02-01 13:10:32 +08:00
Mr.Lin 4a05db6a19 更改favicon 2020-02-01 12:37:31 +08:00
Mr.Lin eecf654483 Delete favicon.ico 2020-02-01 12:36:09 +08:00
Mr.Lin da5b6786b5 Update README.md 2020-02-01 11:55:44 +08:00
Mr.Lin 3cbf2029b0 Update README.md 2020-02-01 11:54:36 +08:00
Mr.Lin e48b0faa1c Update README.md 2020-02-01 11:26:09 +08:00
Mr.Lin 4985cc4ce4 Update README.md 2020-02-01 11:25:41 +08:00
304 changed files with 32903 additions and 17 deletions
File diff suppressed because one or more lines are too long
Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

-1
View File
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
$(document).ready(function(){var b=10;var e=Math.floor(Math.random()*b)+1;var f="//cdn.jsdelivr.net/gh/ldxw/CDN@v2.0.1/Noisky_files/img/background-image/bg-"+e+".jpg";$("header").css("background-image","url("+f+")");var a=$(".navigation-wrapper");function c(){a.on(function(){a.toggleClass("visible animated bounceOutUp")});a.toggleClass("animated bounceInDown animated bounceOutUp")}$(".btn-mobile-menu").click(function(){if(a.css("display")==="block"){c()}else{a.toggleClass("visible animated bounceInDown")}$(".btn-mobile-menu_icon").toggleClass("fa fa-list fa fa-angle-up animated fadeIn")});$(".panel-main").on("click",":not(.mobile,.btn-mobile-menu,.navigation-wrapper)",function(){if(a.hasClass("bounceInDown")){c();$(".btn-mobile-menu_icon").toggleClass("fa fa-list fa fa-angle-up animated fadeIn")}});a.click(function(g){g.stopPropagation()});$(".year").html(new Date().getFullYear());(function d(){$.ajax({url:"https://v1.hitokoto.cn/?encode=json&charset=utf-8",dataType:"jsonp",async:true,jsonp:"callback",jsonpCallback:"hitokoto",success:function(g){$("#hitokoto").html("<p>"+g.hitokoto+"</p>")},error:function(){$("#hitokoto").html("<p>读取数据失败了的说……_(:з」∠)_</p>")}})})()});
+12 -3
View File
@@ -1,3 +1,12 @@
# CDN # jsdelivr cdn
妞.我爱你静态文件
//cdn.jsdelivr.net/gh/ldxw/CDN@ [![](https://data.jsdelivr.com/v1/package/gh/ldxw/cdn/badge)](https://www.jsdelivr.com/package/gh/ldxw/cdn)
# 目录说明
- [图片壁纸](https://github.com/ldxw/cdn/tree/master/image) | image
- [logo](https://github.com/ldxw/cdn/tree/master/logo) | logo
- [静态文件](https://github.com/ldxw/cdn/tree/master/static) | static
# cdn链接
https://cdn.jsdelivr.net/gh/ldxw/cdn@master/
+173
View File
@@ -0,0 +1,173 @@
#!/bin/bash
set -e
# 自用创建上传证书
CERT_USER="3x-ui-cert"
CERT_DIR="/mnt/docker/docker-compose/dpanel/dpanel/compose/3x-ui/data/cert"
SFTP_ROOT="/srv/sftp/${CERT_USER}"
SFTP_DIR="${SFTP_ROOT}/cert"
echo "========== 3x-ui 证书 SFTP 用户一条龙配置 =========="
if [ "$(id -u)" != "0" ]; then
echo "错误:请使用 root 用户执行"
exit 1
fi
echo
echo "1. 安装依赖..."
apt update
apt install -y openssh-server acl
echo
echo "2. 创建/检查 3x-ui 证书目录..."
mkdir -p "$CERT_DIR"
echo
echo "3. 创建/检查 SFTP 用户:$CERT_USER"
if id "$CERT_USER" >/dev/null 2>&1; then
echo "用户已存在:$CERT_USER"
else
useradd --badname -m -s /usr/sbin/nologin "$CERT_USER"
echo "请设置 $CERT_USER 的密码:"
passwd "$CERT_USER"
fi
CERT_UID="$(id -u "$CERT_USER")"
CERT_GID="$(id -g "$CERT_USER")"
echo "当前用户 UID$CERT_UID"
echo "当前用户 GID$CERT_GID"
echo
echo "4. 创建 SFTP 隔离目录..."
mkdir -p "$SFTP_DIR"
# Chroot 根目录必须 root 拥有,且不能给普通用户写权限
chown root:root /srv
chmod 755 /srv
mkdir -p /srv/sftp
chown root:root /srv/sftp
chmod 755 /srv/sftp
chown root:root "$SFTP_ROOT"
chmod 755 "$SFTP_ROOT"
echo
echo "5. 重新 bind 挂载证书目录..."
if mountpoint -q "$SFTP_DIR"; then
umount "$SFTP_DIR" 2>/dev/null || umount -l "$SFTP_DIR"
fi
mount --bind "$CERT_DIR" "$SFTP_DIR"
echo
echo "6. 修复 /etc/fstab 自动挂载..."
cp /etc/fstab "/etc/fstab.bak.$(date +%F-%H%M%S)"
TMP_FSTAB="$(mktemp)"
awk -v sftp="$SFTP_DIR" '$2 != sftp {print}' /etc/fstab > "$TMP_FSTAB"
cat "$TMP_FSTAB" > /etc/fstab
rm -f "$TMP_FSTAB"
echo "$CERT_DIR $SFTP_DIR none bind 0 0" >> /etc/fstab
systemctl daemon-reload 2>/dev/null || true
echo
echo "7. 给真实路径上级目录添加穿透权限..."
P="$(dirname "$CERT_DIR")"
while [ "$P" != "/" ]; do
setfacl -m u:${CERT_USER}:--x "$P" 2>/dev/null || true
setfacl -m m::rwx "$P" 2>/dev/null || true
P="$(dirname "$P")"
done
echo
echo "8. 修复 cert 目录本身权限..."
chgrp -R "$CERT_GID" "$CERT_DIR"
chmod -R g+rwX "$CERT_DIR"
find "$CERT_DIR" -type d -exec chmod g+s {} \;
# 给 SFTP 用户完整读写权限
setfacl -R -m u:${CERT_USER}:rwX "$CERT_DIR"
setfacl -R -m u:${CERT_UID}:rwX "$CERT_DIR"
setfacl -R -m m::rwx "$CERT_DIR"
# 默认继承权限:以后新建文件夹/文件也能写
find "$CERT_DIR" -type d -exec setfacl -m d:u:${CERT_USER}:rwx {} \;
find "$CERT_DIR" -type d -exec setfacl -m d:u:${CERT_UID}:rwx {} \;
find "$CERT_DIR" -type d -exec setfacl -m d:m::rwx {} \;
echo
echo "9. 可选给 UID 1000 读取权限,避免覆盖当前用户..."
if [ "$CERT_UID" != "1000" ]; then
setfacl -R -m u:1000:rX "$CERT_DIR" 2>/dev/null || true
find "$CERT_DIR" -type d -exec setfacl -m d:u:1000:rX {} \; 2>/dev/null || true
else
echo "当前 $CERT_USER 就是 UID 1000,跳过 u:1000:rX,避免覆盖写权限"
fi
# 最终再覆盖一次,确保 3x-ui-cert 一定是 rwx
setfacl -R -m u:${CERT_USER}:rwX "$CERT_DIR"
setfacl -R -m m::rwx "$CERT_DIR"
find "$CERT_DIR" -type d -exec setfacl -m d:u:${CERT_USER}:rwx {} \;
find "$CERT_DIR" -type d -exec setfacl -m d:m::rwx {} \;
echo
echo "10. 写入 SSH SFTP 限制配置..."
rm -f /etc/ssh/sshd_config.d/3x-ui-cert-sftp.conf
sed -i '/# BEGIN 3X_UI_CERT_SFTP/,/# END 3X_UI_CERT_SFTP/d' /etc/ssh/sshd_config
cat >>/etc/ssh/sshd_config <<'SSHEOF'
# BEGIN 3X_UI_CERT_SFTP
Match User 3x-ui-cert
ChrootDirectory /srv/sftp/3x-ui-cert
ForceCommand internal-sftp -d /cert
PasswordAuthentication yes
PubkeyAuthentication no
PermitTTY no
AllowTcpForwarding no
X11Forwarding no
AllowAgentForwarding no
# END 3X_UI_CERT_SFTP
SSHEOF
echo
echo "11. 检查并重启 SSH..."
sshd -t
systemctl restart ssh 2>/dev/null || systemctl restart sshd 2>/dev/null || service ssh restart
echo
echo "12. 测试 SFTP 实际路径写入..."
runuser -u "$CERT_USER" -- touch "$SFTP_DIR/.sftp_write_test"
rm -f "$SFTP_DIR/.sftp_write_test"
echo
echo "13. 测试新建域名目录和证书文件..."
TEST_DIR="$SFTP_DIR/.test-domain"
runuser -u "$CERT_USER" -- mkdir -p "$TEST_DIR"
runuser -u "$CERT_USER" -- touch "$TEST_DIR/fullchain.pem"
runuser -u "$CERT_USER" -- touch "$TEST_DIR/privkey.pem"
rm -rf "$TEST_DIR"
echo
echo "========== 配置完成 =========="
echo
echo "SFTP 用户:$CERT_USER"
echo "SFTP 端口:22"
echo "SFTP 上传目录:/cert"
echo "真实目录:$CERT_DIR"
echo
echo "第三方连接命令:"
echo "sftp -o PubkeyAuthentication=no -o PreferredAuthentications=password ${CERT_USER}@服务器IP"
echo
echo "当前 ACL"
getfacl "$CERT_DIR"
echo
echo "重点确认应该看到:"
echo "user:${CERT_USER}:rwx"
echo "default:user:${CERT_USER}:rwx"
+676
View File
@@ -0,0 +1,676 @@
#!/bin/bash
set -euo pipefail
# Debian 12/13 Fail2Ban SSH 修复/安装脚本
#
# 功能:
# - 自动识别当前 SSH 客户端 IP,并加入 ignoreip,避免误封自己
# - 检测旧版 Fail2Ban,可选择修复、删除重装、仅检测、安全测试、紧急救援
# - 使用 systemd journal backend,避免 /var/log/auth.log 不存在
# - 使用 nftables 封禁
# - 启动后等待 fail2ban socket 和 sshd jail 真正 ready
# - 安全测试只封禁文档测试 IP 203.0.113.123,并立即解封
#
# 用法:
# bash setup_f2b_fixed.sh
#
# 可选:手动追加白名单 IP
# IGNORE_IPS="127.0.0.1/8 ::1 你的公网IP" bash setup_f2b_fixed.sh
#
# 救援模式:
# bash setup_f2b_fixed.sh --rescue
#
# 修复模式:
# bash setup_f2b_fixed.sh --repair
#
# 状态检测:
# bash setup_f2b_fixed.sh --status
#
# 安全封禁测试:
# bash setup_f2b_fixed.sh --safe-test
CONF_FILE="/etc/fail2ban/jail.d/99-sshd-hardening.local"
F2B_LOCAL="/etc/fail2ban/fail2ban.local"
TS="$(date +%Y%m%d-%H%M%S)"
BACKUP_DIR="/root/fail2ban-backup-${TS}"
DEFAULT_IGNORE_IPS="127.0.0.1/8 ::1"
IGNORE_IPS="${IGNORE_IPS:-$DEFAULT_IGNORE_IPS}"
PRESERVED_IGNORE_IPS=""
TEST_IP="203.0.113.123"
need_root() {
if [ "$(id -u)" -ne 0 ]; then
echo "请以 root 权限运行"
exit 1
fi
}
dedupe_words() {
echo "$*" | tr ' ' '\n' | awk 'NF && !seen[$0]++' | paste -sd' ' -
}
get_ssh_client_ip() {
if [ -n "${SSH_CONNECTION:-}" ]; then
echo "$SSH_CONNECTION" | awk '{print $1}'
return 0
fi
if [ -n "${SSH_CLIENT:-}" ]; then
echo "$SSH_CLIENT" | awk '{print $1}'
return 0
fi
echo ""
}
collect_old_ignoreips() {
if [ ! -d /etc/fail2ban ]; then
return 0
fi
find /etc/fail2ban -type f \( -name "*.local" -o -name "*.conf" \) -print0 2>/dev/null |
while IFS= read -r -d '' file; do
awk '
/^[[:space:]]*ignoreip[[:space:]]*=/ {
val=$0
sub(/^[^=]*=/, "", val)
sub(/[[:space:]]*#.*/, "", val)
gsub(/^[[:space:]]+|[[:space:]]+$/, "", val)
if (val != "") print val
}
' "$file"
done | tr '\n' ' '
}
backup_fail2ban_config() {
if [ -d /etc/fail2ban ]; then
echo "正在备份旧配置到:$BACKUP_DIR"
mkdir -p "$BACKUP_DIR"
cp -a /etc/fail2ban/. "$BACKUP_DIR/" 2>/dev/null || true
echo "备份完成:$BACKUP_DIR"
else
echo "未发现 /etc/fail2ban,跳过备份。"
fi
}
detect_ssh_unit() {
if systemctl cat ssh.service >/dev/null 2>&1; then
echo "ssh.service"
return 0
fi
if systemctl cat sshd.service >/dev/null 2>&1; then
echo "sshd.service"
return 0
fi
echo "ssh.service"
}
detect_ssh_ports() {
local sshd_bin=""
if command -v sshd >/dev/null 2>&1; then
sshd_bin="$(command -v sshd)"
elif [ -x /usr/sbin/sshd ]; then
sshd_bin="/usr/sbin/sshd"
fi
if [ -n "$sshd_bin" ]; then
"$sshd_bin" -T 2>/dev/null | awk '$1 == "port" {print $2}' | paste -sd, - || true
fi
}
clear_fail2ban_nftables() {
if ! command -v nft >/dev/null 2>&1; then
echo "未检测到 nft 命令,跳过 nftables 清理。"
return 0
fi
echo "正在清理 Fail2Ban 相关 nftables 表..."
nft list tables 2>/dev/null |
awk 'tolower($0) ~ /f2b|fail2ban/ {print $2, $3}' |
while read -r family table; do
if [ -n "${family:-}" ] && [ -n "${table:-}" ]; then
echo "删除 nftables 表:$family $table"
nft delete table "$family" "$table" 2>/dev/null || true
fi
done
}
rescue_fail2ban() {
echo
echo "========== 紧急救援模式 =========="
echo "将停止并禁用 Fail2Ban,同时清理 Fail2Ban nftables 表。"
local ssh_client_ip
ssh_client_ip="$(get_ssh_client_ip)"
if command -v fail2ban-client >/dev/null 2>&1; then
if [ -n "$ssh_client_ip" ]; then
echo "尝试解封当前 SSH 客户端 IP$ssh_client_ip"
fail2ban-client set sshd unbanip "$ssh_client_ip" 2>/dev/null || true
fi
echo "尝试执行全局解封..."
fail2ban-client unban --all 2>/dev/null || true
fi
echo "停止 Fail2Ban..."
systemctl stop fail2ban 2>/dev/null || true
echo "禁用 Fail2Ban 开机启动..."
systemctl disable fail2ban 2>/dev/null || true
echo "清理旧 socket 文件..."
rm -f /run/fail2ban/fail2ban.sock
rm -f /var/run/fail2ban/fail2ban.sock
clear_fail2ban_nftables
echo
echo "救援完成。"
echo "现在可以重新尝试 SSH 登录。"
echo "恢复后建议运行:"
echo " bash setup_f2b_fixed.sh --repair"
echo "=================================="
echo
}
wait_fail2ban_ready() {
local timeout="${1:-45}"
local i=0
echo
echo "正在等待 Fail2Ban 完全启动,最多等待 ${timeout} 秒..."
while [ "$i" -lt "$timeout" ]; do
if systemctl is-active --quiet fail2ban; then
if fail2ban-client ping >/dev/null 2>&1; then
if fail2ban-client status sshd >/dev/null 2>&1; then
echo "Fail2Ban 已启动,socket 正常,sshd jail 已加载。"
return 0
fi
fi
fi
sleep 1
i=$((i + 1))
done
echo
echo "Fail2Ban 未能在 ${timeout} 秒内完全 ready。"
echo
echo "systemd 状态:"
systemctl status fail2ban --no-pager -l || true
echo
echo "socket 检测:"
if [ -S /run/fail2ban/fail2ban.sock ]; then
echo "socket 存在:/run/fail2ban/fail2ban.sock"
elif [ -S /var/run/fail2ban/fail2ban.sock ]; then
echo "socket 存在:/var/run/fail2ban/fail2ban.sock"
else
echo "socket 不存在。"
fi
echo
echo "fail2ban-client ping"
fail2ban-client ping || true
echo
echo "fail2ban-client status"
fail2ban-client status || true
echo
echo "最近 Fail2Ban 日志:"
journalctl -u fail2ban -n 100 --no-pager || true
return 1
}
show_fail2ban_status() {
echo
echo "========== Fail2Ban 当前状态 =========="
if ! command -v fail2ban-client >/dev/null 2>&1; then
echo "未检测到 fail2ban-client。"
echo "======================================"
return 0
fi
echo
echo "[1/4] systemd 服务状态:"
if systemctl is-active --quiet fail2ban; then
echo "systemd: active"
else
echo "systemd: inactive 或 failed"
fi
systemctl status fail2ban --no-pager -l || true
echo
echo "[2/4] socket 检测:"
if [ -S /run/fail2ban/fail2ban.sock ]; then
echo "socket 存在:/run/fail2ban/fail2ban.sock"
elif [ -S /var/run/fail2ban/fail2ban.sock ]; then
echo "socket 存在:/var/run/fail2ban/fail2ban.sock"
else
echo "socket 不存在:/run/fail2ban/fail2ban.sock"
echo "这通常表示 Fail2Ban 还没初始化完成,或启动后又异常退出。"
fi
echo
echo "[3/4] fail2ban-client ping"
if fail2ban-client ping >/dev/null 2>&1; then
fail2ban-client ping
else
echo "fail2ban-client 无法连接 socket。"
fi
echo
echo "[4/4] jail 状态:"
if fail2ban-client status >/dev/null 2>&1; then
fail2ban-client status
local jails
jails="$(fail2ban-client status 2>/dev/null | sed -n 's/.*Jail list:[[:space:]]*//p' | tr ',' ' ' | xargs || true)"
if [ -n "$jails" ]; then
for jail in $jails; do
echo
echo "---------- jail: $jail ----------"
fail2ban-client status "$jail" 2>/dev/null || true
done
fi
else
echo "Fail2Ban 服务可能 active,但 client/socket 尚不可用。"
echo
echo "最近 Fail2Ban 日志:"
journalctl -u fail2ban -n 80 --no-pager || true
fi
echo "======================================"
echo
}
fix_fail2ban_startup() {
echo
echo "开始修复 Fail2Ban 启动/socket 状态..."
if ! command -v fail2ban-client >/dev/null 2>&1; then
echo "未检测到 fail2ban-client,请先安装 Fail2Ban。"
return 1
fi
echo "停止 Fail2Ban..."
systemctl stop fail2ban 2>/dev/null || true
echo "清理旧 socket 文件..."
rm -f /run/fail2ban/fail2ban.sock
rm -f /var/run/fail2ban/fail2ban.sock
echo "测试配置..."
fail2ban-client -t
echo "重新启动 Fail2Ban..."
systemctl start fail2ban
wait_fail2ban_ready 45
echo "启动/socket 状态修复完成。"
}
install_packages() {
echo "正在安装 Fail2Ban 与 nftables..."
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y fail2ban nftables python3-systemd
echo "安装完成后先停止 Fail2Ban,等待写入安全配置后再启动..."
systemctl stop fail2ban 2>/dev/null || true
rm -f /run/fail2ban/fail2ban.sock
rm -f /var/run/fail2ban/fail2ban.sock
}
write_fail2ban_config() {
local ssh_client_ip
local old_ignore
local ssh_ports
local ssh_unit
local manual_ip=""
ssh_client_ip="$(get_ssh_client_ip)"
old_ignore="$(collect_old_ignoreips || true)"
if [ -n "$PRESERVED_IGNORE_IPS" ]; then
echo "检测到删除前保留的旧 ignoreip:$PRESERVED_IGNORE_IPS"
IGNORE_IPS="$IGNORE_IPS $PRESERVED_IGNORE_IPS"
fi
if [ -n "$old_ignore" ]; then
echo "检测到当前配置中的 ignoreip$old_ignore"
IGNORE_IPS="$IGNORE_IPS $old_ignore"
fi
if [ -n "$ssh_client_ip" ]; then
echo "检测到当前 SSH 客户端 IP$ssh_client_ip"
echo "会自动加入 ignoreip,避免误封当前登录来源。"
IGNORE_IPS="$IGNORE_IPS $ssh_client_ip"
else
echo "未检测到 SSH_CONNECTION,可能不是通过 SSH 执行。"
echo "强烈建议手动指定白名单,例如:"
echo ' IGNORE_IPS="127.0.0.1/8 ::1 你的公网IP" bash setup_f2b_fixed.sh'
echo
read -r -t 20 -p "请输入要追加的白名单 IP,留空跳过,20 秒后自动跳过:" manual_ip || true
manual_ip="${manual_ip:-}"
if [ -n "$manual_ip" ]; then
IGNORE_IPS="$IGNORE_IPS $manual_ip"
fi
fi
IGNORE_IPS="$(dedupe_words "$IGNORE_IPS")"
ssh_ports="$(detect_ssh_ports)"
ssh_ports="${ssh_ports:-22}"
ssh_unit="$(detect_ssh_unit)"
echo
echo "SSH 端口:$ssh_ports"
echo "SSH systemd unit$ssh_unit"
echo "最终 ignoreip$IGNORE_IPS"
echo
mkdir -p /etc/fail2ban/jail.d
if [ -f "$CONF_FILE" ]; then
cp "$CONF_FILE" "${CONF_FILE}.bak.${TS}"
fi
if [ -f "$F2B_LOCAL" ]; then
cp "$F2B_LOCAL" "${F2B_LOCAL}.bak.${TS}"
fi
echo "写入 SSH jail 配置:$CONF_FILE"
cat > "$CONF_FILE" <<EOF
[DEFAULT]
ignoreip = ${IGNORE_IPS}
# 使用 systemd journal,避免 /var/log/auth.log 不存在
backend = systemd
# 使用 nftables
banaction = nftables-multiport
banaction_allports = nftables-allports
# 避免反向 DNS 查询拖慢
usedns = no
# 基础封禁时间:10 天
bantime = 10d
# 统计窗口:1 小时
findtime = 1h
# 1 小时内失败 3 次即封禁
maxretry = 3
# 阶梯式封禁
bantime.increment = true
bantime.factor = 1
bantime.maxtime = 20w
bantime.rndtime = 10m
[sshd]
enabled = true
port = ${ssh_ports}
filter = sshd[mode=aggressive]
# Debian 通常是 ssh.service;部分系统可能是 sshd.service
journalmatch = _SYSTEMD_UNIT=${ssh_unit}
EOF
echo "写入 Fail2Ban 本地配置:$F2B_LOCAL"
cat > "$F2B_LOCAL" <<EOF
[Definition]
# IPv6 自动处理。写在 fail2ban.local,可避免 allowipv6 警告。
allowipv6 = auto
# 为阶梯式封禁保留更久历史记录
dbpurgeage = 180d
EOF
echo
echo "测试 Fail2Ban 配置..."
fail2ban-client -t
echo
echo "启用并重启 Fail2Ban..."
systemctl enable fail2ban
systemctl restart fail2ban
wait_fail2ban_ready 45
echo
echo "修复/安装完成。"
echo "当前 ignoreip$IGNORE_IPS"
echo
}
safe_ban_test() {
echo
echo "========== 安全封禁测试 =========="
echo "测试 IP$TEST_IP"
echo "说明:这是文档测试网段,不是你的真实 IP。测试后会立即解封。"
if ! command -v fail2ban-client >/dev/null 2>&1; then
echo "fail2ban-client 不存在,无法测试。"
echo "================================="
return 0
fi
if ! systemctl is-active --quiet fail2ban; then
echo "Fail2Ban 当前未运行,尝试启动..."
systemctl start fail2ban || true
wait_fail2ban_ready 45 || true
fi
if ! fail2ban-client status sshd >/dev/null 2>&1; then
echo "sshd jail 不存在或未启动,无法测试。"
echo "请先确认:fail2ban-client status sshd"
echo "================================="
return 0
fi
echo
echo "执行测试封禁..."
fail2ban-client set sshd banip "$TEST_IP" || true
echo
echo "封禁后 sshd jail 状态:"
fail2ban-client status sshd || true
echo
echo "立即解封测试 IP..."
fail2ban-client set sshd unbanip "$TEST_IP" || true
echo
echo "解封后 sshd jail 状态:"
fail2ban-client status sshd || true
echo
echo "安全封禁测试完成。"
echo "================================="
echo
}
ask_safe_test() {
echo
local testyn=""
read -r -t 20 -p "是否进行安全封禁测试?只封测试 IP ${TEST_IP} 并立即解封。[y/N] " testyn || true
testyn="${testyn:-n}"
if [[ "$testyn" =~ ^[Yy]$ ]]; then
safe_ban_test
else
echo "已跳过安全封禁测试。"
fi
}
repair_current_install() {
echo
echo "准备修复当前 Fail2Ban 配置,不删除软件包..."
backup_fail2ban_config
echo "先停止 Fail2Ban,避免修复过程中误封当前连接..."
systemctl stop fail2ban 2>/dev/null || true
rm -f /run/fail2ban/fail2ban.sock
rm -f /var/run/fail2ban/fail2ban.sock
if ! command -v fail2ban-client >/dev/null 2>&1; then
install_packages
else
echo "确保 nftables 与 python3-systemd 已安装..."
export DEBIAN_FRONTEND=noninteractive
apt-get install -y nftables python3-systemd || true
fi
write_fail2ban_config
}
remove_and_reinstall() {
echo
echo "准备删除旧版 Fail2Ban 并重新安装..."
PRESERVED_IGNORE_IPS="$(collect_old_ignoreips || true)"
if [ -n "$PRESERVED_IGNORE_IPS" ]; then
echo "已保存旧版 ignoreip$PRESERVED_IGNORE_IPS"
fi
backup_fail2ban_config
echo "停止 Fail2Ban..."
systemctl stop fail2ban 2>/dev/null || true
echo "卸载 Fail2Ban..."
apt-get remove --purge -y fail2ban || true
echo "清理旧配置目录..."
rm -rf /etc/fail2ban
rm -f /run/fail2ban/fail2ban.sock
rm -f /var/run/fail2ban/fail2ban.sock
install_packages
write_fail2ban_config
}
main_menu() {
local ssh_client_ip
ssh_client_ip="$(get_ssh_client_ip)"
echo
echo "========== Fail2Ban SSH 修复/安装脚本 =========="
if [ -n "$ssh_client_ip" ]; then
echo "当前 SSH 客户端 IP$ssh_client_ip"
echo "该 IP 会自动加入 ignoreip。"
else
echo "未检测到当前 SSH 客户端 IP。"
echo "建议运行时手动指定 IGNORE_IPS。"
fi
echo "================================================"
echo
if command -v fail2ban-client >/dev/null 2>&1; then
echo "检测到系统已安装 Fail2Ban。"
echo
echo "请选择操作:"
echo " 1) 修复当前安装,不删除旧版,推荐"
echo " 2) 删除旧版 Fail2Ban 后重新安装"
echo " 3) 保留旧版,只检测状态,并可安全封禁测试"
echo " 4) 紧急救援:停止/禁用 Fail2Ban 并清理 Fail2Ban nftables 表"
echo " 5) 修复 Fail2Ban 启动/socket 状态"
echo " 0) 退出"
echo
local choice=""
read -r -p "请输入选项 [1/2/3/4/5/0],默认 1" choice
choice="${choice:-1}"
case "$choice" in
1)
repair_current_install
show_fail2ban_status
ask_safe_test
;;
2)
remove_and_reinstall
show_fail2ban_status
ask_safe_test
;;
3)
show_fail2ban_status
ask_safe_test
;;
4)
rescue_fail2ban
;;
5)
fix_fail2ban_startup
show_fail2ban_status
;;
0)
echo "已退出。"
exit 0
;;
*)
echo "无效选项,退出。"
exit 1
;;
esac
else
echo "未检测到 Fail2Ban,将进行全新安装。"
install_packages
write_fail2ban_config
show_fail2ban_status
ask_safe_test
fi
}
need_root
case "${1:-}" in
--rescue)
rescue_fail2ban
exit 0
;;
--repair)
repair_current_install
show_fail2ban_status
ask_safe_test
exit 0
;;
--status)
show_fail2ban_status
exit 0
;;
--safe-test)
safe_ban_test
exit 0
;;
esac
main_menu
@@ -0,0 +1 @@
button,hr,input{overflow:visible}audio,canvas,progress,video{display:inline-block}progress,sub,sup{vertical-align:baseline}html{font-family:sans-serif;line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0} menu,article,aside,details,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,input{}button,select{text-transform:none}[type=submit], [type=reset],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}[hidden],template{display:none}/*# sourceMappingURL=normalize.min.css.map */
@@ -0,0 +1,443 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<title>白昼黑夜切换器</title>
<link
rel="stylesheet"
href="https://cdn.oplist.org/gh/ldxw/cdn@master/code/html/react-day-night-toggle-theme-animation/css/normalize-5.0.0.min.css"
/>
<style>
@import url("https://fonts.googleapis.com/css?family=Gloria+Hallelujah");
* {
box-sizing: border-box;
}
body {
background: #7ec0ee;
margin: 0;
padding: 0;
height: 100vh;
width: 100vw;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
font-size: 3rem;
font-family: "Gloria Hallelujah", cursive;
font-weight: bold;
color: #fafafa;
}
</style>
</head>
<body>
<div id="root">Loading...</div>
<!-- 修正CDN链接格式,删除多余空引号 -->
<script src="https://cdn.jsdmirror.cn/npm/react@16.3.0-alpha.1/umd/react.development.js"></script>
<script src="https://cdn.jsdmirror.cn/npm/react-dom@16.3.0-alpha.1/umd/react-dom.development.js"></script>
<script src="https://cdn.jsdmirror.cn/npm/styled-components@2.2.1/dist/styled-components.min.js"></script>
<script>
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true,
});
} else {
obj[key] = value;
}
return obj;
}
function _extends() {
_extends =
Object.assign ||
function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
const {
React,
ReactDOM,
styled: { default: styled, keyframes },
} = window;
const { createContext, Component } = React;
const { render } = ReactDOM;
const THEME_CONSTANTS = {
NIGHT: "night",
DAY: "day",
};
// 修正时间判断逻辑(原d<6语法错误,应取getHours()
const d = new Date();
const hours = d.getHours();
const defaultValue =
hours < 6 || hours > 19 ? THEME_CONSTANTS.NIGHT : THEME_CONSTANTS.DAY;
const ThemeContext = createContext(defaultValue);
const $night = "#111";
const $day = "#7ec0ee";
const $starColor = "#fefcd7";
const $sunColor = "#f9fa57";
const $size = 25;
const $transitionDuration = 0.75;
const $skyChangeDuration = 0.5;
const $positionX = 20;
const $positionY = 10;
const Container = styled.div`
background-color: ${(p) =>
p.theme === THEME_CONSTANTS.NIGHT ? $night : $day};
height: 100vh;
transition: background ${$skyChangeDuration} ease;
width: 100vw;
`;
// 修正BigStar组件,补充闭合花括号
const BigStar = styled.div`
border-radius: 100%;
height: ${$size}vh;
left: ${$positionX}vw;
position: absolute;
top: ${$positionY}vh;
transition: transform ${$transitionDuration}s ease 0s;
width: ${$size}vh;
z-index: 2;
`;
// 修正extend语法对齐
const Moon = BigStar.extend`
background-color: ${$starColor};
box-shadow: 0px 0px 50px #eee;
overflow: hidden;
transform: ${(p) =>
p.theme === THEME_CONSTANTS.NIGHT
? "scale(1)"
: "scale(0.5) translateX(-75vw) translateY(-120vh)"};
&:after {
box-shadow: inset 10px 10px 10px #bbb;
border-radius: 100%;
content: "";
display: block;
height: 100%;
width: 100%;
}
`;
const Craters = styled.div`
background-color: #ddd;
border-radius: 100%;
opacity: 0.5;
position: absolute;
top: 15%;
left: 15%;
height: ${$size * 0.25}vh;
width: ${$size * 0.25}vh;
&:before {
background-color: #ddd;
content: "";
display: block;
border-radius: 100%;
height: 50%;
width: 50%;
position: absolute;
top: 110%;
}
&:after {
background-color: #ddd;
content: "";
display: block;
border-radius: 100%;
height: 30%;
width: 30%;
position: absolute;
top: 80%;
right: 110%;
}
`;
const CloudMove = keyframes`
0% {
transform: translateX(300%);
opacity: 0;
}
50% {
opacity: .75;
}
25%, 75% {
opacity: 0;
}
100% {
transform: translateX(-300%);
opacity: 0;
}
`;
const Star = styled.div`
background: ${(p) =>
p.theme === THEME_CONSTANTS.NIGHT ? $starColor : $day};
border-radius: 100%;
height: ${(p) => p.size}px;
left: ${(p) => p.x}vw;
position: absolute;
top: ${(p) => p.y}vh;
opacity: ${(p) => (p.theme === THEME_CONSTANTS.NIGHT ? 1 : 0)};
transition: background ${$transitionDuration}s ease 0s,
opacity ${$transitionDuration}s ease 0s;
width: ${(p) => p.size}px;
z-index: 1;
`;
// 修正extend语法对齐
const Sun = BigStar.extend`
background-color: ${$sunColor};
box-shadow: 0px 0px 150px yellow;
transform: ${(p) =>
p.theme === THEME_CONSTANTS.NIGHT
? "scale(0.5) translateX(75vw) translateY(200vh)"
: "scale(1)"};
`;
const Features = styled.div`
height: 50%;
width: 50%;
position: absolute;
right: 15%;
bottom: 15%;
transition: opacity 0.15s ease 0s;
transition-delay: 0.75s;
opacity: ${(p) => (p.show ? "1" : "0")};
`;
const Blink = keyframes`
0%, 5%, 7%, 100% {
transform: scaleY(1);
}
6% {
transform: scaleY(0);
}
`;
const Eye = styled.div`
border-radius: 100%;
height: 15%;
width: 15%;
background-color: black;
position: absolute;
top: 25%;
animation: ${Blink} 10s infinite 1s;
transform-origin: center;
&:after {
background-color: #fff;
border-radius: 100%;
content: "";
height: 40%;
width: 40%;
position: absolute;
top: 5%;
right: 30%;
display: block;
}
`;
const LeftEye = Eye.extend`
left: 25%;
`;
const RightEye = Eye.extend`
left: 75%;
`;
const Smile = styled.div`
width: 55%;
height: 25%;
border-style: solid;
border-width: 4px;
border-top-color: transparent;
border-left-color: transparent;
border-right-color: transparent;
border-bottom-color: black;
border-radius: 100%;
position: absolute;
top: 35%;
left: 50%;
transform: translateX(-50%);
`;
const Face = ({ show }) =>
React.createElement(
Features,
{ show: show },
React.createElement(LeftEye, null),
React.createElement(RightEye, null),
React.createElement(Smile, null)
);
const ThemeToggleInput = styled.input`
cursor: pointer;
z-index: 3;
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
opacity: 0;
&:checked ~ div {
transform: translateX(20px);
}
`;
const ThemeToggleContainer = styled.div`
left: 10px;
position: fixed;
top: 10px;
width: 50px;
height: 30px;
border: 4px solid
${(p) => (p.theme === THEME_CONSTANTS.NIGHT ? "#fff" : "dodgerblue")};
border-radius: 30px;
transition: border-color 0.25s ease 0s;
`;
const ThemeToggleSwitch = styled.div`
height: 20px;
width: 20px;
background: ${(p) =>
p.theme === THEME_CONSTANTS.NIGHT ? $starColor : $sunColor};
border-radius: 100%;
position: absolute;
top: 50%;
margin-top: -10px;
left: 1px;
transform: translateX(0px);
transition: background 0.25s ease 0s, transform 0.25s ease 0s;
`;
const ThemeToggle = ({ onChange, checked }) =>
React.createElement(
ThemeContext.Consumer,
null,
(theme) =>
React.createElement(
ThemeToggleContainer,
{ theme: theme },
React.createElement(ThemeToggleInput, {
type: "checkbox",
onChange: onChange,
checked: checked,
title: "Toggle theme",
}),
React.createElement(ThemeToggleSwitch, { theme: theme })
)
);
const BackDrop = ({ stars }) =>
React.createElement(
ThemeContext.Consumer,
null,
(theme) =>
React.createElement(
Container,
{ theme: theme },
React.createElement(
Sun,
{ theme: theme },
React.createElement(Face, {
show: theme === THEME_CONSTANTS.DAY,
})
),
React.createElement(
Moon,
{ theme: theme },
React.createElement(Face, {
show: theme === THEME_CONSTANTS.NIGHT,
}),
React.createElement(Craters, null)
),
stars.map((star, idx) =>
React.createElement(
Star,
_extends({ key: `star--${idx}` }, star, { theme: theme })
)
)
)
);
const generateItems = (
numberOfItems = 5,
sizeLimit = 10,
animationDuration = 10
) => {
const items = [];
for (let i = 0; i < numberOfItems; i++) {
items.push({
animated: Math.random() > 0.75,
delay: Math.floor(Math.random() * animationDuration),
duration: Math.floor(Math.random() * animationDuration + 5),
size: Math.floor(Math.random() * sizeLimit + 1),
x: Math.floor(Math.random() * 100 + 1),
y: Math.floor(Math.random() * 100 + 1),
});
}
return items;
};
class App extends Component {
constructor(...args) {
super(...args);
_defineProperty(this, "state", {
theme: defaultValue,
});
_defineProperty(
this,
"toggle",
(e) => {
const { theme } = this.state;
this.setState({
theme:
theme === THEME_CONSTANTS.NIGHT
? THEME_CONSTANTS.DAY
: THEME_CONSTANTS.NIGHT,
});
}
);
}
// 修正render方法定义(原写在constructor内,不符合React规范)
render() {
const { stars } = this.props;
const { theme } = this.state;
return React.createElement(
ThemeContext.Provider,
{ value: theme },
React.createElement(BackDrop, { stars: stars }),
React.createElement(ThemeToggle, {
checked: theme === THEME_CONSTANTS.NIGHT, // 修正checked判断逻辑
onChange: this.toggle,
})
);
}
}
_defineProperty(App, "defaultProps", { stars: generateItems(20, 4) });
render(
React.createElement(App, null),
document.querySelector("#root")
);
</script>
</body>
</html>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 646 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 619 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Some files were not shown because too many files have changed in this diff Show More