How to Programmatically Generate and Host OpenVPN Client Configuration Profiles for Seamless User Import


2 views

Setting up OpenVPN clients traditionally involves multiple manual steps that create friction for end users. The standard workflow requires:

  • Downloading a zip archive containing configuration and certificate files
  • Extracting files to the correct filesystem location
  • Manually editing .ovpn files to adjust paths and settings

We can streamline this process by programmatically generating and hosting ready-to-use OpenVPN profiles. Here's a Python implementation using Flask:


from flask import Flask, send_file
import os
import zipfile
import io

app = Flask(__name__)

@app.route('/generate-profile/<username>')
def generate_profile(username):
    # Create in-memory zip file
    memory_file = io.BytesIO()
    
    with zipfile.ZipFile(memory_file, 'w') as zf:
        # Add client certificate
        zf.writestr(f'{username}.crt', get_client_cert(username))
        
        # Add client key
        zf.writestr(f'{username}.key', get_client_key(username))
        
        # Generate and add .ovpn config
        config = generate_ovpn_config(username)
        zf.writestr(f'{username}.ovpn', config)
    
    memory_file.seek(0)
    return send_file(
        memory_file,
        mimetype='application/zip',
        as_attachment=True,
        download_name=f'{username}_openvpn_profile.zip'
    )

def generate_ovpn_config(username):
    return f'''client
dev tun
proto udp
remote vpn.example.com 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
cipher AES-256-CBC
verb 3
key-direction 1
<ca>
{get_ca_cert()}
</ca>
<cert>
{get_client_cert(username)}
</cert>
<key>
{get_client_key(username)}
</key>
'''

For even simpler deployment, we can embed all certificates directly in the .ovpn file:


def generate_all_in_one_ovpn(username):
    config = f'''client
dev tun
proto udp
remote vpn.example.com 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
cipher AES-256-CBC
verb 3

<ca>
{get_ca_cert()}
</ca>

<cert>
{get_client_cert(username)}
</cert>

<key>
{get_client_key(username)}
</key>

tls-auth ta.key 1
<tls-auth>
{get_tls_auth_key()}
</tls-auth>
'''
    return config

When implementing this solution, consider these security aspects:

  • Implement proper authentication before generating profiles
  • Use HTTPS for all connections
  • Set appropriate cache-control headers
  • Rotate certificates periodically

For enterprise deployments, you might want to integrate with existing authentication systems like LDAP or OAuth.

Here's how users can programmatically fetch and apply the configuration on Linux:


#!/bin/bash

# Download and install OpenVPN profile
curl -u username:password https://vpn.example.com/generate-profile/$USERNAME -o profile.zip
unzip profile.zip -d /etc/openvpn/client/
systemctl restart openvpn-client@$USERNAME

Distributing OpenVPN configurations shouldn't require users to manually handle certificate files and configuration tweaks. The ideal solution should generate complete, ready-to-use .ovpn profiles that include all necessary certificates and keys inline.

Here's a Python script that generates client configurations dynamically:


#!/usr/bin/env python3
import os
from configparser import ConfigParser

def generate_ovpn_profile(client_name, server_config):
    """Generate a complete OpenVPN client profile with inline certificates"""
    template = f"""client
dev tun
proto {server_config['proto']}
remote {server_config['host']} {server_config['port']}
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
cipher AES-256-CBC
verb 3

<ca>
{open(server_config['ca_path']).read()}
</ca>
<cert>
{open(f"pki/issued/{client_name}.crt").read()}
</cert>
<key>
{open(f"pki/private/{client_name}.key").read()}
</key>
"""
    return template

Create a simple Flask endpoint to serve generated profiles:


from flask import Flask, make_response
app = Flask(__name__)

@app.route('/download/vpn-profile/<username>')
def download_profile(username):
    profile = generate_ovpn_profile(username, server_config)
    response = make_response(profile)
    response.headers['Content-Type'] = 'application/x-openvpn-profile'
    response.headers['Content-Disposition'] = f'attachment; filename={username}.ovpn'
    return response

When implementing automated profile generation:

  • Store CA and server certificates outside web root
  • Implement proper authentication before profile download
  • Set appropriate file permissions (600 for private keys)
  • Consider certificate expiration and revocation

For different operating systems, provide these instructions:


# Linux example
curl -u USERNAME https://vpn.example.com/download/vpn-profile/$USER > ~/client.ovpn
sudo mv ~/client.ovpn /etc/openvpn/client/
sudo systemctl start openvpn-client@client

For Windows users, create a PowerShell script that:


$client = New-Object System.Net.WebClient
$client.Credentials = New-Object System.Net.NetworkCredential($env:USERNAME, (Read-Host -AsSecureString))
$client.DownloadFile("https://vpn.example.com/download/vpn-profile/$env:USERNAME", "$env:USERPROFILE\OpenVPN\config\client.ovpn")