有3台服务器如下:
192.168.111.201(反向代理到正向代理服务器)
192.168.111.202(正向代理服务器)
192.168.111.203(目标WEB系统)
防火墙网络策略如图所示:
1、192.168.111.200 只能访问 192.168.111.201 的 8081端口。
2、192.168.111.201 只能访问 192.168.111.202 的 8082端口。
2、192.168.111.202 只能访问 192.168.111.203 的 8083/8084/8085端口。
目标:
本地192.168.111.200 发送http请求 http://192.168.111.203:8083/web 能够返回结果。
一、网络策略
1、192.168.111.201 开放8081端口
#清空防火墙规则
mv public.xml public.xml.bak
systemctl restart firewalld#开启端口
firewall-cmd --permanent --add-port=8081/tcp#重新加载生效
firewall-cmd --reload#查询有哪些端口是开启的
firewall-cmd --list-port
2、192.168.111.202 只对 192.168.111.201 开放8082端口
#清空防火墙规则
mv public.xml public.xml.bak
systemctl restart firewalld#只允许192.168.111.201访问8082端口,注意这里有两个port不是写错了
firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=192.168.111.201 port port=8082 protocol=tcp accept'#重新加载生效
firewall-cmd --reload#查看规则
firewall-cmd --list-rich-rules#删除规则
firewall-cmd --permanent --remove-rich-rule='<RULE>'
3、192.168.111.203 只对 192.168.111.202 开放8083端口
#清空防火墙规则
mv public.xml public.xml.bak
systemctl restart firewalld#只允许192.168.111.202访问8083端口,注意这里有两个port不是写错了
firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=192.168.111.202 port port=8083 protocol=tcp accept'#重新加载生效
firewall-cmd --reload#查看规则
firewall-cmd --list-rich-rules#删除规则
firewall-cmd --permanent --remove-rich-rule='<RULE>'
测试网络策略
#安装telnet客户端
yum -y install telnet.*telnet 192.168.111.202 8082 只有192.168.111.201通
telnet 192.168.111.203 8083 只有192.168.111.202通
二、192.168.111.201
安装 nginx
# 安装四个依赖
yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel
# 有些系统还需要多安装几个依赖
# yum -y install make gcc-c++ libtool
yum -y install wget
wget http://nginx.org/download/nginx-1.13.7.tar.gz
tar -zxvf nginx-1.13.7.tar.gz
cd nginx-1.13.7
./configure --prefix=/usr/local/nginx --with-stream
make && make install
修改配置 vi /usr/local/nginx/conf/nginx.conf,反向代理到 192.168.111.202
stream {server {listen 8081;proxy_pass 192.168.111.202:8082;}
}
启动nginx
cd /usr/local/nginx/sbin/
./nginx
三、192.168.111.202
部署正向代理服务器
package com.study.proxy;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.StringTokenizer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 正向代理* @Date: 2023/11/20 14:49*/
public class Proxy {private static Logger logger = LoggerFactory.getLogger(Proxy.class);public static void main(String[] args) throws Exception {startProxyServer();}// 启动代理服务器public static void startProxyServer() {new Thread(new ProxyServer()).start();}// 代理服务器static class ProxyServer implements Runnable {@Overridepublic void run() {try {// 监听指定的端口int port = 8082; //一般使用49152到65535之间的端口ServerSocket server = new ServerSocket(port);// 当一个ServerSocket关闭并释放其绑定的端口后,操作系统通常会在几分钟内不允许其他Socket再次绑定到该端口。// true:操作系统将允许其他Socket立即绑定到刚刚被释放的端口。server.setReuseAddress(true);// 使用线程池,防止过多线程耗尽资源ExecutorService threadPool = Executors.newFixedThreadPool(50);while (true) {Socket socket = server.accept(); //会一直阻塞,直到有客户端连接进来// new Thread 只是创建一个类的对象实例而已。而真正创建线程的是start()方法。// 这里并没有直接调用start()方法,所以并没创建新线程,而是交给线程池去执行。threadPool.submit(new ProxyClient(socket));}} catch (Exception e) {logger.error("ProxyServer", e.getMessage(), e);}}}// 代理客户端static class ProxyClient implements Runnable {private final Socket proxySocket;//代理Socketprivate Socket targetSocket = null;//目标Socketpublic ProxyClient(Socket socket) {this.proxySocket = socket;}@Overridepublic void run() {try {//客户端请求的报文InputStream req = proxySocket.getInputStream();int read;int contentLength = 0;//body长度String method = null;//请求方法String url = null;//请求地址String protocol = null;//请求协议ByteArrayOutputStream os = new ByteArrayOutputStream();ByteArrayOutputStream reqBack = new ByteArrayOutputStream();//解析,提取请求报文while ((read = req.read()) != -1) {os.write(read);reqBack.write(read);if (read == '\n') {//CONNECT www.xx.com:443/xx/yy HTTP/1.1String line = os.toString("UTF-8");os.reset();//重置,以便再次使用if ("\r\n".equals(line)) {//空行,请求头结束标志break;}StringTokenizer stringTokenizer = new StringTokenizer(line, " ");if (method == null) {//八种请求方法:GET、POST、HEAD、OPTIONS、PUT、PATCH、DELETE、TRACE、CONNECT 方法method = stringTokenizer.nextToken().toLowerCase();//connecturl = stringTokenizer.nextToken();//www.xx.com:443/xx/yyprotocol = stringTokenizer.nextToken().trim();//HTTP/1.1} else {String key = stringTokenizer.nextToken().toLowerCase();if ("content-length:".equals(key)) {String value = stringTokenizer.nextToken().trim();contentLength = Integer.parseInt(value);}}}}if (contentLength > 0) {for (int i = 0; i < contentLength; i++) {reqBack.write(req.read());}}//完整请求报文// String request = reqBack.toString("UTF-8");// System.out.println("请求报文开始");// System.out.print(request);// System.out.println("\r\n请求报文结束");//拼接完整urlif (url != null && !url.startsWith("http")) {url = method.equals("connect") ? "https://" + url : "http://" + url;}URL u = new URL(url);//目标ipString targetHost = u.getHost();//目标端口int targetPort = u.getPort();if (targetPort == -1) {targetPort = 80;}//目标SockettargetSocket = new Socket(targetHost, targetPort);if ("connect".equals(method)) {//https//HTTP/1.1 200 Connection established//报文直接发送给代理SocketOutputStream outputStream = proxySocket.getOutputStream();outputStream.write((protocol + " 200 Connection established\r\n").getBytes(StandardCharsets.UTF_8));outputStream.write("Proxy-agent: ProxyServer/1.0\r\n".getBytes(StandardCharsets.UTF_8));outputStream.write("\r\n".getBytes(StandardCharsets.UTF_8));outputStream.flush();//前者转发给后者,代理Socket转发给目标SocketThread proxy2target = new Thread(new ForwardData(proxySocket, targetSocket));proxy2target.start();//前者转发给后者,目标Socket转发给代理SocketThread target2proxy = new Thread(new ForwardData(targetSocket, proxySocket));target2proxy.start();proxy2target.join();} else {//http//请求报文转发给目标SocketOutputStream outputStream = targetSocket.getOutputStream();outputStream.write(reqBack.toByteArray());outputStream.flush();//前者转发给后者,目标Socket转发给代理SocketThread thread = new Thread(new ForwardData(targetSocket, proxySocket));thread.start();thread.join();}} catch (Exception e) {logger.error("ProxyClient", e.getMessage(), e);} finally {try {if (targetSocket != null) {targetSocket.close();}} catch (IOException e) {logger.error("ProxyClient", e.getMessage(), e);}try {if (proxySocket != null) {proxySocket.close();}} catch (IOException e) {logger.error("ProxyClient", e.getMessage(), e);}}// logger.error("ProxyClient", "结束");}// 转发数据static class ForwardData implements Runnable {private final Socket inputSocket;private final Socket outputSocket;public ForwardData(Socket inputSocket, Socket outputSocket) {this.inputSocket = inputSocket;this.outputSocket = outputSocket;}@Overridepublic void run() {try {InputStream inputStream = inputSocket.getInputStream();OutputStream outputStream = outputSocket.getOutputStream();int read;while ((read = inputStream.read()) != -1) {outputStream.write(read);}} catch (Exception e) {// logger.error("ForwardData", inputSocket + e.getMessage());}}}}
}
四、192.168.111.203
部署目标web服务
@RestController
public class WebController {@GetMapping("/web")public String web() {return "ok";}
}
五、本地测试
package com.study.client;import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;/*** 测试*/
public class Test {public static void main(String[] args) {// 正向代理服务器System.setProperty("http.proxyHost", "192.168.111.201");System.setProperty("http.proxyPort", "8081");// 目标地址String url = "http://192.168.111.203:8083/web";String sendGet = sendGet(url);System.out.println(sendGet);}public static String sendGet(String url) {HttpURLConnection con = null;InputStream is = null;try {con = (HttpURLConnection) new URL(url).openConnection();is = con.getInputStream();ByteArrayOutputStream baos = new ByteArrayOutputStream();byte[] buf = new byte[1024];int len;while ((len = is.read(buf)) != -1) {baos.write(buf, 0, len);baos.flush();}return baos.toString("UTF-8");} catch (Exception e) {e.printStackTrace();} finally {try {if (is != null) {is.close();}if (con != null) {con.disconnect();}} catch (IOException e) {e.printStackTrace();}}return null;}
}
运行结果:
注释代理,连接超时