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("=" * 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():
|
||||
"""Main entry point with command-line interface."""
|
||||
parser = argparse.ArgumentParser(
|
||||
@ -118,6 +126,7 @@ Examples:
|
||||
%(prog)s test # Run unit tests
|
||||
%(prog)s health-check # Check system health
|
||||
%(prog)s status # Show configuration status
|
||||
%(prog)s gen-client <user> # Generate a client .ovpn file
|
||||
|
||||
Environment Variables:
|
||||
DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD
|
||||
@ -127,11 +136,18 @@ See .env.example for full configuration options.
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'command',
|
||||
choices=['auth', 'session', 'init-db', 'seed-data', 'test', 'health-check', 'status'],
|
||||
help='Command to execute'
|
||||
)
|
||||
subparsers = parser.add_subparsers(dest='command', help='Commands')
|
||||
|
||||
subparsers.add_parser('auth', help='Run authentication (for OpenVPN)')
|
||||
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(
|
||||
'--version',
|
||||
@ -139,7 +155,7 @@ See .env.example for full configuration options.
|
||||
version='VPN Access Server 1.0.0'
|
||||
)
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
if len(sys.argv) < 2:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
@ -161,6 +177,8 @@ See .env.example for full configuration options.
|
||||
sys.exit(1)
|
||||
elif args.command == 'status':
|
||||
show_status()
|
||||
elif args.command == 'gen-client':
|
||||
run_gen_client(args.username)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
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