diff --git a/API.md b/API.md new file mode 100644 index 0000000..4e20f50 --- /dev/null +++ b/API.md @@ -0,0 +1,306 @@ +## HTTP API + +Olm can be controlled with an embedded HTTP server when using `--enable-http`. This allows you to start it as a daemon and trigger it with the following endpoints. The API can listen on either a TCP address or a Unix socket/Windows named pipe. + +### Socket vs TCP + +By default, when `--enable-http` is used, Olm listens on a TCP address (configured via `--http-addr`, default `:9452`). Alternatively, Olm can listen on a Unix socket (Linux/macOS) or Windows named pipe for local-only communication with better security. + +**Unix Socket (Linux/macOS):** +- Socket path example: `/var/run/olm/olm.sock` +- The directory is created automatically if it doesn't exist +- Socket permissions are set to `0666` to allow access +- Existing socket files are automatically removed on startup +- Socket file is cleaned up when Olm stops + +**Windows Named Pipe:** +- Pipe path example: `\\.\pipe\olm` +- If the path doesn't start with `\`, it's automatically prefixed with `\\.\pipe\` +- Security descriptor grants full access to Everyone and the current owner +- Named pipes are automatically cleaned up by Windows + +**Connecting to the Socket:** + +```bash +# Linux/macOS - using curl with Unix socket +curl --unix-socket /var/run/olm/olm.sock http://localhost/status + +--- + +### POST /connect +Initiates a new connection request to a Pangolin server. + +**Request Body:** +```json +{ + "id": "string", + "secret": "string", + "endpoint": "string", + "userToken": "string", + "mtu": 1280, + "dns": "8.8.8.8", + "dnsProxyIP": "string", + "upstreamDNS": ["8.8.8.8:53", "1.1.1.1:53"], + "interfaceName": "olm", + "holepunch": false, + "tlsClientCert": "string", + "pingInterval": "3s", + "pingTimeout": "5s", + "orgId": "string" +} +``` + +**Required Fields:** +- `id`: Olm ID generated by Pangolin +- `secret`: Authentication secret for the Olm ID +- `endpoint`: Target Pangolin endpoint URL + +**Optional Fields:** +- `userToken`: User authentication token +- `mtu`: MTU for the internal WireGuard interface (default: 1280) +- `dns`: DNS server to use for resolving the endpoint +- `dnsProxyIP`: DNS proxy IP address +- `upstreamDNS`: Array of upstream DNS servers +- `interfaceName`: Name of the WireGuard interface (default: olm) +- `holepunch`: Enable NAT hole punching (default: false) +- `tlsClientCert`: TLS client certificate +- `pingInterval`: Interval for pinging the server (default: 3s) +- `pingTimeout`: Timeout for each ping (default: 5s) +- `orgId`: Organization ID to connect to + +**Response:** +- **Status Code:** `202 Accepted` +- **Content-Type:** `application/json` + +```json +{ + "status": "connection request accepted" +} +``` + +**Error Responses:** +- `405 Method Not Allowed` - Non-POST requests +- `400 Bad Request` - Invalid JSON or missing required fields +- `409 Conflict` - Already connected to a server (disconnect first) + +--- + +### GET /status +Returns the current connection status, registration state, and peer information. + +**Response:** +- **Status Code:** `200 OK` +- **Content-Type:** `application/json` + +```json +{ + "connected": true, + "registered": true, + "terminated": false, + "version": "1.0.0", + "agent": "olm", + "orgId": "org_123", + "peers": { + "10": { + "siteId": 10, + "name": "Site A", + "connected": true, + "rtt": 145338339, + "lastSeen": "2025-08-13T14:39:17.208334428-07:00", + "endpoint": "p.fosrl.io:21820", + "isRelay": true, + "peerAddress": "100.89.128.5", + "holepunchConnected": false + }, + "8": { + "siteId": 8, + "name": "Site B", + "connected": false, + "rtt": 0, + "lastSeen": "2025-08-13T14:39:19.663823645-07:00", + "endpoint": "p.fosrl.io:21820", + "isRelay": true, + "peerAddress": "100.89.128.10", + "holepunchConnected": false + } + }, + "networkSettings": { + "tunnelIP": "100.89.128.3/20" + } +} +``` + +**Fields:** +- `connected`: Boolean indicating if connected to Pangolin +- `registered`: Boolean indicating if registered with the server +- `terminated`: Boolean indicating if the connection was terminated +- `version`: Olm version string +- `agent`: Agent identifier +- `orgId`: Current organization ID +- `peers`: Map of peer statuses by site ID + - `siteId`: Peer site identifier + - `name`: Site name + - `connected`: Boolean peer connection state + - `rtt`: Peer round-trip time (integer, nanoseconds) + - `lastSeen`: Last time peer was seen (RFC3339 timestamp) + - `endpoint`: Peer endpoint address + - `isRelay`: Whether the peer is relayed (true) or direct (false) + - `peerAddress`: Peer's IP address in the tunnel + - `holepunchConnected`: Whether holepunch connection is established +- `networkSettings`: Current network configuration including tunnel IP + +**Error Responses:** +- `405 Method Not Allowed` - Non-GET requests + +--- + +### POST /disconnect +Disconnects from the current Pangolin server and tears down the WireGuard tunnel. + +**Request Body:** None required + +**Response:** +- **Status Code:** `200 OK` +- **Content-Type:** `application/json` + +```json +{ + "status": "disconnect initiated" +} +``` + +**Error Responses:** +- `405 Method Not Allowed` - Non-POST requests +- `409 Conflict` - Not currently connected to a server + +--- + +### POST /switch-org +Switches to a different organization while maintaining the connection. + +**Request Body:** +```json +{ + "orgId": "string" +} +``` + +**Required Fields:** +- `orgId`: The organization ID to switch to + +**Response:** +- **Status Code:** `200 OK` +- **Content-Type:** `application/json` + +```json +{ + "status": "org switch request accepted" +} +``` + +**Error Responses:** +- `405 Method Not Allowed` - Non-POST requests +- `400 Bad Request` - Invalid JSON or missing orgId field +- `500 Internal Server Error` - Org switch failed + +--- + +### POST /exit +Initiates a graceful shutdown of the Olm process. + +**Request Body:** None required + +**Response:** +- **Status Code:** `200 OK` +- **Content-Type:** `application/json` + +```json +{ + "status": "shutdown initiated" +} +``` + +**Note:** The response is sent before shutdown begins. There is a 100ms delay before the actual shutdown to ensure the response is delivered. + +**Error Responses:** +- `405 Method Not Allowed` - Non-POST requests + +--- + +### GET /health +Simple health check endpoint to verify the API server is running. + +**Response:** +- **Status Code:** `200 OK` +- **Content-Type:** `application/json` + +```json +{ + "status": "ok" +} +``` + +**Error Responses:** +- `405 Method Not Allowed` - Non-GET requests + +--- + +## Usage Examples + +### Connect to a peer +```bash +curl -X POST http://localhost:9452/connect \ + -H "Content-Type: application/json" \ + -d '{ + "id": "31frd0uzbjvp721", + "secret": "h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6", + "endpoint": "https://example.com" + }' +``` + +### Connect with additional options +```bash +curl -X POST http://localhost:9452/connect \ + -H "Content-Type: application/json" \ + -d '{ + "id": "31frd0uzbjvp721", + "secret": "h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6", + "endpoint": "https://example.com", + "mtu": 1400, + "holepunch": true, + "pingInterval": "5s" + }' +``` + +### Check connection status +```bash +curl http://localhost:9452/status +``` + +### Switch organization +```bash +curl -X POST http://localhost:9452/switch-org \ + -H "Content-Type: application/json" \ + -d '{"orgId": "org_456"}' +``` + +### Disconnect from server +```bash +curl -X POST http://localhost:9452/disconnect +``` + +### Health check +```bash +curl http://localhost:9452/health +``` + +### Shutdown Olm +```bash +curl -X POST http://localhost:9452/exit +``` + +### Using Unix socket (Linux/macOS) +```bash +curl --unix-socket /var/run/olm/olm.sock http://localhost/status +curl --unix-socket /var/run/olm/olm.sock -X POST http://localhost/disconnect +``` \ No newline at end of file diff --git a/Makefile b/Makefile index 7e4cdf9..e2cb690 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -all: go-build-release +all: local docker-build-release: @if [ -z "$(tag)" ]; then \ @@ -12,15 +12,9 @@ docker-build-release: local: CGO_ENABLED=0 go build -o bin/olm -build: - docker build -t fosrl/olm:latest . - go-build-release: CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o bin/olm_linux_arm64 CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/olm_linux_amd64 CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o bin/olm_darwin_arm64 CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o bin/olm_darwin_amd64 - CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o bin/olm_windows_amd64.exe - -clean: - rm olm \ No newline at end of file + CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o bin/olm_windows_amd64.exe \ No newline at end of file diff --git a/README.md b/README.md index a67138f..97d0f66 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Olm is a [WireGuard](https://www.wireguard.com/) tunnel client designed to secur Olm is used with Pangolin and Newt as part of the larger system. See documentation below: -- [Full Documentation](https://docs.pangolin.net) +- [Full Documentation](https://docs.pangolin.net/manage/clients/understanding-clients) ## Key Functions @@ -18,136 +18,6 @@ Using the Olm ID and a secret, the olm will make HTTP requests to Pangolin to re When Olm receives WireGuard control messages, it will use the information encoded (endpoint, public key) to bring up a WireGuard tunnel on your computer to a remote Newt. It will ping over the tunnel to ensure the peer is brought up. -## CLI Args - -- `endpoint`: The endpoint where both Gerbil and Pangolin reside in order to connect to the websocket. -- `id`: Olm ID generated by Pangolin to identify the olm. -- `secret`: A unique secret (not shared and kept private) used to authenticate the olm ID with the websocket in order to receive commands. -- `org` (optional): Organization ID to connect to. -- `user-token` (optional): User authentication token. -- `mtu` (optional): MTU for the internal WG interface. Default: 1280 -- `dns` (optional): DNS server to use to resolve the endpoint. Default: 8.8.8.8 -- `upstream-dns` (optional): Upstream DNS server(s), comma-separated. Default: 8.8.8.8:53 -- `log-level` (optional): The log level to use (DEBUG, INFO, WARN, ERROR, FATAL). Default: INFO -- `ping-interval` (optional): Interval for pinging the server. Default: 3s -- `ping-timeout` (optional): Timeout for each ping. Default: 5s -- `interface` (optional): Name of the WireGuard interface. Default: olm -- `enable-api` (optional): Enable API server for receiving connection requests. Default: false -- `http-addr` (optional): HTTP server address (e.g., ':9452'). Default: :9452 -- `socket-path` (optional): Unix socket path (or named pipe on Windows). Default: /var/run/olm.sock (Linux/macOS) or olm (Windows) -- `disable-holepunch` (optional): Disable hole punching. Default: false -- `override-dns` (optional): Override system DNS settings. Default: false -- `disable-relay` (optional): Disable relay connections. Default: false - -## Environment Variables - -All CLI arguments can also be set via environment variables: - -- `PANGOLIN_ENDPOINT`: Equivalent to `--endpoint` -- `OLM_ID`: Equivalent to `--id` -- `OLM_SECRET`: Equivalent to `--secret` -- `ORG`: Equivalent to `--org` -- `USER_TOKEN`: Equivalent to `--user-token` -- `MTU`: Equivalent to `--mtu` -- `DNS`: Equivalent to `--dns` -- `UPSTREAM_DNS`: Equivalent to `--upstream-dns` -- `LOG_LEVEL`: Equivalent to `--log-level` -- `INTERFACE`: Equivalent to `--interface` -- `ENABLE_API`: Set to "true" to enable API server (equivalent to `--enable-api`) -- `HTTP_ADDR`: Equivalent to `--http-addr` -- `SOCKET_PATH`: Equivalent to `--socket-path` -- `PING_INTERVAL`: Equivalent to `--ping-interval` -- `PING_TIMEOUT`: Equivalent to `--ping-timeout` -- `DISABLE_HOLEPUNCH`: Set to "true" to disable hole punching (equivalent to `--disable-holepunch`) -- `OVERRIDE_DNS`: Set to "true" to override system DNS settings (equivalent to `--override-dns`) -- `DISABLE_RELAY`: Set to "true" to disable relay connections (equivalent to `--disable-relay`) -- `CONFIG_FILE`: Set to the location of a JSON file to load secret values - -Examples: - -```bash -olm \ ---id 31frd0uzbjvp721 \ ---secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 \ ---endpoint https://example.com -``` - -You can also run it with Docker compose. For example, a service in your `docker-compose.yml` might look like this using environment vars (recommended): - -```yaml -services: - olm: - image: fosrl/olm - container_name: olm - restart: unless-stopped - network_mode: host - devices: - - /dev/net/tun:/dev/net/tun - environment: - - PANGOLIN_ENDPOINT=https://example.com - - OLM_ID=31frd0uzbjvp721 - - OLM_SECRET=h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 -``` - -You can also pass the CLI args to the container: - -```yaml -services: - olm: - image: fosrl/olm - container_name: olm - restart: unless-stopped - network_mode: host - devices: - - /dev/net/tun:/dev/net/tun - command: - - --id 31frd0uzbjvp721 - - --secret h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6 - - --endpoint https://example.com -``` - -**Docker Configuration Notes:** - -- `network_mode: host` brings the olm network interface to the host system, allowing the WireGuard tunnel to function properly -- `devices: - /dev/net/tun:/dev/net/tun` is required to give the container access to the TUN device for creating WireGuard interfaces - -## Loading secrets from files - -You can use `CONFIG_FILE` to define a location of a config file to store the credentials between runs. - -``` -$ cat ~/.config/olm-client/config.json -{ - "id": "spmzu8rbpzj1qq6", - "secret": "f6v61mjutwme2kkydbw3fjo227zl60a2tsf5psw9r25hgae3", - "endpoint": "https://app.pangolin.net", - "org": "", - "userToken": "", - "mtu": 1280, - "dns": "8.8.8.8", - "upstreamDNS": ["8.8.8.8:53"], - "interface": "olm", - "logLevel": "INFO", - "enableApi": false, - "httpAddr": "", - "socketPath": "/var/run/olm.sock", - "pingInterval": "3s", - "pingTimeout": "5s", - "disableHolepunch": false, - "overrideDNS": false, - "disableRelay": false, - "tlsClientCert": "" -} -``` - -This file is also written to when olm first starts up. So you do not need to run every time with --id and secret if you have run it once! - -Default locations: - -- **macOS**: `~/Library/Application Support/olm-client/config.json` -- **Windows**: `%PROGRAMDATA%\olm\olm-client\config.json` -- **Linux/Others**: `~/.config/olm-client/config.json` - ## Hole Punching In the default mode, olm uses both relaying through Gerbil and NAT hole punching to connect to newt. If you want to disable hole punching, use the `--disable-holepunch` flag. Hole punching attempts to orchestrate a NAT hole punch between the two sites so that traffic flows directly, which can save data costs and improve speed. If hole punching fails, traffic will fall back to relaying through Gerbil. @@ -158,373 +28,14 @@ Right now, basic NAT hole punching is supported. We plan to add: - [ ] UPnP - [ ] LAN detection -## Windows Service - -On Windows, olm has to be installed and run as a Windows service. When running it with the cli args live above it will attempt to install and run the service to function like a cli tool. You can also run the following: - -### Service Management Commands - -``` -# Install the service -olm.exe install - -# Start the service -olm.exe start - -# Stop the service -olm.exe stop - -# Check service status -olm.exe status - -# Remove the service -olm.exe remove - -# Run in debug mode (console output) with our without id & secret -olm.exe debug - -# Show help -olm.exe help -``` - -Note running the service requires credentials in `%PROGRAMDATA%\olm\olm-client\config.json`. - -### Service Configuration - -When running as a service, Olm will read configuration from environment variables or you can modify the service to include command-line arguments: - -1. Install the service: `olm.exe install` -2. Set the credentials in `%PROGRAMDATA%\olm\olm-client\config.json`. Hint: if you run olm once with --id and --secret this file will be populated! -3. Start the service: `olm.exe start` - -### Service Logs - -When running as a service, logs are written to: - -- Windows Event Log (Application log, source: "OlmWireguardService") -- Log files in: `%PROGRAMDATA%\olm\logs\olm.log` - -You can view the Windows Event Log using Event Viewer or PowerShell: - -```powershell -Get-EventLog -LogName Application -Source "OlmWireguardService" -Newest 10 -``` - -## HTTP API - -Olm can be controlled with an embedded HTTP server when using `--enable-http`. This allows you to start it as a daemon and trigger it with the following endpoints. The API can listen on either a TCP address or a Unix socket/Windows named pipe. - -### Socket vs TCP - -By default, when `--enable-http` is used, Olm listens on a TCP address (configured via `--http-addr`, default `:9452`). Alternatively, Olm can listen on a Unix socket (Linux/macOS) or Windows named pipe for local-only communication with better security. - -**Unix Socket (Linux/macOS):** -- Socket path example: `/var/run/olm/olm.sock` -- The directory is created automatically if it doesn't exist -- Socket permissions are set to `0666` to allow access -- Existing socket files are automatically removed on startup -- Socket file is cleaned up when Olm stops - -**Windows Named Pipe:** -- Pipe path example: `\\.\pipe\olm` -- If the path doesn't start with `\`, it's automatically prefixed with `\\.\pipe\` -- Security descriptor grants full access to Everyone and the current owner -- Named pipes are automatically cleaned up by Windows - -**Connecting to the Socket:** - -```bash -# Linux/macOS - using curl with Unix socket -curl --unix-socket /var/run/olm/olm.sock http://localhost/status - ---- - -### POST /connect -Initiates a new connection request to a Pangolin server. - -**Request Body:** -```json -{ - "id": "string", - "secret": "string", - "endpoint": "string", - "userToken": "string", - "mtu": 1280, - "dns": "8.8.8.8", - "dnsProxyIP": "string", - "upstreamDNS": ["8.8.8.8:53", "1.1.1.1:53"], - "interfaceName": "olm", - "holepunch": false, - "tlsClientCert": "string", - "pingInterval": "3s", - "pingTimeout": "5s", - "orgId": "string" -} -``` - -**Required Fields:** -- `id`: Olm ID generated by Pangolin -- `secret`: Authentication secret for the Olm ID -- `endpoint`: Target Pangolin endpoint URL - -**Optional Fields:** -- `userToken`: User authentication token -- `mtu`: MTU for the internal WireGuard interface (default: 1280) -- `dns`: DNS server to use for resolving the endpoint -- `dnsProxyIP`: DNS proxy IP address -- `upstreamDNS`: Array of upstream DNS servers -- `interfaceName`: Name of the WireGuard interface (default: olm) -- `holepunch`: Enable NAT hole punching (default: false) -- `tlsClientCert`: TLS client certificate -- `pingInterval`: Interval for pinging the server (default: 3s) -- `pingTimeout`: Timeout for each ping (default: 5s) -- `orgId`: Organization ID to connect to - -**Response:** -- **Status Code:** `202 Accepted` -- **Content-Type:** `application/json` - -```json -{ - "status": "connection request accepted" -} -``` - -**Error Responses:** -- `405 Method Not Allowed` - Non-POST requests -- `400 Bad Request` - Invalid JSON or missing required fields -- `409 Conflict` - Already connected to a server (disconnect first) - ---- - -### GET /status -Returns the current connection status, registration state, and peer information. - -**Response:** -- **Status Code:** `200 OK` -- **Content-Type:** `application/json` - -```json -{ - "connected": true, - "registered": true, - "terminated": false, - "version": "1.0.0", - "agent": "olm", - "orgId": "org_123", - "peers": { - "10": { - "siteId": 10, - "name": "Site A", - "connected": true, - "rtt": 145338339, - "lastSeen": "2025-08-13T14:39:17.208334428-07:00", - "endpoint": "p.fosrl.io:21820", - "isRelay": true, - "peerAddress": "100.89.128.5", - "holepunchConnected": false - }, - "8": { - "siteId": 8, - "name": "Site B", - "connected": false, - "rtt": 0, - "lastSeen": "2025-08-13T14:39:19.663823645-07:00", - "endpoint": "p.fosrl.io:21820", - "isRelay": true, - "peerAddress": "100.89.128.10", - "holepunchConnected": false - } - }, - "networkSettings": { - "tunnelIP": "100.89.128.3/20" - } -} -``` - -**Fields:** -- `connected`: Boolean indicating if connected to Pangolin -- `registered`: Boolean indicating if registered with the server -- `terminated`: Boolean indicating if the connection was terminated -- `version`: Olm version string -- `agent`: Agent identifier -- `orgId`: Current organization ID -- `peers`: Map of peer statuses by site ID - - `siteId`: Peer site identifier - - `name`: Site name - - `connected`: Boolean peer connection state - - `rtt`: Peer round-trip time (integer, nanoseconds) - - `lastSeen`: Last time peer was seen (RFC3339 timestamp) - - `endpoint`: Peer endpoint address - - `isRelay`: Whether the peer is relayed (true) or direct (false) - - `peerAddress`: Peer's IP address in the tunnel - - `holepunchConnected`: Whether holepunch connection is established -- `networkSettings`: Current network configuration including tunnel IP - -**Error Responses:** -- `405 Method Not Allowed` - Non-GET requests - ---- - -### POST /disconnect -Disconnects from the current Pangolin server and tears down the WireGuard tunnel. - -**Request Body:** None required - -**Response:** -- **Status Code:** `200 OK` -- **Content-Type:** `application/json` - -```json -{ - "status": "disconnect initiated" -} -``` - -**Error Responses:** -- `405 Method Not Allowed` - Non-POST requests -- `409 Conflict` - Not currently connected to a server - ---- - -### POST /switch-org -Switches to a different organization while maintaining the connection. - -**Request Body:** -```json -{ - "orgId": "string" -} -``` - -**Required Fields:** -- `orgId`: The organization ID to switch to - -**Response:** -- **Status Code:** `200 OK` -- **Content-Type:** `application/json` - -```json -{ - "status": "org switch request accepted" -} -``` - -**Error Responses:** -- `405 Method Not Allowed` - Non-POST requests -- `400 Bad Request` - Invalid JSON or missing orgId field -- `500 Internal Server Error` - Org switch failed - ---- - -### POST /exit -Initiates a graceful shutdown of the Olm process. - -**Request Body:** None required - -**Response:** -- **Status Code:** `200 OK` -- **Content-Type:** `application/json` - -```json -{ - "status": "shutdown initiated" -} -``` - -**Note:** The response is sent before shutdown begins. There is a 100ms delay before the actual shutdown to ensure the response is delivered. - -**Error Responses:** -- `405 Method Not Allowed` - Non-POST requests - ---- - -### GET /health -Simple health check endpoint to verify the API server is running. - -**Response:** -- **Status Code:** `200 OK` -- **Content-Type:** `application/json` - -```json -{ - "status": "ok" -} -``` - -**Error Responses:** -- `405 Method Not Allowed` - Non-GET requests - ---- - -## Usage Examples - -### Connect to a peer -```bash -curl -X POST http://localhost:9452/connect \ - -H "Content-Type: application/json" \ - -d '{ - "id": "31frd0uzbjvp721", - "secret": "h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6", - "endpoint": "https://example.com" - }' -``` - -### Connect with additional options -```bash -curl -X POST http://localhost:9452/connect \ - -H "Content-Type: application/json" \ - -d '{ - "id": "31frd0uzbjvp721", - "secret": "h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6", - "endpoint": "https://example.com", - "mtu": 1400, - "holepunch": true, - "pingInterval": "5s" - }' -``` - -### Check connection status -```bash -curl http://localhost:9452/status -``` - -### Switch organization -```bash -curl -X POST http://localhost:9452/switch-org \ - -H "Content-Type: application/json" \ - -d '{"orgId": "org_456"}' -``` - -### Disconnect from server -```bash -curl -X POST http://localhost:9452/disconnect -``` - -### Health check -```bash -curl http://localhost:9452/health -``` - -### Shutdown Olm -```bash -curl -X POST http://localhost:9452/exit -``` - -### Using Unix socket (Linux/macOS) -```bash -curl --unix-socket /var/run/olm/olm.sock http://localhost/status -curl --unix-socket /var/run/olm/olm.sock -X POST http://localhost/disconnect -``` - ## Build ### Binary -Make sure to have Go 1.23.1 installed. +Make sure to have Go 1.25 installed. ```bash -make local +make ``` ## Licensing diff --git a/olm.iss b/olm.iss index e903528..1893f8e 100644 --- a/olm.iss +++ b/olm.iss @@ -44,8 +44,8 @@ Name: "english"; MessagesFile: "compiler:Default.isl" [Files] ; The 'DestName' flag ensures that 'olm_windows_amd64.exe' is installed as 'olm.exe' -Source: "C:\Users\Administrator\Downloads\olm_windows_amd64.exe"; DestDir: "{app}"; DestName: "{#MyAppExeName}"; Flags: ignoreversion -Source: "C:\Users\Administrator\Downloads\wintun.dll"; DestDir: "{app}"; Flags: ignoreversion +Source: "Z:\olm_windows_amd64.exe"; DestDir: "{app}"; DestName: "{#MyAppExeName}"; Flags: ignoreversion +Source: "Z:\wintun.dll"; DestDir: "{app}"; Flags: ignoreversion ; NOTE: Don't use "Flags: ignoreversion" on any shared system files [Icons] @@ -78,7 +78,7 @@ begin Result := True; exit; end; - + // Perform a case-insensitive check to see if the path is already present. // We add semicolons to prevent partial matches (e.g., matching C:\App in C:\App2). if Pos(';' + UpperCase(Path) + ';', ';' + UpperCase(OrigPath) + ';') > 0 then @@ -109,7 +109,7 @@ begin PathList.Delimiter := ';'; PathList.StrictDelimiter := True; PathList.DelimitedText := OrigPath; - + // Find and remove the matching entry (case-insensitive) for I := PathList.Count - 1 downto 0 do begin @@ -119,10 +119,10 @@ begin PathList.Delete(I); end; end; - + // Reconstruct the PATH NewPath := PathList.DelimitedText; - + // Write the new PATH back to the registry if RegWriteExpandStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', @@ -145,8 +145,8 @@ begin // Get the application installation path AppPath := ExpandConstant('{app}'); Log('Removing PATH entry for: ' + AppPath); - + // Remove only our path entry from the system PATH RemovePathEntry(AppPath); end; -end; \ No newline at end of file +end; diff --git a/olm/olm.go b/olm/olm.go index 1f02d8e..494ac02 100644 --- a/olm/olm.go +++ b/olm/olm.go @@ -471,7 +471,7 @@ func StartTunnel(config TunnelConfig) { // Get existing peer from PeerManager existingPeer, exists := peerManager.GetPeer(updateData.SiteId) if !exists { - logger.Error("Peer with site ID %d not found", updateData.SiteId) + logger.Warn("Peer with site ID %d not found", updateData.SiteId) return } @@ -502,6 +502,13 @@ func StartTunnel(config TunnelConfig) { return } + // If the endpoint changed, trigger holepunch to refresh NAT mappings + if updateData.Endpoint != "" && updateData.Endpoint != existingPeer.Endpoint { + logger.Info("Endpoint changed for site %d, triggering holepunch to refresh NAT mappings", updateData.SiteId) + holePunchManager.TriggerHolePunch() + holePunchManager.ResetInterval() + } + // Update successful logger.Info("Successfully updated peer for site %d", updateData.SiteId) }) @@ -778,6 +785,13 @@ func StartTunnel(config TunnelConfig) { return } + // Get existing peer from PeerManager + _, exists := peerManager.GetPeer(handshakeData.SiteId) + if exists { + logger.Warn("Peer with site ID %d already added", handshakeData.SiteId) + return + } + exitNode := holepunch.ExitNode{ Endpoint: handshakeData.ExitNode.Endpoint, PublicKey: handshakeData.ExitNode.PublicKey, diff --git a/olm/util.go b/olm/util.go index 9da1f00..6bfd171 100644 --- a/olm/util.go +++ b/olm/util.go @@ -1,9 +1,6 @@ package olm import ( - "fmt" - "net" - "strings" "time" "github.com/fosrl/newt/logger" @@ -11,33 +8,6 @@ import ( "github.com/fosrl/olm/websocket" ) -// Helper function to format endpoints correctly -func formatEndpoint(endpoint string) string { - if endpoint == "" { - return "" - } - // Check if it's already a valid host:port that SplitHostPort can parse (e.g., [::1]:8080 or 1.2.3.4:8080) - _, _, err := net.SplitHostPort(endpoint) - if err == nil { - return endpoint // Already valid, no change needed - } - - // If it failed, it might be our malformed "ipv6:port" string. Let's check and fix it. - lastColon := strings.LastIndex(endpoint, ":") - if lastColon > 0 { // Ensure there is a colon and it's not the first character - hostPart := endpoint[:lastColon] - // Check if the host part is a literal IPv6 address - if ip := net.ParseIP(hostPart); ip != nil && ip.To4() == nil { - // It is! Reformat it with brackets. - portPart := endpoint[lastColon+1:] - return fmt.Sprintf("[%s]:%s", hostPart, portPart) - } - } - - // If it's not the specific malformed case, return it as is. - return endpoint -} - func sendPing(olm *websocket.Client) error { err := olm.SendMessage("olm/ping", map[string]interface{}{ "timestamp": time.Now().Unix(), @@ -83,16 +53,3 @@ func GetNetworkSettingsJSON() (string, error) { func GetNetworkSettingsIncrementor() int { return network.GetIncrementor() } - -// stringSlicesEqual compares two string slices for equality -func stringSlicesEqual(a, b []string) bool { - if len(a) != len(b) { - return false - } - for i := range a { - if a[i] != b[i] { - return false - } - } - return true -} diff --git a/peers/monitor/monitor.go b/peers/monitor/monitor.go index ac91cb3..6c2e77b 100644 --- a/peers/monitor/monitor.go +++ b/peers/monitor/monitor.go @@ -191,12 +191,12 @@ func (pm *PeerMonitor) AddPeer(siteID int, endpoint string, holepunchEndpoint st // update holepunch endpoint for a peer func (pm *PeerMonitor) UpdateHolepunchEndpoint(siteID int, endpoint string) { - go func() { - time.Sleep(3 * time.Second) - pm.mutex.Lock() - defer pm.mutex.Unlock() - pm.holepunchEndpoints[siteID] = endpoint - }() + // Short delay to allow WireGuard peer reconfiguration to complete + // The NAT mapping refresh is handled separately by TriggerHolePunch in olm.go + pm.mutex.Lock() + defer pm.mutex.Unlock() + pm.holepunchEndpoints[siteID] = endpoint + logger.Debug("Updated holepunch endpoint for site %d to %s", siteID, endpoint) } // RapidTestPeer performs a rapid connectivity test for a newly added peer. @@ -294,6 +294,12 @@ func (pm *PeerMonitor) RemovePeer(siteID int) { pm.removePeerUnlocked(siteID) } +func (pm *PeerMonitor) RemoveHolepunchEndpoint(siteID int) { + pm.mutex.Lock() + defer pm.mutex.Unlock() + delete(pm.holepunchEndpoints, siteID) +} + // Start begins monitoring all peers func (pm *PeerMonitor) Start() { pm.mutex.Lock()