
Introduction
Sitting in a cafe, sipping delicious coffee, and thinking of enjoying the free Wi-Fi service of the cafe but the concern is safety. Have you ever felt this situation? Yes, of course accessing an unknown public wifi could be a risk to our online security. A Virtual Private Network (VPN) can help protect your data in such situations. VPNs encrypt your internet traffic, creating a secure tunnel between your device and the internet. This encryption makes it virtually impossible for hackers to steal your information.
There are so many paid, and non-paid VPN services available but weāre not gonna talk about those here. Instead, in this article, you will learn how to create a simple VPN server using Python. Weāll use the āsocketā and āsslā libraries to establish a secure connection between a client and a server.
Itās important to note that this is for educational purposes only, and these homemade VPNs may not offer the same level of security and features as commercial VPN services.
Recommended: Create a Simple Proxy Server Using Python
Requirements and Installations
Before you begin, make sure Python is installed on your system. You can download it from www.python.org. Next, Install the pyOpenSSL library for SSL/TTLS connections using the following command:
pip install pyopenssl
Set Up the Environment
Create a separate folder named āVPN_Serverāā and navigate to this directory.
Itās mandatory to generate SSL certificates to run the VPN server and client programs securely. So, open your terminal or command prompt and do the following step-by-step:
Generate a private key
openssl genpkey -algorithm RSA -out server.key -pkeyopt rsa_keygen_bits:4096
Create a certificate signing request (CSR)
Create a file named āserver.csr.cnfā with the following content:
[req] distinguished_name = req_distinguished_name req_extensions = req_ext prompt = no [req_distinguished_name] C = US ST = California L = San Francisco O = Example Company OU = IT CN = 127.0.0.1 [req_ext] subjectAltName = @alt_names [alt_names] IP.1 = 127.0.0.1
Then run the command:
openssl req -new -key server.key -out server.csr -config server.csr.cnf
Generate the self-signed certificate
Create a file named āserver.crt.cnfā with the following content:
[req] distinguished_name = req_distinguished_name x509_extensions = v3_req prompt = no [req_distinguished_name] C = US ST = California L = San Francisco O = Example Company OU = IT CN = 127.0.0.1 [v3_req] subjectAltName = @alt_names [alt_names] IP.1 = 127.0.0.1
Then run the command:
openssl x509 -req -in server.csr -signkey server.key -out server.crt -days 365 -extfile server.crt.cnf -extensions v3_req
The Program
We will create the VPN server and client programs separately.
vpn_server.py
import socket import ssl def start_vpn_server(host, port): context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) context.load_cert_chain(certfile="server.crt", keyfile="server.key") bindsocket = socket.socket() bindsocket.bind((host, port)) bindsocket.listen(5) print(f"VPN server listening on {host}:{port}") while True: newsocket, fromaddr = bindsocket.accept() print(f"Connection from {fromaddr}") conn = context.wrap_socket(newsocket, server_side=True) try: data = conn.recv(1024) print(f"Received: {data}") if data: conn.sendall(b'Hello, VPN Client!') except Exception as e: print(f"Error: {e}") finally: try: conn.shutdown(socket.SHUT_RDWR) except OSError as e: print(f"Error during shutdown: {e}") conn.close() if __name__ == "__main__": start_vpn_server('127.0.0.1', 8443)
Explanation
Letās break down the key parts of the above Python script:
start_vpn_server
function: This function takes two arguments: āhostā (the serverās IP address) and āportā (the port number the server listens on).context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
: This line creates an SSL context object. The āssl.Purpose.CLIENT_AUTHā argument specifies that the server expects client authentication (meaning the client will also present a certificate).context.load_cert_chain(certfile="server.crt", keyfile="server.key")
: This line loads the serverās SSL certificate (āserver.crtā) and private key (āserver.keyā) into the context object. These files are crucial for establishing a secure connection.bindsocket = socket.socket()
: This line creates a new TCP socket object (ābindsocketā) using the socket library.bindsocket.bind((host, port))
: This line binds the socket to the specified host and port. This tells the operating system to listen for incoming connections on that address and port combination.bindsocket.listen(5)
: This line sets the socket to listen for incoming connections. The 5 specifies the maximum number of pending connections that can be queued before the server starts refusing new connections.newsocket, fromaddr = bindsocket.accept()
: This line waits for a new connection and assigns the new socket object (ānewsocketā) and the clientās address (āfromaddrā) to variables.conn = context.wrap_socket(newsocket, server_side=True)
: This line wraps the new socket (ānewsocketā) in an SSL socket (āconnā) using the previously created SSL context. The āserver_side=Trueā argument specifies that this is a server-side connection.data = conn.recv(1024)
: This line receives data (up to 1024 bytes) from the client through the secure SSL connection (conn).conn.sendall(b'Hello, VPN Client!')
: If data was received, this line sends a simple message back to the client as a response.conn.shutdown(socket.SHUT_RDWR)
: This line shuts down both reading and writing on the secure connection.conn.close()
: This line closes the secure connection (conn).start_vpn_server('127.0.0.1', 8443)
: This line calls the āstart_vpn_serverā function with the serverās IP address (127.0.0.1) and port number (8443).
vpn_client.py
import socket import ssl def vpn_client(host, port): context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH) context.load_verify_locations("server.crt") raw_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) raw_socket.connect((host, port)) conn = context.wrap_socket(raw_socket, server_hostname=host) try: conn.send(b"Hello, VPN Server!") data = conn.recv(1024) print(f"Received from server: {data}") finally: conn.close() if __name__ == "__main__": vpn_client('127.0.0.1', 8443)
Explanation
Here is the breakdown of the key parts of the above Python script:
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
: This line creates an SSL context object (context). The āssl.Purpose.SERVER_AUTHā argument specifies that the client expects server authentication (meaning it will verify the serverās certificate).context.load_verify_locations("server.crt")
: This line attempts to load the serverās SSL certificate (āserver.crtā) into the context object. This certificate is used to verify the serverās identity for a secure connection.raw_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
: This line creates a new TCP socket object (āraw_socketā) using the āsocketā library. āsocket.AF_INETā specifies the address family (IPv4 in this case), and āsocket.SOCK_STREAMā specifies a stream socket for continuous data exchange.raw_socket.connect((host, port))
: This line attempts to connect the socket (āraw_socketā) to the server at the specified host and port.conn = context.wrap_socket(raw_socket, server_hostname=host)
: This line wraps the raw socket (āraw_socketā) in an SSL socket (āconnā) using the previously created SSL context (ācontextā). The āserver_hostnameā argument specifies the expected hostname of the server for certificate verification.
How to run the program?
Start the VPN Server
Open a terminal and navigate to the directory containing āvpn_server.pyā. Now run the server script:
python vpn_server.py
You should see a message indicating that the server is listening on 127.0.0.1:8443.
Run the VPN Client
Open another terminal and navigate to the directory containing āvpn_client.pyā. Now run the client script:
python vpn_client.py
The client should connect to the server, send a message, and print the response.
Output
When you run the server and client programs, you should see the following outputs:
Server Terminal Output
VPN server listening on 127.0.0.1:8443 Connection from ('127.0.0.1', 12345) Received: b'Hello, VPN Server!'
Client Terminal Output
Received from server: b'Hello, VPN Server!'
Recommended: Create a Python Network Scanner: Find IPs & MACs
Summary
In this tutorial, we built a simple VPN server using Python. We used the āsocketā and āsslā libraries to establish a secure connection between a client and a server. But before running the server and client scripts, itās mandatory to generate SSL certificates to establish the connection securely.
Every step described in this tutorial is important to set up the VPN server and client successfully. So, follow every detail carefully. Remember, this is a great starting point for further exploration into secure communications and network programming with Python.
For any query, reach out to me at contact@pyseek.com.
Happy Coding!