Python 运维作业2——作业2(FTP+SMTP+日志)

更新地址:

  1. https://cmd.dayi.ink/jQKDT6i0QMSY8betlwVmzw
  2. https://type.dayiyi.top/index.php/archives/316/

要求

在当前路径下创建文件夹,并用自己的姓名命名,该文件夹下有若干中文命名的文件和文件夹。

编程实现如下功能:

(1)编写FTP程序,设置用户名、密码、上传下载速率限制,家目录为姓名命名的目录。用户具有更改目录、删除文件或目录、创建目录、上传下载文件的权限。FTP日志分别输出到屏幕和以学号命名的.Log文件中,输出至屏幕的日志等级为INFO,输出文件的日志等级为WARNING。该目录下,仅当有文件或文件被修改时,记录一条日志事件,要求包括事件发生时间、事件发生位置、事件的严重程度、事件内容,并将该事件输出至以学号命名的.Log文件中。

(2)编写程序实现邮件发送,将.log文件的内容作为邮件正文,.log文件为附件。

提交要求:粘贴两个程序的代码,上传运行过程的截图,截图形式如下所示。

基础的环境

1. 依赖

  1. WIN+X 选终端管理员,win10 下叫powershell(管理员)

安装包:

pip install -i https://mirrors.ustc.edu.cn/pypi/web/simple chardet colorama colorlog watchdog ansi2html pyftpdlib

如果有奇怪的报错,请不要用python 3.12

2. 需要一个SMTP服务器(可以自己去整一个)

这里具体可以去看章节:QQ邮箱SMTP地址

3. 配置

这个能跑,但是你想学到东东还是要自己写比较好。

小TIPS

  1. CTRL+H 直接把dayi替换掉。
  2. 发件人和收件人和登录名可能是一个,也可以不一样。(qq就可以全部一样)
  3. 授权码记得改。

再来个邮箱方便参考

  1. 授权码问题:

这里,授权码没改的缘故。

  1. 如果显示被占用什么的,去FTPserver文件换个端口。(在127.0.0.1附近,34行)之后登录记得改端口

QQ邮箱SMTP地址:

其他的邮箱服务商同理

1. 打开QQ邮箱:

https://mail.qq.com/

2. 打开设置,然后到这里的SMTP,点击开启服务

老板QQ:

3. 然后是密保信息

4. 复制这个就可以啦

密保信息填完之后,复制授权码

截图的要求:

登录FTP后:

1. filezilla 下载地址:https://pc.qq.com/detail/6/detail_22246.html

点击立即下载后,普通下载,证书忽略过期(速度快)

2. 使用filezilla登录FTP服务器:

用户名,密码在主程序的传参里

3. 登录

4. 尝试传送点文件ovo,增删改查。

日志正常输出(等级为WARNING)

5. 过了一会自动断开连接

进行文件的上传、下载和创建操作后:

感觉跟差不多,多弄点图

FTP程序显示的运行结果:

以学号命名的.Log文件内容:

邮件发送:

邮件图:

【文件!】文件下载:

0.依赖

pip install -i https://mirrors.ustc.edu.cn/pypi/web/simple chardet colorama colorlog watchdog ansi2html pyftpdlib

2. 仅修改main.py的配置即可。(修改授权码,学号,登录名等)

2. 下载后,运行main.py,会自动启动watch进程和FTP进程

3. ctrl+c 会尝试发送邮件

运行样子:

有个小bug,可能会重复发送两次邮件(但是完全不影响结果,晚点再改)

这下面是一些调试过程的图

邮件发送!

当当:

FTP服务器

发送邮件:

运行截图:

  1. 启动截图

  1. 登录:

  1. 增删改查

  1. 发送邮件

  1. 邮件:

内容:

代码:

ftp_server.py

import logging.handlers
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
import os

# 好看的日志输出,初始化日志模块
import prog_1

def main(username="dayi",password="abc123...",home_dir="./dayi",log_file="202123030408.log"):
  # 初始化日志模块
  os.makedirs(home_dir, exist_ok=True) # 创建日志目录
  logger = prog_1.init_logger(output_filename=log_file)
  logger.info("Ftp 服务器启动~~~~~~~~~~~~~~~~")

  handler = FTPHandler
  authorizer = DummyAuthorizer()
  authorizer.add_user(username, password, home_dir, perm="elradfmwM")  # 全部权限
  handler.authorizer = authorizer
  handler.banner = "Welcome to my FTP server! ovo! hello~~~~~~~~~my friend!"
  handler.masquerade_address = "127.0.0.1"  # 伪装地址
  handler.max_login_attempts = 3  # 最大登录尝试次数
  handler.permit_foreign_addresses = True  # 允许远程访问
  
  # 下载上传速度设置
  dtp_handler = handler.dtp_handler
  dtp_handler.read_limit = 1024 * 1024  
  dtp_handler.write_limit = 1024 * 1024  
  handler.dtp_handler = dtp_handler

  handler.tcp_no_delay = True  # 禁用Nagle算法
  handler.passive_ports = range(60000, 65535)  # 被动模式端口范围
  # 启动FTP服务器
  server = FTPServer(("127.0.0.1", 21), handler)
  logging.info("FTP server started on 127.0.0.1:21")
  server.serve_forever()


if __name__ == "__main__":
  main()

2. prog_1.py

from watchdog.observers import Observer  # 导入watchdog库中的Observer类,用于监控文件系统事件
from watchdog.events import FileSystemEventHandler  # 导入FileSystemEventHandler类,用于处理文件系统事件
import time
import logging
import colorlog
import os
from colorlog.escape_codes import escape_codes
import chardet

logger = logging.getLogger()  # 获取logger实例

