"""
Utility for generating OpenVPN client configuration files.
"""
import os
import subprocess
from typing import Tuple
def generate_client_config(username: str, email: str) -> Tuple[bool, str]:
"""
Generates a .ovpn file for a given user.
Args:
username: The username for which to generate the config.
email: The email for which to generate the config and used as password
Returns:
A tuple containing a boolean indicating success and a message.
"""
easyrsa_dir = "/home/arthur/openvpn-ca/"
ca_path = "/home/arthur/openvpn-ca/pki/ca.crt"
ta_path = "/home/arthur/openvpn-ca/ta.key"
client_crt_path = f"/home/arthur/openvpn-ca/pki/issued/{username}.crt"
client_key_path = f"/home/arthur/openvpn-ca/pki/private/{username}.key"
output_path = f"generated-clients/{username}.ovpn"
# config password into env
password = email
env = os.environ.copy()
env["EASYRSA_PASSOUT"] = f"pass:{password}"
# Step 1: Generate the client certificate
try:
command = ["./easyrsa", "--batch", "build-client-full", username]
process = subprocess.run(
command,
cwd=easyrsa_dir,
env=env,
check=True,
capture_output=True,
text=True
)
except FileNotFoundError:
return False, f"Error: 'easyrsa' script not found in {easyrsa_dir}. Please check the path."
except subprocess.CalledProcessError as e:
return False, f"Error generating certificate for user: {username}. Stderr: {e.stderr}"
# Step 2: Verify that all required files exist
for f in [ca_path, ta_path, client_crt_path, client_key_path]:
if not os.path.isfile(f):
return False, f"Error: Cannot read file '{f}'. File not found after generation."
# Step 3: Read the content of the files
try:
with open(ca_path, 'r') as f:
ca_content = f.read()
with open(client_crt_path, 'r') as f:
client_crt_content = f.read()
with open(client_key_path, 'r') as f:
client_key_content = f.read()
with open(ta_path, 'r') as f:
ta_content = f.read()
except IOError as e:
return False, f"Error reading files: {e}"
# Step 4: Assemble the .ovpn configuration
ovpn_config = f"""
client
dev tun
proto udp
remote 14.241.240.102 1194 # use FTP IP address
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
cipher AES-256-GCM
# push mac address info
push-peer-info
verb 3
{ca_content}
{client_crt_content}
{client_key_content}
{ta_content}
key-direction 1
"""
# Step 5: Write the configuration to the output file
try:
output_dir = os.path.dirname(output_path)
if not os.path.isdir(output_dir) or not os.access(output_dir, os.W_OK):
local_output_dir = "generated-clients"
if not os.path.exists(local_output_dir):
os.makedirs(local_output_dir)
local_output_path = os.path.join(local_output_dir, f"{username}.ovpn")
with open(local_output_path, 'w') as f:
f.write(ovpn_config)
return True, f"Successfully generated client config. Could not write to server path. Saved config locally to: {local_output_path}"
with open(output_path, 'w') as f:
f.write(ovpn_config)
return True, f"Successfully generated client config: {output_path}"
except IOError as e:
return False, f"Error writing to file: {e}"