v2.3
This commit is contained in:
@@ -0,0 +1,82 @@
|
|||||||
|
# Python cache
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Conda environment
|
||||||
|
.conda/
|
||||||
|
|
||||||
|
# Virtual Environment
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
.gemini/
|
||||||
|
|
||||||
|
# Git
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Documentation (not needed in container)
|
||||||
|
Documentation/
|
||||||
|
*.md
|
||||||
|
FILES_LIST.txt
|
||||||
|
|
||||||
|
# Test and utility scripts (not needed in container)
|
||||||
|
test_*.py
|
||||||
|
check_*.py
|
||||||
|
quick_test.py
|
||||||
|
|
||||||
|
# GUI-only files (not needed for web deployment)
|
||||||
|
decrypt_file.py
|
||||||
|
web_app.py
|
||||||
|
ImaP.ui
|
||||||
|
*.exe
|
||||||
|
|
||||||
|
# Shell/Batch scripts (not needed in container, except entrypoint)
|
||||||
|
*.bat
|
||||||
|
*.ps1
|
||||||
|
!docker-entrypoint.sh
|
||||||
|
!docker-service.sh
|
||||||
|
|
||||||
|
# Caddy/Proxy configs (not using)
|
||||||
|
Caddyfile*
|
||||||
|
nginx.conf*
|
||||||
|
docker-compose.*.yml
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Data files (mounted as volumes, not baked into image)
|
||||||
|
uploads/
|
||||||
|
output/
|
||||||
|
|
||||||
|
# Config files (mounted as volumes at runtime)
|
||||||
|
config/
|
||||||
|
keys/
|
||||||
|
users_config.json
|
||||||
|
radius_config.json
|
||||||
|
email_config.json
|
||||||
|
email_config_api.json
|
||||||
|
|
||||||
|
# Standalone scripts not imported by web app
|
||||||
|
api_client.py
|
||||||
|
send_mail.py
|
||||||
|
|
||||||
|
# Model diagram
|
||||||
|
audit_portal_model.png
|
||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
|
||||||
|
# Virtual Environment
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Sensitive Configuration Files
|
||||||
|
radius_config.json
|
||||||
|
users_config.json
|
||||||
|
keys/private_key.pem
|
||||||
|
|
||||||
|
# Uploads and Outputs
|
||||||
|
uploads/*.enc
|
||||||
|
uploads/*.txt
|
||||||
|
output/*.xlsx
|
||||||
|
output/*.txt
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
.docker/
|
||||||
|
|
||||||
|
# Backups
|
||||||
|
backups/
|
||||||
|
email_config.json
|
||||||
|
email_config_api.json
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# DUNGLH-ANTT Key by Termius
|
||||||
|
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAJ7ycGKJZb08QexTGDM6wki0ZzWeSvWG2LEMUZPTFs+ Generated By Termius Dunglh-ANTT
|
||||||
|
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
# Dockerfile for Audit Hardening Tool - Web Deployment
|
||||||
|
# Optimized for offline deployment (export/load workflow)
|
||||||
|
|
||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||||
|
PYTHONUNBUFFERED=1 \
|
||||||
|
TZ=Asia/Ho_Chi_Minh
|
||||||
|
|
||||||
|
# Install minimal system dependencies
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
gcc \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
|
&& apt-get clean
|
||||||
|
|
||||||
|
# Copy requirements first for better Docker layer caching
|
||||||
|
COPY requirements.docker.txt ./requirements.txt
|
||||||
|
|
||||||
|
# Install Python dependencies
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Create necessary data directories (mount points)
|
||||||
|
RUN mkdir -p uploads output config keys tools docs
|
||||||
|
|
||||||
|
# Copy entrypoint script
|
||||||
|
COPY docker-entrypoint.sh /usr/local/bin/
|
||||||
|
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||||
|
|
||||||
|
# Expose port
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
|
||||||
|
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/', timeout=5)" || exit 1
|
||||||
|
|
||||||
|
# Set entrypoint
|
||||||
|
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||||
|
|
||||||
|
# Run the application with proxy headers support
|
||||||
|
CMD ["uvicorn","web_app_fastapi:app","--host","0.0.0.0","--port","8000","--proxy-headers","--forwarded-allow-ips","*","--root-path","/audit"]
|
||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,593 @@
|
|||||||
|
import base64
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
from os import mkdir
|
||||||
|
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
|
||||||
|
|
||||||
|
from Crypto.Cipher import PKCS1_OAEP, AES
|
||||||
|
from Crypto.PublicKey import RSA
|
||||||
|
from Crypto.Util.Padding import unpad
|
||||||
|
from colorama import Fore
|
||||||
|
from colorama import init
|
||||||
|
from openpyxl import load_workbook
|
||||||
|
from tqdm import tqdm
|
||||||
|
from unidecode import unidecode
|
||||||
|
|
||||||
|
init()
|
||||||
|
current = datetime.datetime.now()
|
||||||
|
try:
|
||||||
|
os.mkdir("output")
|
||||||
|
except FileExistsError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def extract_system_info(lines):
|
||||||
|
"""Extract system information from decrypted file content."""
|
||||||
|
system_info = {
|
||||||
|
'script_version': '',
|
||||||
|
'os_name': '',
|
||||||
|
'kernel_version': '',
|
||||||
|
'architecture': '',
|
||||||
|
'hostname': '',
|
||||||
|
'fqdn': '',
|
||||||
|
'ip_address': '',
|
||||||
|
'all_ips': '',
|
||||||
|
'audit_time': '',
|
||||||
|
'timezone': '',
|
||||||
|
'uptime': '',
|
||||||
|
'cpu_model': '',
|
||||||
|
'cpu_cores': '',
|
||||||
|
'total_memory': '',
|
||||||
|
'used_memory': '',
|
||||||
|
'free_memory': '',
|
||||||
|
'swap_total': '',
|
||||||
|
'primary_interface': '',
|
||||||
|
'mac_address': '',
|
||||||
|
'default_gateway': '',
|
||||||
|
'dns_servers': '',
|
||||||
|
'selinux_status': '',
|
||||||
|
'firewall_status': '',
|
||||||
|
'last_boot': '',
|
||||||
|
'current_users': '',
|
||||||
|
'load_average': '',
|
||||||
|
# Compliance statistics
|
||||||
|
'passed_count': 0,
|
||||||
|
'failed_count': 0,
|
||||||
|
'total_checks': 0,
|
||||||
|
'compliance_percentage': 0.0,
|
||||||
|
# Mandatory/Optional statistics
|
||||||
|
'mandatory_passed': 0,
|
||||||
|
'mandatory_failed': 0,
|
||||||
|
'mandatory_total': 0,
|
||||||
|
'mandatory_percentage': 0.0,
|
||||||
|
'optional_passed': 0,
|
||||||
|
'optional_failed': 0,
|
||||||
|
'optional_total': 0,
|
||||||
|
'optional_percentage': 0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
passed_count = 0
|
||||||
|
failed_count = 0
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
line_stripped = line.strip()
|
||||||
|
|
||||||
|
# Count PASSED and FAILED checks from JSON format lines
|
||||||
|
if line_stripped.startswith('{') and line_stripped.endswith('}'):
|
||||||
|
if '"PASSED"' in line_stripped:
|
||||||
|
passed_count += 1
|
||||||
|
elif '"FAILED"' in line_stripped:
|
||||||
|
failed_count += 1
|
||||||
|
|
||||||
|
# Parse system information
|
||||||
|
if line_stripped.startswith('Script Version:'):
|
||||||
|
system_info['script_version'] = line_stripped.replace('Script Version:', '').strip()
|
||||||
|
elif line_stripped.startswith('Operating System:'):
|
||||||
|
system_info['os_name'] = line_stripped.replace('Operating System:', '').strip()
|
||||||
|
elif line_stripped.startswith('Kernel Version:'):
|
||||||
|
system_info['kernel_version'] = line_stripped.replace('Kernel Version:', '').strip()
|
||||||
|
elif line_stripped.startswith('Architecture:'):
|
||||||
|
system_info['architecture'] = line_stripped.replace('Architecture:', '').strip()
|
||||||
|
elif line_stripped.startswith('Hostname:'):
|
||||||
|
system_info['hostname'] = line_stripped.replace('Hostname:', '').strip()
|
||||||
|
elif line_stripped.startswith('FQDN:'):
|
||||||
|
system_info['fqdn'] = line_stripped.replace('FQDN:', '').strip()
|
||||||
|
elif line_stripped.startswith('IP Address:'):
|
||||||
|
system_info['ip_address'] = line_stripped.replace('IP Address:', '').strip()
|
||||||
|
elif line_stripped.startswith('All IP Addresses:'):
|
||||||
|
system_info['all_ips'] = line_stripped.replace('All IP Addresses:', '').strip()
|
||||||
|
elif line_stripped.startswith('Audit Time:'):
|
||||||
|
system_info['audit_time'] = line_stripped.replace('Audit Time:', '').strip()
|
||||||
|
elif line_stripped.startswith('Timezone:'):
|
||||||
|
system_info['timezone'] = line_stripped.replace('Timezone:', '').strip()
|
||||||
|
elif line_stripped.startswith('Uptime:'):
|
||||||
|
system_info['uptime'] = line_stripped.replace('Uptime:', '').strip()
|
||||||
|
elif line_stripped.startswith('CPU Model:'):
|
||||||
|
system_info['cpu_model'] = line_stripped.replace('CPU Model:', '').strip()
|
||||||
|
elif line_stripped.startswith('CPU Cores:'):
|
||||||
|
system_info['cpu_cores'] = line_stripped.replace('CPU Cores:', '').strip()
|
||||||
|
elif line_stripped.startswith('Total Memory:'):
|
||||||
|
system_info['total_memory'] = line_stripped.replace('Total Memory:', '').strip()
|
||||||
|
elif line_stripped.startswith('Used Memory:'):
|
||||||
|
system_info['used_memory'] = line_stripped.replace('Used Memory:', '').strip()
|
||||||
|
elif line_stripped.startswith('Free Memory:'):
|
||||||
|
system_info['free_memory'] = line_stripped.replace('Free Memory:', '').strip()
|
||||||
|
elif line_stripped.startswith('Swap Total:'):
|
||||||
|
system_info['swap_total'] = line_stripped.replace('Swap Total:', '').strip()
|
||||||
|
elif line_stripped.startswith('Primary Interface:'):
|
||||||
|
system_info['primary_interface'] = line_stripped.replace('Primary Interface:', '').strip()
|
||||||
|
elif line_stripped.startswith('MAC Address:'):
|
||||||
|
system_info['mac_address'] = line_stripped.replace('MAC Address:', '').strip()
|
||||||
|
elif line_stripped.startswith('Default Gateway:'):
|
||||||
|
system_info['default_gateway'] = line_stripped.replace('Default Gateway:', '').strip()
|
||||||
|
elif line_stripped.startswith('DNS Servers:'):
|
||||||
|
system_info['dns_servers'] = line_stripped.replace('DNS Servers:', '').strip()
|
||||||
|
elif line_stripped.startswith('SELinux Status:'):
|
||||||
|
system_info['selinux_status'] = line_stripped.replace('SELinux Status:', '').strip()
|
||||||
|
elif line_stripped.startswith('Firewall:'):
|
||||||
|
system_info['firewall_status'] = line_stripped.replace('Firewall:', '').strip()
|
||||||
|
elif line_stripped.startswith('Last Boot:'):
|
||||||
|
system_info['last_boot'] = line_stripped.replace('Last Boot:', '').strip()
|
||||||
|
elif line_stripped.startswith('Current Users:'):
|
||||||
|
system_info['current_users'] = line_stripped.replace('Current Users:', '').strip()
|
||||||
|
elif line_stripped.startswith('Load Average:'):
|
||||||
|
system_info['load_average'] = line_stripped.replace('Load Average:', '').strip()
|
||||||
|
|
||||||
|
# Calculate compliance statistics
|
||||||
|
total_checks = passed_count + failed_count
|
||||||
|
system_info['passed_count'] = passed_count
|
||||||
|
system_info['failed_count'] = failed_count
|
||||||
|
system_info['total_checks'] = total_checks
|
||||||
|
|
||||||
|
if total_checks > 0:
|
||||||
|
system_info['compliance_percentage'] = round((passed_count / total_checks) * 100, 1)
|
||||||
|
else:
|
||||||
|
system_info['compliance_percentage'] = 0.0
|
||||||
|
|
||||||
|
return system_info
|
||||||
|
|
||||||
|
def detect_os_from_content(lines):
|
||||||
|
"""
|
||||||
|
Detect OS type from decrypted file content.
|
||||||
|
Reads the 'Operating System:' field and maps it to an OS key.
|
||||||
|
|
||||||
|
Returns the OS key (centos, ubuntu, rhel, oracle, windows) or None if not detected.
|
||||||
|
|
||||||
|
Example values:
|
||||||
|
'CentOS Linux 7 (Core)' -> 'centos'
|
||||||
|
'Red Hat Enterprise ...' -> 'rhel'
|
||||||
|
'Ubuntu 20.04.x LTS' -> 'ubuntu'
|
||||||
|
'Oracle Linux 8.x' -> 'oracle'
|
||||||
|
'Windows Server 2019' -> 'windows'
|
||||||
|
"""
|
||||||
|
os_name_raw = None
|
||||||
|
for line in lines:
|
||||||
|
stripped = line.strip()
|
||||||
|
if stripped.startswith('Operating System:'):
|
||||||
|
os_name_raw = stripped.replace('Operating System:', '').strip()
|
||||||
|
break
|
||||||
|
|
||||||
|
if not os_name_raw:
|
||||||
|
return None
|
||||||
|
|
||||||
|
os_lower = os_name_raw.lower()
|
||||||
|
|
||||||
|
# Order matters: more specific patterns first
|
||||||
|
if 'centos' in os_lower:
|
||||||
|
detected = 'centos'
|
||||||
|
elif 'red hat' in os_lower or 'redhat' in os_lower or 'rhel' in os_lower:
|
||||||
|
detected = 'rhel'
|
||||||
|
elif 'ubuntu' in os_lower:
|
||||||
|
detected = 'ubuntu'
|
||||||
|
elif 'oracle' in os_lower:
|
||||||
|
# Check if version is < 8
|
||||||
|
# Hỗ trợ cả 'Oracle Linux Server release 7.9' và 'Oracle Linux Server 7.9'
|
||||||
|
import re
|
||||||
|
version_match = re.search(r'(?:release\s+|server\s+|linux\s+)(\d+)', os_lower)
|
||||||
|
if not version_match:
|
||||||
|
# Fallback: tìm số version đầu tiên trong chuỗi (ví dụ 'oracle linux 7.9')
|
||||||
|
version_match = re.search(r'(\d+)\.', os_lower)
|
||||||
|
if version_match and int(version_match.group(1)) < 8:
|
||||||
|
print(Fore.YELLOW + f"[CONTENT-DETECT] Oracle Linux < 8 detected (version {version_match.group(1)}). Routing to CentOS config." + Fore.RESET)
|
||||||
|
detected = 'centos'
|
||||||
|
else:
|
||||||
|
detected = 'oracle'
|
||||||
|
elif 'windows' in os_lower:
|
||||||
|
detected = 'windows'
|
||||||
|
else:
|
||||||
|
detected = None
|
||||||
|
|
||||||
|
if detected:
|
||||||
|
print(Fore.CYAN + f"[CONTENT-DETECT] 'Operating System: {os_name_raw}' → OS key: '{detected}'" + Fore.RESET)
|
||||||
|
else:
|
||||||
|
print(Fore.YELLOW + f"[CONTENT-DETECT] Unknown OS from content: '{os_name_raw}'" + Fore.RESET)
|
||||||
|
|
||||||
|
return detected
|
||||||
|
|
||||||
|
|
||||||
|
_cached_private_key = None
|
||||||
|
|
||||||
|
def get_private_key():
|
||||||
|
"""Lấy hoặc nạp RSA private key từ cache trong RAM."""
|
||||||
|
global _cached_private_key
|
||||||
|
if _cached_private_key is not None:
|
||||||
|
return _cached_private_key
|
||||||
|
|
||||||
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
private_key_paths = [
|
||||||
|
'private_key.pem',
|
||||||
|
'../private_key.pem',
|
||||||
|
os.path.join(script_dir, 'private_key.pem'),
|
||||||
|
os.path.join(script_dir, 'keys', 'private_key.pem'),
|
||||||
|
'keys/private_key.pem'
|
||||||
|
]
|
||||||
|
|
||||||
|
for path in private_key_paths:
|
||||||
|
if os.path.exists(path):
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
_cached_private_key = RSA.import_key(f.read())
|
||||||
|
return _cached_private_key
|
||||||
|
|
||||||
|
raise FileNotFoundError("private_key.pem not found! Please ensure it exists in the script directory.")
|
||||||
|
|
||||||
|
|
||||||
|
def _decrypt_rsa_block_worker(block: bytes) -> bytes:
|
||||||
|
"""Hàm worker cấp module phục vụ cho ProcessPoolExecutor / ThreadPoolExecutor giải mã RSA."""
|
||||||
|
private_key = get_private_key()
|
||||||
|
cipher = PKCS1_OAEP.new(private_key)
|
||||||
|
return cipher.decrypt(block)
|
||||||
|
|
||||||
|
|
||||||
|
def is_hybrid_encrypted(raw_text: str) -> bool:
|
||||||
|
"""Kiểm tra xem nội dung file có thuộc chuẩn mã hóa lai MỚI (HYBRID AES-256 + RSA) hay không."""
|
||||||
|
s = raw_text.strip()
|
||||||
|
return s.startswith("HYBRID_V1:") or s.startswith("HYBRID_AES:") or s.startswith("HYBRID:")
|
||||||
|
|
||||||
|
|
||||||
|
def decrypt_hybrid(raw_text: str) -> bytes:
|
||||||
|
"""
|
||||||
|
Giải mã định dạng Mã hóa Lai (Hybrid AES-256 + RSA) siêu tốc (~0.005s).
|
||||||
|
Cấu trúc:
|
||||||
|
HYBRID_V1:<b64_enc_key>:<b64_iv>:<b64_ciphertext>
|
||||||
|
hoặc JSON:
|
||||||
|
HYBRID_V1:{"enc_key": "...", "iv": "...", "data": "..."}
|
||||||
|
"""
|
||||||
|
s = raw_text.strip()
|
||||||
|
# Loại bỏ tiền tố prefix
|
||||||
|
for prefix in ("HYBRID_V1:", "HYBRID_AES:", "HYBRID:"):
|
||||||
|
if s.startswith(prefix):
|
||||||
|
payload = s[len(prefix):].strip()
|
||||||
|
break
|
||||||
|
|
||||||
|
b64_enc_key = ""
|
||||||
|
b64_iv = ""
|
||||||
|
b64_ciphertext = ""
|
||||||
|
|
||||||
|
if payload.startswith("{"):
|
||||||
|
# Format JSON
|
||||||
|
data_json = json.loads(payload)
|
||||||
|
b64_enc_key = data_json.get("enc_key", "")
|
||||||
|
b64_iv = data_json.get("iv", "")
|
||||||
|
b64_ciphertext = data_json.get("data", "")
|
||||||
|
else:
|
||||||
|
# Format chuỗi phân cách bởi dấu hai chấm ':'
|
||||||
|
parts = payload.split(":")
|
||||||
|
if len(parts) >= 3:
|
||||||
|
b64_enc_key = parts[0]
|
||||||
|
b64_iv = parts[1]
|
||||||
|
b64_ciphertext = parts[2]
|
||||||
|
|
||||||
|
if not b64_enc_key or not b64_iv or not b64_ciphertext:
|
||||||
|
raise ValueError("Cấu trúc file Mã hóa Lai (Hybrid Encryption) không hợp lệ!")
|
||||||
|
|
||||||
|
enc_key = base64.b64decode(b64_enc_key)
|
||||||
|
iv = base64.b64decode(b64_iv)
|
||||||
|
ciphertext = base64.b64decode(b64_ciphertext)
|
||||||
|
|
||||||
|
# 1. Giải mã khóa AES bằng RSA Private Key (chỉ 1 block RSA duy nhất)
|
||||||
|
private_key = get_private_key()
|
||||||
|
cipher_rsa = PKCS1_OAEP.new(private_key)
|
||||||
|
aes_key = cipher_rsa.decrypt(enc_key)
|
||||||
|
|
||||||
|
# 2. Giải mã dữ liệu bằng khóa AES-256-CBC (siêu nhanh)
|
||||||
|
cipher_aes = AES.new(aes_key, AES.MODE_CBC, iv)
|
||||||
|
decrypted_padded = cipher_aes.decrypt(ciphertext)
|
||||||
|
|
||||||
|
# Thử gỡ padding PKCS7
|
||||||
|
try:
|
||||||
|
plaintext_bytes = unpad(decrypted_padded, AES.block_size)
|
||||||
|
except ValueError:
|
||||||
|
plaintext_bytes = decrypted_padded
|
||||||
|
|
||||||
|
return plaintext_bytes
|
||||||
|
|
||||||
|
|
||||||
|
def decryption_read_header(filename: str, max_blocks: int = 30) -> list[str]:
|
||||||
|
"""
|
||||||
|
Giải mã nhanh phần header của file .enc để đọc thông tin OS.
|
||||||
|
Tự động hỗ trợ cả chuẩn MỚI (Hybrid AES+RSA) và chuẩn CŨ (Pure RSA).
|
||||||
|
"""
|
||||||
|
with open(filename, "r", encoding="utf-8", errors="replace") as f:
|
||||||
|
raw_text = f.read()
|
||||||
|
|
||||||
|
if is_hybrid_encrypted(raw_text):
|
||||||
|
# Format MỚI: Giải mã siêu tốc bằng AES-256 + RSA (< 0.005s)
|
||||||
|
plaintext_bytes = decrypt_hybrid(raw_text)
|
||||||
|
lines = plaintext_bytes.decode("utf-8", errors="replace").splitlines()
|
||||||
|
return lines[:100]
|
||||||
|
|
||||||
|
# Format CŨ: Giải mã N block RSA đầu tiên (~5KB)
|
||||||
|
ciphertext = base64.b64decode(raw_text.encode('utf-8'))
|
||||||
|
private_key = get_private_key()
|
||||||
|
block_size = private_key.size_in_bytes()
|
||||||
|
cipher = PKCS1_OAEP.new(private_key)
|
||||||
|
|
||||||
|
total_blocks = (len(ciphertext) + block_size - 1) // block_size
|
||||||
|
num_blocks = min(max_blocks, total_blocks)
|
||||||
|
blocks = []
|
||||||
|
|
||||||
|
for i in range(num_blocks):
|
||||||
|
offset = i * block_size
|
||||||
|
block = ciphertext[offset:offset + block_size]
|
||||||
|
try:
|
||||||
|
blocks.append(cipher.decrypt(block))
|
||||||
|
except Exception:
|
||||||
|
break
|
||||||
|
|
||||||
|
header_text = b"".join(blocks).decode('utf-8', errors='replace')
|
||||||
|
return header_text.splitlines()
|
||||||
|
|
||||||
|
|
||||||
|
def decryption_write(filename):
|
||||||
|
"""
|
||||||
|
Giải mã toàn bộ file mã hóa.
|
||||||
|
Tự động chuyển đổi giữa chuẩn MỚI (Hybrid AES-256 + RSA) và chuẩn CŨ (Pure RSA Multi-processing).
|
||||||
|
"""
|
||||||
|
with open(filename, "r", encoding="utf-8", errors="replace") as f:
|
||||||
|
raw_text = f.read()
|
||||||
|
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
if is_hybrid_encrypted(raw_text):
|
||||||
|
# ⚡ Chuẩn MỚI (Hybrid AES-256 + RSA): Tốc độ tức thì < 0.01 giây
|
||||||
|
plaintext_bytes = decrypt_hybrid(raw_text)
|
||||||
|
plaintext = plaintext_bytes.decode("utf-8", errors="replace")
|
||||||
|
print(Fore.GREEN + f" [HYBRID AES+RSA] Decryption done in {time.time() - start_time:.3f}s" + Fore.RESET)
|
||||||
|
else:
|
||||||
|
# 🐢 Chuẩn CŨ (Pure RSA): Giải mã đa luồng / đa tiến trình tối ưu CPU Multi-core
|
||||||
|
ciphertext = base64.b64decode(raw_text.encode('utf-8'))
|
||||||
|
private_key = get_private_key()
|
||||||
|
block_size = private_key.size_in_bytes()
|
||||||
|
total_blocks = (len(ciphertext) + block_size - 1) // block_size
|
||||||
|
|
||||||
|
block_list = [ciphertext[i * block_size:(i + 1) * block_size] for i in range(total_blocks)]
|
||||||
|
max_workers = min(16, (os.cpu_count() or 4) * 2)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
||||||
|
decrypted_blocks = list(executor.map(_decrypt_rsa_block_worker, block_list))
|
||||||
|
except Exception:
|
||||||
|
cipher = PKCS1_OAEP.new(private_key)
|
||||||
|
decrypted_blocks = [cipher.decrypt(b) for b in block_list]
|
||||||
|
|
||||||
|
plaintext = b"".join(decrypted_blocks).decode('utf-8', errors='replace')
|
||||||
|
print(Fore.GREEN + f" [LEGACY RSA] Decryption done in {time.time() - start_time:.1f}s ({total_blocks} blocks, {max_workers} workers)" + Fore.RESET)
|
||||||
|
|
||||||
|
output_path = os.path.basename(filename).replace(".enc", "")
|
||||||
|
with open(output_path, 'w', encoding="utf-8") as f:
|
||||||
|
f.write(plaintext)
|
||||||
|
|
||||||
|
|
||||||
|
def check_file_exist(filename):
|
||||||
|
isFile = os.path.isfile(filename)
|
||||||
|
if isFile:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def string_normalization(string):
|
||||||
|
return unidecode(str(string))
|
||||||
|
|
||||||
|
|
||||||
|
def extract_failed_logs(lines):
|
||||||
|
"""Extract detailed log blocks for each FAILED criteria from decrypted log lines.
|
||||||
|
|
||||||
|
Returns a dict mapping normalized criteria name -> log detail text.
|
||||||
|
"""
|
||||||
|
failed_logs = {}
|
||||||
|
i = 0
|
||||||
|
separator = '######################################'
|
||||||
|
dash_separator = '-------------------'
|
||||||
|
stderr_begin = '########## STDERR BEGIN ##########'
|
||||||
|
stderr_end = '########## STDERR END ##########'
|
||||||
|
|
||||||
|
while i < len(lines):
|
||||||
|
line_stripped = lines[i].strip()
|
||||||
|
|
||||||
|
# Look for FAILED JSON lines
|
||||||
|
if line_stripped.startswith('{') and line_stripped.endswith('}') and '"FAILED"' in line_stripped:
|
||||||
|
try:
|
||||||
|
parsed = json.loads(line_stripped)
|
||||||
|
criteria_name = str(list(parsed.keys())[0])
|
||||||
|
normalized_name = string_normalization(criteria_name)
|
||||||
|
|
||||||
|
# Collect all lines after this FAILED line until the next JSON criteria line
|
||||||
|
log_lines = []
|
||||||
|
j = i + 1
|
||||||
|
while j < len(lines):
|
||||||
|
next_line = lines[j].strip()
|
||||||
|
|
||||||
|
# Stop when we hit the next JSON criteria line (PASSED or FAILED)
|
||||||
|
if next_line.startswith('{') and next_line.endswith('}'):
|
||||||
|
try:
|
||||||
|
next_parsed = json.loads(next_line)
|
||||||
|
next_val = str(list(next_parsed.values())[0])
|
||||||
|
if next_val in ('PASSED', 'FAILED'):
|
||||||
|
break
|
||||||
|
except (json.JSONDecodeError, IndexError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Skip separator lines and STDERR markers
|
||||||
|
if next_line == separator or next_line == dash_separator:
|
||||||
|
j += 1
|
||||||
|
continue
|
||||||
|
if next_line == stderr_begin or next_line == stderr_end:
|
||||||
|
j += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Skip empty lines at the beginning
|
||||||
|
if not log_lines and not next_line:
|
||||||
|
j += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
log_lines.append(lines[j].rstrip())
|
||||||
|
j += 1
|
||||||
|
|
||||||
|
# Remove trailing empty lines
|
||||||
|
while log_lines and not log_lines[-1].strip():
|
||||||
|
log_lines.pop()
|
||||||
|
|
||||||
|
if log_lines:
|
||||||
|
log_text = '\n'.join(log_lines)
|
||||||
|
# Excel cell limit is 32767 characters
|
||||||
|
if len(log_text) > 32767:
|
||||||
|
log_text = log_text[:32764] + '...'
|
||||||
|
failed_logs[normalized_name] = log_text
|
||||||
|
|
||||||
|
i = j # Skip to where we stopped
|
||||||
|
continue
|
||||||
|
except (json.JSONDecodeError, IndexError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return failed_logs
|
||||||
|
|
||||||
|
|
||||||
|
def run_report(checklist_file, config_file, file_input):
|
||||||
|
with open(file_input, "r", encoding="utf8") as file:
|
||||||
|
line = file.read().splitlines()
|
||||||
|
wb = load_workbook(filename=checklist_file)
|
||||||
|
ws = wb.worksheets[0]
|
||||||
|
with open(config_file, 'r', encoding="utf8") as j:
|
||||||
|
contents = json.loads(j.read())
|
||||||
|
index_audit = contents['data']
|
||||||
|
|
||||||
|
mandatory_rows = set()
|
||||||
|
for row_idx in range(1, ws.max_row + 1):
|
||||||
|
cell_val = ws.cell(row=row_idx, column=5).value
|
||||||
|
if cell_val and str(cell_val).strip().lower() == 'x':
|
||||||
|
mandatory_rows.add(row_idx)
|
||||||
|
|
||||||
|
mandatory_passed = 0
|
||||||
|
mandatory_failed = 0
|
||||||
|
optional_passed = 0
|
||||||
|
optional_failed = 0
|
||||||
|
|
||||||
|
failed_logs = extract_failed_logs(line)
|
||||||
|
|
||||||
|
audit_results = {}
|
||||||
|
for one_audit in line:
|
||||||
|
try:
|
||||||
|
parsed_data = json.loads(one_audit)
|
||||||
|
result = str(list(parsed_data.keys())[0])
|
||||||
|
value = str(list(parsed_data.values())[0])
|
||||||
|
normalized_key = string_normalization(result)
|
||||||
|
audit_results[normalized_key] = (value, one_audit)
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for audit_pattern in index_audit:
|
||||||
|
index_a = list(audit_pattern.keys())[0]
|
||||||
|
index_pattern = string_normalization(list(audit_pattern.values())[0])
|
||||||
|
|
||||||
|
entry = audit_results.get(index_pattern)
|
||||||
|
if entry is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
status, raw_line = entry
|
||||||
|
is_mandatory = int(index_a) in mandatory_rows if index_a.isdigit() else False
|
||||||
|
|
||||||
|
if "FAILED" in raw_line:
|
||||||
|
ws['D{}'.format(index_a)] = 'x'
|
||||||
|
if index_pattern in failed_logs:
|
||||||
|
ws['F{}'.format(index_a)] = failed_logs[index_pattern]
|
||||||
|
if is_mandatory:
|
||||||
|
mandatory_failed += 1
|
||||||
|
else:
|
||||||
|
optional_failed += 1
|
||||||
|
else:
|
||||||
|
ws['C{}'.format(index_a)] = 'x'
|
||||||
|
if is_mandatory:
|
||||||
|
mandatory_passed += 1
|
||||||
|
else:
|
||||||
|
optional_passed += 1
|
||||||
|
|
||||||
|
# Find hostname and time dynamically in the file
|
||||||
|
hostname = "unknown"
|
||||||
|
time_generate = "unknown"
|
||||||
|
|
||||||
|
for l in line:
|
||||||
|
if l.strip().startswith("Hostname:"):
|
||||||
|
hostname = l.replace("Hostname:", "").strip().split(" ")[0]
|
||||||
|
elif l.strip().startswith("Audit Time:"):
|
||||||
|
time_generate = l.replace("Audit Time:", "").strip().replace("-", "_").replace(":", "_").replace(" ", "-")
|
||||||
|
# Also support old format with "Time:"
|
||||||
|
elif l.strip().startswith("Time:") and time_generate == "unknown":
|
||||||
|
time_generate = l.replace("Time:", "").strip().replace("-", "_").replace(":", "_").replace(" ", "-")
|
||||||
|
|
||||||
|
# Create file name based strictly on hostname and time
|
||||||
|
file_name = f'{hostname}_{time_generate}.xlsx'
|
||||||
|
|
||||||
|
wb.save(filename=file_name)
|
||||||
|
if check_file_exist(file_name):
|
||||||
|
print(Fore.GREEN + "\nChecklist Done: {}".format(file_name) + Fore.RESET)
|
||||||
|
else:
|
||||||
|
print(Fore.RED + "Error creating Excel file!" + Fore.RESET)
|
||||||
|
|
||||||
|
# Extract and return system info
|
||||||
|
system_info = extract_system_info(line)
|
||||||
|
|
||||||
|
# Add mandatory/optional statistics
|
||||||
|
mandatory_total = mandatory_passed + mandatory_failed
|
||||||
|
optional_total = optional_passed + optional_failed
|
||||||
|
system_info['mandatory_passed'] = mandatory_passed
|
||||||
|
system_info['mandatory_failed'] = mandatory_failed
|
||||||
|
system_info['mandatory_total'] = mandatory_total
|
||||||
|
system_info['mandatory_percentage'] = round((mandatory_passed / mandatory_total) * 100, 1) if mandatory_total > 0 else 0.0
|
||||||
|
system_info['optional_passed'] = optional_passed
|
||||||
|
system_info['optional_failed'] = optional_failed
|
||||||
|
system_info['optional_total'] = optional_total
|
||||||
|
system_info['optional_percentage'] = round((optional_passed / optional_total) * 100, 1) if optional_total > 0 else 0.0
|
||||||
|
|
||||||
|
# Recalculate compliance stats based on config-matched criteria (consistent with Excel)
|
||||||
|
# extract_system_info counts ALL PASSED/FAILED lines in raw text (including criteria
|
||||||
|
# not in the checklist config), which can differ from the Excel result.
|
||||||
|
# Use the actual config-matched counts for accurate web display.
|
||||||
|
config_passed = mandatory_passed + optional_passed
|
||||||
|
config_failed = mandatory_failed + optional_failed
|
||||||
|
config_total = config_passed + config_failed
|
||||||
|
if config_total > 0:
|
||||||
|
system_info['passed_count'] = config_passed
|
||||||
|
system_info['failed_count'] = config_failed
|
||||||
|
system_info['total_checks'] = config_total
|
||||||
|
system_info['compliance_percentage'] = round((config_passed / config_total) * 100, 1)
|
||||||
|
|
||||||
|
return system_info
|
||||||
|
|
||||||
|
|
||||||
|
def run_generate_excel(checklist_file, config_file, filename):
|
||||||
|
global current
|
||||||
|
current = datetime.datetime.now()
|
||||||
|
file_decrypt = os.path.basename(filename).replace(".enc", "")
|
||||||
|
if check_file_exist(file_decrypt):
|
||||||
|
os.remove(file_decrypt)
|
||||||
|
decryption_write(filename)
|
||||||
|
else:
|
||||||
|
decryption_write(filename)
|
||||||
|
if check_file_exist(file_decrypt):
|
||||||
|
system_info = run_report(checklist_file, config_file, file_decrypt)
|
||||||
|
return system_info
|
||||||
|
return None
|
||||||
@@ -0,0 +1,314 @@
|
|||||||
|
"""Authentication router for login and JWT token management."""
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException, status, Form
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from typing import Dict, Any
|
||||||
|
from .. import crud, schemas
|
||||||
|
from ..database import get_db
|
||||||
|
from ..utils import create_access_token, decode_access_token
|
||||||
|
from fastapi import Request, Query
|
||||||
|
from ..security import get_current_user, revoke_token_db, REVOKED_TOKENS, set_latest_jti
|
||||||
|
import os
|
||||||
|
from .radius_login import check_radius_login
|
||||||
|
from ..models import RevokedToken
|
||||||
|
import requests
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/auth", tags=["auth"])
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/login", response_model=schemas.LoginResponse)
|
||||||
|
async def login(login_req: schemas.LoginRequest, db: Session = Depends(get_db)):
|
||||||
|
|
||||||
|
# Convert env về boolean chuẩn
|
||||||
|
IS_PRODUCT = os.getenv("IS_PRODUCT", "False").lower() == "true"
|
||||||
|
|
||||||
|
# =============================
|
||||||
|
# 🚀 PRODUCT MODE (mock data)
|
||||||
|
# =============================
|
||||||
|
if IS_PRODUCT:
|
||||||
|
return {
|
||||||
|
"access_token": "test",
|
||||||
|
"token_type": "bearer",
|
||||||
|
"user": {
|
||||||
|
"id": 1,
|
||||||
|
"email": "test@gmail.com",
|
||||||
|
"fullname": "Test User",
|
||||||
|
"unit_id": 1,
|
||||||
|
"unit_name": "MEDIA",
|
||||||
|
"role_id": 1,
|
||||||
|
"role_name": "Administrator",
|
||||||
|
"status": 2,
|
||||||
|
"status_name": "active",
|
||||||
|
"created_at": "2025-12-02T14:32:10",
|
||||||
|
"updated_at": "2025-12-23T04:24:04"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# =============================
|
||||||
|
# 🧪 DEV MODE (login thật)
|
||||||
|
# =============================
|
||||||
|
user = crud.authenticate_user(db, login_req.email, login_req.password)
|
||||||
|
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Incorrect email or password",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
|
||||||
|
if int(getattr(user, "status", 0) or 0) != 2:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="User is not active",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
|
||||||
|
user_details = crud.get_user_with_details(db, user)
|
||||||
|
|
||||||
|
token_data = {
|
||||||
|
"sub": str(user.id),
|
||||||
|
"email": user.email,
|
||||||
|
"fullname": user.fullname,
|
||||||
|
"unit_id": user.unit_id,
|
||||||
|
"status": user.status,
|
||||||
|
}
|
||||||
|
|
||||||
|
access_token = create_access_token(data=token_data)
|
||||||
|
|
||||||
|
try:
|
||||||
|
payload = decode_access_token(access_token)
|
||||||
|
jti = payload.get("jti")
|
||||||
|
if jti:
|
||||||
|
set_latest_jti(int(user.id), jti)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return {
|
||||||
|
"access_token": access_token,
|
||||||
|
"token_type": "bearer",
|
||||||
|
"user": user_details
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/logout", response_model=schemas.APIResponse, dependencies=[Depends(get_current_user)])
|
||||||
|
async def logout(request: Request, db: Session = Depends(get_db)):
|
||||||
|
"""Logout endpoint: revoke current JWT token."""
|
||||||
|
auth_header = request.headers.get("Authorization")
|
||||||
|
if not auth_header:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Missing authorization header",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
scheme, token = auth_header.split()
|
||||||
|
if scheme.lower() != "bearer":
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Invalid authentication scheme",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Invalid authorization header",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
|
||||||
|
revoke_token_db(db, token)
|
||||||
|
return {"message": "success", "data": {"revoked": True}}
|
||||||
|
|
||||||
|
@router.get("/validate-token", response_model=schemas.APIResponse)
|
||||||
|
async def validate_token(request: Request, db: Session = Depends(get_db)):
|
||||||
|
auth_header = request.headers.get("Authorization")
|
||||||
|
if not auth_header:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Missing authorization header",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
scheme, token = auth_header.split()
|
||||||
|
if scheme.lower() != "bearer":
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Invalid authentication scheme",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Invalid authorization header",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
if token in REVOKED_TOKENS:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Token has been revoked",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
payload = decode_access_token(token)
|
||||||
|
except Exception:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Invalid token",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
jti = payload.get("jti")
|
||||||
|
if jti and db.query(RevokedToken).filter(RevokedToken.jti == jti).first():
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Token has been revoked",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
return {"message": "success", "data": payload}
|
||||||
|
|
||||||
|
@router.post("/login_sso", response_model=schemas.LoginResponse)
|
||||||
|
async def login_sso(
|
||||||
|
login_req: schemas.LoginSSORequest,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
):
|
||||||
|
server = os.getenv("RADIUS_SERVER")
|
||||||
|
secret = os.getenv("RADIUS_SECRET")
|
||||||
|
port = int(os.getenv("RADIUS_PORT", 1812))
|
||||||
|
realm = os.getenv("RADIUS_REALM", "")
|
||||||
|
if not server or not secret:
|
||||||
|
raise HTTPException(status_code=500, detail="Configuration is missing")
|
||||||
|
try:
|
||||||
|
local_part = (login_req.email or "").split("@")[0]
|
||||||
|
except Exception:
|
||||||
|
local_part = login_req.email or ""
|
||||||
|
username = f"{local_part}{realm}"
|
||||||
|
radius_password = f"{login_req.password}{login_req.otp or ''}"
|
||||||
|
ok = check_radius_login(server, port, secret, username, radius_password)
|
||||||
|
if not ok:
|
||||||
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid username or password or OTP")
|
||||||
|
user = crud.get_user_by_email(db, login_req.email)
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found. Please contact to Admin.")
|
||||||
|
if int(getattr(user, "status", 0) or 0) != 2:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="User is not active",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
user_details = crud.get_user_with_details(db, user)
|
||||||
|
token_data = {
|
||||||
|
"sub": str(user.id),
|
||||||
|
"email": user.email,
|
||||||
|
"fullname": user.fullname,
|
||||||
|
"unit_id": user.unit_id,
|
||||||
|
"status": user.status,
|
||||||
|
}
|
||||||
|
access_token = create_access_token(data=token_data)
|
||||||
|
try:
|
||||||
|
payload = decode_access_token(access_token)
|
||||||
|
jti = payload.get("jti")
|
||||||
|
if jti:
|
||||||
|
set_latest_jti(int(user.id), jti)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return {
|
||||||
|
"access_token": access_token,
|
||||||
|
"token_type": "bearer",
|
||||||
|
"user": user_details,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/login_sso_vnpt", response_model=schemas.LoginResponse)
|
||||||
|
async def login_sso_vnpt(
|
||||||
|
ticket: str = Query(..., description="CAS ticket"),
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
):
|
||||||
|
# ===== 1. CAS CONFIG =====
|
||||||
|
CAS_HOST = os.getenv("CAS_HOST") # vd: https://cas.vnpt.vn/cas
|
||||||
|
SERVICE_URL = os.getenv("CAS_SERVICE_URL")
|
||||||
|
|
||||||
|
if not CAS_HOST or not SERVICE_URL:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=500,
|
||||||
|
detail="CAS configuration missing",
|
||||||
|
)
|
||||||
|
|
||||||
|
# ===== 2. BUILD VALIDATE URL =====
|
||||||
|
validate_url = (
|
||||||
|
f"{CAS_HOST.rstrip('/')}/serviceValidate"
|
||||||
|
f"?ticket={ticket}&service={SERVICE_URL}"
|
||||||
|
)
|
||||||
|
print(validate_url)
|
||||||
|
# ===== 3. CALL CAS =====
|
||||||
|
try:
|
||||||
|
resp = requests.get(validate_url, timeout=5)
|
||||||
|
resp.raise_for_status()
|
||||||
|
except Exception:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||||
|
detail="CAS validate service unavailable",
|
||||||
|
)
|
||||||
|
|
||||||
|
# ===== 4. PARSE XML =====
|
||||||
|
try:
|
||||||
|
root = ET.fromstring(resp.text)
|
||||||
|
|
||||||
|
except ET.ParseError:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Invalid CAS response",
|
||||||
|
)
|
||||||
|
|
||||||
|
namespace = {"cas": "http://www.yale.edu/tp/cas"}
|
||||||
|
user_node = root.find(".//cas:user", namespace)
|
||||||
|
|
||||||
|
if user_node is None or not user_node.text:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="CAS returned but refused to validate your identity",
|
||||||
|
)
|
||||||
|
|
||||||
|
# ===== 5. NETID → EMAIL =====
|
||||||
|
netid = user_node.text.strip()
|
||||||
|
email = f"{netid.lower()}@vnpt.vn"
|
||||||
|
|
||||||
|
# ===== 6. CHECK USER DB =====
|
||||||
|
user = crud.get_user_by_email(db, email)
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail="User not found. Please contact to Admin.",
|
||||||
|
)
|
||||||
|
|
||||||
|
if int(getattr(user, "status", 0) or 0) != 2:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="User is not active",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
|
||||||
|
# ===== 7. USER DETAILS =====
|
||||||
|
user_details = crud.get_user_with_details(db, user)
|
||||||
|
|
||||||
|
# ===== 8. TOKEN DATA (GIỮ NGUYÊN THEO YÊU CẦU) =====
|
||||||
|
token_data = {
|
||||||
|
"sub": str(user.id),
|
||||||
|
"email": user.email,
|
||||||
|
"fullname": user.fullname,
|
||||||
|
"unit_id": user.unit_id,
|
||||||
|
"status": user.status,
|
||||||
|
}
|
||||||
|
|
||||||
|
access_token = create_access_token(data=token_data)
|
||||||
|
try:
|
||||||
|
payload = decode_access_token(access_token)
|
||||||
|
jti = payload.get("jti")
|
||||||
|
if jti:
|
||||||
|
set_latest_jti(int(user.id), jti)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ===== 9. RESPONSE =====
|
||||||
|
return {
|
||||||
|
"access_token": access_token,
|
||||||
|
"token_type": "bearer",
|
||||||
|
"user": user_details,
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,114 @@
|
|||||||
|
import os
|
||||||
|
from urllib.parse import quote_plus
|
||||||
|
from sqlalchemy import create_engine, event
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import datetime
|
||||||
|
from sqlalchemy import inspect as sa_inspect
|
||||||
|
from .utils import audit_log_path, get_current_user_ctx
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
# Prefer per-component DB config when provided; fallback to DATABASE_URL; finally to sqlite for dev
|
||||||
|
db_driver = os.getenv("DB_DRIVER")
|
||||||
|
db_user = os.getenv("DB_USER")
|
||||||
|
db_password = os.getenv("DB_PASSWORD")
|
||||||
|
db_host = os.getenv("DB_HOST")
|
||||||
|
db_port = os.getenv("DB_PORT")
|
||||||
|
db_name = os.getenv("DB_NAME")
|
||||||
|
|
||||||
|
database_url = None
|
||||||
|
if db_driver and db_user and db_host and db_port and db_name is not None:
|
||||||
|
# URL-encode username and password. If password is empty or None, omit the ':' portion
|
||||||
|
user_enc = quote_plus(db_user)
|
||||||
|
if db_password:
|
||||||
|
pwd_enc = quote_plus(db_password)
|
||||||
|
auth = f"{user_enc}:{pwd_enc}"
|
||||||
|
else:
|
||||||
|
auth = f"{user_enc}"
|
||||||
|
database_url = f"{db_driver}://{auth}@{db_host}:{db_port}/{db_name}"
|
||||||
|
|
||||||
|
if not database_url:
|
||||||
|
database_url = os.getenv("DATABASE_URL")
|
||||||
|
|
||||||
|
if not database_url:
|
||||||
|
# final fallback for local dev
|
||||||
|
database_url = "sqlite:///./test.db"
|
||||||
|
|
||||||
|
# Engine pool configuration to avoid QueuePool timeouts under concurrency
|
||||||
|
default_pool_size = int(os.getenv("DB_POOL_SIZE", "20"))
|
||||||
|
default_max_overflow = int(os.getenv("DB_POOL_MAX_OVERFLOW", "40"))
|
||||||
|
default_pool_timeout = int(os.getenv("DB_POOL_TIMEOUT", "60"))
|
||||||
|
default_pool_recycle = int(os.getenv("DB_POOL_RECYCLE", "1800")) # seconds
|
||||||
|
|
||||||
|
if database_url.startswith("sqlite"):
|
||||||
|
engine = create_engine(
|
||||||
|
database_url,
|
||||||
|
pool_pre_ping=True,
|
||||||
|
connect_args={"check_same_thread": False},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
engine = create_engine(
|
||||||
|
database_url,
|
||||||
|
pool_pre_ping=True,
|
||||||
|
pool_size=default_pool_size,
|
||||||
|
max_overflow=default_max_overflow,
|
||||||
|
pool_timeout=default_pool_timeout,
|
||||||
|
pool_recycle=default_pool_recycle,
|
||||||
|
)
|
||||||
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
|
||||||
|
def get_db():
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
yield db
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
def _write_audit(line: str) -> None:
|
||||||
|
try:
|
||||||
|
with open(audit_log_path(), "a", encoding="utf-8") as f:
|
||||||
|
f.write(line)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@event.listens_for(SessionLocal, "after_flush")
|
||||||
|
def _audit_after_flush(session, context):
|
||||||
|
user = get_current_user_ctx() or {}
|
||||||
|
uid = str((user.get("sub") or user.get("id") or "-"))
|
||||||
|
email = user.get("email") or "-"
|
||||||
|
now = datetime.datetime.utcnow().isoformat()
|
||||||
|
for obj in list(session.new):
|
||||||
|
ins = sa_inspect(obj)
|
||||||
|
mapper = ins.mapper
|
||||||
|
table = mapper.local_table.name if getattr(mapper, "local_table", None) is not None else getattr(obj, "__tablename__", obj.__class__.__name__)
|
||||||
|
pk = ins.identity
|
||||||
|
line = f"{now} | {uid} | {email} | INSERT {table} | pk={pk}\n"
|
||||||
|
_write_audit(line)
|
||||||
|
for obj in list(session.dirty):
|
||||||
|
if not session.is_modified(obj, include_collections=False):
|
||||||
|
continue
|
||||||
|
ins = sa_inspect(obj)
|
||||||
|
mapper = ins.mapper
|
||||||
|
table = mapper.local_table.name if getattr(mapper, "local_table", None) is not None else getattr(obj, "__tablename__", obj.__class__.__name__)
|
||||||
|
changes = []
|
||||||
|
for attr in ins.attrs:
|
||||||
|
hist = attr.history
|
||||||
|
if hist.has_changes():
|
||||||
|
old = hist.deleted[0] if hist.deleted else None
|
||||||
|
new = hist.added[0] if hist.added else getattr(obj, attr.key)
|
||||||
|
changes.append(f"{attr.key}={old}->{new}")
|
||||||
|
pk = ins.identity
|
||||||
|
chs = ", ".join(changes) if changes else "-"
|
||||||
|
line = f"{now} | {uid} | {email} | UPDATE {table} | pk={pk} | {chs}\n"
|
||||||
|
_write_audit(line)
|
||||||
|
for obj in list(session.deleted):
|
||||||
|
ins = sa_inspect(obj)
|
||||||
|
mapper = ins.mapper
|
||||||
|
table = mapper.local_table.name if getattr(mapper, "local_table", None) is not None else getattr(obj, "__tablename__", obj.__class__.__name__)
|
||||||
|
pk = ins.identity
|
||||||
|
line = f"{now} | {uid} | {email} | DELETE {table} | pk={pk}\n"
|
||||||
|
_write_audit(line)
|
||||||
@@ -0,0 +1,301 @@
|
|||||||
|
import os
|
||||||
|
import time
|
||||||
|
import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
from fastapi import FastAPI, Request, Depends, HTTPException, status
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from fastapi.openapi.utils import get_openapi
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from .utils import decode_access_token, set_current_user_ctx
|
||||||
|
from .database import get_db
|
||||||
|
from . import models
|
||||||
|
from typing import Dict, Any, List, Optional
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
from .database import engine, Base
|
||||||
|
from .routers import auth, users, roles, posts, month, security_index
|
||||||
|
from .routers import hardening
|
||||||
|
from .routers import files
|
||||||
|
from app.routers import system_groups as system_groups_router
|
||||||
|
from app.routers import systems as systems_router
|
||||||
|
|
||||||
|
app = FastAPI(
|
||||||
|
title="ANTT Portal API",
|
||||||
|
swagger_ui_parameters={"persistAuthorization": True},
|
||||||
|
)
|
||||||
|
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"],
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# @app.middleware("http")
|
||||||
|
# async def docs_ip_whitelist(request: Request, call_next):
|
||||||
|
# path = request.url.path
|
||||||
|
# if path.startswith("/docs") or path.startswith("/redoc") or path.startswith("/openapi.json"):
|
||||||
|
# wl = [x.strip() for x in (os.getenv("DOCS_WHITELIST_IPS") or "").split(",") if x.strip()]
|
||||||
|
# client_ip = request.client.host if request.client else ""
|
||||||
|
# if wl and client_ip not in wl:
|
||||||
|
# raise HTTPException(status_code=403, detail="IP not allowed")
|
||||||
|
# return await call_next(request)
|
||||||
|
|
||||||
|
def get_real_ip(request: Request) -> str:
|
||||||
|
xff = request.headers.get("x-forwarded-for")
|
||||||
|
if xff:
|
||||||
|
# Lấy IP đầu tiên (client thật)
|
||||||
|
return xff.split(",")[0].strip()
|
||||||
|
|
||||||
|
x_real_ip = request.headers.get("x-real-ip")
|
||||||
|
if x_real_ip:
|
||||||
|
return x_real_ip.strip()
|
||||||
|
|
||||||
|
if request.client:
|
||||||
|
return request.client.host
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
@app.middleware("http")
|
||||||
|
async def docs_ip_whitelist(request: Request, call_next):
|
||||||
|
path = request.url.path
|
||||||
|
|
||||||
|
if path.startswith(("/docs", "/redoc", "/openapi.json")):
|
||||||
|
wl = [
|
||||||
|
ip.strip()
|
||||||
|
for ip in (os.getenv("DOCS_WHITELIST_IPS") or "").split(",")
|
||||||
|
if ip.strip()
|
||||||
|
]
|
||||||
|
|
||||||
|
client_ip = get_real_ip(request)
|
||||||
|
|
||||||
|
if wl and client_ip not in wl:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=403,
|
||||||
|
detail=f"IP {client_ip} not allowed"
|
||||||
|
)
|
||||||
|
|
||||||
|
return await call_next(request)
|
||||||
|
|
||||||
|
def _activity_log_dir() -> Path:
|
||||||
|
base = os.getenv("ACTIVITY_LOG_DIR", "logs")
|
||||||
|
p = Path(base).resolve()
|
||||||
|
p.mkdir(parents=True, exist_ok=True)
|
||||||
|
return p
|
||||||
|
|
||||||
|
def _activity_log_path() -> Path:
|
||||||
|
now = datetime.datetime.utcnow()
|
||||||
|
month_dir = _activity_log_dir() / now.strftime("%Y%m")
|
||||||
|
month_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
return month_dir / f"{now.strftime('%Y-%m-%d')}.txt"
|
||||||
|
|
||||||
|
@app.middleware("http")
|
||||||
|
async def activity_logger(request: Request, call_next):
|
||||||
|
path = request.url.path
|
||||||
|
if path.startswith("/auth/login"):
|
||||||
|
return await call_next(request)
|
||||||
|
if path.startswith("/auth/login_sso"):
|
||||||
|
return await call_next(request)
|
||||||
|
method = request.method.upper()
|
||||||
|
qs = request.url.query or ""
|
||||||
|
ip = request.client.host if request.client else "-"
|
||||||
|
start = time.time()
|
||||||
|
status_code = 500
|
||||||
|
user_payload = None
|
||||||
|
auth_header = request.headers.get("Authorization") or ""
|
||||||
|
token = None
|
||||||
|
try:
|
||||||
|
scheme, token = auth_header.split()
|
||||||
|
if scheme.lower() != "bearer":
|
||||||
|
token = None
|
||||||
|
except Exception:
|
||||||
|
token = None
|
||||||
|
if token:
|
||||||
|
try:
|
||||||
|
user_payload = decode_access_token(token)
|
||||||
|
except Exception:
|
||||||
|
user_payload = None
|
||||||
|
try:
|
||||||
|
set_current_user_ctx(user_payload)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
response = await call_next(request)
|
||||||
|
status_code = response.status_code
|
||||||
|
return response
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
set_current_user_ctx(None)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
user_id = "-"
|
||||||
|
user_email = "-"
|
||||||
|
if user_payload:
|
||||||
|
try:
|
||||||
|
user_id = str(user_payload.get("sub") or "-")
|
||||||
|
user_email = user_payload.get("email") or "-"
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
duration_ms = int((time.time() - start) * 1000)
|
||||||
|
line = f"{datetime.datetime.utcnow().isoformat()} | {ip} | {user_id} | {user_email} | {method} {path}{('?' + qs) if qs else ''} | {status_code} | {duration_ms}ms\n"
|
||||||
|
with open(_activity_log_path(), "a", encoding="utf-8") as f:
|
||||||
|
f.write(line)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@app.on_event("startup")
|
||||||
|
def on_startup():
|
||||||
|
# create tables if AUTO_CREATE_DB is enabled (convenience for dev only)
|
||||||
|
if os.getenv("AUTO_CREATE_DB", "0") == "1":
|
||||||
|
Base.metadata.create_all(bind=engine)
|
||||||
|
|
||||||
|
|
||||||
|
app.include_router(auth.router)
|
||||||
|
app.include_router(roles.router, prefix="/roles", tags=["roles"])
|
||||||
|
app.include_router(users.router, prefix="/users", tags=["users"])
|
||||||
|
app.include_router(posts.router, prefix="/posts", tags=["posts"])
|
||||||
|
app.include_router(month.router, prefix="/months", tags=["months"])
|
||||||
|
app.include_router(security_index.router)
|
||||||
|
app.include_router(hardening.router)
|
||||||
|
# Đăng ký categories
|
||||||
|
from .routers import categories
|
||||||
|
app.include_router(categories.router, prefix="/categories", tags=["categories"])
|
||||||
|
app.include_router(files.router, prefix="/files", tags=["files"])
|
||||||
|
from .routers import pentest
|
||||||
|
from .routers import pentest_service
|
||||||
|
from .routers import jira
|
||||||
|
from .routers import units
|
||||||
|
from .routers import overview
|
||||||
|
from .routers import scorecard
|
||||||
|
from .routers import email as email_router
|
||||||
|
from .routers import documents as documents_router
|
||||||
|
from .routers import soc_ticket
|
||||||
|
from .routers import logsource
|
||||||
|
app.include_router(pentest.router, prefix="/pentest", tags=["pentest"])
|
||||||
|
app.include_router(pentest_service.router)
|
||||||
|
app.include_router(soc_ticket.router)
|
||||||
|
app.include_router(logsource.router)
|
||||||
|
from .routers import mail_history
|
||||||
|
app.include_router(mail_history.router, prefix="/mail-history", tags=["mail-history"])
|
||||||
|
from .routers import import_history as import_history_router
|
||||||
|
app.include_router(import_history_router.router, prefix="/import-history", tags=["import-history"])
|
||||||
|
from .routers import manage_systems as manage_systems_router
|
||||||
|
app.include_router(manage_systems_router.router)
|
||||||
|
# app.include_router(system_groups_router.router)
|
||||||
|
app.include_router(systems_router.router)
|
||||||
|
app.include_router(jira.router, prefix="/jira", tags=["jira"])
|
||||||
|
app.include_router(units.router)
|
||||||
|
app.include_router(overview.router)
|
||||||
|
app.include_router(scorecard.router)
|
||||||
|
app.include_router(email_router.email_roles_router)
|
||||||
|
app.include_router(email_router.email_users_router)
|
||||||
|
app.include_router(documents_router.router)
|
||||||
|
|
||||||
|
def custom_openapi():
|
||||||
|
if app.openapi_schema:
|
||||||
|
return app.openapi_schema
|
||||||
|
|
||||||
|
openapi_schema = get_openapi(
|
||||||
|
title="ANTT Portal API",
|
||||||
|
version="1.0.0",
|
||||||
|
description="API Gateway for ANTT Portal",
|
||||||
|
routes=app.routes,
|
||||||
|
)
|
||||||
|
|
||||||
|
openapi_schema["components"]["securitySchemes"] = {
|
||||||
|
"Bearer": {
|
||||||
|
"type": "http",
|
||||||
|
"scheme": "bearer",
|
||||||
|
"bearerFormat": "JWT",
|
||||||
|
"description": "Enter JWT token (no 'Bearer' prefix needed)",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# Áp dụng security toàn cục để UI biết cần Bearer
|
||||||
|
openapi_schema["security"] = [{"Bearer": []}]
|
||||||
|
|
||||||
|
# Add security requirement to all paths except /auth/login
|
||||||
|
if "paths" in openapi_schema:
|
||||||
|
for path, path_item in openapi_schema["paths"].items():
|
||||||
|
# Bỏ qua các endpoint auth
|
||||||
|
if "/auth/" in path:
|
||||||
|
continue
|
||||||
|
# Thêm security cho từng operation nếu chưa có
|
||||||
|
for method in ["get", "post", "put", "delete", "patch"]:
|
||||||
|
if method in path_item:
|
||||||
|
operation = path_item[method]
|
||||||
|
if "security" not in operation:
|
||||||
|
operation["security"] = [{"Bearer": []}]
|
||||||
|
|
||||||
|
app.openapi_schema = openapi_schema
|
||||||
|
return app.openapi_schema
|
||||||
|
|
||||||
|
|
||||||
|
app.openapi = custom_openapi
|
||||||
|
|
||||||
|
# Public API: system groups tree via ManageSystem (levels 0/1/2), API key + IP whitelist
|
||||||
|
@app.post("/sync/system-groups", tags=["system_groups"])
|
||||||
|
def system_groups_tree(
|
||||||
|
request: Request,
|
||||||
|
payload: Dict[str, Any],
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
):
|
||||||
|
cfg_key = os.getenv("SYSTEM_GROUPS_API_KEY")
|
||||||
|
if not cfg_key:
|
||||||
|
raise HTTPException(status_code=500, detail="SYSTEM_GROUPS_API_KEY is not configured")
|
||||||
|
use_key = str(payload.get("api_key") or "")
|
||||||
|
if (use_key or "").strip() != cfg_key.strip():
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Invalid API key",
|
||||||
|
headers={"WWW-Authenticate": "ApiKey"},
|
||||||
|
)
|
||||||
|
wl = [x.strip() for x in (os.getenv("SYSTEM_GROUPS_WHITELIST_IPS") or "").split(",") if x.strip()]
|
||||||
|
client_ip = request.client.host if request.client else ""
|
||||||
|
if wl and client_ip not in wl:
|
||||||
|
raise HTTPException(status_code=403, detail="IP not allowed")
|
||||||
|
|
||||||
|
items: List[models.ManageSystem] = db.query(models.ManageSystem).order_by(models.ManageSystem.level.asc(), models.ManageSystem.id.asc()).all()
|
||||||
|
type_map = {
|
||||||
|
0: "Web quản trị",
|
||||||
|
1: "Web dịch vụ",
|
||||||
|
2: "API",
|
||||||
|
3: "Mobile app",
|
||||||
|
4: "Khác",
|
||||||
|
5: "CNTT",
|
||||||
|
6: "ATTT",
|
||||||
|
}
|
||||||
|
nodes: Dict[int, Dict[str, Any]] = {}
|
||||||
|
for it in items:
|
||||||
|
tval = int(it.type) if getattr(it, "type", None) is not None else None
|
||||||
|
nodes[int(it.id)] = {
|
||||||
|
"id": int(it.id),
|
||||||
|
"name": it.name,
|
||||||
|
"level": int(it.level),
|
||||||
|
"parent_id": int(it.parent_id) if getattr(it, "parent_id", None) is not None else None,
|
||||||
|
"url_ip": getattr(it, "url_ip", None),
|
||||||
|
"unit_id": int(it.unit_id) if getattr(it, "unit_id", None) is not None else None,
|
||||||
|
"type": tval,
|
||||||
|
"type_name": type_map.get(tval) if tval is not None else None,
|
||||||
|
"private": bool(it.private) if getattr(it, "private", None) is not None else None,
|
||||||
|
"priority_level": int(it.priority_level) if getattr(it, "priority_level", None) is not None else None,
|
||||||
|
"status": bool(it.status) if getattr(it, "status", None) is not None else None,
|
||||||
|
"children": [],
|
||||||
|
}
|
||||||
|
roots: List[Dict[str, Any]] = []
|
||||||
|
for it in items:
|
||||||
|
node = nodes[int(it.id)]
|
||||||
|
pid = getattr(it, "parent_id", None)
|
||||||
|
if pid is None:
|
||||||
|
roots.append(node)
|
||||||
|
else:
|
||||||
|
parent = nodes.get(int(pid))
|
||||||
|
if parent:
|
||||||
|
parent["children"].append(node)
|
||||||
|
else:
|
||||||
|
roots.append(node)
|
||||||
|
return {"message": "success", "data": roots}
|
||||||
@@ -0,0 +1,541 @@
|
|||||||
|
from sqlalchemy import Column, BigInteger, Integer, String, DateTime, Text, Boolean, Float, Index, UniqueConstraint
|
||||||
|
from .database import Base
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
|
class Role(Base):
|
||||||
|
__tablename__ = "role"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
name = Column(String(100), nullable=False, unique=True)
|
||||||
|
description = Column(String(255))
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
# relationships
|
||||||
|
# relationships are intentionally omitted; handle joins in SQL queries as needed
|
||||||
|
|
||||||
|
|
||||||
|
class Permission(Base):
|
||||||
|
__tablename__ = "permission"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
name = Column(String(255), nullable=False)
|
||||||
|
path = Column(String(255), nullable=False)
|
||||||
|
method = Column(String(20), nullable=False)
|
||||||
|
description = Column(String(255))
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
# relationships intentionally omitted
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Unit(Base):
|
||||||
|
__tablename__ = "units"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
name = Column(String(255), nullable=False)
|
||||||
|
description = Column(Text)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
# relationships intentionally omitted
|
||||||
|
|
||||||
|
__table_args__ = (Index('ix_units_name', 'name'),)
|
||||||
|
|
||||||
|
|
||||||
|
class User(Base):
|
||||||
|
|
||||||
|
__tablename__ = "users"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
email = Column(String(255), nullable=False, unique=True)
|
||||||
|
fullname = Column(String(255), nullable=False)
|
||||||
|
unit_id = Column(BigInteger, nullable=True)
|
||||||
|
role_id = Column(BigInteger, nullable=True)
|
||||||
|
status = Column(Integer, default=1)
|
||||||
|
#status 1 is created, 2 is active, 3 is disabled
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
|
||||||
|
hashed_password = Column(String(255), nullable=True)
|
||||||
|
|
||||||
|
# relationships intentionally omitted
|
||||||
|
|
||||||
|
__table_args__ = (Index('ix_users_email', 'email'),)
|
||||||
|
|
||||||
|
class Category(Base):
|
||||||
|
__tablename__ = "category"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
name = Column(String(255), nullable=False, unique=True)
|
||||||
|
|
||||||
|
# relationships intentionally omitted
|
||||||
|
|
||||||
|
|
||||||
|
class Post(Base):
|
||||||
|
__tablename__ = "posts"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
content = Column(Text, nullable=False)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
title = Column(String(255), nullable=False)
|
||||||
|
created_by = Column(BigInteger)
|
||||||
|
category_id = Column(BigInteger)
|
||||||
|
status = Column(Boolean, default=True)
|
||||||
|
thumbnail = Column(String(255), nullable=True)
|
||||||
|
|
||||||
|
# relationships intentionally omitted
|
||||||
|
|
||||||
|
|
||||||
|
class Month(Base):
|
||||||
|
__tablename__ = "month"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
name = Column(String(50), nullable=False)
|
||||||
|
from_date = Column(DateTime, nullable=False)
|
||||||
|
end_date = Column(DateTime, nullable=False)
|
||||||
|
|
||||||
|
# relationships intentionally omitted
|
||||||
|
|
||||||
|
|
||||||
|
class NAC(Base):
|
||||||
|
__tablename__ = "nac"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
month_id = Column(BigInteger, nullable=False)
|
||||||
|
unit_id = Column(BigInteger, nullable=False)
|
||||||
|
total = Column(BigInteger, default=0)
|
||||||
|
installed = Column(BigInteger, default=0)
|
||||||
|
ignored = Column(BigInteger, default=0)
|
||||||
|
rate = Column(Float, default=0)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
# relationships intentionally omitted
|
||||||
|
|
||||||
|
|
||||||
|
class SmartIR(Base):
|
||||||
|
__tablename__ = "smartir"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
month_id = Column(BigInteger, nullable=False)
|
||||||
|
unit_id = Column(BigInteger, nullable=False)
|
||||||
|
total = Column(BigInteger, default=0)
|
||||||
|
installed = Column(BigInteger, default=0)
|
||||||
|
new_install = Column(BigInteger, default=0)
|
||||||
|
ignored = Column(BigInteger, default=0)
|
||||||
|
rate = Column(Float, default=0)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
# relationships intentionally omitted
|
||||||
|
|
||||||
|
|
||||||
|
class Compliance(Base):
|
||||||
|
__tablename__ = "compliance"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
month_id = Column(BigInteger, nullable=False)
|
||||||
|
unit_id = Column(BigInteger, nullable=False)
|
||||||
|
windows_key = Column(BigInteger, default=0)
|
||||||
|
office = Column(BigInteger, default=0)
|
||||||
|
ms17010 = Column(BigInteger, default=0)
|
||||||
|
firewall = Column(BigInteger, default=0)
|
||||||
|
uac = Column(BigInteger, default=0)
|
||||||
|
winrar = Column(BigInteger, default=0)
|
||||||
|
antivirus = Column(BigInteger, default=0)
|
||||||
|
update_win = Column(BigInteger, default=0)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
# relationships intentionally omitted
|
||||||
|
|
||||||
|
|
||||||
|
class LogSource(Base):
|
||||||
|
__tablename__ = "logsource"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
month_id = Column(BigInteger, nullable=False)
|
||||||
|
unit_id = Column(BigInteger, nullable=False)
|
||||||
|
log_id = Column(BigInteger)
|
||||||
|
name = Column(String(255), nullable=False)
|
||||||
|
description = Column(Text)
|
||||||
|
is_enabled = Column(Boolean, default=True)
|
||||||
|
source_type_id = Column(BigInteger)
|
||||||
|
status = Column(Integer) # lưu mã 1=OK, 2=ERROR, 3=DISABLE, 4=NOT AVAILABLE
|
||||||
|
message = Column(String(255))
|
||||||
|
other = Column(String(255))
|
||||||
|
file_id = Column(BigInteger, nullable=True)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
# relationships intentionally omitted
|
||||||
|
|
||||||
|
|
||||||
|
class LogSourceComment(Base):
|
||||||
|
__tablename__ = "logsource_comment"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
logsource_id = Column(BigInteger, nullable=False)
|
||||||
|
user_id = Column(BigInteger, nullable=False)
|
||||||
|
comment = Column(Text, nullable=False)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
|
||||||
|
class SourceType(Base):
|
||||||
|
__tablename__ = "source_type"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
name = Column(String(255), nullable=False)
|
||||||
|
type = Column(String(100))
|
||||||
|
|
||||||
|
# relationships intentionally omitted
|
||||||
|
|
||||||
|
|
||||||
|
class Ticket(Base):
|
||||||
|
__tablename__ = "ticket"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
open_count = Column(BigInteger, default=0)
|
||||||
|
in_process = Column(BigInteger, default=0)
|
||||||
|
completed = Column(BigInteger, default=0)
|
||||||
|
unit_id = Column(BigInteger, nullable=False)
|
||||||
|
month_id = Column(BigInteger, nullable=False)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
# relationships intentionally omitted
|
||||||
|
|
||||||
|
|
||||||
|
class RootAccess(Base):
|
||||||
|
__tablename__ = "root_access"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
month_id = Column(BigInteger, nullable=False)
|
||||||
|
unit_id = Column(BigInteger, nullable=False)
|
||||||
|
number = Column(BigInteger, default=0)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
# relationships intentionally omitted
|
||||||
|
|
||||||
|
|
||||||
|
class AttackStatics(Base):
|
||||||
|
__tablename__ = "attack_statics"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
month_id = Column(BigInteger, nullable=False)
|
||||||
|
siem_fintech = Column(BigInteger, default=0)
|
||||||
|
siem_media = Column(BigInteger, default=0)
|
||||||
|
ticket = Column(BigInteger, default=0)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
# relationships intentionally omitted
|
||||||
|
|
||||||
|
|
||||||
|
class SmartIRDetail(Base):
|
||||||
|
__tablename__ = "smartir_detail"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
month_id = Column(BigInteger, nullable=False)
|
||||||
|
unit_id = Column(BigInteger, nullable=True)
|
||||||
|
vnpt_ma_nhan_vien = Column(String(255), nullable=True)
|
||||||
|
name = Column(String(255), nullable=True)
|
||||||
|
email = Column(String(255), nullable=True)
|
||||||
|
phong_ban = Column(String(255), nullable=True)
|
||||||
|
ip = Column(String(255), nullable=True)
|
||||||
|
pc_name = Column(String(255), nullable=True)
|
||||||
|
mac = Column(String(255), nullable=True)
|
||||||
|
agent_version = Column(String(255), nullable=True)
|
||||||
|
last_online = Column(String(255), nullable=True)
|
||||||
|
status = Column(Integer, nullable=True)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
# relationships intentionally omitted
|
||||||
|
|
||||||
|
class NACDetail(Base):
|
||||||
|
__tablename__ = "nac_detail"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
month_id = Column(BigInteger, nullable=False)
|
||||||
|
unit_id = Column(BigInteger, nullable=True)
|
||||||
|
name = Column(String(255), nullable=True)
|
||||||
|
email = Column(String(255), nullable=True)
|
||||||
|
status = Column(Integer, nullable=True)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
class ComplianceDetail(Base):
|
||||||
|
__tablename__ = "compliance_detail"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
unit_id = Column(BigInteger, nullable=True)
|
||||||
|
month_id = Column(BigInteger, nullable=False)
|
||||||
|
vnpt_ma_nhan_vien = Column(String(255), nullable=True)
|
||||||
|
name = Column(String(255), nullable=True)
|
||||||
|
email = Column(String(255), nullable=True)
|
||||||
|
phong_ban = Column(String(255), nullable=True)
|
||||||
|
windows_key = Column(BigInteger, nullable=True)
|
||||||
|
office = Column(BigInteger, nullable=True)
|
||||||
|
ms17010 = Column(BigInteger, nullable=True)
|
||||||
|
firewall = Column(BigInteger, nullable=True)
|
||||||
|
uac = Column(BigInteger, nullable=True)
|
||||||
|
winrar = Column(BigInteger, nullable=True)
|
||||||
|
antivirus = Column(BigInteger, nullable=True)
|
||||||
|
update_win = Column(BigInteger, nullable=True)
|
||||||
|
status = Column(Integer, nullable=True)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
class ComplianceSmartIR(Base):
|
||||||
|
__tablename__ = "compliance_smartir"
|
||||||
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
month_id = Column(BigInteger, nullable=False)
|
||||||
|
unit_id = Column(BigInteger, nullable=True)
|
||||||
|
vnpt_ma_nhan_vien = Column(String(20), nullable=False)
|
||||||
|
name = Column(String(255), nullable=False)
|
||||||
|
email = Column(String(255), nullable=False)
|
||||||
|
phong_ban = Column(String(255), nullable=True)
|
||||||
|
ten_may_tinh = Column(String(255), nullable=True)
|
||||||
|
mac = Column(String(50), nullable=True)
|
||||||
|
active_windows = Column(Integer, nullable=True)
|
||||||
|
bat_windows_firewall = Column(Integer, nullable=True)
|
||||||
|
bat_uac = Column(Integer, nullable=True)
|
||||||
|
cai_winrar_ban_moi = Column(Integer, nullable=True)
|
||||||
|
bat_av_fullscan = Column(Integer, nullable=True)
|
||||||
|
update_windows = Column(Integer, nullable=True)
|
||||||
|
status = Column(Integer, nullable=False, default=0)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
updated_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
class MailHistory(Base):
|
||||||
|
__tablename__ = "mail_history"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
post_id = Column(BigInteger)
|
||||||
|
subject = Column(String(255), nullable=True)
|
||||||
|
role_id = Column(BigInteger, nullable=True)
|
||||||
|
content = Column(Text)
|
||||||
|
status = Column(Integer, nullable=True)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
# relationships intentionally omitted
|
||||||
|
|
||||||
|
|
||||||
|
class FileUpload(Base):
|
||||||
|
__tablename__ = "file_upload"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
path = Column(String(255), nullable=False)
|
||||||
|
table_name = Column(String(255), nullable=False)
|
||||||
|
table_id = Column(BigInteger, nullable=False)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
__table_args__ = (Index('ix_fileupload_table', 'table_name', 'table_id'),)
|
||||||
|
|
||||||
|
|
||||||
|
class ImportHistory(Base):
|
||||||
|
__tablename__ = "import_history"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
type = Column(String(255), nullable=False)
|
||||||
|
file_path = Column(String(255), nullable=False)
|
||||||
|
month_id = Column(BigInteger, nullable=False)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
__table_args__ = (Index('ix_import_history_type_month', 'type', 'month_id'),)
|
||||||
|
|
||||||
|
|
||||||
|
class MMFintech(Base):
|
||||||
|
__tablename__ = "mm_fintech"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True, index=True)
|
||||||
|
month_id = Column(BigInteger, nullable=False, index=True)
|
||||||
|
unit_id = Column(BigInteger, nullable=True, index=True)
|
||||||
|
file_path = Column(String(255), nullable=False)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
class ConvertHardening(Base):
|
||||||
|
__tablename__ = "convert_hardening"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True, index=True)
|
||||||
|
filename = Column(String(255), nullable=False, index=True)
|
||||||
|
user_id = Column(BigInteger, nullable=False, index=True)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
|
||||||
|
class SystemGroup(Base):
|
||||||
|
__tablename__ = "system_group"
|
||||||
|
|
||||||
|
id = Column(BigInteger, primary_key=True, index=True, autoincrement=True)
|
||||||
|
name = Column(String(255), nullable=False, index=True)
|
||||||
|
description = Column(String(1000), nullable=True)
|
||||||
|
|
||||||
|
class System(Base):
|
||||||
|
__tablename__ = "system"
|
||||||
|
|
||||||
|
id = Column(BigInteger, primary_key=True, index=True, autoincrement=True)
|
||||||
|
name = Column(String(255), nullable=False, index=True)
|
||||||
|
url_ip = Column(String(255), nullable=True, index=True)
|
||||||
|
unit_id = Column(BigInteger, nullable=True, index=True)
|
||||||
|
system_group_id = Column(BigInteger, nullable=True, index=True)
|
||||||
|
description = Column(String(1000), nullable=True)
|
||||||
|
status = Column(Integer, nullable=True, index=True)
|
||||||
|
|
||||||
|
class ManageSystem(Base):
|
||||||
|
__tablename__ = "manage_system"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
name = Column(String(255), nullable=False, unique=True)
|
||||||
|
level = Column(Integer, nullable=False)
|
||||||
|
url_ip = Column(String(255), nullable=True)
|
||||||
|
unit_id = Column(BigInteger, nullable=True)
|
||||||
|
parent_id = Column(BigInteger, nullable=True)
|
||||||
|
type = Column(Integer, nullable=True)
|
||||||
|
private = Column(Boolean, default=False)
|
||||||
|
priority_level = Column(Integer, nullable=True)
|
||||||
|
status = Column(Boolean, default=True)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
|
||||||
|
__table_args__ = (
|
||||||
|
Index('ix_manage_system_name', 'name'),
|
||||||
|
Index('ix_manage_system_level_parent', 'level', 'parent_id'),
|
||||||
|
Index('ix_manage_system_unit', 'unit_id'),
|
||||||
|
)
|
||||||
|
|
||||||
|
class PentestService(Base):
|
||||||
|
__tablename__ = "pentest_service"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
name = Column(Text, nullable=True)
|
||||||
|
type = Column(Integer, nullable=False) # 1: Đánh giá ATTT, 2: Công việc phối hợp, 3: Phối hợp xử lý, 4: Khác
|
||||||
|
code = Column(String(50), nullable=False, unique=True)
|
||||||
|
file_path = Column(String(255), nullable=True)
|
||||||
|
description = Column(Text, nullable=True)
|
||||||
|
due_date = Column(DateTime, nullable=True)
|
||||||
|
unit_id = Column(BigInteger, nullable=True)
|
||||||
|
status = Column(Integer, default=0) # 0: nháp, 1: tạo mới, 2: tiếp nhận, 3: đã xử lý, 4: đóng, 5: hủy
|
||||||
|
user_id = Column(BigInteger, nullable=True)
|
||||||
|
target_id = Column(BigInteger, nullable=True)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
class PentestServiceComment(Base):
|
||||||
|
__tablename__ = "pentest_service_comment"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
pentest_service_id = Column(BigInteger, nullable=False)
|
||||||
|
content = Column(Text, nullable=False)
|
||||||
|
user_id = Column(BigInteger, nullable=False)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
class PentestServiceHistory(Base):
|
||||||
|
__tablename__ = "pentest_service_history"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
pentest_service_id = Column(BigInteger, nullable=False)
|
||||||
|
old_status = Column(Integer, nullable=True)
|
||||||
|
new_status = Column(Integer, nullable=False)
|
||||||
|
user_id = Column(BigInteger, nullable=False)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
class PentestServiceAssign(Base):
|
||||||
|
__tablename__ = "pentest_service_assign"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
user_id = Column(BigInteger, nullable=True)
|
||||||
|
email = Column(String(255), nullable=True)
|
||||||
|
pentest_service_id = Column(BigInteger, nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class RevokedToken(Base):
|
||||||
|
__tablename__ = "revoked_token"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
jti = Column(String(64), nullable=False, unique=True, index=True)
|
||||||
|
user_id = Column(BigInteger, nullable=True)
|
||||||
|
revoked_at = Column(DateTime, default=datetime.datetime.utcnow, nullable=False)
|
||||||
|
expires_at = Column(DateTime, nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
class EmailRole(Base):
|
||||||
|
__tablename__ = "email_role"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
name = Column(String(255), nullable=False)
|
||||||
|
description = Column(String(1000), nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
class EmailUser(Base):
|
||||||
|
__tablename__ = "email_user"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
email = Column(String(255), nullable=False)
|
||||||
|
name = Column(String(255), nullable=True)
|
||||||
|
role_id = Column(BigInteger, nullable=False)
|
||||||
|
type = Column(Integer, nullable=False)
|
||||||
|
__table_args__ = (Index('ix_email_user_role', 'role_id'),)
|
||||||
|
|
||||||
|
|
||||||
|
class Document(Base):
|
||||||
|
__tablename__ = "documents"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
name = Column(String(255), nullable=False)
|
||||||
|
number_doc = Column(String(255), nullable=False)
|
||||||
|
sign_date = Column(DateTime, nullable=True)
|
||||||
|
level = Column(Integer, nullable=False)
|
||||||
|
validate_date = Column(DateTime, nullable=True)
|
||||||
|
file_path = Column(String(255), nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
class SecurityIndex(Base):
|
||||||
|
__tablename__ = "security_index"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True, index=True)
|
||||||
|
soc = Column(Integer, default=0)
|
||||||
|
pentest = Column(Integer, default=0)
|
||||||
|
access_policy = Column(Integer, default=0)
|
||||||
|
two_fa_policy = Column(Integer, default=0) # Cannot start with digit in Python, mapped to 2fa_policy if needed
|
||||||
|
config_network = Column(Integer, default=0)
|
||||||
|
config_server = Column(Integer, default=0)
|
||||||
|
patch_security = Column(Integer, default=0)
|
||||||
|
month_id = Column(BigInteger, nullable=False, index=True)
|
||||||
|
unit_id = Column(BigInteger, nullable=False, index=True)
|
||||||
|
description = Column(Text, nullable=True)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
class SocTarget(Base):
|
||||||
|
__tablename__ = "soc_target"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
unit_id = Column(BigInteger, nullable=False)
|
||||||
|
priority = Column(Integer, nullable=False) # 1=critical → 5=low
|
||||||
|
name = Column(String(255), nullable=False)
|
||||||
|
code = Column(String(100), unique=True)
|
||||||
|
status = Column(Integer, nullable=False, default=0) # 0:Open, 1:In Progress, 2:Resolved, 3:Closed
|
||||||
|
deadline = Column(DateTime)
|
||||||
|
description = Column(Text, nullable=False)
|
||||||
|
system_id = Column(BigInteger, nullable=True)
|
||||||
|
system_type = Column(Integer, nullable=True)
|
||||||
|
source_ip = Column(Text, nullable=True)
|
||||||
|
destination_ip = Column(Text, nullable=True)
|
||||||
|
soc_target_type_id = Column(BigInteger, nullable=True)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
|
||||||
|
created_by = Column(BigInteger, nullable=True)
|
||||||
|
|
||||||
|
class SocTargetType(Base):
|
||||||
|
__tablename__ = "soc_target_type"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
name = Column(String(255), nullable=False)
|
||||||
|
|
||||||
|
class TicketRelated(Base):
|
||||||
|
__tablename__ = "ticket_related"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
soc_target_id = Column(BigInteger, nullable=False)
|
||||||
|
related_soc_target_id = Column(BigInteger, nullable=False)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
class SocTargetAssign(Base):
|
||||||
|
__tablename__ = "soc_target_assign"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
target_id = Column(BigInteger, nullable=False)
|
||||||
|
user_id = Column(BigInteger, nullable=True)
|
||||||
|
email = Column(String(255), nullable=True)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
class SocTargetHistory(Base):
|
||||||
|
__tablename__ = "soc_target_history"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
target_id = Column(BigInteger, nullable=False)
|
||||||
|
field_name = Column(String(100), nullable=False)
|
||||||
|
old_value = Column(Text)
|
||||||
|
new_value = Column(Text)
|
||||||
|
changed_by = Column(BigInteger, nullable=True)
|
||||||
|
changed_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
class SocTargetComment(Base):
|
||||||
|
__tablename__ = "soc_target_comment"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
target_id = Column(BigInteger, nullable=False)
|
||||||
|
user_id = Column(BigInteger, nullable=True)
|
||||||
|
email = Column(String(255), nullable=True)
|
||||||
|
content = Column(Text, nullable=False)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
class SocTargetAttachment(Base):
|
||||||
|
__tablename__ = "soc_target_attachment"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
target_id = Column(BigInteger, nullable=False)
|
||||||
|
file_name = Column(String(255), nullable=False)
|
||||||
|
file_path = Column(String(500), nullable=False)
|
||||||
|
file_size = Column(BigInteger, nullable=True)
|
||||||
|
file_type = Column(String(100), nullable=True)
|
||||||
|
uploaded_by = Column(BigInteger, nullable=True)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
class PostComment(Base):
|
||||||
|
__tablename__ = "post_comment"
|
||||||
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||||
|
post_id = Column(BigInteger, nullable=False)
|
||||||
|
user_id = Column(BigInteger, nullable=False)
|
||||||
|
content = Column(Text, nullable=False)
|
||||||
|
created_at = Column(DateTime, default=datetime.datetime.utcnow)
|
||||||
|
updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
import hashlib
|
||||||
|
import random
|
||||||
|
|
||||||
|
# RADIUS Attribute Type names (RFC 2865 + common)
|
||||||
|
RADIUS_ATTR_NAMES = {
|
||||||
|
1: "User-Name",
|
||||||
|
6: "Service-Type",
|
||||||
|
8: "Framed-IP-Address",
|
||||||
|
11: "Filter-Id",
|
||||||
|
18: "Reply-Message",
|
||||||
|
25: "Class",
|
||||||
|
26: "Vendor-Specific",
|
||||||
|
27: "Session-Timeout",
|
||||||
|
}
|
||||||
|
|
||||||
|
def encrypt_password(password: str, secret: str, request_authenticator: bytes) -> bytes:
|
||||||
|
"""Mã hóa mật khẩu PAP theo chuẩn RADIUS RFC 2865."""
|
||||||
|
password_bytes = password.encode('utf-8')
|
||||||
|
if len(password_bytes) % 16 != 0:
|
||||||
|
password_bytes += b'\x00' * (16 - len(password_bytes) % 16)
|
||||||
|
|
||||||
|
encrypted = b''
|
||||||
|
last = secret.encode('utf-8') + request_authenticator
|
||||||
|
for i in range(0, len(password_bytes), 16):
|
||||||
|
block = password_bytes[i:i+16]
|
||||||
|
md5_hash = hashlib.md5(last).digest()
|
||||||
|
encrypted_block = bytes(a ^ b for a, b in zip(block, md5_hash))
|
||||||
|
encrypted += encrypted_block
|
||||||
|
last = secret.encode('utf-8') + encrypted_block
|
||||||
|
return encrypted
|
||||||
|
|
||||||
|
|
||||||
|
def build_access_request(username: str, password: str, secret: str,
|
||||||
|
identifier: int = None) -> bytes:
|
||||||
|
"""Tạo gói Access-Request PAP cho RADIUS server."""
|
||||||
|
if identifier is None:
|
||||||
|
identifier = random.randint(0, 255)
|
||||||
|
|
||||||
|
request_authenticator = bytes(random.getrandbits(8) for _ in range(16))
|
||||||
|
|
||||||
|
username_attr = b'\x01' + struct.pack('B', len(username) + 2) + username.encode('utf-8')
|
||||||
|
password_attr_bytes = encrypt_password(password, secret, request_authenticator)
|
||||||
|
password_attr = b'\x02' + struct.pack('B', len(password_attr_bytes) + 2) + password_attr_bytes
|
||||||
|
|
||||||
|
attrs = username_attr + password_attr
|
||||||
|
length = 20 + len(attrs)
|
||||||
|
header = struct.pack('!BBH', 1, identifier, length) + request_authenticator
|
||||||
|
return header + attrs
|
||||||
|
|
||||||
|
|
||||||
|
def parse_radius_attributes(attributes_raw: bytes) -> dict:
|
||||||
|
"""
|
||||||
|
Parse phần attributes của gói RADIUS response.
|
||||||
|
Trả về dict: {type_int: [value_bytes, ...]}
|
||||||
|
"""
|
||||||
|
result: dict[int, list] = {}
|
||||||
|
idx = 0
|
||||||
|
while idx < len(attributes_raw):
|
||||||
|
if idx + 2 > len(attributes_raw):
|
||||||
|
break
|
||||||
|
attr_type = attributes_raw[idx]
|
||||||
|
attr_len = attributes_raw[idx + 1]
|
||||||
|
if attr_len < 2 or idx + attr_len > len(attributes_raw):
|
||||||
|
break
|
||||||
|
attr_value = attributes_raw[idx + 2: idx + attr_len]
|
||||||
|
result.setdefault(attr_type, []).append(attr_value)
|
||||||
|
idx += attr_len
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def check_radius_login(server: str, port: int, secret: str, username: str,
|
||||||
|
password: str, timeout: int = 5) -> bool:
|
||||||
|
"""
|
||||||
|
Kiểm tra username/password với RADIUS server (PAP).
|
||||||
|
Trả về True nếu Access-Accept, False nếu thất bại.
|
||||||
|
(Giữ nguyên signature để không ảnh hưởng code cũ)
|
||||||
|
"""
|
||||||
|
ok, _ = check_radius_login_extended(server, port, secret, username, password, timeout)
|
||||||
|
return ok
|
||||||
|
|
||||||
|
|
||||||
|
def check_radius_login_extended(server: str, port: int, secret: str, username: str,
|
||||||
|
password: str, timeout: int = 5) -> tuple[bool, dict]:
|
||||||
|
"""
|
||||||
|
Kiểm tra username/password với RADIUS server (PAP).
|
||||||
|
Trả về (success: bool, attributes: dict)
|
||||||
|
attributes chứa các giá trị decode từ RADIUS response, ví dụ:
|
||||||
|
{
|
||||||
|
'filter_id': ['Trung tâm An ninh thông tin'],
|
||||||
|
'reply_message': ['privacyIDEA access granted'],
|
||||||
|
'raw': {11: [b'...'], 18: [b'...']},
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
sock.settimeout(timeout)
|
||||||
|
attrs_decoded: dict = {}
|
||||||
|
try:
|
||||||
|
req_packet = build_access_request(username, password, secret)
|
||||||
|
sock.sendto(req_packet, (server, port))
|
||||||
|
|
||||||
|
resp, _ = sock.recvfrom(4096)
|
||||||
|
code = resp[0]
|
||||||
|
length = struct.unpack('!H', resp[2:4])[0]
|
||||||
|
raw_attrs = parse_radius_attributes(resp[20:length])
|
||||||
|
|
||||||
|
# Decode các attributes thường gặp sang chuỗi
|
||||||
|
def _decode_list(raw_list: list) -> list[str]:
|
||||||
|
return [v.decode('utf-8', errors='replace') for v in raw_list]
|
||||||
|
|
||||||
|
attrs_decoded['raw'] = raw_attrs
|
||||||
|
|
||||||
|
# Filter-Id (Type 11) — thường chứa thông tin nhóm/đơn vị
|
||||||
|
if 11 in raw_attrs:
|
||||||
|
attrs_decoded['filter_id'] = _decode_list(raw_attrs[11])
|
||||||
|
|
||||||
|
# Reply-Message (Type 18)
|
||||||
|
if 18 in raw_attrs:
|
||||||
|
attrs_decoded['reply_message'] = _decode_list(raw_attrs[18])
|
||||||
|
|
||||||
|
# Class (Type 25)
|
||||||
|
if 25 in raw_attrs:
|
||||||
|
attrs_decoded['class'] = _decode_list(raw_attrs[25])
|
||||||
|
|
||||||
|
if code == 2: # Access-Accept
|
||||||
|
print(f"[RADIUS] ✅ Đăng nhập thành công: {username}")
|
||||||
|
if attrs_decoded.get('filter_id'):
|
||||||
|
print(f"[RADIUS] Filter-Id: {attrs_decoded['filter_id']}")
|
||||||
|
return True, attrs_decoded
|
||||||
|
else:
|
||||||
|
print(f"[RADIUS] ❌ Sai username hoặc password: {username}")
|
||||||
|
return False, attrs_decoded
|
||||||
|
|
||||||
|
except socket.timeout:
|
||||||
|
print("[RADIUS] ⏱ Timeout khi kết nối đến RADIUS server.")
|
||||||
|
return False, {}
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[RADIUS] ⚠️ Lỗi: {e}")
|
||||||
|
return False, {}
|
||||||
|
finally:
|
||||||
|
sock.close()
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,342 @@
|
|||||||
|
"""JWT authentication dependencies for FastAPI."""
|
||||||
|
from fastapi import Depends, HTTPException, status
|
||||||
|
from fastapi.security import HTTPBearer
|
||||||
|
from starlette.requests import Request
|
||||||
|
from app.utils import decode_access_token
|
||||||
|
import jwt
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from app.database import get_db
|
||||||
|
from app.models import RevokedToken, User
|
||||||
|
import re
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
security = HTTPBearer()
|
||||||
|
|
||||||
|
|
||||||
|
# In-memory revoked token store (non-persistent). Use DB for production.
|
||||||
|
# In-memory fallback (không bền vững). Giữ lại để chặn ngay trong tiến trình hiện tại.
|
||||||
|
REVOKED_TOKENS: set[str] = set()
|
||||||
|
LATEST_JTI_PER_USER: dict[int, str] = {}
|
||||||
|
|
||||||
|
def revoke_token(token: str) -> None:
|
||||||
|
REVOKED_TOKENS.add(token)
|
||||||
|
|
||||||
|
def set_latest_jti(user_id: int, jti: str) -> None:
|
||||||
|
LATEST_JTI_PER_USER[user_id] = jti
|
||||||
|
|
||||||
|
def revoke_token_db(db: Session, token: str) -> None:
|
||||||
|
"""Decode token and persist its jti to DB for revocation."""
|
||||||
|
payload = decode_access_token(token)
|
||||||
|
jti = payload.get("jti")
|
||||||
|
sub = payload.get("sub")
|
||||||
|
exp = payload.get("exp")
|
||||||
|
expires_at = None
|
||||||
|
if isinstance(exp, (int, float)):
|
||||||
|
expires_at = datetime.datetime.utcfromtimestamp(exp)
|
||||||
|
elif isinstance(exp, datetime.datetime):
|
||||||
|
expires_at = exp
|
||||||
|
|
||||||
|
if not jti:
|
||||||
|
# không có jti thì vẫn dùng in-memory để tránh bỏ sót
|
||||||
|
REVOKED_TOKENS.add(token)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not db.query(RevokedToken).filter(RevokedToken.jti == jti).first():
|
||||||
|
db.add(RevokedToken(
|
||||||
|
jti=jti,
|
||||||
|
user_id=int(sub) if sub is not None else None,
|
||||||
|
expires_at=expires_at
|
||||||
|
))
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
async def get_current_user(request: Request, db: Session = Depends(get_db)):
|
||||||
|
"""Dependency to extract and validate JWT token from request headers.
|
||||||
|
|
||||||
|
Returns the decoded token payload with user info.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
HTTPException: If token is invalid, expired, or missing
|
||||||
|
"""
|
||||||
|
auth_header = request.headers.get("Authorization")
|
||||||
|
if not auth_header:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Missing authorization header",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
scheme, token = auth_header.split()
|
||||||
|
if scheme.lower() != "bearer":
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Invalid authentication scheme",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Invalid authorization header",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Reject revoked tokens (in-memory fallback)
|
||||||
|
if token in REVOKED_TOKENS:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Token has been revoked",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
payload = decode_access_token(token)
|
||||||
|
# Check DB revocation via jti
|
||||||
|
jti = payload.get("jti")
|
||||||
|
if jti and db.query(RevokedToken).filter(RevokedToken.jti == jti).first():
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Token has been revoked",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
|
||||||
|
user_id: str = payload.get("sub")
|
||||||
|
if user_id is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Invalid token",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
uid_int = int(user_id)
|
||||||
|
latest = LATEST_JTI_PER_USER.get(uid_int)
|
||||||
|
if latest and jti and jti != latest:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Token is not the latest",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
u = db.query(User).filter(User.id == uid_int).first()
|
||||||
|
if u:
|
||||||
|
if payload.get("role_id") is None:
|
||||||
|
payload["role_id"] = getattr(u, "role_id", None)
|
||||||
|
if payload.get("unit_id") is None:
|
||||||
|
payload["unit_id"] = getattr(u, "unit_id", None)
|
||||||
|
if not payload.get("email"):
|
||||||
|
payload["email"] = getattr(u, "email", None)
|
||||||
|
if not payload.get("fullname"):
|
||||||
|
payload["fullname"] = getattr(u, "fullname", None)
|
||||||
|
if payload.get("status") is None:
|
||||||
|
payload["status"] = getattr(u, "status", None)
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return payload
|
||||||
|
except jwt.ExpiredSignatureError:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Token has expired",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
except jwt.InvalidTokenError:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Invalid token",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
# ---- RBAC helpers ----
|
||||||
|
|
||||||
|
# Role IDs
|
||||||
|
ROLE_ADMIN = 1
|
||||||
|
ROLE_DIRECTOR = 2
|
||||||
|
ROLE_MANAGER = 3
|
||||||
|
ROLE_LEADER_PM = 4
|
||||||
|
ROLE_ADMIN_LIMITED = 9
|
||||||
|
|
||||||
|
def _role_id(user: dict) -> int | None:
|
||||||
|
try:
|
||||||
|
rid = user.get("role_id")
|
||||||
|
return int(rid) if rid is not None else None
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def is_admin(user: dict) -> bool:
|
||||||
|
return _role_id(user) in (ROLE_ADMIN, ROLE_ADMIN_LIMITED)
|
||||||
|
|
||||||
|
def is_director(user: dict) -> bool:
|
||||||
|
return _role_id(user) == ROLE_DIRECTOR
|
||||||
|
|
||||||
|
def is_manager(user: dict) -> bool:
|
||||||
|
return _role_id(user) == ROLE_MANAGER
|
||||||
|
|
||||||
|
def is_leader_pm(user: dict) -> bool:
|
||||||
|
return _role_id(user) == ROLE_LEADER_PM
|
||||||
|
|
||||||
|
def get_user_email(user: dict) -> str:
|
||||||
|
return (user.get("email") or "").strip()
|
||||||
|
|
||||||
|
def get_user_unit_id(user: dict) -> int | None:
|
||||||
|
try:
|
||||||
|
uid = user.get("unit_id")
|
||||||
|
return int(uid) if uid is not None else None
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
# RBAC
|
||||||
|
ROLE_ADMIN = 1
|
||||||
|
ROLE_DIRECTOR = 2
|
||||||
|
ROLE_MANAGER = 3
|
||||||
|
ROLE_LEADER_PM = 4
|
||||||
|
ROLE_ADMIN_LIMITED = 9
|
||||||
|
|
||||||
|
def _role_id(user: dict) -> int | None:
|
||||||
|
try:
|
||||||
|
rid = user.get("role_id")
|
||||||
|
return int(rid) if rid is not None else None
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# def is_admin(user: dict) -> bool:
|
||||||
|
# return _role_id(user) in (ROLE_ADMIN, ROLE_ADMIN_LIMITED)
|
||||||
|
|
||||||
|
# def is_director(user: dict) -> bool:
|
||||||
|
# return _role_id(user) == ROLE_DIRECTOR
|
||||||
|
|
||||||
|
# def is_manager(user: dict) -> bool:
|
||||||
|
# return _role_id(user) == ROLE_MANAGER
|
||||||
|
|
||||||
|
# def is_leader_pm(user: dict) -> bool:
|
||||||
|
# return _role_id(user) == ROLE_LEADER_PM
|
||||||
|
|
||||||
|
# def get_user_email(user: dict) -> str:
|
||||||
|
# return (user.get("email") or "").strip()
|
||||||
|
|
||||||
|
def _method_path(request: Request) -> tuple[str, str]:
|
||||||
|
return request.method.upper(), request.url.path.lower()
|
||||||
|
|
||||||
|
def _path_match(path: str, patterns: list[str]) -> bool:
|
||||||
|
for p in patterns:
|
||||||
|
q = p.lower()
|
||||||
|
if "*" in q:
|
||||||
|
regex = "^" + re.escape(q).replace("\\*", ".*") + "$"
|
||||||
|
if re.match(regex, path):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
if path == q or path.startswith(q + "/"):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
DIRECTOR_GET = [
|
||||||
|
"/months",
|
||||||
|
"/units",
|
||||||
|
"/jira/tickets-overview",
|
||||||
|
"/scorecard*",
|
||||||
|
"/overview/nac",
|
||||||
|
"/overview/compliance",
|
||||||
|
"/months/*/compliance",
|
||||||
|
"/overview/smart-ir",
|
||||||
|
"/pentest/get-overview",
|
||||||
|
"/pentest/vuln-lastest-12month",
|
||||||
|
"/pentest/category",
|
||||||
|
"/pentest/category-vuln",
|
||||||
|
"/pentest/get-top-vulns",
|
||||||
|
"/pentest/get-target-detail",
|
||||||
|
"/pentest/download-target-checklist",
|
||||||
|
"/months/*/files",
|
||||||
|
"/files/download",
|
||||||
|
"/jira/tickets-week",
|
||||||
|
"/jira/tickets-12-month-lastest",
|
||||||
|
"/months/*/root-access",
|
||||||
|
"/months/*/attack-statics",
|
||||||
|
"/months/*/logsource",
|
||||||
|
"/months/*/nac-smartir",
|
||||||
|
"/months/*/compliance",
|
||||||
|
"/posts",
|
||||||
|
"/categories",
|
||||||
|
"/documents",
|
||||||
|
"/documents/download",
|
||||||
|
"/months/*/pam-tsc",
|
||||||
|
"/hardening/*",
|
||||||
|
"/pentest/log_history/*",
|
||||||
|
"/pentest-service*",
|
||||||
|
"/logsource/*/comments",
|
||||||
|
]
|
||||||
|
DIRECTOR_POST = [
|
||||||
|
"/auth/login",
|
||||||
|
"/auth/logout",
|
||||||
|
"/pentest-service*",
|
||||||
|
]
|
||||||
|
|
||||||
|
MANAGER_GET = DIRECTOR_GET + [
|
||||||
|
"/pentest/update-vuln-status",
|
||||||
|
]
|
||||||
|
MANAGER_POST = DIRECTOR_POST
|
||||||
|
|
||||||
|
LEADER_GET = MANAGER_GET
|
||||||
|
LEADER_POST = MANAGER_POST
|
||||||
|
|
||||||
|
def enforce_rbac(request: Request, user: dict = Depends(get_current_user)):
|
||||||
|
method, path = _method_path(request)
|
||||||
|
|
||||||
|
# Cho phép tất cả các nhóm người dùng đã đăng nhập truy cập vào /pentest-service
|
||||||
|
if "pentest-service" in path:
|
||||||
|
return
|
||||||
|
|
||||||
|
rid = _role_id(user)
|
||||||
|
if rid == ROLE_ADMIN:
|
||||||
|
return
|
||||||
|
if rid == ROLE_ADMIN_LIMITED:
|
||||||
|
deny_get = [
|
||||||
|
# "/pentest/category-vuln",
|
||||||
|
"/pentest/get-target-detail",
|
||||||
|
"/pentest/download-target-checklist",
|
||||||
|
]
|
||||||
|
deny_post = [
|
||||||
|
"/users",
|
||||||
|
"/roles",
|
||||||
|
]
|
||||||
|
deny_put = [
|
||||||
|
"/users/*",
|
||||||
|
"/roles/*",
|
||||||
|
]
|
||||||
|
deny_delete = [
|
||||||
|
"/users/*",
|
||||||
|
"/roles/*",
|
||||||
|
]
|
||||||
|
if method == "GET" and _path_match(path, deny_get):
|
||||||
|
raise HTTPException(status_code=403, detail="Forbidden")
|
||||||
|
if method == "POST" and _path_match(path, deny_post):
|
||||||
|
raise HTTPException(status_code=403, detail="Forbidden")
|
||||||
|
if method == "PUT" and _path_match(path, deny_put):
|
||||||
|
raise HTTPException(status_code=403, detail="Forbidden")
|
||||||
|
if method == "DELETE" and _path_match(path, deny_delete):
|
||||||
|
raise HTTPException(status_code=403, detail="Forbidden")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Cho phép các method khác GET cho pentest-service
|
||||||
|
NONGET_ALLOWED_ALL_ROLES = [
|
||||||
|
"/systems/search-leak",
|
||||||
|
"/hardening/convert/upload",
|
||||||
|
"/pentest-service*",
|
||||||
|
"/posts/*/comments", # Cho phép tất cả người dùng đã đăng nhập thêm comment
|
||||||
|
"/logsource/*/comments", # Cho phép tất cả người dùng đã đăng nhập thêm bình luận LogSource
|
||||||
|
]
|
||||||
|
|
||||||
|
if method != "GET":
|
||||||
|
if _path_match(path, NONGET_ALLOWED_ALL_ROLES):
|
||||||
|
return
|
||||||
|
raise HTTPException(status_code=403, detail="Forbidden")
|
||||||
|
|
||||||
|
if is_director(user):
|
||||||
|
allowed = DIRECTOR_GET if method == "GET" else DIRECTOR_POST
|
||||||
|
elif is_manager(user):
|
||||||
|
allowed = MANAGER_GET if method == "GET" else MANAGER_POST
|
||||||
|
elif is_leader_pm(user):
|
||||||
|
allowed = LEADER_GET if method == "GET" else LEADER_POST
|
||||||
|
else:
|
||||||
|
# Nếu không thuộc các nhóm trên nhưng là /pentest-service thì đã return ở trên
|
||||||
|
raise HTTPException(status_code=403, detail="Forbidden")
|
||||||
|
|
||||||
|
if not _path_match(path, allowed):
|
||||||
|
raise HTTPException(status_code=403, detail="Forbidden")
|
||||||
|
return
|
||||||
|
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
from passlib.context import CryptContext
|
||||||
|
import hashlib
|
||||||
|
import jwt
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from typing import Dict, Any
|
||||||
|
import os
|
||||||
|
import uuid
|
||||||
|
from pathlib import Path
|
||||||
|
import contextvars
|
||||||
|
|
||||||
|
# Password hashing
|
||||||
|
pwd_context = CryptContext(schemes=["pbkdf2_sha256"], deprecated="auto")
|
||||||
|
|
||||||
|
|
||||||
|
def hash_password(password: str) -> str:
|
||||||
|
"""Hash a plain-text password using PBKDF2-SHA256.
|
||||||
|
|
||||||
|
This supports arbitrary password lengths and avoids backend problems.
|
||||||
|
"""
|
||||||
|
return pwd_context.hash(password)
|
||||||
|
|
||||||
|
|
||||||
|
def verify_password(plain: str, hashed: str) -> bool:
|
||||||
|
return pwd_context.verify(plain, hashed)
|
||||||
|
# return True
|
||||||
|
|
||||||
|
|
||||||
|
# JWT configuration
|
||||||
|
JWT_SECRET = os.getenv("JWT_SECRET", "your-secret-key-change-this-in-production")
|
||||||
|
JWT_ALGORITHM = "HS256"
|
||||||
|
JWT_EXPIRATION_HOURS = int(os.getenv("JWT_EXPIRATION_HOURS", 24))
|
||||||
|
|
||||||
|
|
||||||
|
def create_access_token(data: Dict[str, Any], expires_delta: timedelta = None) -> str:
|
||||||
|
"""Create a JWT access token.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: Dictionary of claims to encode
|
||||||
|
expires_delta: Optional timedelta for token expiration; defaults to JWT_EXPIRATION_HOURS
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JWT token string
|
||||||
|
"""
|
||||||
|
to_encode = data.copy()
|
||||||
|
if expires_delta:
|
||||||
|
expire = datetime.utcnow() + expires_delta
|
||||||
|
else:
|
||||||
|
expire = datetime.utcnow() + timedelta(hours=JWT_EXPIRATION_HOURS)
|
||||||
|
to_encode.update({"exp": expire, "jti": uuid.uuid4().hex, "iat": datetime.utcnow()})
|
||||||
|
encoded_jwt = jwt.encode(to_encode, JWT_SECRET, algorithm=JWT_ALGORITHM)
|
||||||
|
return encoded_jwt
|
||||||
|
|
||||||
|
|
||||||
|
def decode_access_token(token: str) -> Dict[str, Any]:
|
||||||
|
"""Decode and verify a JWT access token.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
token: JWT token string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Decoded payload dictionary
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
jwt.InvalidTokenError: If token is invalid or expired
|
||||||
|
"""
|
||||||
|
return jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
|
||||||
|
|
||||||
|
def audit_log_path() -> str:
|
||||||
|
base = os.getenv("AUDIT_LOG_DIR", "audit_logs")
|
||||||
|
p = Path(base).resolve()
|
||||||
|
p.mkdir(parents=True, exist_ok=True)
|
||||||
|
now = datetime.utcnow()
|
||||||
|
month_dir = p / now.strftime("%Y%m")
|
||||||
|
month_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
return str(month_dir / f"{now.strftime('%Y-%m-%d')}.txt")
|
||||||
|
|
||||||
|
# Per-request user context for audit logging
|
||||||
|
CURRENT_USER_CTX: contextvars.ContextVar = contextvars.ContextVar("CURRENT_USER_CTX", default=None)
|
||||||
|
|
||||||
|
def set_current_user_ctx(user: dict | None) -> None:
|
||||||
|
try:
|
||||||
|
CURRENT_USER_CTX.set(user)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_current_user_ctx() -> dict | None:
|
||||||
|
try:
|
||||||
|
return CURRENT_USER_CTX.get()
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.orm import declarative_base, sessionmaker
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Create SQLite database in the app directory (mapped volume in Docker)
|
||||||
|
DB_FILE = os.path.join(os.path.dirname(__file__), "audit_data.db")
|
||||||
|
SQLALCHEMY_DATABASE_URL = f"sqlite:///{DB_FILE}"
|
||||||
|
|
||||||
|
engine = create_engine(
|
||||||
|
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
|
||||||
|
)
|
||||||
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
# Dependency
|
||||||
|
def get_db():
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
yield db
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
@@ -0,0 +1,805 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Email Notification Module for Audit Hardening Tool
|
||||||
|
Gửi email thông báo khi xử lý file hoàn tất
|
||||||
|
Hỗ trợ 2 phương thức: SMTP trực tiếp hoặc HTTP API
|
||||||
|
"""
|
||||||
|
|
||||||
|
import smtplib
|
||||||
|
import imaplib
|
||||||
|
import ssl
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import base64
|
||||||
|
import requests
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
from email.mime.base import MIMEBase
|
||||||
|
from email import encoders
|
||||||
|
from email.utils import formatdate
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import List, Optional
|
||||||
|
from pathlib import Path
|
||||||
|
from colorama import Fore
|
||||||
|
|
||||||
|
|
||||||
|
class EmailNotifier:
|
||||||
|
"""Class xử lý gửi email thông báo"""
|
||||||
|
|
||||||
|
def __init__(self, config_path: str = "email_config.json"):
|
||||||
|
"""
|
||||||
|
Khởi tạo EmailNotifier với cấu hình từ file JSON
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config_path: Đường dẫn tới file cấu hình email
|
||||||
|
"""
|
||||||
|
self.config = self._load_config(config_path)
|
||||||
|
self.enabled = self.config.get("enabled", False)
|
||||||
|
self.email_method = self.config.get("email_method", "smtp") # "smtp" hoặc "api"
|
||||||
|
|
||||||
|
def _load_config(self, config_path: str) -> dict:
|
||||||
|
"""Load cấu hình email từ file JSON"""
|
||||||
|
try:
|
||||||
|
with open(config_path, 'r', encoding='utf-8') as f:
|
||||||
|
config = json.load(f)
|
||||||
|
print(Fore.GREEN + f"[EMAIL] Configuration loaded. Enabled: {config.get('enabled', False)}" + Fore.RESET)
|
||||||
|
return config
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(Fore.YELLOW + f"[EMAIL] {config_path} not found. Email notifications disabled." + Fore.RESET)
|
||||||
|
return {"enabled": False}
|
||||||
|
except Exception as e:
|
||||||
|
print(Fore.RED + f"[EMAIL] Error loading config: {e}" + Fore.RESET)
|
||||||
|
return {"enabled": False}
|
||||||
|
|
||||||
|
def _create_email(
|
||||||
|
self,
|
||||||
|
to_emails: List[str],
|
||||||
|
subject: str,
|
||||||
|
body_html: str,
|
||||||
|
body_text: str = None,
|
||||||
|
attachments: List[str] = None
|
||||||
|
) -> MIMEMultipart:
|
||||||
|
"""
|
||||||
|
Tạo đối tượng email
|
||||||
|
|
||||||
|
Args:
|
||||||
|
to_emails: Danh sách email nhận
|
||||||
|
subject: Tiêu đề email
|
||||||
|
body_html: Nội dung HTML
|
||||||
|
body_text: Nội dung text thuần (fallback)
|
||||||
|
attachments: Danh sách file đính kèm
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
MIMEMultipart object
|
||||||
|
"""
|
||||||
|
msg = MIMEMultipart('alternative')
|
||||||
|
msg['From'] = self.config.get('sender_email', '')
|
||||||
|
msg['To'] = ', '.join(to_emails)
|
||||||
|
msg['Subject'] = subject
|
||||||
|
msg['Date'] = formatdate(localtime=True)
|
||||||
|
|
||||||
|
# Thêm phần text và HTML
|
||||||
|
if body_text:
|
||||||
|
msg.attach(MIMEText(body_text, 'plain', 'utf-8'))
|
||||||
|
msg.attach(MIMEText(body_html, 'html', 'utf-8'))
|
||||||
|
|
||||||
|
# Thêm file đính kèm nếu có
|
||||||
|
if attachments and self.config.get('send_attachment', False):
|
||||||
|
for file_path in attachments:
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
try:
|
||||||
|
with open(file_path, 'rb') as f:
|
||||||
|
part = MIMEBase('application', 'octet-stream')
|
||||||
|
part.set_payload(f.read())
|
||||||
|
encoders.encode_base64(part)
|
||||||
|
part.add_header(
|
||||||
|
'Content-Disposition',
|
||||||
|
f'attachment; filename="{os.path.basename(file_path)}"'
|
||||||
|
)
|
||||||
|
msg.attach(part)
|
||||||
|
except Exception as e:
|
||||||
|
print(Fore.YELLOW + f"[EMAIL] Cannot attach file {file_path}: {e}" + Fore.RESET)
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
|
def _file_to_base64(self, file_path: str) -> dict:
|
||||||
|
"""
|
||||||
|
Convert file thành base64 để gửi qua API
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file_path: Đường dẫn tới file
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict với filename và content_base64
|
||||||
|
"""
|
||||||
|
MAX_FILE_SIZE = 100 * 1024 * 1024 # 100MB
|
||||||
|
p = Path(file_path)
|
||||||
|
size = p.stat().st_size
|
||||||
|
if size > MAX_FILE_SIZE:
|
||||||
|
print(Fore.YELLOW + f"[EMAIL] File too large for API attachment: {p.name} ({size} bytes)" + Fore.RESET)
|
||||||
|
raise ValueError(f"{p.name} too large ({size} bytes > {MAX_FILE_SIZE} bytes)")
|
||||||
|
|
||||||
|
with p.open("rb") as f:
|
||||||
|
return {
|
||||||
|
"filename": p.name,
|
||||||
|
"content_base64": base64.b64encode(f.read()).decode()
|
||||||
|
}
|
||||||
|
|
||||||
|
def _send_via_api(
|
||||||
|
self,
|
||||||
|
to_emails: List[str],
|
||||||
|
subject: str,
|
||||||
|
html_content: str,
|
||||||
|
attachments: List[str] = None
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Gửi email qua HTTP API (giống send_mail.py)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
to_emails: Danh sách email nhận
|
||||||
|
subject: Tiêu đề email
|
||||||
|
html_content: Nội dung HTML
|
||||||
|
attachments: Danh sách đường dẫn file đính kèm
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True nếu gửi thành công
|
||||||
|
"""
|
||||||
|
api_url = self.config.get('email_api_url')
|
||||||
|
api_key = self.config.get('email_api_key')
|
||||||
|
|
||||||
|
if not api_url or not api_key:
|
||||||
|
print(Fore.RED + "[EMAIL] ✗ API URL or API Key not configured in email_config.json" + Fore.RESET)
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Chuẩn bị attachments base64
|
||||||
|
att_list = []
|
||||||
|
if attachments and self.config.get('send_attachment', False):
|
||||||
|
for file_path in attachments:
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
try:
|
||||||
|
att_list.append(self._file_to_base64(file_path))
|
||||||
|
except Exception as e:
|
||||||
|
print(Fore.YELLOW + f"[EMAIL] Cannot attach file {file_path}: {e}" + Fore.RESET)
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"api_key": api_key,
|
||||||
|
"to_emails": to_emails,
|
||||||
|
"cc_emails": [],
|
||||||
|
"subject": subject,
|
||||||
|
"html_content": html_content,
|
||||||
|
"attachments": att_list
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
print(Fore.CYAN + f"[EMAIL] Sending via API: {api_url}" + Fore.RESET)
|
||||||
|
r = requests.post(
|
||||||
|
api_url,
|
||||||
|
json=payload,
|
||||||
|
timeout=120,
|
||||||
|
verify=False
|
||||||
|
)
|
||||||
|
|
||||||
|
if not r.ok:
|
||||||
|
print(Fore.RED + f"[EMAIL] ✗ API Error: {r.status_code} - {r.text}" + Fore.RESET)
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(Fore.GREEN + f"[EMAIL] ✓ API Email sent successfully to: {', '.join(to_emails)}" + Fore.RESET)
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(Fore.RED + f"[EMAIL] ✗ API Request failed: {e}" + Fore.RESET)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _save_to_sent_folder(self, msg: MIMEMultipart) -> bool:
|
||||||
|
"""Lưu email vào thư mục Sent qua IMAP"""
|
||||||
|
if not self.config.get('save_to_sent', False):
|
||||||
|
return True
|
||||||
|
|
||||||
|
try:
|
||||||
|
ssl_context = ssl.create_default_context()
|
||||||
|
imap = imaplib.IMAP4_SSL(
|
||||||
|
host=self.config.get('imap_server', 'email.vnpt.vn'),
|
||||||
|
port=self.config.get('imap_port', 993),
|
||||||
|
ssl_context=ssl_context
|
||||||
|
)
|
||||||
|
|
||||||
|
imap.login(
|
||||||
|
self.config.get('sender_email', ''),
|
||||||
|
self.config.get('sender_password', '')
|
||||||
|
)
|
||||||
|
|
||||||
|
# Thử các tên thư mục Sent phổ biến
|
||||||
|
sent_folders = ['Sent', 'INBOX.Sent', 'Sent Items', 'Sent Messages']
|
||||||
|
|
||||||
|
for folder in sent_folders:
|
||||||
|
try:
|
||||||
|
status, _ = imap.select(folder)
|
||||||
|
if status == 'OK':
|
||||||
|
imap.append(folder, '\\Seen', None, msg.as_bytes())
|
||||||
|
print(Fore.CYAN + f"[EMAIL] Saved to {folder} folder" + Fore.RESET)
|
||||||
|
imap.logout()
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
imap.logout()
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(Fore.YELLOW + f"[EMAIL] Could not save to Sent: {e}" + Fore.RESET)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def send_processing_notification(
|
||||||
|
self,
|
||||||
|
output_files: List[str],
|
||||||
|
os_type: str,
|
||||||
|
processing_time: float = None,
|
||||||
|
recipients: List[str] = None,
|
||||||
|
base_url: str = "http://localhost:8000",
|
||||||
|
system_info_list: List[dict] = None,
|
||||||
|
attachments: List[str] = None,
|
||||||
|
is_admin: bool = False
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Gửi email thông báo khi xử lý file hoàn tất
|
||||||
|
|
||||||
|
Args:
|
||||||
|
output_files: Danh sách tên file output đã tạo
|
||||||
|
os_type: Loại hệ điều hành đã xử lý
|
||||||
|
processing_time: Thời gian xử lý (giây)
|
||||||
|
recipients: Danh sách email nhận (mặc định từ config)
|
||||||
|
base_url: URL base của ứng dụng
|
||||||
|
system_info_list: Danh sách thông tin hệ thống từ các file đã xử lý
|
||||||
|
attachments: Danh sách file đính kèm (đầy đủ đường dẫn)
|
||||||
|
is_admin: True nếu admin đăng nhập, sẽ gửi kèm file
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True nếu gửi thành công, False nếu lỗi
|
||||||
|
"""
|
||||||
|
if not self.enabled:
|
||||||
|
print(Fore.YELLOW + "[EMAIL] Email notifications are disabled" + Fore.RESET)
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Sử dụng recipients mặc định nếu không được chỉ định
|
||||||
|
to_emails = recipients or self.config.get('default_recipients', [])
|
||||||
|
|
||||||
|
if not to_emails:
|
||||||
|
print(Fore.YELLOW + "[EMAIL] No recipients specified" + Fore.RESET)
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Tạo tiêu đề
|
||||||
|
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
subject = f"[Audit Tool] Xử lý hoàn tất - {os_type.upper()} - {len(output_files)} file(s)"
|
||||||
|
|
||||||
|
# Tạo danh sách file với link download
|
||||||
|
files_html = ""
|
||||||
|
files_text = ""
|
||||||
|
for filename in output_files:
|
||||||
|
download_url = f"{base_url}/download/{filename}"
|
||||||
|
files_html += f'<li style="margin: 8px 0;"><a href="{download_url}" style="color: #1976d2; text-decoration: none;">📊 {filename}</a></li>\n'
|
||||||
|
files_text += f" - {filename}: {download_url}\n"
|
||||||
|
|
||||||
|
# Tạo nội dung thông tin hệ thống
|
||||||
|
system_info_html = ""
|
||||||
|
system_info_text = ""
|
||||||
|
if system_info_list:
|
||||||
|
for idx, system_info in enumerate(system_info_list):
|
||||||
|
if not system_info:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Tính màu compliance
|
||||||
|
compliance_pct = system_info.get('compliance_percentage', 0)
|
||||||
|
if compliance_pct >= 80:
|
||||||
|
compliance_color = "#4caf50"
|
||||||
|
elif compliance_pct >= 50:
|
||||||
|
compliance_color = "#ff9800"
|
||||||
|
else:
|
||||||
|
compliance_color = "#f44336"
|
||||||
|
|
||||||
|
# Script version badge
|
||||||
|
script_version_html = ''
|
||||||
|
if system_info.get('script_version'):
|
||||||
|
script_version_html = f'<span style="background: #e8f5e9; color: #2e7d32; padding: 4px 10px; border-radius: 15px; font-size: 11px; font-weight: 600; margin-left: 5px;">🏷️ Script v{system_info.get("script_version")}</span>'
|
||||||
|
|
||||||
|
# Mandatory/Optional stats
|
||||||
|
mandatory_passed = system_info.get('mandatory_passed', 0)
|
||||||
|
mandatory_total = system_info.get('mandatory_total', 0)
|
||||||
|
optional_passed = system_info.get('optional_passed', 0)
|
||||||
|
optional_total = system_info.get('optional_total', 0)
|
||||||
|
|
||||||
|
# Mandatory color
|
||||||
|
if mandatory_total > 0 and mandatory_passed == mandatory_total:
|
||||||
|
mandatory_color = "#4caf50"
|
||||||
|
elif mandatory_total > 0:
|
||||||
|
mandatory_color = "#ff9800"
|
||||||
|
else:
|
||||||
|
mandatory_color = "#666"
|
||||||
|
|
||||||
|
# Optional color
|
||||||
|
if optional_total > 0 and optional_passed == optional_total:
|
||||||
|
optional_color = "#4caf50"
|
||||||
|
elif optional_total > 0:
|
||||||
|
optional_color = "#ff9800"
|
||||||
|
else:
|
||||||
|
optional_color = "#666"
|
||||||
|
|
||||||
|
# Progress bar width
|
||||||
|
progress_width = int(compliance_pct * 2) # max 200px
|
||||||
|
|
||||||
|
system_info_html += f'''
|
||||||
|
<div style="background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%); border: 1px solid #e0e0e0; border-radius: 15px; padding: 25px; margin-bottom: 20px;">
|
||||||
|
<!-- Header with hostname and OS -->
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; padding-bottom: 15px; border-bottom: 2px solid #e3f2fd;">
|
||||||
|
<div>
|
||||||
|
<h3 style="color: #1565c0; margin: 0; font-size: 22px;">🖥️ {system_info.get('hostname', 'Unknown Host')}</h3>
|
||||||
|
<p style="color: #666; margin: 5px 0 0 0; font-size: 14px;">{system_info.get('os_name', 'Unknown OS')}</p>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: right;">
|
||||||
|
<span style="background: #e3f2fd; color: #1565c0; padding: 5px 12px; border-radius: 20px; font-size: 12px; font-weight: 600;">
|
||||||
|
{system_info.get('audit_time', '')}
|
||||||
|
</span>
|
||||||
|
{script_version_html}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Info Grid - 2 columns -->
|
||||||
|
<table style="width: 100%; border-collapse: collapse;">
|
||||||
|
<tr>
|
||||||
|
<!-- Left Column: Basic Info + Hardware -->
|
||||||
|
<td style="vertical-align: top; width: 50%; padding-right: 10px;">
|
||||||
|
<!-- Basic Info -->
|
||||||
|
<div style="background: #fff; border-radius: 10px; padding: 15px; border: 1px solid #e8e8e8; margin-bottom: 10px;">
|
||||||
|
<h4 style="color: #1976d2; margin: 0 0 12px 0; font-size: 12px; text-transform: uppercase; letter-spacing: 1px;">📋 Thông tin cơ bản</h4>
|
||||||
|
<table style="width: 100%; font-size: 12px;">
|
||||||
|
{f'<tr><td style="color: #666; padding: 4px 0;">IP Address:</td><td style="text-align: right;"><strong>{system_info.get("ip_address", "N/A")}</strong></td></tr>' if system_info.get('ip_address') else ''}
|
||||||
|
{f'<tr><td style="color: #666; padding: 4px 0;">Kernel:</td><td style="text-align: right; font-size: 10px;"><strong>{system_info.get("kernel_version", "N/A")}</strong></td></tr>' if system_info.get('kernel_version') else ''}
|
||||||
|
{f'<tr><td style="color: #666; padding: 4px 0;">Architecture:</td><td style="text-align: right;"><strong>{system_info.get("architecture", "N/A")}</strong></td></tr>' if system_info.get('architecture') else ''}
|
||||||
|
{f'<tr><td style="color: #666; padding: 4px 0;">Uptime:</td><td style="text-align: right;"><strong>{system_info.get("uptime", "N/A")}</strong></td></tr>' if system_info.get('uptime') else ''}
|
||||||
|
{f'<tr><td style="color: #666; padding: 4px 0;">Timezone:</td><td style="text-align: right;"><strong>{system_info.get("timezone", "N/A")}</strong></td></tr>' if system_info.get('timezone') else ''}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Hardware Info -->
|
||||||
|
<div style="background: #fff; border-radius: 10px; padding: 15px; border: 1px solid #e8e8e8;">
|
||||||
|
<h4 style="color: #4caf50; margin: 0 0 12px 0; font-size: 12px; text-transform: uppercase; letter-spacing: 1px;">⚙️ Phần cứng</h4>
|
||||||
|
<table style="width: 100%; font-size: 12px;">
|
||||||
|
{f'<tr><td style="color: #666; padding: 4px 0;">CPU:</td><td style="text-align: right; font-size: 10px;"><strong>{system_info.get("cpu_model", "N/A")}</strong></td></tr>' if system_info.get('cpu_model') else ''}
|
||||||
|
{f'<tr><td style="color: #666; padding: 4px 0;">CPU Cores:</td><td style="text-align: right;"><strong>{system_info.get("cpu_cores", "N/A")}</strong></td></tr>' if system_info.get('cpu_cores') else ''}
|
||||||
|
{f'<tr><td style="color: #666; padding: 4px 0;">Total Memory:</td><td style="text-align: right;"><strong>{system_info.get("total_memory", "N/A")}</strong></td></tr>' if system_info.get('total_memory') else ''}
|
||||||
|
{f'<tr><td style="color: #666; padding: 4px 0;">Used Memory:</td><td style="text-align: right;"><strong>{system_info.get("used_memory", "N/A")}</strong></td></tr>' if system_info.get('used_memory') else ''}
|
||||||
|
{f'<tr><td style="color: #666; padding: 4px 0;">Free Memory:</td><td style="text-align: right;"><strong>{system_info.get("free_memory", "N/A")}</strong></td></tr>' if system_info.get('free_memory') else ''}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Right Column: Compliance + Network -->
|
||||||
|
<td style="vertical-align: top; width: 50%; padding-left: 10px;">
|
||||||
|
<!-- Compliance Stats -->
|
||||||
|
<div style="background: #fff; border-radius: 10px; padding: 15px; border: 1px solid #e8e8e8; text-align: center;">
|
||||||
|
<h4 style="color: #e91e63; margin: 0 0 15px 0; font-size: 12px; text-transform: uppercase; letter-spacing: 1px;">📊 Tỉ lệ tuân thủ</h4>
|
||||||
|
|
||||||
|
<!-- Compliance Percentage with progress bar -->
|
||||||
|
<div style="font-size: 36px; font-weight: bold; color: {compliance_color}; margin: 10px 0;">
|
||||||
|
{compliance_pct}%
|
||||||
|
</div>
|
||||||
|
<div style="background: #e0e0e0; border-radius: 10px; height: 10px; width: 100%; margin: 8px 0 15px 0;">
|
||||||
|
<div style="background: {compliance_color}; border-radius: 10px; height: 10px; width: {compliance_pct}%;"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mandatory / Non-Mandatory Stats -->
|
||||||
|
<table style="width: 100%; margin-top: 10px;">
|
||||||
|
<tr>
|
||||||
|
<td style="text-align: center; padding: 10px 8px; background: #e3f2fd; border-radius: 8px; width: 45%; border-left: 3px solid #1565c0;">
|
||||||
|
<div style="font-size: 10px; color: #1565c0; font-weight: 600; text-transform: uppercase; margin-bottom: 5px;">🔒 Bắt buộc đạt</div>
|
||||||
|
<div style="font-size: 20px; font-weight: bold; color: {mandatory_color};">{mandatory_passed}/{mandatory_total}</div>
|
||||||
|
{f'<div style="font-size: 11px; color: #999; margin-top: 4px;">{round((mandatory_passed / mandatory_total) * 100, 1) if mandatory_total > 0 else 0}%</div>' if mandatory_total > 0 else ''}
|
||||||
|
</td>
|
||||||
|
<td style="width: 10%;"></td>
|
||||||
|
<td style="text-align: center; padding: 10px 8px; background: #f3e5f5; border-radius: 8px; width: 45%; border-left: 3px solid #7b1fa2;">
|
||||||
|
<div style="font-size: 10px; color: #7b1fa2; font-weight: 600; text-transform: uppercase; margin-bottom: 5px;">📋 Tuỳ chọn đạt</div>
|
||||||
|
<div style="font-size: 20px; font-weight: bold; color: {optional_color};">{optional_passed}/{optional_total}</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- Total checks -->
|
||||||
|
<div style="margin-top: 10px; color: #666; font-size: 12px;">
|
||||||
|
Tổng số: <strong>{system_info.get('total_checks', 0)}</strong> tiêu chí
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Network Info -->
|
||||||
|
<div style="background: #fff; border-radius: 10px; padding: 15px; border: 1px solid #e8e8e8; margin-top: 10px;">
|
||||||
|
<h4 style="color: #ff9800; margin: 0 0 12px 0; font-size: 12px; text-transform: uppercase; letter-spacing: 1px;">🌐 Mạng</h4>
|
||||||
|
<table style="width: 100%; font-size: 12px;">
|
||||||
|
{f'<tr><td style="color: #666; padding: 4px 0;">Interface:</td><td style="text-align: right;"><strong>{system_info.get("primary_interface", "N/A")}</strong></td></tr>' if system_info.get('primary_interface') else ''}
|
||||||
|
{f'<tr><td style="color: #666; padding: 4px 0;">MAC:</td><td style="text-align: right; font-size: 10px;"><strong>{system_info.get("mac_address", "N/A")}</strong></td></tr>' if system_info.get('mac_address') else ''}
|
||||||
|
{f'<tr><td style="color: #666; padding: 4px 0;">Gateway:</td><td style="text-align: right;"><strong>{system_info.get("default_gateway", "N/A")}</strong></td></tr>' if system_info.get('default_gateway') else ''}
|
||||||
|
{f'<tr><td style="color: #666; padding: 4px 0;">DNS:</td><td style="text-align: right;"><strong>{system_info.get("dns_servers", "N/A")}</strong></td></tr>' if system_info.get('dns_servers') else ''}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
'''
|
||||||
|
# Text version
|
||||||
|
script_ver_text = f" (Script v{system_info.get('script_version')})" if system_info.get('script_version') else ''
|
||||||
|
mandatory_pct_text = round((mandatory_passed / mandatory_total) * 100, 1) if mandatory_total > 0 else 0
|
||||||
|
system_info_text += f'''
|
||||||
|
--- Hệ thống {idx + 1}: {system_info.get('hostname', 'Unknown')}{script_ver_text} ---
|
||||||
|
OS: {system_info.get('os_name', 'N/A')}
|
||||||
|
IP: {system_info.get('ip_address', 'N/A')}
|
||||||
|
Kernel: {system_info.get('kernel_version', 'N/A')}
|
||||||
|
Architecture: {system_info.get('architecture', 'N/A')}
|
||||||
|
Uptime: {system_info.get('uptime', 'N/A')}
|
||||||
|
Timezone: {system_info.get('timezone', 'N/A')}
|
||||||
|
CPU: {system_info.get('cpu_model', 'N/A')} ({system_info.get('cpu_cores', 'N/A')} cores)
|
||||||
|
Memory: {system_info.get('total_memory', 'N/A')} (Used: {system_info.get('used_memory', 'N/A')}, Free: {system_info.get('free_memory', 'N/A')})
|
||||||
|
Interface: {system_info.get('primary_interface', 'N/A')} | MAC: {system_info.get('mac_address', 'N/A')}
|
||||||
|
Gateway: {system_info.get('default_gateway', 'N/A')} | DNS: {system_info.get('dns_servers', 'N/A')}
|
||||||
|
|
||||||
|
📊 Tỉ lệ tuân thủ: {compliance_pct}%
|
||||||
|
- 🔒 Bắt buộc đạt: {mandatory_passed}/{mandatory_total} ({mandatory_pct_text}%)
|
||||||
|
- 📋 Tuỳ chọn đạt: {optional_passed}/{optional_total}
|
||||||
|
- Tổng số: {system_info.get('total_checks', 0)} tiêu chí
|
||||||
|
'''
|
||||||
|
|
||||||
|
# ===== Tạo BẢNG TỔNG HỢP KẾT QUẢ (giống web) =====
|
||||||
|
summary_table_html = ""
|
||||||
|
if system_info_list and len([s for s in system_info_list if s]) > 0:
|
||||||
|
valid_infos = [s for s in system_info_list if s]
|
||||||
|
|
||||||
|
# Build summary table rows
|
||||||
|
summary_rows = ""
|
||||||
|
for idx_s, si in enumerate(valid_infos):
|
||||||
|
s_compliance = si.get('compliance_percentage', 0)
|
||||||
|
s_mandatory_passed = si.get('mandatory_passed', 0)
|
||||||
|
s_mandatory_total = si.get('mandatory_total', 0)
|
||||||
|
s_mandatory_failed = si.get('mandatory_failed', 0)
|
||||||
|
s_mandatory_pct = round((s_mandatory_passed / s_mandatory_total) * 100, 1) if s_mandatory_total > 0 else 0
|
||||||
|
|
||||||
|
# Compliance color
|
||||||
|
if s_compliance >= 80:
|
||||||
|
s_pct_bg = "#e8f5e9"; s_pct_color = "#2e7d32"
|
||||||
|
elif s_compliance >= 50:
|
||||||
|
s_pct_bg = "#fff3e0"; s_pct_color = "#e65100"
|
||||||
|
else:
|
||||||
|
s_pct_bg = "#ffebee"; s_pct_color = "#c62828"
|
||||||
|
|
||||||
|
# Mandatory color
|
||||||
|
if s_mandatory_failed == 0 and s_mandatory_total > 0:
|
||||||
|
s_mand_bg = "#e8f5e9"; s_mand_color = "#2e7d32"
|
||||||
|
elif s_mandatory_total > 0:
|
||||||
|
s_mand_bg = "#ffebee"; s_mand_color = "#c62828"
|
||||||
|
else:
|
||||||
|
s_mand_bg = "#f5f5f5"; s_mand_color = "#999"
|
||||||
|
|
||||||
|
# Note
|
||||||
|
if s_mandatory_failed > 0 and s_compliance < 80:
|
||||||
|
note_html = f'''
|
||||||
|
<div style="padding: 6px 10px; background: #ffebee; border-left: 3px solid #c62828; border-radius: 4px; color: #c62828; font-size: 11px; margin-bottom: 4px;">
|
||||||
|
❌ Còn <strong>{s_mandatory_failed}</strong> tiêu chí bắt buộc chưa đạt
|
||||||
|
</div>
|
||||||
|
<div style="padding: 6px 10px; background: #fff8e1; border-left: 3px solid #ffa000; border-radius: 4px; color: #e65100; font-size: 11px;">
|
||||||
|
⚠️ Tỉ lệ đạt chỉ {s_compliance}% (yêu cầu ≥80%)
|
||||||
|
</div>'''
|
||||||
|
elif s_mandatory_failed > 0:
|
||||||
|
note_html = f'''
|
||||||
|
<div style="padding: 6px 10px; background: #fff8e1; border-left: 3px solid #ffa000; border-radius: 4px; color: #e65100; font-size: 11px;">
|
||||||
|
⚠️ Còn <strong style="color: #c62828;">{s_mandatory_failed}</strong> tiêu chí bắt buộc chưa đạt
|
||||||
|
</div>'''
|
||||||
|
elif s_compliance < 80:
|
||||||
|
note_html = f'''
|
||||||
|
<div style="padding: 6px 10px; background: #fff8e1; border-left: 3px solid #ffa000; border-radius: 4px; color: #e65100; font-size: 11px;">
|
||||||
|
⚠️ Tỉ lệ đạt chỉ {s_compliance}% (yêu cầu ≥80%)
|
||||||
|
</div>'''
|
||||||
|
elif s_mandatory_total > 0:
|
||||||
|
note_html = '''
|
||||||
|
<div style="padding: 6px 10px; background: #e8f5e9; border-left: 3px solid #4caf50; border-radius: 4px; color: #2e7d32; font-size: 11px;">
|
||||||
|
✅ Đạt yêu cầu
|
||||||
|
</div>'''
|
||||||
|
else:
|
||||||
|
note_html = '<span style="color: #bbb; font-style: italic; font-size: 11px;">Không có dữ liệu</span>'
|
||||||
|
|
||||||
|
row_bg = "#fafbfc" if idx_s % 2 == 0 else "#ffffff"
|
||||||
|
|
||||||
|
summary_rows += f'''
|
||||||
|
<tr style="background: {row_bg};">
|
||||||
|
<td style="padding: 12px 14px; text-align: center; color: #666; font-weight: 500; border-bottom: 1px solid #f0f0f0;">{idx_s + 1}</td>
|
||||||
|
<td style="padding: 12px 14px; border-bottom: 1px solid #f0f0f0;">
|
||||||
|
<div style="font-weight: 600; color: #1565c0;">{si.get('hostname', 'N/A')}</div>
|
||||||
|
<div style="font-size: 11px; color: #999; margin-top: 2px;">{si.get('os_name', '')}</div>
|
||||||
|
</td>
|
||||||
|
<td style="padding: 12px 14px; color: #333; font-family: Consolas, monospace; font-size: 13px; border-bottom: 1px solid #f0f0f0;">{si.get('ip_address', 'N/A')}</td>
|
||||||
|
<td style="padding: 12px 14px; text-align: center; border-bottom: 1px solid #f0f0f0;">
|
||||||
|
<span style="display: inline-block; padding: 4px 12px; border-radius: 15px; font-weight: 700; font-size: 13px; background: {s_pct_bg}; color: {s_pct_color};">{s_compliance}%</span>
|
||||||
|
<div style="font-size: 10px; color: #999; margin-top: 3px;">{si.get('passed_count', 0)}/{si.get('total_checks', 0)} tiêu chí</div>
|
||||||
|
</td>
|
||||||
|
<td style="padding: 12px 14px; text-align: center; border-bottom: 1px solid #f0f0f0;">
|
||||||
|
<span style="display: inline-block; padding: 4px 12px; border-radius: 15px; font-weight: 700; font-size: 13px; background: {s_mand_bg}; color: {s_mand_color};">{s_mandatory_passed}/{s_mandatory_total}</span>
|
||||||
|
{f'<div style="font-size: 10px; color: #999; margin-top: 3px;">{s_mandatory_pct}%</div>' if s_mandatory_total > 0 else ''}
|
||||||
|
</td>
|
||||||
|
<td style="padding: 12px 14px; border-bottom: 1px solid #f0f0f0;">{note_html}</td>
|
||||||
|
</tr>'''
|
||||||
|
|
||||||
|
# Summary footer row (if multiple servers)
|
||||||
|
summary_footer = ""
|
||||||
|
if len(valid_infos) > 1:
|
||||||
|
total_passed_all = sum(s.get('passed_count', 0) for s in valid_infos)
|
||||||
|
total_checks_all = sum(s.get('total_checks', 0) for s in valid_infos)
|
||||||
|
avg_pct = round((total_passed_all / total_checks_all) * 100, 1) if total_checks_all > 0 else 0
|
||||||
|
total_mp = sum(s.get('mandatory_passed', 0) for s in valid_infos)
|
||||||
|
total_mt = sum(s.get('mandatory_total', 0) for s in valid_infos)
|
||||||
|
total_mf = sum(s.get('mandatory_failed', 0) for s in valid_infos)
|
||||||
|
|
||||||
|
if avg_pct >= 80:
|
||||||
|
avg_bg = "#e8f5e9"; avg_color = "#2e7d32"
|
||||||
|
elif avg_pct >= 50:
|
||||||
|
avg_bg = "#fff3e0"; avg_color = "#e65100"
|
||||||
|
else:
|
||||||
|
avg_bg = "#ffebee"; avg_color = "#c62828"
|
||||||
|
|
||||||
|
footer_note = f'<span style="color: #c62828; font-weight: 600;">⚠️ Tổng {total_mf} tiêu chí bắt buộc chưa đạt</span>' if total_mf > 0 else '<span style="color: #2e7d32; font-weight: 600;">✅ Tất cả đạt</span>'
|
||||||
|
|
||||||
|
summary_footer = f'''
|
||||||
|
<tr style="background: linear-gradient(135deg, #f5f5f5, #eeeeee); font-weight: 600;">
|
||||||
|
<td colspan="3" style="padding: 14px 16px; text-align: right; color: #555; font-size: 13px; border-top: 2px solid #e0e0e0;">
|
||||||
|
📈 Tổng hợp ({len(valid_infos)} máy chủ):
|
||||||
|
</td>
|
||||||
|
<td style="padding: 14px 16px; text-align: center; border-top: 2px solid #e0e0e0;">
|
||||||
|
<span style="display: inline-block; padding: 4px 12px; border-radius: 15px; font-weight: 700; font-size: 13px; background: {avg_bg}; color: {avg_color};">{avg_pct}%</span>
|
||||||
|
</td>
|
||||||
|
<td style="padding: 14px 16px; text-align: center; border-top: 2px solid #e0e0e0;">
|
||||||
|
<span style="font-size: 13px; color: #333;">{total_mp}/{total_mt}</span>
|
||||||
|
</td>
|
||||||
|
<td style="padding: 14px 16px; border-top: 2px solid #e0e0e0;">{footer_note}</td>
|
||||||
|
</tr>'''
|
||||||
|
|
||||||
|
summary_table_html = f'''
|
||||||
|
<h3 style="color: #1976d2; margin: 25px 0 15px 0; font-size: 18px;">📋 Bảng tổng hợp kết quả</h3>
|
||||||
|
<div style="overflow-x: auto; border-radius: 10px; border: 1px solid #e0e0e0;">
|
||||||
|
<table style="width: 100%; border-collapse: separate; border-spacing: 0; font-size: 13px; border-radius: 10px; overflow: hidden;">
|
||||||
|
<thead>
|
||||||
|
<tr style="background: linear-gradient(135deg, #1565c0 0%, #1976d2 100%);">
|
||||||
|
<th style="padding: 12px 14px; color: white; font-weight: 600; text-align: center; font-size: 12px; letter-spacing: 0.5px; white-space: nowrap;">STT</th>
|
||||||
|
<th style="padding: 12px 14px; color: white; font-weight: 600; text-align: left; font-size: 12px; letter-spacing: 0.5px; white-space: nowrap;">🖥️ Tên máy chủ</th>
|
||||||
|
<th style="padding: 12px 14px; color: white; font-weight: 600; text-align: left; font-size: 12px; letter-spacing: 0.5px; white-space: nowrap;">🌐 Địa chỉ IP</th>
|
||||||
|
<th style="padding: 12px 14px; color: white; font-weight: 600; text-align: center; font-size: 12px; letter-spacing: 0.5px; white-space: nowrap;">📊 Tỉ lệ đạt</th>
|
||||||
|
<th style="padding: 12px 14px; color: white; font-weight: 600; text-align: center; font-size: 12px; letter-spacing: 0.5px; white-space: nowrap;">🔒 Bắt buộc đạt</th>
|
||||||
|
<th style="padding: 12px 14px; color: white; font-weight: 600; text-align: left; font-size: 12px; letter-spacing: 0.5px; min-width: 180px;">📝 Ghi chú</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{summary_rows}
|
||||||
|
</tbody>
|
||||||
|
{f"<tfoot>{summary_footer}</tfoot>" if summary_footer else ""}
|
||||||
|
</table>
|
||||||
|
</div>'''
|
||||||
|
|
||||||
|
# Tạo nội dung HTML với màu tối hơn cho chế độ sáng
|
||||||
|
body_html = f"""
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<style>
|
||||||
|
body {{ font-family: 'Segoe UI', Arial, sans-serif; line-height: 1.6; color: #333333; margin: 0; padding: 0; background: #f5f5f5; }}
|
||||||
|
.container {{ max-width: 800px; margin: 0 auto; padding: 20px; }}
|
||||||
|
.header {{ background-color: #1565c0; color: #ffffff; padding: 25px; border-radius: 12px 12px 0 0; text-align: center; }}
|
||||||
|
.content {{ background: #ffffff; padding: 25px; border: 1px solid #e0e0e0; color: #333333; }}
|
||||||
|
.footer {{ background: #f8f9fa; padding: 15px; text-align: center; font-size: 12px; color: #555555; border-radius: 0 0 12px 12px; border: 1px solid #e0e0e0; border-top: none; }}
|
||||||
|
.success {{ color: #1b5e20; background: #d4edda; padding: 15px; border-radius: 8px; border-left: 4px solid #28a745; margin-bottom: 20px; }}
|
||||||
|
.info-table {{ width: 100%; border-collapse: collapse; margin: 15px 0; }}
|
||||||
|
.info-table td {{ padding: 10px 12px; border-bottom: 1px solid #eee; color: #333333; }}
|
||||||
|
.info-table td:first-child {{ font-weight: bold; width: 40%; color: #444444; }}
|
||||||
|
ul {{ margin: 10px 0; padding-left: 20px; list-style: none; }}
|
||||||
|
a {{ color: #1565c0; text-decoration: none; }}
|
||||||
|
a:hover {{ text-decoration: underline; }}
|
||||||
|
.note {{ background: #e7f3ff; border-left: 4px solid #2196F3; padding: 12px 15px; margin: 20px 0; border-radius: 5px; font-size: 13px; color: #333333; }}
|
||||||
|
h3 {{ color: #1565c0; }}
|
||||||
|
p {{ color: #333333; }}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="color: #333333;">
|
||||||
|
<div class="container">
|
||||||
|
<div class="header" style="background-color: #1565c0;">
|
||||||
|
<h2 style="margin: 0; font-size: 24px; color: #ffffff !important;">🔒 Audit Hardening Tool</h2>
|
||||||
|
<p style="margin: 8px 0 0 0; font-size: 14px; color: #ffffff !important;">Thông báo xử lý hoàn tất</p>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<div class="success">
|
||||||
|
<strong>✅ Xử lý file audit đã hoàn tất thành công!</strong>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="info-table">
|
||||||
|
<tr><td>Hệ điều hành:</td><td>{os_type.upper()}</td></tr>
|
||||||
|
<tr><td>Số file đã tạo:</td><td>{len(output_files)} file(s)</td></tr>
|
||||||
|
<tr><td>Thời gian xử lý:</td><td>{processing_time:.2f} giây</td></tr>
|
||||||
|
<tr><td>Thời điểm:</td><td>{timestamp}</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{summary_table_html}
|
||||||
|
|
||||||
|
{'<h3 style="color: #1976d2; margin: 25px 0 15px 0; font-size: 18px;">🖥️ Thông tin chi tiết hệ thống</h3>' + system_info_html if system_info_html else ''}
|
||||||
|
|
||||||
|
<h3 style="color: #1976d2; margin: 25px 0 15px 0; font-size: 18px;">📄 Danh sách file kết quả</h3>
|
||||||
|
<ul>
|
||||||
|
{files_html}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p style="margin-top: 25px; text-align: center;">
|
||||||
|
<a href="{base_url}/my_files" style="background: linear-gradient(135deg, #1976d2 0%, #1565c0 100%); color: white; padding: 12px 25px; border-radius: 8px; display: inline-block; font-weight: 600; text-decoration: none;">
|
||||||
|
📁 Xem tất cả file của tôi
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<p style="margin: 0;">Email này được gửi tự động từ Audit Hardening Tool</p>
|
||||||
|
<p style="margin: 5px 0 0 0;">VNPT-MEDIA - An Toàn Thông Tin</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Tạo nội dung text thuần
|
||||||
|
body_text = f"""
|
||||||
|
Audit Hardening Tool - Thông báo xử lý hoàn tất
|
||||||
|
===============================================
|
||||||
|
|
||||||
|
✅ Xử lý file audit đã hoàn tất thành công!
|
||||||
|
|
||||||
|
Thông tin:
|
||||||
|
- Hệ điều hành: {os_type.upper()}
|
||||||
|
- Số file đã tạo: {len(output_files)} file(s)
|
||||||
|
- Thời gian xử lý: {processing_time:.2f} giây
|
||||||
|
- Thời điểm: {timestamp}
|
||||||
|
{system_info_text if system_info_text else ''}
|
||||||
|
Danh sách file kết quả:
|
||||||
|
{files_text}
|
||||||
|
|
||||||
|
|
||||||
|
Truy cập {base_url}/my_files để xem tất cả file của bạn.
|
||||||
|
|
||||||
|
---
|
||||||
|
Email này được gửi tự động từ Audit Hardening Tool
|
||||||
|
VNPT-MEDIA - An Toàn Thông Tin
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Tạo email với file đính kèm nếu là admin
|
||||||
|
email_attachments = None
|
||||||
|
if is_admin and attachments:
|
||||||
|
email_attachments = attachments
|
||||||
|
print(Fore.CYAN + f"[EMAIL] Admin user - attaching {len(attachments)} file(s)" + Fore.RESET)
|
||||||
|
|
||||||
|
# ========== Chọn phương thức gửi email ==========
|
||||||
|
if self.email_method == 'api':
|
||||||
|
# Gửi qua HTTP API
|
||||||
|
return self._send_via_api(
|
||||||
|
to_emails=to_emails,
|
||||||
|
subject=subject,
|
||||||
|
html_content=body_html,
|
||||||
|
attachments=email_attachments
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Gửi qua SMTP (phương thức cũ)
|
||||||
|
msg = self._create_email(
|
||||||
|
to_emails=to_emails,
|
||||||
|
subject=subject,
|
||||||
|
body_html=body_html,
|
||||||
|
body_text=body_text,
|
||||||
|
attachments=email_attachments
|
||||||
|
)
|
||||||
|
|
||||||
|
print(Fore.CYAN + f"[EMAIL] Connecting to SMTP server: {self.config.get('smtp_server')}:{self.config.get('smtp_port')}..." + Fore.RESET)
|
||||||
|
|
||||||
|
smtp = smtplib.SMTP(
|
||||||
|
self.config.get('smtp_server', 'email.vnpt.vn'),
|
||||||
|
self.config.get('smtp_port', 587),
|
||||||
|
timeout=self.config.get('timeout', 30)
|
||||||
|
)
|
||||||
|
smtp.ehlo()
|
||||||
|
smtp.starttls()
|
||||||
|
smtp.ehlo()
|
||||||
|
|
||||||
|
smtp.login(
|
||||||
|
self.config.get('sender_email', ''),
|
||||||
|
self.config.get('sender_password', '')
|
||||||
|
)
|
||||||
|
|
||||||
|
smtp.sendmail(
|
||||||
|
self.config.get('sender_email', ''),
|
||||||
|
to_emails,
|
||||||
|
msg.as_string()
|
||||||
|
)
|
||||||
|
|
||||||
|
smtp.quit()
|
||||||
|
|
||||||
|
print(Fore.GREEN + f"[EMAIL] ✓ Notification sent to: {', '.join(to_emails)}" + Fore.RESET)
|
||||||
|
|
||||||
|
# Lưu vào thư mục Sent
|
||||||
|
self._save_to_sent_folder(msg)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except smtplib.SMTPException as e:
|
||||||
|
print(Fore.RED + f"[EMAIL] ✗ SMTP Error: {e}" + Fore.RESET)
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
print(Fore.RED + f"[EMAIL] ✗ Error sending email: {e}" + Fore.RESET)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# Global instance
|
||||||
|
_email_notifier = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_email_notifier() -> EmailNotifier:
|
||||||
|
"""Lấy instance EmailNotifier (singleton pattern)"""
|
||||||
|
global _email_notifier
|
||||||
|
if _email_notifier is None:
|
||||||
|
_email_notifier = EmailNotifier()
|
||||||
|
return _email_notifier
|
||||||
|
|
||||||
|
|
||||||
|
def send_processing_complete_email(
|
||||||
|
output_files: List[str],
|
||||||
|
os_type: str,
|
||||||
|
processing_time: float = 0,
|
||||||
|
recipients: List[str] = None,
|
||||||
|
base_url: str = "http://localhost:8000",
|
||||||
|
system_info_list: List[dict] = None,
|
||||||
|
attachments: List[str] = None,
|
||||||
|
is_admin: bool = False
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Hàm tiện ích để gửi email thông báo xử lý hoàn tất
|
||||||
|
|
||||||
|
Args:
|
||||||
|
output_files: Danh sách tên file output
|
||||||
|
os_type: Loại OS đã xử lý
|
||||||
|
processing_time: Thời gian xử lý (giây)
|
||||||
|
recipients: Email nhận (None = dùng mặc định)
|
||||||
|
base_url: URL của ứng dụng
|
||||||
|
system_info_list: Danh sách thông tin hệ thống từ các file đã xử lý
|
||||||
|
attachments: Danh sách đường dẫn file đính kèm
|
||||||
|
is_admin: True nếu admin đăng nhập
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True nếu thành công
|
||||||
|
"""
|
||||||
|
notifier = get_email_notifier()
|
||||||
|
return notifier.send_processing_notification(
|
||||||
|
output_files=output_files,
|
||||||
|
os_type=os_type,
|
||||||
|
processing_time=processing_time,
|
||||||
|
recipients=recipients,
|
||||||
|
base_url=base_url,
|
||||||
|
system_info_list=system_info_list,
|
||||||
|
attachments=attachments,
|
||||||
|
is_admin=is_admin
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Test gửi email
|
||||||
|
print("=" * 50)
|
||||||
|
print(" Test Email Notification")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
success = send_processing_complete_email(
|
||||||
|
output_files=["Test_Result_2025-02-03.xlsx"],
|
||||||
|
os_type="centos",
|
||||||
|
processing_time=5.23,
|
||||||
|
base_url="http://localhost:8000"
|
||||||
|
)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
print("\n✅ Email sent successfully!")
|
||||||
|
else:
|
||||||
|
print("\n❌ Failed to send email")
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,59 @@
|
|||||||
|
from sqlalchemy import Column, Integer, String, Float, Boolean, DateTime
|
||||||
|
from datetime import datetime
|
||||||
|
from database import Base
|
||||||
|
|
||||||
|
class AuditRecord(Base):
|
||||||
|
__tablename__ = "audit_records"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
filename = Column(String, index=True, unique=True)
|
||||||
|
file_size = Column(Integer)
|
||||||
|
file_date = Column(String) # Stored as formatted string for simplicity: '%d/%m/%Y %H:%M'
|
||||||
|
|
||||||
|
hostname = Column(String, index=True)
|
||||||
|
ip_address = Column(String, index=True)
|
||||||
|
os_name = Column(String, index=True)
|
||||||
|
|
||||||
|
compliance_percentage = Column(Float, default=0.0)
|
||||||
|
passed_count = Column(Integer, default=0)
|
||||||
|
failed_count = Column(Integer, default=0)
|
||||||
|
total_checks = Column(Integer, default=0)
|
||||||
|
|
||||||
|
mandatory_passed = Column(Integer, default=0)
|
||||||
|
mandatory_failed = Column(Integer, default=0)
|
||||||
|
mandatory_total = Column(Integer, default=0)
|
||||||
|
mandatory_percentage = Column(Float, default=0.0)
|
||||||
|
|
||||||
|
optional_passed = Column(Integer, default=0)
|
||||||
|
optional_failed = Column(Integer, default=0)
|
||||||
|
optional_total = Column(Integer, default=0)
|
||||||
|
|
||||||
|
has_txt = Column(Boolean, default=False)
|
||||||
|
txt_filename = Column(String, default="")
|
||||||
|
|
||||||
|
created_at = Column(DateTime, default=datetime.utcnow)
|
||||||
|
|
||||||
|
|
||||||
|
class UserEmailRecord(Base):
|
||||||
|
"""Lưu trữ email người dùng (@vnpt.vn) và thống kê số file đã upload."""
|
||||||
|
__tablename__ = "user_email_records"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
email = Column(String, index=True, unique=True) # email @vnpt.vn (unique per user)
|
||||||
|
display_name = Column(String, default="") # phần trước @, dùng để hiển thị
|
||||||
|
total_uploads = Column(Integer, default=0) # tổng số lần upload (batch)
|
||||||
|
total_files = Column(Integer, default=0) # tổng số file đã xử lý
|
||||||
|
first_seen = Column(DateTime, default=datetime.utcnow)
|
||||||
|
last_seen = Column(DateTime, default=datetime.utcnow)
|
||||||
|
|
||||||
|
class UserFileRecord(Base):
|
||||||
|
"""Lưu vết các file thuộc quyền sở hữu của user (đã đăng nhập)."""
|
||||||
|
__tablename__ = "user_file_records"
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
filename = Column(String, index=True)
|
||||||
|
username = Column(String, index=True)
|
||||||
|
unit_id = Column(String, index=True)
|
||||||
|
unit_name = Column(String, index=True)
|
||||||
|
created_at = Column(DateTime, default=datetime.utcnow)
|
||||||
|
|
||||||
@@ -0,0 +1,651 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="vi">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<script>
|
||||||
|
var savedTheme = localStorage.getItem('audit_theme');
|
||||||
|
if (savedTheme === 'dark' || (!savedTheme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||||
|
document.documentElement.setAttribute('data-theme', 'dark');
|
||||||
|
} else {
|
||||||
|
document.documentElement.setAttribute('data-theme', 'light');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="icon" type="image/png" href="{{ url_for('icon', path='/icon.png') }}">
|
||||||
|
<title>Quản lý Công cụ - Admin - VNPT-MEDIA</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background: var(--bg-main);
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 20px;
|
||||||
|
color: var(--text-main);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
padding: 20px;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border-radius: 15px;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
color: var(--text-main);
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger {
|
||||||
|
background: #e74c3c;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger:hover {
|
||||||
|
background: #c0392b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 25px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 18px;
|
||||||
|
color: var(--text-main);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools-table th,
|
||||||
|
.tools-table td {
|
||||||
|
padding: 15px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools-table th {
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 13px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools-table tr:hover {
|
||||||
|
background: var(--card-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 5px 12px;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-available {
|
||||||
|
background: rgba(46, 204, 113, 0.2);
|
||||||
|
color: #2ecc71;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-unavailable {
|
||||||
|
background: rgba(231, 76, 60, 0.2);
|
||||||
|
color: #e74c3c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.os-icon {
|
||||||
|
font-size: 20px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Upload Form */
|
||||||
|
.upload-form {
|
||||||
|
background: var(--table-row-hover);
|
||||||
|
border: 2px dashed var(--border-color);
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 30px;
|
||||||
|
text-align: center;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-form:hover {
|
||||||
|
border-color: #667eea;
|
||||||
|
background: var(--empty-state-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-form.dragover {
|
||||||
|
border-color: #667eea;
|
||||||
|
background: var(--hover-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input,
|
||||||
|
.form-group select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 15px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--input-border);
|
||||||
|
background: var(--card-bg);
|
||||||
|
color: var(--text-main);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input:focus,
|
||||||
|
.form-group select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group select option {
|
||||||
|
background: var(--input-bg);
|
||||||
|
color: var(--input-text);
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input[type="file"] {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
padding: 15px 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-success {
|
||||||
|
background: rgba(46, 204, 113, 0.2);
|
||||||
|
color: #2ecc71;
|
||||||
|
border: 1px solid rgba(46, 204, 113, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-error {
|
||||||
|
background: rgba(231, 76, 60, 0.2);
|
||||||
|
color: #e74c3c;
|
||||||
|
border: 1px solid rgba(231, 76, 60, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state-icon {
|
||||||
|
font-size: 48px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.form-row {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools-table {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools-table th,
|
||||||
|
.tools-table td {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{% include 'header.html' %}
|
||||||
|
<div class="container">
|
||||||
|
<!-- Top Navigation Tabs -->
|
||||||
|
<div class="top-nav-tabs"
|
||||||
|
style="display: flex; justify-content: center; gap: 10px; margin-bottom: 20px; flex-wrap: wrap; background: rgba(25, 118, 210, 0.05); padding: 15px; border-radius: 12px; border: 1px solid rgba(25, 118, 210, 0.1);">
|
||||||
|
<a href="{{ url_for('index') }}" class="nav-tab"
|
||||||
|
style="padding: 10px 20px; background: var(--card-bg); color: var(--text-main); text-decoration: none; border-radius: 8px; font-weight: 600; font-size: 14px; transition: all 0.3s ease; border: 1px solid var(--border-color); box-shadow: 0 2px 5px rgba(0,0,0,0.05);">🏠 Trang chủ</a>
|
||||||
|
<a href="{{ url_for('my_files') }}" class="nav-tab"
|
||||||
|
style="padding: 10px 20px; background: var(--card-bg); color: var(--text-main); text-decoration: none; border-radius: 8px; font-weight: 600; font-size: 14px; transition: all 0.3s ease; border: 1px solid var(--border-color); box-shadow: 0 2px 5px rgba(0,0,0,0.05);">📁 File của tôi</a>
|
||||||
|
{% if user_info and user_info.role == 'admin' %}
|
||||||
|
<a href="{{ url_for('admin_tools') }}" class="nav-tab"
|
||||||
|
style="padding: 10px 20px; background: linear-gradient(135deg, #1976d2 0%, #1565c0 100%); color: white; text-decoration: none; border-radius: 8px; font-weight: 600; font-size: 14px; transition: all 0.3s ease; box-shadow: 0 4px 10px rgba(25, 118, 210, 0.3);">⚙️ Quản trị</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if user_info and user_info.role == 'admin' %}
|
||||||
|
<!-- Admin Sub Navigation Tabs (Dark mode style) -->
|
||||||
|
<div class="admin-sub-tabs"
|
||||||
|
style="display: flex; justify-content: center; gap: 8px; margin-bottom: 25px; flex-wrap: wrap;">
|
||||||
|
<a href="{{ url_for('admin_tools') }}" class="sub-tab"
|
||||||
|
style="padding: 8px 16px; background: #667eea; color: white; text-decoration: none; border-radius: 20px; font-weight: 600; font-size: 13px; transition: all 0.3s ease; box-shadow: 0 2px 5px rgba(102, 126, 234, 0.3);">🔧 Cài đặt chung</a>
|
||||||
|
<a href="{{ url_for('admin_users_page') }}" class="sub-tab"
|
||||||
|
style="padding: 8px 16px; background: rgba(255, 255, 255, 0.1); color: var(--text-main); text-decoration: none; border-radius: 20px; font-weight: 600; font-size: 13px; transition: all 0.3s ease; border: 1px solid var(--input-border);">👥 Người dùng & Đơn vị</a>
|
||||||
|
<a href="{{ url_for('list_outputs') }}" class="sub-tab"
|
||||||
|
style="padding: 8px 16px; background: rgba(255, 255, 255, 0.1); color: var(--text-main); text-decoration: none; border-radius: 20px; font-weight: 600; font-size: 13px; transition: all 0.3s ease; border: 1px solid var(--input-border);">📂 Tất cả File</a>
|
||||||
|
<a href="{{ url_for('server_stats_page') }}" class="sub-tab"
|
||||||
|
style="padding: 8px 16px; background: rgba(255, 255, 255, 0.1); color: var(--text-main); text-decoration: none; border-radius: 20px; font-weight: 600; font-size: 13px; transition: all 0.3s ease; border: 1px solid var(--input-border);">📊 Thống kê</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="header">
|
||||||
|
<h1>🔧 Quản lý Công cụ Đánh giá</h1>
|
||||||
|
<div class="header-actions">
|
||||||
|
<a href="{{ url_for('index') }}" class="btn btn-secondary">← Về trang chủ</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if message %}
|
||||||
|
<div class="alert alert-{{ message.type }}">
|
||||||
|
{{ message.text }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Upload New Tool -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h2 class="card-title">📤 Tải lên Công cụ mới</h2>
|
||||||
|
</div>
|
||||||
|
<form action="tools/upload" method="post" enctype="multipart/form-data" class="upload-form" id="uploadForm">
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Hệ điều hành *</label>
|
||||||
|
<select name="os_type" required>
|
||||||
|
<option value="">-- Chọn OS --</option>
|
||||||
|
{% for os_key, meta in os_metadata.items() %}
|
||||||
|
<option value="{{ os_key }}">{{ meta.icon }} {{ meta.display_name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Phiên bản OS (vd: 22.04, 24.04, 2019) *</label>
|
||||||
|
<input type="text" name="os_version_label" placeholder="22.04" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Tên công cụ *</label>
|
||||||
|
<input type="text" name="tool_name" placeholder="Ubuntu 22.04 Hardening" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Phiên bản script (vd: v2.0.0) *</label>
|
||||||
|
<input type="text" name="version" placeholder="v2.0.0" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group" style="grid-column: 1 / -1;">
|
||||||
|
<label>File công cụ (.sh, .py, .ps1, .zip) *</label>
|
||||||
|
<input type="file" name="tool_file" accept=".sh,.py,.ps1,.zip,.tar.gz" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">⬆️ Tải lên</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Current Tools -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h2 class="card-title">📦 Công cụ hiện có</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if tools_list %}
|
||||||
|
<table class="tools-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Hệ điều hành</th>
|
||||||
|
<th>Phiên bản OS</th>
|
||||||
|
<th>Tên công cụ</th>
|
||||||
|
<th>Script ver.</th>
|
||||||
|
<th>Cập nhật</th>
|
||||||
|
<th>Trạng thái</th>
|
||||||
|
<th>Thao tác</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for tool in tools_list %}
|
||||||
|
<tr>
|
||||||
|
<td><span class="os-icon">{{ tool.icon }}</span>{{ tool.display_name }}</td>
|
||||||
|
<td>{{ tool.os_version_label or '-' }}</td>
|
||||||
|
<td>{{ tool.name }}</td>
|
||||||
|
<td><code>{{ tool.version }}</code></td>
|
||||||
|
<td>{{ tool.updated }}</td>
|
||||||
|
<td>
|
||||||
|
{% if tool.available %}
|
||||||
|
<span class="status-badge status-available">✓ Có sẵn</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="status-badge status-unavailable">✗ Chưa có</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td style="white-space: nowrap;">
|
||||||
|
{% if tool.available %}
|
||||||
|
<div style="display: inline-flex; gap: 6px; align-items: center;">
|
||||||
|
<a href="{{ url_for('download_tool', version_key=tool.version_key) }}"
|
||||||
|
class="btn btn-secondary" style="padding: 5px 10px; font-size: 12px; white-space: nowrap;">⬇️ Tải</a>
|
||||||
|
<form action="tools/delete/{{ tool.version_key }}" method="post" style="display: inline; margin: 0;"
|
||||||
|
onsubmit="return confirm('Bạn có chắc muốn xóa công cụ này?')">
|
||||||
|
<button type="submit" class="btn btn-danger"
|
||||||
|
style="padding: 5px 10px; font-size: 12px; white-space: nowrap;">🗑️ Xóa</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<span style="color: var(--text-muted);">-</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<div class="empty-state">
|
||||||
|
<div class="empty-state-icon">📭</div>
|
||||||
|
<p>Chưa có công cụ nào được tải lên.</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Upload Documentation Form -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h2 class="card-title">⬆️ Tải lên Tài liệu Cấu hình mới</h2>
|
||||||
|
</div>
|
||||||
|
<form action="{{ url_for('admin_docs_upload') }}" method="post" enctype="multipart/form-data">
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Tên tài liệu *</label>
|
||||||
|
<input type="text" name="doc_name" placeholder="VD: Hướng dẫn Hardening OS" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Phiên bản tài liệu (vd: v1.0) *</label>
|
||||||
|
<input type="text" name="doc_version" placeholder="v1.0" value="v1.0" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group" style="grid-column: 1 / -1;">
|
||||||
|
<label>File tài liệu (.pdf, .docx, .xlsx, .zip) *</label>
|
||||||
|
<input type="file" name="doc_file" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">⬆️ Tải lên Tài liệu</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Current Documentation -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h2 class="card-title">📚 Quản lý Tài liệu Cấu hình hiện có</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if docs_list %}
|
||||||
|
<table class="tools-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Tên tài liệu</th>
|
||||||
|
<th>Tên file</th>
|
||||||
|
<th>Phiên bản</th>
|
||||||
|
<th>Kích thước</th>
|
||||||
|
<th>Cập nhật</th>
|
||||||
|
<th>Trạng thái</th>
|
||||||
|
<th>Thao tác</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for doc in docs_list %}
|
||||||
|
<tr>
|
||||||
|
<td><strong>📄 {{ doc.name }}</strong></td>
|
||||||
|
<td><code>{{ doc.file }}</code></td>
|
||||||
|
<td><code>{{ doc.version }}</code></td>
|
||||||
|
<td>{{ doc.size }}</td>
|
||||||
|
<td>{{ doc.updated }}</td>
|
||||||
|
<td>
|
||||||
|
{% if doc.available %}
|
||||||
|
<span class="status-badge status-available">✓ Có sẵn</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="status-badge status-unavailable">✗ Lỗi file</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td style="white-space: nowrap;">
|
||||||
|
{% if doc.available %}
|
||||||
|
<div style="display: inline-flex; gap: 6px; align-items: center;">
|
||||||
|
<a href="{{ url_for('download_doc', doc_key=doc.doc_key) }}"
|
||||||
|
class="btn btn-secondary" style="padding: 5px 10px; font-size: 12px; white-space: nowrap;">⬇️ Tải</a>
|
||||||
|
<form action="{{ url_for('admin_docs_delete', doc_key=doc.doc_key) }}" method="post" style="display: inline; margin: 0;"
|
||||||
|
onsubmit="return confirm('Bạn có chắc muốn xóa tài liệu này?')">
|
||||||
|
<button type="submit" class="btn btn-danger"
|
||||||
|
style="padding: 5px 10px; font-size: 12px; white-space: nowrap;">🗑️ Xóa</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<form action="{{ url_for('admin_docs_delete', doc_key=doc.doc_key) }}" method="post" style="display: inline; margin: 0;"
|
||||||
|
onsubmit="return confirm('Bạn có chắc muốn xóa bản ghi này?')">
|
||||||
|
<button type="submit" class="btn btn-danger"
|
||||||
|
style="padding: 5px 10px; font-size: 12px; white-space: nowrap;">🗑️ Xóa</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<div class="empty-state">
|
||||||
|
<div class="empty-state-icon">📭</div>
|
||||||
|
<p>Chưa có tài liệu cấu hình nào được tải lên.</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Email Notification Settings -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h2 class="card-title">⚙️ Cài đặt Hệ thống</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="display: flex; align-items: flex-start; gap: 30px; flex-wrap: wrap;">
|
||||||
|
<!-- Email Toggle Block -->
|
||||||
|
<div style="flex: 1; min-width: 280px;">
|
||||||
|
<p style="color: var(--text-muted); font-size: 13px; margin-bottom: 15px;">
|
||||||
|
Bật / Tắt chức năng gửi email thông báo khi xử lý file hoàn tất.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{% if not email_module_available %}
|
||||||
|
<!-- Module không có sẵn -->
|
||||||
|
<div style="display: flex; align-items: center; gap: 15px; padding: 18px 20px; background: rgba(231,76,60,0.12); border: 1px solid rgba(231,76,60,0.3); border-radius: 12px;">
|
||||||
|
<span style="font-size: 32px;">📧</span>
|
||||||
|
<div>
|
||||||
|
<div style="font-weight: 600; font-size: 15px; color: #e74c3c;">Không khả dụng</div>
|
||||||
|
<div style="font-size: 12px; color: var(--text-muted); margin-top: 4px;">
|
||||||
|
Module <code style="background: rgba(255,255,255,0.1); padding: 1px 6px; border-radius: 4px;">email_notifier</code> chưa được cài đặt trên server này.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% elif email_enabled %}
|
||||||
|
<!-- Module có, đang BẬT -->
|
||||||
|
<div style="display: flex; align-items: center; gap: 15px; padding: 18px 20px; background: rgba(46,204,113,0.12); border: 1px solid rgba(46,204,113,0.3); border-radius: 12px; margin-bottom: 15px;">
|
||||||
|
<span style="font-size: 32px;">✅</span>
|
||||||
|
<div>
|
||||||
|
<div style="font-weight: 600; font-size: 15px; color: #2ecc71;">Đang BẬT</div>
|
||||||
|
<div style="font-size: 12px; color: var(--text-muted); margin-top: 4px;">
|
||||||
|
Hệ thống sẽ gửi email thông báo sau mỗi lần xử lý file thành công.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<form action="email/disable" method="post">
|
||||||
|
<button type="submit" class="btn btn-danger" style="width: 100%;">
|
||||||
|
🔕 Tắt gửi Email
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<!-- Module có, đang TẮT -->
|
||||||
|
<div style="display: flex; align-items: center; gap: 15px; padding: 18px 20px; background: var(--card-bg); border: 1px solid rgba(255,255,255,0.15); border-radius: 12px; margin-bottom: 15px;">
|
||||||
|
<span style="font-size: 32px;">🔕</span>
|
||||||
|
<div>
|
||||||
|
<div style="font-weight: 600; font-size: 15px; color: var(--text-muted);">Đang TẮT</div>
|
||||||
|
<div style="font-size: 12px; color: var(--text-muted); margin-top: 4px;">
|
||||||
|
Không gửi email sau khi xử lý. Nhấn bên dưới để bật.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<form action="email/enable" method="post">
|
||||||
|
<button type="submit" class="btn btn-primary" style="width: 100%;">
|
||||||
|
📧 Bật gửi Email
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<p style="margin-top: 12px; font-size: 11px; color: #666; font-style: italic;">
|
||||||
|
⚠️ Lưu ý: cài đặt này chỉ giữ trong phiên chạy hiện tại. Khi khởi động lại server, email sẽ trở về trạng thái <strong>TẮT</strong>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- User Email Stats -->
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h2 class="card-title">👥 Thống kê Người dùng (VNPT Email)</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if email_stats and email_stats|length > 0 %}
|
||||||
|
<div style="overflow-x: auto;">
|
||||||
|
<table class="tools-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Người dùng</th>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Số lần Upload</th>
|
||||||
|
<th>Tổng File</th>
|
||||||
|
<th>Lần đầu</th>
|
||||||
|
<th>Gần nhất</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for stat in email_stats %}
|
||||||
|
<tr>
|
||||||
|
<td style="font-weight: 600; color: var(--text-main);">{{ stat.display_name }}</td>
|
||||||
|
<td style="color: #90caf9;">{{ stat.email }}</td>
|
||||||
|
<td><span class="status-badge" style="background: rgba(102,126,234,0.2); color: #8ba4f9;">{{ stat.total_uploads }}</span></td>
|
||||||
|
<td><span class="status-badge status-available">{{ stat.total_files }}</span></td>
|
||||||
|
<td style="font-size: 13px; color: var(--text-muted);">{{ stat.first_seen }}</td>
|
||||||
|
<td style="font-size: 13px; color: var(--text-muted);">{{ stat.last_seen }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="empty-state">
|
||||||
|
<div class="empty-state-icon">📭</div>
|
||||||
|
<p>Chưa có dữ liệu thống kê từ người dùng.</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Drag and drop support
|
||||||
|
const uploadForm = document.getElementById('uploadForm');
|
||||||
|
|
||||||
|
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
||||||
|
uploadForm.addEventListener(eventName, preventDefaults, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
function preventDefaults(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
['dragenter', 'dragover'].forEach(eventName => {
|
||||||
|
uploadForm.addEventListener(eventName, () => uploadForm.classList.add('dragover'), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
['dragleave', 'drop'].forEach(eventName => {
|
||||||
|
uploadForm.addEventListener(eventName, () => uploadForm.classList.remove('dragover'), false);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,225 @@
|
|||||||
|
<script>
|
||||||
|
// Theme initialization (Anti-FOUC)
|
||||||
|
(function() {
|
||||||
|
var savedTheme = localStorage.getItem('audit_theme');
|
||||||
|
if (savedTheme === 'dark' || (!savedTheme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||||
|
document.documentElement.setAttribute('data-theme', 'dark');
|
||||||
|
} else {
|
||||||
|
document.documentElement.setAttribute('data-theme', 'light');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg-main: #f4f6f9;
|
||||||
|
--card-bg: #ffffff;
|
||||||
|
--text-main: #2b3445;
|
||||||
|
--text-muted: #7d879c;
|
||||||
|
--border-color: #e3e9ef;
|
||||||
|
--border-color-light: #f1f5f9;
|
||||||
|
--primary-color: #1976d2;
|
||||||
|
--hover-bg: #f8f9fa;
|
||||||
|
--input-bg: #ffffff;
|
||||||
|
--input-text: #2b3445;
|
||||||
|
--input-border: #ced4da;
|
||||||
|
--sub-tab-bg: #e3f2fd;
|
||||||
|
--sub-tab-text: #1565c0;
|
||||||
|
--sub-tab-border: #bbdefb;
|
||||||
|
--sub-tab-active-bg: #1565c0;
|
||||||
|
--sub-tab-active-text: #ffffff;
|
||||||
|
--empty-state-bg: #f8f9fa;
|
||||||
|
--table-header-bg: #f1f5f9;
|
||||||
|
--table-header-text: #475569;
|
||||||
|
--table-row-hover: #f8fafc;
|
||||||
|
--table-border: #e2e8f0;
|
||||||
|
--modal-bg: #ffffff;
|
||||||
|
--shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
||||||
|
--success-color: #2e7d32;
|
||||||
|
--success-bg: #e8f5e9;
|
||||||
|
--danger-color: #c62828;
|
||||||
|
--danger-bg: #ffebee;
|
||||||
|
--warning-color: #e65100;
|
||||||
|
--warning-bg: #fff3e0;
|
||||||
|
--info-color: #0277bd;
|
||||||
|
--info-bg: #e1f5fe;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] {
|
||||||
|
--bg-main: #0f172a;
|
||||||
|
--card-bg: #1e293b;
|
||||||
|
--text-main: #f1f5f9;
|
||||||
|
--text-muted: #94a3b8;
|
||||||
|
--border-color: #334155;
|
||||||
|
--border-color-light: #1e293b;
|
||||||
|
--primary-color: #3b82f6;
|
||||||
|
--hover-bg: #334155;
|
||||||
|
--input-bg: #0f172a;
|
||||||
|
--input-text: #f1f5f9;
|
||||||
|
--input-border: #475569;
|
||||||
|
--sub-tab-bg: #1e293b;
|
||||||
|
--sub-tab-text: #e2e8f0;
|
||||||
|
--sub-tab-border: #334155;
|
||||||
|
--sub-tab-active-bg: #3b82f6;
|
||||||
|
--sub-tab-active-text: #ffffff;
|
||||||
|
--empty-state-bg: #1e293b;
|
||||||
|
--table-header-bg: #334155;
|
||||||
|
--table-header-text: #e2e8f0;
|
||||||
|
--table-row-hover: #1e293b;
|
||||||
|
--table-border: #334155;
|
||||||
|
--modal-bg: #1e293b;
|
||||||
|
--shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||||
|
--success-color: #4caf50;
|
||||||
|
--success-bg: rgba(76, 175, 80, 0.1);
|
||||||
|
--danger-color: #ef5350;
|
||||||
|
--danger-bg: rgba(244, 67, 54, 0.1);
|
||||||
|
--warning-color: #ff9800;
|
||||||
|
--warning-bg: rgba(255, 152, 0, 0.1);
|
||||||
|
--info-color: #29b6f6;
|
||||||
|
--info-bg: rgba(3, 169, 244, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.global-header {
|
||||||
|
width: 100%;
|
||||||
|
background: linear-gradient(135deg, #1565c0 0%, #0d47a1 100%);
|
||||||
|
color: white;
|
||||||
|
padding: 10px 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Đẩy toàn bộ nội dung body xuống để không bị header che mất */
|
||||||
|
body {
|
||||||
|
margin-top: 60px !important;
|
||||||
|
background-color: var(--bg-main);
|
||||||
|
color: var(--text-main);
|
||||||
|
transition: background-color 0.3s ease, color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-logo {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 18px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-logo span {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
padding: 3px 8px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-user-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-user-details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-user-name {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-user-role {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #bbdefb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-action-btn {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 6px 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: background 0.3s;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-action-btn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle-btn {
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-guest {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
padding: 6px 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="global-header">
|
||||||
|
<div class="header-logo">
|
||||||
|
🛡️ Audit Hardening Tool <span>VNPT-MEDIA</span>
|
||||||
|
</div>
|
||||||
|
<div class="header-user-info">
|
||||||
|
<button id="themeToggleBtn" class="header-action-btn theme-toggle-btn" title="Chuyển đổi giao diện Sáng/Tối">
|
||||||
|
<span id="themeIcon">🌞</span>
|
||||||
|
</button>
|
||||||
|
{% if is_logged_in and user_info %}
|
||||||
|
<div class="header-user-details">
|
||||||
|
<span class="header-user-name">👤 {{ user_info.full_name or user_info.username }}</span>
|
||||||
|
{% if user_info.role %}
|
||||||
|
<span class="header-user-role">{{ user_info.role|upper }} {% if user_info.unit_name %} | {{ user_info.unit_name }}{% endif %}</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<a href="{{ url_for('logout') }}" class="header-action-btn">🚪 Đăng xuất</a>
|
||||||
|
{% else %}
|
||||||
|
<div class="header-guest">
|
||||||
|
👤 Khách
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
const toggleBtn = document.getElementById('themeToggleBtn');
|
||||||
|
const themeIcon = document.getElementById('themeIcon');
|
||||||
|
|
||||||
|
function updateIcon() {
|
||||||
|
if (document.documentElement.getAttribute('data-theme') === 'dark') {
|
||||||
|
themeIcon.textContent = '🌙';
|
||||||
|
} else {
|
||||||
|
themeIcon.textContent = '🌞';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateIcon();
|
||||||
|
|
||||||
|
toggleBtn.addEventListener('click', function() {
|
||||||
|
let currentTheme = document.documentElement.getAttribute('data-theme');
|
||||||
|
let newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
||||||
|
|
||||||
|
document.documentElement.setAttribute('data-theme', newTheme);
|
||||||
|
localStorage.setItem('audit_theme', newTheme);
|
||||||
|
updateIcon();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,599 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="vi">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<script>
|
||||||
|
var savedTheme = localStorage.getItem('audit_theme');
|
||||||
|
if (savedTheme === 'dark' || (!savedTheme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||||
|
document.documentElement.setAttribute('data-theme', 'dark');
|
||||||
|
} else {
|
||||||
|
document.documentElement.setAttribute('data-theme', 'light');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="icon" type="image/png" href="{{ url_for('icon', path='/icon.png') }}">
|
||||||
|
<title>Đăng nhập - Công cụ Kiểm tra Hardening - VNPT-MEDIA</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--bg-main: #f4f6f9;
|
||||||
|
--card-bg: #ffffff;
|
||||||
|
--text-main: #2b3445;
|
||||||
|
--text-muted: #7d879c;
|
||||||
|
--border-color: #e3e9ef;
|
||||||
|
--border-color-light: #f1f5f9;
|
||||||
|
--primary-color: #1976d2;
|
||||||
|
--hover-bg: #f8f9fa;
|
||||||
|
--input-bg: #ffffff;
|
||||||
|
--input-text: #2b3445;
|
||||||
|
--input-border: #ced4da;
|
||||||
|
--empty-state-bg: #f8f9fa;
|
||||||
|
--success-color: #2e7d32;
|
||||||
|
--success-bg: #e8f5e9;
|
||||||
|
--danger-color: #c62828;
|
||||||
|
--danger-bg: #ffebee;
|
||||||
|
--warning-color: #e65100;
|
||||||
|
--warning-bg: #fff3e0;
|
||||||
|
--info-color: #0277bd;
|
||||||
|
--info-bg: #e1f5fe;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] {
|
||||||
|
--bg-main: #0f172a;
|
||||||
|
--card-bg: #1e293b;
|
||||||
|
--text-main: #f1f5f9;
|
||||||
|
--text-muted: #94a3b8;
|
||||||
|
--border-color: #334155;
|
||||||
|
--border-color-light: #1e293b;
|
||||||
|
--primary-color: #3b82f6;
|
||||||
|
--hover-bg: #334155;
|
||||||
|
--input-bg: #0f172a;
|
||||||
|
--input-text: #f1f5f9;
|
||||||
|
--input-border: #475569;
|
||||||
|
--empty-state-bg: #1e293b;
|
||||||
|
--success-color: #4caf50;
|
||||||
|
--success-bg: rgba(76, 175, 80, 0.1);
|
||||||
|
--danger-color: #ef5350;
|
||||||
|
--danger-bg: rgba(244, 67, 54, 0.1);
|
||||||
|
--warning-color: #ff9800;
|
||||||
|
--warning-bg: rgba(255, 152, 0, 0.1);
|
||||||
|
--info-color: #29b6f6;
|
||||||
|
--info-bg: rgba(3, 169, 244, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background: var(--bg-main);
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px 10px;
|
||||||
|
position: relative;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
body::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -50%;
|
||||||
|
left: -50%;
|
||||||
|
width: 200%;
|
||||||
|
height: 200%;
|
||||||
|
background:
|
||||||
|
linear-gradient(135deg, transparent 0%, transparent 48%, rgba(33, 150, 243, 0.05) 48%, rgba(33, 150, 243, 0.05) 52%, transparent 52%, transparent 100%),
|
||||||
|
linear-gradient(45deg, transparent 0%, transparent 48%, rgba(33, 150, 243, 0.08) 48%, rgba(33, 150, 243, 0.08) 52%, transparent 52%, transparent 100%),
|
||||||
|
linear-gradient(135deg, transparent 0%, transparent 48%, rgba(33, 150, 243, 0.03) 48%, rgba(33, 150, 243, 0.03) 52%, transparent 52%, transparent 100%);
|
||||||
|
background-size: 300px 300px, 400px 400px, 500px 500px;
|
||||||
|
background-position: 0 0, 100px 100px, 200px 0;
|
||||||
|
z-index: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
body::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0 400px 400px 0;
|
||||||
|
border-color: transparent rgba(33, 150, 243, 0.03) transparent transparent;
|
||||||
|
z-index: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-around;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-icon span {
|
||||||
|
width: 100%;
|
||||||
|
height: 3px;
|
||||||
|
background: #1976d2;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container {
|
||||||
|
background: var(--card-bg);
|
||||||
|
border-radius: 15px;
|
||||||
|
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
|
||||||
|
padding: 50px 55px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 480px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
gap: 0;
|
||||||
|
margin-bottom: 35px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
border-bottom: 2px solid var(--sub-tab-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo img {
|
||||||
|
width: 65px;
|
||||||
|
height: 65px;
|
||||||
|
object-fit: contain;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-text {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--primary-color);
|
||||||
|
letter-spacing: 0.35em;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding-right: 0.35em; /* compensate last letter-spacing */
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-icon {
|
||||||
|
display: inline-block;
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
background: #1976d2;
|
||||||
|
border-radius: 50% 0 50% 50%;
|
||||||
|
position: relative;
|
||||||
|
margin-right: 10px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-icon::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border-radius: 50%;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 14px 18px;
|
||||||
|
background-color: var(--input-bg);
|
||||||
|
color: var(--text-main);
|
||||||
|
border: 1px solid var(--input-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 15px;
|
||||||
|
transition: border-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input::placeholder {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-wrapper input {
|
||||||
|
padding-right: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-toggle {
|
||||||
|
position: absolute;
|
||||||
|
right: 12px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-login {
|
||||||
|
width: 100%;
|
||||||
|
padding: 14px;
|
||||||
|
background: #1976d2;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.3s;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-login:hover {
|
||||||
|
background: #1565c0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SSO Portal Button */
|
||||||
|
.sso-divider {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
margin: 20px 0;
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.sso-divider::before,
|
||||||
|
.sso-divider::after {
|
||||||
|
content: '';
|
||||||
|
flex: 1;
|
||||||
|
height: 1px;
|
||||||
|
background-color: var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sso {
|
||||||
|
width: 100%;
|
||||||
|
padding: 14px;
|
||||||
|
background: linear-gradient(135deg, #0d47a1, #1976d2);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sso:hover {
|
||||||
|
background: linear-gradient(135deg, #1565c0, #1e88e5);
|
||||||
|
box-shadow: 0 4px 15px rgba(25, 118, 210, 0.4);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sso-info {
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
background-color: var(--info-bg);
|
||||||
|
border-radius: 6px;
|
||||||
|
border-left: 3px solid #1976d2;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--primary-color);
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-text {
|
||||||
|
margin-top: 30px;
|
||||||
|
padding-top: 20px;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-text a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-text a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-text strong {
|
||||||
|
color: var(--danger-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 20px;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--primary-color);
|
||||||
|
font-size: 13px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer strong {
|
||||||
|
color: var(--danger-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-error {
|
||||||
|
background-color: var(--danger-bg);
|
||||||
|
color: var(--danger-color);
|
||||||
|
border: 1px solid #ef9a9a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-success {
|
||||||
|
background-color: var(--success-bg);
|
||||||
|
color: var(--success-color);
|
||||||
|
border: 1px solid #a5d6a7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Styles */
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.login-container {
|
||||||
|
padding: 30px 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-text {
|
||||||
|
font-size: 38px;
|
||||||
|
letter-spacing: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-icon {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-icon::after {
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
position: relative;
|
||||||
|
bottom: auto;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 480px) {
|
||||||
|
body {
|
||||||
|
padding: 10px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container {
|
||||||
|
padding: 25px 20px;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-text {
|
||||||
|
font-size: 32px;
|
||||||
|
letter-spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-icon::after {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input {
|
||||||
|
padding: 10px 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-login {
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-guest {
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-text {
|
||||||
|
font-size: 11px;
|
||||||
|
margin-top: 20px;
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
font-size: 11px;
|
||||||
|
position: relative;
|
||||||
|
bottom: auto;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-icon {
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="menu-icon">
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="login-container">
|
||||||
|
<div class="logo">
|
||||||
|
<img src="{{ url_for('icon', path='/icon.png') }}" alt="VNPT-MEDIA Logo">
|
||||||
|
<span class="logo-text">VNPT-MEDIA</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if message %}
|
||||||
|
<div class="alert alert-{{ message.type }}">
|
||||||
|
{{ message.text }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="post" action="login" id="loginForm">
|
||||||
|
<div class="form-group">
|
||||||
|
<div style="position: relative;">
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="emailInput"
|
||||||
|
name="username"
|
||||||
|
placeholder="ten.nv@vnpt.vn"
|
||||||
|
required
|
||||||
|
autocomplete="email"
|
||||||
|
style="padding-left: 44px;"
|
||||||
|
oninput="validateEmailDomain(this)"
|
||||||
|
>
|
||||||
|
<span style="position:absolute;left:14px;top:50%;transform:translateY(-50%);font-size:17px;pointer-events:none;">✉️</span>
|
||||||
|
</div>
|
||||||
|
<div id="emailHint" style="margin-top:6px;font-size:12px;color: var(--danger-color);display:none;">
|
||||||
|
⚠️ Vui lòng nhập email có đuôi <strong>@vnpt.vn</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="password-wrapper">
|
||||||
|
<input type="password" id="password" name="password" placeholder="Mật khẩu" required autocomplete="current-password">
|
||||||
|
<span class="password-toggle" onclick="togglePassword()">👁</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px; flex-wrap: nowrap;">
|
||||||
|
<span style="font-size: 14px; color: var(--text-muted); white-space: nowrap;">🔐 Mã xác thực 2FA (OTP)</span>
|
||||||
|
<span style="
|
||||||
|
background: linear-gradient(135deg, #1565c0, #1976d2);
|
||||||
|
color: white;
|
||||||
|
padding: 2px 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
">RADIUS</span>
|
||||||
|
</div>
|
||||||
|
<input type="text" name="otp" id="otp"
|
||||||
|
maxlength="6" pattern="[0-9]*" inputmode="numeric"
|
||||||
|
autocomplete="one-time-code"
|
||||||
|
style="letter-spacing: 8px; text-align: center; font-size: 18px; font-weight: 600;">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn-login">🔑 Đăng nhập qua RADIUS</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% if sso_enabled %}
|
||||||
|
<div class="sso-divider">hoặc</div>
|
||||||
|
|
||||||
|
<div style="text-align:center; margin-bottom: 10px;">
|
||||||
|
<div class="sso-info">
|
||||||
|
🌐 <strong>Đăng nhập qua SSO Portal:</strong><br>
|
||||||
|
Truy cập hệ thống bằng tài khoản tập đoàn VNPT-MEDIA.<br>
|
||||||
|
SSO Portal sẽ tự động chuyển hướng bạn về đây sau khi xác thực.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function togglePassword() {
|
||||||
|
const passwordInput = document.getElementById('password');
|
||||||
|
const toggle = document.querySelector('.password-toggle');
|
||||||
|
|
||||||
|
if (passwordInput.type === 'password') {
|
||||||
|
passwordInput.type = 'text';
|
||||||
|
toggle.textContent = '🙈';
|
||||||
|
} else {
|
||||||
|
passwordInput.type = 'password';
|
||||||
|
toggle.textContent = '👁';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate domain @vnpt.vn khi người dùng gõ
|
||||||
|
function validateEmailDomain(input) {
|
||||||
|
const hint = document.getElementById('emailHint');
|
||||||
|
const val = input.value.trim().toLowerCase();
|
||||||
|
// Chỉ hiện cảnh báo khi người dùng đã gõ @ nhưng domain sai
|
||||||
|
if (val.includes('@') && !val.endsWith('@vnpt.vn')) {
|
||||||
|
hint.style.display = 'block';
|
||||||
|
input.style.borderColor = '#e53935';
|
||||||
|
} else {
|
||||||
|
hint.style.display = 'none';
|
||||||
|
input.style.borderColor = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chặn submit nếu domain sai
|
||||||
|
document.getElementById('loginForm').addEventListener('submit', function(e) {
|
||||||
|
const emailVal = document.getElementById('emailInput').value.trim().toLowerCase();
|
||||||
|
if (!emailVal.endsWith('@vnpt.vn')) {
|
||||||
|
e.preventDefault();
|
||||||
|
document.getElementById('emailHint').style.display = 'block';
|
||||||
|
document.getElementById('emailInput').style.borderColor = '#e53935';
|
||||||
|
document.getElementById('emailInput').focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Auto-focus OTP field after password
|
||||||
|
document.getElementById('password').addEventListener('keydown', function(e) {
|
||||||
|
if (e.key === 'Tab' || e.key === 'Enter') {
|
||||||
|
if (e.key === 'Enter') e.preventDefault();
|
||||||
|
document.getElementById('otp').focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// OTP: only allow digits
|
||||||
|
document.getElementById('otp').addEventListener('input', function(e) {
|
||||||
|
this.value = this.value.replace(/[^0-9]/g, '');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,44 @@
|
|||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
|
||||||
|
a = Analysis(
|
||||||
|
['windows_audit.py'],
|
||||||
|
pathex=[],
|
||||||
|
binaries=[],
|
||||||
|
datas=[('audit_cis_windows.ps1.enc', '.')],
|
||||||
|
hiddenimports=['colorama', 'unidecode', 'Crypto', 'Crypto.Cipher', 'Crypto.PublicKey', 'Crypto.Util.Padding'],
|
||||||
|
hookspath=[],
|
||||||
|
hooksconfig={},
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
win_no_prefer_redirects=False,
|
||||||
|
win_private_assemblies=False,
|
||||||
|
cipher=block_cipher,
|
||||||
|
noarchive=False,
|
||||||
|
)
|
||||||
|
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
[],
|
||||||
|
name='AuditTool',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
runtime_tmpdir=None,
|
||||||
|
console=True,
|
||||||
|
disable_windowed_traceback=False,
|
||||||
|
argv_emulation=False,
|
||||||
|
target_arch=None,
|
||||||
|
codesign_identity=None,
|
||||||
|
entitlements_file=None,
|
||||||
|
)
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,201 @@
|
|||||||
|
Unnamed: 0,Unnamed: 1,KẾT QUẢ,Unnamed: 3,Unnamed: 4,Unnamed: 5
|
||||||
|
,Hạng mục đánh giá ,Đáp ứng,,,Ghi chú
|
||||||
|
,,Có ,Không,Bắt buộc,
|
||||||
|
1,Thiết lập ban đầu,,,,
|
||||||
|
1.1,Cấu hình filesystem,,,,
|
||||||
|
1.1.1,Cấu hình vô hiệu hoá các filesystem không sử dụng,,,,
|
||||||
|
1.1.1.1,Cấu hình vô hiệu hoá cramfs filesystem,,,,
|
||||||
|
1.1.1.2,Cấu hình vô hiệu hoá freevxfs filesystem,,,,
|
||||||
|
1.1.1.3,Cấu hình vô hiệu hoá hfs filesystem,,,,
|
||||||
|
1.1.1.4,Cấu hình vô hiệu hoá hfsplus filesystem,,,,
|
||||||
|
1.1.1.5,Cấu hình vô hiệu hoá jffs2 filesystem,,,,
|
||||||
|
1.1.1.6,Cấu hình vô hiệu hoá squashfs filesystem,,,,
|
||||||
|
1.1.1.7,Cấu hình vô hiệu hoá udf filesystem,,,,
|
||||||
|
1.1.1.8,Cấu hình vô hiệu hoá usb storage,,,,
|
||||||
|
1.1.2,Cấu hình phân vùng /tmp,,,,
|
||||||
|
1.1.2.1,Cấu hình tuỳ chọn nodev cho phân vùng /tmp,,,,
|
||||||
|
1.1.2.2,Cấu hình tuỳ chọn nosuid cho phân vùng /tmp,,,,
|
||||||
|
1.1.2.3,Cấu hình tuỳ chọn noexec cho phân vùng /tmp,,,,
|
||||||
|
1.1.3,Cấu hình phân vùng /var/tmp,,,,
|
||||||
|
1.1.3.1,Cấu hình tuỳ chọn nodev cho phân vùng /var/tmp,,,,
|
||||||
|
1.1.3.2,Cấu hình tuỳ chọn nosuid cho phân vùng /var/tmp,,,,
|
||||||
|
1.1.3.3,Cấu hình tuỳ chọn noexec cho phân vùng /var/tmp,,,,
|
||||||
|
1.1.4,Cấu hình phân vùng /home,,,,
|
||||||
|
1.1.4.1,Cấu hình tuỳ chọn nodev cho phân vùng /home,,,,
|
||||||
|
1.1.4.2,Cấu hình tuỳ chọn nosuid cho phân vùng /home,,,,
|
||||||
|
1.1.5,Cấu hình phân vùng /dev/shm,,,,
|
||||||
|
1.1.5.1,Cấu hình tuỳ chọn nodev cho phân vùng /dev/shm,,,,
|
||||||
|
1.1.5.2,Cấu hình tuỳ chọn nosuid cho phân vùng /dev/shm,,,,
|
||||||
|
1.1.5.3,Cấu hình tuỳ chọn noexec cho phân vùng /dev/shm,,,,
|
||||||
|
1.2,Cấu hình cập nhật phần mềm,,,,
|
||||||
|
1.2.1 ,Cấu hình kích hoạt gpgcheck,,,,
|
||||||
|
1.3,Kiểm tra tính toàn vẹn của filesystem,,,,
|
||||||
|
1.3.1,Kiểm tra cài đặt AIDE,,,,
|
||||||
|
1.3.2 ,Cấu hình kiểm tra tính toàn vẹn của filesystem,,,,
|
||||||
|
1.4,Cấu hình khởi động an toàn,,,,
|
||||||
|
1.4.1,Phân quyền đối với file cấu hình bootloader,,,,
|
||||||
|
1.4.2,Cấu hình mật khẩu cho bootloader,,,,
|
||||||
|
1.4.3,Cấu hình xác thực khi truy cập single user mode,,,,
|
||||||
|
1.4.4,Cấu hình vô hiệu hoá interactive boot ,,,,
|
||||||
|
1.5,Additional Process Hardening,,,,
|
||||||
|
1.5.1 ,Cấu hình vô hiệu hoá core dump,,,,
|
||||||
|
1.5.2,Cấu hình kích hoạt ASLR (address space layout randomization),,,,
|
||||||
|
1.5.3,Cấu hình vô hiệu hoá prelink,,,,
|
||||||
|
1.6,Kiểm soát nội dung cảnh báo,,,,
|
||||||
|
1.6.1,Kiểm soát nội dung motd (Message Of The Day),,,,
|
||||||
|
1.6.2,Kiểm soát nội dung thông báo khi đăng nhập,,,,
|
||||||
|
1.6.3,Kiểm soát nội dung thông báo khi đăng nhập từ xa,,,,
|
||||||
|
1.6.4,Cấu hình phân quyền đối với file /etc/motd,,,,
|
||||||
|
1.6.5,Cấu hình phân quyền đối với file /etc/issue,,,,
|
||||||
|
1.6.6,Cấu hình phân quyền đối với file /etc/issue.net,,,,
|
||||||
|
1.6.7,Kiểm soát nội dung thông báo khi truy cập GNOME,,,,
|
||||||
|
2,Service,,,,
|
||||||
|
2.1,Cấu hình Time Synchronization,,,,
|
||||||
|
2.1.1,Cấu hình sử dụng chrony,,,x,
|
||||||
|
2.1.2,Cấu hình sử dụng ntp,,,x,
|
||||||
|
2.2,Các Service với mục đích riêng biệt,,,,
|
||||||
|
2.2.1,Cấu hình vô hiệu hoá xinetd services,,,,
|
||||||
|
2.2.2,Cấu hình vô hiệu hoá chargen services,,,,
|
||||||
|
2.2.3,Cấu hình vô hiệu hoá daytime services,,,,
|
||||||
|
2.2.4,Cấu hình vô hiệu hoá discard services,,,,
|
||||||
|
2.2.5,Cấu hình vô hiệu hoá echo services,,,,
|
||||||
|
2.2.6,Cấu hình vô hiệu hoá time services,,,,
|
||||||
|
2.2.7,Cấu hình vô hiệu hoá rsh server,,,,
|
||||||
|
2.2.8,Cấu hình vô hiệu hoá talk server,,,,
|
||||||
|
2.2.9,Cấu hình vô hiệu hoá autofs services,,,,
|
||||||
|
2.2.10,Cấu hình vô hiệu hoá X window server services,,,,
|
||||||
|
2.2.11,Cấu hình vô hiệu hoá avahi daemon services,,,,
|
||||||
|
2.2.12,Cấu hình vô hiệu hoá cups services,,,,
|
||||||
|
2.2.13,Cấu hình vô hiệu hoá dhcp server services,,,,
|
||||||
|
2.2.14,Cấu hình vô hiệu hoá ldap server services,,,,
|
||||||
|
2.2.15,Cấu hình vô hiệu hoá dns server services,,,,
|
||||||
|
2.2.16,Cấu hình vô hiệu hoá dnsmasq services,,,,
|
||||||
|
2.2.17,Cấu hình vô hiệu hoá ftp server services,,,,
|
||||||
|
2.2.18,Cấu hình vô hiệu hoá tftp server services,,,,
|
||||||
|
2.2.19,Cấu hình vô hiệu hoá web server services,,,,
|
||||||
|
2.2.20,Cấu hình vô hiệu hoá imap and pop3 server services,,,,
|
||||||
|
2.2.21,Cấu hình vô hiệu hoá samba file server services,,,,
|
||||||
|
2.2.22,Cấu hình vô hiệu hoá web proxy server services,,,,
|
||||||
|
2.2.23,Cấu hình vô hiệu hoá snmp services,,,,
|
||||||
|
2.2.24,Cấu hình vô hiệu hoá nis server services,,,,
|
||||||
|
2.2.25,Cấu hình vô hiệu hoá telnet server services,,,,
|
||||||
|
2.2.26,Cấu hình mail transfer agents sang chế độ local-only,,,,
|
||||||
|
2.2.27,Cấu hình vô hiệu hoá network file system services,,,,
|
||||||
|
2.2.28,Cấu hình vô hiệu hoá rpcbind services,,,,
|
||||||
|
2.2.29,Cấu hình vô hiệu hoá rsync services,,,,
|
||||||
|
2.3,Service Clients,,,,
|
||||||
|
2.3.1,Cấu hình vô hiệu hoá nis client,,,,
|
||||||
|
2.3.2,Cấu hình vô hiệu hoá rsh client,,,,
|
||||||
|
2.3.3,Cấu hình vô hiệu hoá talk client,,,,
|
||||||
|
2.3.4,Cấu hình vô hiệu hoá telnet client,,,,
|
||||||
|
2.3.5,Cấu hình vô hiệu hoá ldap client,,,,
|
||||||
|
2.3.6,Cấu hình vô hiệu hoá ftp client,,,,
|
||||||
|
2.3.7,Cấu hình vô hiệu hoá tftp client,,,,
|
||||||
|
3,Cấu hình mạng,,,,
|
||||||
|
3.1,Tham số cấu hình mạng (Host Only),,,,
|
||||||
|
3.1.1 ,Cấu hình vô hiệu hoá IP forwarding,,,,
|
||||||
|
3.1.2,Cấu hình vô hiệu hoá tính năng chuyển hướng gói tin (packet redirect),,,,
|
||||||
|
3.2,Tham số cấu hình mạng (Host và Router),,,,
|
||||||
|
3.2.1 ,Cấu hình từ chối các gói tin với nguồn được định tuyến trước,,,,
|
||||||
|
3.2.2,Cấu hình từ chối các ICMP redirect message,,,,
|
||||||
|
3.2.3,Cấu hình từ chối các secure ICMP redirect message,,,,
|
||||||
|
3.2.4,Cấu hình từ chối các gói tin ICMP request broadcast,,,,
|
||||||
|
3.2.5,Cấu hình bỏ qua phản hồi ICMP không hợp lệ,,,,
|
||||||
|
3.2.6,Cấu hình Reverse Path Filtering,,,,
|
||||||
|
3.2.7,Cấu hình TCP SYN Cookies,,,,
|
||||||
|
3.4,Cấu hình Firewall,,,,
|
||||||
|
3.4.1,Cấu hình firewalld,,,,
|
||||||
|
3.4.1.1,Cấu hình kích hoạt firewalld,,,x,
|
||||||
|
3.4.1.3,Cấu hình firewalld rule cho tất cả các port và protocol đang mở,,,x,
|
||||||
|
3.4.1.4,Cấu hình chính sách từ chối mặc định cho firewalld,,,x,
|
||||||
|
3.4.2,Iptables,,,,
|
||||||
|
3.4.2.1,Cấu hình kích hoạt Iptables,,,x,
|
||||||
|
3.4.2.3,Cấu hình iptables loopback traffic,,,x,
|
||||||
|
3.4.2.4,Cấu hình iptables rule cho tất cả các port và protocol đang mở,,,x,
|
||||||
|
3.4.2.5,Cấu hình chính sách từ chối mặc định cho iptables,,,x,
|
||||||
|
4,Logging và Auditing,,,,
|
||||||
|
4.1,Cấu hình logging,,,,
|
||||||
|
4.1.1,Cấu hình rsyslog,,,,
|
||||||
|
4.1.1.1 ,Cấu hình kích hoạt rsyslog service,,,,
|
||||||
|
4.1.1.2,Phân quyền đối với file log sinh ra từ rsyslog,,,,
|
||||||
|
4.1.1.3,Cấu hình lưu trữ log sinh ra từ rsyslog tập trung,,,x,
|
||||||
|
4.1.1.4,Phân quyền đối với tất cả các file log,,,,
|
||||||
|
5,"Cấu hình truy cập, xác thực và ủy quyền",,,,
|
||||||
|
5.1,Cấu hình cron,,,,
|
||||||
|
5.1.1 ,Cấu hình kích hoạt cron daemon,,,,
|
||||||
|
5.1.2 ,Cấu hình phân quyền cho file /etc/crontab,,,,
|
||||||
|
5.1.3 ,Cấu hình phân quyền cho file /etc/cron.hourly,,,,
|
||||||
|
5.1.4 ,Cấu hình phân quyền cho file /etc/cron.daily,,,,
|
||||||
|
5.1.5 ,Cấu hình phân quyền cho file /etc/cron.weekly,,,,
|
||||||
|
5.1.6 ,Cấu hình phân quyền cho của file /etc/cron.monthly,,,,
|
||||||
|
5.1.7 ,Cấu hình phân quyền cho file /etc/cron.d,,,,
|
||||||
|
5.1.8 ,Cấu hình at/cron hạn chế chỉ cho người dùng được ủy quyền,,,,
|
||||||
|
5.2,Cấu hình máy chủ SSH,,,,
|
||||||
|
5.2.1 ,Cấu hình phân quyền cho file /etc/ssh/sshd_config,,,,
|
||||||
|
5.2.2,Cấu hình phân quyền cho các file SSH private host key,,,,
|
||||||
|
5.2.3,Cấu hình phân quyền cho các file SSH public host key,,,,
|
||||||
|
5.2.4,Cấu hình giới hạn truy cập cho máy chủ SSH,,,,
|
||||||
|
5.2.5,Cấu hình LogLevel cho máy chủ SSH,,,,
|
||||||
|
5.2.6,Cấu hình sử dụng SSH PAM,,,,
|
||||||
|
5.2.7,Cấu hình vô hiệu hoá đăng nhập bằng root cho máy chủ SSH,,,,
|
||||||
|
5.2.8,Cấu hình vô hiệu hoá HostbasedAuthentication cho máy chủ SSH,,,,
|
||||||
|
5.2.9,Cấu hình vô hiệu hoá PermitEmptyPasswords cho máy chủ SSH,,,,
|
||||||
|
5.2.10,Cấu hình vô hiệu hoá PermitUserEnviroment cho máy chủ SSH,,,,
|
||||||
|
5.2.11,Cấu hình vô hiệu hoá IgnoreRhosts cho máy chủ SSH,,,,
|
||||||
|
5.2.12,Cấu hình vô hiệu hoá X11 Forwarding cho máy chủ SSH,,,,
|
||||||
|
5.2.13,Cấu hình vô hiệu hoá SSH AllowTcpForwarding,,,,
|
||||||
|
5.2.14,Cấu hình cảnh báo SSH,,,,
|
||||||
|
5.2.15,Cấu hình SSH MaxAuthTries,,,,
|
||||||
|
5.2.16,Cấu hình SSH MaxStartups,,,,
|
||||||
|
5.2.17,Cấu hình SSH MaxSessions,,,,
|
||||||
|
5.2.18,Cấu hình SSH LoginGraceTime,,,,
|
||||||
|
5.2.19,Cấu hình khoảng thời gian chờ không hoạt động cho máy chủ SSH,,,,
|
||||||
|
5.2.20,Cấu hình các thuật toán MAC được cho phép,,,,
|
||||||
|
5.3,Cấu hình PAM,,,,
|
||||||
|
5.3.1 ,Cấu hình điều kiện tạo mật khẩu,,,x,
|
||||||
|
5.3.2 ,Cấu hình khoá truy cập do nhiều lần nhập mật khẩu thất bại,,,x,
|
||||||
|
5.3.3 ,Giới hạn việc sử dụng lại mật khẩu,,,,
|
||||||
|
5.3.4 ,Cấu hình thuật toán hash mật khẩu sang SHA-512,,,,
|
||||||
|
5.4,Cấu hình tài khoản người dùng và môi trường,,,,
|
||||||
|
5.4.1 ,Cấu hình mật khẩu người dùng,,,,
|
||||||
|
5.4.1.1 ,Cấu hình thời gian hết hạn sử dụng mật khẩu,,,x,
|
||||||
|
5.4.1.2 ,Cấu hình thời gian tối thiểu giữa những lần thay đổi mật khẩu,,,x,
|
||||||
|
5.4.1.3 ,Cấu hình thời gian cảnh báo mật khẩu hết hạn,,,,
|
||||||
|
5.4.1.4 ,Cấu hình thời gian khoá tài khoản không thay mật khẩu sau khi hết hạn,,,,
|
||||||
|
5.4.1.5,Đảm bảo thời gian thay đổi mật khẩu lần cuối hợp lệ,,,,
|
||||||
|
5.4.2 ,Cấu hình vô hiệu hoá đăng nhập bằng tài khoản hệ thống,,,,
|
||||||
|
5.4.3,Cấu hình shell timeout mặc định,,,,
|
||||||
|
5.4.4,Cấu hình group mặc định của tài khoản root,,,,
|
||||||
|
5.4.5,Cấu hình user umask mặc định,,,,
|
||||||
|
5.4.6,Cấu hình hạn chế truy cập cho câu lệnh su,,,,
|
||||||
|
6,System Maintenance,,,,
|
||||||
|
6.1,Quyền của file hệ thống,,,,
|
||||||
|
6.1.1,Cấu hình sticky bit cho tất cả các thư mục dùng chung,,,,
|
||||||
|
6.1.2,Cấu hình phân quyền cho file /etc/passwd,,,,
|
||||||
|
6.1.3,Cấu hình phân quyền cho file /etc/shadow,,,,
|
||||||
|
6.1.4,Cấu hình phân quyền cho file /etc/group,,,,
|
||||||
|
6.1.5,Cấu hình phân quyền cho file /etc/gshadow,,,,
|
||||||
|
6.1.6,Cấu hình phân quyền cho file /etc/passwd-,,,,
|
||||||
|
6.1.7,Cấu hình phân quyền cho file /etc/shadow-,,,,
|
||||||
|
6.1.8,Cấu hình phân quyền cho file /etc/group-,,,,
|
||||||
|
6.1.9,Cấu hình phân quyền cho file /etc/gshadow-,,,,
|
||||||
|
6.1.10,Đảm bảo không có file world-writable tồn tại,,,,
|
||||||
|
6.1.11,Đảm bảo các file hoặc thư mục không có chủ sở hữu không tồn tại,,,,
|
||||||
|
6.1.12,Đảm bảo các file hoặc thư mục không có nhóm không tồn tại,,,,
|
||||||
|
6.2,Thiết lập cho người dùng và nhóm,,,,
|
||||||
|
6.2.1 ,Đảm bảo trường mật khẩu không để trống,,,,
|
||||||
|
6.2.2,Đảm bảo mọi nhóm trong file /etc/passwd tồn tại trong file /etc/group,,,,
|
||||||
|
6.2.3,Đảm bảo UID không bị lặp,,,,
|
||||||
|
6.2.4,Đảm bảo GID không bị lặp,,,,
|
||||||
|
6.2.5,Đảm bảo tên người dùng không bị lặp,,,,
|
||||||
|
6.2.6,Đảm bảo tên group không bị lặp,,,,
|
||||||
|
6.2.7,Đảm bảo tính toàn vẹn cho biến môi trường PATH của root,,,,
|
||||||
|
6.2.8,Đảm bảo root là tài khoản duy nhất có UID là 0,,,,
|
||||||
|
6.2.9,Đảm bảo mọi người dùng đều tồn tại thư mục home,,,,
|
||||||
|
6.2.10,Đảm bảo người dùng sở hữu thư mục home của chính họ,,,,
|
||||||
|
6.2.11,Đảm bảo quyền thư mục home của người dùng có mức bảo mật cao,,,,
|
||||||
|
6.2.12,Đảm bảo các file dot của người dùng không cấp quyền write cho group hoặc other,,,,
|
||||||
|
6.2.13,Đảm bảo không người dùng nào có file .forward,,,,
|
||||||
|
6.2.14,Đảm bảo không người dùng nào có file .netrc,,,,
|
||||||
|
6.2.15,Đảm bảo không người dùng nào có file .rhosts,,,,
|
||||||
|
,0,0,0,,
|
||||||
|
@@ -0,0 +1,236 @@
|
|||||||
|
STT,Hạng mục đánh giá,Unnamed: 2,Unnamed: 3,Unnamed: 4
|
||||||
|
,,KẾT QUẢ,,
|
||||||
|
,,,,
|
||||||
|
,,Đạt,Không,
|
||||||
|
2.1,Thiết lập ban đầu,,,
|
||||||
|
2.1.1,Cấu hình filesystem,,,
|
||||||
|
2.1.1.1,"Cấu hình vô hiệu hoá cramfs, freevxfs, jffs2, hfs, hfsplus, squashfs, udf filesystem",,,
|
||||||
|
,Cấu hình vô hiệu hoá cramfs filesystem,,,
|
||||||
|
,Cấu hình vô hiệu hoá freevxfs filesystem,,,
|
||||||
|
,Cấu hình vô hiệu hoá hfs filesystem,,,
|
||||||
|
,Cấu hình vô hiệu hoá hfsplus filesystem,,,
|
||||||
|
,Cấu hình vô hiệu hoá jffs2 filesystem,,,
|
||||||
|
,Cấu hình vô hiệu hoá squashfs filesystem,,,
|
||||||
|
,Cấu hình vô hiệu hoá udf filesystem,,,
|
||||||
|
,Cấu hình vô hiệu hoá usb storage,,,
|
||||||
|
,Cấu hình phân vùng /tmp,,,
|
||||||
|
2.1.1.2,Cấu hình tuỳ chọn nodev cho phân vùng /tmp,,,
|
||||||
|
2.1.1.3,Cấu hình tuỳ chọn nosuid cho phân vùng /tmp,,,
|
||||||
|
2.1.1.4,Cấu hình tuỳ chọn noexec cho phân vùng /tmp,,,
|
||||||
|
,Cấu hình phân vùng /var/tmp,,,
|
||||||
|
2.1.1.5,Cấu hình tuỳ chọn nodev cho phân vùng /var/tmp,,,
|
||||||
|
2.1.1.6,Cấu hình tuỳ chọn nosuid cho phân vùng /var/tmp,,,
|
||||||
|
2.1.1.7,Cấu hình tuỳ chọn noexec cho phân vùng /var/tmp,,,
|
||||||
|
,Cấu hình phân vùng /home,,,
|
||||||
|
2.1.1.8,Cấu hình tuỳ chọn nodev cho phân vùng /home,,,
|
||||||
|
,Cấu hình phân vùng /dev/shm,,,
|
||||||
|
2.1.1.9,Cấu hình tuỳ chọn nodev cho phân vùng /dev/shm,,,
|
||||||
|
2.1.1.10,Cấu hình tuỳ chọn nosuid cho phân vùng /dev/shm,,,
|
||||||
|
2.1.1.11,Cấu hình tuỳ chọn noexec cho phân vùng /dev/shm,,,
|
||||||
|
2.1.1.12,Cấu hình sticky bit cho tất cả các thư mục dùng chung,,,
|
||||||
|
2.1.1.13,Cấu hình vô hiệu hoá automounting,,,
|
||||||
|
2.1.2,Cấu hình sudo (áp dụng dành riêng cho Ubuntu),,,
|
||||||
|
2.1.2.1,Kiểm tra việc cài đặt sudo,,,
|
||||||
|
2.1.2.2,Cấu hình các lệnh sudo sử dụng pty,,,
|
||||||
|
2.1.2.3,Đảm bảo file nhật ký của sudo tồn tại,,,
|
||||||
|
2.1.3,Cấu hình cập nhật phần mềm,,,
|
||||||
|
2.1.3.1,Cấu hình kích hoạt gpgcheck,,,
|
||||||
|
2.1.4,Kiểm tra tính toàn vẹn của filesystem,,,
|
||||||
|
2.1.4.1,Kiểm tra việc cài đặt AIDE,,,
|
||||||
|
2.1.4.2,Cấu hình kiểm tra tính toàn vẹn của filesystem,,,
|
||||||
|
2.1.5,Cấu hình khởi động an toàn,,,
|
||||||
|
2.1.5.1,Phân quyền đối với file cấu hình bootloader,,,
|
||||||
|
2.1.5.2,Cấu hình mật khẩu cho bootloader,,,
|
||||||
|
2.1.5.3,Cấu hình xác thực khi truy cập single user mode,,,
|
||||||
|
2.1.5.4,Cấu hình vô hiệu hoá interactive boot,,,
|
||||||
|
2.1.6,Additional Process Hardening,,,
|
||||||
|
2.1.6.1,Cấu hình kiểm soát core dump,,,
|
||||||
|
2.1.6.2,Cấu hình kích hoạt ASLR (address space layout randomization),,,
|
||||||
|
2.1.6.3,Cấu hình vô hiệu hoá prelink,,,
|
||||||
|
2.1.7,Kiểm soát nội dung cảnh báo,,,
|
||||||
|
2.1.7.1,Kiểm soát nội dung motd (Message Of The Day),,,
|
||||||
|
2.1.7.2,Kiểm soát nội dung thông báo khi đăng nhập,,,
|
||||||
|
2.1.7.3,Kiểm soát nội dung thông báo khi đăng nhập từ xa,,,
|
||||||
|
2.1.7.4,Cấu hình phân quyền đối với file /etc/motd,,,
|
||||||
|
2.1.7.5,Cấu hình phân quyền đối với file /etc/issue,,,
|
||||||
|
2.1.7.6,Cấu hình phân quyền đối với file /etc/issue.net,,,
|
||||||
|
2.1.7.7,Kiểm soát nội dung thông báo khi truy cập GNOME,,,
|
||||||
|
2.2,Service,,,
|
||||||
|
2.2.1, Cấu hình inetd Service,,,
|
||||||
|
2.2.1.1,"Cấu hình vô hiệu hoá chargen service, daytime service,",,,
|
||||||
|
,"discard service, echo service, time service, rsh, talk service, telnet, tftp, rsync, xinetd.",,,
|
||||||
|
2.2.2,Các Service với mục đích riêng biệt,,,
|
||||||
|
2.2.2.1,Cấu hình sử dụng NTP,,,
|
||||||
|
2.2.2.2,Cấu hình sử dụng chrony,,,
|
||||||
|
2.2.2.3,"Cấu hình vô hiệu hoá X window, Avahi, CUPS, DHCP, LDAP, NFS, RPC, DNS , FTP, HTTP, POP3, IMAP, Samba, HTTP Proxy, SNMP, NIS",,,
|
||||||
|
,Cấu hình vô hiệu hoá xinetd services,,,
|
||||||
|
,Cấu hình vô hiệu hoá autofs services,,,
|
||||||
|
,Cấu hình vô hiệu hoá X window server services,,,
|
||||||
|
,Cấu hình vô hiệu hoá avahi daemon services,,,
|
||||||
|
,Cấu hình vô hiệu hoá cups services,,,
|
||||||
|
,Cấu hình vô hiệu hoá dhcp server services,,,
|
||||||
|
,Cấu hình vô hiệu hoá ldap server services,,,
|
||||||
|
,Cấu hình vô hiệu hoá dns server services,,,
|
||||||
|
,Cấu hình vô hiệu hoá dnsmasq services,,,
|
||||||
|
,Cấu hình vô hiệu hoá ftp server services,,,
|
||||||
|
,Cấu hình vô hiệu hoá tftp server services,,,
|
||||||
|
,Cấu hình vô hiệu hoá web server services,,,
|
||||||
|
,Cấu hình vô hiệu hoá imap and pop3 server services,,,
|
||||||
|
,Cấu hình vô hiệu hoá samba file server services,,,
|
||||||
|
,Cấu hình vô hiệu hoá web proxy server services,,,
|
||||||
|
,Cấu hình vô hiệu hoá snmp services,,,
|
||||||
|
,Cấu hình vô hiệu hoá nis server services,,,
|
||||||
|
,Cấu hình vô hiệu hoá network file system services,,,
|
||||||
|
,Cấu hình vô hiệu hoá rsync services,,,
|
||||||
|
2.2.2.4,Cấu hình mail transfer agents sang chế độ local-only,,,
|
||||||
|
2.2.3,Service Clients,,,
|
||||||
|
2.2.3.1,"Cấu hình xoá bỏ NIS client, rsh client, talk client, telnet client, LDAP client",,,
|
||||||
|
,Cấu hình vô hiệu hoá nis client,,,
|
||||||
|
,Cấu hình vô hiệu hoá rsh client,,,
|
||||||
|
,Cấu hình vô hiệu hoá talk client,,,
|
||||||
|
,Cấu hình vô hiệu hoá telnet client,,,
|
||||||
|
,Cấu hình vô hiệu hoá ldap client,,,
|
||||||
|
,Cấu hình vô hiệu hoá rpc,,,
|
||||||
|
,Cấu hình vô hiệu hoá ftp client,,,
|
||||||
|
2.3,Cấu hình mạng,,,
|
||||||
|
2.3.1,Tham số cấu hình mạng (Host Only),,,
|
||||||
|
2.3.1.1,Cấu hình vô hiệu hoá IP forwarding,,,
|
||||||
|
2.3.1.2,Cấu hình vô hiệu hoá tính năng chuyển hướng gói tin (packet redirect),,,
|
||||||
|
2.3.2,Tham số cấu hình mạng (Host và Router),,,
|
||||||
|
2.3.2.1,Cấu hình từ chối các gói tin với nguồn được định tuyến trước,,,
|
||||||
|
2.3.2.2,Cấu hình từ chối các ICMP redirect message,,,
|
||||||
|
2.3.2.3,Cấu hình từ chối các secure ICMP redirect message,,,
|
||||||
|
2.3.2.4,Ghi lại các gói tin khả nghi (Suspicious Packets),,,
|
||||||
|
2.3.2.5,Cấu hình từ chối các gói tin ICMP request broadcast,,,
|
||||||
|
2.3.2.6,Cấu hình bỏ qua phản hồi ICMP không hợp lệ,,,
|
||||||
|
2.3.2.7,Cấu hình Reverse Path Filtering,,,
|
||||||
|
2.3.2.8,Cấu hình TCP SYN Cookies,,,
|
||||||
|
2.3.3,TCP Wrappers,,,
|
||||||
|
2.3.3.1,Kiêm tra việc cài đặt TCP Wrappers,,,
|
||||||
|
2.3.3.2,Cấu hình file /etc/host.allow,,,
|
||||||
|
2.3.3.3,Cấu hình file /etc/hosts.deny,,,
|
||||||
|
2.3.3.4,Cấu hình quyền truy cập /etc/hosts.allow,,,
|
||||||
|
2.3.3.5,Cấu hình quyền truy cập /etc/hosts.deny,,,
|
||||||
|
2.3.4,Tưởng lửa (áp dụng dành riêng cho ubuntu),,,
|
||||||
|
2.3.4.1,Cài đặt tưởng lửa mềm (soft firewall),,,
|
||||||
|
2.3.4.1.1,Cài đặt Firewall package ,,,
|
||||||
|
2.3.4.2,Cấu hình ufw (UncomplicatedFirewal),,,
|
||||||
|
2.3.4.2.1,Cấu hình kích hoạt ufw,,,
|
||||||
|
2.3.4.2.2,Cấu hình chính sách từ chối mặc định của tường lửa,,,
|
||||||
|
2.3.4.2.3,Cấu hình loopback traffic,,,
|
||||||
|
2.3.4.3,Cấu hình tường lửa nftable,,,
|
||||||
|
2.3.4.3.1,Cấu hình tạo ít nhất 1 bảng trong nftable,,,
|
||||||
|
2.3.4.3.2,Cấu hình các base chain,,,
|
||||||
|
2.3.4.3.3,Cấu hình loopback traffic,,,
|
||||||
|
2.3.4.3.4,Cấu hình chính sách từ chối mặc định của tường lửa,,,
|
||||||
|
2.3.4.3.5,Cấu hình kích hoạt nftable,,,
|
||||||
|
2.3.4.3.6,Cấu hình nftable rule,,,
|
||||||
|
2.3.5,Iptables,,,
|
||||||
|
2.3.5.1,Cấu hình kích hoạt Iptables,,,
|
||||||
|
2.3.5.2,Cấu hình chính sách từ chối mặc định trên firewall,,,
|
||||||
|
2.3.5.3,Cấu hình loopback traffic,,,
|
||||||
|
2.3.5.4,Cấu hình iptables rule cho tất cả các port và protocol đang mở,,,
|
||||||
|
2.4,Logging và Auditing,,,
|
||||||
|
2.4.1,Cấu hình logging,,,
|
||||||
|
2.4.1.1,Cấu hình rsyslog,,,
|
||||||
|
2.4.1.1.1,Cấu hình kích hoạt rsyslog service,,,
|
||||||
|
2.4.1.1.2,Phân quyền đối với file log sinh ra từ rsyslog,,,
|
||||||
|
2.4.1.1.3,Cấu hình lưu trữ log sinh ra từ rsyslog tập trung,,,
|
||||||
|
2.4.1.2,Phân quyền đối với tất cả các file log,,,
|
||||||
|
,Áp dụng đối với Centos,,,
|
||||||
|
2.4.1.2.1,Cấu hình kích hoạt syslog-ng service,,,
|
||||||
|
2.4.1.2.2,Phân quyền đối với file log sinh ra từ syslog-ng,,,
|
||||||
|
2.4.1.2.3,Cấu hình lưu trữ log tập trung,,,
|
||||||
|
2.4.1.3,Cấu hình journald,,,
|
||||||
|
,Áp dụng riêng cho ubuntu,,,
|
||||||
|
2.4.1.3.1,Cấu hình journald để gửi file log đến rsyslog,,,
|
||||||
|
2.4.1.3.2,Cấu hình journald để nén các file log lớn,,,
|
||||||
|
2.4.1.3.3,Cấu hình journald để viết các logfile vào persistent disk,,,
|
||||||
|
2.4.1.3.4,Đảm bảo các quyền trên toàn bộ logfile được cấu hình,,,
|
||||||
|
2.4.1.4,Đảm bảo rsyslog hoặc syslog-ng được cài đặt,,,
|
||||||
|
2.4.1.5,Phân quyền đối với tất cả các file log,,,
|
||||||
|
2.5,"Cấu hình truy cập, xác thực và ủy quyền",,,
|
||||||
|
2.5.1,Cấu hình cron,,,
|
||||||
|
2.5.1.1,Cấu hình kích hoạt cron daemon,,,
|
||||||
|
2.5.1.2,Cấu hình phân quyền cho file /etc/crontab,,,
|
||||||
|
2.5.1.2.1,Cấu hình phân quyền cho file /etc/cron.hourly,,,
|
||||||
|
2.5.1.3,Cấu hình phân quyền cho file /etc/cron.daily,,,
|
||||||
|
2.5.1.4,Cấu hình phân quyền cho file /etc/cron.weekly,,,
|
||||||
|
2.5.1.5,Cấu hình phân quyền cho của file /etc/cron.monthly,,,
|
||||||
|
2.5.1.6,Cấu hình phân quyền cho file /etc/cron.d,,,
|
||||||
|
2.5.1.7,Cấu hình at/cron hạn chế chỉ cho người dùng được ủy quyền,,,
|
||||||
|
2.5.2,Cấu hình máy chủ SSH,,,
|
||||||
|
2.5.2.1,Cấu hình quyền cho file /etc/ssh/sshd_config,,,
|
||||||
|
2.5.2.1.1,Cấu hình quyền cho các file SSH private host,,,
|
||||||
|
2.5.2.2,Cấu hình quyền cho các file SSH public host key,,,
|
||||||
|
2.5.2.3,Cấu hình giao thức SSH sử dụng SSHv2,,,
|
||||||
|
2.5.2.4,Cấu hình LogLevel cho máy chủ SSH,,,
|
||||||
|
2.5.2.5,Cấu hình vô hiệu hoá X11 Forwarding cho máy chủ SSH,,,
|
||||||
|
2.5.2.6,Cấu hình SSH MaxAuthTries,,,
|
||||||
|
2.5.2.7,Cấu hình vô hiệu hoá IgnoreRhosts cho máy chủ SSH,,,
|
||||||
|
2.5.2.8,Cấu hình vô hiệu hoá HostbasedAuthentication cho máy chủ SSH,,,
|
||||||
|
2.5.2.9,Cấu hình vô hiệu hoá đăng nhập bằng root cho máy chủ SSH,,,
|
||||||
|
2.5.2.10,Cấu hình vô hiệu hoá PermitEmptyPasswords cho máy chủ SSH,,,
|
||||||
|
2.5.2.11,Cấu hình vô hiệu hoá PermitUserEnviroment cho máy chủ SSH,,,
|
||||||
|
2.5.2.12,Cấu hình sử dụng các thuật toán mã hoá được cho phép,,,
|
||||||
|
2.5.2.13,Cấu hình các thuật toán MAC được cho phép,,,
|
||||||
|
2.5.2.14,Đảm bảo chỉ có thuật toán trao đổi khóa mạnh được sử dụng,,,
|
||||||
|
2.5.2.15,Cấu hình khoảng thời gian chờ không hoạt động cho máy chủ SSH,,,
|
||||||
|
2.5.2.16,Cấu hình SSH LoginGraceTime,,,
|
||||||
|
2.5.2.17,Cấu hình giới hạn truy cập cho máy chủ SSH,,,
|
||||||
|
2.5.2.18,Cấu hình cảnh báo SSH,,,
|
||||||
|
2.5.2.19,Cấu hình sử dụng SSH PAM,,,
|
||||||
|
2.5.2.20,Cấu hình SSH MaxStartups,,,
|
||||||
|
2.5.2.21,Cấu hình SSH MaxSessions,,,
|
||||||
|
2.5.3,Cấu hình PAM,,,
|
||||||
|
2.5.3.1,Cấu hình điều kiện tạo mật khẩu,,,
|
||||||
|
2.5.3.2,Cấu hình khoá truy cập do nhiều lần nhập mật khẩu thất bại,,,
|
||||||
|
2.5.3.3,Giới hạn việc sử dụng lại mật khẩu,,,
|
||||||
|
2.5.3.4,Cấu hình thuật toán hash mật khẩu mạnh,,,
|
||||||
|
2.5.4,Cấu hình tài khoản người dùng và môi trường,,,
|
||||||
|
,Cấu hình mật khẩu người dùng,,,
|
||||||
|
2.5.4.1,Cấu hình thời gian hết hạn sử dụng mật khẩu,,,
|
||||||
|
2.5.4.2,Cấu hình thời gian tối thiểu giữa những lần thay đổi mật khẩu,,,
|
||||||
|
2.5.4.3,Cấu hình thời gian cảnh báo mật khẩu hết hạn,,,
|
||||||
|
2.5.4.4,Cấu hình thời gian khoá tài khoản không thay mật khẩu sau khi hết hạn,,,
|
||||||
|
2.5.4.5,Đảm bảo thời gian thay đổi mật khẩu lần cuối hợp lệ,,,
|
||||||
|
2.5.4.6, Đảm bảo group mặc định của tài khoản root là GID 0,,,
|
||||||
|
2.5.4.7,Cấu hình tham số user umask mặc định,,,
|
||||||
|
2.5.4.8,Đảm bảo shell timeout mặc định của người dùng là 900 giây hoặc ít hơn,,,
|
||||||
|
2.5.4.9,Cấu hình hạn chế truy cập cho câu lệnh su,,,
|
||||||
|
2.6,System Maintenance,,,
|
||||||
|
2.6.1,Quyền của file hệ thống,,,
|
||||||
|
2.6.1.1,"Cấu hình quyền cho file /etc/passwd /etc/shadow, /etc/group,",,,
|
||||||
|
,"/etc/gshadow, /etc/passwd-, /etc/shadow-, /etc/group-, /etc/gshadow-",,,
|
||||||
|
,Cấu hình phân quyền cho file /etc/passwd,,,
|
||||||
|
,Cấu hình phân quyền cho file /etc/shadow,,,
|
||||||
|
,Cấu hình phân quyền cho file /etc/group,,,
|
||||||
|
,Cấu hình phân quyền cho file /etc/gshadow,,,
|
||||||
|
,Cấu hình phân quyền cho file /etc/passwd-,,,
|
||||||
|
,Cấu hình phân quyền cho file /etc/shadow-,,,
|
||||||
|
,Cấu hình phân quyền cho file /etc/group-,,,
|
||||||
|
,Cấu hình phân quyền cho file /etc/gshadow-,,,
|
||||||
|
,Đảm bảo không có file world-writable tồn tại,,,
|
||||||
|
2.6.1.2,Đảm bảo các file hoặc thư mục không có chủ sở hữu không tồn tại,,,
|
||||||
|
2.6.1.3,Đảm bảo các file hoặc thư mục không có nhóm không tồn tại,,,
|
||||||
|
2.6.2,Thiết lập cho người dùng và nhóm,,,
|
||||||
|
2.6.2.1,Đảm bảo trường mật khẩu không để trống,,,
|
||||||
|
2.62.2,"Đảm bảo không có bản ghi chứa ""+"" trong file /etc/passwd",,,
|
||||||
|
2.6.2.3,Đảm bảo mọi người dùng đều tồn tại thư mục home,,,
|
||||||
|
2.6.2.4,"Đảm bảo không có bản ghi chứa ""+"" trong file /etc/shadow",,,
|
||||||
|
2.6.2.5,"Đảm bảo không có bản ghi chứa ""+"" trong file /etc/group",,,
|
||||||
|
2.6.2.6,Đảm bảo root là tài khoản duy nhất có UID là 0,,,
|
||||||
|
2.6.2.7,Đảm bảo tính toàn vẹn cho biến môi trường PATH của root,,,
|
||||||
|
2.6.2.8,Đảm bảo người dùng sở hữu thư mục home của chính họ,,,
|
||||||
|
2.6.2.9,Đảm bảo các file dot của người dùng không cấp quyền write cho group hoặc world-wide,,,
|
||||||
|
2.6.2.10,Đảm bảo không người dùng nào có file .forward,,,
|
||||||
|
2.6.2.11,Đảm bảo không người dùng nào có file .netrc,,,
|
||||||
|
2.6.2.12,Đảm bảo file netrc của người dùng không cấp quyền cho group hoặc other,,,
|
||||||
|
2.6.2.13,Đảm bảo không người dùng nào có file .rhosts,,,
|
||||||
|
2.6.2.14,Đảm bảo mọi nhóm trong file /etc/passwd tồn tại trong file /etc/group,,,
|
||||||
|
2.6.2.15,Đảm bảo UID không bị lặp,,,
|
||||||
|
2.6.2.16,Đảm bảo GID không bị lặp,,,
|
||||||
|
2.6.2.17,Đảm bảo tên người dùng không bị lặp,,,
|
||||||
|
2.6.2.18,Đảm bảo tên group không lặp,,,
|
||||||
|
2.6.2.19,Đảm bảo không có tài khoản trong shadow group,,,
|
||||||
|
Tổng,,,,
|
||||||
|
@@ -0,0 +1,595 @@
|
|||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"4": "1. Thiết lập ban đầu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"5": "1.1. Cấu hình filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"6": "1.1.1. Cấu hình vô hiệu hoá các filesystem không sử dụng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"7": "1.1.1.1. Cấu hình vô hiệu hoá cramfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"8": "1.1.1.2. Cấu hình vô hiệu hoá freevxfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"9": "1.1.1.3. Cấu hình vô hiệu hoá hfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"10": "1.1.1.4. Cấu hình vô hiệu hoá hfsplus filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"11": "1.1.1.5. Cấu hình vô hiệu hoá jffs2 filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"12": "1.1.1.6. Cấu hình vô hiệu hoá squashfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"13": "1.1.1.7. Cấu hình vô hiệu hoá udf filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"14": "1.1.1.8. Cấu hình vô hiệu hoá usb-storage filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"15": "1.1.2. Cấu hình phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"16": "1.1.2.1. Cấu hình tuỳ chọn nodev cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"17": "1.1.2.2. Cấu hình tuỳ chọn nosuid cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"18": "1.1.2.3. Cấu hình tuỳ chọn noexec cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"19": "1.1.3. Cấu hình phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"20": "1.1.3.1. Cấu hình tuỳ chọn nodev cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"21": "1.1.3.2. Cấu hình tuỳ chọn nosuid cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"22": "1.1.3.3. Cấu hình tuỳ chọn noexec cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"23": "1.1.4. Cấu hình phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"24": "1.1.4.1. Cấu hình tuỳ chọn nodev cho phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"25": "1.1.4.2. Cấu hình tuỳ chọn nosuid cho phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"26": "1.1.5. Cấu hình phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"27": "1.1.5.1. Cấu hình tuỳ chọn nodev cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"28": "1.1.5.2. Cấu hình tuỳ chọn nosuid cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"29": "1.1.5.3. Cấu hình tuỳ chọn noexec cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"30": "1.2. Cấu hình cập nhật phần mềm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"31": "1.2.1. Cấu hình kích hoạt gpgcheck"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"32": "1.3. Kiểm tra tính toàn vẹn của filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"33": "1.3.1. Kiểm tra cài đặt AIDE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"34": "1.3.2. Cấu hình kiểm tra tính toàn vẹn của filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"35": "1.4. Cấu hình khởi động an toàn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"36": "1.4.1. Phân quyền đối với file cấu hình bootloader"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"37": "1.4.2. Cấu hình xác thực khi truy cập rescue mode"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"38": "1.4.3. Cấu hình xác thực khi truy cập single user mode"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"39": "1.4.4. Cấu hình vô hiệu hoá interactive boot"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"40": "1.5. Additional Process Hardening"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"41": "1.5.1. Cấu hình vô hiệu hoá core dump"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"42": "1.5.2. Cấu hình kích hoạt ASLR (address space layout randomization)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"43": "1.5.3. Cấu hình vô hiệu hoá prelink"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"44": "1.6. Kiểm soát nội dung cảnh báo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"45": "1.6.1. Kiểm soát nội dung motd (Message Of The Day)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"46": "1.6.2. Kiểm soát nội dung thông báo khi đăng nhập"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"47": "1.6.3. Kiểm soát nội dung thông báo khi đăng nhập từ xa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"48": "1.6.4. Cấu hình phân quyền đối với file /etc/motd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"49": "1.6.5. Cấu hình phân quyền đối với file /etc/issue"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"50": "1.6.6. Cấu hình phân quyền đối với file /etc/issue.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"51": "1.6.7. Kiểm soát nội dung thông báo khi truy cập GNOME"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"52": "2. Service"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"53": "2.1. Cấu hình Time Synchronization"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"54": "2.1.1. Cấu hình sử dụng chrony"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"55": "2.1.2. Cấu hình sử dụng NTP"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"56": "2.2. Các Service với mục đích riêng biệt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"57": "2.2.1. Cấu hình vô hiệu hoá xinetd services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"58": "2.2.2. Cấu hình vô hiệu hoá chargen services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"59": "2.2.3. Cấu hình vô hiệu hoá daytime services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"60": "2.2.4. Cấu hình vô hiệu hoá discard services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"61": "2.2.5. Cấu hình vô hiệu hoá echo services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"62": "2.2.6. Cấu hình vô hiệu hoá time services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"63": "2.2.7. Cấu hình vô hiệu hoá rsh server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"64": "2.2.8. Cấu hình vô hiệu hoá talk server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"65": "2.2.9. Cấu hình vô hiệu hoá autofs services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"66": "2.2.10. Cấu hình vô hiệu hoá X window server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"67": "2.2.11. Cấu hình vô hiệu hoá avahi daemon services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"68": "2.2.12. Cấu hình vô hiệu hoá cups services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"69": "2.2.13. Cấu hình vô hiệu hoá dhcp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"70": "2.2.14. Cấu hình vô hiệu hoá ldap server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"71": "2.2.15. Cấu hình vô hiệu hoá dns server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"72": "2.2.16. Cấu hình vô hiệu hoá dnsmasq services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"73": "2.2.17. Cấu hình vô hiệu hoá ftp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"74": "2.2.18. Cấu hình vô hiệu hoá tftp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"75": "2.2.19. Cấu hình vô hiệu hoá web server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"76": "2.2.20. Cấu hình vô hiệu hoá imap and pop3 server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"77": "2.2.21. Cấu hình vô hiệu hoá samba file server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"78": "2.2.22. Cấu hình vô hiệu hoá web proxy server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"79": "2.2.23. Cấu hình vô hiệu hoá snmp services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"80": "2.2.24. Cấu hình vô hiệu hoá nis server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"81": "2.2.25. Cấu hình vô hiệu hoá telnet server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"82": "2.2.26. Cấu hình mail transfer agents sang chế độ local-only"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"83": "2.2.27. Cấu hình vô hiệu hoá network file system services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"84": "2.2.28. Cấu hình vô hiệu hoá rpcbind services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"85": "2.2.29. Cấu hình vô hiệu hoá rsync services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"86": "2.3. Service Clients"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"87": "2.3.1. Cấu hình vô hiệu hoá NIS Client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"88": "2.3.2. Cấu hình vô hiệu hoá rsh client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"89": "2.3.3. Cấu hình vô hiệu hoá talk client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"90": "2.3.4. Cấu hình vô hiệu hoá telnet client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"91": "2.3.5. Cấu hình vô hiệu hoá LDAP client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"92": "2.3.6. Cấu hình vô hiệu hoá ftp client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"93": "2.3.7. Cấu hình vô hiệu hoá tftp client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"94": "3. Cấu hình mạng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"95": "3.1. Tham số cấu hình mạng (Host Only)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"96": "3.1.1. Cấu hình vô hiệu hoá IP forwarding"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"97": "3.1.2. Cấu hình vô hiệu hoá tính năng chuyển hướng gói tin (packet redirect)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"98": "3.2. Tham số cấu hình mạng (Host và Router)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"99": "3.2.1. Cấu hình từ chối các gói tin với nguồn được định tuyến trước"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"100": "3.2.2. Cấu hình từ chối các ICMP redirect message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"101": "3.2.3. Cấu hình từ chối các secure ICMP redirect message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"102": "3.2.4. Cấu hình từ chối các gói tin ICMP request broadcast"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"103": "3.2.5. Cấu hình bỏ qua phản hồi ICMP không hợp lệ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"104": "3.2.6. Cấu hình Reverse Path Filtering"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"105": "3.2.7. Cấu hình TCP SYN Cookies"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"106": "3.4. Cấu hình Firewall"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"107": "3.4.1. Cấu hình firewalld"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"108": "3.4.1.1. Cấu hình kích hoạt firewalld"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"109": "3.4.1.3. Cấu hình firewalld rule cho tất cả các port và protocol đang mở"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"110": "3.4.1.4. Cấu hình chính sách từ chối mặc định cho firewalld"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"111": "3.4.2. Iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"112": "3.4.2.1. Cấu hình kích hoạt Iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"113": "3.4.2.3. Cấu hình iptables loopback traffic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"114": "3.4.2.4. Cấu hình iptables rule cho tất cả các port và protocol đang mở"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"115": "3.4.2.5. Cấu hình chính sách từ chối mặc định cho iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"116": "4. Logging và Auditing"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"117": "4.1. Cấu hình logging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"118": "4.1.1. Cấu hình rsyslog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"119": "4.1.1.1. Cấu hình kích hoạt rsyslog service"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"120": "4.1.1.2. Phân quyền đối với file log sinh ra từ rsyslog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"121": "4.1.1.3. Cấu hình lưu trữ log sinh ra từ rsyslog tập trung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"122": "4.1.1.4. Phân quyền đối với tất cả các file log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"123": "5. Cấu hình truy cập, xác thực và ủy quyền"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"124": "5.1. Cấu hình cron"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"125": "5.1.1. Cấu hình kích hoạt cron daemon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"126": "5.1.2. Cấu hình phân quyền cho file /etc/crontab"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"127": "5.1.3. Cấu hình phân quyền cho file /etc/cron.hourly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"128": "5.1.4. Cấu hình phân quyền cho file /etc/cron.daily"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"129": "5.1.5. Cấu hình phân quyền cho file /etc/cron.weekly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"130": "5.1.6. Cấu hình phân quyền cho của file /etc/cron.monthly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"131": "5.1.7. Cấu hình phân quyền cho file /etc/cron.d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"132": "5.1.8. Cấu hình at/cron hạn chế chỉ cho người dùng được ủy quyền"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"133": "5.2. Cấu hình máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"134": "5.2.1. Cấu hình phân quyền cho file /etc/ssh/sshd_config"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"135": "5.2.2. Cấu hình phân quyền cho các file SSH private host key"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"136": "5.2.3. Cấu hình phân quyền cho các file SSH public host key"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"137": "5.2.4. Cấu hình giới hạn truy cập cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"138": "5.2.5. Cấu hình LogLevel cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"139": "5.2.6. Cấu hình sử dụng SSH PAM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"140": "5.2.7. Cấu hình vô hiệu hoá đăng nhập bằng root cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"141": "5.2.8. Cấu hình vô hiệu hoá HostbasedAuthentication cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"142": "5.2.9. Cấu hình vô hiệu hoá PermitEmptyPasswords cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"143": "5.2.10. Cấu hình vô hiệu hoá PermitUserEnviroment cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"144": "5.2.11. Cấu hình vô hiệu hoá IgnoreRhosts cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"145": "5.2.12. Cấu hình vô hiệu hoá X11 Forwarding cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"146": "5.2.13. Cấu hình vô hiệu hoá SSH AllowTcpForwarding"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"147": "5.2.14. Cấu hình cảnh báo SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"148": "5.2.15. Cấu hình SSH MaxAuthTries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"149": "5.2.16. Cấu hình SSH MaxStartups"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"150": "5.2.17. Cấu hình SSH MaxSessions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"151": "5.2.18. Cấu hình SSH LoginGraceTime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"152": "5.2.19. Cấu hình khoảng thời gian chờ không hoạt động cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"153": "5.2.20. Cấu hình các thuật toán MAC được cho phép"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"154": "5.3. Cấu hình PAM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"155": "5.3.1. Cấu hình điều kiện tạo mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"156": "5.3.2. Cấu hình khoá truy cập do nhiều lần nhập mật khẩu thất bại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"157": "5.3.3. Giới hạn việc sử dụng lại mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"158": "5.3.4. Cấu hình thuật toán hash mật khẩu sang SHA-512"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"159": "5.4. Cấu hình tài khoản người dùng và môi trường"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"160": "5.4.1. Cấu hình mật khẩu người dùng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"161": "5.4.1.1. Cấu hình thời gian hết hạn sử dụng mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"162": "5.4.1.2. Cấu hình thời gian tối thiểu giữa những lần thay đổi mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"163": "5.4.1.3. Cấu hình thời gian cảnh báo mật khẩu hết hạn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"164": "5.4.1.4. Cấu hình thời gian khoá tài khoản không thay mật khẩu sau khi hết hạn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"165": "5.4.1.5. Đảm bảo thời gian thay đổi mật khẩu lần cuối hợp lệ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"166": "5.4.2. Cấu hình vô hiệu hoá đăng nhập bằng tài khoản hệ thống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"167": "5.4.3. Cấu hình shell timeout mặc định"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"168": "5.4.4. Cấu hình group mặc định của tài khoản root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"169": "5.4.5. Cấu hình user umask mặc định"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"170": "5.4.6. Cấu hình hạn chế truy cập cho câu lệnh su"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"171": "6. System Maintenance"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"172": "6.1. Quyền của file hệ thống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"173": "6.1.1. Cấu hình sticky bit cho tất cả các thư mục dùng chung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"174": "6.1.2. Cấu hình phân quyền cho file /etc/passwd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"175": "6.1.3. Cấu hình phân quyền cho file /etc/shadow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"176": "6.1.4. Cấu hình phân quyền cho file /etc/group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"177": "6.1.5. Cấu hình phân quyền cho file /etc/gshadow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"178": "6.1.6. Cấu hình phân quyền cho file /etc/passwd-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"179": "6.1.7. Cấu hình phân quyền cho file /etc/shadow-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"180": "6.1.8. Cấu hình phân quyền cho file /etc/group-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"181": "6.1.9. Cấu hình phân quyền cho file /etc/gshadow-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"182": "6.1.10. Đảm bảo không có file world-writable tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"183": "6.1.11. Đảm bảo các file hoặc thư mục không có chủ sở hữu không tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"184": "6.1.12. Đảm bảo các file hoặc thư mục không có nhóm không tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"185": "6.2. Thiết lập cho người dùng và nhóm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"186": "6.2.1. Đảm bảo trường mật khẩu không để trống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"187": "6.2.2. Đảm bảo mọi nhóm trong file /etc/passwd tồn tại trong file /etc/group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"188": "6.2.3. Đảm bảo UID không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"189": "6.2.4. Đảm bảo GID không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"190": "6.2.5. Đảm bảo tên người dùng không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"191": "6.2.6. Đảm bảo tên group không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"192": "6.2.7. Đảm bảo tính toàn vẹn cho biến môi trường PATH của root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"193": "6.2.8. Đảm bảo root là tài khoản duy nhất có UID là 0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"194": "6.2.9. Đảm bảo mọi người dùng đều tồn tại thư mục home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"195": "6.2.10. Đảm bảo người dùng sở hữu thư mục home của chính họ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"196": "6.2.11. Đảm bảo quyền thư mục home của người dùng có mức bảo mật cao"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"197": "6.2.12. Đảm bảo các file dot của người dùng không cấp quyền write cho group hoặc other"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"198": "6.2.13. Đảm bảo không người dùng nào có file .forward"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"199": "6.2.14. Đảm bảo không người dùng nào có file .netrc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"200": "6.2.15. Đảm bảo không người dùng nào có file .rhosts"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,484 @@
|
|||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"7": "1.1.1.1. Cấu hình vô hiệu hoá cramfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"8": "1.1.1.2. Cấu hình vô hiệu hoá freevxfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"9": "1.1.1.3. Cấu hình vô hiệu hoá hfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"10": "1.1.1.4. Cấu hình vô hiệu hoá hfsplus filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"11": "1.1.1.5. Cấu hình vô hiệu hoá jffs2 filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"12": "1.1.1.6. Cấu hình vô hiệu hoá squashfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"13": "1.1.1.7. Cấu hình vô hiệu hoá udf filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"14": "1.1.1.8. Cấu hình vô hiệu hoá usb storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"16": "1.1.2.1. Cấu hình tuỳ chọn nodev cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"17": "1.1.2.2. Cấu hình tuỳ chọn nosuid cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"18": "1.1.2.3. Cấu hình tuỳ chọn noexec cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"20": "1.1.3.1. Cấu hình tuỳ chọn nodev cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"21": "1.1.3.2. Cấu hình tuỳ chọn nosuid cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"22": "1.1.3.3. Cấu hình tuỳ chọn noexec cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"24": "1.1.4.1. Cấu hình tuỳ chọn nodev cho phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"25": "1.1.4.2. Cấu hình tuỳ chọn nosuid cho phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"27": "1.1.5.1. Cấu hình tuỳ chọn nodev cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"28": "1.1.5.2. Cấu hình tuỳ chọn nosuid cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"29": "1.1.5.3. Cấu hình tuỳ chọn noexec cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"31": "1.2.1. Cấu hình kích hoạt gpgcheck"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"33": "1.3.1. Kiểm tra cài đặt AIDE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"34": "1.3.2. Cấu hình kiểm tra tính toàn vẹn của filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"36": "1.4.1. Phân quyền đối với file cấu hình bootloader"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"37": "1.4.2. Cấu hình mật khẩu cho bootloader"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"38": "1.4.3. Cấu hình xác thực khi truy cập single user mode"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"39": "1.4.4. Cấu hình vô hiệu hoá interactive boot"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"41": "1.5.1. Cấu hình vô hiệu hoá core dump"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"42": "1.5.2. Cấu hình kích hoạt ASLR (address space layout randomization)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"43": "1.5.3. Cấu hình vô hiệu hoá prelink"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"45": "1.6.1. Kiểm soát nội dung motd (Message Of The Day)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"46": "1.6.2. Kiểm soát nội dung thông báo khi đăng nhập"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"47": "1.6.3. Kiểm soát nội dung thông báo khi đăng nhập từ xa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"48": "1.6.4. Cấu hình phân quyền đối với file /etc/motd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"49": "1.6.5. Cấu hình phân quyền đối với file /etc/issue"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"50": "1.6.6. Cấu hình phân quyền đối với file /etc/issue.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"51": "1.6.7. Kiểm soát nội dung thông báo khi truy cập GNOME"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"54": "2.1.1. Cấu hình sử dụng chrony"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"55": "2.1.2. Cấu hình sử dụng ntp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"57": "2.2.1. Cấu hình vô hiệu hoá xinetd services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"58": "2.2.2. Cấu hình vô hiệu hoá chargen services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"59": "2.2.3. Cấu hình vô hiệu hoá daytime services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"60": "2.2.4. Cấu hình vô hiệu hoá discard services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"61": "2.2.5. Cấu hình vô hiệu hoá echo services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"62": "2.2.6. Cấu hình vô hiệu hoá time services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"63": "2.2.7. Cấu hình vô hiệu hoá rsh server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"64": "2.2.8. Cấu hình vô hiệu hoá talk server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"65": "2.2.9. Cấu hình vô hiệu hoá autofs services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"66": "2.2.10. Cấu hình vô hiệu hoá X window server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"67": "2.2.11. Cấu hình vô hiệu hoá avahi daemon services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"68": "2.2.12. Cấu hình vô hiệu hoá cups services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"69": "2.2.13. Cấu hình vô hiệu hoá dhcp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"70": "2.2.14. Cấu hình vô hiệu hoá ldap server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"71": "2.2.15. Cấu hình vô hiệu hoá dns server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"72": "2.2.16. Cấu hình vô hiệu hoá dnsmasq services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"73": "2.2.17. Cấu hình vô hiệu hoá ftp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"74": "2.2.18. Cấu hình vô hiệu hoá tftp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"75": "2.2.19. Cấu hình vô hiệu hoá web server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"76": "2.2.20. Cấu hình vô hiệu hoá imap and pop3 server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"77": "2.2.21. Cấu hình vô hiệu hoá samba file server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"78": "2.2.22. Cấu hình vô hiệu hoá web proxy server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"79": "2.2.23. Cấu hình vô hiệu hoá snmp services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"80": "2.2.24. Cấu hình vô hiệu hoá nis server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"81": "2.2.25. Cấu hình vô hiệu hoá telnet server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"82": "2.2.26. Cấu hình mail transfer agents sang chế độ local-only"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"83": "2.2.27. Cấu hình vô hiệu hoá network file system services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"84": "2.2.28. Cấu hình vô hiệu hoá rpcbind services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"85": "2.2.29. Cấu hình vô hiệu hoá rsync services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"87": "2.3.1. Cấu hình vô hiệu hoá nis client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"88": "2.3.2. Cấu hình vô hiệu hoá rsh client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"89": "2.3.3. Cấu hình vô hiệu hoá talk client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"90": "2.3.4. Cấu hình vô hiệu hoá telnet client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"91": "2.3.5. Cấu hình vô hiệu hoá ldap client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"92": "2.3.6. Cấu hình vô hiệu hoá ftp client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"93": "2.3.7. Cấu hình vô hiệu hoá tftp client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"96": "3.1.1. Cấu hình vô hiệu hoá IP forwarding"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"97": "3.1.2. Cấu hình vô hiệu hoá tính năng chuyển hướng gói tin (packet redirect)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"99": "3.2.1. Cấu hình từ chối các gói tin với nguồn được định tuyến trước"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"100": "3.2.2. Cấu hình từ chối các ICMP redirect message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"101": "3.2.3. Cấu hình từ chối các secure ICMP redirect message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"102": "3.2.4. Cấu hình từ chối các gói tin ICMP request broadcast"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"103": "3.2.5. Cấu hình bỏ qua phản hồi ICMP không hợp lệ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"104": "3.2.6. Cấu hình Reverse Path Filtering"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"105": "3.2.7. Cấu hình TCP SYN Cookies"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"108": "3.3.1.1. Cấu hình kích hoạt Iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"109": "3.3.1.2. Cấu hình iptables loopback traffic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"110": "3.3.1.3. Cấu hình iptables rule cho tất cả các port và protocol đang mở"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"111": "3.3.1.4. Cấu hình chính sách từ chối mặc định cho iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"115": "4.1.1.1. Cấu hình kích hoạt rsyslog service"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"116": "4.1.1.2. Phân quyền đối với file log sinh ra từ rsyslog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"117": "4.1.1.3. Cấu hình lưu trữ log sinh ra từ rsyslog tập trung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"118": "4.1.1.4. Phân quyền đối với tất cả các file log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"121": "5.1.1. Cấu hình kích hoạt cron daemon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"122": "5.1.2. Cấu hình phân quyền cho file /etc/crontab"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"123": "5.1.3. Cấu hình phân quyền cho file /etc/cron.hourly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"124": "5.1.4. Cấu hình phân quyền cho file /etc/cron.daily"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"125": "5.1.5. Cấu hình phân quyền cho file /etc/cron.weekly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"126": "5.1.6. Cấu hình phân quyền cho file /etc/cron.monthly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"127": "5.1.7. Cấu hình phân quyền cho file /etc/cron.d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"128": "5.1.8. Cấu hình at/cron hạn chế chỉ cho người dùng được uỷ quyền"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"130": "5.2.1. Cấu hình phân quyền cho file /etc/ssh/sshd_config"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"131": "5.2.2. Cấu hình phân quyền cho các file SSH private host key"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"132": "5.2.3. Cấu hình phân quyền cho các file SSH public host key"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"133": "5.2.4. Cấu hình giới hạn truy cập cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"134": "5.2.5. Cấu hình LogLevel cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"135": "5.2.6. Cấu hình sử dụng SSH PAM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"136": "5.2.7. Cấu hình vô hiệu hoá đăng nhập bằng root cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"137": "5.2.8. Cấu hình vô hiệu hoá HostbasedAuthentication cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"138": "5.2.9. Cấu hình vô hiệu hoá PermitEmptyPasswords cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"139": "5.2.10. Cấu hình vô hiệu hoá PermitUserEnviroment cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"140": "5.2.11. Cấu hình vô hiệu hoá IgnoreRhosts cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"141": "5.2.12. Cấu hình vô hiệu hoá X11 Forwarding cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"142": "5.2.13. Cấu hình vô hiệu hoá SSH AllowTcpForwarding"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"143": "5.2.14. Cấu hình cảnh báo SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"144": "5.2.15. Cấu hình SSH MaxAuthTries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"145": "5.2.16. Cấu hình SSH MaxStartups"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"146": "5.2.17. Cấu hình SSH MaxSessions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"147": "5.2.18. Cấu hình SSH LoginGraceTime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"148": "5.2.19. Cấu hình khoảng thời gian chờ không hoạt động cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"149": "5.2.20. Cấu hình các thuật toán MAC được cho phép"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"151": "5.3.1. Cấu hình điều kiện tạo mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"152": "5.3.2. Cấu hình khoá truy cập do nhiều lần nhập mật khẩu thất bại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"153": "5.3.3. Giới hạn việc sử dụng lại mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"154": "5.3.4. Cấu hình thuật toán hash mật khẩu sang SHA-512"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"157": "5.4.1.1. Cấu hình thời gian hết hạn sử dụng mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"158": "5.4.1.2. Cấu hình thời gian tối thiểu giữa những lần thay đổi mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"159": "5.4.1.3. Cấu hình thời gian cảnh báo mật khẩu hết hạn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"160": "5.4.1.4. Cấu hình thời gian khoá tài khoản không thay mật khẩu sau khi hết hạn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"161": "5.4.1.5. Đảm bảo thời gian thay đổi mật khẩu lần cuối hợp lệ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"162": "5.4.2. Cấu hình vô hiệu hoá đăng nhập bằng tài khoản hệ thống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"163": "5.4.3. Cấu hình shell timeout mặc định"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"164": "5.4.4. Cấu hình group mặc định của tài khoản root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"165": "5.4.5. Cấu hình user umask mặc định"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"166": "5.4.6. Cấu hình hạn chế truy cập cho câu lệnh su"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"169": "6.1.1. Cấu hình sticky bit cho tất cả các thư mục dùng chung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"170": "6.1.2. Cấu hình phân quyền cho file /etc/passwd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"171": "6.1.3. Cấu hình phân quyền cho file /etc/shadow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"172": "6.1.4. Cấu hình phân quyền cho file /etc/group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"173": "6.1.5. Cấu hình phân quyền cho file /etc/gshadow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"174": "6.1.6. Cấu hình phân quyền cho file /etc/passwd-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"175": "6.1.7. Cấu hình phân quyền cho file /etc/shadow-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"176": "6.1.8. Cấu hình phân quyền cho file /etc/group-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"177": "6.1.9. Cấu hình phân quyền cho file /etc/gshadow-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"178": "6.1.10. Đảm bảo không có file world-writable tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"179": "6.1.11. Đảm bảo các file hoặc thư mục không có chủ sở hữu không tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"180": "6.1.12. Đảm bảo các file hoặc thư mục không có nhóm không tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"182": "6.2.1. Đảm bảo trường mật khẩu không để trống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"183": "6.2.2. Đảm bảo mọi nhóm trong file /etc/passwd tồn tại trong file /etc/group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"184": "6.2.3. Đảm bảo UID không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"185": "6.2.4. Đảm bảo GID không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"186": "6.2.5. Đảm bảo tên người dùng không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"187": "6.2.6. Đảm bảo tên group không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"188": "6.2.7. Đảm bảo tính toàn vẹn cho biến môi trường PATH của root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"189": "6.2.8. Đảm bảo root là tài khoản duy nhất có UID là 0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"190": "6.2.9. Đảm bảo mọi người dùng đều tồn tại thư mục home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"191": "6.2.10. Đảm bảo người dùng sở hữu thư mục home của chính họ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"192": "6.2.11. Đảm bảo quyền thư mục home của người dùng có mức bảo mật cao"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"193": "6.2.12. Đảm bảo các file dot của người dùng không cấp quyền write cho group hoặc other"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"194": "6.2.13. Đảm bảo không người dùng nào có file .forward"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"195": "6.2.14. Đảm bảo không người dùng nào có file .netrc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"196": "6.2.15. Đảm bảo không người dùng nào có file .rhosts"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,595 @@
|
|||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"": "1. Thiết lập ban đầu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"": "1.1. Cấu hình filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"": "1.1.1. Cấu hình vô hiệu hoá các filesystem không sử dụng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"7": "1.1.1.1. Cấu hình vô hiệu hoá cramfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"8": "1.1.1.2. Cấu hình vô hiệu hoá freevxfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"9": "1.1.1.3. Cấu hình vô hiệu hoá hfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"10": "1.1.1.4. Cấu hình vô hiệu hoá hfsplus filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"11": "1.1.1.5. Cấu hình vô hiệu hoá jffs2 filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"12": "1.1.1.6. Cấu hình vô hiệu hoá squashfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"13": "1.1.1.7. Cấu hình vô hiệu hoá udf filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"14": "1.1.1.8. Cấu hình vô hiệu hoá usb-storage filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"15": "1.1.2. Cấu hình phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"16": "1.1.2.1. Cấu hình tuỳ chọn nodev cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"17": "1.1.2.2. Cấu hình tuỳ chọn nosuid cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"18": "1.1.2.3. Cấu hình tuỳ chọn noexec cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"19": "1.1.3. Cấu hình phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"20": "1.1.3.1. Cấu hình tuỳ chọn nodev cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"21": "1.1.3.2. Cấu hình tuỳ chọn nosuid cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"22": "1.1.3.3. Cấu hình tuỳ chọn noexec cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"23": "1.1.4. Cấu hình phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"24": "1.1.4.1. Cấu hình tuỳ chọn nodev cho phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"25": "1.1.4.2. Cấu hình tuỳ chọn nosuid cho phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"26": "1.1.5. Cấu hình phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"27": "1.1.5.1. Cấu hình tuỳ chọn nodev cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"28": "1.1.5.2. Cấu hình tuỳ chọn nosuid cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"29": "1.1.5.3. Cấu hình tuỳ chọn noexec cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"30": "1.2. Cấu hình cập nhật phần mềm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"31": "1.2.1. Cấu hình kích hoạt gpgcheck"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"32": "1.3. Kiểm tra tính toàn vẹn của filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"33": "1.3.1. Kiểm tra cài đặt AIDE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"34": "1.3.2. Cấu hình kiểm tra tính toàn vẹn của filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"35": "1.4. Cấu hình khởi động an toàn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"36": "1.4.1. Phân quyền đối với file cấu hình bootloader"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"37": "1.4.2. Cấu hình xác thực khi truy cập rescue mode"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"38": "1.4.3. Cấu hình xác thực khi truy cập single user mode"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"39": "1.4.4. Cấu hình vô hiệu hoá interactive boot"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"40": "1.5. Additional Process Hardening"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"41": "1.5.1. Cấu hình vô hiệu hoá core dump"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"42": "1.5.2. Cấu hình kích hoạt ASLR (address space layout randomization)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"43": "1.5.3. Cấu hình vô hiệu hoá prelink"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"44": "1.6. Kiểm soát nội dung cảnh báo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"45": "1.6.1. Kiểm soát nội dung motd (Message Of The Day)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"46": "1.6.2. Kiểm soát nội dung thông báo khi đăng nhập"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"47": "1.6.3. Kiểm soát nội dung thông báo khi đăng nhập từ xa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"48": "1.6.4. Cấu hình phân quyền đối với file /etc/motd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"49": "1.6.5. Cấu hình phân quyền đối với file /etc/issue"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"50": "1.6.6. Cấu hình phân quyền đối với file /etc/issue.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"51": "1.6.7. Kiểm soát nội dung thông báo khi truy cập GNOME"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"52": "2. Service"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"53": "2.1. Cấu hình Time Synchronization"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"54": "2.1.1. Cấu hình sử dụng chrony"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"55": "2.1.2. Cấu hình sử dụng NTP"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"56": "2.2. Các Service với mục đích riêng biệt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"57": "2.2.1. Cấu hình vô hiệu hoá xinetd services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"58": "2.2.2. Cấu hình vô hiệu hoá chargen services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"59": "2.2.3. Cấu hình vô hiệu hoá daytime services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"60": "2.2.4. Cấu hình vô hiệu hoá discard services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"61": "2.2.5. Cấu hình vô hiệu hoá echo services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"62": "2.2.6. Cấu hình vô hiệu hoá time services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"63": "2.2.7. Cấu hình vô hiệu hoá rsh server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"64": "2.2.8. Cấu hình vô hiệu hoá talk server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"65": "2.2.9. Cấu hình vô hiệu hoá autofs services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"66": "2.2.10. Cấu hình vô hiệu hoá X window server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"67": "2.2.11. Cấu hình vô hiệu hoá avahi daemon services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"68": "2.2.12. Cấu hình vô hiệu hoá cups services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"69": "2.2.13. Cấu hình vô hiệu hoá dhcp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"70": "2.2.14. Cấu hình vô hiệu hoá ldap server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"71": "2.2.15. Cấu hình vô hiệu hoá dns server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"72": "2.2.16. Cấu hình vô hiệu hoá dnsmasq services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"73": "2.2.17. Cấu hình vô hiệu hoá ftp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"74": "2.2.18. Cấu hình vô hiệu hoá tftp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"75": "2.2.19. Cấu hình vô hiệu hoá web server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"76": "2.2.20. Cấu hình vô hiệu hoá imap and pop3 server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"77": "2.2.21. Cấu hình vô hiệu hoá samba file server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"78": "2.2.22. Cấu hình vô hiệu hoá web proxy server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"79": "2.2.23. Cấu hình vô hiệu hoá snmp services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"80": "2.2.24. Cấu hình vô hiệu hoá nis server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"81": "2.2.25. Cấu hình vô hiệu hoá telnet server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"82": "2.2.26. Cấu hình mail transfer agents sang chế độ local-only"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"83": "2.2.27. Cấu hình vô hiệu hoá network file system services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"84": "2.2.28. Cấu hình vô hiệu hoá rpcbind services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"85": "2.2.29. Cấu hình vô hiệu hoá rsync services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"86": "2.3. Service Clients"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"87": "2.3.1. Cấu hình vô hiệu hoá NIS Client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"88": "2.3.2. Cấu hình vô hiệu hoá rsh client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"89": "2.3.3. Cấu hình vô hiệu hoá talk client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"90": "2.3.4. Cấu hình vô hiệu hoá telnet client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"91": "Cấu hình vô hiệu hoá LDAP client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"92": "2.3.6. Cấu hình vô hiệu hoá ftp client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"93": "2.3.7. Cấu hình vô hiệu hoá tftp client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"94": "3. Cấu hình mạng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"95": "3.1. Tham số cấu hình mạng (Host Only)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"96": "3.1.1. Cấu hình vô hiệu hoá IP forwarding"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"97": "3.1.2. Cấu hình vô hiệu hoá tính năng chuyển hướng gói tin (packet redirect)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"98": "3.2. Tham số cấu hình mạng (Host và Router)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"99": "3.2.1. Cấu hình từ chối các gói tin với nguồn được định tuyến trước"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"100": "3.2.2. Cấu hình từ chối các ICMP redirect message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"101": "3.2.3. Cấu hình từ chối các secure ICMP redirect message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"102": "3.2.4. Cấu hình từ chối các gói tin ICMP request broadcast"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"103": "3.2.5. Cấu hình bỏ qua phản hồi ICMP không hợp lệ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"104": "3.2.6. Cấu hình Reverse Path Filtering"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"105": "3.2.7. Cấu hình TCP SYN Cookies"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"106": "3.4. Cấu hình Firewall"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"107": "3.4.1. Cấu hình firewalld"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"108": "3.4.1.1. Cấu hình kích hoạt firewalld"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"109": "3.4.1.3. Cấu hình firewalld rule cho tất cả các port và protocol đang mở"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"110": "3.4.1.4. Cấu hình chính sách từ chối mặc định cho firewalld"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"111": "3.4.2. Iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"112": "3.4.2.1. Cấu hình kích hoạt Iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"113": "3.4.2.3. Cấu hình iptables loopback traffic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"114": "3.4.2.4. Cấu hình iptables rule cho tất cả các port và protocol đang mở"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"115": "3.4.2.5. Cấu hình chính sách từ chối mặc định cho iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"116": "4. Logging và Auditing"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"117": "4.1. Cấu hình logging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"118": "4.1.1. Cấu hình rsyslog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"119": "4.1.1.1. Cấu hình kích hoạt rsyslog service"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"120": "4.1.1.2. Phân quyền đối với file log sinh ra từ rsyslog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"121": "4.1.1.3. Cấu hình lưu trữ log sinh ra từ rsyslog tập trung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"122": "4.1.1.4. Phân quyền đối với tất cả các file log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"123": "5. Cấu hình truy cập, xác thực và ủy quyền"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"124": "5.1. Cấu hình cron"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"125": "5.1.1. Cấu hình kích hoạt cron daemon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"126": "5.1.2. Cấu hình phân quyền cho file /etc/crontab"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"127": "5.1.3. Cấu hình phân quyền cho file /etc/cron.hourly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"128": "5.1.4. Cấu hình phân quyền cho file /etc/cron.daily"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"129": "5.1.5. Cấu hình phân quyền cho file /etc/cron.weekly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"130": "5.1.6. Cấu hình phân quyền cho file /etc/cron.monthly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"131": "5.1.7. Cấu hình phân quyền cho file /etc/cron.d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"132": "5.1.8. Cấu hình at/cron hạn chế chỉ cho người dùng được ủy quyền"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"133": "5.2. Cấu hình máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"134": "5.2.1. Cấu hình phân quyền cho file /etc/ssh/sshd_config"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"135": "5.2.2. Cấu hình phân quyền cho các file SSH private host key"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"136": "5.2.3. Cấu hình phân quyền cho các file SSH public host key"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"137": "5.2.4. Cấu hình giới hạn truy cập cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"138": "5.2.5. Cấu hình LogLevel cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"139": "5.2.6. Cấu hình sử dụng SSH PAM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"140": "5.2.7. Cấu hình vô hiệu hoá đăng nhập bằng root cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"141": "5.2.8. Cấu hình vô hiệu hoá HostbasedAuthentication cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"142": "5.2.9. Cấu hình vô hiệu hoá PermitEmptyPasswords cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"143": "5.2.10. Cấu hình vô hiệu hoá PermitUserEnviroment cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"144": "5.2.11. Cấu hình vô hiệu hoá IgnoreRhosts cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"145": "5.2.12. Cấu hình vô hiệu hoá X11 Forwarding cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"146": "5.2.13. Cấu hình vô hiệu hoá SSH AllowTcpForwarding"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"147": "5.2.14. Cấu hình cảnh báo SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"148": "5.2.15. Cấu hình SSH MaxAuthTries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"149": "5.2.16. Cấu hình SSH MaxStartups"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"150": "5.2.17. Cấu hình SSH MaxSessions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"151": "5.2.18. Cấu hình SSH LoginGraceTime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"152": "5.2.19. Cấu hình khoảng thời gian chờ không hoạt động cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"153": "5.2.20. Cấu hình các thuật toán MAC được cho phép"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"154": "5.3. Cấu hình PAM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"155": "5.3.1. Cấu hình điều kiện tạo mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"156": "5.3.2. Cấu hình khoá truy cập do nhiều lần nhập mật khẩu thất bại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"157": "5.3.3. Giới hạn việc sử dụng lại mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"158": "5.3.4. Cấu hình thuật toán hash mật khẩu sang SHA-512"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"159": "5.4. Cấu hình tài khoản người dùng và môi trường"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"160": "5.4.1. Cấu hình mật khẩu người dùng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"161": "5.4.1.1. Cấu hình thời gian hết hạn sử dụng mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"162": "5.4.1.2. Cấu hình thời gian tối thiểu giữa những lần thay đổi mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"163": "5.4.1.3. Cấu hình thời gian cảnh báo mật khẩu hết hạn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"164": "5.4.1.4. Cấu hình thời gian khoá tài khoản không thay mật khẩu sau khi hết hạn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"165": "5.4.1.5. Đảm bảo thời gian thay đổi mật khẩu lần cuối hợp lệ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"166": "5.4.2. Cấu hình vô hiệu hoá đăng nhập bằng tài khoản hệ thống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"167": "5.4.3. Cấu hình shell timeout mặc định"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"168": "5.4.4. Cấu hình group mặc định của tài khoản root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"169": "5.4.5. Cấu hình user umask mặc định"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"170": "5.4.6. Cấu hình hạn chế truy cập cho câu lệnh su"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"171": "6. System Maintenance"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"172": "6.1. Quyền của file hệ thống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"173": "6.1.1. Cấu hình sticky bit cho tất cả các thư mục dùng chung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"174": "6.1.2. Cấu hình phân quyền cho file /etc/passwd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"175": "6.1.3. Cấu hình phân quyền cho file /etc/shadow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"176": "6.1.4. Cấu hình phân quyền cho file /etc/group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"177": "6.1.5. Cấu hình phân quyền cho file /etc/gshadow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"178": "6.1.6. Cấu hình phân quyền cho file /etc/passwd-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"179": "6.1.7. Cấu hình phân quyền cho file /etc/shadow-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"180": "6.1.8. Cấu hình phân quyền cho file /etc/group-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"181": "6.1.9. Cấu hình phân quyền cho file /etc/gshadow-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"182": "6.1.10. Đảm bảo không có file world-writable tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"183": "6.1.11. Đảm bảo các file hoặc thư mục không có chủ sở hữu không tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"184": "6.1.12. Đảm bảo các file hoặc thư mục không có nhóm không tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"185": "6.2. Thiết lập cho người dùng và nhóm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"186": "6.2.1. Đảm bảo trường mật khẩu không để trống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"187": "6.2.2. Đảm bảo mọi nhóm trong file /etc/passwd tồn tại trong file /etc/group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"188": "6.2.3. Đảm bảo UID không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"189": "6.2.4. Đảm bảo GID không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"190": "6.2.5. Đảm bảo tên người dùng không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"191": "6.2.6. Đảm bảo tên group không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"192": "6.2.7. Đảm bảo tính toàn vẹn cho biến môi trường PATH của root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"193": "6.2.8. Đảm bảo root là tài khoản duy nhất có UID là 0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"194": "6.2.9. Đảm bảo mọi người dùng đều tồn tại thư mục home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"195": "6.2.10. Đảm bảo người dùng sở hữu thư mục home của chính họ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"196": "6.2.11. Đảm bảo quyền thư mục home của người dùng có mức bảo mật cao"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"197": "6.2.12. Đảm bảo các file dot của người dùng không cấp quyền write cho group hoặc other"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"198": "6.2.13. Đảm bảo không người dùng nào có file .forward"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"199": "6.2.14. Đảm bảo không người dùng nào có file .netrc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"200": "6.2.15. Đảm bảo không người dùng nào có file .rhosts"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,577 @@
|
|||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"": "1. Thiết lập ban đầu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"": "1.1. Cấu hình filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"": "1.1.1. Cấu hình vô hiệu hoá các filesystem không sử dụng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"7": "1.1.1.1. Cấu hình vô hiệu hoá cramfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"8": "1.1.1.2. Cấu hình vô hiệu hoá freevxfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"9": "1.1.1.3. Cấu hình vô hiệu hoá hfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"10": "1.1.1.4. Cấu hình vô hiệu hoá hfsplus filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"11": "1.1.1.5. Cấu hình vô hiệu hoá jffs2 filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"12": "1.1.1.6. Cấu hình vô hiệu hoá squashfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"13": "1.1.1.7. Cấu hình vô hiệu hoá udf filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"14": "1.1.8. Cấu hình vô hiệu hoá usb storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"15": "1.1.2. Cấu hình phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"16": "1.1.2.1. Cấu hình tuỳ chọn nodev cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"17": "1.1.2.2. Cấu hình tuỳ chọn nosuid cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"18": "1.1.2.3. Cấu hình tuỳ chọn noexec cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"19": "1.1.3. Cấu hình phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"20": "1.1.3.1. Cấu hình tuỳ chọn nodev cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"21": "1.1.3.2. Cấu hình tuỳ chọn nosuid cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"22": "1.1.3.3. Cấu hình tuỳ chọn noexec cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"23": "1.1.4. Cấu hình phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"24": "1.1.4.1. Cấu hình tuỳ chọn nodev cho phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"25": "1.1.4.2. Cấu hình tuỳ chọn nosuid cho phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"26": "1.1.5. Cấu hình phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"27": "1.1.5.1. Cấu hình tuỳ chọn nodev cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"28": "1.1.5.2. Cấu hình tuỳ chọn nosuid cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"29": "1.1.5.3. Cấu hình tuỳ chọn noexec cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"30": "1.2. Kiểm tra tính toàn vẹn của filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"31": "1.2.1. Kiểm tra cài đặt AIDE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"32": "1.2.2. Cấu hình kiểm tra tính toàn vẹn của filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"33": "1.3. Cấu hình khởi động an toàn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"34": "1.3.1. Phân quyền đối với file cấu hình bootloader"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"35": "1.3.1. Phân quyền đối với file cấu hình bootloader"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"36": "1.3.3. Cấu hình xác thực khi truy cập single user mode"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"37": "1.4. Additional Process Hardening"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"38": "1.4.1. Cấu hình kích hoạt ASLR (address space layout randomization)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"39": "1.4.2. Cấu hình vô hiệu hoá prelink"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"40": "1.4.3. Cấu hình vô hiệu hoá core dump"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"41": "1.5. Kiểm soát nội dung cảnh báo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"42": "1.5.1. Kiểm soát nội dung motd (Message Of The Day)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"43": "1.5.2. Kiểm soát nội dung thông báo khi đăng nhập"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"44": "1.5.3. Kiểm soát nội dung thông báo khi đăng nhập từ xa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"45": "1.5.4. Cấu hình phân quyền đối với file /etc/motd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"46": "1.5.5. Cấu hình phân quyền đối với file /etc/issue"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"47": "1.5.6. Cấu hình phân quyền đối với file /etc/issue.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"48": "1.5.7. Kiểm soát nội dung thông báo khi truy cập GNOME"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"49": "2. Service"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"50": "2.1. Cấu hình Time Synchronization"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"51": "2.1.1. Cấu hình sử dụng chrony"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"52": "2.1.2. Cấu hình sử dụng NTP"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"53": "2.2. Các Service với mục đích riêng biệt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"54": "2.2.1. Cấu hình vô hiệu hoá xinetd services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"55": "2.2.2. Cấu hình vô hiệu hoá autofs services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"56": "2.2.3. Cấu hình vô hiệu hoá X window server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"57": "2.2.4. Cấu hình vô hiệu hoá avahi daemon services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"58": "2.2.5. Cấu hình vô hiệu hoá cups services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"59": "2.2.6. Cấu hình vô hiệu hoá dhcp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"60": "2.2.7. Cấu hình vô hiệu hoá ldap server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"61": "2.2.8. Cấu hình vô hiệu hoá dns server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"62": "2.2.9. Cấu hình vô hiệu hoá dnsmasq services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"63": "2.2.10. Cấu hình vô hiệu hoá ftp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"64": "2.2.11. Cấu hình vô hiệu hoá tftp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"65": "2.2.12. Cấu hình vô hiệu hoá web server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"66": "2.2.13. Cấu hình vô hiệu hoá imap and pop3 server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"67": "2.2.14. Cấu hình vô hiệu hoá samba file server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"68": "2.2.15. Cấu hình vô hiệu hoá web proxy server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"69": "2.2.16. Cấu hình vô hiệu hoá snmp services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"70": "2.2.17. Cấu hình vô hiệu hoá nis server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"71": "2.2.18. Cấu hình mail transfer agents sang chế độ local-only"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"72": "2.2.19. Cấu hình vô hiệu hoá network file system services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"73": "2.2.20. Cấu hình vô hiệu hoá rsync services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"74": "2.3. Service Clients"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"75": "2.3.1. Cấu hình vô hiệu hoá nis client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"76": "2.3.2. Cấu hình vô hiệu hoá rsh client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"77": "2.3.3. Cấu hình vô hiệu hoá talk client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"78": "2.3.4. Cấu hình vô hiệu hoá telnet client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"79": "2.3.5. Cấu hình vô hiệu hoá ldap client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"80": "2.3.6. Cấu hình vô hiệu hoá rpc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"81": "2.3.7. Cấu hình vô hiệu hoá ftp client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"82": "3. Cấu hình mạng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"83": "3.1. Tham số cấu hình mạng (Host Only)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"84": "3.1.1. Cấu hình vô hiệu hoá IP forwarding"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"85": "3.1.2. Cấu hình vô hiệu hoá tính năng chuyển hướng gói tin (packet redirect)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"86": "3.2. Tham số cấu hình mạng (Host và Router)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"87": "3.2.1. Cấu hình từ chối các gói tin với nguồn được định tuyến trước"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"88": "3.2.2. Cấu hình từ chối các ICMP redirect message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"89": "3.2.3. Cấu hình từ chối các secure ICMP redirect message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"90": "3.2.4. Cấu hình từ chối các gói tin ICMP request broadcast"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"91": "3.2.5. Cấu hình bỏ qua phản hồi ICMP không hợp lệ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"92": "3.2.6. Cấu hình Reverse Path Filtering"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"93": "3.2.7. Cấu hình TCP SYN Cookies"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"94": "3.4. Cấu hình Firewall"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"95": "3.4.1. Cấu hình Uncomplicated Firewall"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"96": "3.4.1.1. Cấu hình kích hoạt ufw"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"97": "3.4.1.2. Cấu hình vô hiệu hoá iptables-persistent, nftables khi sử dụng ufw"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"98": "3.4.1.3. Cấu hình ufw loopback traffic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"99": "3.4.1.4. Cấu hình ufw rule cho tất cả các port và protocol đang mở"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"100": "3.4.1.5. Cấu hình chính sách từ chối mặc định cho ufw"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"101": "3.4.2. Iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"102": "3.4.2.1. Cấu hình kích hoạt Iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"103": "3.4.2.2. Cấu hình vô hiệu hoá ufw, nftables khi sử dụng iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"104": "3.4.2.3. Cấu hình iptables loopback traffic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"105": "3.4.2.4. Cấu hình iptables rule cho tất cả các port và protocol đang mở"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"106": "3.4.2.5. Cấu hình chính sách từ chối mặc định cho iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"107": "4. Logging và Auditing"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"108": "4.1. Cấu hình logging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"109": "4.1.1. Cấu hình rsyslog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"110": "4.1.1.1. Cấu hình kích hoạt rsyslog service"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"111": "4.1.1.2. Phân quyền đối với file log sinh ra từ rsyslog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"112": "4.1.1.3. Cấu hình lưu trữ log sinh ra từ rsyslog tập trung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"113": "4.1.1.4. Phân quyền đối với tất cả các file log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"114": "5. Cấu hình truy cập, xác thực và ủy quyền"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"115": "5.1. Cấu hình cron"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"116": "5.1.1. Cấu hình kích hoạt cron daemon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"117": "5.1.2. Cấu hình phân quyền cho file /etc/crontab"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"118": "5.1.3. Cấu hình phân quyền cho file /etc/cron.hourly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"119": "5.1.4. Cấu hình phân quyền cho file /etc/cron.daily"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"120": "5.1.5. Cấu hình phân quyền cho file /etc/cron.weekly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"121": "5.1.6. Cấu hình phân quyền cho của file /etc/cron.monthly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"122": "5.1.7. Cấu hình phân quyền cho file /etc/cron.d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"123": "5.1.8. Cấu hình at/cron hạn chế chỉ cho người dùng được ủy quyền"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"124": "5.2. Cấu hình máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"125": "5.2.1. Cấu hình phân quyền cho file /etc/ssh/sshd_config"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"126": "5.2.2. Cấu hình phân quyền cho các file SSH private host key"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"127": "5.2.3. Cấu hình phân quyền cho các file SSH public host key"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"128": "5.2.4. Cấu hình giới hạn truy cập cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"129": "5.2.5. Cấu hình LogLevel cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"130": "5.2.6. Cấu hình sử dụng SSH PAM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"131": "5.2.7. Cấu hình vô hiệu hoá đăng nhập bằng root cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"132": "5.2.8. Cấu hình vô hiệu hoá HostbasedAuthentication cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"133": "5.2.9. Cấu hình vô hiệu hoá PermitEmptyPasswords cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"134": "5.2.10. Cấu hình vô hiệu hoá PermitUserEnviroment cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"135": "5.2.11. Cấu hình vô hiệu hoá IgnoreRhosts cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"136": "5.2.12. Cấu hình vô hiệu hoá X11 Forwarding cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"137": "5.2.13. Cấu hình sử dụng các thuật toán mã hoá được cho phép"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"138": "5.2.14. Cấu hình các thuật toán MAC được cho phép"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"139": "5.2.15. Cấu hình thuật toán trao đổi khoá được cho phép"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"140": "5.2.16. Cấu hình vô hiệu hoá SSH AllowTcpForwarding"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"141": "5.2.17. Cấu hình cảnh báo SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"142": "5.2.18. Cấu hình SSH MaxAuthTries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"143": "5.2.19. Cấu hình SSH MaxStartups"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"144": "5.2.20. Cấu hình SSH MaxSessions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"145": "5.2.21. Cấu hình SSH LoginGraceTime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"146": "5.2.22. Cấu hình khoảng thời gian chờ không hoạt động cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"147": "5.3. Cấu hình PAM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"148": "5.3.1. Cấu hình điều kiện tạo mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"149": "5.3.2. Cấu hình khoá truy cập do nhiều lần nhập mật khẩu thất bại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"150": "5.3.3. Giới hạn việc sử dụng lại mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"151": "5.3.4. Cấu hình thuật toán hash mật khẩu mạnh"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"152": "5.4. Cấu hình tài khoản người dùng và môi trường"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"153": "5.4.1. Cấu hình mật khẩu người dùng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"154": "5.4.1.1. Cấu hình thời gian tối thiểu giữa những lần thay đổi mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"155": "5.4.1.2. Cấu hình thời gian hết hạn sử dụng mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"156": "5.4.1.3. Cấu hình thời gian cảnh báo mật khẩu hết hạn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"157": "5.4.1.4. Cấu hình thời gian khoá tài khoản không thay mật khẩu sau khi hết hạn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"158": "5.4.1.5. Đảm bảo thời gian thay đổi mật khẩu lần cuối hợp lệ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"159": "5.4.2. Cấu hình vô hiệu hoá đăng nhập bằng tài khoản hệ thống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"160": "5.4.3. Cấu hình group mặc định của tài khoản root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"161": "5.4.4. Cấu hình user umask mặc định"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"162": "5.4.5. Cấu hình shell timeout mặc định"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"163": "5.4.6. Cấu hình hạn chế truy cập cho câu lệnh su"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"164": "6. System Maintenance"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"165": "6.1. Quyền của file hệ thống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"166": "6.1.1. Cấu hình phân quyền cho file /etc/passwd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"167": "6.1.2. Cấu hình phân quyền cho file /etc/shadow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"168": "6.1.3. Cấu hình phân quyền cho file /etc/group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"169": "6.1.4. Cấu hình phân quyền cho file /etc/gshadow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"170": "6.1.5. Cấu hình phân quyền cho file /etc/passwd-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"171": "6.1.6. Cấu hình phân quyền cho file /etc/shadow-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"172": "6.1.7. Cấu hình phân quyền cho file /etc/group-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"173": "6.1.8. Cấu hình phân quyền cho file /etc/gshadow-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"174": "6.1.9. Đảm bảo không có file world-writable tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"175": "6.1.10. Đảm bảo các file hoặc thư mục không có chủ sở hữu không tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"176": "6.1.11. Đảm bảo các file hoặc thư mục không có nhóm không tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"177": "6.2. Thiết lập cho người dùng và nhóm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"178": "6.2.1. Cấu hình tài khoản trong /etc/passwd sử dụng shadowed passwords"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"179": "6.2.2. Đảm bảo trường mật khẩu không để trống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"180": "6.2.3. Đảm bảo mọi nhóm trong file /etc/passwd tồn tại trong file /etc/group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"181": "6.2.4. Đảm bảo shadow group rỗng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"182": "6.2.5. Đảm bảo UID không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"183": "6.2.6. Đảm bảo GID không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"184": "6.2.7. Đảm bảo tên người dùng không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"185": "6.2.8. Đảm bảo tên group không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"186": "6.2.9. Đảm bảo tính toàn vẹn cho biến môi trường PATH của root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"187": "6.2.10. Đảm bảo root là tài khoản duy nhất có UID là 0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"188": "6.2.11. Đảm bảo mọi người dùng đều tồn tại thư mục home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"189": "6.2.12. Đảm bảo người dùng sở hữu thư mục home của chính họ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"190": "6.2.13. Đảm bảo quyền thư mục home của người dùng có mức bảo mật cao"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"191": "6.2.14. Đảm bảo không người dùng nào có file .netrc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"192": "6.2.15. Đảm bảo không người dùng nào có file .forward"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"193": "6.2.16. Đảm bảo không người dùng nào có file .rhosts"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"194": "6.2.17. Đảm bảo các file dot của người dùng không cấp quyền write cho group hoặc other"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,577 @@
|
|||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"": "1. Thiết lập ban đầu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"": "1.1. Cấu hình filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"": "1.1.1. Cấu hình vô hiệu hoá các filesystem không sử dụng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"7": "1.1.1.1. Cấu hình vô hiệu hoá cramfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"8": "1.1.1.2. Cấu hình vô hiệu hoá freevxfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"9": "1.1.1.3. Cấu hình vô hiệu hoá hfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"10": "1.1.1.4. Cấu hình vô hiệu hoá hfsplus filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"11": "1.1.1.5. Cấu hình vô hiệu hoá jffs2 filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"12": "1.1.1.6. Cấu hình vô hiệu hoá squashfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"13": "1.1.1.7. Cấu hình vô hiệu hoá udf filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"14": "1.1.8. Cấu hình vô hiệu hoá usb storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"15": "1.1.2. Cấu hình phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"16": "1.1.2.1. Cấu hình tuỳ chọn nodev cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"17": "1.1.2.2. Cấu hình tuỳ chọn nosuid cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"18": "1.1.2.3. Cấu hình tuỳ chọn noexec cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"19": "1.1.3. Cấu hình phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"20": "1.1.3.1. Cấu hình tuỳ chọn nodev cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"21": "1.1.3.2. Cấu hình tuỳ chọn nosuid cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"22": "1.1.3.3. Cấu hình tuỳ chọn noexec cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"23": "1.1.4. Cấu hình phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"24": "1.1.4.1. Cấu hình tuỳ chọn nodev cho phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"25": "1.1.4.2. Cấu hình tuỳ chọn nosuid cho phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"26": "1.1.5. Cấu hình phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"27": "1.1.5.1. Cấu hình tuỳ chọn nodev cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"28": "1.1.5.2. Cấu hình tuỳ chọn nosuid cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"29": "1.1.5.3. Cấu hình tuỳ chọn noexec cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"30": "1.2. Kiểm tra tính toàn vẹn của filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"31": "1.2.1. Kiểm tra cài đặt AIDE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"32": "1.2.2. Cấu hình kiểm tra tính toàn vẹn của filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"33": "1.3. Cấu hình khởi động an toàn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"34": "1.3.1. Phân quyền đối với file cấu hình bootloader"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"35": "1.3.1. Phân quyền đối với file cấu hình bootloader"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"36": "1.3.3. Cấu hình xác thực khi truy cập single user mode"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"37": "1.4. Additional Process Hardening"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"38": "1.4.1. Cấu hình kích hoạt ASLR (address space layout randomization)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"39": "1.4.2. Cấu hình vô hiệu hoá prelink"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"40": "1.4.3. Cấu hình vô hiệu hoá core dump"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"41": "1.5. Kiểm soát nội dung cảnh báo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"42": "1.5.1. Kiểm soát nội dung motd (Message Of The Day)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"43": "1.5.2. Kiểm soát nội dung thông báo khi đăng nhập"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"44": "1.5.3. Kiểm soát nội dung thông báo khi đăng nhập từ xa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"45": "1.5.4. Cấu hình phân quyền đối với file /etc/motd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"46": "1.5.5. Cấu hình phân quyền đối với file /etc/issue"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"47": "1.5.6. Cấu hình phân quyền đối với file /etc/issue.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"48": "1.5.7. Kiểm soát nội dung thông báo khi truy cập GNOME"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"49": "2. Service"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"50": "2.1. Cấu hình Time Synchronization"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"51": "2.1.1. Cấu hình sử dụng chrony"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"52": "2.1.2. Cấu hình sử dụng NTP"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"53": "2.2. Các Service với mục đích riêng biệt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"54": "2.2.1. Cấu hình vô hiệu hoá xinetd services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"55": "2.2.2. Cấu hình vô hiệu hoá autofs services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"56": "2.2.3. Cấu hình vô hiệu hoá X window server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"57": "2.2.4. Cấu hình vô hiệu hoá avahi daemon services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"58": "2.2.5. Cấu hình vô hiệu hoá cups services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"59": "2.2.6. Cấu hình vô hiệu hoá dhcp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"60": "2.2.7. Cấu hình vô hiệu hoá ldap server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"61": "2.2.8. Cấu hình vô hiệu hoá dns server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"62": "2.2.9. Cấu hình vô hiệu hoá dnsmasq services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"63": "2.2.10. Cấu hình vô hiệu hoá ftp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"64": "2.2.11. Cấu hình vô hiệu hoá tftp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"65": "2.2.12. Cấu hình vô hiệu hoá web server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"66": "2.2.13. Cấu hình vô hiệu hoá imap and pop3 server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"67": "2.2.14. Cấu hình vô hiệu hoá samba file server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"68": "2.2.15. Cấu hình vô hiệu hoá web proxy server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"69": "2.2.16. Cấu hình vô hiệu hoá snmp services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"70": "2.2.17. Cấu hình vô hiệu hoá nis server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"71": "2.2.18. Cấu hình mail transfer agents sang chế độ local-only"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"72": "2.2.19. Cấu hình vô hiệu hoá network file system services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"73": "2.2.20. Cấu hình vô hiệu hoá rsync services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"74": "2.3. Service Clients"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"75": "2.3.1. Cấu hình vô hiệu hoá nis client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"76": "2.3.2. Cấu hình vô hiệu hoá rsh client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"77": "2.3.3. Cấu hình vô hiệu hoá talk client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"78": "2.3.4. Cấu hình vô hiệu hoá telnet client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"79": "2.3.5. Cấu hình vô hiệu hoá ldap client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"80": "2.3.6. Cấu hình vô hiệu hoá rpc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"81": "2.3.7. Cấu hình vô hiệu hoá ftp client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"82": "3. Cấu hình mạng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"83": "3.1. Tham số cấu hình mạng (Host Only)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"84": "3.1.1. Cấu hình vô hiệu hoá IP forwarding"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"85": "3.1.2. Cấu hình vô hiệu hoá tính năng chuyển hướng gói tin (packet redirect)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"86": "3.2. Tham số cấu hình mạng (Host và Router)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"87": "3.2.1. Cấu hình từ chối các gói tin với nguồn được định tuyến trước"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"88": "3.2.2. Cấu hình từ chối các ICMP redirect message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"89": "3.2.3. Cấu hình từ chối các secure ICMP redirect message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"90": "3.2.4. Cấu hình từ chối các gói tin ICMP request broadcast"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"91": "3.2.5. Cấu hình bỏ qua phản hồi ICMP không hợp lệ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"92": "3.2.6. Cấu hình Reverse Path Filtering"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"93": "3.2.7. Cấu hình TCP SYN Cookies"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"94": "3.4. Cấu hình Firewall"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"95": "3.4.1. Cấu hình Uncomplicated Firewall"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"96": "3.4.1.1. Cấu hình kích hoạt ufw"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"97": "3.4.1.2. Cấu hình vô hiệu hoá iptables-persistent, nftables khi sử dụng ufw"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"98": "3.4.1.3. Cấu hình ufw loopback traffic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"99": "3.4.1.4. Cấu hình ufw rule cho tất cả các port và protocol đang mở"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"100": "3.4.1.5. Cấu hình chính sách từ chối mặc định cho ufw"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"101": "3.4.2. Iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"102": "3.4.2.1. Cấu hình kích hoạt Iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"103": "3.4.2.2. Cấu hình vô hiệu hoá ufw, nftables khi sử dụng iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"104": "3.4.2.3. Cấu hình iptables loopback traffic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"105": "3.4.2.4. Cấu hình iptables rule cho tất cả các port và protocol đang mở"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"106": "3.4.2.5. Cấu hình chính sách từ chối mặc định cho iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"107": "4. Logging và Auditing"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"108": "4.1. Cấu hình logging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"109": "4.1.1. Cấu hình rsyslog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"110": "4.1.1.1. Cấu hình kích hoạt rsyslog service"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"111": "4.1.1.2. Phân quyền đối với file log sinh ra từ rsyslog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"112": "4.1.1.3. Cấu hình lưu trữ log sinh ra từ rsyslog tập trung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"113": "4.1.1.4. Phân quyền đối với tất cả các file log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"114": "5. Cấu hình truy cập, xác thực và ủy quyền"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"115": "5.1. Cấu hình cron"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"116": "5.1.1. Cấu hình kích hoạt cron daemon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"117": "5.1.2. Cấu hình phân quyền cho file /etc/crontab"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"118": "5.1.3. Cấu hình phân quyền cho file /etc/cron.hourly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"119": "5.1.4. Cấu hình phân quyền cho file /etc/cron.daily"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"120": "5.1.5. Cấu hình phân quyền cho file /etc/cron.weekly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"121": "5.1.6. Cấu hình phân quyền cho của file /etc/cron.monthly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"122": "5.1.7. Cấu hình phân quyền cho file /etc/cron.d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"123": "5.1.8. Cấu hình at/cron hạn chế chỉ cho người dùng được ủy quyền"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"124": "5.2. Cấu hình máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"125": "5.2.1. Cấu hình phân quyền cho file /etc/ssh/sshd_config"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"126": "5.2.2. Cấu hình phân quyền cho các file SSH private host key"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"127": "5.2.3. Cấu hình phân quyền cho các file SSH public host key"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"128": "5.2.4. Cấu hình giới hạn truy cập cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"129": "5.2.5. Cấu hình LogLevel cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"130": "5.2.6. Cấu hình sử dụng SSH PAM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"131": "5.2.7. Cấu hình vô hiệu hoá đăng nhập bằng root cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"132": "5.2.8. Cấu hình vô hiệu hoá HostbasedAuthentication cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"133": "5.2.9. Cấu hình vô hiệu hoá PermitEmptyPasswords cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"134": "5.2.10. Cấu hình vô hiệu hoá PermitUserEnviroment cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"135": "5.2.11. Cấu hình vô hiệu hoá IgnoreRhosts cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"136": "5.2.12. Cấu hình vô hiệu hoá X11 Forwarding cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"137": "5.2.13. Cấu hình sử dụng các thuật toán mã hoá được cho phép"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"138": "5.2.14. Cấu hình các thuật toán MAC được cho phép"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"139": "5.2.15. Cấu hình thuật toán trao đổi khoá được cho phép"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"140": "5.2.16. Cấu hình vô hiệu hoá SSH AllowTcpForwarding"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"141": "5.2.17. Cấu hình cảnh báo SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"142": "5.2.18. Cấu hình SSH MaxAuthTries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"143": "5.2.19. Cấu hình SSH MaxStartups"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"144": "5.2.20. Cấu hình SSH MaxSessions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"145": "5.2.21. Cấu hình SSH LoginGraceTime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"146": "5.2.22. Cấu hình khoảng thời gian chờ không hoạt động cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"147": "5.3. Cấu hình PAM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"148": "5.3.1. Cấu hình điều kiện tạo mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"149": "5.3.2. Cấu hình khoá truy cập do nhiều lần nhập mật khẩu thất bại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"150": "5.3.3. Giới hạn việc sử dụng lại mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"151": "5.3.4. Cấu hình thuật toán hash mật khẩu mạnh"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"152": "5.4. Cấu hình tài khoản người dùng và môi trường"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"153": "5.4.1. Cấu hình mật khẩu người dùng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"154": "5.4.1.1. Cấu hình thời gian tối thiểu giữa những lần thay đổi mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"155": "5.4.1.2. Cấu hình thời gian hết hạn sử dụng mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"156": "5.4.1.3. Cấu hình thời gian cảnh báo mật khẩu hết hạn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"157": "5.4.1.4. Cấu hình thời gian khoá tài khoản không thay mật khẩu sau khi hết hạn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"158": "5.4.1.5. Đảm bảo thời gian thay đổi mật khẩu lần cuối hợp lệ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"159": "5.4.2. Cấu hình vô hiệu hoá đăng nhập bằng tài khoản hệ thống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"160": "5.4.3. Cấu hình group mặc định của tài khoản root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"161": "5.4.4. Cấu hình user umask mặc định"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"162": "5.4.5. Cấu hình shell timeout mặc định"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"163": "5.4.6. Cấu hình hạn chế truy cập cho câu lệnh su"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"164": "6. System Maintenance"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"165": "6.1. Quyền của file hệ thống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"166": "6.1.1. Cấu hình phân quyền cho file /etc/passwd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"167": "6.1.2. Cấu hình phân quyền cho file /etc/shadow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"168": "6.1.3. Cấu hình phân quyền cho file /etc/group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"169": "6.1.4. Cấu hình phân quyền cho file /etc/gshadow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"170": "6.1.5. Cấu hình phân quyền cho file /etc/passwd-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"171": "6.1.6. Cấu hình phân quyền cho file /etc/shadow-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"172": "6.1.7. Cấu hình phân quyền cho file /etc/group-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"173": "6.1.8. Cấu hình phân quyền cho file /etc/gshadow-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"174": "6.1.9. Đảm bảo không có file world-writable tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"175": "6.1.10. Đảm bảo các file hoặc thư mục không có chủ sở hữu không tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"176": "6.1.11. Đảm bảo các file hoặc thư mục không có nhóm không tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"177": "6.2. Thiết lập cho người dùng và nhóm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"178": "6.2.1. Cấu hình tài khoản trong /etc/passwd sử dụng shadowed passwords"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"179": "6.2.2. Đảm bảo trường mật khẩu không để trống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"180": "6.2.3. Đảm bảo mọi nhóm trong file /etc/passwd tồn tại trong file /etc/group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"181": "6.2.4. Đảm bảo shadow group rỗng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"182": "6.2.5. Đảm bảo UID không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"183": "6.2.6. Đảm bảo GID không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"184": "6.2.7. Đảm bảo tên người dùng không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"185": "6.2.8. Đảm bảo tên group không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"186": "6.2.9. Đảm bảo tính toàn vẹn cho biến môi trường PATH của root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"187": "6.2.10. Đảm bảo root là tài khoản duy nhất có UID là 0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"188": "6.2.11. Đảm bảo mọi người dùng đều tồn tại thư mục home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"189": "6.2.12. Đảm bảo người dùng sở hữu thư mục home của chính họ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"190": "6.2.13. Đảm bảo quyền thư mục home của người dùng có mức bảo mật cao"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"191": "6.2.14. Đảm bảo không người dùng nào có file .netrc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"192": "6.2.15. Đảm bảo không người dùng nào có file .forward"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"193": "6.2.16. Đảm bảo không người dùng nào có file .rhosts"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"194": "6.2.17. Đảm bảo các file dot của người dùng không cấp quyền write cho group hoặc other"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,619 @@
|
|||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"6": "1.1.1. Cấu hình tham số 'Enforce password history'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"7": "1.1.2. Cấu hình tham số 'Maximum password age'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"8": "1.1.3. Cấu hình tham số 'Minimum password age'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"9": "1.1.4. Cấu hình tham số 'Minimum password length'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"10": "1.1.5. Cấu hình chính sách 'Password must meet complexity requirements'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"11": "1.1.6. Cấu hình tham số 'Store passwords using reversible encryption'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"13": "1.2.1. Cấu hình tham số 'Account lockout duration'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"14": "1.2.2. Cấu hình tham số 'Account lockout threshold'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"15": "1.2.3. Cấu hình tham số 'Reset account lockout counter after'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"18": "2.1.1. Cấu hình chính sách 'Access Credential Manager as a trusted caller'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"19": "2.1.2. Cấu hình chính sách 'Access this computer from the network' [Chỉ MS]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"20": "2.1.2. Cấu hình chính sách 'Access this computer from the network' [Chỉ DC]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"21": "2.1.3. Cấu hình chính sách 'Act as part of the operating system'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"22": "2.1.4. Cấu hình chỉ định người dùng được thêm các máy trạm vào Domain [Chỉ DC]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"23": "2.1.5. Cấu hình chính sách 'Adjust memory quotas for a process'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"24": "2.1.6. Cấu hình chính sách 'Allow log on locally'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"25": "2.1.7. Cấu hình chính sách 'Allow log on through Remote Desktop Services' [Chỉ DC]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"26": "2.1.7. Cấu hình chính sách 'Allow log on through Remote Desktop Services' [Chỉ MS]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"27": "2.1.8. Cấu hình sao lưu tệp và thư mục 'Back up files and directories'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"28": "2.1.9. Cấu hình thay đổi thời gian hệ thống 'Change the system time'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"29": "2.1.10. Cấu hình thay đổi thời gian hệ thống 'Change the time zone'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"30": "2.1.11. Cấu hình chính sách 'Create a pagefile'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"31": "2.1.12. Cấu hình chính sách 'Create a token object'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"32": "2.1.13. Cấu hình chính sách 'Create global objects'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"33": "2.1.14. Cấu hình chính sách 'Create permanent shared objects'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"34": "2.1.15. Cấu hình chính sách 'Create symbolic links' [Chỉ DC]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"35": "2.1.15. Cấu hình chính sách 'Create symbolic links' [Chỉ MS]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"36": "2.1.16. Cấu hình chính sách 'Debug programs'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"37": "2.1.17. Cấu hình chính sách 'Deny access to this computer from the network' [Chỉ DC]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"38": "2.1.17. Cấu hình chính sách 'Deny access to this computer from the network' [Chỉ MS]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"39": "2.1.18. Cấu hình chính sách 'Deny log on as a batch job'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"40": "2.1.19. Cấu hình chính sách 'Deny log on as a service'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"41": "2.1.20. Cấu hình chính sách 'Deny log on locally'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"42": "2.1.21. Cấu hình chính sách 'Deny log on through Remote Desktop Services' [Chỉ DC]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"43": "2.1.21. Cấu hình chính sách 'Deny log on through Remote Desktop Services' [Chỉ MS]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"44": "2.1.22. Cấu hình chính sách 'Enable computer and user accounts to be trusted for delegation' [Chỉ DC]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"45": "2.1.22. Cấu hình chính sách 'Enable computer and user accounts to be trusted for delegation' [Chỉ MS]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"46": "2.1.23. Cấu hình chính sách 'Force shutdown from a remote system'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"47": "2.1.24. Cấu hình chính sách 'Generate security audits'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"48": "2.1.25. Cấu hình chính sách 'Impersonate a client after authentication' [Chỉ DC]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"49": "2.1.25. Cấu hình chính sách 'Impersonate a client after authentication' [Chỉ MS]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"50": "2.1.26. Cấu hình chính sách 'Increase scheduling priority'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"51": "2.1.27. Cấu hình chính sách 'Load and unload device drivers'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"52": "2.1.28. Cấu hình chính sách 'Lock pages in memory'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"53": "2.1.29. Cấu hình chính sách 'Manage auditing and security log' [Chỉ DC]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"54": "2.1.29. Cấu hình chính sách 'Manage auditing and security log' [Chỉ MS]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"55": "2.1.30. Cấu hình chính sách 'Modify an object label'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"56": "2.1.31. Cấu hình giá trị 'Modify firmware environment values'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"57": "2.1.32. Cấu hình chính sách 'Perform volume maintenance tasks'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"58": "2.1.33. Cấu hình chính sách 'Profile single process'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"59": "2.1.34. Cấu hình chính sách 'Profile system performance'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"60": "2.1.35. Cấu hình chính sách 'Replace a process level token'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"61": "2.1.36. Cấu hình chính sách 'Restore files and directories'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"62": "2.1.37. Cấu hình chính sách 'Shut down the system'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"63": "2.1.38. Cấu hình chính sách 'Synchronize directory service data' [Chỉ DC]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"64": "2.1.39. Cấu hình chính sách 'Take ownership of files or other objects'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"66": "2.2.1.1. Cấu hình trạng thái tài khoản 'Administrator account status'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"67": "2.2.1.2. Cấu hình chính sách tài khoản 'Block Microsoft accounts'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"68": "2.2.1.3. Cấu hình trạng thái tài khoản 'Guest account status' [Chỉ MS]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"69": "2.2.1.4. Cấu hình chính sách tài khoản 'Limit local account use of blank passwords to console logon only'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"70": "2.2.1.5. Cấu hình thay đổi tên mặc định tài khoản quản trị 'Rename administrator account'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"71": "2.2.1.6. Cấu hình thay đổi tên mặc định tài khoản Guests 'Rename guest account'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"73": "2.2.2.1. Cấu hình chính sách 'Audit: Force audit policy subcategory settings to override audit policy category settings'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"74": "2.2.2.2. Cấu hình chính sách 'Audit: Shut down system immediately if unable to log security audits'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"76": "2.2.3.1. Cấu hình chính sách 'Devices: Allowed to format and eject removable media'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"77": "2.2.3.2. Cấu hình chính sách 'Devices: Prevent users from installing printer drivers'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"79": "2.2.4.1. Cấu hình chính sách 'Domain controller: Allow server operators to schedule tasks' is set to 'Disabled'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"80": "2.2.4.2. Cấu hình chính sách 'Domain controller: Refuse machine account password changes'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"82": "2.2.5.1. Cấu hình chính sách 'Domain member: Digitally encrypt or sign secure channel data (always)'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"83": "2.2.5.2. Cấu hình chính sách 'Domain member: Digitally encrypt secure channel data (when possible)'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"84": "2.2.5.3. Cấu hình chính sách 'Domain member: Digitally sign secure channel data (when possible)'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"85": "2.2.5.4. Cấu hình chính sách 'Domain member: Disable machine account password changes'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"86": "2.2.5.5. Cấu hình chính sách 'Domain member: Maximum machine account password age'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"87": "2.2.5.6. Cấu hình chính sách 'Domain member: Require strong (Windows 2000 or later) session key'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"89": "2.2.6.1. Thiết lập 'Interactive logon: Do not display last user name'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"90": "2.2.6.2. Thiết lập 'CTRL+ALT+DEL'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"91": "2.2.6.3. Thiết lập 'Interactive logon: Machine inactivity limit'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"92": "2.2.6.4. Cấu hình 'Interactive logon: Message text for users attempting to log on'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"93": "2.2.6.5. Thiết lập 'Interactive logon: Message title for users attempting to log on'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"94": "2.2.6.6. Thiết lập 'Interactive logon: Prompt user to change password before expiration'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"95": "2.2.6.7. Thiết lập 'Interactive logon: Require Domain Controller Authentication to unlock workstation' [Chỉ MS]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"96": "2.2.6.8. Thiết lập 'Interactive logon: Smart card removal behavior' is set to 'Lock Workstation'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"98": "2.2.7.1. Thiết lập 'Microsoft network client: Digitally sign communications (if server agrees)'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"99": "2.2.7.2. Thiết lập 'Microsoft network client: Send unencrypted password to third-party SMB servers'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"100": "2.2.7.3. Thiết lập 'Microsoft network client: Digitally sign communications (if server always)'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"102": "2.2.8.1. Thiết lập 'Microsoft network server: Amount of idle time required before suspending session'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"103": "2.2.8.2. Thiết lập 'Microsoft network server: Disconnect clients when logon hours expire'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"104": "2.2.8.3. Thiết lập 'Microsoft network server: Digitally sign communications (always)'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"105": "2.2.8.4. Thiết lập 'Microsoft network server: Digitally sign communications (if client agrees)'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"106": "2.2.8.5. Thiết lập 'Microsoft network server: Server SPN target name validation level' [Chỉ MS]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"108": "2.2.9.1. Thiết lập 'Network access: Allow anonymous SID/Name translation'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"109": "2.2.9.2. Thiết lập 'Network access: Do not allow anonymous enumeration of SAM accounts' [Chỉ MS]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"110": "2.2.9.3. Thiết lập 'Network access: Do not allow anonymous enumeration of SAM accounts and shares' [Chỉ MS]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"111": "2.2.9.4. Thiết lập 'Network access: Let Everyone permissions apply to anonymous users'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"112": "2.2.9.5. Cấu hình 'Network access: Named Pipes that can be accessed anonymously' [Chỉ MS]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"113": "2.2.9.5. Cấu hình 'Network access: Named Pipes that can be accessed anonymously' [Chỉ DC]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"114": "2.2.9.6. Cấu hình 'Network access: Remotely accessible registry paths'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"115": "2.2.9.7. Cấu hình 'Network access: Remotely accessible registry paths and sub-paths'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"116": "2.2.9.8. Cấu hình 'Network access: Restrict anonymous access to Named Pipes and Shares'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"117": "2.2.9.9. Cấu hình 'Network access: Shares that can be accessed anonymously'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"118": "2.2.9.10. Cấu hình 'Network access: Sharing and security model for local accounts'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"120": "2.2.10.1. Thiết lập 'Network security: Allow LocalSystem NULL session fallback'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"121": "2.2.10.2. Thiết lập 'Network Security: Allow PKU2U authentication requests to this computer to use online identities'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"122": "2.2.10.3. Thiết lập 'Network security: Configure encryption types allowed for Kerberos'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"123": "2.2.10.4. Thiết lập 'Network security: Do not store LAN Manager hash value on next password change'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"124": "2.2.10.5. Thiết lập 'Network security: Force logoff when logon hours expire'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"125": "2.2.10.6. Thiết lập 'Network security: Allow Local System to use computer identity for NTLM'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"126": "2.2.10.7. Thiết lập 'Network security: LAN Manager authentication level'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"127": "2.2.10.8. Thiết lập 'Network security: LDAP client signing requirements'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"128": "2.2.10.9. Thiết lập 'Network security: Minimum session security for NTLM SSP based (including secure RPC) clients'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"129": "2.2.10.10. Thiết lập 'Network security: Minimum session security for NTLM SSP based (including secure RPC) servers'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"131": "2.2.11.1. Thiết lập cơ chế 'Shutdown: Allow system to be shut down without having to log on'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"133": "2.2.12.1. Cấu hình chính sách 'System objects: Require case insensitivity for non-Windows subsystems'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"134": "2.2.12.2. Cấu hình chính sách 'System objects: Strengthen default permissions of internal system objects (e.g. Symbolic Links)'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"136": "2.2.13.1. Thiết lập 'User Account Control: Admin Approval Mode for the Built-in Administrator account'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"137": "2.2.13.2. Thiết lập 'User Account Control: Behavior of the elevation prompt for administrators in Admin Approval Mode'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"138": "2.2.13.3. Thiết lập 'User Account Control: Detect application installations and prompt for elevation'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"139": "2.2.13.4. Thiết lập 'User Account Control: Only elevate UIAccess applications that are installed in secure locations'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"140": "2.2.13.5. Thiết lập 'User Account Control: Run all administrators in Admin Approval Mode'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"141": "2.2.13.6. Thiết lập 'User Account Control: Switch to the secure desktop when prompting for elevation'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"142": "2.2.13.7. Thiết lập 'User Account Control: Virtualize file and registry write failures to per-user locations'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"143": "2.2.13.8. Thiết lập 'User Account Control: Behavior of the elevation prompt for standard users' is set to 'Automatically deny elevation requests'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"146": "3.1.1. Thiết lập trạng thái 'Windows Firewall: Domain: Firewall state'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"147": "3.1.2. Thiết lập trạng thái 'Windows Firewall: Domain: Inbound connections'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"148": "3.1.3. Thiết lập trạng thái 'Windows Firewall: Domain: Outbound connections'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"149": "3.1.4. Cấu hình vị trí lưu trữ nhật ký 'Windows Firewall: Domain: Logging: Name'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"150": "3.1.5. Cấu hình kích thước giới hạn 'Windows Firewall: Domain: Logging: Size limit (KB)'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"151": "3.1.6. Thiết lập chính sách 'Windows Firewall: Domain: Logging: Log dropped packets'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"152": "3.1.7. Thiết lập chính sách 'Windows Firewall: Domain: Logging: Log successful connections'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"153": "3.1.8. Thiết lập chính sách 'Windows Firewall: Domain: Settings: Display a notification'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"155": "3.2.1. Thiết lập trạng thái 'Windows Firewall: Private: Firewall state'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"156": "3.2.2. Thiết lập trạng thái 'Windows Firewall: Private: Inbound connections'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"157": "3.2.3. Thiết lập trạng thái 'Windows Firewall: Private: Outbound connections'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"158": "3.2.4. Cấu hình vị trí lưu trữ nhật ký 'Windows Firewall: Private: Logging: Name'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"159": "3.2.5. Cấu hình kích thước giới hạn 'Windows Firewall: Private: Logging: Size limit (KB)'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"160": "3.2.6. Thiết lập chính sách 'Windows Firewall: Private: Logging: Log dropped packets'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"161": "3.2.7. Thiết lập chính sách 'Windows Firewall: Private: Logging: Log successful connections'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"162": "3.2.8. Thiết lập trạng thái 'Windows Firewall: Private: Settings: Display a notification'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"164": "3.3.1. Thiết lập trạng thái 'Windows Firewall: Public: Firewall state'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"165": "3.3.2. Thiết lập trạng thái 'Windows Firewall: Public: Inbound connections'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"166": "3.3.3. Thiết lập trạng thái 'Windows Firewall: Public: Outbound connections'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"167": "3.3.4. Cấu hình vị trí lưu trữ nhật ký 'Windows Firewall: Public: Logging: Name'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"168": "3.3.5. Cấu hình kích thước giới hạn 'Windows Firewall: Public: Logging: Size limit (KB)'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"169": "3.3.6. Thiết lập chính sách 'Windows Firewall: Public: Logging: Log dropped packets'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"170": "3.3.7. Thiết lập chính sách 'Windows Firewall: Public: Logging: Log successful connections'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"171": "3.3.8. Thiết lập trạng thái 'Windows Firewall: Public: Settings: Display a notification'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"172": "3.3.9. Thiết lập trạng thái 'Windows Firewall: Public: Settings: Apply local connection security rules'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"175": "4.1.1. Cấu hình chính sách 'Audit Credential Validation'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"176": "4.1.2. Cấu hình chính sách 'Audit Kerberos Authentication Service' [Chỉ DC]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"177": "4.1.3. Cấu hình chính sách 'Audit Kerberos Service Ticket Operations' [Chỉ DC]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"179": "4.2.1. Cấu hình chính sách 'Audit Application Group Management'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"180": "4.2.2. Cấu hình chính sách 'Audit Computer Account Management' [Chỉ DC]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"181": "4.2.3. Cấu hình chính sách 'Audit Distribution Group Management' [Chỉ DC]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"182": "4.2.4. Cấu hình chính sách 'Audit Other Account Management Events' [Chỉ DC]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"183": "4.2.5. Cấu hình chính sách 'Audit Security Group Management'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"184": "4.2.6. Cấu hình chính sách 'Audit User Account Management'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"186": "4.3.1. Cấu hình chính sách 'Audit Process Creation'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"187": "4.3.2. Cấu hình chính sách 'Audit PNP Activity'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"189": "4.4.1. Cấu hình chính sách 'Audit Directory Service Access' [Chỉ DC]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"190": "4.4.2. Cấu hình chính sách 'Audit Directory Service Changes' [Chỉ DC]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"192": "4.5.1. Cấu hình chính sách 'Audit Account Lockout'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"193": "4.5.2. Cấu hình chính sách 'Audit Logoff'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"194": "4.5.3. Cấu hình chính sách 'Audit Logon'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"195": "4.5.4. Cấu hình chính sách 'Audit Other Logon/Logoff Events'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"196": "4.5.5. Cấu hình chính sách 'Audit Special Logon'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"197": "4.5.6. Cấu hình chính sách 'Audit Group Membership'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"199": "4.6.1. Cấu hình chính sách 'Audit Detailed File Share'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"200": "4.6.2. Cấu hình chính sách 'Audit File Share'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"201": "4.6.3. Cấu hình chính sách 'Audit Other Object Access Events'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"202": "4.6.4. Cấu hình chính sách 'Audit Removable Storage'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"204": "4.7.1. Cấu hình chính sách 'Audit Audit Policy Change'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"205": "4.7.2. Cấu hình chính sách 'Audit Authentication Policy Change'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"206": "4.7.3. Cấu hình chính sách 'Audit Authorization Policy Change'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"207": "4.7.4. Cấu hình chính sách 'Audit MPSSVC Rule-Level Policy Change'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"208": "4.7.5. Cấu hình chính sách 'Audit Other Policy Change Events'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"210": "4.8.1. Cấu hình chính sách 'Audit Sensitive Privilege Use'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"212": "4.9.1. Cấu hình chính sách 'Audit IPsec Driver'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"213": "4.9.2. Cấu hình chính sách 'Audit Other System Events'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"214": "4.9.3. Cấu hình chính sách 'Audit Security State Change'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"215": "4.9.4. Cấu hình chính sách 'Audit Security System Extension'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"216": "4.9.5. Cấu hình chính sách 'Audit System Integrity'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"219": "5.1.1. Cấu hình chính sách 'Turn off app notifications on the lock screen'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"220": "5.1.2. Cấu hình chính sách 'Turn off picture password sign-in'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"221": "5.1.3. Cấu hình chính sách 'Turn on convenience PIN sign-in'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"222": "5.1.4. Cấu hình chính sách 'Block user from showing account details on sign-in'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"223": "5.1.5. Cấu hình chính sách 'Do not display network selection UI'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"224": "5.1.6. Cấu hình chính sách 'Do not enumerate connected users on domain joined computers'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"225": "5.1.7. Cấu hình chính sách 'Enumerate local users on domain-joined computers' [Chỉ MS]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"227": "5.2.1. Cấu hình chính sách 'Require a password when a computer wakes (on battery)'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"228": "5.2.2. Cấu hình chính sách 'Require a password when a computer wakes (plugged in)'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"230": "5.3.1. Cấu hình chính sách 'Disallow Autoplay for non-volume devices'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"231": "5.3.2. Cấu hình chính sách 'Set the default behavior for AutoRun'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"232": "5.3.3. Cấu hình chính sách 'Turn off Autoplay'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"235": "5.4.1.1. Thiết lập chính sách 'Application: Control Event Log behavior when the log file reaches its maximum size'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"236": "5.4.1.2. Thiết lập chính sách 'Application: Specify the maximum log file size (KB)'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"238": "5.4.2.1. Thiết lập chính sách 'Security: Control Event Log behavior when the log file reaches its maximum size'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"239": "5.4.2.2. Thiết lập chính sách 'Security: Specify the maximum log file size (KB)'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"241": "5.4.3.1. Thiết lập chính sách 'Setup: Control Event Log behavior when the log file reaches its maximum size'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"242": "5.4.3.2. Thiết lập chính sách 'Setup: Specify the maximum log file size (KB)'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"244": "5.4.4.1. Thiết lập chính sách 'System: Control Event Log behavior when the log file reaches its maximum size'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"245": "5.4.4.2. Thiết lập chính sách 'System: Specify the maximum log file size (KB)'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"247": "6.1. Cài đặt và cập nhật các bản vá bảo mật"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"249": "7.1. Kiểm tra trạng thái phần mềm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"250": "7.2. Kiểm tra tính năng tự động cập nhật của phần mềm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"251": "7.3. Thực hiện lịch quét định kỳ máy chủ"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
Unnamed: 0,Unnamed: 1,KẾT QUẢ,Unnamed: 3,Unnamed: 4,Unnamed: 5
|
||||||
|
,Hạng mục đánh giá ,Đáp ứng,,,Ghi chú
|
||||||
|
,,Có ,Không,Bắt buộc,
|
||||||
|
1,Thiết lập ban đầu,,,,
|
||||||
|
1.1,Cấu hình filesystem,,,,
|
||||||
|
1.1.1,Cấu hình vô hiệu hoá các filesystem không sử dụng,,,,
|
||||||
|
1.1.1.1,Cấu hình vô hiệu hoá cramfs filesystem,,,,
|
||||||
|
1.1.1.2,Cấu hình vô hiệu hoá freevxfs filesystem,,,,
|
||||||
|
1.1.1.3,Cấu hình vô hiệu hoá hfs filesystem,,,,
|
||||||
|
1.1.1.4,Cấu hình vô hiệu hoá hfsplus filesystem,,,,
|
||||||
|
1.1.1.5,Cấu hình vô hiệu hoá jffs2 filesystem,,,,
|
||||||
|
1.1.1.6,Cấu hình vô hiệu hoá squashfs filesystem,,,,
|
||||||
|
1.1.1.7,Cấu hình vô hiệu hoá udf filesystem,,,,
|
||||||
|
1.1.1.8,Cấu hình vô hiệu hoá usb storage,,,,
|
||||||
|
1.1.2,Cấu hình phân vùng /tmp,,,,
|
||||||
|
1.1.2.1,Cấu hình tuỳ chọn nodev cho phân vùng /tmp,,,,
|
||||||
|
1.1.2.2,Cấu hình tuỳ chọn nosuid cho phân vùng /tmp,,,,
|
||||||
|
1.1.2.3,Cấu hình tuỳ chọn noexec cho phân vùng /tmp,,,,
|
||||||
|
1.1.3,Cấu hình phân vùng /var/tmp,,,,
|
||||||
|
1.1.3.1,Cấu hình tuỳ chọn nodev cho phân vùng /var/tmp,,,,
|
||||||
|
1.1.3.2,Cấu hình tuỳ chọn nosuid cho phân vùng /var/tmp,,,,
|
||||||
|
1.1.3.3,Cấu hình tuỳ chọn noexec cho phân vùng /var/tmp,,,,
|
||||||
|
1.1.4,Cấu hình phân vùng /home,,,,
|
||||||
|
1.1.4.1,Cấu hình tuỳ chọn nodev cho phân vùng /home,,,,
|
||||||
|
1.1.4.2,Cấu hình tuỳ chọn nosuid cho phân vùng /home,,,,
|
||||||
|
1.1.5,Cấu hình phân vùng /dev/shm,,,,
|
||||||
|
1.1.5.1,Cấu hình tuỳ chọn nodev cho phân vùng /dev/shm,,,,
|
||||||
|
1.1.5.2,Cấu hình tuỳ chọn nosuid cho phân vùng /dev/shm,,,,
|
||||||
|
1.1.5.3,Cấu hình tuỳ chọn noexec cho phân vùng /dev/shm,,,,
|
||||||
|
1.2,Kiểm tra tính toàn vẹn của filesystem,,,,
|
||||||
|
1.2.1 ,Kiểm tra cài đặt AIDE,,,,
|
||||||
|
1.2.2,Cấu hình kiểm tra tính toàn vẹn của filesystem,,,,
|
||||||
|
1.3,Cấu hình khởi động an toàn,,,,
|
||||||
|
1.3.1,Phân quyền đối với file cấu hình bootloader,,,,
|
||||||
|
1.3.2,Cấu hình mật khẩu cho bootloader,,,,
|
||||||
|
1.3.3,Cấu hình xác thực khi truy cập single user mode,,,,
|
||||||
|
1.4,Additional Process Hardening,,,,
|
||||||
|
1.4.1 ,Cấu hình kích hoạt ASLR (address space layout randomization),,,,
|
||||||
|
1.4.2,Cấu hình vô hiệu hoá prelink,,,,
|
||||||
|
1.4.3,Cấu hình vô hiệu hoá core dump,,,,
|
||||||
|
1.5,Kiểm soát nội dung cảnh báo,,,,
|
||||||
|
1.5.1,Kiểm soát nội dung motd (Message Of The Day),,,,
|
||||||
|
1.5.2,Kiểm soát nội dung thông báo khi đăng nhập,,,,
|
||||||
|
1.5.3,Kiểm soát nội dung thông báo khi đăng nhập từ xa,,,,
|
||||||
|
1.5.4,Cấu hình phân quyền đối với file /etc/motd,,,,
|
||||||
|
1.5.5,Cấu hình phân quyền đối với file /etc/issue,,,,
|
||||||
|
1.5.6,Cấu hình phân quyền đối với file /etc/issue.net,,,,
|
||||||
|
1.5.7,Kiểm soát nội dung thông báo khi truy cập GNOME,,,,
|
||||||
|
2,Service,,,,
|
||||||
|
2.1,Cấu hình Time Synchronization,,,,
|
||||||
|
2.1.1,Cấu hình sử dụng chrony,,,x,
|
||||||
|
2.1.2,Cấu hình sử dụng ntp,,,x,
|
||||||
|
2.2,Các Service với mục đích riêng biệt,,,,
|
||||||
|
2.2.1,Cấu hình vô hiệu hoá xinetd services,,,,
|
||||||
|
2.2.2,Cấu hình vô hiệu hoá autofs services,,,,
|
||||||
|
2.2.3,Cấu hình vô hiệu hoá X window server services,,,,
|
||||||
|
2.2.4,Cấu hình vô hiệu hoá avahi daemon services,,,,
|
||||||
|
2.2.5,Cấu hình vô hiệu hoá cups services,,,,
|
||||||
|
2.2.6,Cấu hình vô hiệu hoá dhcp server services,,,,
|
||||||
|
2.2.7,Cấu hình vô hiệu hoá ldap server services,,,,
|
||||||
|
2.2.8,Cấu hình vô hiệu hoá dns server services,,,,
|
||||||
|
2.2.9,Cấu hình vô hiệu hoá dnsmasq services,,,,
|
||||||
|
2.2.10,Cấu hình vô hiệu hoá ftp server services,,,,
|
||||||
|
2.2.11,Cấu hình vô hiệu hoá tftp server services,,,,
|
||||||
|
2.2.12,Cấu hình vô hiệu hoá web server services,,,,
|
||||||
|
2.2.13,Cấu hình vô hiệu hoá imap and pop3 server services,,,,
|
||||||
|
2.2.14,Cấu hình vô hiệu hoá samba file server services,,,,
|
||||||
|
2.2.15,Cấu hình vô hiệu hoá web proxy server services,,,,
|
||||||
|
2.2.16,Cấu hình vô hiệu hoá snmp services,,,,
|
||||||
|
2.2.17,Cấu hình vô hiệu hoá nis server services,,,,
|
||||||
|
2.2.18,Cấu hình mail transfer agents sang chế độ local-only,,,,
|
||||||
|
2.2.19,Cấu hình vô hiệu hoá network file system services,,,,
|
||||||
|
2.2.20,Cấu hình vô hiệu hoá rsync services,,,,
|
||||||
|
2.3,Service Clients,,,,
|
||||||
|
2.3.1,Cấu hình vô hiệu hoá nis client,,,,
|
||||||
|
2.3.2,Cấu hình vô hiệu hoá rsh client,,,,
|
||||||
|
2.3.3,Cấu hình vô hiệu hoá talk client,,,,
|
||||||
|
2.3.4,Cấu hình vô hiệu hoá telnet client,,,,
|
||||||
|
2.3.5,Cấu hình vô hiệu hoá ldap client,,,,
|
||||||
|
2.3.6,Cấu hình vô hiệu hoá rpc,,,,
|
||||||
|
2.3.7,Cấu hình vô hiệu hoá ftp client,,,,
|
||||||
|
3,Cấu hình mạng,,,,
|
||||||
|
3.1,Tham số cấu hình mạng (Host Only),,,,
|
||||||
|
3.1.1 ,Cấu hình vô hiệu hoá IP forwarding,,,,
|
||||||
|
3.1.2,Cấu hình vô hiệu hoá tính năng chuyển hướng gói tin (packet redirect),,,,
|
||||||
|
3.2,Tham số cấu hình mạng (Host và Router),,,,
|
||||||
|
3.2.1 ,Cấu hình từ chối các gói tin với nguồn được định tuyến trước,,,,
|
||||||
|
3.2.2,Cấu hình từ chối các ICMP redirect message,,,,
|
||||||
|
3.2.3,Cấu hình từ chối các secure ICMP redirect message,,,,
|
||||||
|
3.2.4,Cấu hình từ chối các gói tin ICMP request broadcast,,,,
|
||||||
|
3.2.5,Cấu hình bỏ qua phản hồi ICMP không hợp lệ,,,,
|
||||||
|
3.2.6,Cấu hình Reverse Path Filtering,,,,
|
||||||
|
3.2.7,Cấu hình TCP SYN Cookies,,,,
|
||||||
|
3.4,Cấu hình Firewall,,,,
|
||||||
|
3.4.1,Cấu hình Uncomplicated Firewall,,,,
|
||||||
|
3.4.1.1,Cấu hình kích hoạt ufw,,,x,
|
||||||
|
3.4.1.2,"Cấu hình vô hiệu hoá iptables-persistent, nftables khi sử dụng ufw",,,x,
|
||||||
|
3.4.1.3,Cấu hình ufw loopback traffic,,,x,
|
||||||
|
3.4.1.4,Cấu hình ufw rule cho tất cả các port và protocol đang mở,,,x,
|
||||||
|
3.4.1.5,Cấu hình chính sách từ chối mặc định cho ufw,,,x,
|
||||||
|
3.4.2,Iptables,,,,
|
||||||
|
3.4.2.1,Cấu hình kích hoạt Iptables,,,x,
|
||||||
|
3.4.2.2,"Cấu hình vô hiệu hoá ufw, nftables khi sử dụng iptables",,,x,
|
||||||
|
3.4.2.3,Cấu hình iptables loopback traffic,,,x,
|
||||||
|
3.4.2.4,Cấu hình iptables rule cho tất cả các port và protocol đang mở,,,x,
|
||||||
|
3.4.2.5,Cấu hình chính sách từ chối mặc định cho iptables,,,x,
|
||||||
|
4,Logging và Auditing,,,,
|
||||||
|
4.1,Cấu hình logging,,,,
|
||||||
|
4.1.1,Cấu hình rsyslog,,,,
|
||||||
|
4.1.1.1 ,Cấu hình kích hoạt rsyslog service,,,,
|
||||||
|
4.1.1.2,Phân quyền đối với file log sinh ra từ rsyslog,,,,
|
||||||
|
4.1.1.3,Cấu hình lưu trữ log sinh ra từ rsyslog tập trung,,,x,
|
||||||
|
4.1.1.4,Phân quyền đối với tất cả các file log,,,,
|
||||||
|
5,"Cấu hình truy cập, xác thực và ủy quyền",,,,
|
||||||
|
5.1,Cấu hình cron,,,,
|
||||||
|
5.1.1 ,Cấu hình kích hoạt cron daemon,,,,
|
||||||
|
5.1.2,Cấu hình phân quyền cho file /etc/crontab,,,,
|
||||||
|
5.1.3,Cấu hình phân quyền cho file /etc/cron.hourly,,,,
|
||||||
|
5.1.4,Cấu hình phân quyền cho file /etc/cron.daily,,,,
|
||||||
|
5.1.5,Cấu hình phân quyền cho file /etc/cron.weekly,,,,
|
||||||
|
5.1.6,Cấu hình phân quyền cho của file /etc/cron.monthly,,,,
|
||||||
|
5.1.7,Cấu hình phân quyền cho file /etc/cron.d,,,,
|
||||||
|
5.1.8,Cấu hình at/cron hạn chế chỉ cho người dùng được ủy quyền,,,,
|
||||||
|
5.2,Cấu hình máy chủ SSH,,,,
|
||||||
|
5.2.1 ,Cấu hình phân quyền cho file /etc/ssh/sshd_config,,,,
|
||||||
|
5.2.2,Cấu hình phân quyền cho các file SSH private host key,,,,
|
||||||
|
5.2.3,Cấu hình phân quyền cho các file SSH public host key,,,,
|
||||||
|
5.2.4,Cấu hình giới hạn truy cập cho máy chủ SSH,,,,
|
||||||
|
5.2.5,Cấu hình LogLevel cho máy chủ SSH,,,,
|
||||||
|
5.2.6,Cấu hình sử dụng SSH PAM,,,,
|
||||||
|
5.2.7,Cấu hình vô hiệu hoá đăng nhập bằng root cho máy chủ SSH,,,,
|
||||||
|
5.2.8,Cấu hình vô hiệu hoá HostbasedAuthentication cho máy chủ SSH,,,,
|
||||||
|
5.2.9,Cấu hình vô hiệu hoá PermitEmptyPasswords cho máy chủ SSH,,,,
|
||||||
|
5.2.10,Cấu hình vô hiệu hoá PermitUserEnviroment cho máy chủ SSH,,,,
|
||||||
|
5.2.11,Cấu hình vô hiệu hoá IgnoreRhosts cho máy chủ SSH,,,,
|
||||||
|
5.2.12,Cấu hình vô hiệu hoá X11 Forwarding cho máy chủ SSH,,,,
|
||||||
|
5.2.13,Cấu hình sử dụng các thuật toán mã hoá được cho phép,,,,
|
||||||
|
5.2.14,Cấu hình các thuật toán MAC được cho phép,,,,
|
||||||
|
5.2.15,Cấu hình thuật toán trao đổi khoá được cho phép,,,,
|
||||||
|
5.2.16,Cấu hình vô hiệu hoá SSH AllowTcpForwarding,,,,
|
||||||
|
5.2.17,Cấu hình cảnh báo SSH,,,,
|
||||||
|
5.2.18,Cấu hình SSH MaxAuthTries,,,,
|
||||||
|
5.2.19,Cấu hình SSH MaxStartups,,,,
|
||||||
|
5.2.20,Cấu hình SSH MaxSessions,,,,
|
||||||
|
5.2.21,Cấu hình SSH LoginGraceTime,,,,
|
||||||
|
5.2.22,Cấu hình khoảng thời gian chờ không hoạt động cho máy chủ SSH,,,,
|
||||||
|
5.3,Cấu hình PAM,,,,
|
||||||
|
5.3.1 ,Cấu hình điều kiện tạo mật khẩu,,,x,
|
||||||
|
5.3.2 ,Cấu hình khoá truy cập do nhiều lần nhập mật khẩu thất bại,,,x,
|
||||||
|
5.3.3 ,Giới hạn việc sử dụng lại mật khẩu,,,,
|
||||||
|
5.3.4 ,Cấu hình thuật toán hash mật khẩu mạnh,,,,
|
||||||
|
5.4,Cấu hình tài khoản người dùng và môi trường,,,,
|
||||||
|
5.4.1 ,Cấu hình mật khẩu người dùng,,,,
|
||||||
|
5.4.1.1 ,Cấu hình thời gian tối thiểu giữa những lần thay đổi mật khẩu,,,x,
|
||||||
|
5.4.1.2 ,Cấu hình thời gian hết hạn sử dụng mật khẩu,,,x,
|
||||||
|
5.4.1.3 ,Cấu hình thời gian cảnh báo mật khẩu hết hạn,,,,
|
||||||
|
5.4.1.4 ,Cấu hình thời gian khoá tài khoản không thay mật khẩu sau khi hết hạn,,,,
|
||||||
|
5.4.1.5,Đảm bảo thời gian thay đổi mật khẩu lần cuối hợp lệ,,,,
|
||||||
|
5.4.2 ,Cấu hình vô hiệu hoá đăng nhập bằng tài khoản hệ thống,,,,
|
||||||
|
5.4.3,Cấu hình group mặc định của tài khoản root,,,,
|
||||||
|
5.4.4,Cấu hình user umask mặc định,,,,
|
||||||
|
5.4.5,Cấu hình shell timeout mặc định,,,,
|
||||||
|
5.4.6,Cấu hình hạn chế truy cập cho câu lệnh su,,,,
|
||||||
|
6,System Maintenance,,,,
|
||||||
|
6.1,Quyền của file hệ thống,,,,
|
||||||
|
6.1.1,Cấu hình phân quyền cho file /etc/passwd,,,,
|
||||||
|
6.1.2,Cấu hình phân quyền cho file /etc/shadow,,,,
|
||||||
|
6.1.3,Cấu hình phân quyền cho file /etc/group,,,,
|
||||||
|
6.1.4,Cấu hình phân quyền cho file /etc/gshadow,,,,
|
||||||
|
6.1.5,Cấu hình phân quyền cho file /etc/passwd-,,,,
|
||||||
|
6.1.6,Cấu hình phân quyền cho file /etc/shadow-,,,,
|
||||||
|
6.1.7,Cấu hình phân quyền cho file /etc/group-,,,,
|
||||||
|
6.1.8,Cấu hình phân quyền cho file /etc/gshadow-,,,,
|
||||||
|
6.1.9,Đảm bảo không có file world-writable tồn tại,,,,
|
||||||
|
6.1.10,Đảm bảo các file hoặc thư mục không có chủ sở hữu không tồn tại,,,,
|
||||||
|
6.1.11,Đảm bảo các file hoặc thư mục không có nhóm không tồn tại,,,,
|
||||||
|
6.2,Thiết lập cho người dùng và nhóm,,,,
|
||||||
|
6.2.1 ,Cấu hình tài khoản trong /etc/passwd sử dụng shadowed passwords,,,,
|
||||||
|
6.2.2,Đảm bảo trường mật khẩu không để trống,,,,
|
||||||
|
6.2.3,Đảm bảo mọi nhóm trong file /etc/passwd tồn tại trong file /etc/group,,,,
|
||||||
|
6.2.4,Đảm bảo shadow group rỗng,,,,
|
||||||
|
6.2.5,Đảm bảo UID không bị lặp,,,,
|
||||||
|
6.2.6,Đảm bảo GID không bị lặp,,,,
|
||||||
|
6.2.7,Đảm bảo tên người dùng không bị lặp,,,,
|
||||||
|
6.2.8,Đảm bảo tên group không bị lặp,,,,
|
||||||
|
6.2.9,Đảm bảo tính toàn vẹn cho biến môi trường PATH của root,,,,
|
||||||
|
6.2.10,Đảm bảo root là tài khoản duy nhất có UID là 0,,,,
|
||||||
|
6.2.11,Đảm bảo mọi người dùng đều tồn tại thư mục home,,,,
|
||||||
|
6.2.12,Đảm bảo người dùng sở hữu thư mục home của chính họ,,,,
|
||||||
|
6.2.13,Đảm bảo quyền thư mục home của người dùng có mức bảo mật cao,,,,
|
||||||
|
6.2.14,Đảm bảo không người dùng nào có file .netrc,,,,
|
||||||
|
6.2.15,Đảm bảo không người dùng nào có file .forward,,,,
|
||||||
|
6.2.16,Đảm bảo không người dùng nào có file .rhosts,,,,
|
||||||
|
6.2.17,Đảm bảo các file dot của người dùng không cấp quyền write cho group hoặc other,,,,
|
||||||
|
,0,0,0,17,
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,206 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
##############################################################################
|
||||||
|
# SCRIPT CHẨN ĐOÁN CHI TIẾT NGUYÊN NHÂN FAILED CÁC TIÊU CHÍ CIS RHEL
|
||||||
|
# Tiêu chí: 1.3.1, 1.3.2, 5.1.6, 5.1.8
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Màu sắc hiển thị
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
echo "=============================================================================="
|
||||||
|
echo " KIỂM TRA & CHẨN ĐOÁN CHI TIẾT CÁC ĐIỀU KIỆN FAILED"
|
||||||
|
echo "=============================================================================="
|
||||||
|
echo -e "Thời gian kiểm tra : $(date)"
|
||||||
|
echo -e "Hostname : $(hostname)"
|
||||||
|
echo -e "Hệ điều hành : $(cat /etc/redhat-release 2>/dev/null || cat /etc/os-release 2>/dev/null | grep PRETTY_NAME | cut -d= -f2 | tr -d '"')"
|
||||||
|
echo "=============================================================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# 1. KIỂM TRA TIÊU CHÍ 1.3.1: Cài đặt AIDE
|
||||||
|
##############################################################################
|
||||||
|
echo -e "${CYAN}--- [TIÊU CHÍ 1.3.1] Kiểm tra cài đặt gói AIDE ---${NC}"
|
||||||
|
echo "Lệnh thực hiện: rpm -q aide"
|
||||||
|
|
||||||
|
aide_pkg=$(rpm -q aide 2>&1)
|
||||||
|
if rpm -q aide >/dev/null 2>&1 || rpm -qa | grep -qi "^aide"; then
|
||||||
|
echo -e "Kết quả: ${GREEN}[PASSED]${NC} Gói AIDE đã được cài đặt ($aide_pkg)."
|
||||||
|
aide_installed=1
|
||||||
|
else
|
||||||
|
echo -e "Kết quả: ${RED}[FAILED]${NC} Gói AIDE chưa được cài đặt trên hệ thống."
|
||||||
|
echo -e "${YELLOW}-> Lý do FAILED:${NC} Lệnh 'rpm -q aide' trả về: $aide_pkg"
|
||||||
|
echo -e "${YELLOW}-> Cách khắc phục:${NC}"
|
||||||
|
echo " yum install aide # (hoặc dnf install aide)"
|
||||||
|
aide_installed=0
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# 2. KIỂM TRA TIÊU CHÍ 1.3.2: Cấu hình lịch chạy kiểm tra tính toàn vẹn (AIDE cron)
|
||||||
|
##############################################################################
|
||||||
|
echo -e "${CYAN}--- [TIÊU CHÍ 1.3.2] Cấu hình kiểm tra tính toàn vẹn filesystem ---${NC}"
|
||||||
|
echo "Yêu cầu: Gói AIDE phải được cài đặt VÀ có cấu hình crontab hoặc systemd timer chạy 'aide --check'."
|
||||||
|
|
||||||
|
cron_check=0
|
||||||
|
timer_check=0
|
||||||
|
|
||||||
|
# Kiểm tra crontab của root và file hệ thống
|
||||||
|
cron_matches=$(crontab -l 2>/dev/null | grep -E '^\s*[^#].*\baide\b.*--check')
|
||||||
|
sys_cron_matches=$(grep -rsE '^\s*[^#].*\baide\b.*--check' /etc/cron* /var/spool/cron 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -n "$cron_matches" ] || [ -n "$sys_cron_matches" ]; then
|
||||||
|
cron_check=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Kiểm tra systemd timer
|
||||||
|
if systemctl is-enabled aidecheck.timer 2>/dev/null | grep -q "^enabled" || \
|
||||||
|
systemctl is-enabled aide-check.timer 2>/dev/null | grep -q "^enabled"; then
|
||||||
|
timer_check=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $aide_installed -eq 1 ] && { [ $cron_check -eq 1 ] || [ $timer_check -eq 1 ]; }; then
|
||||||
|
echo -e "Kết quả: ${GREEN}[PASSED]${NC} Đã cấu hình lịch kiểm tra AIDE định kỳ."
|
||||||
|
else
|
||||||
|
echo -e "Kết quả: ${RED}[FAILED]${NC} Chưa thỏa mãn yêu cầu tiêu chí 1.3.2."
|
||||||
|
echo -e "${YELLOW}-> Chi tiết chẩn đoán:${NC}"
|
||||||
|
if [ $aide_installed -eq 0 ]; then
|
||||||
|
echo " [X] Gói AIDE chưa được cài đặt (Tiền đề bắt buộc)."
|
||||||
|
else
|
||||||
|
echo " [V] Gói AIDE đã được cài đặt."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $cron_check -eq 1 ]; then
|
||||||
|
echo " [V] Tìm thấy lệnh chạy trong cron:"
|
||||||
|
[ -n "$cron_matches" ] && echo " + crontab: $cron_matches"
|
||||||
|
[ -n "$sys_cron_matches" ] && echo " + system cron: $sys_cron_matches"
|
||||||
|
else
|
||||||
|
echo " [X] Không tìm thấy dòng lệnh 'aide --check' trong crontab hoặc /etc/cron*."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $timer_check -eq 1 ]; then
|
||||||
|
echo " [V] Systemd timer aidecheck.timer đang enabled."
|
||||||
|
else
|
||||||
|
echo " [X] Systemd timer aidecheck.timer không được enable."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${YELLOW}-> Cách khắc phục:${NC}"
|
||||||
|
echo " 1. Cài đặt và khởi tạo AIDE:"
|
||||||
|
echo " yum install aide"
|
||||||
|
echo " aide --init"
|
||||||
|
echo " mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz"
|
||||||
|
echo " 2. Thêm lịch chạy vào crontab (chạy lúc 5h sáng mỗi ngày):"
|
||||||
|
echo " crontab -u root -e"
|
||||||
|
echo " # Thêm dòng sau:"
|
||||||
|
echo " 0 5 * * * /usr/sbin/aide --check"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# 3. KIỂM TRA TIÊU CHÍ 5.1.6: Phân quyền file /etc/cron.monthly
|
||||||
|
##############################################################################
|
||||||
|
echo -e "${CYAN}--- [TIÊU CHÍ 5.1.6] Cấu hình phân quyền cho file /etc/cron.monthly ---${NC}"
|
||||||
|
target_file="/etc/cron.monthly"
|
||||||
|
|
||||||
|
if [ ! -e "$target_file" ]; then
|
||||||
|
echo -e "Kết quả: ${GREEN}[PASSED]${NC} Thư mục/file $target_file không tồn tại (Không bị đánh giá lỗi)."
|
||||||
|
else
|
||||||
|
file_stat=$(stat -c "%a:%u:%g" "$target_file" 2>/dev/null)
|
||||||
|
file_mode=$(stat -c "%a" "$target_file" 2>/dev/null)
|
||||||
|
file_owner=$(stat -c "%U (%u)" "$target_file" 2>/dev/null)
|
||||||
|
file_group=$(stat -c "%G (%g)" "$target_file" 2>/dev/null)
|
||||||
|
ls_info=$(ls -ld "$target_file" 2>/dev/null)
|
||||||
|
|
||||||
|
echo "Thông tin hiện tại của $target_file:"
|
||||||
|
echo " + Quyền (Permissions): $file_mode (Yêu cầu: Group và Other KHÔNG được có quyền write - tối đa 755 hoặc 700)"
|
||||||
|
echo " + Chủ sở hữu (Owner) : $file_owner (Yêu cầu: root / 0)"
|
||||||
|
echo " + Nhóm (Group) : $file_group (Yêu cầu: root / 0)"
|
||||||
|
echo " + Chi tiết ls -ld : $ls_info"
|
||||||
|
|
||||||
|
if echo "$file_stat" | grep -qE "^[0-7][0-5][0-5]:0:0$"; then
|
||||||
|
echo -e "Kết quả: ${GREEN}[PASSED]${NC} Phân quyền thỏa mãn điều kiện chuẩn CIS."
|
||||||
|
else
|
||||||
|
echo -e "Kết quả: ${RED}[FAILED]${NC} Phân quyền hoặc chủ sở hữu chưa đạt chuẩn."
|
||||||
|
echo -e "${YELLOW}-> Lý do FAILED:${NC}"
|
||||||
|
if ! echo "$file_mode" | grep -qE "^[0-7][0-5][0-5]$"; then
|
||||||
|
echo " [X] Quyền $file_mode đang cho phép Group hoặc Other có quyền ghi (write)."
|
||||||
|
fi
|
||||||
|
if [ "$(stat -c "%u" "$target_file")" != "0" ] || [ "$(stat -c "%g" "$target_file")" != "0" ]; then
|
||||||
|
echo " [X] Owner hoặc Group không phải là root (0:0)."
|
||||||
|
fi
|
||||||
|
echo -e "${YELLOW}-> Cách khắc phục:${NC}"
|
||||||
|
echo " chown root:root $target_file"
|
||||||
|
echo " chmod og-rwx $target_file # (Đưa về 700 hoặc 750/755 tùy hệ thống)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# 4. KIỂM TRA TIÊU CHÍ 5.1.8: Giới hạn truy cập at/cron (cron.allow, cron.deny...)
|
||||||
|
##############################################################################
|
||||||
|
echo -e "${CYAN}--- [TIÊU CHÍ 5.1.8] Cấu hình at/cron hạn chế chỉ cho người dùng được ủy quyền ---${NC}"
|
||||||
|
echo "Yêu cầu CIS: /etc/cron.allow & /etc/at.allow PHẢI TỒN TẠI | /etc/cron.deny & /etc/at.deny KHÔNG ĐƯỢC TỒN TẠI."
|
||||||
|
|
||||||
|
cron_allow_exist=0
|
||||||
|
cron_deny_exist=0
|
||||||
|
at_allow_exist=0
|
||||||
|
at_deny_exist=0
|
||||||
|
|
||||||
|
[ -f /etc/cron.allow ] && cron_allow_exist=1
|
||||||
|
[ -f /etc/cron.deny ] && cron_deny_exist=1
|
||||||
|
[ -f /etc/at.allow ] && at_allow_exist=1
|
||||||
|
[ -f /etc/at.deny ] && at_deny_exist=1
|
||||||
|
|
||||||
|
echo "Trạng thái các file trên hệ thống:"
|
||||||
|
if [ $cron_allow_exist -eq 1 ]; then
|
||||||
|
echo -e " + /etc/cron.allow : ${GREEN}Tồn tại${NC} ($(ls -l /etc/cron.allow 2>/dev/null | awk '{print $1, $3":"$4}'))"
|
||||||
|
else
|
||||||
|
echo -e " + /etc/cron.allow : ${RED}KHÔNG tồn tại (Bắt buộc phải có)${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $cron_deny_exist -eq 1 ]; then
|
||||||
|
echo -e " + /etc/cron.deny : ${RED}ĐANG TỒN TẠI (Bắt buộc phải XÓA bỏ)${NC} ($(ls -l /etc/cron.deny 2>/dev/null | awk '{print $1, $5" bytes"}'))"
|
||||||
|
else
|
||||||
|
echo -e " + /etc/cron.deny : ${GREEN}Không tồn tại (Đúng)${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $at_allow_exist -eq 1 ]; then
|
||||||
|
echo -e " + /etc/at.allow : ${GREEN}Tồn tại${NC} ($(ls -l /etc/at.allow 2>/dev/null | awk '{print $1, $3":"$4}'))"
|
||||||
|
else
|
||||||
|
echo -e " + /etc/at.allow : ${RED}KHÔNG tồn tại (Bắt buộc phải có)${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $at_deny_exist -eq 1 ]; then
|
||||||
|
echo -e " + /etc/at.deny : ${RED}ĐANG TỒN TẠI (Bắt buộc phải XÓA bỏ)${NC} ($(ls -l /etc/at.deny 2>/dev/null | awk '{print $1, $5" bytes"}'))"
|
||||||
|
else
|
||||||
|
echo -e " + /etc/at.deny : ${GREEN}Không tồn tại (Đúng)${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $cron_allow_exist -eq 1 ] && [ $cron_deny_exist -eq 0 ] && [ $at_allow_exist -eq 1 ] && [ $at_deny_exist -eq 0 ]; then
|
||||||
|
echo -e "Kết quả: ${GREEN}[PASSED]${NC} Cấu hình giới hạn at/cron hoàn toàn tuân thủ CIS."
|
||||||
|
else
|
||||||
|
echo -e "Kết quả: ${RED}[FAILED]${NC} Cấu hình at/cron chưa đạt chuẩn CIS."
|
||||||
|
echo -e "${YELLOW}-> Nguyên nhân chính khiến bạn FAILED:${NC}"
|
||||||
|
[ $cron_deny_exist -eq 1 ] && echo " [X] File /etc/cron.deny vẫn đang tồn tại (dù là file 0 bytes thì CIS vẫn đánh giá FAILED)."
|
||||||
|
[ $at_deny_exist -eq 1 ] && echo " [X] File /etc/at.deny vẫn đang tồn tại."
|
||||||
|
[ $cron_allow_exist -eq 0 ] && echo " [X] Thiếu file /etc/cron.allow."
|
||||||
|
[ $at_allow_exist -eq 0 ] && echo " [X] Thiếu file /etc/at.allow."
|
||||||
|
|
||||||
|
echo -e "${YELLOW}-> Cách khắc phục triệt để:${NC}"
|
||||||
|
echo " # Xóa file deny (Bắt buộc):"
|
||||||
|
echo " rm -f /etc/cron.deny /etc/at.deny"
|
||||||
|
echo " # Tạo file allow (Bắt buộc):"
|
||||||
|
echo " touch /etc/cron.allow /etc/at.allow"
|
||||||
|
echo " # Phân quyền chuẩn 600 root:root:"
|
||||||
|
echo " chown root:root /etc/cron.allow /etc/at.allow"
|
||||||
|
echo " chmod 600 /etc/cron.allow /etc/at.allow"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
echo "=============================================================================="
|
||||||
|
echo "LƯU Ý QUAN TRỌNG VỀ CÔNG CỤ AUDIT:"
|
||||||
|
echo "Nếu bạn chạy script audit được mã hóa (.enc), hãy chắc chắn rằng file mã hóa"
|
||||||
|
echo "đã được tạo lại từ file .sh mới nhất sau khi sửa mã nguồn."
|
||||||
|
echo "=============================================================================="
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
1.1|Cấu hình filesystem
|
||||||
|
1.1.1|Cấu hình vô hiệu hoá các filesystem không sử dụng
|
||||||
|
1.1.1.1|Cấu hình vô hiệu hoá cramfs filesystem
|
||||||
|
1.1.1.2|Cấu hình vô hiệu hoá freevxfs filesystem
|
||||||
|
1.1.1.3|Cấu hình vô hiệu hoá hfs filesystem
|
||||||
|
1.1.1.4|Cấu hình vô hiệu hoá hfsplus filesystem
|
||||||
|
1.1.1.5|Cấu hình vô hiệu hoá jffs2 filesystem
|
||||||
|
1.1.1.6|Cấu hình vô hiệu hoá squashfs filesystem
|
||||||
|
1.1.1.7|Cấu hình vô hiệu hoá udf filesystem
|
||||||
|
1.1.1.8|Cấu hình vô hiệu hoá usb storage
|
||||||
|
1.1.2|Cấu hình phân vùng /tmp
|
||||||
|
1.1.2.1|Cấu hình tuỳ chọn nodev cho phân vùng /tmp
|
||||||
|
1.1.2.2|Cấu hình tuỳ chọn nosuid cho phân vùng /tmp
|
||||||
|
1.1.2.3|Cấu hình tuỳ chọn noexec cho phân vùng /tmp
|
||||||
|
1.1.3|Cấu hình phân vùng /var/tmp
|
||||||
|
1.1.3.1|Cấu hình tuỳ chọn nodev cho phân vùng /var/tmp
|
||||||
|
1.1.3.2|Cấu hình tuỳ chọn nosuid cho phân vùng /var/tmp
|
||||||
|
1.1.3.3|Cấu hình tuỳ chọn noexec cho phân vùng /var/tmp
|
||||||
|
1.1.4|Cấu hình phân vùng /home
|
||||||
|
1.1.4.1|Cấu hình tuỳ chọn nodev cho phân vùng /home
|
||||||
|
1.1.4.2|Cấu hình tuỳ chọn nosuid cho phân vùng /home
|
||||||
|
1.1.5|Cấu hình phân vùng /dev/shm
|
||||||
|
1.1.5.1|Cấu hình tuỳ chọn nodev cho phân vùng /dev/shm
|
||||||
|
1.1.5.2|Cấu hình tuỳ chọn nosuid cho phân vùng /dev/shm
|
||||||
|
1.1.5.3|Cấu hình tuỳ chọn noexec cho phân vùng /dev/shm
|
||||||
|
1.2|Cấu hình cập nhật phần mềm
|
||||||
|
1.2.1|Cấu hình kích hoạt gpgcheck
|
||||||
|
1.3|Kiểm tra tính toàn vẹn của filesystem
|
||||||
|
1.3.1|Kiểm tra cài đặt AIDE
|
||||||
|
1.3.2|Cấu hình kiểm tra tính toàn vẹn của filesystem
|
||||||
|
1.4|Cấu hình khởi động an toàn
|
||||||
|
1.4.1|Phân quyền đối với file cấu hình bootloader
|
||||||
|
1.4.2|Cấu hình mật khẩu cho bootloader
|
||||||
|
1.4.3|Cấu hình xác thực khi truy cập single user mode
|
||||||
|
1.4.4|Cấu hình vô hiệu hoá interactive boot
|
||||||
|
1.5|Additional Process Hardening
|
||||||
|
1.5.1|Cấu hình vô hiệu hoá core dump
|
||||||
|
1.5.2|Cấu hình kích hoạt ASLR (address space layout randomization)
|
||||||
|
1.5.3|Cấu hình vô hiệu hoá prelink
|
||||||
|
1.6|Kiểm soát nội dung cảnh báo
|
||||||
|
1.6.1|Kiểm soát nội dung motd (Message Of The Day)
|
||||||
|
1.6.2|Kiểm soát nội dung thông báo khi đăng nhập
|
||||||
|
1.6.3|Kiểm soát nội dung thông báo khi đăng nhập từ xa
|
||||||
|
1.6.4|Cấu hình phân quyền đối với file /etc/motd
|
||||||
|
1.6.5|Cấu hình phân quyền đối với file /etc/issue
|
||||||
|
1.6.6|Cấu hình phân quyền đối với file /etc/issue.net
|
||||||
|
1.6.7|Kiểm soát nội dung thông báo khi truy cập GNOME
|
||||||
|
2.1|Cấu hình Time Synchronization
|
||||||
|
2.1.1|Cấu hình sử dụng chrony
|
||||||
|
2.1.2|Cấu hình sử dụng ntp
|
||||||
|
2.2|Các Service với mục đích riêng biệt
|
||||||
|
2.2.1|Cấu hình vô hiệu hoá xinetd services
|
||||||
|
2.2.2|Cấu hình vô hiệu hoá chargen services
|
||||||
|
2.2.3|Cấu hình vô hiệu hoá daytime services
|
||||||
|
2.2.4|Cấu hình vô hiệu hoá discard services
|
||||||
|
2.2.5|Cấu hình vô hiệu hoá echo services
|
||||||
|
2.2.6|Cấu hình vô hiệu hoá time services
|
||||||
|
2.2.7|Cấu hình vô hiệu hoá rsh server
|
||||||
|
2.2.8|Cấu hình vô hiệu hoá talk server
|
||||||
|
2.2.9|Cấu hình vô hiệu hoá autofs services
|
||||||
|
2.2.10|Cấu hình vô hiệu hoá X window server services
|
||||||
|
2.2.11|Cấu hình vô hiệu hoá avahi daemon services
|
||||||
|
2.2.12|Cấu hình vô hiệu hoá cups services
|
||||||
|
2.2.13|Cấu hình vô hiệu hoá dhcp server services
|
||||||
|
2.2.14|Cấu hình vô hiệu hoá ldap server services
|
||||||
|
2.2.15|Cấu hình vô hiệu hoá dns server services
|
||||||
|
2.2.16|Cấu hình vô hiệu hoá dnsmasq services
|
||||||
|
2.2.17|Cấu hình vô hiệu hoá ftp server services
|
||||||
|
2.2.18|Cấu hình vô hiệu hoá tftp server services
|
||||||
|
2.2.19|Cấu hình vô hiệu hoá web server services
|
||||||
|
2.2.20|Cấu hình vô hiệu hoá imap and pop3 server services
|
||||||
|
2.2.21|Cấu hình vô hiệu hoá samba file server services
|
||||||
|
2.2.22|Cấu hình vô hiệu hoá web proxy server services
|
||||||
|
2.2.23|Cấu hình vô hiệu hoá snmp services
|
||||||
|
2.2.24|Cấu hình vô hiệu hoá nis server services
|
||||||
|
2.2.25|Cấu hình vô hiệu hoá telnet server services
|
||||||
|
2.2.26|Cấu hình mail transfer agents sang chế độ local-only
|
||||||
|
2.2.27|Cấu hình vô hiệu hoá network file system services
|
||||||
|
2.2.28|Cấu hình vô hiệu hoá rpcbind services
|
||||||
|
2.2.29|Cấu hình vô hiệu hoá rsync services
|
||||||
|
2.3|Service Clients
|
||||||
|
2.3.1|Cấu hình vô hiệu hoá nis client
|
||||||
|
2.3.2|Cấu hình vô hiệu hoá rsh client
|
||||||
|
2.3.3|Cấu hình vô hiệu hoá talk client
|
||||||
|
2.3.4|Cấu hình vô hiệu hoá telnet client
|
||||||
|
2.3.5|Cấu hình vô hiệu hoá ldap client
|
||||||
|
2.3.6|Cấu hình vô hiệu hoá ftp client
|
||||||
|
2.3.7|Cấu hình vô hiệu hoá tftp client
|
||||||
|
3.1|Tham số cấu hình mạng (Host Only)
|
||||||
|
3.1.1|Cấu hình vô hiệu hoá IP forwarding
|
||||||
|
3.1.2|Cấu hình vô hiệu hoá tính năng chuyển hướng gói tin (packet redirect)
|
||||||
|
3.2|Tham số cấu hình mạng (Host và Router)
|
||||||
|
3.2.1|Cấu hình từ chối các gói tin với nguồn được định tuyến trước
|
||||||
|
3.2.2|Cấu hình từ chối các ICMP redirect message
|
||||||
|
3.2.3|Cấu hình từ chối các secure ICMP redirect message
|
||||||
|
3.2.4|Cấu hình từ chối các gói tin ICMP request broadcast
|
||||||
|
3.2.5|Cấu hình bỏ qua phản hồi ICMP không hợp lệ
|
||||||
|
3.2.6|Cấu hình Reverse Path Filtering
|
||||||
|
3.2.7|Cấu hình TCP SYN Cookies
|
||||||
|
3.4|Cấu hình Firewall
|
||||||
|
3.4.1|Cấu hình firewalld
|
||||||
|
3.4.1.1|Cấu hình kích hoạt firewalld
|
||||||
|
3.4.1.3|Cấu hình firewalld rule cho tất cả các port và protocol đang mở
|
||||||
|
3.4.1.4|Cấu hình chính sách từ chối mặc định cho firewalld
|
||||||
|
3.4.2|Iptables
|
||||||
|
3.4.2.1|Cấu hình kích hoạt Iptables
|
||||||
|
3.4.2.3|Cấu hình iptables loopback traffic
|
||||||
|
3.4.2.4|Cấu hình iptables rule cho tất cả các port và protocol đang mở
|
||||||
|
3.4.2.5|Cấu hình chính sách từ chối mặc định cho iptables
|
||||||
|
4.1|Cấu hình logging
|
||||||
|
4.1.1|Cấu hình rsyslog
|
||||||
|
4.1.1.1|Cấu hình kích hoạt rsyslog service
|
||||||
|
4.1.1.2|Phân quyền đối với file log sinh ra từ rsyslog
|
||||||
|
4.1.1.3|Cấu hình lưu trữ log sinh ra từ rsyslog tập trung
|
||||||
|
4.1.1.4|Phân quyền đối với tất cả các file log
|
||||||
|
5.1|Cấu hình cron
|
||||||
|
5.1.1|Cấu hình kích hoạt cron daemon
|
||||||
|
5.1.2|Cấu hình phân quyền cho file /etc/crontab
|
||||||
|
5.1.3|Cấu hình phân quyền cho file /etc/cron.hourly
|
||||||
|
5.1.4|Cấu hình phân quyền cho file /etc/cron.daily
|
||||||
|
5.1.5|Cấu hình phân quyền cho file /etc/cron.weekly
|
||||||
|
5.1.6|Cấu hình phân quyền cho của file /etc/cron.monthly
|
||||||
|
5.1.7|Cấu hình phân quyền cho file /etc/cron.d
|
||||||
|
5.1.8|Cấu hình at/cron hạn chế chỉ cho người dùng được ủy quyền
|
||||||
|
5.2|Cấu hình máy chủ SSH
|
||||||
|
5.2.1|Cấu hình phân quyền cho file /etc/ssh/sshd_config
|
||||||
|
5.2.2|Cấu hình phân quyền cho các file SSH private host key
|
||||||
|
5.2.3|Cấu hình phân quyền cho các file SSH public host key
|
||||||
|
5.2.4|Cấu hình giới hạn truy cập cho máy chủ SSH
|
||||||
|
5.2.5|Cấu hình LogLevel cho máy chủ SSH
|
||||||
|
5.2.6|Cấu hình sử dụng SSH PAM
|
||||||
|
5.2.7|Cấu hình vô hiệu hoá đăng nhập bằng root cho máy chủ SSH
|
||||||
|
5.2.8|Cấu hình vô hiệu hoá HostbasedAuthentication cho máy chủ SSH
|
||||||
|
5.2.9|Cấu hình vô hiệu hoá PermitEmptyPasswords cho máy chủ SSH
|
||||||
|
5.2.10|Cấu hình vô hiệu hoá PermitUserEnviroment cho máy chủ SSH
|
||||||
|
5.2.11|Cấu hình vô hiệu hoá IgnoreRhosts cho máy chủ SSH
|
||||||
|
5.2.12|Cấu hình vô hiệu hoá X11 Forwarding cho máy chủ SSH
|
||||||
|
5.2.13|Cấu hình vô hiệu hoá SSH AllowTcpForwarding
|
||||||
|
5.2.14|Cấu hình cảnh báo SSH
|
||||||
|
5.2.15|Cấu hình SSH MaxAuthTries
|
||||||
|
5.2.16|Cấu hình SSH MaxStartups
|
||||||
|
5.2.17|Cấu hình SSH MaxSessions
|
||||||
|
5.2.18|Cấu hình SSH LoginGraceTime
|
||||||
|
5.2.19|Cấu hình khoảng thời gian chờ không hoạt động cho máy chủ SSH
|
||||||
|
5.2.20|Cấu hình các thuật toán MAC được cho phép
|
||||||
|
5.3|Cấu hình PAM
|
||||||
|
5.3.1|Cấu hình điều kiện tạo mật khẩu
|
||||||
|
5.3.2|Cấu hình khoá truy cập do nhiều lần nhập mật khẩu thất bại
|
||||||
|
5.3.3|Giới hạn việc sử dụng lại mật khẩu
|
||||||
|
5.3.4|Cấu hình thuật toán hash mật khẩu sang SHA-512
|
||||||
|
5.4|Cấu hình tài khoản người dùng và môi trường
|
||||||
|
5.4.1|Cấu hình mật khẩu người dùng
|
||||||
|
5.4.1.1|Cấu hình thời gian hết hạn sử dụng mật khẩu
|
||||||
|
5.4.1.2|Cấu hình thời gian tối thiểu giữa những lần thay đổi mật khẩu
|
||||||
|
5.4.1.3|Cấu hình thời gian cảnh báo mật khẩu hết hạn
|
||||||
|
5.4.1.4|Cấu hình thời gian khoá tài khoản không thay mật khẩu sau khi hết hạn
|
||||||
|
5.4.1.5|Đảm bảo thời gian thay đổi mật khẩu lần cuối hợp lệ
|
||||||
|
5.4.2|Cấu hình vô hiệu hoá đăng nhập bằng tài khoản hệ thống
|
||||||
|
5.4.3|Cấu hình shell timeout mặc định
|
||||||
|
5.4.4|Cấu hình group mặc định của tài khoản root
|
||||||
|
5.4.5|Cấu hình user umask mặc định
|
||||||
|
5.4.6|Cấu hình hạn chế truy cập cho câu lệnh su
|
||||||
|
6.1|Quyền của file hệ thống
|
||||||
|
6.1.1|Cấu hình sticky bit cho tất cả các thư mục dùng chung
|
||||||
|
6.1.2|Cấu hình phân quyền cho file /etc/passwd
|
||||||
|
6.1.3|Cấu hình phân quyền cho file /etc/shadow
|
||||||
|
6.1.4|Cấu hình phân quyền cho file /etc/group
|
||||||
|
6.1.5|Cấu hình phân quyền cho file /etc/gshadow
|
||||||
|
6.1.6|Cấu hình phân quyền cho file /etc/passwd-
|
||||||
|
6.1.7|Cấu hình phân quyền cho file /etc/shadow-
|
||||||
|
6.1.8|Cấu hình phân quyền cho file /etc/group-
|
||||||
|
6.1.9|Cấu hình phân quyền cho file /etc/gshadow-
|
||||||
|
6.1.10|Đảm bảo không có file world-writable tồn tại
|
||||||
|
6.1.11|Đảm bảo các file hoặc thư mục không có chủ sở hữu không tồn tại
|
||||||
|
6.1.12|Đảm bảo các file hoặc thư mục không có nhóm không tồn tại
|
||||||
|
6.2|Thiết lập cho người dùng và nhóm
|
||||||
|
6.2.1|Đảm bảo trường mật khẩu không để trống
|
||||||
|
6.2.2|Đảm bảo mọi nhóm trong file /etc/passwd tồn tại trong file /etc/group
|
||||||
|
6.2.3|Đảm bảo UID không bị lặp
|
||||||
|
6.2.4|Đảm bảo GID không bị lặp
|
||||||
|
6.2.5|Đảm bảo tên người dùng không bị lặp
|
||||||
|
6.2.6|Đảm bảo tên group không bị lặp
|
||||||
|
6.2.7|Đảm bảo tính toàn vẹn cho biến môi trường PATH của root
|
||||||
|
6.2.8|Đảm bảo root là tài khoản duy nhất có UID là 0
|
||||||
|
6.2.9|Đảm bảo mọi người dùng đều tồn tại thư mục home
|
||||||
|
6.2.10|Đảm bảo người dùng sở hữu thư mục home của chính họ
|
||||||
|
6.2.11|Đảm bảo quyền thư mục home của người dùng có mức bảo mật cao
|
||||||
|
6.2.12|Đảm bảo các file dot của người dùng không cấp quyền write cho group hoặc other
|
||||||
|
6.2.13|Đảm bảo không người dùng nào có file .forward
|
||||||
|
6.2.14|Đảm bảo không người dùng nào có file .netrc
|
||||||
|
6.2.15|Đảm bảo không người dùng nào có file .rhosts
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import base64
|
||||||
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||||
|
from cryptography.hazmat.primitives import padding
|
||||||
|
|
||||||
|
|
||||||
|
def decrypt_file(encrypted_file_path, output_file_path=None):
|
||||||
|
key_Mes = "fb8MH7yIuVUeCp5W4S9cKFtsHaxXIP/zvkO36wNNcO4="
|
||||||
|
key = base64.b64decode(key_Mes.encode("ascii"))
|
||||||
|
|
||||||
|
with open(encrypted_file_path, 'rb') as f:
|
||||||
|
encrypted_data = f.read()
|
||||||
|
|
||||||
|
cipher = Cipher(
|
||||||
|
algorithms.AES(key),
|
||||||
|
modes.ECB()
|
||||||
|
)
|
||||||
|
|
||||||
|
decryptor = cipher.decryptor()
|
||||||
|
padded_data = decryptor.update(encrypted_data) + decryptor.finalize()
|
||||||
|
|
||||||
|
# Remove PKCS7 padding
|
||||||
|
unpadder = padding.PKCS7(128).unpadder()
|
||||||
|
data = unpadder.update(padded_data) + unpadder.finalize()
|
||||||
|
|
||||||
|
if output_file_path is None:
|
||||||
|
output_file_path = encrypted_file_path.replace('.enc', '.dec')
|
||||||
|
|
||||||
|
with open(output_file_path, 'wb') as output_file:
|
||||||
|
output_file.write(data)
|
||||||
|
|
||||||
|
print(f"Decrypted to: {output_file_path}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description='Decrypt file')
|
||||||
|
parser.add_argument('-p', '--path', help='Path to encrypted file', required=True)
|
||||||
|
parser.add_argument('-o', '--out_file', help='Output file path', default=None)
|
||||||
|
args = parser.parse_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
args = main()
|
||||||
|
decrypt_file(args.path, args.out_file)
|
||||||
Binary file not shown.
@@ -0,0 +1,62 @@
|
|||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import base64
|
||||||
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||||
|
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
|
||||||
|
from cryptography.hazmat.primitives import hashes, padding
|
||||||
|
|
||||||
|
|
||||||
|
def genSecretKey(key, salt):
|
||||||
|
hkdf = HKDF(
|
||||||
|
algorithm=hashes.SHA256(),
|
||||||
|
length=32,
|
||||||
|
salt=salt,
|
||||||
|
info=None
|
||||||
|
)
|
||||||
|
secret_key = hkdf.derive(key)
|
||||||
|
return secret_key
|
||||||
|
|
||||||
|
|
||||||
|
def encrypt_file(file_path, encrypted_file_path=None):
|
||||||
|
file_name = os.path.basename(file_path)
|
||||||
|
key_Mes = "fb8MH7yIuVUeCp5W4S9cKFtsHaxXIP/zvkO36wNNcO4="
|
||||||
|
key = base64.b64decode(key_Mes.encode("ascii"))
|
||||||
|
|
||||||
|
with open(file_path, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
padder = padding.PKCS7(128).padder()
|
||||||
|
padded_data = padder.update(data)
|
||||||
|
padded_data += padder.finalize()
|
||||||
|
|
||||||
|
cipher = Cipher(
|
||||||
|
algorithms.AES(key),
|
||||||
|
modes.ECB()
|
||||||
|
)
|
||||||
|
|
||||||
|
encryptor = cipher.encryptor()
|
||||||
|
ct = encryptor.update(padded_data) + encryptor.finalize()
|
||||||
|
if encrypted_file_path is None:
|
||||||
|
encrypted_file_path = f"{file_name}.enc"
|
||||||
|
with open(encrypted_file_path, 'wb') as encrypted_file:
|
||||||
|
encrypted_file.write(ct)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Parse Arguments
|
||||||
|
parser = argparse.ArgumentParser(description='Encrypt file')
|
||||||
|
parser.add_argument('-p', '--path', help='Path to file', required=True)
|
||||||
|
parser.add_argument('-o', '--out_file', help='Write to file', default=None)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
args = main()
|
||||||
|
file_path = args.path
|
||||||
|
if args.out_file is not None:
|
||||||
|
encrypted_file_path = args.out_file
|
||||||
|
encrypt_file(file_path, encrypted_file_path)
|
||||||
|
else:
|
||||||
|
encrypt_file(file_path)
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,466 @@
|
|||||||
|
============================================================================
|
||||||
|
THÔNG TIN HỆ THỐNG
|
||||||
|
============================================================================
|
||||||
|
|
||||||
|
Script Version: 2.0.2
|
||||||
|
|
||||||
|
--- Thông tin cơ bản ---
|
||||||
|
Operating System: Ubuntu 24.04.4 LTS
|
||||||
|
Kernel Version: 6.8.0-124-generic
|
||||||
|
Architecture: x86_64
|
||||||
|
Hostname: antt-ksc01
|
||||||
|
FQDN: antt-ksc01.vascloud.vnpt.vn
|
||||||
|
IP Address: 10.144.82.37
|
||||||
|
All IP Addresses: 10.144.82.37
|
||||||
|
Audit Time: 2026-07-16 16:14:20
|
||||||
|
Timezone: Asia/Ho_Chi_Minh
|
||||||
|
Uptime: up 1 week, 1 day, 5 hours, 41 minutes
|
||||||
|
|
||||||
|
--- Thông tin CPU ---
|
||||||
|
CPU Model: Intel(R) Xeon(R) Gold 5220R CPU @ 2.20GHz
|
||||||
|
CPU Cores: 16
|
||||||
|
CPU Architecture: x86_64
|
||||||
|
|
||||||
|
--- Thông tin Memory ---
|
||||||
|
Total Memory: 15Gi
|
||||||
|
Used Memory: 1.5Gi
|
||||||
|
Free Memory: 7.9Gi
|
||||||
|
Swap Total: 15Gi
|
||||||
|
|
||||||
|
--- Thông tin Disk ---
|
||||||
|
Disk Usage:
|
||||||
|
/ 16G used of 482G (4%)
|
||||||
|
/ 16G used of 482G (4%)
|
||||||
|
/ 16G used of 482G (4%)
|
||||||
|
/tmp 0 used of 7.8G (0%)
|
||||||
|
|
||||||
|
--- Thông tin Network Interfaces ---
|
||||||
|
Primary Interface: ens160
|
||||||
|
MAC Address: 00:50:56:9e:6d:4a
|
||||||
|
Default Gateway: 10.144.82.33
|
||||||
|
DNS Servers: 127.0.0.53
|
||||||
|
|
||||||
|
--- Thông tin bổ sung ---
|
||||||
|
AppArmor Status: Enabled
|
||||||
|
UFW Status: Status: active
|
||||||
|
Last Boot: 2026-07-08 10:33
|
||||||
|
Current Users: 3
|
||||||
|
Load Average: 0.76 0.40 0.50
|
||||||
|
Ubuntu Version Detected: 24.04
|
||||||
|
|
||||||
|
============================================================================
|
||||||
|
IPv6 Status: DISABLED (kernel support not available)
|
||||||
|
############################################################################
|
||||||
|
{"1.1.1.1. Cấu hình vô hiệu hoá cramfs filesystem" : "PASSED"}
|
||||||
|
{"1.1.1.2. Cấu hình vô hiệu hoá freevxfs filesystem" : "PASSED"}
|
||||||
|
{"1.1.1.3. Cấu hình vô hiệu hoá hfs filesystem" : "PASSED"}
|
||||||
|
{"1.1.1.4. Cấu hình vô hiệu hoá hfsplus filesystem" : "PASSED"}
|
||||||
|
{"1.1.1.5. Cấu hình vô hiệu hoá jffs2 filesystem" : "PASSED"}
|
||||||
|
{"1.1.1.6. Cấu hình vô hiệu hoá squashfs filesystem" : "PASSED"}
|
||||||
|
{"1.1.1.7. Cấu hình vô hiệu hoá udf filesystem" : "PASSED"}
|
||||||
|
{"1.1.2.1. Cấu hình tuỳ chọn nodev cho phân vùng /tmp" : "PASSED"}
|
||||||
|
{"1.1.2.2. Cấu hình tuỳ chọn nosuid cho phân vùng /tmp" : "PASSED"}
|
||||||
|
{"1.1.2.3. Cấu hình tuỳ chọn noexec cho phân vùng /tmp" : "PASSED"}
|
||||||
|
-------------------
|
||||||
|
# Chỉnh sửa file /etc/fstab và thêm tùy chọn nodev, nosuid, noexec vào trường thứ 4 trong cấu hình của phân vùng /tmp. Ví dụ:
|
||||||
|
# tmpfs /tmp tmpfs defaults,nodev,nosuid,noexec 0 0
|
||||||
|
# Thực hiện các lệnh sau để mount phân vùng /tmp và cập nhật cấu hình fstab:
|
||||||
|
# mount /tmp
|
||||||
|
# mount -o remount /tmp
|
||||||
|
-------------------
|
||||||
|
######################################
|
||||||
|
{"1.1.3.1. Cấu hình tuỳ chọn nodev cho phân vùng /var/tmp" : "PASSED"}
|
||||||
|
{"1.1.3.2. Cấu hình tuỳ chọn nosuid cho phân vùng /var/tmp" : "PASSED"}
|
||||||
|
{"1.1.3.3. Cấu hình tuỳ chọn noexec cho phân vùng /var/tmp" : "PASSED"}
|
||||||
|
-------------------
|
||||||
|
# Chỉnh sửa file /etc/fstab và thêm tùy chọn nodev, nosuid, noexec vào trường thứ 4 trong cấu hình của phân vùng /var/tmp. Ví dụ:
|
||||||
|
# tmpfs /var/tmp tmpfs defaults,nodev,nosuid,noexec 0 0
|
||||||
|
# Thực hiện các lệnh sau để mount phân vùng /var/tmp và cập nhật cấu hình fstab:
|
||||||
|
# mount /var/tmp
|
||||||
|
# mount -o remount /var/tmp
|
||||||
|
-------------------
|
||||||
|
######################################
|
||||||
|
{"1.1.4.1. Cấu hình tuỳ chọn nodev cho phân vùng /home" : "PASSED"}
|
||||||
|
{"1.1.4.2. Cấu hình tuỳ chọn nosuid cho phân vùng /home" : "PASSED"}
|
||||||
|
-------------------
|
||||||
|
# Chỉnh sửa file /etc/fstab và thêm tùy chọn nodev, nosuid, noexec vào trường thứ 4 trong cấu hình của phân vùng /home. Ví dụ:
|
||||||
|
# <device> /home <fstype> defaults,nodev,nosuid 0 0
|
||||||
|
# Thực hiện các lệnh sau để cập nhật cấu hình fstab:
|
||||||
|
# mount -o remount /home
|
||||||
|
-------------------
|
||||||
|
######################################
|
||||||
|
{"1.1.5.1. Cấu hình tuỳ chọn nodev cho phân vùng /dev/shm" : "PASSED"}
|
||||||
|
{"1.1.5.2. Cấu hình tuỳ chọn nosuid cho phân vùng /dev/shm" : "PASSED"}
|
||||||
|
{"1.1.5.3. Cấu hình tuỳ chọn noexec cho phân vùng /dev/shm" : "PASSED"}
|
||||||
|
-------------------
|
||||||
|
# Chỉnh sửa file /etc/fstab và thêm tùy chọn nodev, nosuid, noexec vào trường thứ 4 trong cấu hình của phân vùng /dev/shm. Ví dụ:
|
||||||
|
# tmpfs /dev/shm tmpfs defaults,nodev,nosuid,noexec 0 0
|
||||||
|
# Thực hiện các lệnh sau để mount phân vùng /dev/shm và cập nhật cấu hình fstab:
|
||||||
|
# mount /dev/shm
|
||||||
|
# mount -o remount /dev/shm
|
||||||
|
-------------------
|
||||||
|
######################################
|
||||||
|
{"1.1.6. Cấu hình sticky bit cho tất cả các thư mục dùng chung" : "PASSED"}
|
||||||
|
{"2.2.2. Cấu hình vô hiệu hoá autofs services" : "PASSED"}
|
||||||
|
{"1.1.8. Cấu hình vô hiệu hoá usb storage" : "PASSED"}
|
||||||
|
{"1.2.1. Kiểm tra cài đặt AIDE" : "PASSED"}
|
||||||
|
{"1.2.2. Cấu hình kiểm tra tính toàn vẹn của filesystem" : "PASSED"}
|
||||||
|
{"1.3.1. Phân quyền đối với file cấu hình bootloader" : "PASSED"}
|
||||||
|
{"1.3.2. Cấu hình xác thực khi truy cập single user mode" : "PASSED"}
|
||||||
|
{"1.3.3. Cấu hình xác thực khi truy cập single user mode" : "PASSED"}
|
||||||
|
{"1.4.1. Cấu hình kích hoạt ASLR (address space layout randomization)" : "PASSED"}
|
||||||
|
{"1.4.2. Cấu hình vô hiệu hoá prelink" : "PASSED"}
|
||||||
|
{"1.4.3. Cấu hình vô hiệu hoá core dump" : "PASSED"}
|
||||||
|
{"1.5.1. Kiểm soát nội dung motd (Message Of The Day)" : "PASSED"}
|
||||||
|
{"1.5.2. Kiểm soát nội dung thông báo khi đăng nhập" : "PASSED"}
|
||||||
|
{"1.5.3. Kiểm soát nội dung thông báo khi đăng nhập từ xa" : "PASSED"}
|
||||||
|
{"1.5.4. Cấu hình phân quyền đối với file /etc/motd" : "PASSED"}
|
||||||
|
######################################
|
||||||
|
{"1.5.5. Cấu hình phân quyền đối với file /etc/issue" : "PASSED"}
|
||||||
|
{"1.5.6. Cấu hình phân quyền đối với file /etc/issue.net" : "PASSED"}
|
||||||
|
{"1.5.7. Kiểm soát nội dung thông báo khi truy cập GNOME" : "PASSED"}
|
||||||
|
{"2.1.1. Cấu hình sử dụng chrony" : "PASSED"}
|
||||||
|
{"2.1.2. Cấu hình sử dụng NTP" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"2.2.1. Cấu hình vô hiệu hoá xinetd services" : "PASSED"}
|
||||||
|
----------[PASSED]----------
|
||||||
|
#dpkg -l | grep xinetd
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-enabled xinetd.service
|
||||||
|
not-found
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-active xinetd.service
|
||||||
|
inactive
|
||||||
|
----------LOG----------
|
||||||
|
#systemctl is-enabled xinetd.service
|
||||||
|
not-found
|
||||||
|
#systemctl is-active xinetd.service
|
||||||
|
inactive
|
||||||
|
{"2.2.3. Cấu hình vô hiệu hoá X window server services" : "PASSED"}
|
||||||
|
{"2.2.4. Cấu hình vô hiệu hoá avahi daemon services" : "PASSED"}
|
||||||
|
{"2.2.5. Cấu hình vô hiệu hoá cups services" : "PASSED"}
|
||||||
|
{"2.2.6. Cấu hình vô hiệu hoá dhcp server services" : "PASSED"}
|
||||||
|
{"2.2.7. Cấu hình vô hiệu hoá ldap server services" : "PASSED"}
|
||||||
|
{"2.2.6. Cấu hình vô hiệu hoá NFS" : "PASSED"}
|
||||||
|
{"2.2.8. Cấu hình vô hiệu hoá dns server services" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"2.2.9. Cấu hình vô hiệu hoá dnsmasq services" : "PASSED"}
|
||||||
|
----------[PASSED]----------
|
||||||
|
#grep -E "\s[7-9].[0-9]" /etc/os-release
|
||||||
|
----------[PASSED]----------
|
||||||
|
#dpkg-query -l dnsmasq
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-enabled dnsmasq.service
|
||||||
|
not-found
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-active dnsmasq.service
|
||||||
|
inactive
|
||||||
|
----------LOG----------
|
||||||
|
PRETTY_NAME="Ubuntu 24.04.4 LTS"
|
||||||
|
NAME="Ubuntu"
|
||||||
|
VERSION_ID="24.04"
|
||||||
|
VERSION="24.04.4 LTS (Noble Numbat)"
|
||||||
|
VERSION_CODENAME=noble
|
||||||
|
ID=ubuntu
|
||||||
|
ID_LIKE=debian
|
||||||
|
HOME_URL="https://www.ubuntu.com/"
|
||||||
|
SUPPORT_URL="https://help.ubuntu.com/"
|
||||||
|
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
|
||||||
|
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
|
||||||
|
UBUNTU_CODENAME=noble
|
||||||
|
LOGO=ubuntu-logo
|
||||||
|
{"2.2.10. Cấu hình vô hiệu hoá ftp server services" : "PASSED"}
|
||||||
|
{"2.2.12. Cấu hình vô hiệu hoá web server services" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"2.2.11. Cấu hình vô hiệu hoá tftp server services" : "PASSED"}
|
||||||
|
----------[PASSED]----------
|
||||||
|
#grep -E "\s[7-9].[0-9]" /etc/os-release
|
||||||
|
----------[PASSED]----------
|
||||||
|
#dpkg -l | grep tftpd-hpa
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-enabled tftp.socket
|
||||||
|
not-found
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-enabled tftpd-hpa.service
|
||||||
|
not-found
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-active tftp.socket
|
||||||
|
inactive
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-active tftpd-hpa.service
|
||||||
|
inactive
|
||||||
|
----------LOG----------
|
||||||
|
{"2.2.13. Cấu hình vô hiệu hoá imap and pop3 server services" : "PASSED"}
|
||||||
|
{"2.2.14. Cấu hình vô hiệu hoá samba file server services" : "PASSED"}
|
||||||
|
{"2.2.15. Cấu hình vô hiệu hoá web proxy server services" : "PASSED"}
|
||||||
|
{"2.2.16. Cấu hình vô hiệu hoá snmp services" : "PASSED"}
|
||||||
|
{"2.2.14. Cấu hình vô hiệu hoá telnetd" : "PASSED"}
|
||||||
|
{"2.2.15. Cấu hình vô hiệu hoá rsh-server" : "PASSED"}
|
||||||
|
{"2.2.18. Cấu hình mail transfer agents sang chế độ local-only" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"2.2.17. Cấu hình vô hiệu hoá nis server services" : "PASSED"}
|
||||||
|
----------[PASSED]----------
|
||||||
|
#dpkg -l | grep ypserv
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-enabled ypserv.service
|
||||||
|
not-found
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-active ypserv.service
|
||||||
|
inactive
|
||||||
|
----------LOG----------
|
||||||
|
#systemctl is-enabled ypserv.service
|
||||||
|
not-found
|
||||||
|
#systemctl is-active ypserv.service
|
||||||
|
inactive
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"2.2.19. Cấu hình vô hiệu hoá network file system services" : "PASSED"}
|
||||||
|
----------[PASSED]----------
|
||||||
|
#dpkg -l | grep nfs-kernel-server
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-enabled nfs-server.service
|
||||||
|
not-found
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-active nfs-server.service
|
||||||
|
inactive
|
||||||
|
----------LOG----------
|
||||||
|
#systemctl is-enabled nfs-server.service
|
||||||
|
not-found
|
||||||
|
#systemctl is-active nfs-server.service
|
||||||
|
inactive
|
||||||
|
{"2.2.20. Cấu hình vô hiệu hoá rsync services" : "PASSED"}
|
||||||
|
{"2.3.1. Cấu hình vô hiệu hoá nis client" : "PASSED"}
|
||||||
|
{"2.3.2. Cấu hình vô hiệu hoá rsh client" : "PASSED"}
|
||||||
|
{"2.3.3. Cấu hình vô hiệu hoá talk client" : "PASSED"}
|
||||||
|
{"2.3.4. Cấu hình vô hiệu hoá telnet client" : "PASSED"}
|
||||||
|
{"2.3.5. Cấu hình vô hiệu hoá ldap client" : "PASSED"}
|
||||||
|
{"2.3.6. Cấu hình vô hiệu hoá rpc" : "PASSED"}
|
||||||
|
{"2.3.7. Cấu hình vô hiệu hoá ftp client" : "PASSED"}
|
||||||
|
----------[PASSED]----------
|
||||||
|
#grep -E "\s[7-9].[0-9]" /etc/os-release
|
||||||
|
----------[PASSED]----------
|
||||||
|
#dpkg -l | grep ftp
|
||||||
|
ii openssh-sftp-server 1:9.6p1-3ubuntu13.18 amd64 secure shell (SSH) sftp server module, for SFTP access from remote machines
|
||||||
|
ii tnftp 20230507-2build3 amd64 enhanced ftp client
|
||||||
|
----------LOG----------
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"3.1.1. Cấu hình vô hiệu hoá IP forwarding" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"3.1.2. Cấu hình vô hiệu hoá tính năng chuyển hướng gói tin (packet redirect)" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"3.2.1. Cấu hình từ chối các gói tin với nguồn được định tuyến trước" : "PASSED"}
|
||||||
|
|
||||||
|
########## STDERR BEGIN ##########
|
||||||
|
/bin/bash: line 2: f: command not found
|
||||||
|
|
||||||
|
########## STDERR END ##########
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"3.2.2. Cấu hình từ chối các ICMP redirect message" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"3.2.3. Cấu hình từ chối các secure ICMP redirect message" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"3.2.4. Cấu hình từ chối các gói tin ICMP request broadcast" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"3.2.5. Cấu hình bỏ qua phản hồi ICMP không hợp lệ" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"3.2.6. Cấu hình Reverse Path Filtering" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"3.3.1. Cấu hình từ chối các gói tin với nguồn được định tuyến trước (Source Routed Packet Acceptance)" : "PASSED"}
|
||||||
|
{"3.3.2. Cấu hình từ chối các ICMP redirect message" : "PASSED"}
|
||||||
|
{"3.3.3. Cấu hình từ chối các secure ICMP redirect message" : "PASSED"}
|
||||||
|
{"3.3.4. Cấu hình từ chối các gói tin ICMP request broadcast" : "PASSED"}
|
||||||
|
{"3.3.5. Cấu hình bỏ qua phản hồi ICMP không hợp lệ" : "PASSED"}
|
||||||
|
{"3.3.6. Cấu hình Reverse Path Filtering" : "PASSED"}
|
||||||
|
{"3.2.7. Cấu hình TCP SYN Cookies" : "PASSED"}
|
||||||
|
{"3.3.8. Cấu hình từ chối IPv6 router advertisements" : "PASSED"}
|
||||||
|
{"3.4.1.1. Cấu hình kích hoạt ufw" : "PASSED"}
|
||||||
|
{"3.4.2.1. Cấu hình kích hoạt Iptables" : "PASSED"}
|
||||||
|
{"3.4.1.2. Cấu hình vô hiệu hoá iptables-persistent, nftables khi sử dụng ufw" : "PASSED"}
|
||||||
|
{"3.4.1.3. Cấu hình ufw loopback traffic" : "PASSED"}
|
||||||
|
{"3.4.1.4. Cấu hình ufw rule cho tất cả các port và protocol đang mở" : "PASSED"}
|
||||||
|
######################################
|
||||||
|
# Các rule UFW hiện tại:
|
||||||
|
-------------------
|
||||||
|
Status: active
|
||||||
|
|
||||||
|
To Action From
|
||||||
|
-- ------ ----
|
||||||
|
[ 1] Anywhere on lo ALLOW IN Anywhere
|
||||||
|
[ 2] Anywhere ALLOW OUT Anywhere on lo (out)
|
||||||
|
[ 3] Anywhere DENY IN 127.0.0.0/8
|
||||||
|
[ 4] 22/tcp ALLOW IN Anywhere
|
||||||
|
[ 5] 53 ALLOW OUT Anywhere (out)
|
||||||
|
[ 6] 80/tcp ALLOW OUT Anywhere (out)
|
||||||
|
[ 7] 443/tcp ALLOW OUT Anywhere (out)
|
||||||
|
[ 8] 123/udp ALLOW OUT Anywhere (out)
|
||||||
|
[ 9] 11371/tcp ALLOW OUT Anywhere (out)
|
||||||
|
[10] Anywhere (v6) on lo ALLOW IN Anywhere (v6)
|
||||||
|
[11] Anywhere (v6) ALLOW OUT Anywhere (v6) on lo (out)
|
||||||
|
[12] Anywhere (v6) DENY IN ::1
|
||||||
|
[13] 22/tcp (v6) ALLOW IN Anywhere (v6)
|
||||||
|
[14] 53 (v6) ALLOW OUT Anywhere (v6) (out)
|
||||||
|
[15] 80/tcp (v6) ALLOW OUT Anywhere (v6) (out)
|
||||||
|
[16] 443/tcp (v6) ALLOW OUT Anywhere (v6) (out)
|
||||||
|
[17] 123/udp (v6) ALLOW OUT Anywhere (v6) (out)
|
||||||
|
[18] 11371/tcp (v6) ALLOW OUT Anywhere (v6) (out)
|
||||||
|
|
||||||
|
-------------------
|
||||||
|
# Inbound rules (ALLOW):
|
||||||
|
Anywhere on lo ALLOW Anywhere
|
||||||
|
22/tcp ALLOW Anywhere
|
||||||
|
Anywhere (v6) on lo ALLOW Anywhere (v6)
|
||||||
|
22/tcp (v6) ALLOW Anywhere (v6)
|
||||||
|
-------------------
|
||||||
|
# Outbound rules (ALLOW OUT):
|
||||||
|
Anywhere ALLOW OUT Anywhere on lo
|
||||||
|
53 ALLOW OUT Anywhere
|
||||||
|
80/tcp ALLOW OUT Anywhere
|
||||||
|
443/tcp ALLOW OUT Anywhere
|
||||||
|
123/udp ALLOW OUT Anywhere
|
||||||
|
11371/tcp ALLOW OUT Anywhere
|
||||||
|
Anywhere (v6) ALLOW OUT Anywhere (v6) on lo
|
||||||
|
53 (v6) ALLOW OUT Anywhere (v6)
|
||||||
|
80/tcp (v6) ALLOW OUT Anywhere (v6)
|
||||||
|
443/tcp (v6) ALLOW OUT Anywhere (v6)
|
||||||
|
123/udp (v6) ALLOW OUT Anywhere (v6)
|
||||||
|
11371/tcp (v6) ALLOW OUT Anywhere (v6)
|
||||||
|
-------------------
|
||||||
|
# IPv6 rules:
|
||||||
|
Anywhere (v6) on lo ALLOW Anywhere (v6)
|
||||||
|
Anywhere (v6) DENY ::1
|
||||||
|
22/tcp (v6) ALLOW Anywhere (v6)
|
||||||
|
Anywhere (v6) ALLOW OUT Anywhere (v6) on lo
|
||||||
|
53 (v6) ALLOW OUT Anywhere (v6)
|
||||||
|
80/tcp (v6) ALLOW OUT Anywhere (v6)
|
||||||
|
443/tcp (v6) ALLOW OUT Anywhere (v6)
|
||||||
|
123/udp (v6) ALLOW OUT Anywhere (v6)
|
||||||
|
11371/tcp (v6) ALLOW OUT Anywhere (v6)
|
||||||
|
######################################
|
||||||
|
{"3.4.1.5. Cấu hình chính sách từ chối mặc định cho ufw" : "PASSED"}
|
||||||
|
{"3.4.2.2. Cấu hình vô hiệu hoá ufw, nftables khi sử dụng iptables" : "PASSED"}
|
||||||
|
{"3.4.2.3. Cấu hình iptables loopback traffic" : "PASSED"}
|
||||||
|
{"3.4.2.4. Cấu hình iptables rule cho tất cả các port và protocol đang mở" : "PASSED"}
|
||||||
|
{"3.4.2.5. Cấu hình chính sách từ chối mặc định cho iptables" : "PASSED"}
|
||||||
|
{"4.1.1.1. Cấu hình kích hoạt rsyslog service" : "PASSED"}
|
||||||
|
{"4.1.1.2. Phân quyền đối với file log sinh ra từ rsyslog" : "PASSED"}
|
||||||
|
{"4.1.1.3. Cấu hình lưu trữ log sinh ra từ rsyslog tập trung" : "FAILED"}
|
||||||
|
######################################
|
||||||
|
# Chỉnh sửa file /etc/rsyslog.conf và /etc/rsyslog.d/*.conf và thêm vào 1 trong các dòng sau:
|
||||||
|
# <files to sent to the remote log server> action(type="omfwd" target="<FQDN or ip of loghost>" port="<port number>" protocol="tcp" action.resumeRetryCount="<number of re-tries>" queue.type="LinkedList" queue.size=<number of messages to queue>") # Hoặc
|
||||||
|
# *.* @@< FQDN or ip of loghost >
|
||||||
|
# Thực hiện câu lệnh sau để khởi động lại rsyslog:
|
||||||
|
# systemctl restart rsyslog
|
||||||
|
-------------------
|
||||||
|
#grep -E '^\h*([^#]+\s+)?action\(([^#]+\s+)?\btarget=\?[^#"]+\?\b' /etc/rsyslog.conf /etc/rsyslog.d/*.conf 2>/dev/null -ne 0
|
||||||
|
######################################
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"4.1.1.4. Phân quyền đối với tất cả các file log" : "PASSED"}
|
||||||
|
{"4.1.2. Phân quyền đối với tất cả các file log" : "PASSED"}
|
||||||
|
{"5.1.1. Cấu hình kích hoạt cron daemon" : "PASSED"}
|
||||||
|
{"5.1.2. Cấu hình phân quyền cho file /etc/crontab" : "PASSED"}
|
||||||
|
{"5.1.3. Cấu hình phân quyền cho file /etc/cron.hourly" : "PASSED"}
|
||||||
|
{"5.1.4. Cấu hình phân quyền cho file /etc/cron.daily" : "PASSED"}
|
||||||
|
{"5.1.5. Cấu hình phân quyền cho file /etc/cron.weekly" : "PASSED"}
|
||||||
|
{"5.1.6. Cấu hình phân quyền cho của file /etc/cron.monthly" : "PASSED"}
|
||||||
|
{"5.1.7. Cấu hình phân quyền cho file /etc/cron.d" : "PASSED"}
|
||||||
|
{"5.1.8. Cấu hình at/cron hạn chế chỉ cho người dùng được ủy quyền" : "PASSED"}
|
||||||
|
{"5.2.1. Cấu hình phân quyền cho file /etc/ssh/sshd_config" : "PASSED"}
|
||||||
|
{"5.2.2. Cấu hình phân quyền cho các file SSH private host key" : "PASSED"}
|
||||||
|
{"5.2.3. Cấu hình phân quyền cho các file SSH public host key" : "PASSED"}
|
||||||
|
{"5.2.4. Cấu hình giới hạn truy cập cho máy chủ SSH" : "PASSED"}
|
||||||
|
{"5.2.5. Cấu hình LogLevel cho máy chủ SSH" : "PASSED"}
|
||||||
|
{"5.2.6. Cấu hình sử dụng SSH PAM" : "PASSED"}
|
||||||
|
{"5.2.7. Cấu hình vô hiệu hoá đăng nhập bằng root cho máy chủ SSH" : "PASSED"}
|
||||||
|
{"5.2.8. Cấu hình vô hiệu hoá HostbasedAuthentication cho máy chủ SSH" : "PASSED"}
|
||||||
|
{"5.2.9. Cấu hình vô hiệu hoá PermitEmptyPasswords cho máy chủ SSH" : "PASSED"}
|
||||||
|
{"5.2.10. Cấu hình vô hiệu hoá PermitUserEnviroment cho máy chủ SSH" : "PASSED"}
|
||||||
|
{"5.2.11. Cấu hình vô hiệu hoá IgnoreRhosts cho máy chủ SSH" : "PASSED"}
|
||||||
|
{"5.2.12. Cấu hình vô hiệu hoá X11 Forwarding cho máy chủ SSH" : "PASSED"}
|
||||||
|
{"5.2.13. Cấu hình sử dụng các thuật toán mã hoá được cho phép" : "PASSED"}
|
||||||
|
{"5.2.14. Cấu hình các thuật toán MAC được cho phép" : "PASSED"}
|
||||||
|
{"5.2.15. Cấu hình thuật toán trao đổi khoá được cho phép" : "PASSED"}
|
||||||
|
{"5.2.16. Cấu hình vô hiệu hoá SSH AllowTcpForwarding" : "PASSED"}
|
||||||
|
{"5.2.17. Cấu hình cảnh báo SSH" : "PASSED"}
|
||||||
|
{"5.2.18. Cấu hình SSH MaxAuthTries" : "PASSED"}
|
||||||
|
{"5.2.19. Cấu hình SSH MaxStartups" : "PASSED"}
|
||||||
|
{"5.2.20. Cấu hình SSH MaxSessions" : "PASSED"}
|
||||||
|
{"5.2.21. Cấu hình SSH LoginGraceTime" : "PASSED"}
|
||||||
|
{"5.2.22. Cấu hình khoảng thời gian chờ không hoạt động cho máy chủ SSH" : "PASSED"}
|
||||||
|
{"5.3.1. Cấu hình điều kiện tạo mật khẩu" : "PASSED"}
|
||||||
|
{"5.3.2. Cấu hình khoá truy cập do nhiều lần nhập mật khẩu thất bại" : "PASSED"}
|
||||||
|
{"5.3.3. Giới hạn việc sử dụng lại mật khẩu" : "PASSED"}
|
||||||
|
{"5.3.4. Cấu hình thuật toán hash mật khẩu mạnh" : "FAILED"}
|
||||||
|
######################################
|
||||||
|
# Đối với phiên bản 22.04 trở lên:
|
||||||
|
# Chỉnh sửa file /etc/pam.d/common-password đảm bảo rằng không có bất cứ thuật toán hash nào được cấu hình trong pam_unix.so như dưới đây:
|
||||||
|
# password [success=1 default=ignore] pam_unix.so remember=5
|
||||||
|
# Thêm hoặc chỉnh sửa ENCRYPT_METHOD trong file /etc/login.defs như sau:
|
||||||
|
# ENCRYPT_METHOD yescrypt
|
||||||
|
# Đối với phiên bản 20.04 trở về trước: Chỉnh sửa file /etc/pam.d/common-password để có tuỳ chọn sha512 cho pam_unix.so như dưới đây:
|
||||||
|
# password [success=1 default=ignore] pam_unix.so sha512 remember=5
|
||||||
|
-------------------
|
||||||
|
#grep -P "^\h*password\h*\[success=1 default=ignore\]\h*pam_unix.so.*sha512" /etc/pam.d/common-password -ne 0
|
||||||
|
-------------------
|
||||||
|
#grep -v ^# /etc/pam.d/common-password | grep -E "(yescrypt|md5|bigcrypt|sha256|sha512|blowfish)" -eq 0
|
||||||
|
password [success=1 default=ignore] pam_unix.so obscure use_authtok try_first_pass yescrypt
|
||||||
|
######################################
|
||||||
|
{"5.4.1.1. Cấu hình thời gian tối thiểu giữa những lần thay đổi mật khẩu" : "PASSED"}
|
||||||
|
{"5.4.1.2. Cấu hình thời gian hết hạn sử dụng mật khẩu" : "PASSED"}
|
||||||
|
{"5.4.1.3. Cấu hình thời gian cảnh báo mật khẩu hết hạn" : "PASSED"}
|
||||||
|
{"5.4.1.4. Cấu hình thời gian khoá tài khoản không thay mật khẩu sau khi hết hạn" : "PASSED"}
|
||||||
|
{"5.4.1.5. Đảm bảo thời gian thay đổi mật khẩu lần cuối hợp lệ" : "PASSED"}
|
||||||
|
{"5.4.2. Cấu hình vô hiệu hoá đăng nhập bằng tài khoản hệ thống" : "PASSED"}
|
||||||
|
{"5.4.3. Cấu hình group mặc định của tài khoản root" : "PASSED"}
|
||||||
|
{"5.4.4. Cấu hình user umask mặc định" : "PASSED"}
|
||||||
|
{"5.4.5. Cấu hình shell timeout mặc định" : "PASSED"}
|
||||||
|
{"5.4.6. Cấu hình hạn chế truy cập cho câu lệnh su" : "PASSED"}
|
||||||
|
{"6.1.1. Cấu hình phân quyền cho file /etc/passwd" : "PASSED"}
|
||||||
|
{"6.1.2. Cấu hình phân quyền cho file /etc/shadow" : "PASSED"}
|
||||||
|
{"6.1.3. Cấu hình phân quyền cho file /etc/group" : "PASSED"}
|
||||||
|
{"6.1.4. Cấu hình phân quyền cho file /etc/gshadow" : "PASSED"}
|
||||||
|
{"6.1.5. Cấu hình phân quyền cho file /etc/passwd-" : "PASSED"}
|
||||||
|
{"6.1.6. Cấu hình phân quyền cho file /etc/shadow-" : "PASSED"}
|
||||||
|
{"6.1.7. Cấu hình phân quyền cho file /etc/group-" : "PASSED"}
|
||||||
|
{"6.1.8. Cấu hình phân quyền cho file /etc/gshadow-" : "PASSED"}
|
||||||
|
{"6.1.9. Đảm bảo không có file world-writable tồn tại" : "PASSED"}
|
||||||
|
{"6.1.10. Đảm bảo các file hoặc thư mục không có chủ sở hữu không tồn tại" : "PASSED"}
|
||||||
|
{"6.1.11. Đảm bảo các file hoặc thư mục không có nhóm không tồn tại" : "PASSED"}
|
||||||
|
{"6.2.1. Cấu hình tài khoản trong /etc/passwd sử dụng shadowed passwords" : "PASSED"}
|
||||||
|
{"6.2.2. Đảm bảo trường mật khẩu không để trống" : "FAILED"}
|
||||||
|
######################################
|
||||||
|
# Nếu có tài khoản nào trong file /etc/shadow không có mật khẩu, thực hiện câu lệnh sau để khoá tài khoản đến khi xác định được nguyên nhân tài khoản đó không có mật khẩu:
|
||||||
|
# passwd -l <username>
|
||||||
|
# Đồng thời, kiểm tra tài khoản đó được đăng nhập hay chưa và tìm hiểu xem tài khoản đó được sử dụng với mục đích gì để nếu nó cần phải bị xóa.
|
||||||
|
-------------------
|
||||||
|
#awk -F: '$5==90 && $6==7 {print}' /etc/shadow | awk -F: ' $2=="!" || $2=="" || $2=="!!"{print $1 " " $2}' -eq 0
|
||||||
|
ldnobody !
|
||||||
|
######################################
|
||||||
|
{"6.2.3. Đảm bảo mọi nhóm trong file /etc/passwd tồn tại trong file /etc/group" : "PASSED"}
|
||||||
|
{"6.2.4. Đảm bảo shadow group rỗng" : "PASSED"}
|
||||||
|
{"6.2.5. Đảm bảo UID không bị lặp" : "PASSED"}
|
||||||
|
{"6.2.6. Đảm bảo GID không bị lặp" : "PASSED"}
|
||||||
|
{"6.2.7. Đảm bảo tên người dùng không bị lặp" : "PASSED"}
|
||||||
|
{"6.2.8. Đảm bảo tên group không bị lặp" : "PASSED"}
|
||||||
|
{"6.2.9. Đảm bảo tính toàn vẹn cho biến môi trường PATH của root" : "PASSED"}
|
||||||
|
{"6.2.10. Đảm bảo root là tài khoản duy nhất có UID là 0" : "PASSED"}
|
||||||
|
{"6.2.11. Đảm bảo mọi người dùng đều tồn tại thư mục home" : "PASSED"}
|
||||||
|
{"6.2.12. Đảm bảo người dùng sở hữu thư mục home của chính họ" : "PASSED"}
|
||||||
|
{"6.2.13. Đảm bảo quyền thư mục home của người dùng có mức bảo mật cao" : "PASSED"}
|
||||||
|
{"6.2.14. Đảm bảo không người dùng nào có file .netrc" : "PASSED"}
|
||||||
|
{"6.2.15. Đảm bảo không người dùng nào có file .forward" : "PASSED"}
|
||||||
|
{"6.2.16. Đảm bảo không người dùng nào có file .rhosts" : "PASSED"}
|
||||||
|
{"6.2.17. Đảm bảo các file dot của người dùng không cấp quyền write cho group hoặc other" : "PASSED"}
|
||||||
@@ -0,0 +1,464 @@
|
|||||||
|
============================================================================
|
||||||
|
THÔNG TIN HỆ THỐNG
|
||||||
|
============================================================================
|
||||||
|
|
||||||
|
Script Version: 2.0.2
|
||||||
|
|
||||||
|
--- Thông tin cơ bản ---
|
||||||
|
Operating System: Ubuntu 24.04.4 LTS
|
||||||
|
Kernel Version: 6.8.0-124-generic
|
||||||
|
Architecture: x86_64
|
||||||
|
Hostname: antt-ksc01
|
||||||
|
FQDN: antt-ksc01.vascloud.vnpt.vn
|
||||||
|
IP Address: 10.144.82.37
|
||||||
|
All IP Addresses: 10.144.82.37
|
||||||
|
Audit Time: 2026-07-16 14:18:53
|
||||||
|
Timezone: Asia/Ho_Chi_Minh
|
||||||
|
Uptime: up 1 week, 1 day, 3 hours, 45 minutes
|
||||||
|
|
||||||
|
--- Thông tin CPU ---
|
||||||
|
CPU Model: Intel(R) Xeon(R) Gold 5220R CPU @ 2.20GHz
|
||||||
|
CPU Cores: 16
|
||||||
|
CPU Architecture: x86_64
|
||||||
|
|
||||||
|
--- Thông tin Memory ---
|
||||||
|
Total Memory: 15Gi
|
||||||
|
Used Memory: 1.5Gi
|
||||||
|
Free Memory: 8.5Gi
|
||||||
|
Swap Total: 15Gi
|
||||||
|
|
||||||
|
--- Thông tin Disk ---
|
||||||
|
Disk Usage:
|
||||||
|
/ 15G used of 482G (4%)
|
||||||
|
/ 15G used of 482G (4%)
|
||||||
|
/ 15G used of 482G (4%)
|
||||||
|
/tmp 0 used of 7.8G (0%)
|
||||||
|
|
||||||
|
--- Thông tin Network Interfaces ---
|
||||||
|
Primary Interface: ens160
|
||||||
|
MAC Address: 00:50:56:9e:6d:4a
|
||||||
|
Default Gateway: 10.144.82.33
|
||||||
|
DNS Servers: 127.0.0.53
|
||||||
|
|
||||||
|
--- Thông tin bổ sung ---
|
||||||
|
AppArmor Status: Enabled
|
||||||
|
UFW Status: Status: active
|
||||||
|
Last Boot: 2026-07-08 10:33
|
||||||
|
Current Users: 3
|
||||||
|
Load Average: 0.20 0.09 0.08
|
||||||
|
Ubuntu Version Detected: 24.04
|
||||||
|
|
||||||
|
============================================================================
|
||||||
|
IPv6 Status: DISABLED (kernel support not available)
|
||||||
|
############################################################################
|
||||||
|
{"1.1.1.1. Cấu hình vô hiệu hoá cramfs filesystem" : "PASSED"}
|
||||||
|
{"1.1.1.2. Cấu hình vô hiệu hoá freevxfs filesystem" : "PASSED"}
|
||||||
|
{"1.1.1.3. Cấu hình vô hiệu hoá hfs filesystem" : "PASSED"}
|
||||||
|
{"1.1.1.4. Cấu hình vô hiệu hoá hfsplus filesystem" : "PASSED"}
|
||||||
|
{"1.1.1.5. Cấu hình vô hiệu hoá jffs2 filesystem" : "PASSED"}
|
||||||
|
{"1.1.1.6. Cấu hình vô hiệu hoá squashfs filesystem" : "PASSED"}
|
||||||
|
{"1.1.1.7. Cấu hình vô hiệu hoá udf filesystem" : "PASSED"}
|
||||||
|
{"1.1.2.1. Cấu hình tuỳ chọn nodev cho phân vùng /tmp" : "PASSED"}
|
||||||
|
{"1.1.2.2. Cấu hình tuỳ chọn nosuid cho phân vùng /tmp" : "PASSED"}
|
||||||
|
{"1.1.2.3. Cấu hình tuỳ chọn noexec cho phân vùng /tmp" : "PASSED"}
|
||||||
|
-------------------
|
||||||
|
# Chỉnh sửa file /etc/fstab và thêm tùy chọn nodev, nosuid, noexec vào trường thứ 4 trong cấu hình của phân vùng /tmp. Ví dụ:
|
||||||
|
# tmpfs /tmp tmpfs defaults,nodev,nosuid,noexec 0 0
|
||||||
|
# Thực hiện các lệnh sau để mount phân vùng /tmp và cập nhật cấu hình fstab:
|
||||||
|
# mount /tmp
|
||||||
|
# mount -o remount /tmp
|
||||||
|
-------------------
|
||||||
|
######################################
|
||||||
|
{"1.1.3.1. Cấu hình tuỳ chọn nodev cho phân vùng /var/tmp" : "PASSED"}
|
||||||
|
{"1.1.3.2. Cấu hình tuỳ chọn nosuid cho phân vùng /var/tmp" : "PASSED"}
|
||||||
|
{"1.1.3.3. Cấu hình tuỳ chọn noexec cho phân vùng /var/tmp" : "PASSED"}
|
||||||
|
-------------------
|
||||||
|
# Chỉnh sửa file /etc/fstab và thêm tùy chọn nodev, nosuid, noexec vào trường thứ 4 trong cấu hình của phân vùng /var/tmp. Ví dụ:
|
||||||
|
# tmpfs /var/tmp tmpfs defaults,nodev,nosuid,noexec 0 0
|
||||||
|
# Thực hiện các lệnh sau để mount phân vùng /var/tmp và cập nhật cấu hình fstab:
|
||||||
|
# mount /var/tmp
|
||||||
|
# mount -o remount /var/tmp
|
||||||
|
-------------------
|
||||||
|
######################################
|
||||||
|
{"1.1.4.1. Cấu hình tuỳ chọn nodev cho phân vùng /home" : "PASSED"}
|
||||||
|
{"1.1.4.2. Cấu hình tuỳ chọn nosuid cho phân vùng /home" : "PASSED"}
|
||||||
|
-------------------
|
||||||
|
# Chỉnh sửa file /etc/fstab và thêm tùy chọn nodev, nosuid, noexec vào trường thứ 4 trong cấu hình của phân vùng /home. Ví dụ:
|
||||||
|
# <device> /home <fstype> defaults,nodev,nosuid 0 0
|
||||||
|
# Thực hiện các lệnh sau để cập nhật cấu hình fstab:
|
||||||
|
# mount -o remount /home
|
||||||
|
-------------------
|
||||||
|
######################################
|
||||||
|
{"1.1.5.1. Cấu hình tuỳ chọn nodev cho phân vùng /dev/shm" : "PASSED"}
|
||||||
|
{"1.1.5.2. Cấu hình tuỳ chọn nosuid cho phân vùng /dev/shm" : "PASSED"}
|
||||||
|
{"1.1.5.3. Cấu hình tuỳ chọn noexec cho phân vùng /dev/shm" : "PASSED"}
|
||||||
|
-------------------
|
||||||
|
# Chỉnh sửa file /etc/fstab và thêm tùy chọn nodev, nosuid, noexec vào trường thứ 4 trong cấu hình của phân vùng /dev/shm. Ví dụ:
|
||||||
|
# tmpfs /dev/shm tmpfs defaults,nodev,nosuid,noexec 0 0
|
||||||
|
# Thực hiện các lệnh sau để mount phân vùng /dev/shm và cập nhật cấu hình fstab:
|
||||||
|
# mount /dev/shm
|
||||||
|
# mount -o remount /dev/shm
|
||||||
|
-------------------
|
||||||
|
######################################
|
||||||
|
{"1.1.6. Cấu hình sticky bit cho tất cả các thư mục dùng chung" : "PASSED"}
|
||||||
|
{"2.2.2. Cấu hình vô hiệu hoá autofs services" : "PASSED"}
|
||||||
|
{"1.1.8. Cấu hình vô hiệu hoá usb storage" : "PASSED"}
|
||||||
|
{"1.2.1. Kiểm tra cài đặt AIDE" : "PASSED"}
|
||||||
|
{"1.2.2. Cấu hình kiểm tra tính toàn vẹn của filesystem" : "PASSED"}
|
||||||
|
{"1.3.1. Phân quyền đối với file cấu hình bootloader" : "PASSED"}
|
||||||
|
{"1.3.2. Cấu hình xác thực khi truy cập single user mode" : "PASSED"}
|
||||||
|
{"1.3.3. Cấu hình xác thực khi truy cập single user mode" : "PASSED"}
|
||||||
|
{"1.4.1. Cấu hình kích hoạt ASLR (address space layout randomization)" : "PASSED"}
|
||||||
|
{"1.4.2. Cấu hình vô hiệu hoá prelink" : "PASSED"}
|
||||||
|
{"1.4.3. Cấu hình vô hiệu hoá core dump" : "PASSED"}
|
||||||
|
{"1.5.1. Kiểm soát nội dung motd (Message Of The Day)" : "PASSED"}
|
||||||
|
{"1.5.2. Kiểm soát nội dung thông báo khi đăng nhập" : "PASSED"}
|
||||||
|
{"1.5.3. Kiểm soát nội dung thông báo khi đăng nhập từ xa" : "PASSED"}
|
||||||
|
{"1.5.4. Cấu hình phân quyền đối với file /etc/motd" : "PASSED"}
|
||||||
|
######################################
|
||||||
|
{"1.5.5. Cấu hình phân quyền đối với file /etc/issue" : "PASSED"}
|
||||||
|
{"1.5.6. Cấu hình phân quyền đối với file /etc/issue.net" : "PASSED"}
|
||||||
|
{"1.5.7. Kiểm soát nội dung thông báo khi truy cập GNOME" : "PASSED"}
|
||||||
|
{"2.1.1. Cấu hình sử dụng chrony" : "PASSED"}
|
||||||
|
{"2.1.2. Cấu hình sử dụng NTP" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"2.2.1. Cấu hình vô hiệu hoá xinetd services" : "PASSED"}
|
||||||
|
----------[PASSED]----------
|
||||||
|
#dpkg -l | grep xinetd
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-enabled xinetd.service
|
||||||
|
not-found
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-active xinetd.service
|
||||||
|
inactive
|
||||||
|
----------LOG----------
|
||||||
|
#systemctl is-enabled xinetd.service
|
||||||
|
not-found
|
||||||
|
#systemctl is-active xinetd.service
|
||||||
|
inactive
|
||||||
|
{"2.2.3. Cấu hình vô hiệu hoá X window server services" : "PASSED"}
|
||||||
|
{"2.2.4. Cấu hình vô hiệu hoá avahi daemon services" : "PASSED"}
|
||||||
|
{"2.2.5. Cấu hình vô hiệu hoá cups services" : "PASSED"}
|
||||||
|
{"2.2.6. Cấu hình vô hiệu hoá dhcp server services" : "PASSED"}
|
||||||
|
{"2.2.7. Cấu hình vô hiệu hoá ldap server services" : "PASSED"}
|
||||||
|
{"2.2.6. Cấu hình vô hiệu hoá NFS" : "PASSED"}
|
||||||
|
{"2.2.8. Cấu hình vô hiệu hoá dns server services" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"2.2.9. Cấu hình vô hiệu hoá dnsmasq services" : "PASSED"}
|
||||||
|
----------[PASSED]----------
|
||||||
|
#grep -E "\s[7-9].[0-9]" /etc/os-release
|
||||||
|
----------[PASSED]----------
|
||||||
|
#dpkg-query -l dnsmasq
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-enabled dnsmasq.service
|
||||||
|
not-found
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-active dnsmasq.service
|
||||||
|
inactive
|
||||||
|
----------LOG----------
|
||||||
|
PRETTY_NAME="Ubuntu 24.04.4 LTS"
|
||||||
|
NAME="Ubuntu"
|
||||||
|
VERSION_ID="24.04"
|
||||||
|
VERSION="24.04.4 LTS (Noble Numbat)"
|
||||||
|
VERSION_CODENAME=noble
|
||||||
|
ID=ubuntu
|
||||||
|
ID_LIKE=debian
|
||||||
|
HOME_URL="https://www.ubuntu.com/"
|
||||||
|
SUPPORT_URL="https://help.ubuntu.com/"
|
||||||
|
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
|
||||||
|
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
|
||||||
|
UBUNTU_CODENAME=noble
|
||||||
|
LOGO=ubuntu-logo
|
||||||
|
{"2.2.10. Cấu hình vô hiệu hoá ftp server services" : "PASSED"}
|
||||||
|
{"2.2.12. Cấu hình vô hiệu hoá web server services" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"2.2.11. Cấu hình vô hiệu hoá tftp server services" : "PASSED"}
|
||||||
|
----------[PASSED]----------
|
||||||
|
#grep -E "\s[7-9].[0-9]" /etc/os-release
|
||||||
|
----------[PASSED]----------
|
||||||
|
#dpkg -l | grep tftpd-hpa
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-enabled tftp.socket
|
||||||
|
not-found
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-enabled tftpd-hpa.service
|
||||||
|
not-found
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-active tftp.socket
|
||||||
|
inactive
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-active tftpd-hpa.service
|
||||||
|
inactive
|
||||||
|
----------LOG----------
|
||||||
|
{"2.2.13. Cấu hình vô hiệu hoá imap and pop3 server services" : "PASSED"}
|
||||||
|
{"2.2.14. Cấu hình vô hiệu hoá samba file server services" : "PASSED"}
|
||||||
|
{"2.2.15. Cấu hình vô hiệu hoá web proxy server services" : "PASSED"}
|
||||||
|
{"2.2.16. Cấu hình vô hiệu hoá snmp services" : "PASSED"}
|
||||||
|
{"2.2.14. Cấu hình vô hiệu hoá telnetd" : "PASSED"}
|
||||||
|
{"2.2.15. Cấu hình vô hiệu hoá rsh-server" : "PASSED"}
|
||||||
|
{"2.2.18. Cấu hình mail transfer agents sang chế độ local-only" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"2.2.17. Cấu hình vô hiệu hoá nis server services" : "PASSED"}
|
||||||
|
----------[PASSED]----------
|
||||||
|
#dpkg -l | grep ypserv
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-enabled ypserv.service
|
||||||
|
not-found
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-active ypserv.service
|
||||||
|
inactive
|
||||||
|
----------LOG----------
|
||||||
|
#systemctl is-enabled ypserv.service
|
||||||
|
not-found
|
||||||
|
#systemctl is-active ypserv.service
|
||||||
|
inactive
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"2.2.19. Cấu hình vô hiệu hoá network file system services" : "PASSED"}
|
||||||
|
----------[PASSED]----------
|
||||||
|
#dpkg -l | grep nfs-kernel-server
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-enabled nfs-server.service
|
||||||
|
not-found
|
||||||
|
----------[PASSED]----------
|
||||||
|
#systemctl is-active nfs-server.service
|
||||||
|
inactive
|
||||||
|
----------LOG----------
|
||||||
|
#systemctl is-enabled nfs-server.service
|
||||||
|
not-found
|
||||||
|
#systemctl is-active nfs-server.service
|
||||||
|
inactive
|
||||||
|
{"2.2.20. Cấu hình vô hiệu hoá rsync services" : "PASSED"}
|
||||||
|
{"2.3.1. Cấu hình vô hiệu hoá nis client" : "PASSED"}
|
||||||
|
{"2.3.2. Cấu hình vô hiệu hoá rsh client" : "PASSED"}
|
||||||
|
{"2.3.3. Cấu hình vô hiệu hoá talk client" : "PASSED"}
|
||||||
|
{"2.3.4. Cấu hình vô hiệu hoá telnet client" : "PASSED"}
|
||||||
|
{"2.3.5. Cấu hình vô hiệu hoá ldap client" : "PASSED"}
|
||||||
|
{"2.3.6. Cấu hình vô hiệu hoá rpc" : "PASSED"}
|
||||||
|
{"2.3.7. Cấu hình vô hiệu hoá ftp client" : "PASSED"}
|
||||||
|
----------[PASSED]----------
|
||||||
|
#grep -E "\s[7-9].[0-9]" /etc/os-release
|
||||||
|
----------[PASSED]----------
|
||||||
|
#dpkg -l | grep ftp
|
||||||
|
ii openssh-sftp-server 1:9.6p1-3ubuntu13.16 amd64 secure shell (SSH) sftp server module, for SFTP access from remote machines
|
||||||
|
ii tnftp 20230507-2build3 amd64 enhanced ftp client
|
||||||
|
----------LOG----------
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"3.1.1. Cấu hình vô hiệu hoá IP forwarding" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"3.1.2. Cấu hình vô hiệu hoá tính năng chuyển hướng gói tin (packet redirect)" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"3.2.1. Cấu hình từ chối các gói tin với nguồn được định tuyến trước" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"3.2.2. Cấu hình từ chối các ICMP redirect message" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"3.2.3. Cấu hình từ chối các secure ICMP redirect message" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"3.2.4. Cấu hình từ chối các gói tin ICMP request broadcast" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"3.2.5. Cấu hình bỏ qua phản hồi ICMP không hợp lệ" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"3.2.6. Cấu hình Reverse Path Filtering" : "PASSED"}
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"3.3.1. Cấu hình từ chối các gói tin với nguồn được định tuyến trước (Source Routed Packet Acceptance)" : "PASSED"}
|
||||||
|
{"3.3.2. Cấu hình từ chối các ICMP redirect message" : "PASSED"}
|
||||||
|
{"3.3.3. Cấu hình từ chối các secure ICMP redirect message" : "PASSED"}
|
||||||
|
{"3.3.4. Cấu hình từ chối các gói tin ICMP request broadcast" : "PASSED"}
|
||||||
|
{"3.3.5. Cấu hình bỏ qua phản hồi ICMP không hợp lệ" : "PASSED"}
|
||||||
|
{"3.3.6. Cấu hình Reverse Path Filtering" : "PASSED"}
|
||||||
|
{"3.2.7. Cấu hình TCP SYN Cookies" : "PASSED"}
|
||||||
|
{"3.3.8. Cấu hình từ chối IPv6 router advertisements" : "PASSED"}
|
||||||
|
{"3.4.1.1. Cấu hình kích hoạt ufw" : "PASSED"}
|
||||||
|
{"3.4.2.1. Cấu hình kích hoạt Iptables" : "PASSED"}
|
||||||
|
{"3.4.1.2. Cấu hình vô hiệu hoá iptables-persistent, nftables khi sử dụng ufw" : "PASSED"}
|
||||||
|
{"3.4.1.3. Cấu hình ufw loopback traffic" : "PASSED"}
|
||||||
|
{"3.4.1.4. Cấu hình ufw rule cho tất cả các port và protocol đang mở" : "PASSED"}
|
||||||
|
######################################
|
||||||
|
# Các rule UFW hiện tại:
|
||||||
|
-------------------
|
||||||
|
Status: active
|
||||||
|
|
||||||
|
To Action From
|
||||||
|
-- ------ ----
|
||||||
|
[ 1] Anywhere on lo ALLOW IN Anywhere
|
||||||
|
[ 2] Anywhere ALLOW OUT Anywhere on lo (out)
|
||||||
|
[ 3] Anywhere DENY IN 127.0.0.0/8
|
||||||
|
[ 4] 22/tcp ALLOW IN Anywhere
|
||||||
|
[ 5] 53 ALLOW OUT Anywhere (out)
|
||||||
|
[ 6] 80/tcp ALLOW OUT Anywhere (out)
|
||||||
|
[ 7] 443/tcp ALLOW OUT Anywhere (out)
|
||||||
|
[ 8] 123/udp ALLOW OUT Anywhere (out)
|
||||||
|
[ 9] Anywhere (v6) on lo ALLOW IN Anywhere (v6)
|
||||||
|
[10] Anywhere (v6) ALLOW OUT Anywhere (v6) on lo (out)
|
||||||
|
[11] Anywhere (v6) DENY IN ::1
|
||||||
|
[12] 22/tcp (v6) ALLOW IN Anywhere (v6)
|
||||||
|
[13] 53 (v6) ALLOW OUT Anywhere (v6) (out)
|
||||||
|
[14] 80/tcp (v6) ALLOW OUT Anywhere (v6) (out)
|
||||||
|
[15] 443/tcp (v6) ALLOW OUT Anywhere (v6) (out)
|
||||||
|
[16] 123/udp (v6) ALLOW OUT Anywhere (v6) (out)
|
||||||
|
|
||||||
|
-------------------
|
||||||
|
# Inbound rules (ALLOW):
|
||||||
|
Anywhere on lo ALLOW Anywhere
|
||||||
|
22/tcp ALLOW Anywhere
|
||||||
|
Anywhere (v6) on lo ALLOW Anywhere (v6)
|
||||||
|
22/tcp (v6) ALLOW Anywhere (v6)
|
||||||
|
-------------------
|
||||||
|
# Outbound rules (ALLOW OUT):
|
||||||
|
Anywhere ALLOW OUT Anywhere on lo
|
||||||
|
53 ALLOW OUT Anywhere
|
||||||
|
80/tcp ALLOW OUT Anywhere
|
||||||
|
443/tcp ALLOW OUT Anywhere
|
||||||
|
123/udp ALLOW OUT Anywhere
|
||||||
|
Anywhere (v6) ALLOW OUT Anywhere (v6) on lo
|
||||||
|
53 (v6) ALLOW OUT Anywhere (v6)
|
||||||
|
80/tcp (v6) ALLOW OUT Anywhere (v6)
|
||||||
|
443/tcp (v6) ALLOW OUT Anywhere (v6)
|
||||||
|
123/udp (v6) ALLOW OUT Anywhere (v6)
|
||||||
|
-------------------
|
||||||
|
# IPv6 rules:
|
||||||
|
Anywhere (v6) on lo ALLOW Anywhere (v6)
|
||||||
|
Anywhere (v6) DENY ::1
|
||||||
|
22/tcp (v6) ALLOW Anywhere (v6)
|
||||||
|
Anywhere (v6) ALLOW OUT Anywhere (v6) on lo
|
||||||
|
53 (v6) ALLOW OUT Anywhere (v6)
|
||||||
|
80/tcp (v6) ALLOW OUT Anywhere (v6)
|
||||||
|
443/tcp (v6) ALLOW OUT Anywhere (v6)
|
||||||
|
123/udp (v6) ALLOW OUT Anywhere (v6)
|
||||||
|
######################################
|
||||||
|
{"3.4.1.5. Cấu hình chính sách từ chối mặc định cho ufw" : "PASSED"}
|
||||||
|
{"3.4.2.2. Cấu hình vô hiệu hoá ufw, nftables khi sử dụng iptables" : "PASSED"}
|
||||||
|
{"3.4.2.3. Cấu hình iptables loopback traffic" : "PASSED"}
|
||||||
|
{"3.4.2.4. Cấu hình iptables rule cho tất cả các port và protocol đang mở" : "PASSED"}
|
||||||
|
{"3.4.2.5. Cấu hình chính sách từ chối mặc định cho iptables" : "PASSED"}
|
||||||
|
{"4.1.1.1. Cấu hình kích hoạt rsyslog service" : "PASSED"}
|
||||||
|
{"4.1.1.2. Phân quyền đối với file log sinh ra từ rsyslog" : "PASSED"}
|
||||||
|
{"4.1.1.3. Cấu hình lưu trữ log sinh ra từ rsyslog tập trung" : "FAILED"}
|
||||||
|
######################################
|
||||||
|
# Chỉnh sửa file /etc/rsyslog.conf và /etc/rsyslog.d/*.conf và thêm vào 1 trong các dòng sau:
|
||||||
|
# <files to sent to the remote log server> action(type="omfwd" target="<FQDN or ip of loghost>" port="<port number>" protocol="tcp" action.resumeRetryCount="<number of re-tries>" queue.type="LinkedList" queue.size=<number of messages to queue>") # Hoặc
|
||||||
|
# *.* @@< FQDN or ip of loghost >
|
||||||
|
# Thực hiện câu lệnh sau để khởi động lại rsyslog:
|
||||||
|
# systemctl restart rsyslog
|
||||||
|
-------------------
|
||||||
|
#grep -E '^\h*([^#]+\s+)?action\(([^#]+\s+)?\btarget=\?[^#"]+\?\b' /etc/rsyslog.conf /etc/rsyslog.d/*.conf 2>/dev/null -ne 0
|
||||||
|
######################################
|
||||||
|
|
||||||
|
######################################
|
||||||
|
{"4.1.1.4. Phân quyền đối với tất cả các file log" : "PASSED"}
|
||||||
|
{"4.1.2. Phân quyền đối với tất cả các file log" : "PASSED"}
|
||||||
|
{"5.1.1. Cấu hình kích hoạt cron daemon" : "PASSED"}
|
||||||
|
{"5.1.2. Cấu hình phân quyền cho file /etc/crontab" : "PASSED"}
|
||||||
|
{"5.1.3. Cấu hình phân quyền cho file /etc/cron.hourly" : "PASSED"}
|
||||||
|
{"5.1.4. Cấu hình phân quyền cho file /etc/cron.daily" : "PASSED"}
|
||||||
|
{"5.1.5. Cấu hình phân quyền cho file /etc/cron.weekly" : "PASSED"}
|
||||||
|
{"5.1.6. Cấu hình phân quyền cho của file /etc/cron.monthly" : "PASSED"}
|
||||||
|
{"5.1.7. Cấu hình phân quyền cho file /etc/cron.d" : "PASSED"}
|
||||||
|
{"5.1.8. Cấu hình at/cron hạn chế chỉ cho người dùng được ủy quyền" : "PASSED"}
|
||||||
|
{"5.2.1. Cấu hình phân quyền cho file /etc/ssh/sshd_config" : "PASSED"}
|
||||||
|
{"5.2.2. Cấu hình phân quyền cho các file SSH private host key" : "PASSED"}
|
||||||
|
{"5.2.3. Cấu hình phân quyền cho các file SSH public host key" : "PASSED"}
|
||||||
|
{"5.2.4. Cấu hình giới hạn truy cập cho máy chủ SSH" : "PASSED"}
|
||||||
|
{"5.2.5. Cấu hình LogLevel cho máy chủ SSH" : "PASSED"}
|
||||||
|
{"5.2.6. Cấu hình sử dụng SSH PAM" : "PASSED"}
|
||||||
|
{"5.2.7. Cấu hình vô hiệu hoá đăng nhập bằng root cho máy chủ SSH" : "PASSED"}
|
||||||
|
{"5.2.8. Cấu hình vô hiệu hoá HostbasedAuthentication cho máy chủ SSH" : "PASSED"}
|
||||||
|
{"5.2.9. Cấu hình vô hiệu hoá PermitEmptyPasswords cho máy chủ SSH" : "PASSED"}
|
||||||
|
{"5.2.10. Cấu hình vô hiệu hoá PermitUserEnviroment cho máy chủ SSH" : "PASSED"}
|
||||||
|
{"5.2.11. Cấu hình vô hiệu hoá IgnoreRhosts cho máy chủ SSH" : "PASSED"}
|
||||||
|
{"5.2.12. Cấu hình vô hiệu hoá X11 Forwarding cho máy chủ SSH" : "PASSED"}
|
||||||
|
{"5.2.13. Cấu hình sử dụng các thuật toán mã hoá được cho phép" : "PASSED"}
|
||||||
|
{"5.2.14. Cấu hình các thuật toán MAC được cho phép" : "PASSED"}
|
||||||
|
{"5.2.15. Cấu hình thuật toán trao đổi khoá được cho phép" : "PASSED"}
|
||||||
|
{"5.2.16. Cấu hình vô hiệu hoá SSH AllowTcpForwarding" : "PASSED"}
|
||||||
|
{"5.2.17. Cấu hình cảnh báo SSH" : "PASSED"}
|
||||||
|
{"5.2.18. Cấu hình SSH MaxAuthTries" : "PASSED"}
|
||||||
|
{"5.2.19. Cấu hình SSH MaxStartups" : "PASSED"}
|
||||||
|
{"5.2.20. Cấu hình SSH MaxSessions" : "PASSED"}
|
||||||
|
{"5.2.21. Cấu hình SSH LoginGraceTime" : "PASSED"}
|
||||||
|
{"5.2.22. Cấu hình khoảng thời gian chờ không hoạt động cho máy chủ SSH" : "PASSED"}
|
||||||
|
{"5.3.1. Cấu hình điều kiện tạo mật khẩu" : "PASSED"}
|
||||||
|
{"5.3.2. Cấu hình khoá truy cập do nhiều lần nhập mật khẩu thất bại" : "PASSED"}
|
||||||
|
{"5.3.3. Giới hạn việc sử dụng lại mật khẩu" : "PASSED"}
|
||||||
|
{"5.3.4. Cấu hình thuật toán hash mật khẩu mạnh" : "FAILED"}
|
||||||
|
######################################
|
||||||
|
# Đối với phiên bản 22.04 trở lên:
|
||||||
|
# Chỉnh sửa file /etc/pam.d/common-password đảm bảo rằng không có bất cứ thuật toán hash nào được cấu hình trong pam_unix.so như dưới đây:
|
||||||
|
# password [success=1 default=ignore] pam_unix.so remember=5
|
||||||
|
# Thêm hoặc chỉnh sửa ENCRYPT_METHOD trong file /etc/login.defs như sau:
|
||||||
|
# ENCRYPT_METHOD yescrypt
|
||||||
|
# Đối với phiên bản 20.04 trở về trước: Chỉnh sửa file /etc/pam.d/common-password để có tuỳ chọn sha512 cho pam_unix.so như dưới đây:
|
||||||
|
# password [success=1 default=ignore] pam_unix.so sha512 remember=5
|
||||||
|
-------------------
|
||||||
|
#grep -P "^\h*password\h*\[success=1 default=ignore\]\h*pam_unix.so.*sha512" /etc/pam.d/common-password -ne 0
|
||||||
|
-------------------
|
||||||
|
#grep -v ^# /etc/pam.d/common-password | grep -E "(yescrypt|md5|bigcrypt|sha256|sha512|blowfish)" -eq 0
|
||||||
|
password [success=1 default=ignore] pam_unix.so obscure use_authtok try_first_pass yescrypt
|
||||||
|
######################################
|
||||||
|
{"5.4.1.1. Cấu hình thời gian tối thiểu giữa những lần thay đổi mật khẩu" : "PASSED"}
|
||||||
|
{"5.4.1.2. Cấu hình thời gian hết hạn sử dụng mật khẩu" : "PASSED"}
|
||||||
|
{"5.4.1.3. Cấu hình thời gian cảnh báo mật khẩu hết hạn" : "PASSED"}
|
||||||
|
{"5.4.1.4. Cấu hình thời gian khoá tài khoản không thay mật khẩu sau khi hết hạn" : "PASSED"}
|
||||||
|
{"5.4.1.5. Đảm bảo thời gian thay đổi mật khẩu lần cuối hợp lệ" : "PASSED"}
|
||||||
|
{"5.4.2. Cấu hình vô hiệu hoá đăng nhập bằng tài khoản hệ thống" : "PASSED"}
|
||||||
|
{"5.4.3. Cấu hình group mặc định của tài khoản root" : "PASSED"}
|
||||||
|
{"5.4.4. Cấu hình user umask mặc định" : "PASSED"}
|
||||||
|
{"5.4.5. Cấu hình shell timeout mặc định" : "PASSED"}
|
||||||
|
{"5.4.6. Cấu hình hạn chế truy cập cho câu lệnh su" : "PASSED"}
|
||||||
|
{"6.1.1. Cấu hình phân quyền cho file /etc/passwd" : "PASSED"}
|
||||||
|
{"6.1.2. Cấu hình phân quyền cho file /etc/shadow" : "PASSED"}
|
||||||
|
{"6.1.3. Cấu hình phân quyền cho file /etc/group" : "PASSED"}
|
||||||
|
{"6.1.4. Cấu hình phân quyền cho file /etc/gshadow" : "PASSED"}
|
||||||
|
{"6.1.5. Cấu hình phân quyền cho file /etc/passwd-" : "PASSED"}
|
||||||
|
{"6.1.6. Cấu hình phân quyền cho file /etc/shadow-" : "PASSED"}
|
||||||
|
{"6.1.7. Cấu hình phân quyền cho file /etc/group-" : "PASSED"}
|
||||||
|
{"6.1.8. Cấu hình phân quyền cho file /etc/gshadow-" : "PASSED"}
|
||||||
|
{"6.1.9. Đảm bảo không có file world-writable tồn tại" : "PASSED"}
|
||||||
|
{"6.1.10. Đảm bảo các file hoặc thư mục không có chủ sở hữu không tồn tại" : "PASSED"}
|
||||||
|
{"6.1.11. Đảm bảo các file hoặc thư mục không có nhóm không tồn tại" : "PASSED"}
|
||||||
|
{"6.2.1. Cấu hình tài khoản trong /etc/passwd sử dụng shadowed passwords" : "PASSED"}
|
||||||
|
{"6.2.2. Đảm bảo trường mật khẩu không để trống" : "PASSED"}
|
||||||
|
{"6.2.3. Đảm bảo mọi nhóm trong file /etc/passwd tồn tại trong file /etc/group" : "PASSED"}
|
||||||
|
{"6.2.4. Đảm bảo shadow group rỗng" : "PASSED"}
|
||||||
|
{"6.2.5. Đảm bảo UID không bị lặp" : "PASSED"}
|
||||||
|
{"6.2.6. Đảm bảo GID không bị lặp" : "PASSED"}
|
||||||
|
{"6.2.7. Đảm bảo tên người dùng không bị lặp" : "PASSED"}
|
||||||
|
{"6.2.8. Đảm bảo tên group không bị lặp" : "PASSED"}
|
||||||
|
{"6.2.9. Đảm bảo tính toàn vẹn cho biến môi trường PATH của root" : "FAILED"}
|
||||||
|
######################################
|
||||||
|
# Thực hiện đoạn script sau để kiểm tra tính toàn vẹn cho môi trường PATH của root:
|
||||||
|
# RPCV="$(sudo -Hiu root env 2>/dev/null | grep '^PATH=' | cut -d= -f2)"; echo "$RPCV" | grep -q "::" && echo "root's path contains a empty directory (::)"; echo "$RPCV" | grep -q ":$" && echo "root's path contains a trailing (:)" ; for x in $(echo "$RPCV" | tr ":" " "); do if [ -d "$x" ]; then ls -ldH "$x" | awk '$9 == "." {print "PATH contains current working directory (.)"} $3 != "root" {print $9, "is not owned by root"} substr($1,6,1) != "-" {print $9, "is group writable"} substr($1,9,1) != "-" {print $9, "is world writable"}'; else echo "$x is not a directory"; fi; done
|
||||||
|
# Sửa chữa hoặc lý giải kết quả tìm được.
|
||||||
|
-------------------
|
||||||
|
#RPCV="$(sudo -Hiu root env 2>/dev/null | grep '^PATH=' | cut -d= -f2)"; echo "$RPCV" | grep -q "::" && echo "root's path contains a empty directory (::)"; echo "$RPCV" | grep -q ":$" && echo "root's path contains a trailing (:)" ; for x in $(echo "$RPCV" | tr ":" " "); do if [ -d "$x" ]; then ls -ldH "$x" | awk '$9 == "." {print "PATH contains current working directory (.)"} $3 != "root" {print $9, "is not owned by root"} substr($1,6,1) != "-" {print $9, "is group writable"} substr($1,9,1) != "-" {print $9, "is world writable"}'; else echo "$x is not a directory"; fi; done | grep -v "/root/bin" -eq 0
|
||||||
|
/snap/bin is not a directory
|
||||||
|
######################################
|
||||||
|
{"6.2.10. Đảm bảo root là tài khoản duy nhất có UID là 0" : "PASSED"}
|
||||||
|
{"6.2.11. Đảm bảo mọi người dùng đều tồn tại thư mục home" : "PASSED"}
|
||||||
|
{"6.2.12. Đảm bảo người dùng sở hữu thư mục home của chính họ" : "PASSED"}
|
||||||
|
{"6.2.13. Đảm bảo quyền thư mục home của người dùng có mức bảo mật cao" : "FAILED"}
|
||||||
|
######################################
|
||||||
|
# Thực hiện đoạn script sau để đặt quyền cho thư mục home của tất cả người dùng là 750:
|
||||||
|
# awk -F: '($1\!~/(halt|sync|shutdown|nfsnobody)/ && $7\!~/^(\/usr)?\/sbin\/nologin(\/)?$/ && $7\!~/(\/usr)?\/bin\/false(\/)?$/) {print $6}' /etc/passwd | while read -r dir; do if [ -d "$dir" ]; then dirperm=$(stat -L -c "%A" "$dir"); if [ "$(echo "$dirperm" | cut -c6)" != "-" ] || [ "$(echo "$dirperm" | cut -c8)" != "-" ] || [ "$(echo "$dirperm" | cut -c9)" != "-" ] || [ "$(echo "$dirperm" | cut -c10)" != "-" ]; then chmod g-w,o-rwx "$dir"; fi; fi; done
|
||||||
|
-------------------
|
||||||
|
#awk -F: '($1\!~/(halt|sync|shutdown|nfsnobody)/ && $7\!~/^(\/usr)?\/sbin\/nologin(\/)?$/ && $7\!~/(\/usr)?\/bin\/false(\/)?$/) {print $1 " " $6}' /etc/passwd | while read -r user dir; do if [ ! -d "$dir" ]; then echo "User: \"$user\" home directory: \"$dir\" doesn't exist"; else dirperm=$(stat -L -c "%A" "$dir"); if [ "$(echo "$dirperm" | cut -c6)" != "-" ] || [ "$(echo "$dirperm" | cut -c8)" != "-" ] || [ "$(echo "$dirperm" | cut -c9)" != "-" ] || [ "$(echo "$dirperm" | cut -c10)" != "-" ]; then echo "User: \"$user\" home directory: \"$dir\" has permissions: \"$(stat -L -c "%a" "$dir")\""; fi; fi; done -eq 0
|
||||||
|
User: "cicd_to2" home directory: "/home/cicd_to2" has permissions: "755"
|
||||||
|
User: "landesk" home directory: "/opt/landesk" has permissions: "755"
|
||||||
|
######################################
|
||||||
|
{"6.2.14. Đảm bảo không người dùng nào có file .netrc" : "PASSED"}
|
||||||
|
{"6.2.15. Đảm bảo không người dùng nào có file .forward" : "PASSED"}
|
||||||
|
{"6.2.16. Đảm bảo không người dùng nào có file .rhosts" : "PASSED"}
|
||||||
|
{"6.2.17. Đảm bảo các file dot của người dùng không cấp quyền write cho group hoặc other" : "PASSED"}
|
||||||
+1
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,60 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Script chẩn đoán và kiểm tra chi tiết điều kiện AIDE (1.3.1 & 1.3.2)
|
||||||
|
|
||||||
|
echo "=========================================================="
|
||||||
|
echo " KIỂM TRA CHẨN ĐOÁN CHI TIẾT AIDE"
|
||||||
|
echo "=========================================================="
|
||||||
|
echo "Thời gian: $(date)"
|
||||||
|
echo "Người dùng chạy script: $(whoami)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "--- [BƯỚC 1] Kiểm tra gói phần mềm AIDE (Điều kiện 1.3.1) ---"
|
||||||
|
echo "1.1 Kiểm tra bằng lệnh 'rpm -q aide':"
|
||||||
|
rpm -q aide 2>&1 | sed 's/^/ -> /'
|
||||||
|
if rpm -q aide >/dev/null 2>&1 || rpm -qa | grep -qi "^aide"; then
|
||||||
|
echo " => [PASSED] Gói AIDE đã được cài đặt."
|
||||||
|
else
|
||||||
|
echo " => [FAILED] Gói AIDE chưa được cài đặt."
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "--- [BƯỚC 2] Kiểm tra lịch chạy định kỳ (Điều kiện 1.3.2) ---"
|
||||||
|
|
||||||
|
echo "2.1 Kiểm tra 'crontab -l' của user hiện tại ($(whoami)):"
|
||||||
|
crontab -l 2>/dev/null | grep -v "^#" | sed 's/^/ -> /'
|
||||||
|
if crontab -l 2>/dev/null | grep -Eq '^\s*[^#].*\baide\b.*--check'; then
|
||||||
|
echo " => [PASSED] Tìm thấy lệnh chạy AIDE trong crontab hiện tại!"
|
||||||
|
else
|
||||||
|
echo " => [KHÔNG TÌM THẤY] Không khớp lệnh aide --check trong crontab -l."
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "2.2 Kiểm tra trong các file hệ thống /etc/crontab và /etc/cron* và /var/spool/cron:"
|
||||||
|
grep -rnE '^\s*[^#].*\baide\b' /etc/cron* /var/spool/cron 2>/dev/null | sed 's/^/ -> /'
|
||||||
|
if grep -rsEq '^\s*[^#].*\baide\b.*--check' /etc/cron* /var/spool/cron 2>/dev/null; then
|
||||||
|
echo " => [PASSED] Tìm thấy lệnh chạy AIDE trong các file cron hệ thống!"
|
||||||
|
else
|
||||||
|
echo " => [KHÔNG TÌM THẤY] Không có lệnh aide --check trong /etc/cron* hoặc /var/spool/cron."
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "2.3 Kiểm tra systemd timer cho AIDE:"
|
||||||
|
systemctl list-timers --all 2>/dev/null | grep -i aide | sed 's/^/ -> /'
|
||||||
|
if systemctl is-enabled aidecheck.timer >/dev/null 2>&1 || systemctl is-enabled aide-check.timer >/dev/null 2>&1; then
|
||||||
|
echo " => [PASSED] Tìm thấy systemd timer cho AIDE được kích hoạt!"
|
||||||
|
else
|
||||||
|
echo " => [KHÔNG TÌM THẤY] Không có systemd timer aidecheck."
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "=========================================================="
|
||||||
|
echo "--- TỔNG KẾT ĐÁNH GIÁ 1.3.2 ---"
|
||||||
|
if (crontab -l 2>/dev/null | grep -Eq '^\s*[^#].*\baide\b.*--check') || \
|
||||||
|
grep -rsEq '^\s*[^#].*\baide\b.*--check' /etc/cron* /var/spool/cron 2>/dev/null || \
|
||||||
|
systemctl is-enabled aidecheck.timer 2>/dev/null | grep -q "^enabled" || \
|
||||||
|
systemctl is-enabled aide-check.timer 2>/dev/null | grep -q "^enabled"; then
|
||||||
|
echo "=> KẾT QUẢ CUỐI CÙNG: PASSED (Đủ điều kiện 1.3.2)"
|
||||||
|
else
|
||||||
|
echo "=> KẾT QUẢ CUỐI CÙNG: FAILED (Chưa thỏa mãn điều kiện 1.3.2)"
|
||||||
|
fi
|
||||||
|
echo "=========================================================="
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "=========================================================="
|
||||||
|
echo " TEST SCRIPT: KIỂM TRA LỖI FIREWALL DEFAULT DENY "
|
||||||
|
echo "=========================================================="
|
||||||
|
|
||||||
|
echo "[1] Trạng thái của firewalld theo systemctl:"
|
||||||
|
raw_status=$(systemctl is-active firewalld 2>&1)
|
||||||
|
echo " -> Output trả về: [$raw_status]"
|
||||||
|
|
||||||
|
FIREWALLD_ACTIVE=0
|
||||||
|
if echo "$raw_status" | grep -q "^active"; then
|
||||||
|
FIREWALLD_ACTIVE=1
|
||||||
|
echo " -> Đánh giá: FIREWALLD_ACTIVE = 1 (Đã nhận diện đang chạy)"
|
||||||
|
else
|
||||||
|
echo " -> Đánh giá: FIREWALLD_ACTIVE = 0 (Không nhận diện được firewalld qua systemctl)"
|
||||||
|
fi
|
||||||
|
echo "----------------------------------------------------------"
|
||||||
|
|
||||||
|
echo "[2] Đọc các active zone từ firewall-cmd:"
|
||||||
|
active_zones=$(firewall-cmd --list-all-zones 2>/dev/null | grep "active")
|
||||||
|
if [ -z "$active_zones" ]; then
|
||||||
|
echo " -> KHÔNG TÌM THẤY BẤT KỲ ACTIVE ZONE NÀO! (Hoặc firewall-cmd bị lỗi)"
|
||||||
|
else
|
||||||
|
echo " -> Các zone đang active tìm thấy:"
|
||||||
|
echo "$active_zones" | while read -r line; do
|
||||||
|
echo " - $line"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
echo "----------------------------------------------------------"
|
||||||
|
|
||||||
|
echo "[3] Phân tích chi tiết qua awk (chế độ DEBUG):"
|
||||||
|
fw_default_deny=$(firewall-cmd --list-all-zones 2>/dev/null | awk '
|
||||||
|
BEGIN {
|
||||||
|
print " [AWK] Bắt đầu đọc output từ firewall-cmd..." > "/dev/stderr"
|
||||||
|
bad=0
|
||||||
|
}
|
||||||
|
/\(active\)/ {
|
||||||
|
in_active=1;
|
||||||
|
print " [AWK] >> Vào active zone: " $0 > "/dev/stderr"
|
||||||
|
next
|
||||||
|
}
|
||||||
|
/^[^ \t\r\n]/ && !/\(active\)/ {
|
||||||
|
if (in_active == 1) {
|
||||||
|
print " [AWK] << Thoát active zone vì gặp dòng: " $0 > "/dev/stderr"
|
||||||
|
}
|
||||||
|
in_active=0
|
||||||
|
}
|
||||||
|
in_active && /target:/ {
|
||||||
|
t = $2
|
||||||
|
gsub(/%%/, "", t)
|
||||||
|
print " [AWK] -- Bắt được target: [" t "]" > "/dev/stderr"
|
||||||
|
if (t != "DROP" && t != "REJECT") {
|
||||||
|
print " [AWK] -- [!] PHÁT HIỆN VI PHẠM: [" t "] không phải là DROP hay REJECT" > "/dev/stderr"
|
||||||
|
bad=1
|
||||||
|
} else {
|
||||||
|
print " [AWK] -- [v] ĐẠT: [" t "] hợp lệ" > "/dev/stderr"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
print " [AWK] Kết thúc phân tích. Giá trị biến bad = " bad > "/dev/stderr"
|
||||||
|
print (bad ? "0" : "1")
|
||||||
|
}
|
||||||
|
')
|
||||||
|
|
||||||
|
echo "----------------------------------------------------------"
|
||||||
|
echo "[4] TỔNG HỢP KẾT QUẢ:"
|
||||||
|
echo " -> Giá trị biến fw_default_deny: [$fw_default_deny]"
|
||||||
|
|
||||||
|
if [ "$FIREWALLD_ACTIVE" -eq 1 ]; then
|
||||||
|
if [ "$fw_default_deny" = "1" ]; then
|
||||||
|
echo " -> KẾT QUẢ CUỐI CÙNG: PASSED (Đạt chuẩn)"
|
||||||
|
else
|
||||||
|
echo " -> KẾT QUẢ CUỐI CÙNG: FAILED (Vi phạm)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " -> KẾT QUẢ CUỐI CÙNG: FAILED (Bị fail ngay từ bước check firewalld active)"
|
||||||
|
fi
|
||||||
|
echo "=========================================================="
|
||||||
@@ -0,0 +1,286 @@
|
|||||||
|
"""
|
||||||
|
Windows Audit Hardening Tool - v2.0
|
||||||
|
Usage: AuditTool.exe -p audit_cis_windows.ps1.enc
|
||||||
|
AuditTool.exe (uses bundled .enc file)
|
||||||
|
|
||||||
|
Build: python -m PyInstaller --onefile --console -n AuditTool ^
|
||||||
|
--add-data "audit_cis_windows.ps1.enc;." ^
|
||||||
|
windows_audit.py
|
||||||
|
|
||||||
|
Environment: Python 3.8+ (3.8 for Windows Server 2012 compatibility)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import base64
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from Crypto.Cipher import PKCS1_OAEP, AES
|
||||||
|
from Crypto.PublicKey import RSA
|
||||||
|
from Crypto.Util.Padding import unpad
|
||||||
|
from colorama import Fore, init as colorama_init
|
||||||
|
from unidecode import unidecode
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# Globals
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
colorama_init(strip=False, autoreset=True)
|
||||||
|
|
||||||
|
HOSTNAME = socket.gethostname()
|
||||||
|
try:
|
||||||
|
IP_ADDR = socket.gethostbyname(HOSTNAME)
|
||||||
|
except Exception:
|
||||||
|
IP_ADDR = "127.0.0.1"
|
||||||
|
|
||||||
|
AES_KEY_B64 = "fb8MH7yIuVUeCp5W4S9cKFtsHaxXIP/zvkO36wNNcO4="
|
||||||
|
BUNDLED_ENC = "audit_cis_windows.ps1.enc"
|
||||||
|
PUBLIC_KEY_FILE = "public_key.pem"
|
||||||
|
|
||||||
|
BANNER = r"""
|
||||||
|
_ _ _____ _____ _______ _ _ _____ _____ ______ _ _ _____ _ _ _____
|
||||||
|
/\ | | | | __ \_ _|__ __| | | | | /\ | __ \| __ \| ____| \ | |_ _| \ | |/ ____|
|
||||||
|
/ \ | | | | | | || | | | ______ | |__| | / \ | |__) | | | | |__ | \| | | | | \| | | __
|
||||||
|
/ /\ \| | | | | | || | | | |______| | __ | / /\ \ | _ /| | | | __| | . ` | | | | . ` | | |_ |
|
||||||
|
/ ____ \ |__| | |__| || |_ | | | | | |/ ____ \| | \ \| |__| | |____| |\ |_| |_| |\ | |__| |
|
||||||
|
/_/ \_\____/|_____/_____| |_| |_| |_/_/ \_\_| \_\_____/|______|_| \_|_____|_| \_|\_____|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# Path resolution (supports PyInstaller bundle)
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
def resource_path(relative_path):
|
||||||
|
"""Get absolute path to resource, works for dev and PyInstaller."""
|
||||||
|
if getattr(sys, "frozen", False):
|
||||||
|
base = sys._MEIPASS
|
||||||
|
else:
|
||||||
|
base = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
return os.path.join(base, relative_path)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_enc_path(user_path=None):
|
||||||
|
"""
|
||||||
|
Resolve the .enc file path.
|
||||||
|
Priority: user-supplied arg > bundled file > same-dir file.
|
||||||
|
Returns absolute path or None.
|
||||||
|
"""
|
||||||
|
if user_path:
|
||||||
|
if os.path.isfile(user_path):
|
||||||
|
return os.path.abspath(user_path)
|
||||||
|
print(Fore.YELLOW + "[WARNING] Provided path not found: {}".format(user_path) + Fore.RESET)
|
||||||
|
|
||||||
|
# Try PyInstaller bundled location
|
||||||
|
bundled = resource_path(BUNDLED_ENC)
|
||||||
|
if os.path.isfile(bundled):
|
||||||
|
return bundled
|
||||||
|
|
||||||
|
# Try same directory
|
||||||
|
same_dir = os.path.join(os.getcwd(), BUNDLED_ENC)
|
||||||
|
if os.path.isfile(same_dir):
|
||||||
|
return same_dir
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_public_key():
|
||||||
|
"""Find public_key.pem - bundled, same dir, or current dir."""
|
||||||
|
for loc in [
|
||||||
|
resource_path(PUBLIC_KEY_FILE),
|
||||||
|
os.path.join(os.getcwd(), PUBLIC_KEY_FILE),
|
||||||
|
]:
|
||||||
|
if os.path.isfile(loc):
|
||||||
|
return loc
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# Crypto helpers
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
def decrypt_aes(file_path, key_b64):
|
||||||
|
"""Decrypt an AES-ECB (PKCS7 padded) encrypted file."""
|
||||||
|
key = base64.b64decode(key_b64)
|
||||||
|
with open(file_path, "rb") as fh:
|
||||||
|
ct = fh.read()
|
||||||
|
cipher = AES.new(key, AES.MODE_ECB)
|
||||||
|
padded = cipher.decrypt(ct)
|
||||||
|
data = unpad(padded, AES.block_size, style="pkcs7")
|
||||||
|
return data.decode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def encrypt_output_rsa(content):
|
||||||
|
"""Encrypt output with RSA-OAEP; fall back to plaintext if no key."""
|
||||||
|
pk_path = resolve_public_key()
|
||||||
|
if pk_path is None:
|
||||||
|
return "PLAINTEXT:" + content
|
||||||
|
try:
|
||||||
|
pub_key = RSA.import_key(open(pk_path, "rb").read())
|
||||||
|
cipher = PKCS1_OAEP.new(pub_key)
|
||||||
|
raw = content.encode("utf-8")
|
||||||
|
block_size = 190
|
||||||
|
ciphertext = b""
|
||||||
|
for i in range(0, len(raw), block_size):
|
||||||
|
ciphertext += cipher.encrypt(raw[i : i + block_size])
|
||||||
|
return base64.b64encode(ciphertext).decode("utf-8")
|
||||||
|
except Exception as e:
|
||||||
|
print(Fore.YELLOW + "[WARNING] RSA encrypt failed: {}".format(e) + Fore.RESET)
|
||||||
|
return "PLAINTEXT:" + content
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# PowerShell runner
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
def run_powershell_script(script_content, timeout_sec=600):
|
||||||
|
"""
|
||||||
|
Write script to temp .ps1 file, execute via 'powershell -File',
|
||||||
|
return (stdout, stderr, returncode).
|
||||||
|
"""
|
||||||
|
tmp_path = None
|
||||||
|
try:
|
||||||
|
fd, tmp_path = tempfile.mkstemp(suffix=".ps1", prefix="audit_")
|
||||||
|
with os.fdopen(fd, "w", encoding="utf-8") as fh:
|
||||||
|
fh.write(script_content)
|
||||||
|
|
||||||
|
proc = subprocess.run(
|
||||||
|
[
|
||||||
|
"powershell.exe",
|
||||||
|
"-NoProfile",
|
||||||
|
"-ExecutionPolicy", "Bypass",
|
||||||
|
"-File", tmp_path,
|
||||||
|
],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=timeout_sec,
|
||||||
|
encoding="utf-8",
|
||||||
|
errors="replace",
|
||||||
|
)
|
||||||
|
return proc.stdout, proc.stderr, proc.returncode
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return "", "PowerShell execution timed out ({}s)".format(timeout_sec), -1
|
||||||
|
except Exception:
|
||||||
|
return "", traceback.format_exc(), -1
|
||||||
|
finally:
|
||||||
|
if tmp_path and os.path.exists(tmp_path):
|
||||||
|
try:
|
||||||
|
os.remove(tmp_path)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# Output parsing
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
def extract_audit_info(output):
|
||||||
|
"""Extract hostname + timestamp from audit output lines."""
|
||||||
|
hostname = HOSTNAME
|
||||||
|
timestamp = "unknown"
|
||||||
|
for line in output.splitlines():
|
||||||
|
if line.startswith("Hostname:"):
|
||||||
|
val = line.replace("Hostname:", "").strip()
|
||||||
|
hostname = val if val else HOSTNAME
|
||||||
|
elif line.startswith("Time:"):
|
||||||
|
val = line.replace("Time:", "").strip()
|
||||||
|
timestamp = val.replace("-", "_").replace(":", "_").replace(" ", "-") if val else "unknown"
|
||||||
|
return hostname, timestamp
|
||||||
|
|
||||||
|
|
||||||
|
def count_pass_fail(output):
|
||||||
|
"""Count PASSED/FAILED from JSON lines."""
|
||||||
|
passed = failed = 0
|
||||||
|
for line in output.splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith("{") and line.endswith("}"):
|
||||||
|
if '"PASSED"' in line or "'PASSED'" in line:
|
||||||
|
passed += 1
|
||||||
|
elif '"FAILED"' in line or "'FAILED'" in line:
|
||||||
|
failed += 1
|
||||||
|
return passed, failed
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# Main audit flow
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
def run_audit(enc_path=None):
|
||||||
|
"""Decrypt .enc, run PowerShell audit, encrypt+save results."""
|
||||||
|
print(Fore.BLUE + BANNER + Fore.RESET)
|
||||||
|
print(Fore.RED + "Running Audit Hardening v2.0" + Fore.RESET)
|
||||||
|
print(" Hostname : {}".format(HOSTNAME))
|
||||||
|
print(" IP : {}".format(IP_ADDR))
|
||||||
|
print("-" * 70)
|
||||||
|
|
||||||
|
# Step 0 – Resolve input file
|
||||||
|
enc_path = resolve_enc_path(enc_path)
|
||||||
|
if enc_path is None:
|
||||||
|
print(Fore.RED + "ERROR: No .enc file found.")
|
||||||
|
print(" Provide path: AuditTool.exe -p audit_cis_windows.ps1.enc")
|
||||||
|
print(" Or place {} in the same directory.".format(BUNDLED_ENC) + Fore.RESET)
|
||||||
|
return
|
||||||
|
|
||||||
|
print(" Source : {}".format(enc_path))
|
||||||
|
|
||||||
|
# Step 1 – Decrypt
|
||||||
|
print(" [1/4] Decrypting...")
|
||||||
|
try:
|
||||||
|
ps_content = decrypt_aes(enc_path, AES_KEY_B64)
|
||||||
|
except Exception as e:
|
||||||
|
print(Fore.RED + " FAILED: {}".format(e) + Fore.RESET)
|
||||||
|
return
|
||||||
|
print(" [1/4] OK - {} bytes".format(len(ps_content)))
|
||||||
|
|
||||||
|
# Step 2 – Execute PowerShell
|
||||||
|
print(" [2/4] Running PowerShell...")
|
||||||
|
stdout, stderr, rc = run_powershell_script(ps_content)
|
||||||
|
if rc != 0:
|
||||||
|
print(Fore.YELLOW + " [2/4] PowerShell rc={}: {}".format(rc, (stderr or "")[:200]) + Fore.RESET)
|
||||||
|
else:
|
||||||
|
print(" [2/4] OK - {} output lines".format(len(stdout.splitlines())))
|
||||||
|
|
||||||
|
# Step 3 – Combine
|
||||||
|
full_output = stdout
|
||||||
|
if stderr:
|
||||||
|
full_output += "\n[STDERR]\n" + stderr
|
||||||
|
if not full_output.strip():
|
||||||
|
print(Fore.RED + "ERROR: No output from PowerShell" + Fore.RESET)
|
||||||
|
return
|
||||||
|
|
||||||
|
passed, failed = count_pass_fail(full_output)
|
||||||
|
print(" [3/4] Audit: {} PASSED / {} FAILED".format(passed, failed))
|
||||||
|
|
||||||
|
# Step 4 – Encrypt & save
|
||||||
|
hostname_out, timestamp = extract_audit_info(stdout)
|
||||||
|
out_filename = "{}_{}.txt.enc".format(hostname_out, timestamp)
|
||||||
|
print(" [4/4] Encrypting -> {}".format(out_filename))
|
||||||
|
|
||||||
|
encrypted = encrypt_output_rsa(full_output)
|
||||||
|
with open(out_filename, "w", encoding="utf-8") as fh:
|
||||||
|
fh.write(encrypted)
|
||||||
|
|
||||||
|
if os.path.isfile(out_filename):
|
||||||
|
print(Fore.GREEN + "=" * 70 + Fore.RESET)
|
||||||
|
print(Fore.GREEN + " SUCCESS: {} | {} PASS / {} FAIL".format(
|
||||||
|
out_filename, passed, failed) + Fore.RESET)
|
||||||
|
print(Fore.GREEN + "=" * 70 + Fore.RESET)
|
||||||
|
else:
|
||||||
|
print(Fore.RED + " FAILED: Could not write output file" + Fore.RESET)
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# CLI
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Audit Hardening v2.0")
|
||||||
|
parser.add_argument(
|
||||||
|
"-p", "--path",
|
||||||
|
help="Path to encrypted .ps1.enc file (optional if bundled or in same dir)",
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
args = main()
|
||||||
|
run_audit(args.path)
|
||||||
Binary file not shown.
@@ -0,0 +1,38 @@
|
|||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
a = Analysis(
|
||||||
|
['windows_audit.py'],
|
||||||
|
pathex=[],
|
||||||
|
binaries=[],
|
||||||
|
datas=[('audit_cis_windows.ps1', '.')],
|
||||||
|
hiddenimports=['Crypto.Cipher', 'Crypto.PublicKey', 'Crypto.Util.Padding'],
|
||||||
|
hookspath=[],
|
||||||
|
hooksconfig={},
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
noarchive=False,
|
||||||
|
optimize=0,
|
||||||
|
)
|
||||||
|
pyz = PYZ(a.pure)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.datas,
|
||||||
|
[],
|
||||||
|
name='AuditTool',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
runtime_tmpdir=None,
|
||||||
|
console=True,
|
||||||
|
disable_windowed_traceback=False,
|
||||||
|
argv_emulation=False,
|
||||||
|
target_arch=None,
|
||||||
|
codesign_identity=None,
|
||||||
|
entitlements_file=None,
|
||||||
|
)
|
||||||
@@ -0,0 +1,442 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import print_function
|
||||||
|
import base64, os, socket, subprocess, tempfile, sys, binascii
|
||||||
|
import re
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# ---- Compatibility for CentOS 6 (Py2.6/2.7) ----
|
||||||
|
# Safe print (UTF-8, hỗ trợ stream=file)
|
||||||
|
def _p(msg, color=None, stream=None, **kwargs):
|
||||||
|
# Back-compat cho calls kiểu file=...
|
||||||
|
if stream is None and 'file' in kwargs:
|
||||||
|
stream = kwargs['file']
|
||||||
|
if stream is None:
|
||||||
|
stream = sys.stdout
|
||||||
|
try:
|
||||||
|
unicode # Py2 check
|
||||||
|
b = msg.encode('utf-8') if isinstance(msg, unicode) else msg
|
||||||
|
except NameError:
|
||||||
|
b = msg
|
||||||
|
try:
|
||||||
|
if color:
|
||||||
|
stream.write(color)
|
||||||
|
stream.write(b)
|
||||||
|
if color:
|
||||||
|
try:
|
||||||
|
stream.write(Fore.RESET)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
stream.write("\n")
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
stream.write((u'' + msg).encode('utf-8') + "\n")
|
||||||
|
except Exception:
|
||||||
|
sys.stdout.write((u'' + msg).encode('utf-8') + "\n")
|
||||||
|
|
||||||
|
# optparse (có sẵn trên Py2.6)
|
||||||
|
try:
|
||||||
|
from optparse import OptionParser
|
||||||
|
except Exception:
|
||||||
|
OptionParser = None # fallback rất cũ
|
||||||
|
|
||||||
|
# Color handling (tùy chọn)
|
||||||
|
try:
|
||||||
|
from colorama import Fore, init as colorama_init
|
||||||
|
colorama_init()
|
||||||
|
except Exception:
|
||||||
|
class _NoColor(object):
|
||||||
|
def __getattr__(self, name): return ''
|
||||||
|
Fore = _NoColor()
|
||||||
|
|
||||||
|
BUNDLED_SCRIPT = "audit_cis_centos6.sh"
|
||||||
|
|
||||||
|
|
||||||
|
def resource_path(relative_path):
|
||||||
|
if getattr(sys, "frozen", False):
|
||||||
|
return os.path.join(sys._MEIPASS, relative_path)
|
||||||
|
return os.path.join(os.path.dirname(os.path.abspath(__file__)), relative_path)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_script_path(user_path=None):
|
||||||
|
if user_path and os.path.isfile(user_path):
|
||||||
|
return os.path.abspath(user_path)
|
||||||
|
bundled = resource_path(BUNDLED_SCRIPT)
|
||||||
|
if os.path.isfile(bundled):
|
||||||
|
return bundled
|
||||||
|
same_dir = os.path.join(os.getcwd(), BUNDLED_SCRIPT)
|
||||||
|
if os.path.isfile(same_dir):
|
||||||
|
return same_dir
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def detect_os_name():
|
||||||
|
"""
|
||||||
|
Trả về chuỗi tên HĐH ngắn gọn.
|
||||||
|
Ưu tiên /etc/*-release; fallback sang uname -srm.
|
||||||
|
"""
|
||||||
|
paths = [
|
||||||
|
'/etc/centos-release',
|
||||||
|
'/etc/redhat-release',
|
||||||
|
'/etc/os-release', # có trên bản mới hơn
|
||||||
|
'/etc/lsb-release', # có thể chứa DISTRIB_DESCRIPTION=
|
||||||
|
]
|
||||||
|
for p in paths:
|
||||||
|
try:
|
||||||
|
if os.path.isfile(p):
|
||||||
|
data = open(p, 'rb').read()
|
||||||
|
try:
|
||||||
|
text = data.decode('utf-8', 'ignore')
|
||||||
|
except Exception:
|
||||||
|
text = data
|
||||||
|
# /etc/lsb-release
|
||||||
|
if 'DISTRIB_DESCRIPTION=' in text:
|
||||||
|
for line in text.splitlines():
|
||||||
|
if line.startswith('DISTRIB_DESCRIPTION='):
|
||||||
|
val = line.split('=',1)[1].strip().strip('"')
|
||||||
|
return val
|
||||||
|
# các file *release khác: dùng dòng đầu
|
||||||
|
first = text.splitlines()[0].strip()
|
||||||
|
if first:
|
||||||
|
return first
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
# Fallback: uname
|
||||||
|
try:
|
||||||
|
out = subprocess.Popen("uname -srm", shell=True,
|
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
|
||||||
|
try:
|
||||||
|
return out.decode('utf-8','ignore').strip()
|
||||||
|
except Exception:
|
||||||
|
return out.strip()
|
||||||
|
except Exception:
|
||||||
|
return 'Unknown-OS'
|
||||||
|
|
||||||
|
try:
|
||||||
|
hostname = socket.gethostname()
|
||||||
|
IPAddr = socket.gethostbyname(hostname)
|
||||||
|
except Exception:
|
||||||
|
hostname = 'unknown-host'
|
||||||
|
IPAddr = '127.0.0.1'
|
||||||
|
|
||||||
|
cwd = os.getcwd()
|
||||||
|
|
||||||
|
|
||||||
|
def pkcs7_unpad(data, block_size=16):
|
||||||
|
if not data:
|
||||||
|
return data
|
||||||
|
pad_len = ord(data[-1]) if isinstance(data[-1], str) else data[-1]
|
||||||
|
if pad_len < 1 or pad_len > block_size:
|
||||||
|
# Defensive: return as-is to avoid exception storms in Py2 envs
|
||||||
|
return data
|
||||||
|
return data[:-pad_len]
|
||||||
|
|
||||||
|
|
||||||
|
def write_content(content, use_oaep=True):
|
||||||
|
"""
|
||||||
|
Hybrid AES-256-CBC + RSA encryption using OpenSSL CLI.
|
||||||
|
Only 1 RSA operation for the AES key; bulk data encrypted with fast AES.
|
||||||
|
Output: HYBRID_V1:<b64_enc_key>:<b64_iv>:<b64_ciphertext>
|
||||||
|
"""
|
||||||
|
import binascii
|
||||||
|
|
||||||
|
pubkey_path = os.path.join(os.getcwd(), 'public_key.pem')
|
||||||
|
if not os.path.isfile(pubkey_path):
|
||||||
|
raise RuntimeError("public_key.pem khong ton tai tai: %s" % pubkey_path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
unicode
|
||||||
|
if isinstance(content, unicode):
|
||||||
|
message = content.encode('utf-8')
|
||||||
|
else:
|
||||||
|
message = content
|
||||||
|
except NameError:
|
||||||
|
if isinstance(content, str):
|
||||||
|
message = content.encode('utf-8')
|
||||||
|
else:
|
||||||
|
message = content
|
||||||
|
|
||||||
|
aes_key = os.urandom(32)
|
||||||
|
iv = os.urandom(16)
|
||||||
|
|
||||||
|
fd_in, path_in = tempfile.mkstemp(prefix='aes_in_', dir='/tmp')
|
||||||
|
fd_out, path_out = tempfile.mkstemp(prefix='aes_out_', dir='/tmp')
|
||||||
|
os.close(fd_in)
|
||||||
|
os.close(fd_out)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(path_in, 'wb') as f:
|
||||||
|
f.write(message)
|
||||||
|
|
||||||
|
key_hex = binascii.hexlify(aes_key)
|
||||||
|
iv_hex = binascii.hexlify(iv)
|
||||||
|
|
||||||
|
cmd_aes = [
|
||||||
|
'/usr/bin/openssl', 'enc', '-aes-256-cbc',
|
||||||
|
'-K', key_hex, '-iv', iv_hex,
|
||||||
|
'-in', path_in, '-out', path_out
|
||||||
|
]
|
||||||
|
p = subprocess.Popen(cmd_aes, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
out, err = p.communicate()
|
||||||
|
if p.returncode != 0:
|
||||||
|
raise RuntimeError("OpenSSL AES encrypt error (rc=%d): %s" % (p.returncode, err))
|
||||||
|
|
||||||
|
with open(path_out, 'rb') as f:
|
||||||
|
ciphertext = f.read()
|
||||||
|
finally:
|
||||||
|
try: os.remove(path_in)
|
||||||
|
except: pass
|
||||||
|
try: os.remove(path_out)
|
||||||
|
except: pass
|
||||||
|
|
||||||
|
fd_key_in, path_key_in = tempfile.mkstemp(prefix='rsa_key_in_', dir='/tmp')
|
||||||
|
fd_key_out, path_key_out = tempfile.mkstemp(prefix='rsa_key_out_', dir='/tmp')
|
||||||
|
os.close(fd_key_in)
|
||||||
|
os.close(fd_key_out)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(path_key_in, 'wb') as f:
|
||||||
|
f.write(aes_key)
|
||||||
|
|
||||||
|
cmd_rsa = [
|
||||||
|
'/usr/bin/openssl', 'rsautl', '-encrypt',
|
||||||
|
'-pubin', '-inkey', pubkey_path,
|
||||||
|
'-in', path_key_in, '-out', path_key_out
|
||||||
|
]
|
||||||
|
if use_oaep:
|
||||||
|
cmd_rsa.insert(3, '-oaep')
|
||||||
|
else:
|
||||||
|
cmd_rsa.insert(3, '-pkcs')
|
||||||
|
|
||||||
|
p = subprocess.Popen(cmd_rsa, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
out, err = p.communicate()
|
||||||
|
if p.returncode != 0:
|
||||||
|
raise RuntimeError("OpenSSL rsautl error (rc=%d): %s" % (p.returncode, err))
|
||||||
|
|
||||||
|
with open(path_key_out, 'rb') as f:
|
||||||
|
enc_key = f.read()
|
||||||
|
finally:
|
||||||
|
try: os.remove(path_key_in)
|
||||||
|
except: pass
|
||||||
|
try: os.remove(path_key_out)
|
||||||
|
except: pass
|
||||||
|
|
||||||
|
b64_enc_key = base64.b64encode(enc_key)
|
||||||
|
b64_iv = base64.b64encode(iv)
|
||||||
|
b64_ct = base64.b64encode(ciphertext)
|
||||||
|
|
||||||
|
return b"HYBRID_V1:" + b64_enc_key + b":" + b64_iv + b":" + b64_ct
|
||||||
|
|
||||||
|
|
||||||
|
def _key_b64_to_hex(key_b64):
|
||||||
|
key = base64.b64decode(key_b64) # 16/24/32 bytes
|
||||||
|
try:
|
||||||
|
# Py2 cách cũ
|
||||||
|
return key.encode('hex')
|
||||||
|
except Exception:
|
||||||
|
# fallback portable
|
||||||
|
return binascii.b2a_hex(key)
|
||||||
|
|
||||||
|
|
||||||
|
def _key_b64_to_hex_and_cipher(key_b64):
|
||||||
|
"""
|
||||||
|
Base64 -> bytes -> (hex_key, cipher_name) theo độ dài key:
|
||||||
|
16B -> aes-128-ecb, 24B -> aes-192-ecb, 32B -> aes-256-ecb
|
||||||
|
"""
|
||||||
|
key = base64.b64decode(key_b64)
|
||||||
|
klen = len(key)
|
||||||
|
if klen == 16:
|
||||||
|
cipher = 'aes-128-ecb'
|
||||||
|
elif klen == 24:
|
||||||
|
cipher = 'aes-192-ecb'
|
||||||
|
elif klen == 32:
|
||||||
|
cipher = 'aes-256-ecb'
|
||||||
|
else:
|
||||||
|
raise RuntimeError("Độ dài key không hợp lệ: %d bytes (cần 16/24/32)" % klen)
|
||||||
|
# Py2: trả về hex dạng str
|
||||||
|
try:
|
||||||
|
hexkey = key.encode('hex')
|
||||||
|
except Exception:
|
||||||
|
hexkey = binascii.b2a_hex(key)
|
||||||
|
return hexkey, cipher
|
||||||
|
|
||||||
|
|
||||||
|
def run_bash(script_bytes):
|
||||||
|
"""Run bash script passed as BYTES via stdin; returns stdout bytes (Py2-safe)."""
|
||||||
|
if isinstance(script_bytes, unicode):
|
||||||
|
script_bytes = script_bytes.encode('utf-8')
|
||||||
|
p = subprocess.Popen("/bin/bash -s", shell=True,
|
||||||
|
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
out, err = p.communicate(script_bytes)
|
||||||
|
if p.returncode != 0:
|
||||||
|
# Ghi stderr vào cuối output để tiện debug nhưng không làm hỏng bytes
|
||||||
|
out = (out or b"") + b"\n===== STDERR =====\n" + (err or b"")
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
ANSI_RE = re.compile(r'\x1b\[[0-9;]*[A-Za-z]')
|
||||||
|
|
||||||
|
|
||||||
|
def strip_ansi(s):
|
||||||
|
try:
|
||||||
|
# Py2: s có thể là bytes; cứ để bytes, regex vẫn xử được
|
||||||
|
return ANSI_RE.sub('', s)
|
||||||
|
except Exception:
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
def safe_slug(s, maxlen=80):
|
||||||
|
# Chuyển sang ASCII-safe cho tên file: thay khoảng trắng/ký tự lạ bằng _
|
||||||
|
if isinstance(s, unicode):
|
||||||
|
s = s.encode('utf-8')
|
||||||
|
# bỏ ANSI, rồi thay ký tự không an toàn
|
||||||
|
s = strip_ansi(s)
|
||||||
|
s = re.sub(r'[^\w\.-]+', '_', s) # chỉ giữ a-zA-Z0-9_ . -
|
||||||
|
s = s.strip('_')
|
||||||
|
if len(s) > maxlen:
|
||||||
|
s = s[:maxlen]
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
def extract_host_time(text):
|
||||||
|
"""
|
||||||
|
Tìm Hostname và Time bằng regex, không phụ thuộc thứ tự dòng.
|
||||||
|
Kỳ vọng trong output có dạng:
|
||||||
|
Hostname: myhost
|
||||||
|
Time: 2025-11-06 13:00:00
|
||||||
|
"""
|
||||||
|
t = strip_ansi(text)
|
||||||
|
# Tách dòng để tìm
|
||||||
|
lines = t.splitlines()
|
||||||
|
hostname_out, time_out = None, None
|
||||||
|
|
||||||
|
# Regex linh hoạt (bỏ qua hoa/thường, khoảng trắng)
|
||||||
|
re_host = re.compile(r'^\s*Hostname:\s*(\S+)\s*$', re.I)
|
||||||
|
re_time = re.compile(r'^\s*Time:\s*(.+?)\s*$', re.I)
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
if hostname_out is None:
|
||||||
|
m = re_host.match(line)
|
||||||
|
if m:
|
||||||
|
hostname_out = m.group(1)
|
||||||
|
if time_out is None:
|
||||||
|
m = re_time.match(line)
|
||||||
|
if m:
|
||||||
|
time_out = m.group(1)
|
||||||
|
if hostname_out and time_out:
|
||||||
|
break
|
||||||
|
|
||||||
|
if not hostname_out:
|
||||||
|
# fallback an toàn
|
||||||
|
try:
|
||||||
|
hostname_out = socket.gethostname()
|
||||||
|
except Exception:
|
||||||
|
hostname_out = 'unknown-host'
|
||||||
|
|
||||||
|
if not time_out:
|
||||||
|
# fallback: now
|
||||||
|
try:
|
||||||
|
time_out = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
except Exception:
|
||||||
|
time_out = 'unknown-time'
|
||||||
|
|
||||||
|
# Chuẩn hoá
|
||||||
|
hostname_out = safe_slug(hostname_out, maxlen=80)
|
||||||
|
# format lại time sang safe filename
|
||||||
|
time_safe = time_out.replace('/', '-').replace('\\', '-').replace(':', '_').replace(' ', '-')
|
||||||
|
time_safe = safe_slug(time_safe, maxlen=80)
|
||||||
|
|
||||||
|
return hostname_out, time_safe
|
||||||
|
|
||||||
|
|
||||||
|
def run_audit(file_path=None):
|
||||||
|
# Banner
|
||||||
|
print(Fore.BLUE + """
|
||||||
|
_ _ _____ _____ _______ _ _ _____ _____ ______ _ _ _____ _ _ _____
|
||||||
|
/\\ | | | | __ \\_ _|__ __| | | | | /\\ | __ \\| __ \\| ____| \\ | |_ _| \\ | |/ ____|
|
||||||
|
/ \\ | | | | | | || | | | ______ | |__| | / \\ | |__) | | | | |__ | \\| | | | | \\| | | __
|
||||||
|
/ /\\ \\| | | | | | || | | | |______| | __ | / /\\ \\ | _ /| | | | __| | . ` | | | | . ` | | |_ |
|
||||||
|
/ ____ \\ |__| | |__| || |_ | | | | | |/ ____ \\| | \\ \\| |__| | |____| |\\ |_| |_| |\\ | |__| |
|
||||||
|
/_/ \\_\\____/|_____/_____| |_| |_| |_/_/ \\_\\_| \\_\\_____/|______|_| \\_|_____|_| \\_|\\_____|
|
||||||
|
|
||||||
|
""" + Fore.RESET)
|
||||||
|
print(Fore.RED + "Running Audit Hardening..." + Fore.RESET)
|
||||||
|
|
||||||
|
script_path = resolve_script_path(file_path)
|
||||||
|
if script_path is None:
|
||||||
|
_p(u"ERROR: No audit script found. Use -p <script.sh> or bundle the script.", getattr(Fore, 'RED', None))
|
||||||
|
return
|
||||||
|
|
||||||
|
_p(u" Script: %s" % script_path)
|
||||||
|
print(" [1/3] Reading script...")
|
||||||
|
with open(script_path, 'r') as f:
|
||||||
|
script_content = f.read()
|
||||||
|
|
||||||
|
content_u = script_content
|
||||||
|
script_parts = content_u.split(
|
||||||
|
"##################################################################################################################")
|
||||||
|
output = ""
|
||||||
|
for part in script_parts:
|
||||||
|
# Chuẩn hoá: part_bytes là bytes UTF-8; so sánh shebang ở dạng bytes
|
||||||
|
try:
|
||||||
|
unicode
|
||||||
|
if isinstance(part, unicode):
|
||||||
|
part_bytes = part.encode('utf-8')
|
||||||
|
else:
|
||||||
|
part_bytes = part
|
||||||
|
except NameError:
|
||||||
|
part_bytes = part # (không dùng trên Py3 ở đây)
|
||||||
|
|
||||||
|
if b"#!/bin/bash" not in part_bytes:
|
||||||
|
bash_src = b"#!/bin/bash\n" + part_bytes
|
||||||
|
else:
|
||||||
|
bash_src = part_bytes
|
||||||
|
|
||||||
|
out = run_bash(bash_src) # out là bytes
|
||||||
|
try:
|
||||||
|
output += out.decode('utf-8', 'ignore')
|
||||||
|
except Exception:
|
||||||
|
output += out # worst-case, giữ nguyên bytes
|
||||||
|
|
||||||
|
# Extract hostname/time lines from the produced output (keep logic as original)
|
||||||
|
hostname_out, time_generate = extract_host_time(output)
|
||||||
|
|
||||||
|
os_name_raw = detect_os_name()
|
||||||
|
os_name = safe_slug(os_name_raw, maxlen=60) # ASCII-safe
|
||||||
|
file_encrypt_name = '%s_%s_%s.txt.enc' % (hostname_out, os_name, time_generate)
|
||||||
|
enc_b64 = write_content(output)
|
||||||
|
|
||||||
|
with open(file_encrypt_name, 'wb') as f:
|
||||||
|
# enc_b64 is bytes in Py2
|
||||||
|
f.write(enc_b64)
|
||||||
|
|
||||||
|
if is_file_exist(file_encrypt_name):
|
||||||
|
_p(u"THÀNH CÔNG - FILE ENCRYPT %s" % file_encrypt_name, getattr(Fore, 'GREEN', None))
|
||||||
|
else:
|
||||||
|
_p(u"THẤT BẠI!!!", getattr(Fore, 'RED', None))
|
||||||
|
|
||||||
|
|
||||||
|
def run_ubuntu_audit(file_path=None):
|
||||||
|
old_file_path = '%s.txt' % hostname
|
||||||
|
if is_file_exist(old_file_path):
|
||||||
|
try:
|
||||||
|
os.remove(old_file_path)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
run_audit(file_path)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_options():
|
||||||
|
if not OptionParser:
|
||||||
|
raise SystemExit("[ERROR] optparse not available. Please use Python 2.6/2.7.")
|
||||||
|
parser = OptionParser(usage="usage: %prog [-p PATH_TO_SCRIPT]")
|
||||||
|
parser.add_option("-p", "--path", dest="path",
|
||||||
|
help="Path to audit script (optional if bundled)", metavar="FILE", action="store", default=None)
|
||||||
|
(options, args) = parser.parse_args()
|
||||||
|
return options
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
|
||||||
|
opts = parse_options()
|
||||||
|
run_ubuntu_audit(opts.path)
|
||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,48 @@
|
|||||||
|
@echo off
|
||||||
|
REM ============================================================
|
||||||
|
REM Build AuditTool.exe for Windows Server 2012+
|
||||||
|
REM Requires: Python 3.8+, pip
|
||||||
|
REM ============================================================
|
||||||
|
|
||||||
|
echo [1/3] Installing dependencies...
|
||||||
|
python -m pip install pycryptodome colorama unidecode pyinstaller
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo ERROR: pip install failed. Check Python installation.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [2/3] Copying PowerShell audit script...
|
||||||
|
copy /Y ..\audit_check_script\audit_cis_windows.ps1 audit_cis_windows.ps1
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo ERROR: audit_cis_windows.ps1 not found at ..\audit_check_script\
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo [3/3] Building AuditTool.exe with PyInstaller...
|
||||||
|
python -m PyInstaller --onefile --console --name AuditTool ^
|
||||||
|
--add-data "audit_cis_windows.ps1;." ^
|
||||||
|
--hidden-import Crypto.Cipher ^
|
||||||
|
--hidden-import Crypto.PublicKey ^
|
||||||
|
--hidden-import Crypto.Util.Padding ^
|
||||||
|
windows_audit.py
|
||||||
|
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
echo ERROR: PyInstaller build failed
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ========================================
|
||||||
|
echo Build complete!
|
||||||
|
echo Output: dist\AuditTool.exe
|
||||||
|
echo.
|
||||||
|
echo Deploy to Windows Server:
|
||||||
|
echo 1. Copy dist\AuditTool.exe to target server
|
||||||
|
echo 2. Copy keys\public_key.pem to same folder as AuditTool.exe
|
||||||
|
echo 3. Run: AuditTool.exe
|
||||||
|
echo ========================================
|
||||||
|
|
||||||
|
pause
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ============================================================
|
||||||
|
# Build Linux audit binaries with PyInstaller
|
||||||
|
# Run this on the TARGET OS version:
|
||||||
|
# - Ubuntu binary → build on Ubuntu 20.04+
|
||||||
|
# - CentOS 7 binary → build on CentOS 7
|
||||||
|
# - CentOS 6 binary → build on CentOS 6 (Python 2.6+)
|
||||||
|
# - Oracle binary → build on Oracle Linux 7+
|
||||||
|
#
|
||||||
|
# Usage: bash build_all.sh [ubuntu|centos|oracle|centos6|all]
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PARENT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||||
|
CHECK_SCRIPT_DIR="${PARENT_DIR}/audit_check_script"
|
||||||
|
DIST_DIR="${SCRIPT_DIR}/dist"
|
||||||
|
|
||||||
|
mkdir -p "${DIST_DIR}"
|
||||||
|
|
||||||
|
build_ubuntu() {
|
||||||
|
echo "=== Building Ubuntu Audit Tool ==="
|
||||||
|
cp "${CHECK_SCRIPT_DIR}/audit_cis_ubuntu_v202.sh" "${SCRIPT_DIR}/"
|
||||||
|
pip install pycryptodome cryptography colorama unidecode pyinstaller
|
||||||
|
pyinstaller --onefile --console --name ubuntu_audit \
|
||||||
|
--add-data "audit_cis_ubuntu_v202.sh:." \
|
||||||
|
--hidden-import colorama \
|
||||||
|
--hidden-import unidecode \
|
||||||
|
--hidden-import Crypto.Cipher \
|
||||||
|
--hidden-import Crypto.PublicKey \
|
||||||
|
--hidden-import Crypto.Util.Padding \
|
||||||
|
--hidden-import cryptography \
|
||||||
|
--hidden-import cryptography.hazmat.primitives.ciphers \
|
||||||
|
--hidden-import cryptography.hazmat.primitives.padding \
|
||||||
|
ubuntu_audit_v2.py
|
||||||
|
echo " -> ${DIST_DIR}/ubuntu_audit"
|
||||||
|
}
|
||||||
|
|
||||||
|
build_centos() {
|
||||||
|
echo "=== Building CentOS/RHEL Audit Tool ==="
|
||||||
|
cp "${CHECK_SCRIPT_DIR}/audit_cis_centos_v202.sh" "${SCRIPT_DIR}/"
|
||||||
|
pip install pycryptodome cryptography colorama unidecode pyinstaller
|
||||||
|
pyinstaller --onefile --console --name centos_rhel_audit \
|
||||||
|
--add-data "audit_cis_centos_v202.sh:." \
|
||||||
|
--hidden-import colorama \
|
||||||
|
--hidden-import unidecode \
|
||||||
|
--hidden-import Crypto.Cipher \
|
||||||
|
--hidden-import Crypto.PublicKey \
|
||||||
|
--hidden-import Crypto.Util.Padding \
|
||||||
|
--hidden-import cryptography \
|
||||||
|
--hidden-import cryptography.hazmat.primitives.ciphers \
|
||||||
|
--hidden-import cryptography.hazmat.primitives.padding \
|
||||||
|
centos_rhel_audit_v2.py
|
||||||
|
echo " -> ${DIST_DIR}/centos_rhel_audit"
|
||||||
|
}
|
||||||
|
|
||||||
|
build_oracle() {
|
||||||
|
echo "=== Building Oracle Linux Audit Tool ==="
|
||||||
|
cp "${CHECK_SCRIPT_DIR}/audit_cis_centos_v202.sh" "${SCRIPT_DIR}/"
|
||||||
|
pip install pycryptodome cryptography colorama unidecode pyinstaller
|
||||||
|
pyinstaller --onefile --console --name oracle_linux_audit \
|
||||||
|
--add-data "audit_cis_centos_v202.sh:." \
|
||||||
|
--hidden-import colorama \
|
||||||
|
--hidden-import unidecode \
|
||||||
|
--hidden-import Crypto.Cipher \
|
||||||
|
--hidden-import Crypto.PublicKey \
|
||||||
|
--hidden-import Crypto.Util.Padding \
|
||||||
|
--hidden-import cryptography \
|
||||||
|
--hidden-import cryptography.hazmat.primitives.ciphers \
|
||||||
|
--hidden-import cryptography.hazmat.primitives.padding \
|
||||||
|
oracle_linux_audit_v2.py
|
||||||
|
echo " -> ${DIST_DIR}/oracle_linux_audit"
|
||||||
|
}
|
||||||
|
|
||||||
|
build_centos6() {
|
||||||
|
echo "=== Building CentOS 6 Audit Tool ==="
|
||||||
|
cp "${CHECK_SCRIPT_DIR}/audit_cis_centos6.sh" "${SCRIPT_DIR}/"
|
||||||
|
pip install pyinstaller
|
||||||
|
pyinstaller --onefile --console --name centos6_audit \
|
||||||
|
--add-data "audit_cis_centos6.sh:." \
|
||||||
|
audit_centos6.py
|
||||||
|
echo " -> ${DIST_DIR}/centos6_audit"
|
||||||
|
}
|
||||||
|
|
||||||
|
build_all() {
|
||||||
|
build_ubuntu
|
||||||
|
build_centos
|
||||||
|
build_oracle
|
||||||
|
build_centos6
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${1:-all}" in
|
||||||
|
ubuntu) build_ubuntu ;;
|
||||||
|
centos) build_centos ;;
|
||||||
|
oracle) build_oracle ;;
|
||||||
|
centos6) build_centos6 ;;
|
||||||
|
all) build_all ;;
|
||||||
|
*)
|
||||||
|
echo "Usage: bash build_all.sh [ubuntu|centos|oracle|centos6|all]"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "========================================"
|
||||||
|
echo "Build complete! Output: ${DIST_DIR}/"
|
||||||
|
echo ""
|
||||||
|
echo "Deploy to target server:"
|
||||||
|
echo " 1. Copy the binary from dist/"
|
||||||
|
echo " 2. Copy public_key.pem to same folder"
|
||||||
|
echo " 3. Run: ./<binary> (script is bundled, no -p needed)"
|
||||||
|
echo " Or: ./<binary> -p custom_script.sh"
|
||||||
|
echo "========================================"
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
# PyInstaller spec for CentOS 6 Audit Tool (Python 2.6+)
|
||||||
|
# Build: pyinstaller centos6_audit.spec
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
a = Analysis(
|
||||||
|
['audit_centos6.py'],
|
||||||
|
pathex=[],
|
||||||
|
binaries=[],
|
||||||
|
datas=[('audit_cis_centos6.sh', '.')],
|
||||||
|
hiddenimports=[],
|
||||||
|
hookspath=[],
|
||||||
|
hooksconfig={},
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
cipher=block_cipher,
|
||||||
|
noarchive=False,
|
||||||
|
)
|
||||||
|
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
[],
|
||||||
|
name='centos6_audit',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
runtime_tmpdir=None,
|
||||||
|
console=True,
|
||||||
|
disable_windowed_traceback=False,
|
||||||
|
argv_emulation=False,
|
||||||
|
target_arch=None,
|
||||||
|
codesign_identity=None,
|
||||||
|
entitlements_file=None,
|
||||||
|
)
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
# PyInstaller spec for CentOS/RHEL Audit Tool
|
||||||
|
# Build: pyinstaller centos_rhel_audit.spec
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
a = Analysis(
|
||||||
|
['centos_rhel_audit_v2.py'],
|
||||||
|
pathex=[],
|
||||||
|
binaries=[],
|
||||||
|
datas=[('audit_cis_centos_v202.sh', '.')],
|
||||||
|
hiddenimports=[
|
||||||
|
'colorama',
|
||||||
|
'unidecode',
|
||||||
|
'Crypto', 'Crypto.Cipher', 'Crypto.PublicKey', 'Crypto.Util.Padding',
|
||||||
|
'cryptography', 'cryptography.hazmat', 'cryptography.hazmat.primitives',
|
||||||
|
'cryptography.hazmat.primitives.ciphers', 'cryptography.hazmat.primitives.padding',
|
||||||
|
],
|
||||||
|
hookspath=[],
|
||||||
|
hooksconfig={},
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
cipher=block_cipher,
|
||||||
|
noarchive=False,
|
||||||
|
)
|
||||||
|
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
[],
|
||||||
|
name='centos_rhel_audit',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
runtime_tmpdir=None,
|
||||||
|
console=True,
|
||||||
|
disable_windowed_traceback=False,
|
||||||
|
argv_emulation=False,
|
||||||
|
target_arch=None,
|
||||||
|
codesign_identity=None,
|
||||||
|
entitlements_file=None,
|
||||||
|
)
|
||||||
@@ -0,0 +1,253 @@
|
|||||||
|
import argparse
|
||||||
|
import base64
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import subprocess
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from Crypto.Cipher import PKCS1_OAEP
|
||||||
|
from Crypto.PublicKey import RSA
|
||||||
|
from colorama import Fore
|
||||||
|
from cryptography.hazmat.primitives import padding
|
||||||
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||||
|
|
||||||
|
hostname = socket.gethostname()
|
||||||
|
IPAddr = socket.gethostbyname(hostname)
|
||||||
|
cwd = os.getcwd()
|
||||||
|
BUNDLED_SCRIPT = "audit_cis_centos_v202.sh"
|
||||||
|
|
||||||
|
|
||||||
|
def resource_path(relative_path):
|
||||||
|
if getattr(sys, "frozen", False):
|
||||||
|
return os.path.join(sys._MEIPASS, relative_path)
|
||||||
|
return os.path.join(os.path.dirname(os.path.abspath(__file__)), relative_path)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_script_path(user_path=None):
|
||||||
|
if user_path and os.path.isfile(user_path):
|
||||||
|
return os.path.abspath(user_path)
|
||||||
|
bundled = resource_path(BUNDLED_SCRIPT)
|
||||||
|
if os.path.isfile(bundled):
|
||||||
|
return bundled
|
||||||
|
same_dir = os.path.join(os.getcwd(), BUNDLED_SCRIPT)
|
||||||
|
if os.path.isfile(same_dir):
|
||||||
|
return same_dir
|
||||||
|
return None
|
||||||
|
|
||||||
|
def print_progress(current, total):
|
||||||
|
if total == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
bar_len = 40 # độ dài thanh
|
||||||
|
filled = int(bar_len * current / total)
|
||||||
|
bar = '█' * filled + '-' * (bar_len - filled)
|
||||||
|
percent = int(current * 100 / total)
|
||||||
|
|
||||||
|
sys.stdout.write(f'\rĐang xử lý... [{bar}] {percent}%')
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
# Khi xong hết thì xuống dòng mới cho đẹp
|
||||||
|
if current == total:
|
||||||
|
sys.stdout.write('\n')
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def get_os_tag():
|
||||||
|
"""Trả về nhãn OS dạng 'ubuntu_22_04' hoặc 'centos_7', ..."""
|
||||||
|
try:
|
||||||
|
if os.path.isfile("/etc/os-release"):
|
||||||
|
name = ""
|
||||||
|
version = ""
|
||||||
|
with open("/etc/os-release") as f:
|
||||||
|
for line in f:
|
||||||
|
if line.startswith("NAME=") and not name:
|
||||||
|
name = line.split("=", 1)[1].strip().strip('"').lower()
|
||||||
|
elif line.startswith("VERSION_ID=") and not version:
|
||||||
|
version = line.split("=", 1)[1].strip().strip('"').lower()
|
||||||
|
tag = f"{name}_{version}" if version else name
|
||||||
|
# chuẩn hoá: thay khoảng trắng và ký tự lạ
|
||||||
|
tag = re.sub(r"[^a-z0-9]+", "_", tag).strip("_")
|
||||||
|
if tag:
|
||||||
|
return tag
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
if os.path.isfile("/etc/redhat-release"):
|
||||||
|
txt = open("/etc/redhat-release").read().strip().lower()
|
||||||
|
# ví dụ: "centos linux release 7.9.2009 (core)"
|
||||||
|
# lấy vendor + major.minor
|
||||||
|
m = re.search(r"(centos|red hat|rhel|rocky|almalinux).*?release\s+([0-9]+(?:\.[0-9]+)?)", txt)
|
||||||
|
if m:
|
||||||
|
vendor = m.group(1).replace(" ", "")
|
||||||
|
ver = m.group(2).replace(".", "_")
|
||||||
|
return f"{vendor}_{ver}"
|
||||||
|
# fallback rút gọn
|
||||||
|
tag = re.sub(r"[^a-z0-9]+", "_", txt).strip("_")
|
||||||
|
if tag:
|
||||||
|
return tag
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
import platform
|
||||||
|
sysname = platform.system().lower()
|
||||||
|
release = platform.release().lower()
|
||||||
|
tag = f"{sysname}_{release}"
|
||||||
|
tag = re.sub(r"[^a-z0-9]+", "_", tag).strip("_")
|
||||||
|
return tag or "unknown_os"
|
||||||
|
except Exception:
|
||||||
|
return "unknown_os"
|
||||||
|
|
||||||
|
def write_content(content):
|
||||||
|
message = content.encode('utf-8')
|
||||||
|
|
||||||
|
aes_key = os.urandom(32)
|
||||||
|
iv = os.urandom(16)
|
||||||
|
|
||||||
|
padder = padding.PKCS7(128).padder()
|
||||||
|
padded_data = padder.update(message) + padder.finalize()
|
||||||
|
|
||||||
|
cipher_aes = Cipher(algorithms.AES(aes_key), modes.CBC(iv))
|
||||||
|
encryptor = cipher_aes.encryptor()
|
||||||
|
ciphertext = encryptor.update(padded_data) + encryptor.finalize()
|
||||||
|
|
||||||
|
public_key = RSA.import_key(open('public_key.pem').read())
|
||||||
|
cipher_rsa = PKCS1_OAEP.new(public_key)
|
||||||
|
enc_key = cipher_rsa.encrypt(aes_key)
|
||||||
|
|
||||||
|
b64_enc_key = base64.b64encode(enc_key).decode('utf-8')
|
||||||
|
b64_iv = base64.b64encode(iv).decode('utf-8')
|
||||||
|
b64_ct = base64.b64encode(ciphertext).decode('utf-8')
|
||||||
|
|
||||||
|
return f"HYBRID_V1:{b64_enc_key}:{b64_iv}:{b64_ct}"
|
||||||
|
|
||||||
|
|
||||||
|
def is_file_exist(path):
|
||||||
|
isFile = os.path.isfile(path)
|
||||||
|
if isFile:
|
||||||
|
return True
|
||||||
|
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def run_bash(command):
|
||||||
|
process = subprocess.Popen(
|
||||||
|
['/bin/bash', '-c', command],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE
|
||||||
|
)
|
||||||
|
stdout, stderr = process.communicate()
|
||||||
|
|
||||||
|
rc = process.returncode
|
||||||
|
out = stdout.decode(errors="ignore")
|
||||||
|
err = stderr.decode(errors="ignore")
|
||||||
|
|
||||||
|
# Luôn luôn ghép stderr (nếu có) vào log để không mất thông tin
|
||||||
|
if err.strip():
|
||||||
|
out += "\n########## STDERR BEGIN ##########\n"
|
||||||
|
out += err
|
||||||
|
out += "\n########## STDERR END ##########\n"
|
||||||
|
|
||||||
|
# Trả về tuple: (output, stderr, return_code)
|
||||||
|
return out, err, rc
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def run_audit(file_path):
|
||||||
|
print(Fore.BLUE + """
|
||||||
|
_ _ _____ _____ _______ _ _ _____ _____ ______ _ _ _____ _ _ _____
|
||||||
|
/\ | | | | __ \_ _|__ __| | | | | /\ | __ \| __ \| ____| \ | |_ _| \ | |/ ____|
|
||||||
|
/ \ | | | | | | || | | | ______ | |__| | / \ | |__) | | | | |__ | \| | | | | \| | | __
|
||||||
|
/ /\ \| | | | | | || | | | |______| | __ | / /\ \ | _ /| | | | __| | . ` | | | | . ` | | |_ |
|
||||||
|
/ ____ \ |__| | |__| || |_ | | | | | |/ ____ \| | \ \| |__| | |____| |\ |_| |_| |\ | |__| |
|
||||||
|
/_/ \_\____/|_____/_____| |_| |_| |_/_/ \_\_| \_\_____/|______|_| \_|_____|_| \_|\_____|
|
||||||
|
|
||||||
|
|
||||||
|
""" + Fore.RESET)
|
||||||
|
print(Fore.RED + "Running Audit Hardening..." + Fore.RESET)
|
||||||
|
|
||||||
|
script_path = resolve_script_path(file_path)
|
||||||
|
if script_path is None:
|
||||||
|
print(Fore.RED + "ERROR: No audit script found. Use -p <script.sh> or bundle the script." + Fore.RESET)
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f" Script: {script_path}")
|
||||||
|
print(" [1/3] Reading script...")
|
||||||
|
with open(script_path, 'r', encoding='utf-8') as f:
|
||||||
|
script_content = f.read()
|
||||||
|
|
||||||
|
script = script_content.split(
|
||||||
|
"##################################################################################################################")
|
||||||
|
|
||||||
|
# Lọc bỏ các block rỗng trước
|
||||||
|
script_blocks = [block.strip() for block in script if block.strip()]
|
||||||
|
total_blocks = len(script_blocks)
|
||||||
|
|
||||||
|
output = ""
|
||||||
|
errors = [] # Lưu lỗi để hiển thị sau khi loading xong
|
||||||
|
|
||||||
|
for idx, i in enumerate(script_blocks, 1):
|
||||||
|
# Hiển thị progress bar
|
||||||
|
print_progress(idx, total_blocks)
|
||||||
|
|
||||||
|
if "#!/bin/bash" not in i:
|
||||||
|
result, err, rc = run_bash("#!/bin/bash\n{0}".format(i))
|
||||||
|
else:
|
||||||
|
result, err, rc = run_bash(i)
|
||||||
|
|
||||||
|
# Lưu lỗi nếu có (exit code != 0)
|
||||||
|
if rc != 0 and err.strip():
|
||||||
|
errors.append(f"[Block {idx}] {err.strip().split(chr(10))[0]}")
|
||||||
|
|
||||||
|
# Nếu lệnh lỗi và run_bash trả về None thì bỏ qua
|
||||||
|
if result is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
output += result
|
||||||
|
|
||||||
|
# Hiển thị các lỗi sau khi loading xong
|
||||||
|
if errors:
|
||||||
|
print(Fore.YELLOW + f"\n⚠ Có {len(errors)} cảnh báo:" + Fore.RESET)
|
||||||
|
for e in errors:
|
||||||
|
print(Fore.YELLOW + f" • {e}" + Fore.RESET)
|
||||||
|
# Dùng regex để tìm Hostname và Audit Time trong output
|
||||||
|
hostname_match = re.search(r"Hostname:\s*(\S+)", output)
|
||||||
|
time_match = re.search(r"Audit Time:\s*(.+)", output)
|
||||||
|
|
||||||
|
hostname = hostname_match.group(1) if hostname_match else "unknown_host"
|
||||||
|
time_generate = time_match.group(1).strip().replace("-", "_").replace(":", "_").replace(" ", "-") if time_match else "unknown_time"
|
||||||
|
os_tag = get_os_tag()
|
||||||
|
file_encrypt_name = f"{hostname}_{os_tag}_{time_generate}.txt.enc"
|
||||||
|
# file_encrypt_name = '{}_{}.txt.enc'.format(hostname, time_generate)
|
||||||
|
encrypt_result = write_content(output)
|
||||||
|
with open(file_encrypt_name, 'w') as f:
|
||||||
|
f.write(encrypt_result)
|
||||||
|
|
||||||
|
if is_file_exist(file_encrypt_name):
|
||||||
|
print(Fore.GREEN + "THÀNH CÔNG - FILE ENCRYPT {}".format(file_encrypt_name) + Fore.RESET)
|
||||||
|
else:
|
||||||
|
print(Fore.RED + "THẤT BẠI RỒI THỬ LẠI NHÁ !!!" + Fore.RESET)
|
||||||
|
|
||||||
|
|
||||||
|
def run_ubuntu_audit(file_path=None):
|
||||||
|
old_file_path = '{}.txt'.format(hostname)
|
||||||
|
old_file = is_file_exist(old_file_path)
|
||||||
|
if old_file:
|
||||||
|
os.remove(old_file_path)
|
||||||
|
run_audit(file_path)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Parse Arguments
|
||||||
|
parser = argparse.ArgumentParser(description='Audit Hardening')
|
||||||
|
parser.add_argument('-p', '--path', help='Path to audit script (optional if bundled)', default=None)
|
||||||
|
args = parser.parse_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
args = main()
|
||||||
|
run_ubuntu_audit(args.path)
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
# PyInstaller spec for Oracle Linux Audit Tool
|
||||||
|
# Build: pyinstaller oracle_linux_audit.spec
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
a = Analysis(
|
||||||
|
['oracle_linux_audit_v2.py'],
|
||||||
|
pathex=[],
|
||||||
|
binaries=[],
|
||||||
|
datas=[('audit_cis_centos_v202.sh', '.')],
|
||||||
|
hiddenimports=[
|
||||||
|
'colorama',
|
||||||
|
'unidecode',
|
||||||
|
'Crypto', 'Crypto.Cipher', 'Crypto.PublicKey', 'Crypto.Util.Padding',
|
||||||
|
'cryptography', 'cryptography.hazmat', 'cryptography.hazmat.primitives',
|
||||||
|
'cryptography.hazmat.primitives.ciphers', 'cryptography.hazmat.primitives.padding',
|
||||||
|
],
|
||||||
|
hookspath=[],
|
||||||
|
hooksconfig={},
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
cipher=block_cipher,
|
||||||
|
noarchive=False,
|
||||||
|
)
|
||||||
|
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
[],
|
||||||
|
name='oracle_linux_audit',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
runtime_tmpdir=None,
|
||||||
|
console=True,
|
||||||
|
disable_windowed_traceback=False,
|
||||||
|
argv_emulation=False,
|
||||||
|
target_arch=None,
|
||||||
|
codesign_identity=None,
|
||||||
|
entitlements_file=None,
|
||||||
|
)
|
||||||
@@ -0,0 +1,272 @@
|
|||||||
|
import argparse
|
||||||
|
import base64
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import subprocess
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from Crypto.Cipher import PKCS1_OAEP
|
||||||
|
from Crypto.PublicKey import RSA
|
||||||
|
from colorama import Fore
|
||||||
|
from cryptography.hazmat.primitives import padding
|
||||||
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||||
|
|
||||||
|
hostname = socket.gethostname()
|
||||||
|
IPAddr = socket.gethostbyname(hostname)
|
||||||
|
cwd = os.getcwd()
|
||||||
|
BUNDLED_SCRIPT = "audit_cis_centos_v202.sh"
|
||||||
|
|
||||||
|
|
||||||
|
def resource_path(relative_path):
|
||||||
|
if getattr(sys, "frozen", False):
|
||||||
|
return os.path.join(sys._MEIPASS, relative_path)
|
||||||
|
return os.path.join(os.path.dirname(os.path.abspath(__file__)), relative_path)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_script_path(user_path=None):
|
||||||
|
if user_path and os.path.isfile(user_path):
|
||||||
|
return os.path.abspath(user_path)
|
||||||
|
bundled = resource_path(BUNDLED_SCRIPT)
|
||||||
|
if os.path.isfile(bundled):
|
||||||
|
return bundled
|
||||||
|
same_dir = os.path.join(os.getcwd(), BUNDLED_SCRIPT)
|
||||||
|
if os.path.isfile(same_dir):
|
||||||
|
return same_dir
|
||||||
|
return None
|
||||||
|
|
||||||
|
def print_progress(current, total):
|
||||||
|
if total == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
bar_len = 40 # độ dài thanh
|
||||||
|
filled = int(bar_len * current / total)
|
||||||
|
bar = '█' * filled + '-' * (bar_len - filled)
|
||||||
|
percent = int(current * 100 / total)
|
||||||
|
|
||||||
|
sys.stdout.write(f'\rĐang xử lý... [{bar}] {percent}%')
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
# Khi xong hết thì xuống dòng mới cho đẹp
|
||||||
|
if current == total:
|
||||||
|
sys.stdout.write('\n')
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def get_os_tag():
|
||||||
|
"""
|
||||||
|
Trả về nhãn OS dạng 'oracle_linux_8_9', 'rhel_7', 'centos_7_9', 'ubuntu_22_04', ...
|
||||||
|
Ưu tiên /etc/os-release; fallback /etc/oracle-release hoặc /etc/redhat-release; cuối cùng uname.
|
||||||
|
"""
|
||||||
|
# 1) /etc/os-release (chuẩn mới trên hầu hết distro)
|
||||||
|
try:
|
||||||
|
if os.path.isfile("/etc/os-release"):
|
||||||
|
name = ""
|
||||||
|
version = ""
|
||||||
|
with open("/etc/os-release") as f:
|
||||||
|
for line in f:
|
||||||
|
if line.startswith("NAME=") and not name:
|
||||||
|
name = line.split("=", 1)[1].strip().strip('"').lower()
|
||||||
|
elif line.startswith("VERSION_ID=") and not version:
|
||||||
|
version = line.split("=", 1)[1].strip().strip('"').lower()
|
||||||
|
tag = f"{name}_{version}" if version else name
|
||||||
|
tag = re.sub(r"[^a-z0-9]+", "_", tag).strip("_")
|
||||||
|
if tag:
|
||||||
|
return tag
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 2) Oracle Linux đời cũ
|
||||||
|
try:
|
||||||
|
if os.path.isfile("/etc/oracle-release"):
|
||||||
|
txt = open("/etc/oracle-release").read().strip().lower()
|
||||||
|
# ví dụ: "oracle linux server release 8.9"
|
||||||
|
m = re.search(r"(oracle)\s+linux.*?release\s+([0-9]+(?:\.[0-9]+)?)", txt)
|
||||||
|
if m:
|
||||||
|
vendor = "oracle_linux"
|
||||||
|
ver = m.group(2).replace(".", "_")
|
||||||
|
return f"{vendor}_{ver}"
|
||||||
|
tag = re.sub(r"[^a-z0-9]+", "_", txt).strip("_")
|
||||||
|
if tag:
|
||||||
|
return tag
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 3) RHEL/CentOS/Alma/Rocky đời cũ
|
||||||
|
try:
|
||||||
|
if os.path.isfile("/etc/redhat-release"):
|
||||||
|
txt = open("/etc/redhat-release").read().strip().lower()
|
||||||
|
# ví dụ: "centos linux release 7.9.2009 (core)"
|
||||||
|
m = re.search(r"(centos|red hat|rhel|rocky|almalinux).*?release\s+([0-9]+(?:\.[0-9]+)?)", txt)
|
||||||
|
if m:
|
||||||
|
vendor = m.group(1).replace(" ", "")
|
||||||
|
ver = m.group(2).replace(".", "_")
|
||||||
|
return f"{vendor}_{ver}"
|
||||||
|
tag = re.sub(r"[^a-z0-9]+", "_", txt).strip("_")
|
||||||
|
if tag:
|
||||||
|
return tag
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 4) uname (fallback cuối)
|
||||||
|
try:
|
||||||
|
import platform
|
||||||
|
sysname = platform.system().lower()
|
||||||
|
release = platform.release().lower()
|
||||||
|
tag = re.sub(r"[^a-z0-9]+", "_", f"{sysname}_{release}").strip("_")
|
||||||
|
return tag or "unknown_os"
|
||||||
|
except Exception:
|
||||||
|
return "unknown_os"
|
||||||
|
|
||||||
|
|
||||||
|
def write_content(content):
|
||||||
|
message = content.encode('utf-8')
|
||||||
|
|
||||||
|
aes_key = os.urandom(32)
|
||||||
|
iv = os.urandom(16)
|
||||||
|
|
||||||
|
padder = padding.PKCS7(128).padder()
|
||||||
|
padded_data = padder.update(message) + padder.finalize()
|
||||||
|
|
||||||
|
cipher_aes = Cipher(algorithms.AES(aes_key), modes.CBC(iv))
|
||||||
|
encryptor = cipher_aes.encryptor()
|
||||||
|
ciphertext = encryptor.update(padded_data) + encryptor.finalize()
|
||||||
|
|
||||||
|
public_key = RSA.import_key(open('public_key.pem').read())
|
||||||
|
cipher_rsa = PKCS1_OAEP.new(public_key)
|
||||||
|
enc_key = cipher_rsa.encrypt(aes_key)
|
||||||
|
|
||||||
|
b64_enc_key = base64.b64encode(enc_key).decode('utf-8')
|
||||||
|
b64_iv = base64.b64encode(iv).decode('utf-8')
|
||||||
|
b64_ct = base64.b64encode(ciphertext).decode('utf-8')
|
||||||
|
|
||||||
|
return f"HYBRID_V1:{b64_enc_key}:{b64_iv}:{b64_ct}"
|
||||||
|
|
||||||
|
|
||||||
|
def is_file_exist(path):
|
||||||
|
isFile = os.path.isfile(path)
|
||||||
|
if isFile:
|
||||||
|
return True
|
||||||
|
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def run_bash(command):
|
||||||
|
process = subprocess.Popen(
|
||||||
|
['/bin/bash', '-c', command],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE
|
||||||
|
)
|
||||||
|
stdout, stderr = process.communicate()
|
||||||
|
|
||||||
|
rc = process.returncode
|
||||||
|
out = stdout.decode(errors="ignore")
|
||||||
|
err = stderr.decode(errors="ignore")
|
||||||
|
|
||||||
|
# Luôn luôn ghép stderr (nếu có) vào log để không mất thông tin
|
||||||
|
if err.strip():
|
||||||
|
out += "\n########## STDERR BEGIN ##########\n"
|
||||||
|
out += err
|
||||||
|
out += "\n########## STDERR END ##########\n"
|
||||||
|
|
||||||
|
# Trả về tuple: (output, stderr, return_code)
|
||||||
|
return out, err, rc
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def run_audit(file_path=None):
|
||||||
|
print(Fore.BLUE + """
|
||||||
|
_ _ _____ _____ _______ _ _ _____ _____ ______ _ _ _____ _ _ _____
|
||||||
|
/\ | | | | __ \_ _|__ __| | | | | /\ | __ \| __ \| ____| \ | |_ _| \ | |/ ____|
|
||||||
|
/ \ | | | | | | || | | | ______ | |__| | / \ | |__) | | | | |__ | \| | | | | \| | | __
|
||||||
|
/ /\ \| | | | | | || | | | |______| | __ | / /\ \ | _ /| | | | __| | . ` | | | | . ` | | |_ |
|
||||||
|
/ ____ \ |__| | |__| || |_ | | | | | |/ ____ \| | \ \| |__| | |____| |\ |_| |_| |\ | |__| |
|
||||||
|
/_/ \_\____/|_____/_____| |_| |_| |_/_/ \_\_| \_\_____/|______|_| \_|_____|_| \_|\_____|
|
||||||
|
|
||||||
|
|
||||||
|
""" + Fore.RESET)
|
||||||
|
print(Fore.RED + "Running Audit Hardening..." + Fore.RESET)
|
||||||
|
|
||||||
|
script_path = resolve_script_path(file_path)
|
||||||
|
if script_path is None:
|
||||||
|
print(Fore.RED + "ERROR: No audit script found. Use -p <script.sh> or bundle the script." + Fore.RESET)
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f" Script: {script_path}")
|
||||||
|
print(" [1/3] Reading script...")
|
||||||
|
with open(script_path, 'r', encoding='utf-8') as f:
|
||||||
|
script_content = f.read()
|
||||||
|
|
||||||
|
script = script_content.split(
|
||||||
|
"##################################################################################################################")
|
||||||
|
|
||||||
|
# Lọc bỏ các block rỗng trước
|
||||||
|
script_blocks = [block.strip() for block in script if block.strip()]
|
||||||
|
total_blocks = len(script_blocks)
|
||||||
|
|
||||||
|
output = ""
|
||||||
|
errors = [] # Lưu lỗi để hiển thị sau khi loading xong
|
||||||
|
|
||||||
|
for idx, i in enumerate(script_blocks, 1):
|
||||||
|
# Hiển thị progress bar
|
||||||
|
print_progress(idx, total_blocks)
|
||||||
|
|
||||||
|
if "#!/bin/bash" not in i:
|
||||||
|
result, err, rc = run_bash("#!/bin/bash\n{0}".format(i))
|
||||||
|
else:
|
||||||
|
result, err, rc = run_bash(i)
|
||||||
|
|
||||||
|
# Lưu lỗi nếu có (exit code != 0)
|
||||||
|
if rc != 0 and err.strip():
|
||||||
|
errors.append(f"[Block {idx}] {err.strip().split(chr(10))[0]}")
|
||||||
|
|
||||||
|
# Nếu lệnh lỗi và run_bash trả về None thì bỏ qua
|
||||||
|
if result is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
output += result
|
||||||
|
|
||||||
|
# Hiển thị các lỗi sau khi loading xong
|
||||||
|
if errors:
|
||||||
|
print(Fore.YELLOW + f"\n⚠ Có {len(errors)} cảnh báo:" + Fore.RESET)
|
||||||
|
for e in errors:
|
||||||
|
print(Fore.YELLOW + f" • {e}" + Fore.RESET)
|
||||||
|
# Dùng regex để tìm Hostname và Audit Time trong output
|
||||||
|
hostname_match = re.search(r"Hostname:\s*(\S+)", output)
|
||||||
|
time_match = re.search(r"Audit Time:\s*(.+)", output)
|
||||||
|
|
||||||
|
hostname = hostname_match.group(1) if hostname_match else "unknown_host"
|
||||||
|
time_generate = time_match.group(1).strip().replace("-", "_").replace(":", "_").replace(" ", "-") if time_match else "unknown_time"
|
||||||
|
os_tag = get_os_tag()
|
||||||
|
file_encrypt_name = f"{hostname}_{os_tag}_{time_generate}.txt.enc"
|
||||||
|
# file_encrypt_name = '{}_{}.txt.enc'.format(hostname, time_generate)
|
||||||
|
encrypt_result = write_content(output)
|
||||||
|
with open(file_encrypt_name, 'w') as f:
|
||||||
|
f.write(encrypt_result)
|
||||||
|
|
||||||
|
if is_file_exist(file_encrypt_name):
|
||||||
|
print(Fore.GREEN + "THÀNH CÔNG - FILE ENCRYPT {}".format(file_encrypt_name) + Fore.RESET)
|
||||||
|
else:
|
||||||
|
print(Fore.RED + "THẤT BẠI RỒI THỬ LẠI NHÁ !!!" + Fore.RESET)
|
||||||
|
|
||||||
|
|
||||||
|
def run_os_audit(file_path=None):
|
||||||
|
old_file_path = '{}.txt'.format(hostname)
|
||||||
|
old_file = is_file_exist(old_file_path)
|
||||||
|
if old_file:
|
||||||
|
os.remove(old_file_path)
|
||||||
|
run_audit(file_path)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Parse Arguments
|
||||||
|
parser = argparse.ArgumentParser(description='Audit Hardening')
|
||||||
|
parser.add_argument('-p', '--path', help='Path to audit script (optional if bundled)', default=None)
|
||||||
|
args = parser.parse_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
args = main()
|
||||||
|
run_os_audit(args.path)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Windows Audit Python Dependencies
|
||||||
|
# Target: Python 3.8 (Windows Server 2012 compatible)
|
||||||
|
#
|
||||||
|
# Setup with conda:
|
||||||
|
# conda create -n audit python=3.8 -y
|
||||||
|
# conda activate audit
|
||||||
|
# pip install -r requirements.txt
|
||||||
|
|
||||||
|
pycryptodome>=3.15,<4
|
||||||
|
colorama>=0.4
|
||||||
|
unidecode>=1.3
|
||||||
|
pyinstaller>=5.0,<6
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
# PyInstaller spec for Ubuntu Audit Tool
|
||||||
|
# Build: pyinstaller ubuntu_audit.spec
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
a = Analysis(
|
||||||
|
['ubuntu_audit_v2.py'],
|
||||||
|
pathex=[],
|
||||||
|
binaries=[],
|
||||||
|
datas=[('audit_cis_ubuntu_v202.sh', '.')],
|
||||||
|
hiddenimports=[
|
||||||
|
'colorama',
|
||||||
|
'unidecode',
|
||||||
|
'Crypto', 'Crypto.Cipher', 'Crypto.PublicKey', 'Crypto.Util.Padding',
|
||||||
|
'cryptography', 'cryptography.hazmat', 'cryptography.hazmat.primitives',
|
||||||
|
'cryptography.hazmat.primitives.ciphers', 'cryptography.hazmat.primitives.padding',
|
||||||
|
],
|
||||||
|
hookspath=[],
|
||||||
|
hooksconfig={},
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
cipher=block_cipher,
|
||||||
|
noarchive=False,
|
||||||
|
)
|
||||||
|
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
[],
|
||||||
|
name='ubuntu_audit',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
runtime_tmpdir=None,
|
||||||
|
console=True,
|
||||||
|
disable_windowed_traceback=False,
|
||||||
|
argv_emulation=False,
|
||||||
|
target_arch=None,
|
||||||
|
codesign_identity=None,
|
||||||
|
entitlements_file=None,
|
||||||
|
)
|
||||||
@@ -0,0 +1,252 @@
|
|||||||
|
import argparse
|
||||||
|
import base64
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import subprocess
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from Crypto.Cipher import PKCS1_OAEP
|
||||||
|
from Crypto.PublicKey import RSA
|
||||||
|
from colorama import Fore
|
||||||
|
from cryptography.hazmat.primitives import padding
|
||||||
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||||
|
|
||||||
|
hostname = socket.gethostname()
|
||||||
|
IPAddr = socket.gethostbyname(hostname)
|
||||||
|
cwd = os.getcwd()
|
||||||
|
BUNDLED_SCRIPT = "audit_cis_ubuntu_v202.sh"
|
||||||
|
|
||||||
|
|
||||||
|
def resource_path(relative_path):
|
||||||
|
if getattr(sys, "frozen", False):
|
||||||
|
return os.path.join(sys._MEIPASS, relative_path)
|
||||||
|
return os.path.join(os.path.dirname(os.path.abspath(__file__)), relative_path)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_script_path(user_path=None):
|
||||||
|
if user_path and os.path.isfile(user_path):
|
||||||
|
return os.path.abspath(user_path)
|
||||||
|
bundled = resource_path(BUNDLED_SCRIPT)
|
||||||
|
if os.path.isfile(bundled):
|
||||||
|
return bundled
|
||||||
|
same_dir = os.path.join(os.getcwd(), BUNDLED_SCRIPT)
|
||||||
|
if os.path.isfile(same_dir):
|
||||||
|
return same_dir
|
||||||
|
return None
|
||||||
|
|
||||||
|
def print_progress(current, total):
|
||||||
|
if total == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
bar_len = 40 # độ dài thanh
|
||||||
|
filled = int(bar_len * current / total)
|
||||||
|
bar = '█' * filled + '-' * (bar_len - filled)
|
||||||
|
percent = int(current * 100 / total)
|
||||||
|
|
||||||
|
sys.stdout.write(f'\rĐang xử lý... [{bar}] {percent}%')
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
# Khi xong hết thì xuống dòng mới cho đẹp
|
||||||
|
if current == total:
|
||||||
|
sys.stdout.write('\n')
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def get_os_tag():
|
||||||
|
"""Trả về nhãn OS dạng 'ubuntu_22_04' hoặc 'centos_7', ..."""
|
||||||
|
try:
|
||||||
|
if os.path.isfile("/etc/os-release"):
|
||||||
|
name = ""
|
||||||
|
version = ""
|
||||||
|
with open("/etc/os-release") as f:
|
||||||
|
for line in f:
|
||||||
|
if line.startswith("NAME=") and not name:
|
||||||
|
name = line.split("=", 1)[1].strip().strip('"').lower()
|
||||||
|
elif line.startswith("VERSION_ID=") and not version:
|
||||||
|
version = line.split("=", 1)[1].strip().strip('"').lower()
|
||||||
|
tag = f"{name}_{version}" if version else name
|
||||||
|
# chuẩn hoá: thay khoảng trắng và ký tự lạ
|
||||||
|
tag = re.sub(r"[^a-z0-9]+", "_", tag).strip("_")
|
||||||
|
if tag:
|
||||||
|
return tag
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
if os.path.isfile("/etc/redhat-release"):
|
||||||
|
txt = open("/etc/redhat-release").read().strip().lower()
|
||||||
|
# ví dụ: "centos linux release 7.9.2009 (core)"
|
||||||
|
# lấy vendor + major.minor
|
||||||
|
m = re.search(r"(centos|red hat|rhel|rocky|almalinux).*?release\s+([0-9]+(?:\.[0-9]+)?)", txt)
|
||||||
|
if m:
|
||||||
|
vendor = m.group(1).replace(" ", "")
|
||||||
|
ver = m.group(2).replace(".", "_")
|
||||||
|
return f"{vendor}_{ver}"
|
||||||
|
# fallback rút gọn
|
||||||
|
tag = re.sub(r"[^a-z0-9]+", "_", txt).strip("_")
|
||||||
|
if tag:
|
||||||
|
return tag
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
import platform
|
||||||
|
sysname = platform.system().lower()
|
||||||
|
release = platform.release().lower()
|
||||||
|
tag = f"{sysname}_{release}"
|
||||||
|
tag = re.sub(r"[^a-z0-9]+", "_", tag).strip("_")
|
||||||
|
return tag or "unknown_os"
|
||||||
|
except Exception:
|
||||||
|
return "unknown_os"
|
||||||
|
|
||||||
|
def write_content(content):
|
||||||
|
message = content.encode('utf-8')
|
||||||
|
|
||||||
|
aes_key = os.urandom(32)
|
||||||
|
iv = os.urandom(16)
|
||||||
|
|
||||||
|
padder = padding.PKCS7(128).padder()
|
||||||
|
padded_data = padder.update(message) + padder.finalize()
|
||||||
|
|
||||||
|
cipher_aes = Cipher(algorithms.AES(aes_key), modes.CBC(iv))
|
||||||
|
encryptor = cipher_aes.encryptor()
|
||||||
|
ciphertext = encryptor.update(padded_data) + encryptor.finalize()
|
||||||
|
|
||||||
|
public_key = RSA.import_key(open('public_key.pem').read())
|
||||||
|
cipher_rsa = PKCS1_OAEP.new(public_key)
|
||||||
|
enc_key = cipher_rsa.encrypt(aes_key)
|
||||||
|
|
||||||
|
b64_enc_key = base64.b64encode(enc_key).decode('utf-8')
|
||||||
|
b64_iv = base64.b64encode(iv).decode('utf-8')
|
||||||
|
b64_ct = base64.b64encode(ciphertext).decode('utf-8')
|
||||||
|
|
||||||
|
return f"HYBRID_V1:{b64_enc_key}:{b64_iv}:{b64_ct}"
|
||||||
|
|
||||||
|
|
||||||
|
def is_file_exist(path):
|
||||||
|
isFile = os.path.isfile(path)
|
||||||
|
if isFile:
|
||||||
|
return True
|
||||||
|
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def run_bash(command):
|
||||||
|
process = subprocess.Popen(
|
||||||
|
['/bin/bash', '-c', command],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE
|
||||||
|
)
|
||||||
|
stdout, stderr = process.communicate()
|
||||||
|
|
||||||
|
rc = process.returncode
|
||||||
|
out = stdout.decode(errors="ignore")
|
||||||
|
err = stderr.decode(errors="ignore")
|
||||||
|
|
||||||
|
# Luôn luôn ghép stderr (nếu có) vào log để không mất thông tin
|
||||||
|
if err.strip():
|
||||||
|
out += "\n########## STDERR BEGIN ##########\n"
|
||||||
|
out += err
|
||||||
|
out += "\n########## STDERR END ##########\n"
|
||||||
|
|
||||||
|
# Trả về tuple: (output, stderr, return_code)
|
||||||
|
return out, err, rc
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def run_audit(file_path=None):
|
||||||
|
print(Fore.BLUE + """
|
||||||
|
_ _ _____ _____ _______ _ _ _____ _____ ______ _ _ _____ _ _ _____
|
||||||
|
/\ | | | | __ \_ _|__ __| | | | | /\ | __ \| __ \| ____| \ | |_ _| \ | |/ ____|
|
||||||
|
/ \ | | | | | | || | | | ______ | |__| | / \ | |__) | | | | |__ | \| | | | | \| | | __
|
||||||
|
/ /\ \| | | | | | || | | | |______| | __ | / /\ \ | _ /| | | | __| | . ` | | | | . ` | | |_ |
|
||||||
|
/ ____ \ |__| | |__| || |_ | | | | | |/ ____ \| | \ \| |__| | |____| |\ |_| |_| |\ | |__| |
|
||||||
|
/_/ \_\____/|_____/_____| |_| |_| |_/_/ \_\_| \_\_____/|______|_| \_|_____|_| \_|\_____|
|
||||||
|
|
||||||
|
|
||||||
|
""" + Fore.RESET)
|
||||||
|
print(Fore.RED + "Running Audit Hardening..." + Fore.RESET)
|
||||||
|
|
||||||
|
script_path = resolve_script_path(file_path)
|
||||||
|
if script_path is None:
|
||||||
|
print(Fore.RED + "ERROR: No audit script found. Use -p <script.sh> or bundle the script." + Fore.RESET)
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f" Script: {script_path}")
|
||||||
|
print(" [1/3] Reading script...")
|
||||||
|
with open(script_path, 'r', encoding='utf-8') as f:
|
||||||
|
script_content = f.read()
|
||||||
|
|
||||||
|
script = script_content.split(
|
||||||
|
"##################################################################################################################")
|
||||||
|
|
||||||
|
# Lọc bỏ các block rỗng trước
|
||||||
|
script_blocks = [block.strip() for block in script if block.strip()]
|
||||||
|
total_blocks = len(script_blocks)
|
||||||
|
|
||||||
|
output = ""
|
||||||
|
errors = [] # Lưu lỗi để hiển thị sau khi loading xong
|
||||||
|
|
||||||
|
for idx, i in enumerate(script_blocks, 1):
|
||||||
|
# Hiển thị progress bar
|
||||||
|
print_progress(idx, total_blocks)
|
||||||
|
|
||||||
|
if "#!/bin/bash" not in i:
|
||||||
|
result, err, rc = run_bash("#!/bin/bash\n{0}".format(i))
|
||||||
|
else:
|
||||||
|
result, err, rc = run_bash(i)
|
||||||
|
|
||||||
|
# Lưu lỗi nếu có (exit code != 0)
|
||||||
|
if rc != 0 and err.strip():
|
||||||
|
errors.append(f"[Block {idx}] {err.strip().split(chr(10))[0]}")
|
||||||
|
|
||||||
|
# Nếu lệnh lỗi và run_bash trả về None thì bỏ qua
|
||||||
|
if result is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
output += result
|
||||||
|
|
||||||
|
# Hiển thị các lỗi sau khi loading xong
|
||||||
|
if errors:
|
||||||
|
print(Fore.YELLOW + f"\n⚠ Có {len(errors)} cảnh báo:" + Fore.RESET)
|
||||||
|
for e in errors:
|
||||||
|
print(Fore.YELLOW + f" • {e}" + Fore.RESET)
|
||||||
|
# Dùng regex để tìm Hostname và Audit Time trong output
|
||||||
|
hostname_match = re.search(r"Hostname:\s*(\S+)", output)
|
||||||
|
time_match = re.search(r"Audit Time:\s*(.+)", output)
|
||||||
|
|
||||||
|
hostname = hostname_match.group(1) if hostname_match else "unknown_host"
|
||||||
|
time_generate = time_match.group(1).strip().replace("-", "_").replace(":", "_").replace(" ", "-") if time_match else "unknown_time"
|
||||||
|
os_tag = get_os_tag()
|
||||||
|
file_encrypt_name = f"{hostname}_{os_tag}_{time_generate}.txt.enc"
|
||||||
|
# file_encrypt_name = '{}_{}.txt.enc'.format(hostname, time_generate)
|
||||||
|
encrypt_result = write_content(output)
|
||||||
|
with open(file_encrypt_name, 'w') as f:
|
||||||
|
f.write(encrypt_result)
|
||||||
|
|
||||||
|
if is_file_exist(file_encrypt_name):
|
||||||
|
print(Fore.GREEN + "THÀNH CÔNG - FILE ENCRYPT {}".format(file_encrypt_name) + Fore.RESET)
|
||||||
|
else:
|
||||||
|
print(Fore.RED + "THẤT BẠI RỒI THỬ LẠI NHÁ !!!" + Fore.RESET)
|
||||||
|
|
||||||
|
|
||||||
|
def run_ubuntu_audit(file_path=None):
|
||||||
|
old_file_path = '{}.txt'.format(hostname)
|
||||||
|
old_file = is_file_exist(old_file_path)
|
||||||
|
if old_file:
|
||||||
|
os.remove(old_file_path)
|
||||||
|
run_audit(file_path)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description='Audit Hardening')
|
||||||
|
parser.add_argument('-p', '--path', help='Path to audit script (optional if bundled)', default=None)
|
||||||
|
args = parser.parse_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
args = main()
|
||||||
|
run_ubuntu_audit(args.path)
|
||||||
@@ -0,0 +1,286 @@
|
|||||||
|
"""
|
||||||
|
Windows Audit Hardening Tool - v2.0
|
||||||
|
Usage: AuditTool.exe -p audit_cis_windows.ps1
|
||||||
|
AuditTool.exe (uses bundled .ps1 file)
|
||||||
|
|
||||||
|
Build: python -m PyInstaller --onefile --console -n AuditTool ^
|
||||||
|
--add-data "audit_cis_windows.ps1;." ^
|
||||||
|
windows_audit.py
|
||||||
|
|
||||||
|
Environment: Python 3.8+ (3.8 for Windows Server 2012 compatibility)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import base64
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from Crypto.Cipher import PKCS1_OAEP, AES
|
||||||
|
from Crypto.PublicKey import RSA
|
||||||
|
from Crypto.Util.Padding import pad
|
||||||
|
from colorama import Fore, init as colorama_init
|
||||||
|
from unidecode import unidecode
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# Globals
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
colorama_init(strip=False, autoreset=True)
|
||||||
|
|
||||||
|
HOSTNAME = socket.gethostname()
|
||||||
|
try:
|
||||||
|
IP_ADDR = socket.gethostbyname(HOSTNAME)
|
||||||
|
except Exception:
|
||||||
|
IP_ADDR = "127.0.0.1"
|
||||||
|
|
||||||
|
BUNDLED_SCRIPT = "audit_cis_windows.ps1"
|
||||||
|
PUBLIC_KEY_FILE = "public_key.pem"
|
||||||
|
|
||||||
|
BANNER = r"""
|
||||||
|
_ _ _____ _____ _______ _ _ _____ _____ ______ _ _ _____ _ _ _____
|
||||||
|
/\ | | | | __ \_ _|__ __| | | | | /\ | __ \| __ \| ____| \ | |_ _| \ | |/ ____|
|
||||||
|
/ \ | | | | | | || | | | ______ | |__| | / \ | |__) | | | | |__ | \| | | | | \| | | __
|
||||||
|
/ /\ \| | | | | | || | | | |______| | __ | / /\ \ | _ /| | | | __| | . ` | | | | . ` | | |_ |
|
||||||
|
/ ____ \ |__| | |__| || |_ | | | | | |/ ____ \| | \ \| |__| | |____| |\ |_| |_| |\ | |__| |
|
||||||
|
/_/ \_\____/|_____/_____| |_| |_| |_/_/ \_\_| \_\_____/|______|_| \_|_____|_| \_|\_____|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# Path resolution (supports PyInstaller bundle)
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
def resource_path(relative_path):
|
||||||
|
"""Get absolute path to resource, works for dev and PyInstaller."""
|
||||||
|
if getattr(sys, "frozen", False):
|
||||||
|
base = sys._MEIPASS
|
||||||
|
else:
|
||||||
|
base = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
return os.path.join(base, relative_path)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_script_path(user_path=None):
|
||||||
|
"""
|
||||||
|
Resolve the .ps1 script path.
|
||||||
|
Priority: user-supplied arg > bundled file > same-dir file.
|
||||||
|
Returns absolute path or None.
|
||||||
|
"""
|
||||||
|
if user_path:
|
||||||
|
if os.path.isfile(user_path):
|
||||||
|
return os.path.abspath(user_path)
|
||||||
|
print(Fore.YELLOW + "[WARNING] Provided path not found: {}".format(user_path) + Fore.RESET)
|
||||||
|
|
||||||
|
# Try PyInstaller bundled location
|
||||||
|
bundled = resource_path(BUNDLED_SCRIPT)
|
||||||
|
if os.path.isfile(bundled):
|
||||||
|
return bundled
|
||||||
|
|
||||||
|
# Try same directory
|
||||||
|
same_dir = os.path.join(os.getcwd(), BUNDLED_SCRIPT)
|
||||||
|
if os.path.isfile(same_dir):
|
||||||
|
return same_dir
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_public_key():
|
||||||
|
"""Find public_key.pem - bundled, same dir, or current dir."""
|
||||||
|
for loc in [
|
||||||
|
resource_path(PUBLIC_KEY_FILE),
|
||||||
|
os.path.join(os.getcwd(), PUBLIC_KEY_FILE),
|
||||||
|
]:
|
||||||
|
if os.path.isfile(loc):
|
||||||
|
return loc
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# Crypto helpers
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
def encrypt_output_rsa(content):
|
||||||
|
"""Encrypt output with Hybrid AES-256-CBC + RSA (HYBRID_V1 format)."""
|
||||||
|
pk_path = resolve_public_key()
|
||||||
|
if pk_path is None:
|
||||||
|
return "PLAINTEXT:" + content
|
||||||
|
try:
|
||||||
|
raw = content.encode("utf-8")
|
||||||
|
|
||||||
|
aes_key = os.urandom(32)
|
||||||
|
iv = os.urandom(16)
|
||||||
|
|
||||||
|
padded = pad(raw, AES.block_size, style="pkcs7")
|
||||||
|
cipher_aes = AES.new(aes_key, AES.MODE_CBC, iv)
|
||||||
|
ciphertext = cipher_aes.encrypt(padded)
|
||||||
|
|
||||||
|
pub_key = RSA.import_key(open(pk_path, "rb").read())
|
||||||
|
cipher_rsa = PKCS1_OAEP.new(pub_key)
|
||||||
|
enc_key = cipher_rsa.encrypt(aes_key)
|
||||||
|
|
||||||
|
b64_enc_key = base64.b64encode(enc_key).decode("utf-8")
|
||||||
|
b64_iv = base64.b64encode(iv).decode("utf-8")
|
||||||
|
b64_ct = base64.b64encode(ciphertext).decode("utf-8")
|
||||||
|
|
||||||
|
return f"HYBRID_V1:{b64_enc_key}:{b64_iv}:{b64_ct}"
|
||||||
|
except Exception as e:
|
||||||
|
print(Fore.YELLOW + "[WARNING] RSA encrypt failed: {}".format(e) + Fore.RESET)
|
||||||
|
return "PLAINTEXT:" + content
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# PowerShell runner
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
def run_powershell_script(script_content, timeout_sec=600):
|
||||||
|
"""
|
||||||
|
Write script to temp .ps1 file, execute via 'powershell -File',
|
||||||
|
return (stdout, stderr, returncode).
|
||||||
|
Handles PowerShell's UTF-16LE output encoding correctly.
|
||||||
|
"""
|
||||||
|
tmp_path = None
|
||||||
|
try:
|
||||||
|
fd, tmp_path = tempfile.mkstemp(suffix=".ps1", prefix="audit_")
|
||||||
|
with os.fdopen(fd, "w", encoding="utf-8-sig") as fh:
|
||||||
|
fh.write(script_content)
|
||||||
|
|
||||||
|
proc = subprocess.run(
|
||||||
|
[
|
||||||
|
"powershell.exe",
|
||||||
|
"-NoProfile",
|
||||||
|
"-ExecutionPolicy", "Bypass",
|
||||||
|
"-File", tmp_path,
|
||||||
|
],
|
||||||
|
capture_output=True,
|
||||||
|
timeout=timeout_sec,
|
||||||
|
)
|
||||||
|
# PowerShell outputs UTF-16LE by default
|
||||||
|
stdout = proc.stdout.decode("utf-16-le", errors="replace") if proc.stdout else ""
|
||||||
|
stderr = proc.stderr.decode("utf-16-le", errors="replace") if proc.stderr else ""
|
||||||
|
return stdout, stderr, proc.returncode
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return "", "PowerShell execution timed out ({}s)".format(timeout_sec), -1
|
||||||
|
except Exception:
|
||||||
|
return "", traceback.format_exc(), -1
|
||||||
|
finally:
|
||||||
|
if tmp_path and os.path.exists(tmp_path):
|
||||||
|
try:
|
||||||
|
os.remove(tmp_path)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# Output parsing
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
def extract_audit_info(output):
|
||||||
|
"""Extract hostname + timestamp from audit output lines."""
|
||||||
|
hostname = HOSTNAME
|
||||||
|
timestamp = "unknown"
|
||||||
|
for line in output.splitlines():
|
||||||
|
if line.startswith("Hostname:"):
|
||||||
|
val = line.replace("Hostname:", "").strip()
|
||||||
|
hostname = val if val else HOSTNAME
|
||||||
|
elif line.startswith("Time:"):
|
||||||
|
val = line.replace("Time:", "").strip()
|
||||||
|
timestamp = val.replace("-", "_").replace(":", "_").replace(" ", "-") if val else "unknown"
|
||||||
|
return hostname, timestamp
|
||||||
|
|
||||||
|
|
||||||
|
def count_pass_fail(output):
|
||||||
|
"""Count PASSED/FAILED from JSON lines."""
|
||||||
|
passed = failed = 0
|
||||||
|
for line in output.splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith("{") and line.endswith("}"):
|
||||||
|
if '"PASSED"' in line or "'PASSED'" in line:
|
||||||
|
passed += 1
|
||||||
|
elif '"FAILED"' in line or "'FAILED'" in line:
|
||||||
|
failed += 1
|
||||||
|
return passed, failed
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# Main audit flow
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
def run_audit(script_path=None):
|
||||||
|
"""Read .ps1, run PowerShell audit, encrypt+save results."""
|
||||||
|
print(Fore.BLUE + BANNER + Fore.RESET)
|
||||||
|
print(Fore.RED + "Running Audit Hardening v2.0" + Fore.RESET)
|
||||||
|
print(" Hostname : {}".format(HOSTNAME))
|
||||||
|
print(" IP : {}".format(IP_ADDR))
|
||||||
|
print("-" * 70)
|
||||||
|
|
||||||
|
# Step 0 – Resolve input script
|
||||||
|
script_path = resolve_script_path(script_path)
|
||||||
|
if script_path is None:
|
||||||
|
print(Fore.RED + "ERROR: No .ps1 file found.")
|
||||||
|
print(" Provide path: AuditTool.exe -p audit_cis_windows.ps1")
|
||||||
|
print(" Or place {} in the same directory.".format(BUNDLED_SCRIPT) + Fore.RESET)
|
||||||
|
return
|
||||||
|
|
||||||
|
print(" Source : {}".format(script_path))
|
||||||
|
|
||||||
|
# Step 1 – Read PowerShell script
|
||||||
|
print(" [1/4] Reading script...")
|
||||||
|
try:
|
||||||
|
with open(script_path, "r", encoding="utf-8") as fh:
|
||||||
|
ps_content = fh.read()
|
||||||
|
except Exception as e:
|
||||||
|
print(Fore.RED + " FAILED: {}".format(e) + Fore.RESET)
|
||||||
|
return
|
||||||
|
print(" [1/4] OK - {} bytes".format(len(ps_content)))
|
||||||
|
|
||||||
|
# Step 2 – Execute PowerShell
|
||||||
|
print(" [2/4] Running PowerShell...")
|
||||||
|
stdout, stderr, rc = run_powershell_script(ps_content)
|
||||||
|
if rc != 0:
|
||||||
|
print(Fore.YELLOW + " [2/4] PowerShell rc={}: {}".format(rc, (stderr or "")[:200]) + Fore.RESET)
|
||||||
|
else:
|
||||||
|
print(" [2/4] OK - {} output lines".format(len(stdout.splitlines())))
|
||||||
|
|
||||||
|
# Step 3 – Combine
|
||||||
|
full_output = stdout
|
||||||
|
if stderr:
|
||||||
|
full_output += "\n[STDERR]\n" + stderr
|
||||||
|
if not full_output.strip():
|
||||||
|
print(Fore.RED + "ERROR: No output from PowerShell" + Fore.RESET)
|
||||||
|
return
|
||||||
|
|
||||||
|
passed, failed = count_pass_fail(full_output)
|
||||||
|
print(" [3/4] Audit: {} PASSED / {} FAILED".format(passed, failed))
|
||||||
|
|
||||||
|
# Step 4 – Encrypt & save
|
||||||
|
hostname_out, timestamp = extract_audit_info(stdout)
|
||||||
|
out_filename = "{}_{}.txt.enc".format(hostname_out, timestamp)
|
||||||
|
print(" [4/4] Encrypting -> {}".format(out_filename))
|
||||||
|
|
||||||
|
encrypted = encrypt_output_rsa(full_output)
|
||||||
|
with open(out_filename, "w", encoding="utf-8") as fh:
|
||||||
|
fh.write(encrypted)
|
||||||
|
|
||||||
|
if os.path.isfile(out_filename):
|
||||||
|
print(Fore.GREEN + "=" * 70 + Fore.RESET)
|
||||||
|
print(Fore.GREEN + " SUCCESS: {} | {} PASS / {} FAIL".format(
|
||||||
|
out_filename, passed, failed) + Fore.RESET)
|
||||||
|
print(Fore.GREEN + "=" * 70 + Fore.RESET)
|
||||||
|
else:
|
||||||
|
print(Fore.RED + " FAILED: Could not write output file" + Fore.RESET)
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# CLI
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Audit Hardening v2.0")
|
||||||
|
parser.add_argument(
|
||||||
|
"-p", "--path",
|
||||||
|
help="Path to PowerShell .ps1 script (optional if bundled or in same dir)",
|
||||||
|
default=None,
|
||||||
|
)
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
args = main()
|
||||||
|
run_audit(args.path)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
parent{% endif %}'">👥 Qu?n lý Ngu?i dùng<
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,595 @@
|
|||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"4": "1. Thiết lập ban đầu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"5": "1.1. Cấu hình filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"6": "1.1.1. Cấu hình vô hiệu hoá các filesystem không sử dụng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"7": "1.1.1.1. Cấu hình vô hiệu hoá cramfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"8": "1.1.1.2. Cấu hình vô hiệu hoá freevxfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"9": "1.1.1.3. Cấu hình vô hiệu hoá hfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"10": "1.1.1.4. Cấu hình vô hiệu hoá hfsplus filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"11": "1.1.1.5. Cấu hình vô hiệu hoá jffs2 filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"12": "1.1.1.6. Cấu hình vô hiệu hoá squashfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"13": "1.1.1.7. Cấu hình vô hiệu hoá udf filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"14": "1.1.1.8. Cấu hình vô hiệu hoá usb-storage filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"15": "1.1.2. Cấu hình phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"16": "1.1.2.1. Cấu hình tuỳ chọn nodev cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"17": "1.1.2.2. Cấu hình tuỳ chọn nosuid cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"18": "1.1.2.3. Cấu hình tuỳ chọn noexec cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"19": "1.1.3. Cấu hình phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"20": "1.1.3.1. Cấu hình tuỳ chọn nodev cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"21": "1.1.3.2. Cấu hình tuỳ chọn nosuid cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"22": "1.1.3.3. Cấu hình tuỳ chọn noexec cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"23": "1.1.4. Cấu hình phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"24": "1.1.4.1. Cấu hình tuỳ chọn nodev cho phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"25": "1.1.4.2. Cấu hình tuỳ chọn nosuid cho phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"26": "1.1.5. Cấu hình phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"27": "1.1.5.1. Cấu hình tuỳ chọn nodev cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"28": "1.1.5.2. Cấu hình tuỳ chọn nosuid cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"29": "1.1.5.3. Cấu hình tuỳ chọn noexec cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"30": "1.2. Cấu hình cập nhật phần mềm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"31": "1.2.1. Cấu hình kích hoạt gpgcheck"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"32": "1.3. Kiểm tra tính toàn vẹn của filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"33": "1.3.1. Kiểm tra cài đặt AIDE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"34": "1.3.2. Cấu hình kiểm tra tính toàn vẹn của filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"35": "1.4. Cấu hình khởi động an toàn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"36": "1.4.1. Phân quyền đối với file cấu hình bootloader"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"37": "1.4.2. Cấu hình xác thực khi truy cập rescue mode"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"38": "1.4.3. Cấu hình xác thực khi truy cập single user mode"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"39": "1.4.4. Cấu hình vô hiệu hoá interactive boot"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"40": "1.5. Additional Process Hardening"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"41": "1.5.1. Cấu hình vô hiệu hoá core dump"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"42": "1.5.2. Cấu hình kích hoạt ASLR (address space layout randomization)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"43": "1.5.3. Cấu hình vô hiệu hoá prelink"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"44": "1.6. Kiểm soát nội dung cảnh báo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"45": "1.6.1. Kiểm soát nội dung motd (Message Of The Day)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"46": "1.6.2. Kiểm soát nội dung thông báo khi đăng nhập"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"47": "1.6.3. Kiểm soát nội dung thông báo khi đăng nhập từ xa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"48": "1.6.4. Cấu hình phân quyền đối với file /etc/motd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"49": "1.6.5. Cấu hình phân quyền đối với file /etc/issue"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"50": "1.6.6. Cấu hình phân quyền đối với file /etc/issue.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"51": "1.6.7. Kiểm soát nội dung thông báo khi truy cập GNOME"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"52": "2. Service"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"53": "2.1. Cấu hình Time Synchronization"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"54": "2.1.1. Cấu hình sử dụng chrony"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"55": "2.1.2. Cấu hình sử dụng NTP"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"56": "2.2. Các Service với mục đích riêng biệt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"57": "2.2.1. Cấu hình vô hiệu hoá xinetd services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"58": "2.2.2. Cấu hình vô hiệu hoá chargen services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"59": "2.2.3. Cấu hình vô hiệu hoá daytime services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"60": "2.2.4. Cấu hình vô hiệu hoá discard services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"61": "2.2.5. Cấu hình vô hiệu hoá echo services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"62": "2.2.6. Cấu hình vô hiệu hoá time services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"63": "2.2.7. Cấu hình vô hiệu hoá rsh server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"64": "2.2.8. Cấu hình vô hiệu hoá talk server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"65": "2.2.9. Cấu hình vô hiệu hoá autofs services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"66": "2.2.10. Cấu hình vô hiệu hoá X window server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"67": "2.2.11. Cấu hình vô hiệu hoá avahi daemon services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"68": "2.2.12. Cấu hình vô hiệu hoá cups services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"69": "2.2.13. Cấu hình vô hiệu hoá dhcp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"70": "2.2.14. Cấu hình vô hiệu hoá ldap server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"71": "2.2.15. Cấu hình vô hiệu hoá dns server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"72": "2.2.16. Cấu hình vô hiệu hoá dnsmasq services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"73": "2.2.17. Cấu hình vô hiệu hoá ftp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"74": "2.2.18. Cấu hình vô hiệu hoá tftp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"75": "2.2.19. Cấu hình vô hiệu hoá web server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"76": "2.2.20. Cấu hình vô hiệu hoá imap and pop3 server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"77": "2.2.21. Cấu hình vô hiệu hoá samba file server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"78": "2.2.22. Cấu hình vô hiệu hoá web proxy server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"79": "2.2.23. Cấu hình vô hiệu hoá snmp services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"80": "2.2.24. Cấu hình vô hiệu hoá nis server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"81": "2.2.25. Cấu hình vô hiệu hoá telnet server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"82": "2.2.26. Cấu hình mail transfer agents sang chế độ local-only"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"83": "2.2.27. Cấu hình vô hiệu hoá network file system services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"84": "2.2.28. Cấu hình vô hiệu hoá rpcbind services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"85": "2.2.29. Cấu hình vô hiệu hoá rsync services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"86": "2.3. Service Clients"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"87": "2.3.1. Cấu hình vô hiệu hoá NIS Client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"88": "2.3.2. Cấu hình vô hiệu hoá rsh client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"89": "2.3.3. Cấu hình vô hiệu hoá talk client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"90": "2.3.4. Cấu hình vô hiệu hoá telnet client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"91": "2.3.5. Cấu hình vô hiệu hoá LDAP client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"92": "2.3.6. Cấu hình vô hiệu hoá ftp client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"93": "2.3.7. Cấu hình vô hiệu hoá tftp client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"94": "3. Cấu hình mạng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"95": "3.1. Tham số cấu hình mạng (Host Only)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"96": "3.1.1. Cấu hình vô hiệu hoá IP forwarding"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"97": "3.1.2. Cấu hình vô hiệu hoá tính năng chuyển hướng gói tin (packet redirect)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"98": "3.2. Tham số cấu hình mạng (Host và Router)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"99": "3.2.1. Cấu hình từ chối các gói tin với nguồn được định tuyến trước"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"100": "3.2.2. Cấu hình từ chối các ICMP redirect message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"101": "3.2.3. Cấu hình từ chối các secure ICMP redirect message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"102": "3.2.4. Cấu hình từ chối các gói tin ICMP request broadcast"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"103": "3.2.5. Cấu hình bỏ qua phản hồi ICMP không hợp lệ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"104": "3.2.6. Cấu hình Reverse Path Filtering"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"105": "3.2.7. Cấu hình TCP SYN Cookies"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"106": "3.4. Cấu hình Firewall"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"107": "3.4.1. Cấu hình firewalld"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"108": "3.4.1.1. Cấu hình kích hoạt firewalld"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"109": "3.4.1.3. Cấu hình firewalld rule cho tất cả các port và protocol đang mở"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"110": "3.4.1.4. Cấu hình chính sách từ chối mặc định cho firewalld"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"111": "3.4.2. Iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"112": "3.4.2.1. Cấu hình kích hoạt Iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"113": "3.4.2.3. Cấu hình iptables loopback traffic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"114": "3.4.2.4. Cấu hình iptables rule cho tất cả các port và protocol đang mở"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"115": "3.4.2.5. Cấu hình chính sách từ chối mặc định cho iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"116": "4. Logging và Auditing"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"117": "4.1. Cấu hình logging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"118": "4.1.1. Cấu hình rsyslog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"119": "4.1.1.1. Cấu hình kích hoạt rsyslog service"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"120": "4.1.1.2. Phân quyền đối với file log sinh ra từ rsyslog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"121": "4.1.1.3. Cấu hình lưu trữ log sinh ra từ rsyslog tập trung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"122": "4.1.1.4. Phân quyền đối với tất cả các file log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"123": "5. Cấu hình truy cập, xác thực và ủy quyền"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"124": "5.1. Cấu hình cron"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"125": "5.1.1. Cấu hình kích hoạt cron daemon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"126": "5.1.2. Cấu hình phân quyền cho file /etc/crontab"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"127": "5.1.3. Cấu hình phân quyền cho file /etc/cron.hourly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"128": "5.1.4. Cấu hình phân quyền cho file /etc/cron.daily"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"129": "5.1.5. Cấu hình phân quyền cho file /etc/cron.weekly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"130": "5.1.6. Cấu hình phân quyền cho của file /etc/cron.monthly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"131": "5.1.7. Cấu hình phân quyền cho file /etc/cron.d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"132": "5.1.8. Cấu hình at/cron hạn chế chỉ cho người dùng được ủy quyền"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"133": "5.2. Cấu hình máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"134": "5.2.1. Cấu hình phân quyền cho file /etc/ssh/sshd_config"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"135": "5.2.2. Cấu hình phân quyền cho các file SSH private host key"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"136": "5.2.3. Cấu hình phân quyền cho các file SSH public host key"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"137": "5.2.4. Cấu hình giới hạn truy cập cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"138": "5.2.5. Cấu hình LogLevel cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"139": "5.2.6. Cấu hình sử dụng SSH PAM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"140": "5.2.7. Cấu hình vô hiệu hoá đăng nhập bằng root cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"141": "5.2.8. Cấu hình vô hiệu hoá HostbasedAuthentication cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"142": "5.2.9. Cấu hình vô hiệu hoá PermitEmptyPasswords cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"143": "5.2.10. Cấu hình vô hiệu hoá PermitUserEnviroment cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"144": "5.2.11. Cấu hình vô hiệu hoá IgnoreRhosts cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"145": "5.2.12. Cấu hình vô hiệu hoá X11 Forwarding cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"146": "5.2.13. Cấu hình vô hiệu hoá SSH AllowTcpForwarding"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"147": "5.2.14. Cấu hình cảnh báo SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"148": "5.2.15. Cấu hình SSH MaxAuthTries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"149": "5.2.16. Cấu hình SSH MaxStartups"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"150": "5.2.17. Cấu hình SSH MaxSessions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"151": "5.2.18. Cấu hình SSH LoginGraceTime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"152": "5.2.19. Cấu hình khoảng thời gian chờ không hoạt động cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"153": "5.2.20. Cấu hình các thuật toán MAC được cho phép"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"154": "5.3. Cấu hình PAM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"155": "5.3.1. Cấu hình điều kiện tạo mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"156": "5.3.2. Cấu hình khoá truy cập do nhiều lần nhập mật khẩu thất bại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"157": "5.3.3. Giới hạn việc sử dụng lại mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"158": "5.3.4. Cấu hình thuật toán hash mật khẩu sang SHA-512"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"159": "5.4. Cấu hình tài khoản người dùng và môi trường"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"160": "5.4.1. Cấu hình mật khẩu người dùng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"161": "5.4.1.1. Cấu hình thời gian hết hạn sử dụng mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"162": "5.4.1.2. Cấu hình thời gian tối thiểu giữa những lần thay đổi mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"163": "5.4.1.3. Cấu hình thời gian cảnh báo mật khẩu hết hạn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"164": "5.4.1.4. Cấu hình thời gian khoá tài khoản không thay mật khẩu sau khi hết hạn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"165": "5.4.1.5. Đảm bảo thời gian thay đổi mật khẩu lần cuối hợp lệ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"166": "5.4.2. Cấu hình vô hiệu hoá đăng nhập bằng tài khoản hệ thống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"167": "5.4.3. Cấu hình shell timeout mặc định"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"168": "5.4.4. Cấu hình group mặc định của tài khoản root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"169": "5.4.5. Cấu hình user umask mặc định"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"170": "5.4.6. Cấu hình hạn chế truy cập cho câu lệnh su"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"171": "6. System Maintenance"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"172": "6.1. Quyền của file hệ thống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"173": "6.1.1. Cấu hình sticky bit cho tất cả các thư mục dùng chung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"174": "6.1.2. Cấu hình phân quyền cho file /etc/passwd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"175": "6.1.3. Cấu hình phân quyền cho file /etc/shadow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"176": "6.1.4. Cấu hình phân quyền cho file /etc/group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"177": "6.1.5. Cấu hình phân quyền cho file /etc/gshadow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"178": "6.1.6. Cấu hình phân quyền cho file /etc/passwd-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"179": "6.1.7. Cấu hình phân quyền cho file /etc/shadow-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"180": "6.1.8. Cấu hình phân quyền cho file /etc/group-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"181": "6.1.9. Cấu hình phân quyền cho file /etc/gshadow-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"182": "6.1.10. Đảm bảo không có file world-writable tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"183": "6.1.11. Đảm bảo các file hoặc thư mục không có chủ sở hữu không tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"184": "6.1.12. Đảm bảo các file hoặc thư mục không có nhóm không tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"185": "6.2. Thiết lập cho người dùng và nhóm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"186": "6.2.1. Đảm bảo trường mật khẩu không để trống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"187": "6.2.2. Đảm bảo mọi nhóm trong file /etc/passwd tồn tại trong file /etc/group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"188": "6.2.3. Đảm bảo UID không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"189": "6.2.4. Đảm bảo GID không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"190": "6.2.5. Đảm bảo tên người dùng không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"191": "6.2.6. Đảm bảo tên group không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"192": "6.2.7. Đảm bảo tính toàn vẹn cho biến môi trường PATH của root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"193": "6.2.8. Đảm bảo root là tài khoản duy nhất có UID là 0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"194": "6.2.9. Đảm bảo mọi người dùng đều tồn tại thư mục home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"195": "6.2.10. Đảm bảo người dùng sở hữu thư mục home của chính họ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"196": "6.2.11. Đảm bảo quyền thư mục home của người dùng có mức bảo mật cao"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"197": "6.2.12. Đảm bảo các file dot của người dùng không cấp quyền write cho group hoặc other"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"198": "6.2.13. Đảm bảo không người dùng nào có file .forward"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"199": "6.2.14. Đảm bảo không người dùng nào có file .netrc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"200": "6.2.15. Đảm bảo không người dùng nào có file .rhosts"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,484 @@
|
|||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"7": "1.1.1.1. Cấu hình vô hiệu hoá cramfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"8": "1.1.1.2. Cấu hình vô hiệu hoá freevxfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"9": "1.1.1.3. Cấu hình vô hiệu hoá hfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"10": "1.1.1.4. Cấu hình vô hiệu hoá hfsplus filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"11": "1.1.1.5. Cấu hình vô hiệu hoá jffs2 filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"12": "1.1.1.6. Cấu hình vô hiệu hoá squashfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"13": "1.1.1.7. Cấu hình vô hiệu hoá udf filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"14": "1.1.1.8. Cấu hình vô hiệu hoá usb storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"16": "1.1.2.1. Cấu hình tuỳ chọn nodev cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"17": "1.1.2.2. Cấu hình tuỳ chọn nosuid cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"18": "1.1.2.3. Cấu hình tuỳ chọn noexec cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"20": "1.1.3.1. Cấu hình tuỳ chọn nodev cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"21": "1.1.3.2. Cấu hình tuỳ chọn nosuid cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"22": "1.1.3.3. Cấu hình tuỳ chọn noexec cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"24": "1.1.4.1. Cấu hình tuỳ chọn nodev cho phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"25": "1.1.4.2. Cấu hình tuỳ chọn nosuid cho phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"27": "1.1.5.1. Cấu hình tuỳ chọn nodev cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"28": "1.1.5.2. Cấu hình tuỳ chọn nosuid cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"29": "1.1.5.3. Cấu hình tuỳ chọn noexec cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"31": "1.2.1. Cấu hình kích hoạt gpgcheck"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"33": "1.3.1. Kiểm tra cài đặt AIDE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"34": "1.3.2. Cấu hình kiểm tra tính toàn vẹn của filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"36": "1.4.1. Phân quyền đối với file cấu hình bootloader"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"37": "1.4.2. Cấu hình mật khẩu cho bootloader"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"38": "1.4.3. Cấu hình xác thực khi truy cập single user mode"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"39": "1.4.4. Cấu hình vô hiệu hoá interactive boot"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"41": "1.5.1. Cấu hình vô hiệu hoá core dump"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"42": "1.5.2. Cấu hình kích hoạt ASLR (address space layout randomization)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"43": "1.5.3. Cấu hình vô hiệu hoá prelink"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"45": "1.6.1. Kiểm soát nội dung motd (Message Of The Day)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"46": "1.6.2. Kiểm soát nội dung thông báo khi đăng nhập"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"47": "1.6.3. Kiểm soát nội dung thông báo khi đăng nhập từ xa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"48": "1.6.4. Cấu hình phân quyền đối với file /etc/motd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"49": "1.6.5. Cấu hình phân quyền đối với file /etc/issue"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"50": "1.6.6. Cấu hình phân quyền đối với file /etc/issue.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"51": "1.6.7. Kiểm soát nội dung thông báo khi truy cập GNOME"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"54": "2.1.1. Cấu hình sử dụng chrony"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"55": "2.1.2. Cấu hình sử dụng ntp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"57": "2.2.1. Cấu hình vô hiệu hoá xinetd services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"58": "2.2.2. Cấu hình vô hiệu hoá chargen services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"59": "2.2.3. Cấu hình vô hiệu hoá daytime services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"60": "2.2.4. Cấu hình vô hiệu hoá discard services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"61": "2.2.5. Cấu hình vô hiệu hoá echo services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"62": "2.2.6. Cấu hình vô hiệu hoá time services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"63": "2.2.7. Cấu hình vô hiệu hoá rsh server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"64": "2.2.8. Cấu hình vô hiệu hoá talk server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"65": "2.2.9. Cấu hình vô hiệu hoá autofs services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"66": "2.2.10. Cấu hình vô hiệu hoá X window server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"67": "2.2.11. Cấu hình vô hiệu hoá avahi daemon services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"68": "2.2.12. Cấu hình vô hiệu hoá cups services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"69": "2.2.13. Cấu hình vô hiệu hoá dhcp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"70": "2.2.14. Cấu hình vô hiệu hoá ldap server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"71": "2.2.15. Cấu hình vô hiệu hoá dns server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"72": "2.2.16. Cấu hình vô hiệu hoá dnsmasq services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"73": "2.2.17. Cấu hình vô hiệu hoá ftp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"74": "2.2.18. Cấu hình vô hiệu hoá tftp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"75": "2.2.19. Cấu hình vô hiệu hoá web server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"76": "2.2.20. Cấu hình vô hiệu hoá imap and pop3 server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"77": "2.2.21. Cấu hình vô hiệu hoá samba file server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"78": "2.2.22. Cấu hình vô hiệu hoá web proxy server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"79": "2.2.23. Cấu hình vô hiệu hoá snmp services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"80": "2.2.24. Cấu hình vô hiệu hoá nis server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"81": "2.2.25. Cấu hình vô hiệu hoá telnet server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"82": "2.2.26. Cấu hình mail transfer agents sang chế độ local-only"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"83": "2.2.27. Cấu hình vô hiệu hoá network file system services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"84": "2.2.28. Cấu hình vô hiệu hoá rpcbind services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"85": "2.2.29. Cấu hình vô hiệu hoá rsync services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"87": "2.3.1. Cấu hình vô hiệu hoá nis client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"88": "2.3.2. Cấu hình vô hiệu hoá rsh client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"89": "2.3.3. Cấu hình vô hiệu hoá talk client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"90": "2.3.4. Cấu hình vô hiệu hoá telnet client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"91": "2.3.5. Cấu hình vô hiệu hoá ldap client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"92": "2.3.6. Cấu hình vô hiệu hoá ftp client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"93": "2.3.7. Cấu hình vô hiệu hoá tftp client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"96": "3.1.1. Cấu hình vô hiệu hoá IP forwarding"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"97": "3.1.2. Cấu hình vô hiệu hoá tính năng chuyển hướng gói tin (packet redirect)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"99": "3.2.1. Cấu hình từ chối các gói tin với nguồn được định tuyến trước"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"100": "3.2.2. Cấu hình từ chối các ICMP redirect message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"101": "3.2.3. Cấu hình từ chối các secure ICMP redirect message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"102": "3.2.4. Cấu hình từ chối các gói tin ICMP request broadcast"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"103": "3.2.5. Cấu hình bỏ qua phản hồi ICMP không hợp lệ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"104": "3.2.6. Cấu hình Reverse Path Filtering"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"105": "3.2.7. Cấu hình TCP SYN Cookies"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"108": "3.3.1.1. Cấu hình kích hoạt Iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"109": "3.3.1.2. Cấu hình iptables loopback traffic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"110": "3.3.1.3. Cấu hình iptables rule cho tất cả các port và protocol đang mở"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"111": "3.3.1.4. Cấu hình chính sách từ chối mặc định cho iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"115": "4.1.1.1. Cấu hình kích hoạt rsyslog service"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"116": "4.1.1.2. Phân quyền đối với file log sinh ra từ rsyslog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"117": "4.1.1.3. Cấu hình lưu trữ log sinh ra từ rsyslog tập trung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"118": "4.1.1.4. Phân quyền đối với tất cả các file log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"121": "5.1.1. Cấu hình kích hoạt cron daemon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"122": "5.1.2. Cấu hình phân quyền cho file /etc/crontab"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"123": "5.1.3. Cấu hình phân quyền cho file /etc/cron.hourly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"124": "5.1.4. Cấu hình phân quyền cho file /etc/cron.daily"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"125": "5.1.5. Cấu hình phân quyền cho file /etc/cron.weekly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"126": "5.1.6. Cấu hình phân quyền cho file /etc/cron.monthly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"127": "5.1.7. Cấu hình phân quyền cho file /etc/cron.d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"128": "5.1.8. Cấu hình at/cron hạn chế chỉ cho người dùng được uỷ quyền"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"130": "5.2.1. Cấu hình phân quyền cho file /etc/ssh/sshd_config"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"131": "5.2.2. Cấu hình phân quyền cho các file SSH private host key"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"132": "5.2.3. Cấu hình phân quyền cho các file SSH public host key"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"133": "5.2.4. Cấu hình giới hạn truy cập cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"134": "5.2.5. Cấu hình LogLevel cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"135": "5.2.6. Cấu hình sử dụng SSH PAM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"136": "5.2.7. Cấu hình vô hiệu hoá đăng nhập bằng root cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"137": "5.2.8. Cấu hình vô hiệu hoá HostbasedAuthentication cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"138": "5.2.9. Cấu hình vô hiệu hoá PermitEmptyPasswords cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"139": "5.2.10. Cấu hình vô hiệu hoá PermitUserEnviroment cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"140": "5.2.11. Cấu hình vô hiệu hoá IgnoreRhosts cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"141": "5.2.12. Cấu hình vô hiệu hoá X11 Forwarding cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"142": "5.2.13. Cấu hình vô hiệu hoá SSH AllowTcpForwarding"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"143": "5.2.14. Cấu hình cảnh báo SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"144": "5.2.15. Cấu hình SSH MaxAuthTries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"145": "5.2.16. Cấu hình SSH MaxStartups"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"146": "5.2.17. Cấu hình SSH MaxSessions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"147": "5.2.18. Cấu hình SSH LoginGraceTime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"148": "5.2.19. Cấu hình khoảng thời gian chờ không hoạt động cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"149": "5.2.20. Cấu hình các thuật toán MAC được cho phép"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"151": "5.3.1. Cấu hình điều kiện tạo mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"152": "5.3.2. Cấu hình khoá truy cập do nhiều lần nhập mật khẩu thất bại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"153": "5.3.3. Giới hạn việc sử dụng lại mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"154": "5.3.4. Cấu hình thuật toán hash mật khẩu sang SHA-512"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"157": "5.4.1.1. Cấu hình thời gian hết hạn sử dụng mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"158": "5.4.1.2. Cấu hình thời gian tối thiểu giữa những lần thay đổi mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"159": "5.4.1.3. Cấu hình thời gian cảnh báo mật khẩu hết hạn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"160": "5.4.1.4. Cấu hình thời gian khoá tài khoản không thay mật khẩu sau khi hết hạn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"161": "5.4.1.5. Đảm bảo thời gian thay đổi mật khẩu lần cuối hợp lệ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"162": "5.4.2. Cấu hình vô hiệu hoá đăng nhập bằng tài khoản hệ thống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"163": "5.4.3. Cấu hình shell timeout mặc định"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"164": "5.4.4. Cấu hình group mặc định của tài khoản root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"165": "5.4.5. Cấu hình user umask mặc định"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"166": "5.4.6. Cấu hình hạn chế truy cập cho câu lệnh su"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"169": "6.1.1. Cấu hình sticky bit cho tất cả các thư mục dùng chung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"170": "6.1.2. Cấu hình phân quyền cho file /etc/passwd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"171": "6.1.3. Cấu hình phân quyền cho file /etc/shadow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"172": "6.1.4. Cấu hình phân quyền cho file /etc/group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"173": "6.1.5. Cấu hình phân quyền cho file /etc/gshadow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"174": "6.1.6. Cấu hình phân quyền cho file /etc/passwd-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"175": "6.1.7. Cấu hình phân quyền cho file /etc/shadow-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"176": "6.1.8. Cấu hình phân quyền cho file /etc/group-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"177": "6.1.9. Cấu hình phân quyền cho file /etc/gshadow-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"178": "6.1.10. Đảm bảo không có file world-writable tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"179": "6.1.11. Đảm bảo các file hoặc thư mục không có chủ sở hữu không tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"180": "6.1.12. Đảm bảo các file hoặc thư mục không có nhóm không tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"182": "6.2.1. Đảm bảo trường mật khẩu không để trống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"183": "6.2.2. Đảm bảo mọi nhóm trong file /etc/passwd tồn tại trong file /etc/group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"184": "6.2.3. Đảm bảo UID không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"185": "6.2.4. Đảm bảo GID không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"186": "6.2.5. Đảm bảo tên người dùng không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"187": "6.2.6. Đảm bảo tên group không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"188": "6.2.7. Đảm bảo tính toàn vẹn cho biến môi trường PATH của root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"189": "6.2.8. Đảm bảo root là tài khoản duy nhất có UID là 0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"190": "6.2.9. Đảm bảo mọi người dùng đều tồn tại thư mục home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"191": "6.2.10. Đảm bảo người dùng sở hữu thư mục home của chính họ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"192": "6.2.11. Đảm bảo quyền thư mục home của người dùng có mức bảo mật cao"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"193": "6.2.12. Đảm bảo các file dot của người dùng không cấp quyền write cho group hoặc other"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"194": "6.2.13. Đảm bảo không người dùng nào có file .forward"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"195": "6.2.14. Đảm bảo không người dùng nào có file .netrc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"196": "6.2.15. Đảm bảo không người dùng nào có file .rhosts"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,595 @@
|
|||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"": "1. Thiết lập ban đầu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"": "1.1. Cấu hình filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"": "1.1.1. Cấu hình vô hiệu hoá các filesystem không sử dụng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"7": "1.1.1.1. Cấu hình vô hiệu hoá cramfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"8": "1.1.1.2. Cấu hình vô hiệu hoá freevxfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"9": "1.1.1.3. Cấu hình vô hiệu hoá hfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"10": "1.1.1.4. Cấu hình vô hiệu hoá hfsplus filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"11": "1.1.1.5. Cấu hình vô hiệu hoá jffs2 filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"12": "1.1.1.6. Cấu hình vô hiệu hoá squashfs filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"13": "1.1.1.7. Cấu hình vô hiệu hoá udf filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"14": "1.1.1.8. Cấu hình vô hiệu hoá usb-storage filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"15": "1.1.2. Cấu hình phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"16": "1.1.2.1. Cấu hình tuỳ chọn nodev cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"17": "1.1.2.2. Cấu hình tuỳ chọn nosuid cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"18": "1.1.2.3. Cấu hình tuỳ chọn noexec cho phân vùng /tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"19": "1.1.3. Cấu hình phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"20": "1.1.3.1. Cấu hình tuỳ chọn nodev cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"21": "1.1.3.2. Cấu hình tuỳ chọn nosuid cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"22": "1.1.3.3. Cấu hình tuỳ chọn noexec cho phân vùng /var/tmp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"23": "1.1.4. Cấu hình phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"24": "1.1.4.1. Cấu hình tuỳ chọn nodev cho phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"25": "1.1.4.2. Cấu hình tuỳ chọn nosuid cho phân vùng /home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"26": "1.1.5. Cấu hình phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"27": "1.1.5.1. Cấu hình tuỳ chọn nodev cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"28": "1.1.5.2. Cấu hình tuỳ chọn nosuid cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"29": "1.1.5.3. Cấu hình tuỳ chọn noexec cho phân vùng /dev/shm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"30": "1.2. Cấu hình cập nhật phần mềm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"31": "1.2.1. Cấu hình kích hoạt gpgcheck"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"32": "1.3. Kiểm tra tính toàn vẹn của filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"33": "1.3.1. Kiểm tra cài đặt AIDE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"34": "1.3.2. Cấu hình kiểm tra tính toàn vẹn của filesystem"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"35": "1.4. Cấu hình khởi động an toàn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"36": "1.4.1. Phân quyền đối với file cấu hình bootloader"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"37": "1.4.2. Cấu hình xác thực khi truy cập rescue mode"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"38": "1.4.3. Cấu hình xác thực khi truy cập single user mode"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"39": "1.4.4. Cấu hình vô hiệu hoá interactive boot"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"40": "1.5. Additional Process Hardening"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"41": "1.5.1. Cấu hình vô hiệu hoá core dump"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"42": "1.5.2. Cấu hình kích hoạt ASLR (address space layout randomization)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"43": "1.5.3. Cấu hình vô hiệu hoá prelink"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"44": "1.6. Kiểm soát nội dung cảnh báo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"45": "1.6.1. Kiểm soát nội dung motd (Message Of The Day)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"46": "1.6.2. Kiểm soát nội dung thông báo khi đăng nhập"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"47": "1.6.3. Kiểm soát nội dung thông báo khi đăng nhập từ xa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"48": "1.6.4. Cấu hình phân quyền đối với file /etc/motd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"49": "1.6.5. Cấu hình phân quyền đối với file /etc/issue"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"50": "1.6.6. Cấu hình phân quyền đối với file /etc/issue.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"51": "1.6.7. Kiểm soát nội dung thông báo khi truy cập GNOME"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"52": "2. Service"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"53": "2.1. Cấu hình Time Synchronization"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"54": "2.1.1. Cấu hình sử dụng chrony"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"55": "2.1.2. Cấu hình sử dụng NTP"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"56": "2.2. Các Service với mục đích riêng biệt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"57": "2.2.1. Cấu hình vô hiệu hoá xinetd services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"58": "2.2.2. Cấu hình vô hiệu hoá chargen services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"59": "2.2.3. Cấu hình vô hiệu hoá daytime services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"60": "2.2.4. Cấu hình vô hiệu hoá discard services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"61": "2.2.5. Cấu hình vô hiệu hoá echo services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"62": "2.2.6. Cấu hình vô hiệu hoá time services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"63": "2.2.7. Cấu hình vô hiệu hoá rsh server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"64": "2.2.8. Cấu hình vô hiệu hoá talk server"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"65": "2.2.9. Cấu hình vô hiệu hoá autofs services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"66": "2.2.10. Cấu hình vô hiệu hoá X window server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"67": "2.2.11. Cấu hình vô hiệu hoá avahi daemon services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"68": "2.2.12. Cấu hình vô hiệu hoá cups services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"69": "2.2.13. Cấu hình vô hiệu hoá dhcp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"70": "2.2.14. Cấu hình vô hiệu hoá ldap server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"71": "2.2.15. Cấu hình vô hiệu hoá dns server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"72": "2.2.16. Cấu hình vô hiệu hoá dnsmasq services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"73": "2.2.17. Cấu hình vô hiệu hoá ftp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"74": "2.2.18. Cấu hình vô hiệu hoá tftp server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"75": "2.2.19. Cấu hình vô hiệu hoá web server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"76": "2.2.20. Cấu hình vô hiệu hoá imap and pop3 server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"77": "2.2.21. Cấu hình vô hiệu hoá samba file server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"78": "2.2.22. Cấu hình vô hiệu hoá web proxy server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"79": "2.2.23. Cấu hình vô hiệu hoá snmp services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"80": "2.2.24. Cấu hình vô hiệu hoá nis server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"81": "2.2.25. Cấu hình vô hiệu hoá telnet server services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"82": "2.2.26. Cấu hình mail transfer agents sang chế độ local-only"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"83": "2.2.27. Cấu hình vô hiệu hoá network file system services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"84": "2.2.28. Cấu hình vô hiệu hoá rpcbind services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"85": "2.2.29. Cấu hình vô hiệu hoá rsync services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"86": "2.3. Service Clients"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"87": "2.3.1. Cấu hình vô hiệu hoá NIS Client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"88": "2.3.2. Cấu hình vô hiệu hoá rsh client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"89": "2.3.3. Cấu hình vô hiệu hoá talk client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"90": "2.3.4. Cấu hình vô hiệu hoá telnet client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"91": "Cấu hình vô hiệu hoá LDAP client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"92": "2.3.6. Cấu hình vô hiệu hoá ftp client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"93": "2.3.7. Cấu hình vô hiệu hoá tftp client"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"94": "3. Cấu hình mạng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"95": "3.1. Tham số cấu hình mạng (Host Only)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"96": "3.1.1. Cấu hình vô hiệu hoá IP forwarding"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"97": "3.1.2. Cấu hình vô hiệu hoá tính năng chuyển hướng gói tin (packet redirect)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"98": "3.2. Tham số cấu hình mạng (Host và Router)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"99": "3.2.1. Cấu hình từ chối các gói tin với nguồn được định tuyến trước"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"100": "3.2.2. Cấu hình từ chối các ICMP redirect message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"101": "3.2.3. Cấu hình từ chối các secure ICMP redirect message"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"102": "3.2.4. Cấu hình từ chối các gói tin ICMP request broadcast"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"103": "3.2.5. Cấu hình bỏ qua phản hồi ICMP không hợp lệ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"104": "3.2.6. Cấu hình Reverse Path Filtering"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"105": "3.2.7. Cấu hình TCP SYN Cookies"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"106": "3.4. Cấu hình Firewall"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"107": "3.4.1. Cấu hình firewalld"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"108": "3.4.1.1. Cấu hình kích hoạt firewalld"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"109": "3.4.1.3. Cấu hình firewalld rule cho tất cả các port và protocol đang mở"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"110": "3.4.1.4. Cấu hình chính sách từ chối mặc định cho firewalld"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"111": "3.4.2. Iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"112": "3.4.2.1. Cấu hình kích hoạt Iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"113": "3.4.2.3. Cấu hình iptables loopback traffic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"114": "3.4.2.4. Cấu hình iptables rule cho tất cả các port và protocol đang mở"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"115": "3.4.2.5. Cấu hình chính sách từ chối mặc định cho iptables"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"116": "4. Logging và Auditing"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"117": "4.1. Cấu hình logging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"118": "4.1.1. Cấu hình rsyslog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"119": "4.1.1.1. Cấu hình kích hoạt rsyslog service"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"120": "4.1.1.2. Phân quyền đối với file log sinh ra từ rsyslog"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"121": "4.1.1.3. Cấu hình lưu trữ log sinh ra từ rsyslog tập trung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"122": "4.1.1.4. Phân quyền đối với tất cả các file log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"123": "5. Cấu hình truy cập, xác thực và ủy quyền"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"124": "5.1. Cấu hình cron"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"125": "5.1.1. Cấu hình kích hoạt cron daemon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"126": "5.1.2. Cấu hình phân quyền cho file /etc/crontab"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"127": "5.1.3. Cấu hình phân quyền cho file /etc/cron.hourly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"128": "5.1.4. Cấu hình phân quyền cho file /etc/cron.daily"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"129": "5.1.5. Cấu hình phân quyền cho file /etc/cron.weekly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"130": "5.1.6. Cấu hình phân quyền cho của file /etc/cron.monthly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"131": "5.1.7. Cấu hình phân quyền cho file /etc/cron.d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"132": "5.1.8. Cấu hình at/cron hạn chế chỉ cho người dùng được ủy quyền"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"133": "5.2. Cấu hình máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"134": "5.2.1. Cấu hình phân quyền cho file /etc/ssh/sshd_config"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"135": "5.2.2. Cấu hình phân quyền cho các file SSH private host key"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"136": "5.2.3. Cấu hình phân quyền cho các file SSH public host key"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"137": "5.2.4. Cấu hình giới hạn truy cập cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"138": "5.2.5. Cấu hình LogLevel cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"139": "5.2.6. Cấu hình sử dụng SSH PAM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"140": "5.2.7. Cấu hình vô hiệu hoá đăng nhập bằng root cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"141": "5.2.8. Cấu hình vô hiệu hoá HostbasedAuthentication cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"142": "5.2.9. Cấu hình vô hiệu hoá PermitEmptyPasswords cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"143": "5.2.10. Cấu hình vô hiệu hoá PermitUserEnviroment cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"144": "5.2.11. Cấu hình vô hiệu hoá IgnoreRhosts cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"145": "5.2.12. Cấu hình vô hiệu hoá X11 Forwarding cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"146": "5.2.13. Cấu hình vô hiệu hoá SSH AllowTcpForwarding"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"147": "5.2.14. Cấu hình cảnh báo SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"148": "5.2.15. Cấu hình SSH MaxAuthTries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"149": "5.2.16. Cấu hình SSH MaxStartups"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"150": "5.2.17. Cấu hình SSH MaxSessions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"151": "5.2.18. Cấu hình SSH LoginGraceTime"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"152": "5.2.19. Cấu hình khoảng thời gian chờ không hoạt động cho máy chủ SSH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"153": "5.2.20. Cấu hình các thuật toán MAC được cho phép"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"154": "5.3. Cấu hình PAM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"155": "5.3.1. Cấu hình điều kiện tạo mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"156": "5.3.2. Cấu hình khoá truy cập do nhiều lần nhập mật khẩu thất bại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"157": "5.3.3. Giới hạn việc sử dụng lại mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"158": "5.3.4. Cấu hình thuật toán hash mật khẩu sang SHA-512"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"159": "5.4. Cấu hình tài khoản người dùng và môi trường"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"160": "5.4.1. Cấu hình mật khẩu người dùng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"161": "5.4.1.1. Cấu hình thời gian hết hạn sử dụng mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"162": "5.4.1.2. Cấu hình thời gian tối thiểu giữa những lần thay đổi mật khẩu"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"163": "5.4.1.3. Cấu hình thời gian cảnh báo mật khẩu hết hạn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"164": "5.4.1.4. Cấu hình thời gian khoá tài khoản không thay mật khẩu sau khi hết hạn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"165": "5.4.1.5. Đảm bảo thời gian thay đổi mật khẩu lần cuối hợp lệ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"166": "5.4.2. Cấu hình vô hiệu hoá đăng nhập bằng tài khoản hệ thống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"167": "5.4.3. Cấu hình shell timeout mặc định"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"168": "5.4.4. Cấu hình group mặc định của tài khoản root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"169": "5.4.5. Cấu hình user umask mặc định"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"170": "5.4.6. Cấu hình hạn chế truy cập cho câu lệnh su"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"171": "6. System Maintenance"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"172": "6.1. Quyền của file hệ thống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"173": "6.1.1. Cấu hình sticky bit cho tất cả các thư mục dùng chung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"174": "6.1.2. Cấu hình phân quyền cho file /etc/passwd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"175": "6.1.3. Cấu hình phân quyền cho file /etc/shadow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"176": "6.1.4. Cấu hình phân quyền cho file /etc/group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"177": "6.1.5. Cấu hình phân quyền cho file /etc/gshadow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"178": "6.1.6. Cấu hình phân quyền cho file /etc/passwd-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"179": "6.1.7. Cấu hình phân quyền cho file /etc/shadow-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"180": "6.1.8. Cấu hình phân quyền cho file /etc/group-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"181": "6.1.9. Cấu hình phân quyền cho file /etc/gshadow-"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"182": "6.1.10. Đảm bảo không có file world-writable tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"183": "6.1.11. Đảm bảo các file hoặc thư mục không có chủ sở hữu không tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"184": "6.1.12. Đảm bảo các file hoặc thư mục không có nhóm không tồn tại"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"185": "6.2. Thiết lập cho người dùng và nhóm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"186": "6.2.1. Đảm bảo trường mật khẩu không để trống"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"187": "6.2.2. Đảm bảo mọi nhóm trong file /etc/passwd tồn tại trong file /etc/group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"188": "6.2.3. Đảm bảo UID không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"189": "6.2.4. Đảm bảo GID không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"190": "6.2.5. Đảm bảo tên người dùng không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"191": "6.2.6. Đảm bảo tên group không bị lặp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"192": "6.2.7. Đảm bảo tính toàn vẹn cho biến môi trường PATH của root"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"193": "6.2.8. Đảm bảo root là tài khoản duy nhất có UID là 0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"194": "6.2.9. Đảm bảo mọi người dùng đều tồn tại thư mục home"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"195": "6.2.10. Đảm bảo người dùng sở hữu thư mục home của chính họ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"196": "6.2.11. Đảm bảo quyền thư mục home của người dùng có mức bảo mật cao"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"197": "6.2.12. Đảm bảo các file dot của người dùng không cấp quyền write cho group hoặc other"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"198": "6.2.13. Đảm bảo không người dùng nào có file .forward"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"199": "6.2.14. Đảm bảo không người dùng nào có file .netrc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"200": "6.2.15. Đảm bảo không người dùng nào có file .rhosts"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user