Skip to content

Instantly share code, notes, and snippets.

@FaberfoX
Last active June 25, 2026 15:16
Show Gist options
  • Select an option

  • Save FaberfoX/a7b8836956fe952e6972070ce8cf8000 to your computer and use it in GitHub Desktop.

Select an option

Save FaberfoX/a7b8836956fe952e6972070ce8cf8000 to your computer and use it in GitHub Desktop.
Python script to convert a wireguard .conf to the native ChromeOS crosh format

WireGuard .conf to ChromeOS crosh Converter

ChromeOS has native WireGuard support, but the built-in terminal (crosh) cannot directly import .conf files. Furthermore, crosh lacks common shell piping/utilities (like echo) and rejects multi-line pasting or whitespace spaces in arguments like allowed-ips.

This script parses a standard WireGuard configuration file and generates individual, copy-pasteable command blocks optimized for the strict crosh shell environment.

Usage

Run the script inside your Linux development environment (Crostini):

python wg_to_crosh.py <path_to_file.conf> [optional_custom_name]
  • If no custom name is passed, the script automatically uses the filename (e.g., Stark.conf builds a profile named Stark).

How to Paste into Crosh

Because crosh treats multi-line pastes as single-line arguments and handles key inputs via interactive hidden fields, you must copy and paste the commands one single line at a time:

  1. Open crosh in ChromeOS by pressing Ctrl + Alt + T.
  2. Step 1: Triple-click each command line individually, paste it into crosh (Ctrl + Shift + V), and press Enter.
  3. Step 2 (Private Key):
    • Triple-click and execute the wireguard set ... private-key command.
    • crosh will halt and show a hidden Enter private key: prompt.
    • Triple-click the raw key string printed directly below it by the script, paste it into the prompt, and press Enter.
  4. Step 3: Triple-click and run each peer configuration command.
  5. Step 4 (Preshared Key): If your provider (like wg-easy) requires a PSK, execute the command line, then copy and paste the raw key string directly into the hidden crosh prompt.

Verifying and Connecting

Once all steps are finished, verify your connection inside crosh:

wireguard list

The profile will now natively show up in your ChromeOS System Settings -> Network -> VPN section. Click Connect to initialize the tunnel!

import sys
import os
import re
def parse_wg_to_crosh(conf_path, vpn_name=None):
if not os.path.exists(conf_path):
print(f"Error: File '{conf_path}' not found.", file=sys.stderr)
return
# Fix: Extract element [0] from the splitext tuple to pass a string to re.sub
if not vpn_name:
base_name = os.path.splitext(os.path.basename(conf_path))
vpn_name = re.sub(r'[^a-zA-Z0-9_]', '_', base_name[0])
interface_data = {}
peer_data = {}
current_section = None
with open(conf_path, 'r') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#') or line.startswith(';'):
continue
if line.startswith('[') and line.endswith(']'):
current_section = line[1:-1].strip().lower()
continue
if '=' in line:
key, val = line.split('=', 1)
key = key.strip().lower()
val = val.strip()
# Split any inline comments at the end of the line safely
if '#' in val:
val = val.split('#')[0].strip()
if ';' in val:
val = val.split(';')[0].strip()
if current_section == 'interface':
interface_data[key] = val
elif current_section == 'peer':
peer_data[key] = val
# Clean local client IP address (strip subnet slashes)
raw_ip = interface_data.get('address', '')
local_ip = ''
if raw_ip:
first_ip = raw_ip.split(',')[0].strip()
local_ip = first_ip.split('/')[0].strip()
private_key = interface_data.get('privatekey', '')
dns_servers = interface_data.get('dns', '')
mtu = interface_data.get('mtu', '')
peer_public = peer_data.get('publickey', '')
allowed_ips = peer_data.get('allowedips', '')
endpoint = peer_data.get('endpoint', '')
keepalive = peer_data.get('persistentkeepalive', '')
preshared_key = peer_data.get('presharedkey', '')
# Strip all spaces out of allowed-ips so crosh interprets it as a single token
if allowed_ips:
allowed_ips = allowed_ips.replace(' ', '')
print(f"\n==================================================================")
print(f" TRIPLE-CLICK EACH LINE INDIVIDUALLY AND PASTE INTO CROSH ")
print(f"==================================================================")
print(f"\n# --- STEP 1: INITIALIZE PROFILE ---")
print(f"wireguard new {vpn_name}")
if local_ip:
print(f"wireguard set {vpn_name} local-ip {local_ip}")
if dns_servers:
print(f"wireguard set {vpn_name} dns {dns_servers}")
if mtu:
print(f"wireguard set {vpn_name} mtu {mtu}")
print(f"\n# --- STEP 2: CLIENT PRIVATE KEY ---")
if private_key:
print(f"wireguard set {vpn_name} private-key")
print(f"{private_key}")
else:
print(f"# Warning: No PrivateKey found under [Interface]")
print(f"\n# --- STEP 3: PEER INFRASTRUCTURE SETUP ---")
if peer_public:
print(f"wireguard set {vpn_name} peer {peer_public}")
if endpoint and peer_public:
print(f"wireguard set {vpn_name} peer {peer_public} endpoint {endpoint}")
if allowed_ips and peer_public:
print(f"wireguard set {vpn_name} peer {peer_public} allowed-ips {allowed_ips}")
if keepalive and peer_public:
print(f"wireguard set {vpn_name} peer {peer_public} persistent-keepalive {keepalive}")
print(f"\n# --- STEP 4: INTERACTIVE PRESHARED KEY ---")
if preshared_key and peer_public:
print(f"wireguard set {vpn_name} peer {peer_public} preshared-key")
print(f"{preshared_key}")
else:
print(f"# Note: No PresharedKey active or discovered in this file configuration.")
print(f"==================================================================\n")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python wg_to_crosh.py <path_to_file.conf> [custom_connection_name]")
sys.exit(1)
file_arg = sys.argv[1]
name_arg = sys.argv[2] if len(sys.argv) > 2 else None
parse_wg_to_crosh(file_arg, name_arg)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment