本次調研主要爲了解決兩個問題:java
結合這兩點,經過調研是否可使用nginx ssl代理來解決。同時熟悉下nginx對tcp代理的配置。nginx
但願達到的目標網絡模型以下:git
經過對SSL的學習,結合自身業務的考慮,對SSL的使用作以下說明:web
我這裏SSL使用TLSv1,而且服務端不須要校驗客戶端的身份合法性,則使用SSL單向認證方式,只須要服務端證書。另外咱們只須要用到SSL的鏈路加密,因此能夠設置客戶端對服務端證書保持永久信任windows
因爲對網絡相關的知識比較欠缺,因此採用以下步驟一一嘗試可行性。先測試nginx對普通tcp的代理,再測試nginx ssl代理在bio 和 nio IO模型下的使用,最後使用nginx ssl代理Thrift NIO。服務器
BIO:同步阻塞IO;NIO:同步非阻塞IO網絡
在windows7機器上安裝nginx-1.10.1,其中包括了ngx_stream_core_module模塊,可用於代理TCP協議,nginx具體安裝方法在此不詳述。負載均衡
worker_processes 1; events { worker_connections 1024; } stream { server { listen 9000; proxy_pass localhost:9091; } }
public class TcpServer { private ServerSocket serverSocket = null; public void guest (Socket socket) { Thread t = new Thread(new ServiceHandler(socket)); t.start(); } public void start(int port) throws IOException { try { serverSocket = new ServerSocket(port); } catch (IOException e) { throw e; } System.out.println("TCP server start, port -> " + port); while (true) { guest(serverSocket.accept()); System.out.println("Guest client"); } } class ServiceHandler implements Runnable { private Socket socket = null; private BufferedReader reader = null; private PrintWriter writer = null; public ServiceHandler(Socket socket) { this.socket = socket; } public Socket getSocket() { return socket; } @Override public void run() { try { reader = new BufferedReader(new InputStreamReader(this.socket.getInputStream())); writer = new PrintWriter(this.socket.getOutputStream()); String line = null; while ((line = reader.readLine()) != null) { if ("close".equals(line)) { break; } System.out.println("c -> " + line); writer.println("Received, t - " + new Date().toString()); writer.flush(); } } catch (IOException e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { } } if (writer != null) { writer.close(); } if (this.socket != null) { try { this.socket.close(); } catch (IOException e) { } } } } } public static void main(String[] args) { TcpServer tcpServer = new TcpServer(); try { tcpServer.start(9091); } catch (IOException e) { e.printStackTrace(); } } }
public class TcpClient { private Socket socket = null; private BufferedReader reader = null; private PrintWriter writer = null; public void start(int port) throws IOException { try { socket = new Socket("localhost", port); System.out.println("Connected, port -> " + port); } catch (IOException e) { throw e; } try { reader = new BufferedReader(new InputStreamReader(this.socket.getInputStream())); Thread t = new Thread(new TcpReader(reader)); t.setDaemon(true); t.start(); writer = new PrintWriter(this.socket.getOutputStream()); Scanner scanner = new Scanner(System.in); while (true) { System.out.println("input -> "); String input = scanner.next(); writer.println(input); writer.flush(); } } catch (IOException e) { throw e; } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { } } if (writer != null) { writer.close(); } if (this.socket != null) { try { this.socket.close(); } catch (IOException e) { } } } } public class TcpReader implements Runnable { private BufferedReader reader = null; public TcpReader(BufferedReader reader) { this.reader = reader; } @Override public void run() { String returnLine = null; while (true) { try { returnLine = reader.readLine(); System.out.println(returnLine); } catch (IOException e) { e.printStackTrace(); break; } } } } public static void main(String[] args) { TcpClient tcpClient = new TcpClient(); try { tcpClient.start(9000); } catch (IOException e) { e.printStackTrace(); } } }
服務端開啓TCP監聽9091端口,nginx TCP代理9091端口,並監聽9000端口,客戶端鏈接9000端口,經測試鏈接成功,並可與服務端進行交互。socket
worker_processes 1; events { worker_connections 1024; } stream { server { listen 9000 ssl; proxy_pass localhost:9091; ssl_certificate D:/server.crt; ssl_certificate_key D:/_server.key; } }
_server.key爲服務器私鑰,server.crt爲服務器證書,經過openssl生成,具體生成方法在此不詳述。tcp
同3.2.2
import com.spiro.test.net.common.Configuration; import javax.net.ssl.*; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.security.cert.X509Certificate; import java.util.Scanner; /** * Created by tz0643 on 2016/6/17. */ public class SSLTcpClient { private SSLSocket socket = null; private BufferedReader reader = null; private PrintWriter writer = null; public void start(int port) throws Exception { // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) { } public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }; SSLContext sslContext = SSLContext.getInstance("TLSv1"); sslContext.init(null, trustAllCerts, null); try { SSLSocketFactory factory = sslContext.getSocketFactory(); socket = (SSLSocket) factory.createSocket("192.168.10.188", port); System.out.println("Connected, port -> " + port); } catch (IOException e) { throw e; } try { reader = new BufferedReader(new InputStreamReader(this.socket.getInputStream())); Thread t = new Thread(new TcpReader(reader)); t.setDaemon(true); t.start(); writer = new PrintWriter(this.socket.getOutputStream()); Scanner scanner = new Scanner(System.in); while (true) { System.out.println("input -> "); String input = scanner.next(); writer.println(input); writer.flush(); } } catch (IOException e) { throw e; } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { } } if (writer != null) { writer.close(); } if (this.socket != null) { try { this.socket.close(); } catch (IOException e) { } } } } public class TcpReader implements Runnable { private BufferedReader reader = null; public TcpReader(BufferedReader reader) { this.reader = reader; } @Override public void run() { String returnLine = null; while (true) { try { returnLine = reader.readLine(); System.out.println(returnLine); } catch (IOException e) { e.printStackTrace(); break; } } } } public static void main(String[] args) { Configuration conf = Configuration.getInstance(); try { conf.init(); } catch (IOException e) { e.printStackTrace(); System.exit(-1); } SSLTcpClient tcpClient = new SSLTcpClient(); try { tcpClient.start(9000); } catch (Exception e) { e.printStackTrace(); } } }
服務端開啓BIO socket監聽9091端口,nginx TCP SSL代理9091端口,並監聽9000端口,客戶端BIO SSL socket鏈接9000端口,經測試鏈接成功,並可與服務端進行交互。
同3.3.1
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Date; import java.util.Iterator; public class NIOServer { private Selector selector; public void initServer(int port) throws IOException { ServerSocketChannel serverChannel = ServerSocketChannel.open(); serverChannel.configureBlocking(false); serverChannel.socket().bind(new InetSocketAddress(port)); this.selector = Selector.open(); serverChannel.register(selector, SelectionKey.OP_ACCEPT); } /** * @throws IOException */ public void listen() throws IOException { System.out.println("Server started"); while (true) { selector.select(); Iterator ite = this.selector.selectedKeys().iterator(); while (ite.hasNext()) { SelectionKey key = (SelectionKey) ite.next(); ite.remove(); if (key.isAcceptable()) { System.out.println("Accept 1 socket"); ServerSocketChannel server = (ServerSocketChannel) key .channel(); SocketChannel channel = server.accept(); channel.configureBlocking(false); channel.register(this.selector, SelectionKey.OP_READ); } else if (key.isReadable()) { read(key); } } } } public void read(SelectionKey key) throws IOException{ SocketChannel channel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); channel.read(buffer); buffer.flip(); byte[] data = new byte[buffer.remaining()]; buffer.get(data); System.out.println("c -> " + new String(data).trim()); String msg = "Received, t - " + new Date().toString() + "\n"; ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes()); channel.write(outBuffer); } /** * @throws IOException */ public static void main(String[] args) throws IOException { NIOServer server = new NIOServer(); server.initServer(9091); server.listen(); } }
同3.3.3
服務端開啓NIO socket監聽9091端口,nginx TCP SSL代理9091端口,並監聽9000端口,客戶端BIO SSL socket鏈接9000端口,經測試鏈接成功,並可與服務端進行交互。
同3.3.1
public void serve() { try { TNonblockingServerTransport transport = new TNonblockingServerSocket(port); TServer server = new TNonblockingServer( new TNonblockingServer.Args(transport).processor(processor)); System.out.println("Starting the simple nio server..."); server.serve(); } catch (Exception e) { e.printStackTrace(); } }
因爲Thrift客戶端API 參數TSSLTransportParameters必須設置trustStore,故必須根據服務端證書生成trust store文件。其實也可本身從新實現TSSLTransportFactory從而達到不須要設置trustStore,即永久信任服務端證書,這裏暫時不實現。
protected void connectAndInvoke() { TTransport transport = null; try { TSSLTransportFactory.TSSLTransportParameters params = new TSSLTransportFactory.TSSLTransportParameters(); String truststoreFilename = Configuration.getInstance() .getConf("ssl.truststore.filename"); String truststorePassword = Configuration.getInstance() .getConf("ssl.truststore.password"); params.setTrustStore(truststoreFilename, truststorePassword, "SunX509", "JKS"); transport = TSSLTransportFactory.getClientSocket("localhost", 9091, 0, params); transport.open(); TProtocol protocol = new TBinaryProtocol(transport); Calculator.Client client = new Calculator.Client(protocol); perform(client); } catch (TException x) { x.printStackTrace(); } finally { if (transport != null) { transport.close(); } } }
服務端開啓NIO thrift服務監聽9091端口,nginx TCP SSL代理9091端口,並監聽9000端口,客戶端使用Thrift SSL API鏈接9000端口,經測試鏈接成功,RPC調用正常。
通過調研,thrift服務端仍然使用NIO API,經過nginx ssl tcp代理對鏈路進行加密是可行的。只須要修改客戶端代碼爲 Thrift SSL API,同時這裏客戶端必須爲服務端證書生成trust store 文件,固然經過從新實現TSSLTransportFactory仍是能夠作到不須要這個trust store文件,只對鏈路進行加密不驗證服務端的合法性,這個待後續有時間再研究。
另外nginx ssl tcp代理也可用於進行負載均衡,這個相似對web http代理作負載均衡,這裏不作詳細介紹。