add support script for generating client file
This commit is contained in:
parent
9765095bab
commit
0fe85325fc
27
.gemini/settings.json
Normal file
27
.gemini/settings.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"sequentialthinking": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": [
|
||||||
|
"-y",
|
||||||
|
"@modelcontextprotocol/server-sequential-thinking"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"serena": {
|
||||||
|
"command": "uvx",
|
||||||
|
"args": [
|
||||||
|
"--from",
|
||||||
|
"git+https://github.com/oraios/serena",
|
||||||
|
"serena",
|
||||||
|
"start-mcp-server",
|
||||||
|
"--context",
|
||||||
|
"ide-assistant",
|
||||||
|
"--project",
|
||||||
|
"/Users/arthur/Desktop/code/vpn-access-server"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"context7": {
|
||||||
|
"url": "https://mcp.context7.com/sse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
.serena/.gitignore
vendored
Normal file
1
.serena/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/cache
|
||||||
71
.serena/project.yml
Normal file
71
.serena/project.yml
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# language of the project (csharp, python, rust, java, typescript, go, cpp, or ruby)
|
||||||
|
# * For C, use cpp
|
||||||
|
# * For JavaScript, use typescript
|
||||||
|
# Special requirements:
|
||||||
|
# * csharp: Requires the presence of a .sln file in the project folder.
|
||||||
|
language: python
|
||||||
|
|
||||||
|
# the encoding used by text files in the project
|
||||||
|
# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings
|
||||||
|
encoding: "utf-8"
|
||||||
|
|
||||||
|
# whether to use the project's gitignore file to ignore files
|
||||||
|
# Added on 2025-04-07
|
||||||
|
ignore_all_files_in_gitignore: true
|
||||||
|
# list of additional paths to ignore
|
||||||
|
# same syntax as gitignore, so you can use * and **
|
||||||
|
# Was previously called `ignored_dirs`, please update your config if you are using that.
|
||||||
|
# Added (renamed) on 2025-04-07
|
||||||
|
ignored_paths: []
|
||||||
|
|
||||||
|
# whether the project is in read-only mode
|
||||||
|
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
|
||||||
|
# Added on 2025-04-18
|
||||||
|
read_only: false
|
||||||
|
|
||||||
|
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
|
||||||
|
# Below is the complete list of tools for convenience.
|
||||||
|
# To make sure you have the latest list of tools, and to view their descriptions,
|
||||||
|
# execute `uv run scripts/print_tool_overview.py`.
|
||||||
|
#
|
||||||
|
# * `activate_project`: Activates a project by name.
|
||||||
|
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
|
||||||
|
# * `create_text_file`: Creates/overwrites a file in the project directory.
|
||||||
|
# * `delete_lines`: Deletes a range of lines within a file.
|
||||||
|
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
|
||||||
|
# * `execute_shell_command`: Executes a shell command.
|
||||||
|
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
|
||||||
|
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
|
||||||
|
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
|
||||||
|
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
|
||||||
|
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
|
||||||
|
# * `initial_instructions`: Gets the initial instructions for the current project.
|
||||||
|
# Should only be used in settings where the system prompt cannot be set,
|
||||||
|
# e.g. in clients you have no control over, like Claude Desktop.
|
||||||
|
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
|
||||||
|
# * `insert_at_line`: Inserts content at a given line in a file.
|
||||||
|
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
|
||||||
|
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
|
||||||
|
# * `list_memories`: Lists memories in Serena's project-specific memory store.
|
||||||
|
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
|
||||||
|
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
|
||||||
|
# * `read_file`: Reads a file within the project directory.
|
||||||
|
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
|
||||||
|
# * `remove_project`: Removes a project from the Serena configuration.
|
||||||
|
# * `replace_lines`: Replaces a range of lines within a file with new content.
|
||||||
|
# * `replace_symbol_body`: Replaces the full definition of a symbol.
|
||||||
|
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
|
||||||
|
# * `search_for_pattern`: Performs a search for a pattern in the project.
|
||||||
|
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
|
||||||
|
# * `switch_modes`: Activates modes by providing a list of their names
|
||||||
|
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
|
||||||
|
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
|
||||||
|
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
|
||||||
|
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
|
||||||
|
excluded_tools: []
|
||||||
|
|
||||||
|
# initial prompt for the project. It will always be given to the LLM upon activating the project
|
||||||
|
# (contrary to the memories, which are loaded on demand).
|
||||||
|
initial_prompt: ""
|
||||||
|
|
||||||
|
project_name: "vpn-access-server"
|
||||||
93
GEMINI.md
Normal file
93
GEMINI.md
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
# GEMINI.md
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
This project is a **VPN Access Server**, written in Python, designed to extend an OpenVPN server with custom authentication and session management features. It uses a MySQL database to store user information, authorized MAC addresses, and session data.
|
||||||
|
|
||||||
|
The core functionality is to:
|
||||||
|
1. **Validate MAC Addresses**: Ensure only devices with authorized MAC addresses can connect to the VPN.
|
||||||
|
2. **Enforce Session Time**: Manage and enforce session duration policies on a per-user basis.
|
||||||
|
|
||||||
|
The architecture is based on OpenVPN's `client-connect` and `client-disconnect` hooks, which execute Python scripts to perform the validation logic.
|
||||||
|
|
||||||
|
**Technologies:**
|
||||||
|
* **Language:** Python 3
|
||||||
|
* **Database:** MySQL
|
||||||
|
* **VPN:** OpenVPN (as the execution environment)
|
||||||
|
* **Dependencies:** `mysql-connector-python`, `pytest`
|
||||||
|
|
||||||
|
## Building and Running
|
||||||
|
|
||||||
|
The project is managed via a central command-line interface in `main.py`.
|
||||||
|
|
||||||
|
### Initial Setup
|
||||||
|
|
||||||
|
1. **Install Dependencies:**
|
||||||
|
```bash
|
||||||
|
uv pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
*(Note: Based on `pyproject.toml`, you might use `uv pip install .` or `uv pip install -e .` for editable mode)*
|
||||||
|
|
||||||
|
2. **Configure Environment:**
|
||||||
|
Create a `.env` file based on `.env.example` and provide the necessary database credentials:
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
# Edit .env with your DB settings
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Initialize the Database:**
|
||||||
|
This script will create the necessary tables (`employees`, `mac_addresses`, `sessions`) in the database.
|
||||||
|
```bash
|
||||||
|
python main.py init-db
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running the Application
|
||||||
|
|
||||||
|
The main application logic is triggered by OpenVPN. However, you can run the individual components for testing or health checks.
|
||||||
|
|
||||||
|
* **Run a Health Check:**
|
||||||
|
Verifies the configuration and database connection.
|
||||||
|
```bash
|
||||||
|
python main.py health-check
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Show Status:**
|
||||||
|
Displays the current configuration.
|
||||||
|
```bash
|
||||||
|
python main.py status
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Run Authentication/Session Logic (Manual Simulation):**
|
||||||
|
These commands are intended to be called by OpenVPN but can be run manually if the required environment variables are set.
|
||||||
|
```bash
|
||||||
|
# Simulate OpenVPN client-connect
|
||||||
|
python main.py auth
|
||||||
|
|
||||||
|
# Simulate OpenVPN client-disconnect
|
||||||
|
python main.py session
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running Tests
|
||||||
|
|
||||||
|
The project uses `pytest` for testing.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python main.py test
|
||||||
|
```
|
||||||
|
or
|
||||||
|
```bash
|
||||||
|
pytest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development Conventions
|
||||||
|
|
||||||
|
* **Modular Architecture:** The code is structured into modules with clear responsibilities:
|
||||||
|
* `access/auth.py`: User authentication and MAC validation.
|
||||||
|
* `access/session.py`: Session management.
|
||||||
|
* `access/db.py`: Database interaction.
|
||||||
|
* `access/config.py`: Configuration loading.
|
||||||
|
* `access/utils.py`: Shared utility functions.
|
||||||
|
* **Configuration:** Configuration is loaded from environment variables, following the 12-factor app methodology. A `.env` file is used for local development.
|
||||||
|
* **Database Management:** Database connections are managed through a connection pool in `db.py` for efficiency. All SQL queries are centralized in this file.
|
||||||
|
* **CLI:** A single entry point (`main.py`) provides a consistent way to interact with and manage the application.
|
||||||
|
* **Testing:** Unit tests are located in the `tests/` directory and can be run with `pytest`.
|
||||||
102
docs/account-creation.md
Normal file
102
docs/account-creation.md
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
1. run this command to generated rquired client file:
|
||||||
|
```commandline
|
||||||
|
cd /etc/openvpn/easy-rsa/
|
||||||
|
./easyrsa build-client-full <username> nopass
|
||||||
|
```
|
||||||
|
# example create test client key for user name: [arthur]
|
||||||
|
arthur@server:~/openvpn-ca$ ./easyrsa gen-req arthur nopass
|
||||||
|
Using Easy-RSA 'vars' configuration:
|
||||||
|
* /home/arthur/openvpn-ca/vars
|
||||||
|
|
||||||
|
Using SSL:
|
||||||
|
* openssl OpenSSL 3.0.13 30 Jan 2024 (Library: OpenSSL 3.0.13 30 Jan 2024)
|
||||||
|
....+...+...+..+.......+.....+......+...+.......+.....+...+..........+.....+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*..+....+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*.+...........+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
..+...............+...+.....+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*.+............+......+.......+.....+...+....+..+......+.......+......+......+......+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*...+..+......+....+..................+...+.....+.......+...+......+.....+....+.........+......+.....+....+..+.+........+......+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
-----
|
||||||
|
You are about to be asked to enter information that will be incorporated
|
||||||
|
into your certificate request.
|
||||||
|
What you are about to enter is what is called a Distinguished Name or a DN.
|
||||||
|
There are quite a few fields but you can leave some blank
|
||||||
|
For some fields there will be a default value,
|
||||||
|
If you enter '.', the field will be left blank.
|
||||||
|
-----
|
||||||
|
Common Name (eg: your user, host, or server name) [arthur]:
|
||||||
|
|
||||||
|
Notice
|
||||||
|
------
|
||||||
|
Private-Key and Public-Certificate-Request files created.
|
||||||
|
Your files are:
|
||||||
|
* req: /home/arthur/openvpn-ca/pki/reqs/arthur.req
|
||||||
|
* key: /home/arthur/openvpn-ca/pki/private/arthur.key
|
||||||
|
|
||||||
|
arthur@server:~/openvpn-ca$ ./easyrsa sign-req client arthur
|
||||||
|
Using Easy-RSA 'vars' configuration:
|
||||||
|
* /home/arthur/openvpn-ca/vars
|
||||||
|
|
||||||
|
Using SSL:
|
||||||
|
* openssl OpenSSL 3.0.13 30 Jan 2024 (Library: OpenSSL 3.0.13 30 Jan 2024)
|
||||||
|
You are about to sign the following certificate:
|
||||||
|
Please check over the details shown below for accuracy. Note that this request
|
||||||
|
has not been cryptographically verified. Please be sure it came from a trusted
|
||||||
|
source or that you have verified the request checksum with the sender.
|
||||||
|
Request subject, to be signed as a client certificate
|
||||||
|
for '825' days:
|
||||||
|
|
||||||
|
subject=
|
||||||
|
commonName = arthur
|
||||||
|
|
||||||
|
Type the word 'yes' to continue, or any other input to abort.
|
||||||
|
Confirm request details: yes
|
||||||
|
|
||||||
|
Using configuration from /home/arthur/openvpn-ca/pki/openssl-easyrsa.cnf
|
||||||
|
Check that the request matches the signature
|
||||||
|
Signature ok
|
||||||
|
The Subject's Distinguished Name is as follows
|
||||||
|
commonName :ASN.1 12:'arthur'
|
||||||
|
Certificate is to be certified until Dec 19 04:09:41 2027 GMT (825 days)
|
||||||
|
|
||||||
|
Write out database with 1 new entries
|
||||||
|
Database updated
|
||||||
|
|
||||||
|
Notice
|
||||||
|
------
|
||||||
|
Certificate created at:
|
||||||
|
* /home/arthur/openvpn-ca/pki/issued/arthur.crt
|
||||||
|
|
||||||
|
|
||||||
|
2. OpenVPN Server Configuration\
|
||||||
|
```commandline
|
||||||
|
|
||||||
|
### Check location of key before copy
|
||||||
|
------
|
||||||
|
CA creation complete. Your new CA certificate is at:
|
||||||
|
* /home/arthur/openvpn-ca/pki/ca.crt
|
||||||
|
------
|
||||||
|
Certificate created at:
|
||||||
|
* /home/arthur/openvpn-ca/pki/issued/server.crt
|
||||||
|
------
|
||||||
|
Private-Key and Public-Certificate-Request files created.
|
||||||
|
Your files are:
|
||||||
|
* req: /home/arthur/openvpn-ca/pki/reqs/server.req
|
||||||
|
* key: /home/arthur/openvpn-ca/pki/private/server.key
|
||||||
|
------
|
||||||
|
DH parameters of size 2048 created at:
|
||||||
|
* /home/arthur/openvpn-ca/pki/dh.pem
|
||||||
|
------
|
||||||
|
TA Key at:
|
||||||
|
* /home/arthur/openvpn-ca/ta.key
|
||||||
|
|
||||||
|
### Start copying requirement files
|
||||||
|
arthur@server:~/openvpn-ca$ sudo cp pki/ca.crt pki/issued/server.crt pki/private/server.key ta.key pki/dh.pem /etc/openvpn/server/
|
||||||
|
|
||||||
|
### Check the destination again
|
||||||
|
arthur@server:~/openvpn-ca$ ls -la /etc/openvpn/server/
|
||||||
|
total 32
|
||||||
|
drwxr-xr-x 2 root root 4096 Sep 15 04:17 .
|
||||||
|
drwxr-xr-x 4 root root 4096 Sep 15 03:39 ..
|
||||||
|
-rw------- 1 root root 1245 Sep 15 04:17 ca.crt
|
||||||
|
-rw------- 1 root root 424 Sep 15 04:17 dh.pem
|
||||||
|
-rw------- 1 root root 4728 Sep 15 04:17 server.crt
|
||||||
|
-rw------- 1 root root 1708 Sep 15 04:17 server.key
|
||||||
|
-rw------- 1 root root 636 Sep 15 04:17 ta.key
|
||||||
|
```
|
||||||
30
main.py
30
main.py
@ -104,6 +104,14 @@ def show_status():
|
|||||||
print(f"Max Session Limit: {config.server.max_session_limit}s ({config.server.max_session_limit//3600}h)")
|
print(f"Max Session Limit: {config.server.max_session_limit}s ({config.server.max_session_limit//3600}h)")
|
||||||
print("=" * 50)
|
print("=" * 50)
|
||||||
|
|
||||||
|
from util.client.generate_client import generate_client_config
|
||||||
|
|
||||||
|
|
||||||
|
def run_gen_client(username: str):
|
||||||
|
"""Generate a client .ovpn file."""
|
||||||
|
generate_client_config(username)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Main entry point with command-line interface."""
|
"""Main entry point with command-line interface."""
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
@ -118,6 +126,7 @@ Examples:
|
|||||||
%(prog)s test # Run unit tests
|
%(prog)s test # Run unit tests
|
||||||
%(prog)s health-check # Check system health
|
%(prog)s health-check # Check system health
|
||||||
%(prog)s status # Show configuration status
|
%(prog)s status # Show configuration status
|
||||||
|
%(prog)s gen-client <user> # Generate a client .ovpn file
|
||||||
|
|
||||||
Environment Variables:
|
Environment Variables:
|
||||||
DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD
|
DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD
|
||||||
@ -127,11 +136,18 @@ See .env.example for full configuration options.
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
subparsers = parser.add_subparsers(dest='command', help='Commands')
|
||||||
'command',
|
|
||||||
choices=['auth', 'session', 'init-db', 'seed-data', 'test', 'health-check', 'status'],
|
subparsers.add_parser('auth', help='Run authentication (for OpenVPN)')
|
||||||
help='Command to execute'
|
subparsers.add_parser('session', help='Run session management (for OpenVPN)')
|
||||||
)
|
subparsers.add_parser('init-db', help='Initialize database schema')
|
||||||
|
subparsers.add_parser('seed-data', help='Add sample data for testing')
|
||||||
|
subparsers.add_parser('test', help='Run unit tests')
|
||||||
|
subparsers.add_parser('health-check', help='Check system health')
|
||||||
|
subparsers.add_parser('status', help='Show configuration status')
|
||||||
|
|
||||||
|
gen_client_parser = subparsers.add_parser('gen-client', help='Generate a client .ovpn file')
|
||||||
|
gen_client_parser.add_argument('username', help='Username for the client config')
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--version',
|
'--version',
|
||||||
@ -139,7 +155,7 @@ See .env.example for full configuration options.
|
|||||||
version='VPN Access Server 1.0.0'
|
version='VPN Access Server 1.0.0'
|
||||||
)
|
)
|
||||||
|
|
||||||
if len(sys.argv) == 1:
|
if len(sys.argv) < 2:
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@ -161,6 +177,8 @@ See .env.example for full configuration options.
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
elif args.command == 'status':
|
elif args.command == 'status':
|
||||||
show_status()
|
show_status()
|
||||||
|
elif args.command == 'gen-client':
|
||||||
|
run_gen_client(args.username)
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("\n⚠️ Operation cancelled by user")
|
print("\n⚠️ Operation cancelled by user")
|
||||||
|
|||||||
69
scripts/gen-client.sh
Executable file
69
scripts/gen-client.sh
Executable file
@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ $# -ne 4 ]]; then
|
||||||
|
cat >&2 <<USAGE
|
||||||
|
Usage: $0 /path/to/ca.crt /path/to/clientname.crt /path/to/clientname.key /path/to/ta.key
|
||||||
|
Example: ./gen-client.sh /etc/openvpn/ca.crt ./client1.crt ./client1.key ./ta.key
|
||||||
|
This writes output to stdout and also saves to ./<clientname>.ovpn
|
||||||
|
USAGE
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ca="$1"
|
||||||
|
cert="$2"
|
||||||
|
key="$3"
|
||||||
|
ta="$4"
|
||||||
|
|
||||||
|
# verify files exist and are readable
|
||||||
|
for f in "$ca" "$cert" "$key" "$ta"; do
|
||||||
|
if [[ ! -r "$f" ]]; then
|
||||||
|
echo "Error: cannot read file '$f'." >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# derive client name from certificate filename (remove extension)
|
||||||
|
clientname="$(basename "$cert")"
|
||||||
|
clientname="${clientname%.*}"
|
||||||
|
outfile="${clientname}.ovpn"
|
||||||
|
|
||||||
|
# build and write config (also send to stdout). Use a block to avoid command-substitution problems with large files.
|
||||||
|
{
|
||||||
|
cat <<'HEADER'
|
||||||
|
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
|
||||||
|
|
||||||
|
HEADER
|
||||||
|
|
||||||
|
echo "<ca>"
|
||||||
|
cat "$ca"
|
||||||
|
echo "</ca>"
|
||||||
|
echo
|
||||||
|
echo "<cert>"
|
||||||
|
cat "$cert"
|
||||||
|
echo "</cert>"
|
||||||
|
echo
|
||||||
|
echo "<key>"
|
||||||
|
cat "$key"
|
||||||
|
echo "</key>"
|
||||||
|
echo
|
||||||
|
echo "<tls-auth>"
|
||||||
|
cat "$ta"
|
||||||
|
echo "</tls-auth>"
|
||||||
|
echo "key-direction 1"
|
||||||
|
} | tee "$outfile"
|
||||||
|
|
||||||
|
echo "Wrote config to ./${outfile}"
|
||||||
|
|
||||||
BIN
user-data/data.xlsx
Normal file
BIN
user-data/data.xlsx
Normal file
Binary file not shown.
89
util/client/generate_client.py
Normal file
89
util/client/generate_client.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
"""
|
||||||
|
Utility for generating OpenVPN client configuration files.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
def generate_client_config(username: str):
|
||||||
|
"""
|
||||||
|
Generates a .ovpn file for a given user.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
username: The username for which to generate the config.
|
||||||
|
"""
|
||||||
|
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"/etc/openvpn/client/{username}.ovpn"
|
||||||
|
|
||||||
|
# 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):
|
||||||
|
print(f"Error: Cannot read file '{f}'.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
print(f"Error reading files: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 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>
|
||||||
|
{ca_content}
|
||||||
|
</ca>
|
||||||
|
|
||||||
|
<cert>
|
||||||
|
{client_crt_content}
|
||||||
|
</cert>
|
||||||
|
|
||||||
|
<key>
|
||||||
|
{client_key_content}
|
||||||
|
</key>
|
||||||
|
|
||||||
|
<tls-auth>
|
||||||
|
{ta_content}
|
||||||
|
</tls-auth>
|
||||||
|
key-direction 1
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Write the configuration to the output file
|
||||||
|
try:
|
||||||
|
# Ensure the output directory exists
|
||||||
|
output_dir = os.path.dirname(output_path)
|
||||||
|
if not os.path.exists(output_dir):
|
||||||
|
# This part is tricky because of permissions.
|
||||||
|
# For now, we assume the directory exists.
|
||||||
|
# On a real server, this would be handled by deployment scripts.
|
||||||
|
pass
|
||||||
|
|
||||||
|
with open(output_path, 'w') as f:
|
||||||
|
f.write(ovpn_config)
|
||||||
|
print(f"Successfully generated client config: {output_path}")
|
||||||
|
except IOError as e:
|
||||||
|
print(f"Error writing to file: {e}")
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user