前言
部署了一个自己使用的web服务,不想对公网开放。最初用iptables对自己当前的电脑IP开放,禁止其他IP访问。每次路由器重启,或者在外出差,IP经常变动。需要登录服务器,新增新的IP。决定改变控制方式,利用nginx的IP白名单功能,同时用flask写了一个对公网开放的页面。当地址变动时,访问此页面。点击一键更新,就把最新的ip加入到nginx的白名单。同时重新加载nginx配置生效。
被控制服务
需要进行ip访问控制,不对公网开放的nginx配置信息。
default.conf
代码语言:javascript复制配置用加载了ip白名单文件whitelist.conf
# Appadmin
server {
listen 80;
server_name 0.0.0.0;
root /www/web/maccms_v10/;
server_tokens off;
#include none.conf;
index index.php index.html index.htm;
access_log /www/web_logs/wp_access.log wwwlogs;
error_log /www/web_logs/wp_error.log notice;
#auth_basic "请输入用户和密码"; # 验证时的提示信息
#auth_basic_user_file /etc/nginx/password; # 认证文件
location /{
include whitelist.conf;
#默认位置路径为/etc/nginx/ 下,
#如直接写include whitelist.conf,则只需要在/etc/nginx目录下创建whitelist.conf
deny all;
}
location ~ .php$ {
fastcgi_pass php:9000;
fastcgi_index index.php;
include fcgi.conf;
}
#需要注意伪静态的配置
if (!-e $request_filename) {
rewrite ^/index.php(.*)$ /index.php?s=$1 last;
rewrite ^/api.php(.*)$ /api.php?s=$1 last;
rewrite ^/adm0.php(.*)$ /adm0.php?s=$1 last;
rewrite ^(.*)$ /index.php?s=$1 last;
break;
}
location ~ .*.(gif|jpg|jpeg|png|bmp|swf)$ {
expires 30d;
}
location ~ .*.(js|css)?$ {
expires 12h;
}
}
whitelist.conf
代码语言:javascript复制文件内存放需要开放的IP,文件内容:
allow 101.31.158.153;
控制服务
文章开头的一键放通页面用flask框架实现, 单独部署
app.py
代码语言:javascript复制主要实现逻辑,有两个接口。一个接口提供页面,一个接口负责获取IP后更新,同时重新加载被控制服务的nginx配置
from flask import Flask, request, render_template, jsonify
import os
app = Flask(__name__)
# 定义首页路由,渲染包含按钮的HTML页面
@app.route('/')
def index():
return render_template('index.html')
# 定义处理客户端IP上传的路由
@app.route('/upload_ip', methods=['POST'])
def upload_ip():
# 获取客户端的IP地址
#client_ip = request.remote_addr
client_ip = request.headers.get('X-Forwarded-For')
# 这里可以添加处理逻辑,比如将IP地址保存到数据库或文件
print(f"Client IP: {client_ip}")
try:
command = f"echo 'allow {client_ip};' > /srv/lnmp/services/nginx/conf/whitelist.conf && docker exec lnmp-web-1 /bin/bash -c 'nginx -s reload'"
status = os.system(command)
if status == 0:
response = {'status': 'success', 'ip': client_ip}
else:
response = {'status': 'error', 'message': 'update ip fail'}
except Exception as e:
response = {'status': 'error', 'message': str(e)}
# 返回响应
return jsonify(response)
if __name__ == '__main__':
# 将应用监听在0.0.0.0的3000端口
app.run(host='0.0.0.0', port=801, debug=True)
index.html
代码语言:javascript复制提供文章开头的一键更新功能的页面代码
IP 过白
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f0f0f0;
margin: 0;
}
.container {
text-align: center;
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
h1 {
color: #333;
}
#uploadBtn {
background-color: #007bff;
color: #fff;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s ease;
}
#uploadBtn:hover {
background-color: #0056b3;
}
#uploadBtn:focus {
outline: none;
}
更新此客户端IP
确认
$(document).ready(function() {
$('#uploadBtn').click(function() {
$.ajax({
type: 'POST',
url: '/upload_ip',
success: function(response) {
if (response.status === 'success') {
alert('IP 更新成功: ' response.ip);
('Error: ' response.message);
}
},
error: function() {
alert('发生错误.');
}
});
});
});
服务启动
代码语言:javascript复制控制服务通过systemd加载,配置文件为:/etc/systemd/system/ipallow.service。配置内容为
[Unit]
Description=IpAllow App
[Service]
User=root
WorkingDirectory=/opt/ipallow
ExecStart=/usr/local/bin/gunicorn -w 2 -b 0.0.0.0:801 app:app
Restart=always
[Install]
WantedBy=multi-user.target
caddy代理
代码语言:javascript复制控制服务启动了服务器的801端口,通道caddy2代理到443,然后通过公网可访问。不用nginx代理的原因是控制服务会重启nginx,导致前端页面在等待返回结构时异常。
b.test.xyz:443 {
tls service@test.xyz
encode gzip
log {
output file /logs/access.log
}
header / {
Strict-Transport-Security "max-age=31536000;includeSubdomains;preload"
}
#访问认证
basicauth / {
cms $2a$14$bNLxxxxxxxxxxxxxxxxxxxxxxGAbzyOUyoBn1rjfpN/O
}
## HTTP 代理配置
reverse_proxy http://192.168.0.203:801 {
header_up X-Real-IP {http.request.remote.host}
header_up X-Forwarded-For {http.request.remote.host}
header_up X-Forwarded-Port {http.request.port}
header_up X-Forwarded-Proto {http.request.scheme}
}
}
caddy认证密码
代码语言:javascript复制生产caddy的认证密码
caddy hash-password --plaintext 'cmsxxxx'