Creating new process for each request on SSL socke

2019-03-05 06:28发布

I am trying to use keys and certificate generated using java keytool in python server and java client. I have created key & keystore, exported certificate, added certificate to truststore, converted keystore to standard pkcs format and then extracted key and certificate from pkcs to use in python server. (first three steps from here and last three steps from here). This question is somewhat follow up of this question and detailed steps for generating key and certificate can be found in that question.

My SSL server looks like this

server.py

import socket
import multiprocessing as mup
import ssl

def worker_ssl(data_socket, client_address):
    print("Inside worker")
    #some processing

def start_server_ssl():

    socketObj = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_address = ("127.0.0.1", 6000)
    socketObj.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    socketObj.bind(server_address)
    socketObj.listen(10)

    ssl_socket = ssl.wrap_socket(socketObj,
                            server_side = True,
                            certfile="cert.pem",
                            keyfile="key.pem")        

    while True:
        print("Waiting For Connections .........\n")
        try:
            data_socket, client_address = ssl_socket.accept()

            process = mup.Process(target=worker_ssl, args=(data_socket, client_address))            
            process.daemon = True
            process.start()

        except socket.error as msg:
            print (" [ERROR] : %s " % msg)
            continue

    socketObj.close()
    ssl_socket.shutdown(socket.SHUT_RDWR)
    ssl_socket.close()

if __name__ == '__main__':
    start_server_ssl()

SSL Client looks like this:

Client4Py.java

import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

public class Client4Py {
    static KeyStore ks;
    static KeyManagerFactory kmf;
    static TrustManagerFactory tmf;
    static SSLContext sc;
    static TrustManager[] trustManagers;

    static {
        try {
            ks = KeyStore.getInstance("JKS");
            ks.load(new FileInputStream("D:\\javasslstores\\truststore.jks"), "passwd123".toCharArray());

            kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(ks, "passwd123".toCharArray());

            tmf = TrustManagerFactory.getInstance("SunX509"); 
            tmf.init(ks);

            sc = SSLContext.getInstance("TLS"); 

            sc.init(null, tmf.getTrustManagers(), null);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            System.out.println(e.getStackTrace());
        }
    }

    public static void main(String[] args) throws IOException {
        SSLSocketFactory ssf = sc.getSocketFactory();
        SSLSocket socket = (SSLSocket) ssf.createSocket("127.0.0.1", 6000);
        socket.startHandshake();

        PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(),StandardCharsets.UTF_8)));
        //PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));

        out.println("<<startMessage>>");
        out.println("Message from Client4Py");
        out.println("<<endMessage>>");
        out.flush();

        if (out.checkError())
            System.out.println(
                "SSLSocketClient:  java.io.PrintWriter error");

        out.close();
        socket.close();
    }
}

The output on server's console after first running server and then the client is as follows:

1    Waiting For Connections .........
2    
3    Traceback (most recent call last):
4      File "D:\workspaces\workspace6\PythonServer\server.py", line 40, in <module>
5        start_server_ssl()
6      File "D:\workspaces\workspace6\PythonServer\server.py", line 29, in start_server_ssl
7        process.start()
8      File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\process.py", line 105, in start
9        self._popen = self._Popen(self)
10      File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\context.py", line 223, in _Popen
11        return _default_context.get_context().Process._Popen(process_obj)
12      File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\context.py", line 322, in _Popen
13        return Popen(process_obj)
14      File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\popen_spawn_win32.py", line 65, in __init__
15        reduction.dump(process_obj, to_child)
16      File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\reduction.py", line 60, in dump
17        ForkingPickler(file, protocol).dump(obj)
18      File "D:\Programs\python\python-3.6.6-amd64\lib\socket.py", line 185, in __getstate__
19        raise TypeError("Cannot serialize socket object")
20    TypeError: Cannot serialize socket object
21    Traceback (most recent call last):
22      File "<string>", line 1, in <module>
23      File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\spawn.py", line 99, in spawn_main
24        new_handle = reduction.steal_handle(parent_pid, pipe_handle)
25      File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\reduction.py", line 87, in steal_handle
26        _winapi.DUPLICATE_SAME_ACCESS | _winapi.DUPLICATE_CLOSE_SOURCE)
27    PermissionError: [WinError 5] Access is denied

You can see on line 20, there is TypeError: Cannot serialize socket object.

Code starts working after removing all SSL stuffs

When I comment call to wrap_socket(), replace ssl_socket.accept() with socketObject.accept() and comment ssl_socket.shutdown() and close(), the code starts working. It creates new process from worker() as desired and ends printing Inside worker on console. Here is the modified non SSL server:

import socket
import multiprocessing as mup
# import ssl

def worker_ssl(data_socket, client_address):
    print("Inside worker")
    #some processing

def start_server_ssl():

    socketObj = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_address = ("127.0.0.1", 6000)
    socketObj.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    socketObj.bind(server_address)
    socketObj.listen(10)

#     ssl_socket = ssl.wrap_socket(socketObj,
#                             server_side = True,
#                             certfile="cert.pem",
#                             keyfile="key.pem")        

    while True:
        print("Waiting For Connections .........\n")
        try:
            data_socket, client_address = socketObj.accept()

            process = mup.Process(target=worker_ssl, args=(data_socket, client_address))            
            process.daemon = True
            process.start()

        except socket.error as msg:
            print (" [ERROR] : %s " % msg)
            continue

    socketObj.close()
#     ssl_socket.shutdown(socket.SHUT_RDWR)
#     ssl_socket.close()

if __name__ == '__main__':
    start_server_ssl()

1条回答
倾城 Initia
2楼-- · 2019-03-05 07:06

I dont know whether I understood the exact reason, but I am stating it here.

Looking at following stacktrace lines:

17        ForkingPickler(file, protocol).dump(obj)
18      File "D:\Programs\python\python-3.6.6-amd64\lib\socket.py", line 185, in __getstate__
19        raise TypeError("Cannot serialize socket object")
20    TypeError: Cannot serialize socket object

it seems that multiprocessing module's Process.start() method serializes the parameters passed to it for passing them to new process. And it further seems that SSLSocket objects cannot be serialized. However, it seems that Socket objects can be serialized. This issue states the same and thats why the Socket based server works. However, I dont get why is this so (SSLSocket is not serializable but Socket objects can be serialized). I mean is there any method that is implemented by Socket objects which are not implemented by SSLSocket. Also note that the error occurs at socket.py, line 185, in __getstate__, that is in Socket class, but not in SSLSocket class.

I will like if someone confirms above reason (that is SSLSocket objects are not serializable and Socket objects are serializable), explains exactly why is this the case and provides solution for using SSLSocket with multiprocessing module.

Workaround

I ended up forking new process using os.fork. (Examples of os.fork can be found here). Its supported on linux only. So, I installed python on cygwin and then used it.

查看更多
登录 后发表回答