def log_init(output_filename="114514.log"):
    global logger
    logger.setLevel(logging.INFO)  # 设置日志级别
    handler = colorlog.StreamHandler()  # 创建一个StreamHandler用于输出到控制台
    # -[func:%(funcName)s]
    handler.setFormatter(colorlog.ColoredFormatter(
        '[%(log_color)s%(asctime)s.%(msecs)03d%(reset)s][%(log_color)s%(levelname)s%(reset)s]: %(log_color)s%(message)s',  # 设置日志输出格式
        datefmt='%Y-%m-%d %H:%M:%S',  # 设置时间格式
        reset=True,  # 重置终端属性
        log_colors={  # 设置日志级别和日期时间的颜色
            'DEBUG': 'cyan',
            'INFO': 'green',
            'WARNING': 'yellow',
            'ERROR': 'red',
            'CRITICAL': 'red,bg_white',
            'asctime': 'blue',  # 设置日期时间的颜色
        },
    ))
    logger.addHandler(handler)

    # 输出到文件
    log_dir = '.'
    # os.makedirs(log_dir, exist_ok=True) # 创建日志目录
    file_handler = logging.FileHandler(os.path.join(log_dir, output_filename), 'a', encoding='utf-8')
    file_formatter = logging.Formatter('[%(asctime)s.%(msecs)03d][%(levelname)s]: %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
    file_handler.setFormatter(file_formatter)
    file_handler.setLevel(logging.WARNING) # 只输出WARNING及以上级别的日志到文件
    logger.addHandler(file_handler)


def colorize_string(s, color):
    return f"{escape_codes[color]}{s}{escape_codes['reset']}"




# 用chardet来检测编码
def detect_encoding(file_path):
  # 先尝试GBK读取
  try:
    with open(file_path, 'r', encoding='gbk') as f:
       f.read()
    return 'gbk'
  except:
    with open(file_path, 'rb') as f:
      result = chardet.detect(f.read())
    return result['encoding']


class MyHandler(FileSystemEventHandler):
    def __init__(self):
      self.emit_once = True # 减少事件触发次数
    def on_any_event(self, event):
        # 如果包含__pycache__,则忽略
        if "__pycache__" in event.src_path:
          return

        # 事件类型描述
        event_description = {
          'modified': colorize_string("[修改]", 'red'),
          'created': colorize_string("[创建]", 'green'),
          'deleted': colorize_string("[删除]", 'yellow'),
          'moved': colorize_string("[移动]", 'blue')
        }
        # 目录或文件描述
        file_dir_desc = {
         True: colorize_string("【目录】", 'blue'), 
         False: colorize_string("【文件】", 'yellow')  
        }

        # 是否是目录
        dir_or_file = file_dir_desc[event.is_directory]
        logger_str = f"{event_description.get(event.event_type, '未知事件'+str(event.event_type))}{dir_or_file}: 路径:{event.src_path}"
        # 记录事件信息
        logger.warning(logger_str)

        # 对于文件的修改和创建事件,进一步处理
        if event.event_type in ['modified', 'created'] and not event.is_directory:
          file_name_without_path = event.src_path.split("\\")[-1]  # 从路径中提取文件名
          action = "被修改" if event.event_type == 'modified' else "被创建"
          # logger.info(f'文件: {file_name_without_path} {action}')
          
          # 修改事件
          if event.event_type == 'modified':
            return
            # bin
            try:
               with open(event.src_path, 'rb') as file:
                 logger.info('读取文件内容成功(二进制)')
                 file_contents = file.read()
                 print(f"=============={file_name_without_path}文件内容如下(二进制)==============")
                 print(f"{file_name_without_path}文件内容(二进制):\n{file_contents}")  # 打印文件内容
                 print(f"=============={file_name_without_path}文件内容如上(二进制)==============")
            except Exception as e:
               logger.error(f'读取文件时发生错误(二进制读取失败?!): {e}') 
            # UTF-8 + GBK + ANY
            try:
              encoding=detect_encoding(event.src_path)
              logger.info("检测到编码为:"+encoding)
              with open(event.src_path, 'r', encoding=encoding) as file:
                file_contents = file.read()
                print(f"=============={file_name_without_path}文件修改后内容如下({encoding})==============")
                print(f"文件内容({encoding}):\n{file_contents}")  # 打印文件内容
                print(f"=============={file_name_without_path}文件修改后内容如上({encoding})==============")
              #下面的代码应该不会触发,历史遗留 
            except Exception as e:
                try:
                  logger.info('尝试UTF-8编码读取...')
                  with open(event.src_path, 'r',encoding="utf-8") as file:
                    logger.info('读取文件内容成功(UTF-8)')
                    file_contents = file.read()
                    print("==============新文件内容如下==============")
                    print(f"文件内容(UTF-8):\n{file_contents}")  # 打印文件内容
                    print("==============新文件内容如上==============")
                except Exception as e:
                  logger.error(f'读取文件时发生错误: {e}')  # 使用日志记录异常信息
                  logger.info('尝试GBK编码读取...')
                  try:
                      with open(event.src_path, 'r',encoding="gbk") as file:
                        logger.info('读取文件内容成功(GBK)')
                        file_contents = file.read()
                        print("==============新文件内容如下==============")
                        print(f"文件内容:\n{file_contents}")  # 打印文件内容
                        print("==============新文件内容如上==============")
                  except Exception as e:
                        logger.error(f'读取文件再次发生错误,未知报错: {e}')
        
        # # 邮件发送!
        # logger.info('正在尝试发送邮件')
        # time.sleep(1)  # 减少CPU占用
        # try:
        #   import sendmail3
        #   sendmail3.main()
        # except Exception as e:
        #   logger.error(f'发送邮件时发生错误: {e}')

def monitor_folder(path='.'):
  event_handler = MyHandler()  # 创建MyHandler实例
  observer = Observer()  # 创建Observer实例
  observer.schedule(event_handler, path, recursive=False)  # 配置Observer监控的目录和事件处理器
  observer.start()  # 启动Observer
  try:  # 等待捕获键盘中断异常
    while True:  
      time.sleep(1)  # 减少CPU占用
  except KeyboardInterrupt:  # 捕获键盘中断(Ctrl+C)
    observer.stop()  # 停止Observer
    # print("[-]捕获到键盘中断,停止监控") 
    logger.warning("[-]捕获到键盘中断,停止监控")
  observer.join()  # 等待Observer线程结束



# 初始化日志模块
  
# 监控
def watch_file(path=".",output_filename="202123030408.log"):
  log_init(output_filename=output_filename) #初始化日志
  logger.info("看门狗!启动~~~~~~~~~~~~~~~~")
  # 这里实际上是两个模块,子进程
  now_path = path  # 获取当前脚本的路径
  logger.info("[+]当前目录为:"+now_path)
  logger.info("[+]开始监控文件夹:"+ now_path)
  monitor_folder(now_path)  # 调用monitor_folder函数开始监控当前脚本所在的目录

def init_logger(output_filename="202123030408.log"):
  log_init(output_filename=output_filename) #初始化日志
  logger.info("logger被初始化咧,当前输出文件为:"+output_filename)
  return logger


def getlogger():
   global logger
   return logger


# 


if __name__=="__main__":  # 如果此脚本作为主程序运行
  log_init() #初始化日志
  logger.info("logger测试")
  now_path = os.getcwd()  # 获取当前脚本的路径
  logger.info("[+]当前目录为:"+now_path)
  logger.info("[+]开始监控文件夹:"+ now_path)
  monitor_folder(now_path)  # 调用monitor_folder函数开始监控当前脚本所在的目录
  
  

send_mail_ovo.py

彩色日志!

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
import os

from ansi2html import Ansi2HTMLConverter
import re

import prog_1




def send_email(sender, receivers, subject, body, attachment_path, log_path, image_path, smtp_server, smtp_server_port, mail_user, mail_pass,logger):

    logger.info("发送邮件配置如下:")
    logger.info("SMTP服务器:" + smtp_server)
    logger.info("SMTP服务器端口:" + str(smtp_server_port))
    logger.info("发件人:" + sender)
    logger.info("收件人:" + str(receivers))
    logger.info("主题:" + subject)
    logger.info("附件路径:" + attachment_path)
    logger.info("日志路径:" + log_path)


    msg = MIMEMultipart('related')  # 创建邮件对象,'related' 用于发送内嵌资源的邮件
    msg['From'] = sender
    msg['To'] = ";".join(receivers)
    msg['Subject'] = subject

    # HTML 邮件正文
    with open(log_path, "r", encoding="utf-8") as f:
        content = f.read()

    

    # 彩色
    conv = Ansi2HTMLConverter()
    with open(log_path, 'r',encoding='utf-8') as f:
      content = f.read()

    content = re.sub(r'\n', '\n', content)
    html_content = conv.convert(content, full=False)


    # 彩色!
    def colorize(ansi_code, text):
      color_map = {
          '31': 'red',
          '32': 'green',
          '33': 'pink',
          '34': 'blue'
      }
      color = color_map.get(ansi_code, 'black')
      return f'<span style="color:{color}">{text}</span>'
    
    def colorize_info(color, text):
        return f'<span style="color:{color}">{text}</span>'
    level_pattern = r'\[(WARNING|ERROR)\]'
    html_content = re.sub(level_pattern, lambda m: colorize_info('orange' if m.group(1) == 'WARNING' else 'red', m.group(0)), html_content)
    timestamp_pattern = r'\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+\]'
    html_content = re.sub(timestamp_pattern, lambda m: colorize_info('green', m.group(0)), html_content)

    pattern = r'<span class="ansi(\d+)">([^<]+)</span>'
    html_content = re.sub(pattern, lambda m: colorize(m.group(1), m.group(2)), html_content)
    
    with open("test.html",'w',encoding='utf-8') as f:
      f.write(f'<html><body>{html_content}</body></html>')


    html_content = re.sub(r'\n', '<br>', html_content)

    html_body = MIMEText(f'<html><body>{html_content}</body></html>', 'html', 'utf-8')
    # html_body = MIMEText(f'ovo', 'html', 'utf-8')
    msg.attach(html_body)

    # 添加图片
    # with open(image_path, 'rb') as f:
    #     mime_image = MIMEImage(f.read(), 'webp')
    # mime_image.add_header('Content-ID', '<image1>')
    # msg.attach(mime_image)

    # 添加附件
    with open(attachment_path, 'rb') as f:
        att1 = MIMEText(f.read(), 'base64', 'utf-8')
    att1["Content-Type"] = 'application/octet-stream'
    att1["Content-Disposition"] = f'attachment; filename="{os.path.basename(attachment_path)}"'
    msg.attach(att1)

    # 添加日志文件作为附件
    with open(log_path, 'rb') as f:
        att2 = MIMEText(f.read(), 'base64', 'utf-8')
    att2["Content-Type"] = 'application/octet-stream'
    att2["Content-Disposition"] = f'attachment; filename="{os.path.basename(log_path)}"'
    msg.attach(att2)

    # 发送邮件
    try:
        smtpObj = smtplib.SMTP(smtp_server, smtp_server_port)
        smtpObj.ehlo()
        smtpObj.starttls()  # 如果SMTP服务器要求安全连接,则需要
        smtpObj.login(mail_user, mail_pass)
        smtpObj.sendmail(sender, receivers, msg.as_string())
        logger.info("邮件发送成功")
    except smtplib.SMTPException as e:
        logger.error(f"发送邮件出线错误!, 错误信息: {e}")
    finally:
        smtpObj.quit()


def main(logpath='202123030408.log',smtp_server='smtp.qq.com',smtp_server_port=587,mail_user='',mail_pass='',sender='',recipients='[]',subject='日志!'):
    
    logger=prog_1.init_logger(output_filename=logpath) #初始化日志
    logger.info("我是发邮件滴!!正在尝试发送邮件~~~~~~~~~~~~~~~")
    # sender = '851845341@qq.com' #发件人
    # receivers = ['lamu10@163.com'] #收件人
    receivers = recipients
    # subject = '日志!'
    body = '你好呀呀呀' # 其实没什么用
    attachment_path = 'test.html' #这个是html的渲染格式
    log_path = logpath #日志路径

    image_path = '====.pic' #已经废弃!
    # smtp_server = 'smtp.qq.com'
    # smtp_server_port = 587
    # mail_user = "851845341@qq.com"
    # mail_pass = "授权码"
    send_email(sender, receivers, subject, body, attachment_path, log_path, image_path, smtp_server, smtp_server_port, mail_user, mail_pass,logger)

if __name__ == '__main__':
    # 配置
    file_path = '202123030408.log' # 日志文件
    smtp_server = 'smtp.qq.com' # smtp 服务器
    smtp_server_port = 587 # smtp 服务器端口
    mail_user = '81@qq.com' # SMTP登录名
    sender = '8@qq.com' # 发件人
    mail_pass = 'lof' #SMTP密码
    recipients = ['l0@163.com'] #收件人
    subject = '日志!'
    # 发送邮件
    main(logpath=file_path, smtp_server=smtp_server,smtp_server_port=smtp_server_port,mail_user=mail_user,sender=sender,mail_pass=mail_pass,recipients=recipients,subject=subject)

main.py

import time
import logging
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import os
from multiprocessing import Process

from prog_1 import watch_file
import prog_1
import ftp_server
from send_mail_ovo import main as send_mail_

if __name__ == '__main__':
  
  # 日志文件名
  logger_file_name = '202123030408.log'


  # 初始化日志模块
  logger=prog_1.init_logger(output_filename=logger_file_name)
  logger.info("主进程启动~~~~~~~~~~~~~~~~")


  # 看门狗!日志文件:logger_file_name
  p = Process(target=watch_file, args=("./dayi",logger_file_name))
  p.start()

  # FTP服务器, 用户名dayi,密码abc123...,主目录./dayi,日志文件:logger_file_name
  p2 = Process(target=ftp_server.main, args=("dayi","abc123...","./dayi","202123030408.log"))
  p2.start()


  # 主进程保持运行,直到手动中断
  
  # 发送邮件!(CTRL+C) 会自动发送哦,请记得修改配置文件
  file_path = '202123030408.log' # 日志文件
  smtp_server = 'smtp.exmail.qq.com' # smtp 服务器
  smtp_server_port = 587 # smtp 服务器端口
  mail_user = '202123030408@sdust.edu.cn' # SMTP登录名
  sender = 'dayi@sdust.edu.cn' # 发件人
  mail_pass = 'DvWsv' #SMTP密码(授权码)
  recipients = ['lamu10@163.com'] #收件人
  subject = '日志!'


  try:
      while True:
        time.sleep(0.5)
  except KeyboardInterrupt:
      print("[-]捕获到键盘中断,停止主进程")
      
      p2.kill()
      p.kill()

  logger.info('正在尝试发送邮件')
  time.sleep(1)  # 减少CPU占用
  
  mail_sent = False
  if not mail_sent:
    try:
        send_mail_(logpath=file_path, smtp_server=smtp_server,smtp_server_port=smtp_server_port,mail_user=mail_user,sender=sender,mail_pass=mail_pass,recipients=recipients,subject=subject)
        mail_sent = True
    except Exception as e:
        logger.error(f'发送邮件时发生错误: {e}')

  print("主进程已中断")

文件下载:

  1. 香港:https://pic.icee.top/blog/pic_bed/sharex/_pn-2024-03-20-18-23-02_Mollies_Knowledgeable_Submissive.rar
  2. CF:https://p.dabbit.net/blog/pic_bed/sharex/_pn-2024-03-20-18-23-02_Mollies_Knowledgeable_Submissive.rar

Python自动化运维 作业1

记得自己改名字哦

更新:

  1. https://type.dayiyi.top/index.php/archives/312/
  2. https://cmd.dayi.ink/tdhzBUosT1SeW7YEkalrIw

编程实现:

(1)程序1:监控该目录下的文件及目录,显示文件及目录变化信息;当有文件被修改时,显示修改后的文件内容。

加了点日志模块,但是好看了,大概这个样子

(2)程序2:该目录下创建配置文件student.ini,如图所示。

运行之后输出

执行prog_2.py:
文件student.ini不存在,将创建默认配置文件

(3)程序3:读取配置文件中[DEFAULT]中course的值,将其分别用gbk和utf-8进行编码,并将编码分别存入以姓名+编码(如“zhangsan-GBK”)命名的.txt文件中。

执行prog_3.py:
网络运维技术

(4)程序4:修改配置文件,将[student]中的数据项设置为学生本人的信息。

执行prog_4.py:
文件内容如下:
[DEFAULTI]
course = 网络运维技术
university = 山东科技大学
grade = 3
[student]
name = ?
class = ?
studentid = ?
sex = ?
==========修改后的值:===================
姓名: dayi
班级: 网络21
学号: 2021233333333
性别: Male

运行程序1开始进行监控,然后分别运行程序2,3,4,观察程序1的运行结果。

全部日志:(10秒执行一个程序,可以根据时间来区分)

(.venv) PS E:\dayi_\@docs_大三下\1.自动化运维\作业\作业1\dayi> py .\prog_1.py
[2024-03-07 11:14:32][INFO]: logger测试
[2024-03-07 11:14:32][INFO]: [+]当前目录为:E:\dayi_\@docs_大三下\1.自动化运维\作业\作业1\dayi
[2024-03-07 11:14:32][INFO]: [+]开始监控文件夹:E:\dayi_\@docs_大三下\1.自动化运维\作业\作业1\dayi
[2024-03-07 11:14:33][INFO]: [删除]【文件】: 路径:E:\dayi_\@docs_大三下\1.自动化运维\作业\作业1\dayi\dayi-GBK.txt
[2024-03-07 11:14:33][INFO]: [删除]【文件】: 路径:E:\dayi_\@docs_大三下\1.自动化运维\作业\作业1\dayi\dayi-UTF-8.txt
[2024-03-07 11:14:33][INFO]: [删除]【文件】: 路径:E:\dayi_\@docs_大三下\1.自动化运维\作业\作业1\dayi\student.ini
[2024-03-07 11:14:43][INFO]: [创建]【文件】: 路径:E:\dayi_\@docs_大三下\1.自动化运维\作业\作业1\dayi\student.ini
[2024-03-07 11:14:43][INFO]: [修改]【文件】: 路径:E:\dayi_\@docs_大三下\1.自动化运维\作业\作业1\dayi\student.ini
[2024-03-07 11:14:43][INFO]: 读取文件内容成功(二进制)
==============student.ini文件内容如下(二进制)==============
student.ini文件内容(二进制):
b'[DEFAULTI]\r\ncourse = \xe7\xbd\x91\xe7\xbb\x9c\xe8\xbf\x90\xe7\xbb\xb4\xe6\x8a\x80\xe6\x9c\xaf\r\nuniversity = \xe5\xb1\xb1\xe4\xb8\x9c\xe7\xa7\x91\xe6\x8a\x80\xe5\xa4\xa7\xe5\xad\xa6\r\ngrade = 3\r\n\r\n[student]\r\nname = ?\r\nclass = ?\r\nstudentid = ?\r\nsex = ?\r\n\r\n'
==============student.ini文件内容如上(二进制)==============
[2024-03-07 11:14:43][INFO]: 检测到编码为:utf-8
==============student.ini文件修改后内容如下(utf-8)==============
文件内容(utf-8):
[DEFAULTI]
course = 网络运维技术
university = 山东科技大学
grade = 3

[student]
name = ?
class = ?
studentid = ?
sex = ?


==============student.ini文件修改后内容如上(utf-8)==============
[2024-03-07 11:14:53][INFO]: [创建]【文件】: 路径:E:\dayi_\@docs_大三下\1.自动化运维\作业\作业1\dayi\dayi-UTF-8.txt
[2024-03-07 11:14:53][INFO]: [修改]【文件】: 路径:E:\dayi_\@docs_大三下\1.自动化运维\作业\作业1\dayi\dayi-UTF-8.txt
[2024-03-07 11:14:53][INFO]: 读取文件内容成功(二进制)
==============dayi-UTF-8.txt文件内容如下(二进制)==============
dayi-UTF-8.txt文件内容(二进制):
b'\xe7\xbd\x91\xe7\xbb\x9c\xe8\xbf\x90\xe7\xbb\xb4\xe6\x8a\x80\xe6\x9c\xaf'
==============dayi-UTF-8.txt文件内容如上(二进制)==============
[2024-03-07 11:14:53][INFO]: 检测到编码为:utf-8
==============dayi-UTF-8.txt文件修改后内容如下(utf-8)==============
文件内容(utf-8):
网络运维技术
==============dayi-UTF-8.txt文件修改后内容如上(utf-8)==============
[2024-03-07 11:14:53][INFO]: [创建]【文件】: 路径:E:\dayi_\@docs_大三下\1.自动化运维\作业\作业1\dayi\dayi-GBK.txt
[2024-03-07 11:14:53][INFO]: [修改]【文件】: 路径:E:\dayi_\@docs_大三下\1.自动化运维\作业\作业1\dayi\dayi-GBK.txt
[2024-03-07 11:14:53][INFO]: 读取文件内容成功(二进制)
==============dayi-GBK.txt文件内容如下(二进制)==============
dayi-GBK.txt文件内容(二进制):
b'\xcd\xf8\xc2\xe7\xd4\xcb\xce\xac\xbc\xbc\xca\xf5'
==============dayi-GBK.txt文件内容如上(二进制)==============
[2024-03-07 11:14:53][INFO]: 检测到编码为:gbk
==============dayi-GBK.txt文件修改后内容如下(gbk)==============
文件内容(gbk):
网络运维技术
==============dayi-GBK.txt文件修改后内容如上(gbk)==============
[2024-03-07 11:15:03][INFO]: [修改]【文件】: 路径:E:\dayi_\@docs_大三下\1.自动化运维\作业\作业1\dayi\student.ini
[2024-03-07 11:15:03][INFO]: 读取文件内容成功(二进制)
==============student.ini文件内容如下(二进制)==============
student.ini文件内容(二进制):
b'[DEFAULTI]\r\ncourse = \xe7\xbd\x91\xe7\xbb\x9c\xe8\xbf\x90\xe7\xbb\xb4\xe6\x8a\x80\xe6\x9c\xaf\r\nuniversity = \xe5\xb1\xb1\xe4\xb8\x9c\xe7\xa7\x91\xe6\x8a\x80\xe5\xa4\xa7\xe5\xad\xa6\r\ngrade = 3\r\n\r\n[student]\r\nname = dayi\r\nclass = \xe7\xbd\x91\xe7\xbb\x9c21\r\nstudentid = 2021233333333\r\nsex = Male\r\n\r\n'
==============student.ini文件内容如上(二进制)==============
[2024-03-07 11:15:03][INFO]: 检测到编码为:utf-8
==============student.ini文件修改后内容如下(utf-8)==============
文件内容(utf-8):
[DEFAULTI]
course = 网络运维技术
university = 山东科技大学
grade = 3

[student]
name = dayi
class = 网络21
studentid = 2021233333333
sex = Male


==============student.ini文件修改后内容如上(utf-8)==============
[2024-03-07 11:15:03][INFO]: [修改]【文件】: 路径:E:\dayi_\@docs_大三下\1.自动化运维\作业\作业1\dayi\student.ini
[2024-03-07 11:15:03][INFO]: 读取文件内容成功(二进制)
==============student.ini文件内容如下(二进制)==============
student.ini文件内容(二进制):
b'[DEFAULTI]\r\ncourse = \xe7\xbd\x91\xe7\xbb\x9c\xe8\xbf\x90\xe7\xbb\xb4\xe6\x8a\x80\xe6\x9c\xaf\r\nuniversity = \xe5\xb1\xb1\xe4\xb8\x9c\xe7\xa7\x91\xe6\x8a\x80\xe5\xa4\xa7\xe5\xad\xa6\r\ngrade = 3\r\n\r\n[student]\r\nname = dayi\r\nclass = \xe7\xbd\x91\xe7\xbb\x9c21\r\nstudentid = 2021233333333\r\nsex = Male\r\n\r\n'
==============student.ini文件内容如上(二进制)==============
[2024-03-07 11:15:03][INFO]: 检测到编码为:utf-8
==============student.ini文件修改后内容如下(utf-8)==============
文件内容(utf-8):
[DEFAULTI]
course = 网络运维技术
university = 山东科技大学
grade = 3

[student]
name = dayi
class = 网络21
studentid = 2021233333333
sex = Male


==============student.ini文件修改后内容如上(utf-8)==============

代码

记得安装依赖

prog_1

from watchdog.observers import Observer  # 导入watchdog库中的Observer类,用于监控文件系统事件
from watchdog.events import FileSystemEventHandler  # 导入FileSystemEventHandler类,用于处理文件系统事件
import time
import logging
import colorlog
import os
from colorlog.escape_codes import escape_codes
import chardet

logger = logging.getLogger()  # 获取logger实例
def log_init():
    global logger
    logger.setLevel(logging.INFO)  # 设置日志级别
    handler = colorlog.StreamHandler()  # 创建一个StreamHandler用于输出到控制台
    # -[func:%(funcName)s]
    handler.setFormatter(colorlog.ColoredFormatter(
        '[%(log_color)s%(asctime)s%(reset)s][%(log_color)s%(levelname)s%(reset)s]: %(log_color)s%(message)s',  # 设置日志输出格式
        datefmt='%Y-%m-%d %H:%M:%S',  # 设置时间格式
        reset=True,  # 重置终端属性
        log_colors={  # 设置日志级别和日期时间的颜色
            'DEBUG': 'cyan',
            'INFO': 'green',
            'WARNING': 'yellow',
            'ERROR': 'red',
            'CRITICAL': 'red,bg_white',
            'asctime': 'blue',  # 设置日期时间的颜色
        },
    ))
    logger.addHandler(handler)


def colorize_string(s, color):
    return f"{escape_codes[color]}{s}{escape_codes['reset']}"




# 用chardet来检测编码
def detect_encoding(file_path):
  # 先尝试GBK读取
  try:
    with open(file_path, 'r', encoding='gbk') as f:
       f.read()
    return 'gbk'
  except:
    with open(file_path, 'rb') as f:
      result = chardet.detect(f.read())
    return result['encoding']

# # UTF-8 和GBK的前几位编码不一样
# def detect_encoding(file_path):
#   with open(file_path, 'rb') as f:
#     first_three_bytes = f.read(3)
#   if first_three_bytes == b'\xef\xbb\xbf':
#     try:
#        open(file_path,'r',encoding="utf-8").read()
#        return 'utf-8'
#     except:
#         return 'gbk'
#   return 'gbk'

class MyHandler(FileSystemEventHandler):
    def __init__(self):
      self.emit_once = True # 减少事件触发次数
    def on_any_event(self, event):
        # 事件类型描述
        event_description = {
          'modified': colorize_string("[修改]", 'red'),
          'created': colorize_string("[创建]", 'green'),
          'deleted': colorize_string("[删除]", 'yellow'),
          'moved': colorize_string("[移动]", 'blue')
        }
        # 目录或文件描述
        file_dir_desc = {
         True: colorize_string("【目录】", 'blue'), 
         False: colorize_string("【文件】", 'yellow')  
        }

        # 是否是目录
        dir_or_file = file_dir_desc[event.is_directory]
        logger_str = f"{event_description.get(event.event_type, '未知事件'+str(event.event_type))}{dir_or_file}: 路径:{event.src_path}"
        # 记录事件信息
        logger.info(logger_str)

        # 对于文件的修改和创建事件,进一步处理
        if event.event_type in ['modified', 'created'] and not event.is_directory:
          file_name_without_path = event.src_path.split("\\")[-1]  # 从路径中提取文件名
          action = "被修改" if event.event_type == 'modified' else "被创建"
          # logger.info(f'文件: {file_name_without_path} {action}')
          
          # 修改事件
          if event.event_type == 'modified':
            # bin
            try:
               with open(event.src_path, 'rb') as file:
                 logger.info('读取文件内容成功(二进制)')
                 file_contents = file.read()
                 print(f"=============={file_name_without_path}文件内容如下(二进制)==============")
                 print(f"{file_name_without_path}文件内容(二进制):\n{file_contents}")  # 打印文件内容
                 print(f"=============={file_name_without_path}文件内容如上(二进制)==============")
            except Exception as e:
               logger.error(f'读取文件时发生错误(二进制读取失败?!): {e}') 
            # UTF-8 + GBK + ANY
            try:
              encoding=detect_encoding(event.src_path)
              logger.info("检测到编码为:"+encoding)
              with open(event.src_path, 'r', encoding=encoding) as file:
                file_contents = file.read()
                print(f"=============={file_name_without_path}文件修改后内容如下({encoding})==============")
                print(f"文件内容({encoding}):\n{file_contents}")  # 打印文件内容
                print(f"=============={file_name_without_path}文件修改后内容如上({encoding})==============")
              #下面的代码应该不会触发,历史遗留 
            except Exception as e:
                try:
                  logger.info('尝试UTF-8编码读取...')
                  with open(event.src_path, 'r',encoding="utf-8") as file:
                    logger.info('读取文件内容成功(UTF-8)')
                    file_contents = file.read()
                    print("==============新文件内容如下==============")
                    print(f"文件内容(UTF-8):\n{file_contents}")  # 打印文件内容
                    print("==============新文件内容如上==============")
                except Exception as e:
                  logger.error(f'读取文件时发生错误: {e}')  # 使用日志记录异常信息
                  logger.info('尝试GBK编码读取...')
                  try:
                      with open(event.src_path, 'r',encoding="gbk") as file:
                        logger.info('读取文件内容成功(GBK)')
                        file_contents = file.read()
                        print("==============新文件内容如下==============")
                        print(f"文件内容:\n{file_contents}")  # 打印文件内容
                        print("==============新文件内容如上==============")
                  except Exception as e:
                        logger.error(f'读取文件再次发生错误,未知报错: {e}')

def monitor_folder(path='.'):
  event_handler = MyHandler()  # 创建MyHandler实例
  observer = Observer()  # 创建Observer实例
  observer.schedule(event_handler, path, recursive=False)  # 配置Observer监控的目录和事件处理器
  observer.start()  # 启动Observer
  try:  # 等待捕获键盘中断异常
    while True:  
      time.sleep(1)  # 减少CPU占用
  except KeyboardInterrupt:  # 捕获键盘中断(Ctrl+C)
    observer.stop()  # 停止Observer
    # print("[-]捕获到键盘中断,停止监控") 
    logger.warning("[-]捕获到键盘中断,停止监控")
  observer.join()  # 等待Observer线程结束



if __name__=="__main__":  # 如果此脚本作为主程序运行
  log_init() #初始化日志
  logger.info("logger测试")
  now_path = os.getcwd()  # 获取当前脚本的路径
  logger.info("[+]当前目录为:"+now_path)
  logger.info("[+]开始监控文件夹:"+ now_path)
  monitor_folder(now_path)  # 调用monitor_folder函数开始监控当前脚本所在的目录

prog_2

import configparser
import os
def create_default_ini(filename):
    config = configparser.ConfigParser()
    config['DEFAULTI'] = {
        'course': '网络运维技术',
        'university': '山东科技大学',
        'grade': '3'
    }
    config['student'] = {
        'name': '?',
        'class': '?',
        'studentid': '?',
        'seX': '?'
    }
    with open(filename, 'w',encoding="utf-8") as configfile:
        config.write(configfile)

filename = "student.ini"
if not os.path.exists(filename):
    print(f"文件{filename}不存在,将创建默认配置文件")
    create_default_ini(filename)

prog_3

def main():
  import os
  filename = "student.ini"
  if not os.path.exists(filename):
    print(f"[PROG_3]文件{filename}不存在,程序退出")
    return
  
  # 解析配置文件
  import configparser
  config = configparser.ConfigParser()
  config.read(filename,encoding="utf-8")
  
  # 读取配置
  res = config.get('DEFAULTI', 'course')
  print(res)

  # 保存TXT
  c_name = 'dayi'

  with open(c_name+"-UTF-8.txt","w+",encoding="utf-8") as file:
    file.write(res)
  
  with open(c_name+"-GBK.txt","w+",encoding="GBK") as file:
    file.write(res)

if __name__ == '__main__':
  main()

prog_4

import os
import configparser
import os
def read_modify_output():
  # 文件名
  filename = "student.ini"
  if not os.path.exists(filename):
    print(f"[PROG_4]文件{filename}不存在,程序退出")
    return  
  config = configparser.ConfigParser()
  config.read(filename,encoding="utf-8")

  print('文件内容如下:')
  for section in config.sections():
      print(f"[{section}]")
      for key in config[section]:
          print(f"{key} = {config[section][key]}")  
  
  # 修改内容
  config.set('student', 'name', 'dayi')
  config.set('student', 'class', '网络21')
  config.set('student', 'studentid', '2021233333333')
  config.set('student', 'seX', 'Male')  
  

  # 保存文件
  with open(filename, 'w',encoding="utf-8") as configfile:
      config.write(configfile)  
 
  # 重新读取INI文件以确认修改是否成功
  config.read(filename,encoding="utf-8")
  
  # 打印修改后的值
  print("==========修改后的值:===================")
  print("姓名:", config.get('student', 'name'))
  print("班级:", config.get('student', 'class'))
  print("学号:", config.get('student', 'studentid'))
  print("性别:", config.get('student', 'seX'))


if __name__ == '__main__':
  read_modify_output()

runner

import os
import time
os.remove('dayi-GBK.txt')
os.remove('dayi-UTF-8.txt')
os.remove('student.ini')

time.sleep(10)

print("执行prog_2.py:")
os.system("py prog_2.py")

time.sleep(10)

print("执行prog_3.py:")
os.system("py prog_3.py")

time.sleep(10)

print("执行prog_4.py:")
os.system("py prog_4.py")

依赖(提前安装)

requirements.txt

chardet==5.2.0
colorama==0.4.6
colorlog==6.8.2
watchdog==4.0.0

安装:

pip install -i https://mirrors.ustc.edu.cn/pypi/web/simple chardet colorama colorlog watchdog

文件下载

https://p.dabbit.net/blog/pic_bed/sharex/_pn-2024-03-07-12-08-29_Iggypops_Attentive_Circular.rar

网络安全实训

文章版本1.1

更好的阅读地址:

项目背景

某企业购买了防火墙设备,现需要进行企业网络建设,考虑到安全性,要求实现权限最小化,所有非必要端口和业务不可以对外开放。

项目目标

  • 一、掌握AF的部署配置,理解AF作为网关出口部署的原理。
  • 二、掌握AF的终端下载防御配置方法,理解终端下载防护的应用场景。
  • 三、掌握AF的终端僵尸网络防御配置方法,理解终端僵尸网络防护的应用场景。通过AF捕获恶意链接数据包,分析外联过程 。
  • 四、掌握AF防止DDOS攻击外网的配置方法,理解防止DDOS攻击外网的应用场景。通过AF捕获DOS攻击数据包,分析SYN Flood攻击过程 。
  • 五、掌握AF的服务器漏洞攻击防护功能及策略配置。理解WAF防御Web攻击的技术原理,掌握AF的WAF功能及策略配置。通过AF捕获SQL注入攻击数据包,分析SQL注入攻击过程 。

网络拓扑

外网区域:202.96.137.2/24 -> 202.96.137.1 -> SNAT 到租户区域外,这里的IP地址是172.16开头的

网络安全实训任务要求

准备?

  1. 登录后找到拓扑

  1. 一样的就可以 (你看这里就多个机子)

需要的拓扑图:

IP相关信息

实际上172.172.0.0/16 也是公网IP段

内网机器:172.172.4.10/24
内网服务器: 172.172.3.100/24

不知道为什么分的号有所区别,不同的号有不同的AF版本,最后的章节里有老板AF的信息。目前看,四班的号似乎只有scsa19、scsa16 的号是新系统,剩下老板的AF比较多。

任务1

1.1、防火墙做出口网关使用,需要代理内网用户和服务器网段上网进行互联网访问,要求在防火墙上划分区域分别为互联网区域和内网区域和服务器区域,内网PC和服务器访问互联网方向无限制。(配置截图,互联网访问成功截图)

  1. 创建3个区域
  • 互联网区域
  • 服务器区域
  • 内网区域

    都是三层的哦,是有IP的。

    然后分配端口:

  1. 配置三个接口

端口分配eth3 ,外网出口

这个端口将会作为外网出口,这也就是外网区域。

eth3 -> 路由器,外网出口

这里允许Ping。

端口分配eth2 服务器区域

也作为服务器网关的出口IP
  • IP:172.172.3.254/24

服务器的网络将会从这里出来。

端口分配eth1 内网区域网关

这里作为PC网关、内网区域的网关。
  • IP:172.172.4.254/24

然后尝试ping一下,确保可以通

ping之前记得先把服务器IP和PC的IP配好。

服务器IP配置可以通过这个命令:

sudo ip addr add 172.172.3.100/24 dev eth0
sudo ifdown eth0 && sudo ifup eth0

界面位置:系统->分析工具

输入下面的内容即可ping通。

ping 202.96.137.1

# CTRL+C 可以停止
# ping 服务器
ping 172.172.3.100

# ping PC
ping 172.172.4.10

能通即可。

ping网关:

ping内网:

配置路由

这里配置AF的出口路由,也就是路由表,可以配置到外网:静态路由(网络,然后路由)
  • 添加默认网关即可:
0.0.0.0/0 via 202.96.137.1 

配置策略

配置防火墙策略,允许相关区域上网。

界面位置:策略->访问控制

简单全通策略

基本的全通,简单粗暴。允许所有区域相互访问。

新建地址段

服务器地址段和客户机地址段

新建地址段:

普通策略

配置不同区域不同的上网策略
  1. 内网和服务器:
  • : pc地址、服务器地址
  • 目的地址:any

    pc、服务器 区域可以访问任意区域,但是仅可以访问PC地址、服务器地址的IP段。

  1. 内网上网:
  • : pc地址
  • 目的区域:互联网区域
  • 可以让内网正常上网。

  1. 服务器上网
  • : 服务器区域
  • 目的区域:互联网区域
  • 可以让服务器区域正常上网。

配置地址转换NAT

也就是SNAT 源地址转换
  1. 地址转换1

配置NAT,转换后的数据包为出接口地址

  1. 配置策略
  • 让服务器和内网区域,允许访问外网。
  • 禁用的忽略即可。(因为一起用的人有点多)

测试是否可以正常上网

PC测试

  1. 打开控制台

控制台确实挺难进入的。

  1. 配置IP

172.172.4.10/24

  1. 尝试上网

可以正常上网。

  1. 访问服务器

服务器测试

1. 配置IP

为了配置服务器的IP地址,请按照以下步骤操作:

sudo ip addr add 172.16.3.100/24 dev eth0
sudo ifdown eth0 && sudo ifup eth0
2. 访问互联网

确保服务器能够正常访问互联网。

1.2、互联网测试:互联网测试PC机可以通过访问防火墙的映射端口8080,访问到内网WEB服务器的80端口服务。(配置截图、访问成功截图)

PC机可以通过访问防火墙的映射端口8080,访问到内网WEB服务器的80端口服务。需要配置防火墙和进行一些测试。

AF配置目的地址转换

也叫端口映射和虚拟服务器
  • 界面位置:在地址转换配置界面
  • 配置内容202.96.137.2:8080 转换到 172.172.3.100:80

以下是相关配置和说明:

  1. 配置截图:

  2. 地址组配置:

    这里不需要配置地址组,服务器的单IP已新建一个地址组。

    注意是目的地址转换。

尝试访问

配置完毕后,应能够通过公网IP的8080端口访问内网的HTTP服务,对应到80端口的服务器。

  • 访问截图:

问题诊断

目前,8080端口无法打开。

这个机子的IP不像是云服务器的IP,更像是家庭宽带,不过访问的话可以在外网区域进行添加机器访问。

113.120.111.90

任务2

2.1 为保障内网终端安全,需要开启针对内网PC网段的对应的僵尸网络防护、防止下载病毒文件的安全策略,确保终端的安全性。(配置截图)

为保障内网终端安全,需开启以下安全策略:

  1. 添加僵尸网络策略:此策略有助于防止内网PC网段加入僵尸网络。

  2. 添加非法网站检测策略:此策略用于防止内网用户访问非法网站,避免下载病毒文件。

安全策略配置

新建安全策略,以确保终端的安全性。

  • 服务器IP段配置:针对服务器IP段进行安全设置。

  • 防护策略配置:设置具体的安全策略以保护内网。

  • 暂时禁用其他人策略:在配置期间,可以暂时禁用其他人的策略,以避免干扰。配置完成后,记得将其改回。

2.2 分别在AF内网PC口和外网口抓包,分析查看恶意域名访问拦截过程

在AF内网PC口和外网口进行抓包操作,分析查看恶意域名访问拦截过程。

1. 抓包过程

  • 进行抓包时,动作需迅速以捕捉相关数据。

2. Windows尝试访问

  • Windows终端尝试访问恶意域名,发现无法访问,显示访问已被拦截。

3. 抓包结果分析

  • 抓包结果显示恶意访问被成功拦截。

4. 数据包分析

  • 数据包 eth1

  • 数据包 eth3

5. 日志查看

  • 日志显示相关恶意访问已经被AF系统过滤掉。

这里已经被成功过滤啦!

任务3

3.1.为保障内网PC的DOS安全,防止服务器区域和互联网区域攻击PC,需要开启针对内网PC网段的对应的DOS安全策略。(配置截图,攻击拦截截图)

  1. 新增策略组
  2. 开启

3.2. 分别在AF内网PC口和服务器口抓包,分析查看SYN Flood攻击访问拦截过程

为模拟SYN Flood攻击,使用服务器对PC机发起攻击。

准备命令

  1. 用服务器打PC机
# 打SMB端口
ab -n 50 -c 10 http://172.172.4.10:445/

#先不要回车

开打

回车,执行命令,开始SYN Flood攻击。

过一会就好啦,过一会攻击完成,查看捕获的包。

eth2 的包

抓包结果显示,eth2接口捕捉到大量数据包。

eth1的包

会发现少了一些包,被防火墙拦截了一些包。

平台显示

平台显示记录了SYN Flood攻击的拦截过程。

拦截过程:

  1. 防火墙检测到超过阈值的SYN TCP握手包。
  2. 过滤并且对相关主机进行控制。

可以看出AF系统有效地识别并拦截了大规模的SYN Flood攻击,保护了内网环境不受该类型攻击的影响。

任务4

4.1.为保障内网WEB应用安全,防止互联网区域和PC区域攻击WEB应用,需要开启针对服务器网段的对应的安全策略,确保WEB应用的安全性。(配置截图,攻击拦截截图)

先把PC从黑名单里拉出来

移除PC机从黑名单
首先将PC机从黑名单中移除,以确保其能正常访问网络。

添加策略

添加安全策略
配置针对服务器网段的安全策略,确保WEB应用的安全性。

只开WEB防护

只记录日志

策略信息:

4.2. 分别在AF内网PC口和服务器口抓包,分析查看SQL注入攻击访问拦截过程

准备抓包

准备抓包
准备抓包过程,以监测网络活动。

先准备着:

登录平台

使用用户名gordonb和密码abc123登录平台,执行SQL注入攻击测试。

用户名gordonb
密码abc123

到sql注入

输入-1 union select 1,2,3,观察反应。

输入-1 union select 1,2,3

会卡住

PHP远程执行木马

PHP远程执行木马测试

也会出现卡住的情况。

也会卡住

停止抓包分析
抓包结束后,分析数据包。

停止抓包

Eth1数据包

Eth2 数据包

会发现数据包没有出现,不管是php木马的还是文件上传的。

已经被AF拦截。

未发现攻击相关的数据包,被AF拦截。

平台信息

平台显示了拦截过程和相关信息。

没检测到木马,直接显示的php意外哈哈哈哈,也可以。


okay

5、老版AF配网

有一些版本是老板的。

这里的配网:

内网:172.16.4.10/24 via 172.16.4.1
服务器:172.16.3.100/24 via 172.16.3.100

后来发现可能会有点小冲突(跟租户出口)

登录后这个样子:

老版AF配置接口

外网:

内网:

服务器:

PC ip

172.16.4.10/24

SERVER IP

配置地址转换

这样就差不多啦,剩下跟新版AF的大同小异,都是一些功能(感觉老板AF好用)。

(才不是服务器关了上不去了。)