需求与思路

在云服务器上部署了一个公司内部系统,该系统设置了只允许白名单中的IP访问。我们需要将办公网的IP添加到白名单,但由于办公网的IP每两天会发生变化,必须确保办公网用户能够正常访问该网站。

为此,我们可以让办公网的机器每小时向服务器发送请求。服务器将获取客户端的请求信息,并记录下其公网IP。当服务器接收到新的公网IP请求时,将该IP替换到NGINX的白名单中。

通过这种方式,我们可以实现动态更新白名单,确保办公网用户始终能够访问该网站。同时,为了避免allow白名单列表不断增加,我们可以将新IP替换到指定的行,以保持白名单的整洁。

实现

注:以下脚本均在python2.7环境下运行

1. 客户端脚本

#coding=utf-8
import base64
import logging
import urllib2

# 配置日志记录
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# 定义服务器的URL和请求的路径
server_urls = ["http://42.240.xxx.xxx:65306", "http://42.240.xxx.xxx:65306"]
request_path = "/api/update-ip"
username = "user"
password = "asdf5321"

# 获取家里电脑的公网IP地址
def get_public_ip():
    logging.info("正在获取公网IP地址")
    try:
        ip = urllib2.urlopen('https://ifconfig.me/ip').read().strip()
        logging.info("获取到公网IP地址: {}".format(ip))
        return ip
    except Exception as e:
        logging.error("获取公网IP地址时发生错误: {}".format(e))
        return None

# 发送请求到服务器
def send_request():
    public_ip = get_public_ip()
    if not public_ip:
        logging.error("无法获取公网IP,终止请求")
        return

    for server_url in server_urls:
        url = "{}{}?ip={}".format(server_url, request_path, public_ip)
        logging.info("正在发送请求到: {}".format(url))

        try:
            request = urllib2.Request(url)

            # 添加HTTP Basic认证头
            credentials = base64.b64encode("{}:{}".format(username, password))
            request.add_header("Authorization", "Basic {}".format(credentials))

            response = urllib2.urlopen(request)

            if response.getcode() == 200:
                logging.info("请求成功: {}".format(url))
            else:
                logging.warning("请求失败: {}".format(url))
        except urllib2.URLError as e:
            logging.error("请求发生异常: {} - {}".format(url, e))
        except Exception as e:
            logging.error("发生错误: {}".format(e))

# 发送请求
send_request()

循环执行

配置定时任务,每小时执行一次

crontab -e
## 每小时向git服务器发送本机公网IP
0 * * * * python /root/shell/get_local_PublicIP.py

2. 服务端脚本

pip install flask_httpauth
pip install Flask-BasicAuth
pip install psutil

# coding: utf-8
import os
import datetime
import glob
import logging
import socket
from flask import Flask, request
from flask_basicauth import BasicAuth

app = Flask(__name__)
app.config['BASIC_AUTH_USERNAME'] = 'user'
app.config['BASIC_AUTH_PASSWORD'] = 'asdf5321'

basic_auth = BasicAuth(app)

# 配置日志记录
log_path = '/data/script/logs/'
if not os.path.exists(log_path):
    os.makedirs(log_path)

# 创建日志记录器
logger = logging.getLogger('update_ip')
logger.setLevel(logging.INFO)

# 创建文件处理器,将日志写入文件
today = datetime.datetime.now().strftime("%Y-%m-%d")
log_file = os.path.join(log_path, 'update_ip_log_' + today + '.txt')

# 检查日志文件是否存在,如果不存在,则创建一个空的日志文件
if not os.path.exists(log_file):
    open(log_file, 'a').close()

file_handler = logging.FileHandler(log_file)
file_handler.setLevel(logging.INFO)

# 创建格式化器,定义日志记录的格式
formatter = logging.Formatter('%(message)s', datefmt='%Y-%m-%d %H:%M:%S')
file_handler.setFormatter(formatter)

# 将文件处理器添加到日志记录器
logger.addHandler(file_handler)

# 移除可能的默认处理器
logger.handlers = [file_handler]

# 定义接收请求的路由和方法
@app.route('/api/update-ip', methods=['GET'])
@basic_auth.required  # 添加账号密码验证
def update_ip():
    # 获取请求参数中的 IP 地址
    ip = request.args.get('ip')

    # 在这里可以对 IP 地址进行处理或存储
    # 替换文件中的第 1 行为接收到的 IP 地址
    file_path = '/www/server/nginx/conf/allow'
    with open(file_path, 'r') as file:
        lines = file.readlines()
        file_timestamp = lines[1].strip().split('_')[-1].split('.')[0].replace('-', ' ')

    lines[1] = 'allow ' + ip + ';\n'  # 将第 1 行替换为 "allow IP 地址;"

    with open(file_path, 'w') as file:
        file.writelines(lines)

    # 重启 Nginx 服务
    os.system('nginx -s reload')

    # 记录日志
    timestamp = datetime.datetime.now()
    log_message = "{} - 新的 IP 地址:{}".format(timestamp.strftime('%Y-%m-%d %H:%M:%S'), ip)
    logger.info(log_message)

    # 删除 30 天之前的日志
    thirty_days_ago = datetime.datetime.now().date() - datetime.timedelta(days=30)
    log_files = glob.glob(os.path.join(log_path, 'update_ip_log_*.txt'))
    for file in log_files:
        file_timestamp = file.split('_')[-1].split('.')[0]
        log_date = datetime.datetime.strptime(file_timestamp, "%Y-%m-%d").date()
        if log_date < thirty_days_ago:
            os.remove(file)
            log_message = "{} - 删除了过期日志文件:{}".format(timestamp.strftime('%Y-%m-%d %H:%M:%S'), file)
            logger.info(log_message)

    return "IP 地址已更新"

def is_service_running():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    result = sock.connect_ex(('127.0.0.1', 65306))
    if result == 0:
        log_message = "{} - 检测到服务正在运行".format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
        logger.info(log_message)
        return True
    else:
        log_message = "{} - 未检测到服务运行!".format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
        logger.info(log_message)
        return False

if __name__ == '__main__':
    if not is_service_running():
        log_message = "{} - 启动服务".format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
        logger.info(log_message)
        # 监听所有网络接口,使其可以接受来自其他机器的请求
        app.run(host='0.0.0.0', port=65306)
    else:
        log_message = "{} - 服务已在运行,无需再次启动".format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
        logger.info(log_message)

后台启动

nohup python get_staff_publicIP.py &

写入定时任务

为了防止该程序被异常关闭导致服务不可用,定时启动脚本(脚本已配置如果正在运行则不作操作,如果未运行则后台运行)

crontab -e
58 * * * * nohup python /data/script/ get_staff_ip.py &

关闭脚本

ps aux | grep get_staff_ip.py | grep -v grep | awk '{print $2}' | xargs kill
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。