mirror of
https://github.com/fosrl/olm.git
synced 2026-02-11 23:46:57 +00:00
Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7224d9824d | ||
|
|
2b8e240752 | ||
|
|
bee490713d | ||
|
|
1cb7fd94ab | ||
|
|
c0b1cd6bde | ||
|
|
dd00289f8e | ||
|
|
b23a02ee97 | ||
|
|
80f726cfea | ||
|
|
aa8828186f | ||
|
|
0a990d196d | ||
|
|
00e8050949 | ||
|
|
18ee4c93fb | ||
|
|
8fa2da00b6 | ||
|
|
b851cd73c9 | ||
|
|
4c19d7ef6d | ||
|
|
cbecb9a0ce | ||
|
|
4fc8db08ba | ||
|
|
7ca46e0a75 | ||
|
|
a4ea5143af | ||
|
|
e9257b6423 | ||
|
|
3c9d3a1d2c | ||
|
|
b426f14190 | ||
|
|
d48acfba39 | ||
|
|
35b48cd8e5 | ||
|
|
15bca53309 | ||
|
|
898b599db5 | ||
|
|
c07bba18bb | ||
|
|
4c24d3b808 | ||
|
|
ad4ab3d04f | ||
|
|
e21153fae1 | ||
|
|
41c3360e23 | ||
|
|
1960d32443 | ||
|
|
74b83b3303 | ||
|
|
c2c3470868 | ||
|
|
2bda3dc3cc | ||
|
|
52573c8664 | ||
|
|
0d3c34e23f | ||
|
|
891df5c74b | ||
|
|
6f3f162d2b | ||
|
|
f6fa5fd02c | ||
|
|
8f4e0ba29e | ||
|
|
32b7dc7c43 | ||
|
|
78d2ebe1de | ||
|
|
014f8eb4e5 | ||
|
|
cd42803291 | ||
|
|
5c5b303994 | ||
|
|
968873da22 | ||
|
|
e2772f918b | ||
|
|
cdf6a31b67 | ||
|
|
2ce72065a7 | ||
|
|
b3e7aafb58 | ||
|
|
dd610ad850 | ||
|
|
b4b0a832e7 | ||
|
|
79963c1f66 | ||
|
|
5f95282161 | ||
|
|
1cca54f9d5 | ||
|
|
219df22919 | ||
|
|
337d9934fd | ||
|
|
bba4d72a78 | ||
|
|
fb5c793126 | ||
|
|
1821dbb672 | ||
|
|
4fda6fe031 | ||
|
|
f286f0faf6 | ||
|
|
cba3d607bf | ||
|
|
5ca12834a1 |
47
.github/DISCUSSION_TEMPLATE/feature-requests.yml
vendored
Normal file
47
.github/DISCUSSION_TEMPLATE/feature-requests.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Summary
|
||||||
|
description: A clear and concise summary of the requested feature.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Motivation
|
||||||
|
description: |
|
||||||
|
Why is this feature important?
|
||||||
|
Explain the problem this feature would solve or what use case it would enable.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Proposed Solution
|
||||||
|
description: |
|
||||||
|
How would you like to see this feature implemented?
|
||||||
|
Provide as much detail as possible about the desired behavior, configuration, or changes.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Alternatives Considered
|
||||||
|
description: Describe any alternative solutions or workarounds you've thought about.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Additional Context
|
||||||
|
description: Add any other context, mockups, or screenshots about the feature request here.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Before submitting, please:
|
||||||
|
- Check if there is an existing issue for this feature.
|
||||||
|
- Clearly explain the benefit and use case.
|
||||||
|
- Be as specific as possible to help contributors evaluate and implement.
|
||||||
51
.github/ISSUE_TEMPLATE/1.bug_report.yml
vendored
Normal file
51
.github/ISSUE_TEMPLATE/1.bug_report.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
name: Bug Report
|
||||||
|
description: Create a bug report
|
||||||
|
labels: []
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Describe the Bug
|
||||||
|
description: A clear and concise description of what the bug is.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Environment
|
||||||
|
description: Please fill out the relevant details below for your environment.
|
||||||
|
value: |
|
||||||
|
- OS Type & Version: (e.g., Ubuntu 22.04)
|
||||||
|
- Pangolin Version:
|
||||||
|
- Gerbil Version:
|
||||||
|
- Traefik Version:
|
||||||
|
- Newt Version:
|
||||||
|
- Olm Version: (if applicable)
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: To Reproduce
|
||||||
|
description: |
|
||||||
|
Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below.
|
||||||
|
|
||||||
|
If using code blocks, make sure syntax highlighting is correct and double-check that the rendered preview is not broken.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Expected Behavior
|
||||||
|
description: A clear and concise description of what you expected to happen.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Before posting the issue go through the steps you've written down to make sure the steps provided are detailed and clear.
|
||||||
|
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Contributors should be able to follow the steps provided in order to reproduce the bug.
|
||||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Need help or have questions?
|
||||||
|
url: https://github.com/orgs/fosrl/discussions
|
||||||
|
about: Ask questions, get help, and discuss with other community members
|
||||||
|
- name: Request a Feature
|
||||||
|
url: https://github.com/orgs/fosrl/discussions/new?category=feature-requests
|
||||||
|
about: Feature requests should be opened as discussions so others can upvote and comment
|
||||||
40
.github/dependabot.yml
vendored
Normal file
40
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "gomod"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
groups:
|
||||||
|
dev-patch-updates:
|
||||||
|
dependency-type: "development"
|
||||||
|
update-types:
|
||||||
|
- "patch"
|
||||||
|
dev-minor-updates:
|
||||||
|
dependency-type: "development"
|
||||||
|
update-types:
|
||||||
|
- "minor"
|
||||||
|
prod-patch-updates:
|
||||||
|
dependency-type: "production"
|
||||||
|
update-types:
|
||||||
|
- "patch"
|
||||||
|
prod-minor-updates:
|
||||||
|
dependency-type: "production"
|
||||||
|
update-types:
|
||||||
|
- "minor"
|
||||||
|
|
||||||
|
- package-ecosystem: "docker"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
groups:
|
||||||
|
patch-updates:
|
||||||
|
update-types:
|
||||||
|
- "patch"
|
||||||
|
minor-updates:
|
||||||
|
update-types:
|
||||||
|
- "minor"
|
||||||
|
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
40
.github/workflows/cicd.yml
vendored
40
.github/workflows/cicd.yml
vendored
@@ -12,26 +12,42 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
||||||
|
|
||||||
- name: Extract tag name
|
- name: Extract tag name
|
||||||
id: get-tag
|
id: get-tag
|
||||||
run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: 1.23.1
|
go-version: 1.25
|
||||||
|
|
||||||
# - name: Update version in main.go
|
- name: Update version in main.go
|
||||||
# run: |
|
run: |
|
||||||
# TAG=${{ env.TAG }}
|
TAG=${{ env.TAG }}
|
||||||
# if [ -f main.go ]; then
|
if [ -f main.go ]; then
|
||||||
# sed -i 's/Olm version replaceme/Olm version '"$TAG"'/' main.go
|
sed -i 's/version_replaceme/'"$TAG"'/' main.go
|
||||||
# echo "Updated main.go with version $TAG"
|
echo "Updated main.go with version $TAG"
|
||||||
# else
|
else
|
||||||
# echo "main.go not found"
|
echo "main.go not found"
|
||||||
# fi
|
fi
|
||||||
|
- name: Build and push Docker images
|
||||||
|
run: |
|
||||||
|
TAG=${{ env.TAG }}
|
||||||
|
make docker-build-release tag=$TAG
|
||||||
|
|
||||||
- name: Build binaries
|
- name: Build binaries
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
28
.github/workflows/test.yml
vendored
Normal file
28
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
name: Run Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- dev
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v6
|
||||||
|
with:
|
||||||
|
go-version: 1.25
|
||||||
|
|
||||||
|
- name: Build go
|
||||||
|
run: go build
|
||||||
|
|
||||||
|
- name: Build Docker image
|
||||||
|
run: make build
|
||||||
|
|
||||||
|
- name: Build binaries
|
||||||
|
run: make go-build-release
|
||||||
1
.go-version
Normal file
1
.go-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
1.25
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.23.1-alpine AS builder
|
FROM golang:1.25-alpine AS builder
|
||||||
|
|
||||||
# Set the working directory inside the container
|
# Set the working directory inside the container
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
@@ -16,9 +16,9 @@ COPY . .
|
|||||||
RUN CGO_ENABLED=0 GOOS=linux go build -o /olm
|
RUN CGO_ENABLED=0 GOOS=linux go build -o /olm
|
||||||
|
|
||||||
# Start a new stage from scratch
|
# Start a new stage from scratch
|
||||||
FROM ubuntu:22.04 AS runner
|
FROM alpine:3.22 AS runner
|
||||||
|
|
||||||
RUN apt-get update && apt-get install ca-certificates -y && rm -rf /var/lib/apt/lists/*
|
RUN apk --no-cache add ca-certificates
|
||||||
|
|
||||||
# Copy the pre-built binary file from the previous stage and the entrypoint script
|
# Copy the pre-built binary file from the previous stage and the entrypoint script
|
||||||
COPY --from=builder /olm /usr/local/bin/
|
COPY --from=builder /olm /usr/local/bin/
|
||||||
|
|||||||
11
Makefile
11
Makefile
@@ -1,9 +1,20 @@
|
|||||||
|
|
||||||
all: go-build-release
|
all: go-build-release
|
||||||
|
|
||||||
|
docker-build-release:
|
||||||
|
@if [ -z "$(tag)" ]; then \
|
||||||
|
echo "Error: tag is required. Usage: make docker-build-release tag=<tag>"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
docker buildx build --platform linux/arm/v7,linux/arm64,linux/amd64 -t fosrl/olm:latest -f Dockerfile --push .
|
||||||
|
docker buildx build --platform linux/arm/v7,linux/arm64,linux/amd64 -t fosrl/olm:$(tag) -f Dockerfile --push .
|
||||||
|
|
||||||
local:
|
local:
|
||||||
CGO_ENABLED=0 go build -o olm
|
CGO_ENABLED=0 go build -o olm
|
||||||
|
|
||||||
|
build:
|
||||||
|
docker build -t fosrl/olm:latest .
|
||||||
|
|
||||||
go-build-release:
|
go-build-release:
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o bin/olm_linux_arm64
|
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=linux GOARCH=amd64 go build -o bin/olm_linux_amd64
|
||||||
|
|||||||
178
README.md
178
README.md
@@ -1,6 +1,6 @@
|
|||||||
# Olm
|
# Olm
|
||||||
|
|
||||||
Olm is a [WireGuard](https://www.wireguard.com/) tunnel client designed to securely connect you computer to Newt sites running on remote networks.
|
Olm is a [WireGuard](https://www.wireguard.com/) tunnel client designed to securely connect your computer to Newt sites running on remote networks.
|
||||||
|
|
||||||
### Installation and Documentation
|
### Installation and Documentation
|
||||||
|
|
||||||
@@ -48,8 +48,9 @@ All CLI arguments can also be set via environment variables:
|
|||||||
- `PING_INTERVAL`: Equivalent to `--ping-interval`
|
- `PING_INTERVAL`: Equivalent to `--ping-interval`
|
||||||
- `PING_TIMEOUT`: Equivalent to `--ping-timeout`
|
- `PING_TIMEOUT`: Equivalent to `--ping-timeout`
|
||||||
- `HOLEPUNCH`: Set to "true" to enable hole punching (equivalent to `--holepunch`)
|
- `HOLEPUNCH`: Set to "true" to enable hole punching (equivalent to `--holepunch`)
|
||||||
|
- `CONFIG_FILE`: Set to the location of a JSON file to load secret values
|
||||||
|
|
||||||
Example:
|
Examples:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
olm \
|
olm \
|
||||||
@@ -58,6 +59,67 @@ olm \
|
|||||||
--endpoint https://example.com
|
--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://pangolin.fossorial.io",
|
||||||
|
"tlsClientCert": ""
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This file is also written to when newt 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
|
## Hole Punching
|
||||||
|
|
||||||
In the default mode, olm "relays" traffic through Gerbil in the cloud to get down to newt. This is a little more reliable. Support for NAT hole punching is also EXPERIMENTAL right now using the `--holepunch` flag. This will attempt to orchestrate a NAT hole punch between the two sites so that traffic flows directly. This will save data costs and speed. If it fails it should fall back to relaying.
|
In the default mode, olm "relays" traffic through Gerbil in the cloud to get down to newt. This is a little more reliable. Support for NAT hole punching is also EXPERIMENTAL right now using the `--holepunch` flag. This will attempt to orchestrate a NAT hole punch between the two sites so that traffic flows directly. This will save data costs and speed. If it fails it should fall back to relaying.
|
||||||
@@ -97,15 +159,14 @@ olm.exe debug
|
|||||||
olm.exe help
|
olm.exe help
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note running the service requires credentials in `%PROGRAMDATA%\olm\olm-client\config.json`.
|
||||||
|
|
||||||
### Service Configuration
|
### 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:
|
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`
|
1. Install the service: `olm.exe install`
|
||||||
2. Configure the service with your credentials using Windows Service Manager or by setting system environment variables:
|
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!
|
||||||
- `PANGOLIN_ENDPOINT=https://example.com`
|
|
||||||
- `OLM_ID=your_olm_id`
|
|
||||||
- `OLM_SECRET=your_secret`
|
|
||||||
3. Start the service: `olm.exe start`
|
3. Start the service: `olm.exe start`
|
||||||
|
|
||||||
### Service Logs
|
### Service Logs
|
||||||
@@ -121,16 +182,111 @@ You can view the Windows Event Log using Event Viewer or PowerShell:
|
|||||||
Get-EventLog -LogName Application -Source "OlmWireguardService" -Newest 10
|
Get-EventLog -LogName Application -Source "OlmWireguardService" -Newest 10
|
||||||
```
|
```
|
||||||
|
|
||||||
## Build
|
## HTTP Endpoints
|
||||||
|
|
||||||
### Container
|
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:
|
||||||
|
|
||||||
Ensure Docker is installed.
|
### POST /connect
|
||||||
|
Initiates a new connection request.
|
||||||
|
|
||||||
```bash
|
**Request Body:**
|
||||||
make
|
```json
|
||||||
|
{
|
||||||
|
"id": "string",
|
||||||
|
"secret": "string",
|
||||||
|
"endpoint": "string"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Required Fields:**
|
||||||
|
- `id`: Connection identifier
|
||||||
|
- `secret`: Authentication secret
|
||||||
|
- `endpoint`: Target endpoint URL
|
||||||
|
|
||||||
|
**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
|
||||||
|
|
||||||
|
### GET /status
|
||||||
|
Returns the current connection status and peer information.
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
- **Status Code:** `200 OK`
|
||||||
|
- **Content-Type:** `application/json`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "connected",
|
||||||
|
"connected": true,
|
||||||
|
"tunnelIP": "100.89.128.3/20",
|
||||||
|
"version": "version_replaceme",
|
||||||
|
"peers": {
|
||||||
|
"10": {
|
||||||
|
"siteId": 10,
|
||||||
|
"connected": true,
|
||||||
|
"rtt": 145338339,
|
||||||
|
"lastSeen": "2025-08-13T14:39:17.208334428-07:00",
|
||||||
|
"endpoint": "p.fosrl.io:21820",
|
||||||
|
"isRelay": true
|
||||||
|
},
|
||||||
|
"8": {
|
||||||
|
"siteId": 8,
|
||||||
|
"connected": false,
|
||||||
|
"rtt": 0,
|
||||||
|
"lastSeen": "2025-08-13T14:39:19.663823645-07:00",
|
||||||
|
"endpoint": "p.fosrl.io:21820",
|
||||||
|
"isRelay": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fields:**
|
||||||
|
- `status`: Overall connection status ("connected" or "disconnected")
|
||||||
|
- `connected`: Boolean connection state
|
||||||
|
- `tunnelIP`: IP address and subnet of the tunnel (when connected)
|
||||||
|
- `version`: Olm version string
|
||||||
|
- `peers`: Map of peer statuses by site ID
|
||||||
|
- `siteId`: Peer site identifier
|
||||||
|
- `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)
|
||||||
|
|
||||||
|
**Error Responses:**
|
||||||
|
- `405 Method Not Allowed` - Non-GET requests
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Connect to a peer
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8080/connect \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"id": "31frd0uzbjvp721",
|
||||||
|
"secret": "h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6",
|
||||||
|
"endpoint": "https://example.com"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check connection status
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8080/status
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
### Binary
|
### Binary
|
||||||
|
|
||||||
Make sure to have Go 1.23.1 installed.
|
Make sure to have Go 1.23.1 installed.
|
||||||
|
|||||||
149
common.go
149
common.go
@@ -52,9 +52,13 @@ type HolePunchMessage struct {
|
|||||||
NewtID string `json:"newtId"`
|
NewtID string `json:"newtId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ExitNode struct {
|
||||||
|
Endpoint string `json:"endpoint"`
|
||||||
|
PublicKey string `json:"publicKey"`
|
||||||
|
}
|
||||||
|
|
||||||
type HolePunchData struct {
|
type HolePunchData struct {
|
||||||
ServerPubKey string `json:"serverPubKey"`
|
ExitNodes []ExitNode `json:"exitNodes"`
|
||||||
Endpoint string `json:"endpoint"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type EncryptedHolePunchMessage struct {
|
type EncryptedHolePunchMessage struct {
|
||||||
@@ -64,13 +68,12 @@ type EncryptedHolePunchMessage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
peerMonitor *peermonitor.PeerMonitor
|
peerMonitor *peermonitor.PeerMonitor
|
||||||
stopHolepunch chan struct{}
|
stopHolepunch chan struct{}
|
||||||
stopRegister func()
|
stopRegister func()
|
||||||
stopPing chan struct{}
|
stopPing chan struct{}
|
||||||
olmToken string
|
olmToken string
|
||||||
gerbilServerPubKey string
|
holePunchRunning bool
|
||||||
holePunchRunning bool
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -226,8 +229,8 @@ func resolveDomain(domain string) (string, error) {
|
|||||||
return ipAddr, nil
|
return ipAddr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendUDPHolePunchWithConn(conn *net.UDPConn, remoteAddr *net.UDPAddr, olmID string) error {
|
func sendUDPHolePunchWithConn(conn *net.UDPConn, remoteAddr *net.UDPAddr, olmID string, serverPubKey string) error {
|
||||||
if gerbilServerPubKey == "" || olmToken == "" {
|
if serverPubKey == "" || olmToken == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +249,7 @@ func sendUDPHolePunchWithConn(conn *net.UDPConn, remoteAddr *net.UDPAddr, olmID
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt the payload using the server's WireGuard public key
|
// Encrypt the payload using the server's WireGuard public key
|
||||||
encryptedPayload, err := encryptPayload(payloadBytes, gerbilServerPubKey)
|
encryptedPayload, err := encryptPayload(payloadBytes, serverPubKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to encrypt payload: %v", err)
|
return fmt.Errorf("failed to encrypt payload: %v", err)
|
||||||
}
|
}
|
||||||
@@ -319,7 +322,12 @@ func encryptPayload(payload []byte, serverPublicKey string) (interface{}, error)
|
|||||||
return encryptedMsg, nil
|
return encryptedMsg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func keepSendingUDPHolePunch(endpoint string, olmID string, sourcePort uint16) {
|
func keepSendingUDPHolePunchToMultipleExitNodes(exitNodes []ExitNode, olmID string, sourcePort uint16) {
|
||||||
|
if len(exitNodes) == 0 {
|
||||||
|
logger.Warn("No exit nodes provided for hole punching")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Check if hole punching is already running
|
// Check if hole punching is already running
|
||||||
if holePunchRunning {
|
if holePunchRunning {
|
||||||
logger.Debug("UDP hole punch already running, skipping new request")
|
logger.Debug("UDP hole punch already running, skipping new request")
|
||||||
@@ -333,13 +341,114 @@ func keepSendingUDPHolePunch(endpoint string, olmID string, sourcePort uint16) {
|
|||||||
logger.Info("UDP hole punch goroutine ended")
|
logger.Info("UDP hole punch goroutine ended")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
logger.Info("Starting UDP hole punch to %d exit nodes", len(exitNodes))
|
||||||
|
defer logger.Info("UDP hole punch goroutine ended for all exit nodes")
|
||||||
|
|
||||||
|
// Create the UDP connection once and reuse it for all exit nodes
|
||||||
|
localAddr := &net.UDPAddr{
|
||||||
|
Port: int(sourcePort),
|
||||||
|
IP: net.IPv4zero,
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := net.ListenUDP("udp", localAddr)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Failed to bind UDP socket: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
// Resolve all endpoints upfront
|
||||||
|
type resolvedExitNode struct {
|
||||||
|
remoteAddr *net.UDPAddr
|
||||||
|
publicKey string
|
||||||
|
endpointName string
|
||||||
|
}
|
||||||
|
|
||||||
|
var resolvedNodes []resolvedExitNode
|
||||||
|
for _, exitNode := range exitNodes {
|
||||||
|
host, err := resolveDomain(exitNode.Endpoint)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Failed to resolve endpoint %s: %v", exitNode.Endpoint, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
serverAddr := net.JoinHostPort(host, "21820")
|
||||||
|
remoteAddr, err := net.ResolveUDPAddr("udp", serverAddr)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Failed to resolve UDP address for %s: %v", exitNode.Endpoint, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resolvedNodes = append(resolvedNodes, resolvedExitNode{
|
||||||
|
remoteAddr: remoteAddr,
|
||||||
|
publicKey: exitNode.PublicKey,
|
||||||
|
endpointName: exitNode.Endpoint,
|
||||||
|
})
|
||||||
|
logger.Info("Resolved exit node: %s -> %s", exitNode.Endpoint, remoteAddr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resolvedNodes) == 0 {
|
||||||
|
logger.Error("No exit nodes could be resolved")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send initial hole punch to all exit nodes
|
||||||
|
for _, node := range resolvedNodes {
|
||||||
|
if err := sendUDPHolePunchWithConn(conn, node.remoteAddr, olmID, node.publicKey); err != nil {
|
||||||
|
logger.Error("Failed to send initial UDP hole punch to %s: %v", node.endpointName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ticker := time.NewTicker(250 * time.Millisecond)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
timeout := time.NewTimer(15 * time.Second)
|
||||||
|
defer timeout.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stopHolepunch:
|
||||||
|
logger.Info("Stopping UDP holepunch for all exit nodes")
|
||||||
|
return
|
||||||
|
case <-timeout.C:
|
||||||
|
logger.Info("UDP holepunch routine timed out after 15 seconds for all exit nodes")
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
// Send hole punch to all exit nodes
|
||||||
|
for _, node := range resolvedNodes {
|
||||||
|
if err := sendUDPHolePunchWithConn(conn, node.remoteAddr, olmID, node.publicKey); err != nil {
|
||||||
|
logger.Error("Failed to send UDP hole punch to %s: %v", node.endpointName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func keepSendingUDPHolePunch(endpoint string, olmID string, sourcePort uint16, serverPubKey string) {
|
||||||
|
|
||||||
|
// Check if hole punching is already running
|
||||||
|
if holePunchRunning {
|
||||||
|
logger.Debug("UDP hole punch already running, skipping new request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the flag to indicate hole punching is running
|
||||||
|
holePunchRunning = true
|
||||||
|
defer func() {
|
||||||
|
holePunchRunning = false
|
||||||
|
logger.Info("UDP hole punch goroutine ended")
|
||||||
|
}()
|
||||||
|
|
||||||
|
logger.Info("Starting UDP hole punch to %s", endpoint)
|
||||||
|
defer logger.Info("UDP hole punch goroutine ended for %s", endpoint)
|
||||||
|
|
||||||
host, err := resolveDomain(endpoint)
|
host, err := resolveDomain(endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failed to resolve endpoint: %v", err)
|
logger.Error("Failed to resolve endpoint: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
serverAddr := host + ":21820"
|
serverAddr := net.JoinHostPort(host, "21820")
|
||||||
|
|
||||||
// Create the UDP connection once and reuse it
|
// Create the UDP connection once and reuse it
|
||||||
localAddr := &net.UDPAddr{
|
localAddr := &net.UDPAddr{
|
||||||
@@ -361,20 +470,26 @@ func keepSendingUDPHolePunch(endpoint string, olmID string, sourcePort uint16) {
|
|||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
// Execute once immediately before starting the loop
|
// Execute once immediately before starting the loop
|
||||||
if err := sendUDPHolePunchWithConn(conn, remoteAddr, olmID); err != nil {
|
if err := sendUDPHolePunchWithConn(conn, remoteAddr, olmID, serverPubKey); err != nil {
|
||||||
logger.Error("Failed to send UDP hole punch: %v", err)
|
logger.Error("Failed to send UDP hole punch: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ticker := time.NewTicker(250 * time.Millisecond)
|
ticker := time.NewTicker(250 * time.Millisecond)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
timeout := time.NewTimer(15 * time.Second)
|
||||||
|
defer timeout.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-stopHolepunch:
|
case <-stopHolepunch:
|
||||||
logger.Info("Stopping UDP holepunch")
|
logger.Info("Stopping UDP holepunch")
|
||||||
return
|
return
|
||||||
|
case <-timeout.C:
|
||||||
|
logger.Info("UDP holepunch routine timed out after 15 seconds")
|
||||||
|
return
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
if err := sendUDPHolePunchWithConn(conn, remoteAddr, olmID); err != nil {
|
if err := sendUDPHolePunchWithConn(conn, remoteAddr, olmID, serverPubKey); err != nil {
|
||||||
logger.Error("Failed to send UDP hole punch: %v", err)
|
logger.Error("Failed to send UDP hole punch: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -510,7 +625,7 @@ func ConfigurePeer(dev *device.Device, siteConfig SiteConfig, privateKey wgtypes
|
|||||||
// Set up peer monitoring
|
// Set up peer monitoring
|
||||||
if peerMonitor != nil {
|
if peerMonitor != nil {
|
||||||
monitorAddress := strings.Split(siteConfig.ServerIP, "/")[0]
|
monitorAddress := strings.Split(siteConfig.ServerIP, "/")[0]
|
||||||
monitorPeer := fmt.Sprintf("%s:%d", monitorAddress, siteConfig.ServerPort+1) // +1 for the monitor port
|
monitorPeer := net.JoinHostPort(monitorAddress, strconv.Itoa(int(siteConfig.ServerPort+1))) // +1 for the monitor port
|
||||||
logger.Debug("Setting up peer monitor for site %d at %s", siteConfig.SiteId, monitorPeer)
|
logger.Debug("Setting up peer monitor for site %d at %s", siteConfig.SiteId, monitorPeer)
|
||||||
|
|
||||||
primaryRelay, err := resolveDomain(endpoint) // Using global endpoint variable
|
primaryRelay, err := resolveDomain(endpoint) // Using global endpoint variable
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
- PANGOLIN_ENDPOINT=https://example.com
|
- PANGOLIN_ENDPOINT=https://example.com
|
||||||
- OLM_ID=2ix2t8xk22ubpfy
|
- OLM_ID=vdqnz8rwgb95cnp
|
||||||
- OLM_SECRET=nnisrfsdfc7prqsp9ewo1dvtvci50j5uiqotez00dgap0ii2
|
- OLM_SECRET=1sw05qv1tkfdb1k81zpw05nahnnjvmhxjvf746umwagddmdg
|
||||||
- LOG_LEVEL=DEBUG
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
- SYS_MODULE
|
||||||
|
devices:
|
||||||
|
- /dev/net/tun:/dev/net/tun
|
||||||
|
network_mode: host
|
||||||
279
get-olm.sh
Normal file
279
get-olm.sh
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Get Olm - Cross-platform installation script
|
||||||
|
# Usage: curl -fsSL https://raw.githubusercontent.com/fosrl/olm/refs/heads/main/get-olm.sh | bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# GitHub repository info
|
||||||
|
REPO="fosrl/olm"
|
||||||
|
GITHUB_API_URL="https://api.github.com/repos/${REPO}/releases/latest"
|
||||||
|
|
||||||
|
# Function to print colored output
|
||||||
|
print_status() {
|
||||||
|
echo -e "${GREEN}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_warning() {
|
||||||
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to get latest version from GitHub API
|
||||||
|
get_latest_version() {
|
||||||
|
local latest_info
|
||||||
|
|
||||||
|
if command -v curl >/dev/null 2>&1; then
|
||||||
|
latest_info=$(curl -fsSL "$GITHUB_API_URL" 2>/dev/null)
|
||||||
|
elif command -v wget >/dev/null 2>&1; then
|
||||||
|
latest_info=$(wget -qO- "$GITHUB_API_URL" 2>/dev/null)
|
||||||
|
else
|
||||||
|
print_error "Neither curl nor wget is available. Please install one of them." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$latest_info" ]; then
|
||||||
|
print_error "Failed to fetch latest version information" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract version from JSON response (works without jq)
|
||||||
|
local version=$(echo "$latest_info" | grep '"tag_name"' | head -1 | sed 's/.*"tag_name": *"\([^"]*\)".*/\1/')
|
||||||
|
|
||||||
|
if [ -z "$version" ]; then
|
||||||
|
print_error "Could not parse version from GitHub API response" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove 'v' prefix if present
|
||||||
|
version=$(echo "$version" | sed 's/^v//')
|
||||||
|
|
||||||
|
echo "$version"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Detect OS and architecture
|
||||||
|
detect_platform() {
|
||||||
|
local os arch
|
||||||
|
|
||||||
|
# Detect OS
|
||||||
|
case "$(uname -s)" in
|
||||||
|
Linux*) os="linux" ;;
|
||||||
|
Darwin*) os="darwin" ;;
|
||||||
|
MINGW*|MSYS*|CYGWIN*) os="windows" ;;
|
||||||
|
FreeBSD*) os="freebsd" ;;
|
||||||
|
*)
|
||||||
|
print_error "Unsupported operating system: $(uname -s)"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Detect architecture
|
||||||
|
case "$(uname -m)" in
|
||||||
|
x86_64|amd64) arch="amd64" ;;
|
||||||
|
arm64|aarch64) arch="arm64" ;;
|
||||||
|
armv7l|armv6l)
|
||||||
|
if [ "$os" = "linux" ]; then
|
||||||
|
if [ "$(uname -m)" = "armv6l" ]; then
|
||||||
|
arch="arm32v6"
|
||||||
|
else
|
||||||
|
arch="arm32"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
arch="arm64" # Default for non-Linux ARM
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
riscv64)
|
||||||
|
if [ "$os" = "linux" ]; then
|
||||||
|
arch="riscv64"
|
||||||
|
else
|
||||||
|
print_error "RISC-V architecture only supported on Linux"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
print_error "Unsupported architecture: $(uname -m)"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "${os}_${arch}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get installation directory
|
||||||
|
get_install_dir() {
|
||||||
|
local platform="$1"
|
||||||
|
|
||||||
|
if [[ "$platform" == *"windows"* ]]; then
|
||||||
|
echo "$HOME/bin"
|
||||||
|
else
|
||||||
|
# For Unix-like systems, prioritize system-wide directories for sudo access
|
||||||
|
# Check in order of preference: /usr/local/bin, /usr/bin, ~/.local/bin
|
||||||
|
if [ -d "/usr/local/bin" ]; then
|
||||||
|
echo "/usr/local/bin"
|
||||||
|
elif [ -d "/usr/bin" ]; then
|
||||||
|
echo "/usr/bin"
|
||||||
|
else
|
||||||
|
# Fallback to user directory if system directories don't exist
|
||||||
|
echo "$HOME/.local/bin"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if we need sudo for installation
|
||||||
|
need_sudo() {
|
||||||
|
local install_dir="$1"
|
||||||
|
|
||||||
|
# If installing to system directory and we don't have write permission, need sudo
|
||||||
|
if [[ "$install_dir" == "/usr/local/bin" || "$install_dir" == "/usr/bin" ]]; then
|
||||||
|
if [ ! -w "$install_dir" ] 2>/dev/null; then
|
||||||
|
return 0 # Need sudo
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 1 # Don't need sudo
|
||||||
|
}
|
||||||
|
|
||||||
|
# Download and install olm
|
||||||
|
install_olm() {
|
||||||
|
local platform="$1"
|
||||||
|
local install_dir="$2"
|
||||||
|
local binary_name="olm_${platform}"
|
||||||
|
local exe_suffix=""
|
||||||
|
|
||||||
|
# Add .exe suffix for Windows
|
||||||
|
if [[ "$platform" == *"windows"* ]]; then
|
||||||
|
binary_name="${binary_name}.exe"
|
||||||
|
exe_suffix=".exe"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local download_url="${BASE_URL}/${binary_name}"
|
||||||
|
local temp_file="/tmp/olm${exe_suffix}"
|
||||||
|
local final_path="${install_dir}/olm${exe_suffix}"
|
||||||
|
|
||||||
|
print_status "Downloading olm from ${download_url}"
|
||||||
|
|
||||||
|
# Download the binary
|
||||||
|
if command -v curl >/dev/null 2>&1; then
|
||||||
|
curl -fsSL "$download_url" -o "$temp_file"
|
||||||
|
elif command -v wget >/dev/null 2>&1; then
|
||||||
|
wget -q "$download_url" -O "$temp_file"
|
||||||
|
else
|
||||||
|
print_error "Neither curl nor wget is available. Please install one of them."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if we need sudo for installation
|
||||||
|
local use_sudo=""
|
||||||
|
if need_sudo "$install_dir"; then
|
||||||
|
print_status "Administrator privileges required for system-wide installation"
|
||||||
|
if command -v sudo >/dev/null 2>&1; then
|
||||||
|
use_sudo="sudo"
|
||||||
|
else
|
||||||
|
print_error "sudo is required for system-wide installation but not available"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create install directory if it doesn't exist
|
||||||
|
if [ -n "$use_sudo" ]; then
|
||||||
|
$use_sudo mkdir -p "$install_dir"
|
||||||
|
else
|
||||||
|
mkdir -p "$install_dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Move binary to install directory
|
||||||
|
if [ -n "$use_sudo" ]; then
|
||||||
|
$use_sudo mv "$temp_file" "$final_path"
|
||||||
|
$use_sudo chmod +x "$final_path"
|
||||||
|
else
|
||||||
|
mv "$temp_file" "$final_path"
|
||||||
|
chmod +x "$final_path"
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_status "olm installed to ${final_path}"
|
||||||
|
|
||||||
|
# Check if install directory is in PATH (only warn for non-system directories)
|
||||||
|
if [[ "$install_dir" != "/usr/local/bin" && "$install_dir" != "/usr/bin" ]]; then
|
||||||
|
if ! echo "$PATH" | grep -q "$install_dir"; then
|
||||||
|
print_warning "Install directory ${install_dir} is not in your PATH."
|
||||||
|
print_warning "Add it to your PATH by adding this line to your shell profile:"
|
||||||
|
print_warning " export PATH=\"${install_dir}:\$PATH\""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Verify installation
|
||||||
|
verify_installation() {
|
||||||
|
local install_dir="$1"
|
||||||
|
local exe_suffix=""
|
||||||
|
|
||||||
|
if [[ "$PLATFORM" == *"windows"* ]]; then
|
||||||
|
exe_suffix=".exe"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local olm_path="${install_dir}/olm${exe_suffix}"
|
||||||
|
|
||||||
|
if [ -f "$olm_path" ] && [ -x "$olm_path" ]; then
|
||||||
|
print_status "Installation successful!"
|
||||||
|
print_status "olm version: $("$olm_path" --version 2>/dev/null || echo "unknown")"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
print_error "Installation failed. Binary not found or not executable."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main installation process
|
||||||
|
main() {
|
||||||
|
print_status "Installing latest version of olm..."
|
||||||
|
|
||||||
|
# Get latest version
|
||||||
|
print_status "Fetching latest version from GitHub..."
|
||||||
|
VERSION=$(get_latest_version)
|
||||||
|
print_status "Latest version: v${VERSION}"
|
||||||
|
|
||||||
|
# Set base URL with the fetched version
|
||||||
|
BASE_URL="https://github.com/${REPO}/releases/download/${VERSION}"
|
||||||
|
|
||||||
|
# Detect platform
|
||||||
|
PLATFORM=$(detect_platform)
|
||||||
|
print_status "Detected platform: ${PLATFORM}"
|
||||||
|
|
||||||
|
# Get install directory
|
||||||
|
INSTALL_DIR=$(get_install_dir "$PLATFORM")
|
||||||
|
print_status "Install directory: ${INSTALL_DIR}"
|
||||||
|
|
||||||
|
# Inform user about system-wide installation
|
||||||
|
if [[ "$INSTALL_DIR" == "/usr/local/bin" || "$INSTALL_DIR" == "/usr/bin" ]]; then
|
||||||
|
print_status "Installing system-wide for sudo access"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Install olm
|
||||||
|
install_olm "$PLATFORM" "$INSTALL_DIR"
|
||||||
|
|
||||||
|
# Verify installation
|
||||||
|
if verify_installation "$INSTALL_DIR"; then
|
||||||
|
print_status "olm is ready to use!"
|
||||||
|
if [[ "$INSTALL_DIR" == "/usr/local/bin" || "$INSTALL_DIR" == "/usr/bin" ]]; then
|
||||||
|
print_status "olm is installed system-wide and accessible via sudo"
|
||||||
|
fi
|
||||||
|
if [[ "$PLATFORM" == *"windows"* ]]; then
|
||||||
|
print_status "Run 'olm --help' to get started"
|
||||||
|
else
|
||||||
|
print_status "Run 'olm --help' or 'sudo olm --help' to get started"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run main function
|
||||||
|
main "$@"
|
||||||
20
go.mod
20
go.mod
@@ -1,16 +1,13 @@
|
|||||||
module github.com/fosrl/olm
|
module github.com/fosrl/olm
|
||||||
|
|
||||||
go 1.23.1
|
go 1.25
|
||||||
|
|
||||||
toolchain go1.23.2
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fosrl/newt v0.0.0-20250724194014-008be54c554a
|
github.com/fosrl/newt v0.0.0-20250929233849-71c5bf7e65f7
|
||||||
github.com/vishvananda/netlink v1.3.1
|
github.com/vishvananda/netlink v1.3.1
|
||||||
golang.org/x/crypto v0.40.0
|
golang.org/x/crypto v0.42.0
|
||||||
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792
|
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792
|
||||||
golang.org/x/net v0.42.0
|
golang.org/x/sys v0.36.0
|
||||||
golang.org/x/sys v0.34.0
|
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb
|
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10
|
||||||
)
|
)
|
||||||
@@ -20,13 +17,12 @@ require (
|
|||||||
github.com/containerd/errdefs v1.0.0 // indirect
|
github.com/containerd/errdefs v1.0.0 // indirect
|
||||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||||
github.com/distribution/reference v0.6.0 // indirect
|
github.com/distribution/reference v0.6.0 // indirect
|
||||||
github.com/docker/docker v28.3.2+incompatible // indirect
|
github.com/docker/docker v28.4.0+incompatible // indirect
|
||||||
github.com/docker/go-connections v0.5.0 // indirect
|
github.com/docker/go-connections v0.5.0 // indirect
|
||||||
github.com/docker/go-units v0.5.0 // indirect
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/go-logr/logr v1.4.3 // indirect
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
|
||||||
github.com/google/btree v1.1.3 // indirect
|
github.com/google/btree v1.1.3 // indirect
|
||||||
github.com/google/go-cmp v0.7.0 // indirect
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
github.com/google/gopacket v1.1.19 // indirect
|
github.com/google/gopacket v1.1.19 // indirect
|
||||||
@@ -45,11 +41,11 @@ require (
|
|||||||
go.opentelemetry.io/otel v1.37.0 // indirect
|
go.opentelemetry.io/otel v1.37.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.37.0 // indirect
|
go.opentelemetry.io/otel/trace v1.37.0 // indirect
|
||||||
golang.org/x/mod v0.26.0 // indirect
|
golang.org/x/net v0.44.0 // indirect
|
||||||
golang.org/x/sync v0.16.0 // indirect
|
golang.org/x/sync v0.16.0 // indirect
|
||||||
golang.org/x/time v0.12.0 // indirect
|
golang.org/x/time v0.12.0 // indirect
|
||||||
golang.org/x/tools v0.35.0 // indirect
|
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
gvisor.dev/gvisor v0.0.0-20250718192347-d7830d968c56 // indirect
|
gvisor.dev/gvisor v0.0.0-20250718192347-d7830d968c56 // indirect
|
||||||
software.sslmate.com/src/go-pkcs12 v0.5.0 // indirect
|
software.sslmate.com/src/go-pkcs12 v0.6.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
61
go.sum
61
go.sum
@@ -6,29 +6,23 @@ github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151X
|
|||||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||||
github.com/docker/docker v28.3.2+incompatible h1:wn66NJ6pWB1vBZIilP8G3qQPqHy5XymfYn5vsqeA5oA=
|
github.com/docker/docker v28.4.0+incompatible h1:KVC7bz5zJY/4AZe/78BIvCnPsLaC9T/zh72xnlrTTOk=
|
||||||
github.com/docker/docker v28.3.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v28.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/fosrl/newt v0.0.0-20250717220102-cd86e6b6de83 h1:jI6tP2sJNNb70Y+Ixq+oI06fDPnGUbarz/r67g7KvB8=
|
github.com/fosrl/newt v0.0.0-20250730062419-3ccd755d557a h1:bUGN4piHlcqgfdRLrwqiLZZxgcitzBzNDQS1+CHSmJI=
|
||||||
github.com/fosrl/newt v0.0.0-20250717220102-cd86e6b6de83/go.mod h1:oqHsCx1rsEc8hAVGVXemfeolIwlr19biJSQiLYi7Mvo=
|
github.com/fosrl/newt v0.0.0-20250730062419-3ccd755d557a/go.mod h1:PbiPYp1hbL07awrmbqTSTz7lTenieTHN6cIkUVCGD3I=
|
||||||
github.com/fosrl/newt v0.0.0-20250718235538-510e78437ca4 h1:bK/MQyTOLGthrXZ7ExvOCdW0EH0o9b5vwk/+UKnNdg0=
|
github.com/fosrl/newt v0.0.0-20250929233849-71c5bf7e65f7 h1:6bSU8Efyhx1SR53iSw1Wjk5V8vDfizGAudq/GlE9b+o=
|
||||||
github.com/fosrl/newt v0.0.0-20250718235538-510e78437ca4/go.mod h1:oqHsCx1rsEc8hAVGVXemfeolIwlr19biJSQiLYi7Mvo=
|
github.com/fosrl/newt v0.0.0-20250929233849-71c5bf7e65f7/go.mod h1:Ac0k2FmAMC+hu21rAK+p7EnnEGrqKO/QZuGTVHA/XDM=
|
||||||
github.com/fosrl/newt v0.0.0-20250724190153-64c22a94a47a h1:Jgd60yfFJxb5z6L3LcoraaosHjiRgKLnMz6T3mv3D4Q=
|
|
||||||
github.com/fosrl/newt v0.0.0-20250724190153-64c22a94a47a/go.mod h1:oqHsCx1rsEc8hAVGVXemfeolIwlr19biJSQiLYi7Mvo=
|
|
||||||
github.com/fosrl/newt v0.0.0-20250724194014-008be54c554a h1:17r/Uhef6aIxpO0xYGI3771LJx7cTyc1WziDOgghc54=
|
|
||||||
github.com/fosrl/newt v0.0.0-20250724194014-008be54c554a/go.mod h1:oqHsCx1rsEc8hAVGVXemfeolIwlr19biJSQiLYi7Mvo=
|
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
|
||||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
@@ -39,8 +33,6 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
|
|||||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
|
||||||
github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
|
github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
|
||||||
github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o=
|
github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o=
|
||||||
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
||||||
@@ -59,8 +51,6 @@ github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW
|
|||||||
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
||||||
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
||||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU=
|
||||||
@@ -73,55 +63,42 @@ go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mx
|
|||||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
|
||||||
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4=
|
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4=
|
||||||
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc=
|
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc=
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
|
||||||
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+ZbWg+4sHnLp52d5yiIPUxMBSt4X9A=
|
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+ZbWg+4sHnLp52d5yiIPUxMBSt4X9A=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw=
|
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw=
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 h1:3GDAcqdIg1ozBNLgPy4SLT84nfcBjr6rhGtXYtrkWLU=
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 h1:3GDAcqdIg1ozBNLgPy4SLT84nfcBjr6rhGtXYtrkWLU=
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10/go.mod h1:T97yPqesLiNrOYxkwmhMI0ZIlJDm+p0PMR8eRVeR5tQ=
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10/go.mod h1:T97yPqesLiNrOYxkwmhMI0ZIlJDm+p0PMR8eRVeR5tQ=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gvisor.dev/gvisor v0.0.0-20250718192347-d7830d968c56 h1:H+qymc2ndLKNFR5TcaPmsHGiJnhJMqeofBYSRq4oG3c=
|
gvisor.dev/gvisor v0.0.0-20250718192347-d7830d968c56 h1:H+qymc2ndLKNFR5TcaPmsHGiJnhJMqeofBYSRq4oG3c=
|
||||||
gvisor.dev/gvisor v0.0.0-20250718192347-d7830d968c56/go.mod h1:i8iCZyAdwRnLZYaIi2NUL1gfNtAveqxkKAe0JfAv9Bs=
|
gvisor.dev/gvisor v0.0.0-20250718192347-d7830d968c56/go.mod h1:i8iCZyAdwRnLZYaIi2NUL1gfNtAveqxkKAe0JfAv9Bs=
|
||||||
software.sslmate.com/src/go-pkcs12 v0.5.0 h1:EC6R394xgENTpZ4RltKydeDUjtlM5drOYIG9c6TVj2M=
|
software.sslmate.com/src/go-pkcs12 v0.6.0 h1:f3sQittAeF+pao32Vb+mkli+ZyT+VwKaD014qFGq6oU=
|
||||||
software.sslmate.com/src/go-pkcs12 v0.5.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
|
software.sslmate.com/src/go-pkcs12 v0.6.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ type PeerStatus struct {
|
|||||||
Connected bool `json:"connected"`
|
Connected bool `json:"connected"`
|
||||||
RTT time.Duration `json:"rtt"`
|
RTT time.Duration `json:"rtt"`
|
||||||
LastSeen time.Time `json:"lastSeen"`
|
LastSeen time.Time `json:"lastSeen"`
|
||||||
|
Endpoint string `json:"endpoint,omitempty"`
|
||||||
|
IsRelay bool `json:"isRelay"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatusResponse is returned by the status endpoint
|
// StatusResponse is returned by the status endpoint
|
||||||
@@ -30,6 +32,7 @@ type StatusResponse struct {
|
|||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Connected bool `json:"connected"`
|
Connected bool `json:"connected"`
|
||||||
TunnelIP string `json:"tunnelIP,omitempty"`
|
TunnelIP string `json:"tunnelIP,omitempty"`
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
PeerStatuses map[int]*PeerStatus `json:"peers,omitempty"`
|
PeerStatuses map[int]*PeerStatus `json:"peers,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +45,8 @@ type HTTPServer struct {
|
|||||||
peerStatuses map[int]*PeerStatus
|
peerStatuses map[int]*PeerStatus
|
||||||
connectedAt time.Time
|
connectedAt time.Time
|
||||||
isConnected bool
|
isConnected bool
|
||||||
|
tunnelIP string
|
||||||
|
version string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHTTPServer creates a new HTTP server
|
// NewHTTPServer creates a new HTTP server
|
||||||
@@ -87,8 +92,8 @@ func (s *HTTPServer) GetConnectionChannel() <-chan ConnectionRequest {
|
|||||||
return s.connectionChan
|
return s.connectionChan
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePeerStatus updates the status of a peer
|
// UpdatePeerStatus updates the status of a peer including endpoint and relay info
|
||||||
func (s *HTTPServer) UpdatePeerStatus(siteID int, connected bool, rtt time.Duration) {
|
func (s *HTTPServer) UpdatePeerStatus(siteID int, connected bool, rtt time.Duration, endpoint string, isRelay bool) {
|
||||||
s.statusMu.Lock()
|
s.statusMu.Lock()
|
||||||
defer s.statusMu.Unlock()
|
defer s.statusMu.Unlock()
|
||||||
|
|
||||||
@@ -103,6 +108,8 @@ func (s *HTTPServer) UpdatePeerStatus(siteID int, connected bool, rtt time.Durat
|
|||||||
status.Connected = connected
|
status.Connected = connected
|
||||||
status.RTT = rtt
|
status.RTT = rtt
|
||||||
status.LastSeen = time.Now()
|
status.LastSeen = time.Now()
|
||||||
|
status.Endpoint = endpoint
|
||||||
|
status.IsRelay = isRelay
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetConnectionStatus sets the overall connection status
|
// SetConnectionStatus sets the overall connection status
|
||||||
@@ -120,6 +127,37 @@ func (s *HTTPServer) SetConnectionStatus(isConnected bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetTunnelIP sets the tunnel IP address
|
||||||
|
func (s *HTTPServer) SetTunnelIP(tunnelIP string) {
|
||||||
|
s.statusMu.Lock()
|
||||||
|
defer s.statusMu.Unlock()
|
||||||
|
s.tunnelIP = tunnelIP
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetVersion sets the olm version
|
||||||
|
func (s *HTTPServer) SetVersion(version string) {
|
||||||
|
s.statusMu.Lock()
|
||||||
|
defer s.statusMu.Unlock()
|
||||||
|
s.version = version
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatePeerRelayStatus updates only the relay status of a peer
|
||||||
|
func (s *HTTPServer) UpdatePeerRelayStatus(siteID int, endpoint string, isRelay bool) {
|
||||||
|
s.statusMu.Lock()
|
||||||
|
defer s.statusMu.Unlock()
|
||||||
|
|
||||||
|
status, exists := s.peerStatuses[siteID]
|
||||||
|
if !exists {
|
||||||
|
status = &PeerStatus{
|
||||||
|
SiteID: siteID,
|
||||||
|
}
|
||||||
|
s.peerStatuses[siteID] = status
|
||||||
|
}
|
||||||
|
|
||||||
|
status.Endpoint = endpoint
|
||||||
|
status.IsRelay = isRelay
|
||||||
|
}
|
||||||
|
|
||||||
// handleConnect handles the /connect endpoint
|
// handleConnect handles the /connect endpoint
|
||||||
func (s *HTTPServer) handleConnect(w http.ResponseWriter, r *http.Request) {
|
func (s *HTTPServer) handleConnect(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != http.MethodPost {
|
if r.Method != http.MethodPost {
|
||||||
@@ -163,6 +201,8 @@ func (s *HTTPServer) handleStatus(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
resp := StatusResponse{
|
resp := StatusResponse{
|
||||||
Connected: s.isConnected,
|
Connected: s.isConnected,
|
||||||
|
TunnelIP: s.tunnelIP,
|
||||||
|
Version: s.version,
|
||||||
PeerStatuses: s.peerStatuses,
|
PeerStatuses: s.peerStatuses,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
351
main.go
351
main.go
@@ -10,10 +10,12 @@ import (
|
|||||||
"os/signal"
|
"os/signal"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fosrl/newt/logger"
|
"github.com/fosrl/newt/logger"
|
||||||
|
"github.com/fosrl/newt/updates"
|
||||||
"github.com/fosrl/newt/websocket"
|
"github.com/fosrl/newt/websocket"
|
||||||
"github.com/fosrl/olm/httpserver"
|
"github.com/fosrl/olm/httpserver"
|
||||||
"github.com/fosrl/olm/peermonitor"
|
"github.com/fosrl/olm/peermonitor"
|
||||||
@@ -25,6 +27,33 @@ import (
|
|||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 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 main() {
|
func main() {
|
||||||
// Check if we're running as a Windows service
|
// Check if we're running as a Windows service
|
||||||
if isWindowsService() {
|
if isWindowsService() {
|
||||||
@@ -34,8 +63,15 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle service management commands on Windows
|
// Handle service management commands on Windows
|
||||||
if runtime.GOOS == "windows" && len(os.Args) > 1 {
|
if runtime.GOOS == "windows" {
|
||||||
switch os.Args[1] {
|
var command string
|
||||||
|
if len(os.Args) > 1 {
|
||||||
|
command = os.Args[1]
|
||||||
|
} else {
|
||||||
|
command = "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
switch command {
|
||||||
case "install":
|
case "install":
|
||||||
err := installService()
|
err := installService()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -109,15 +145,27 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
case "config":
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
showServiceConfig()
|
||||||
|
} else {
|
||||||
|
fmt.Println("Service configuration is only available on Windows")
|
||||||
|
}
|
||||||
|
return
|
||||||
case "help", "--help", "-h":
|
case "help", "--help", "-h":
|
||||||
fmt.Println("Olm WireGuard VPN Client")
|
fmt.Println("Olm WireGuard VPN Client")
|
||||||
fmt.Println("\nWindows Service Management:")
|
fmt.Println("\nWindows Service Management:")
|
||||||
fmt.Println(" install Install the service")
|
fmt.Println(" install Install the service")
|
||||||
fmt.Println(" remove Remove the service")
|
fmt.Println(" remove Remove the service")
|
||||||
fmt.Println(" start Start the service")
|
fmt.Println(" start [args] Start the service with optional arguments")
|
||||||
fmt.Println(" stop Stop the service")
|
fmt.Println(" stop Stop the service")
|
||||||
fmt.Println(" status Show service status")
|
fmt.Println(" status Show service status")
|
||||||
fmt.Println(" debug Run service in debug mode")
|
fmt.Println(" debug [args] Run service in debug mode with optional arguments")
|
||||||
|
fmt.Println(" logs Tail the service log file")
|
||||||
|
fmt.Println(" config Show current service configuration")
|
||||||
|
fmt.Println("\nExamples:")
|
||||||
|
fmt.Println(" olm start --enable-http --http-addr :9452")
|
||||||
|
fmt.Println(" olm debug --endpoint https://example.com --id myid --secret mysecret")
|
||||||
fmt.Println("\nFor console mode, run without arguments or with standard flags.")
|
fmt.Println("\nFor console mode, run without arguments or with standard flags.")
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
@@ -197,7 +245,11 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
httpAddr = os.Getenv("HTTP_ADDR")
|
httpAddr = os.Getenv("HTTP_ADDR")
|
||||||
pingIntervalStr := os.Getenv("PING_INTERVAL")
|
pingIntervalStr := os.Getenv("PING_INTERVAL")
|
||||||
pingTimeoutStr := os.Getenv("PING_TIMEOUT")
|
pingTimeoutStr := os.Getenv("PING_TIMEOUT")
|
||||||
doHolepunch = os.Getenv("HOLEPUNCH") == "true" // Default to true, can be overridden by flag
|
enableHTTPEnv := os.Getenv("ENABLE_HTTP")
|
||||||
|
holepunchEnv := os.Getenv("HOLEPUNCH")
|
||||||
|
|
||||||
|
enableHTTP = enableHTTPEnv == "true"
|
||||||
|
doHolepunch = holepunchEnv == "true"
|
||||||
|
|
||||||
if endpoint == "" {
|
if endpoint == "" {
|
||||||
serviceFlags.StringVar(&endpoint, "endpoint", "", "Endpoint of your Pangolin server")
|
serviceFlags.StringVar(&endpoint, "endpoint", "", "Endpoint of your Pangolin server")
|
||||||
@@ -229,8 +281,14 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
if pingTimeoutStr == "" {
|
if pingTimeoutStr == "" {
|
||||||
serviceFlags.StringVar(&pingTimeoutStr, "ping-timeout", "5s", " Timeout for each ping (default 3s)")
|
serviceFlags.StringVar(&pingTimeoutStr, "ping-timeout", "5s", " Timeout for each ping (default 3s)")
|
||||||
}
|
}
|
||||||
serviceFlags.BoolVar(&enableHTTP, "enable-http", false, "Enable HTT server for receiving connection requests")
|
if enableHTTPEnv == "" {
|
||||||
serviceFlags.BoolVar(&doHolepunch, "holepunch", false, "Enable hole punching (default false)")
|
serviceFlags.BoolVar(&enableHTTP, "enable-http", false, "Enable HTT server for receiving connection requests")
|
||||||
|
}
|
||||||
|
if holepunchEnv == "" {
|
||||||
|
serviceFlags.BoolVar(&doHolepunch, "holepunch", false, "Enable hole punching (default false)")
|
||||||
|
}
|
||||||
|
|
||||||
|
version := serviceFlags.Bool("version", false, "Print the version")
|
||||||
|
|
||||||
// Parse the service arguments
|
// Parse the service arguments
|
||||||
if err := serviceFlags.Parse(args); err != nil {
|
if err := serviceFlags.Parse(args); err != nil {
|
||||||
@@ -272,6 +330,18 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
loggerLevel := parseLogLevel(logLevel)
|
loggerLevel := parseLogLevel(logLevel)
|
||||||
logger.GetLogger().SetLevel(parseLogLevel(logLevel))
|
logger.GetLogger().SetLevel(parseLogLevel(logLevel))
|
||||||
|
|
||||||
|
olmVersion := "version_replaceme"
|
||||||
|
if *version {
|
||||||
|
fmt.Println("Olm version " + olmVersion)
|
||||||
|
os.Exit(0)
|
||||||
|
} else {
|
||||||
|
logger.Info("Olm version " + olmVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := updates.CheckForUpdate("fosrl", "olm", olmVersion); err != nil {
|
||||||
|
logger.Debug("Failed to check for updates: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Log startup information
|
// Log startup information
|
||||||
logger.Debug("Olm service starting...")
|
logger.Debug("Olm service starting...")
|
||||||
logger.Debug("Parameters: endpoint='%s', id='%s', secret='%s'", endpoint, id, secret)
|
logger.Debug("Parameters: endpoint='%s', id='%s', secret='%s'", endpoint, id, secret)
|
||||||
@@ -313,6 +383,7 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
var httpServer *httpserver.HTTPServer
|
var httpServer *httpserver.HTTPServer
|
||||||
if enableHTTP {
|
if enableHTTP {
|
||||||
httpServer = httpserver.NewHTTPServer(httpAddr)
|
httpServer = httpserver.NewHTTPServer(httpAddr)
|
||||||
|
httpServer.SetVersion(olmVersion)
|
||||||
if err := httpServer.Start(); err != nil {
|
if err := httpServer.Start(); err != nil {
|
||||||
logger.Fatal("Failed to start HTTP server: %v", err)
|
logger.Fatal("Failed to start HTTP server: %v", err)
|
||||||
}
|
}
|
||||||
@@ -354,43 +425,6 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// // wait until we have a client id and secret and endpoint
|
|
||||||
// waitCount := 0
|
|
||||||
// for id == "" || secret == "" || endpoint == "" {
|
|
||||||
// select {
|
|
||||||
// case <-ctx.Done():
|
|
||||||
// logger.Info("Context cancelled while waiting for credentials")
|
|
||||||
// return
|
|
||||||
// default:
|
|
||||||
// missing := []string{}
|
|
||||||
// if id == "" {
|
|
||||||
// missing = append(missing, "id")
|
|
||||||
// }
|
|
||||||
// if secret == "" {
|
|
||||||
// missing = append(missing, "secret")
|
|
||||||
// }
|
|
||||||
// if endpoint == "" {
|
|
||||||
// missing = append(missing, "endpoint")
|
|
||||||
// }
|
|
||||||
// waitCount++
|
|
||||||
// if waitCount%10 == 1 { // Log every 10 seconds instead of every second
|
|
||||||
// logger.Debug("Waiting for missing parameters: %v (waiting %d seconds)", missing, waitCount)
|
|
||||||
// }
|
|
||||||
// time.Sleep(1 * time.Second)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// parse the mtu string into an int
|
|
||||||
mtuInt, err = strconv.Atoi(mtu)
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal("Failed to parse MTU: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
privateKey, err = wgtypes.GeneratePrivateKey()
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal("Failed to generate private key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new olm
|
// Create a new olm
|
||||||
olm, err := websocket.NewClient(
|
olm, err := websocket.NewClient(
|
||||||
"olm",
|
"olm",
|
||||||
@@ -405,6 +439,44 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
}
|
}
|
||||||
endpoint = olm.GetConfig().Endpoint // Update endpoint from config
|
endpoint = olm.GetConfig().Endpoint // Update endpoint from config
|
||||||
id = olm.GetConfig().ID // Update ID from config
|
id = olm.GetConfig().ID // Update ID from config
|
||||||
|
secret = olm.GetConfig().Secret // Update secret from config
|
||||||
|
|
||||||
|
// wait until we have a client id and secret and endpoint
|
||||||
|
waitCount := 0
|
||||||
|
for id == "" || secret == "" || endpoint == "" {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
logger.Info("Context cancelled while waiting for credentials")
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
missing := []string{}
|
||||||
|
if id == "" {
|
||||||
|
missing = append(missing, "id")
|
||||||
|
}
|
||||||
|
if secret == "" {
|
||||||
|
missing = append(missing, "secret")
|
||||||
|
}
|
||||||
|
if endpoint == "" {
|
||||||
|
missing = append(missing, "endpoint")
|
||||||
|
}
|
||||||
|
waitCount++
|
||||||
|
if waitCount%10 == 1 { // Log every 10 seconds instead of every second
|
||||||
|
logger.Debug("Waiting for missing parameters: %v (waiting %d seconds)", missing, waitCount)
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the mtu string into an int
|
||||||
|
mtuInt, err = strconv.Atoi(mtu)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("Failed to parse MTU: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
privateKey, err = wgtypes.GeneratePrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("Failed to generate private key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create TUN device and network stack
|
// Create TUN device and network stack
|
||||||
var dev *device.Device
|
var dev *device.Device
|
||||||
@@ -419,7 +491,7 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
olm.RegisterHandler("olm/wg/holepunch", func(msg websocket.WSMessage) {
|
olm.RegisterHandler("olm/wg/holepunch/all", func(msg websocket.WSMessage) {
|
||||||
logger.Debug("Received message: %v", msg.Data)
|
logger.Debug("Received message: %v", msg.Data)
|
||||||
|
|
||||||
jsonData, err := json.Marshal(msg.Data)
|
jsonData, err := json.Marshal(msg.Data)
|
||||||
@@ -433,12 +505,52 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
gerbilServerPubKey = holePunchData.ServerPubKey
|
// Create a new stopHolepunch channel for the new set of goroutines
|
||||||
|
stopHolepunch = make(chan struct{})
|
||||||
|
|
||||||
go keepSendingUDPHolePunch(holePunchData.Endpoint, id, sourcePort)
|
// Start a single hole punch goroutine for all exit nodes
|
||||||
|
logger.Info("Starting hole punch for %d exit nodes", len(holePunchData.ExitNodes))
|
||||||
|
go keepSendingUDPHolePunchToMultipleExitNodes(holePunchData.ExitNodes, id, sourcePort)
|
||||||
|
})
|
||||||
|
|
||||||
|
olm.RegisterHandler("olm/wg/holepunch", func(msg websocket.WSMessage) {
|
||||||
|
// THIS ENDPOINT IS FOR BACKWARD COMPATIBILITY
|
||||||
|
logger.Debug("Received message: %v", msg.Data)
|
||||||
|
|
||||||
|
type LegacyHolePunchData struct {
|
||||||
|
ServerPubKey string `json:"serverPubKey"`
|
||||||
|
Endpoint string `json:"endpoint"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var legacyHolePunchData LegacyHolePunchData
|
||||||
|
|
||||||
|
jsonData, err := json.Marshal(msg.Data)
|
||||||
|
if err != nil {
|
||||||
|
logger.Info("Error marshaling data: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(jsonData, &legacyHolePunchData); err != nil {
|
||||||
|
logger.Info("Error unmarshaling target data: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop any existing hole punch goroutines by closing the current channel
|
||||||
|
select {
|
||||||
|
case <-stopHolepunch:
|
||||||
|
// Channel already closed
|
||||||
|
default:
|
||||||
|
close(stopHolepunch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new stopHolepunch channel for the new set of goroutines
|
||||||
|
stopHolepunch = make(chan struct{})
|
||||||
|
|
||||||
|
// Start hole punching for each exit node
|
||||||
|
logger.Info("Starting hole punch for exit node: %s with public key: %s", legacyHolePunchData.Endpoint, legacyHolePunchData.ServerPubKey)
|
||||||
|
go keepSendingUDPHolePunch(legacyHolePunchData.Endpoint, id, sourcePort, legacyHolePunchData.ServerPubKey)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Register handlers for different message types
|
|
||||||
olm.RegisterHandler("olm/wg/connect", func(msg websocket.WSMessage) {
|
olm.RegisterHandler("olm/wg/connect", func(msg websocket.WSMessage) {
|
||||||
logger.Debug("Received message: %v", msg.Data)
|
logger.Debug("Received message: %v", msg.Data)
|
||||||
|
|
||||||
@@ -455,7 +567,8 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
close(stopHolepunch)
|
close(stopHolepunch)
|
||||||
|
|
||||||
// wait 10 milliseconds to ensure the previous connection is closed
|
// wait 10 milliseconds to ensure the previous connection is closed
|
||||||
time.Sleep(10 * time.Millisecond)
|
logger.Debug("Waiting 500 milliseconds to ensure previous connection is closed")
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
|
||||||
// if there is an existing tunnel then close it
|
// if there is an existing tunnel then close it
|
||||||
if dev != nil {
|
if dev != nil {
|
||||||
@@ -475,9 +588,6 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tdev, err = func() (tun.Device, error) {
|
tdev, err = func() (tun.Device, error) {
|
||||||
tunFdStr := os.Getenv(ENV_WG_TUN_FD)
|
|
||||||
|
|
||||||
// if on macOS, call findUnusedUTUN to get a new utun device
|
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
interfaceName, err := findUnusedUTUN()
|
interfaceName, err := findUnusedUTUN()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -485,12 +595,10 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
}
|
}
|
||||||
return tun.CreateTUN(interfaceName, mtuInt)
|
return tun.CreateTUN(interfaceName, mtuInt)
|
||||||
}
|
}
|
||||||
|
if tunFdStr := os.Getenv(ENV_WG_TUN_FD); tunFdStr != "" {
|
||||||
if tunFdStr == "" {
|
return createTUNFromFD(tunFdStr, mtuInt)
|
||||||
return tun.CreateTUN(interfaceName, mtuInt)
|
|
||||||
}
|
}
|
||||||
|
return tun.CreateTUN(interfaceName, mtuInt)
|
||||||
return createTUNFromFD(tunFdStr, mtuInt)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -498,26 +606,19 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
realInterfaceName, err2 := tdev.Name()
|
if realInterfaceName, err2 := tdev.Name(); err2 == nil {
|
||||||
if err2 == nil {
|
|
||||||
interfaceName = realInterfaceName
|
interfaceName = realInterfaceName
|
||||||
}
|
}
|
||||||
|
|
||||||
// open UAPI file (or use supplied fd)
|
|
||||||
fileUAPI, err := func() (*os.File, error) {
|
fileUAPI, err := func() (*os.File, error) {
|
||||||
uapiFdStr := os.Getenv(ENV_WG_UAPI_FD)
|
if uapiFdStr := os.Getenv(ENV_WG_UAPI_FD); uapiFdStr != "" {
|
||||||
if uapiFdStr == "" {
|
fd, err := strconv.ParseUint(uapiFdStr, 10, 32)
|
||||||
return uapiOpen(interfaceName)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return os.NewFile(uintptr(fd), ""), nil
|
||||||
}
|
}
|
||||||
|
return uapiOpen(interfaceName)
|
||||||
// use supplied fd
|
|
||||||
|
|
||||||
fd, err := strconv.ParseUint(uapiFdStr, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return os.NewFile(uintptr(fd), ""), nil
|
|
||||||
}()
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("UAPI listen error: %v", err)
|
logger.Error("UAPI listen error: %v", err)
|
||||||
@@ -525,12 +626,7 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dev = device.NewDevice(tdev, NewFixedPortBind(uint16(sourcePort)), device.NewLogger(
|
dev = device.NewDevice(tdev, NewFixedPortBind(uint16(sourcePort)), device.NewLogger(mapToWireGuardLogLevel(loggerLevel), "wireguard: "))
|
||||||
mapToWireGuardLogLevel(loggerLevel),
|
|
||||||
"wireguard: ",
|
|
||||||
))
|
|
||||||
|
|
||||||
errs := make(chan error)
|
|
||||||
|
|
||||||
uapiListener, err = uapiListen(interfaceName, fileUAPI)
|
uapiListener, err = uapiListen(interfaceName, fileUAPI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -542,31 +638,39 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
for {
|
for {
|
||||||
conn, err := uapiListener.Accept()
|
conn, err := uapiListener.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs <- err
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
go dev.IpcHandle(conn)
|
go dev.IpcHandle(conn)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
logger.Info("UAPI listener started")
|
logger.Info("UAPI listener started")
|
||||||
|
|
||||||
// Bring up the device
|
if err = dev.Up(); err != nil {
|
||||||
err = dev.Up()
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("Failed to bring up WireGuard device: %v", err)
|
logger.Error("Failed to bring up WireGuard device: %v", err)
|
||||||
}
|
}
|
||||||
|
if err = ConfigureInterface(interfaceName, wgData); err != nil {
|
||||||
// configure the interface
|
|
||||||
err = ConfigureInterface(realInterfaceName, wgData)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("Failed to configure interface: %v", err)
|
logger.Error("Failed to configure interface: %v", err)
|
||||||
}
|
}
|
||||||
|
if httpServer != nil {
|
||||||
|
httpServer.SetTunnelIP(wgData.TunnelIP)
|
||||||
|
}
|
||||||
|
|
||||||
peerMonitor = peermonitor.NewPeerMonitor(
|
peerMonitor = peermonitor.NewPeerMonitor(
|
||||||
func(siteID int, connected bool, rtt time.Duration) {
|
func(siteID int, connected bool, rtt time.Duration) {
|
||||||
if httpServer != nil {
|
if httpServer != nil {
|
||||||
httpServer.UpdatePeerStatus(siteID, connected, rtt)
|
// Find the site config to get endpoint information
|
||||||
|
var endpoint string
|
||||||
|
var isRelay bool
|
||||||
|
for _, site := range wgData.Sites {
|
||||||
|
if site.SiteId == siteID {
|
||||||
|
endpoint = site.Endpoint
|
||||||
|
// TODO: We'll need to track relay status separately
|
||||||
|
// For now, assume not using relay unless we get relay data
|
||||||
|
isRelay = !doHolepunch
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
httpServer.UpdatePeerStatus(siteID, connected, rtt, endpoint, isRelay)
|
||||||
}
|
}
|
||||||
if connected {
|
if connected {
|
||||||
logger.Info("Peer %d is now connected (RTT: %v)", siteID, rtt)
|
logger.Info("Peer %d is now connected (RTT: %v)", siteID, rtt)
|
||||||
@@ -580,24 +684,23 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
doHolepunch,
|
doHolepunch,
|
||||||
)
|
)
|
||||||
|
|
||||||
// loop over the sites and call ConfigurePeer for each one
|
for i := range wgData.Sites {
|
||||||
for _, site := range wgData.Sites {
|
site := &wgData.Sites[i] // Use a pointer to modify the struct in the slice
|
||||||
if httpServer != nil {
|
if httpServer != nil {
|
||||||
httpServer.UpdatePeerStatus(site.SiteId, false, 0)
|
httpServer.UpdatePeerStatus(site.SiteId, false, 0, site.Endpoint, false)
|
||||||
}
|
}
|
||||||
err = ConfigurePeer(dev, site, privateKey, endpoint)
|
|
||||||
if err != nil {
|
// Format the endpoint before configuring the peer.
|
||||||
|
site.Endpoint = formatEndpoint(site.Endpoint)
|
||||||
|
|
||||||
|
if err := ConfigurePeer(dev, *site, privateKey, endpoint); err != nil {
|
||||||
logger.Error("Failed to configure peer: %v", err)
|
logger.Error("Failed to configure peer: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if err := addRouteForServerIP(site.ServerIP, interfaceName); err != nil {
|
||||||
err = addRouteForServerIP(site.ServerIP, interfaceName)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("Failed to add route for peer: %v", err)
|
logger.Error("Failed to add route for peer: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add routes for remote subnets
|
|
||||||
if err := addRoutesForRemoteSubnets(site.RemoteSubnets, interfaceName); err != nil {
|
if err := addRoutesForRemoteSubnets(site.RemoteSubnets, interfaceName); err != nil {
|
||||||
logger.Error("Failed to add routes for remote subnets: %v", err)
|
logger.Error("Failed to add routes for remote subnets: %v", err)
|
||||||
return
|
return
|
||||||
@@ -640,18 +743,31 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
|
|
||||||
// Update the peer in WireGuard
|
// Update the peer in WireGuard
|
||||||
if dev != nil {
|
if dev != nil {
|
||||||
// Find the existing peer to get old RemoteSubnets
|
// Find the existing peer to get old data
|
||||||
var oldRemoteSubnets string
|
var oldRemoteSubnets string
|
||||||
|
var oldPublicKey string
|
||||||
for _, site := range wgData.Sites {
|
for _, site := range wgData.Sites {
|
||||||
if site.SiteId == updateData.SiteId {
|
if site.SiteId == updateData.SiteId {
|
||||||
oldRemoteSubnets = site.RemoteSubnets
|
oldRemoteSubnets = site.RemoteSubnets
|
||||||
|
oldPublicKey = site.PublicKey
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the public key has changed, remove the old peer first
|
||||||
|
if oldPublicKey != "" && oldPublicKey != updateData.PublicKey {
|
||||||
|
logger.Info("Public key changed for site %d, removing old peer with key %s", updateData.SiteId, oldPublicKey)
|
||||||
|
if err := RemovePeer(dev, updateData.SiteId, oldPublicKey); err != nil {
|
||||||
|
logger.Error("Failed to remove old peer: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format the endpoint before updating the peer.
|
||||||
|
siteConfig.Endpoint = formatEndpoint(siteConfig.Endpoint)
|
||||||
|
|
||||||
if err := ConfigurePeer(dev, siteConfig, privateKey, endpoint); err != nil {
|
if err := ConfigurePeer(dev, siteConfig, privateKey, endpoint); err != nil {
|
||||||
logger.Error("Failed to update peer: %v", err)
|
logger.Error("Failed to update peer: %v", err)
|
||||||
// Send error response if needed
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -671,9 +787,8 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
|
|
||||||
// Update successful
|
// Update successful
|
||||||
logger.Info("Successfully updated peer for site %d", updateData.SiteId)
|
logger.Info("Successfully updated peer for site %d", updateData.SiteId)
|
||||||
// If this is part of a WgData structure, update it
|
for i := range wgData.Sites {
|
||||||
for i, site := range wgData.Sites {
|
if wgData.Sites[i].SiteId == updateData.SiteId {
|
||||||
if site.SiteId == updateData.SiteId {
|
|
||||||
wgData.Sites[i] = siteConfig
|
wgData.Sites[i] = siteConfig
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -711,19 +826,17 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
|
|
||||||
// Add the peer to WireGuard
|
// Add the peer to WireGuard
|
||||||
if dev != nil {
|
if dev != nil {
|
||||||
|
// Format the endpoint before adding the new peer.
|
||||||
|
siteConfig.Endpoint = formatEndpoint(siteConfig.Endpoint)
|
||||||
|
|
||||||
if err := ConfigurePeer(dev, siteConfig, privateKey, endpoint); err != nil {
|
if err := ConfigurePeer(dev, siteConfig, privateKey, endpoint); err != nil {
|
||||||
logger.Error("Failed to add peer: %v", err)
|
logger.Error("Failed to add peer: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if err := addRouteForServerIP(siteConfig.ServerIP, interfaceName); err != nil {
|
||||||
// Add route for the new peer
|
|
||||||
err = addRouteForServerIP(siteConfig.ServerIP, interfaceName)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("Failed to add route for new peer: %v", err)
|
logger.Error("Failed to add route for new peer: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add routes for remote subnets
|
|
||||||
if err := addRoutesForRemoteSubnets(siteConfig.RemoteSubnets, interfaceName); err != nil {
|
if err := addRoutesForRemoteSubnets(siteConfig.RemoteSubnets, interfaceName); err != nil {
|
||||||
logger.Error("Failed to add routes for remote subnets: %v", err)
|
logger.Error("Failed to add routes for remote subnets: %v", err)
|
||||||
return
|
return
|
||||||
@@ -812,18 +925,23 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var removeData RelayPeerData
|
var relayData RelayPeerData
|
||||||
if err := json.Unmarshal(jsonData, &removeData); err != nil {
|
if err := json.Unmarshal(jsonData, &relayData); err != nil {
|
||||||
logger.Error("Error unmarshaling remove data: %v", err)
|
logger.Error("Error unmarshaling relay data: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
primaryRelay, err := resolveDomain(removeData.Endpoint)
|
primaryRelay, err := resolveDomain(relayData.Endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warn("Failed to resolve primary relay endpoint: %v", err)
|
logger.Warn("Failed to resolve primary relay endpoint: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
peerMonitor.HandleFailover(removeData.SiteId, primaryRelay)
|
// Update HTTP server to mark this peer as using relay
|
||||||
|
if httpServer != nil {
|
||||||
|
httpServer.UpdatePeerRelayStatus(relayData.SiteId, relayData.Endpoint, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
peerMonitor.HandleFailover(relayData.SiteId, primaryRelay)
|
||||||
})
|
})
|
||||||
|
|
||||||
olm.RegisterHandler("olm/register/no-sites", func(msg websocket.WSMessage) {
|
olm.RegisterHandler("olm/register/no-sites", func(msg websocket.WSMessage) {
|
||||||
@@ -866,8 +984,9 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
|
|||||||
logger.Debug("Sending registration message to server with public key: %s and relay: %v", publicKey, !doHolepunch)
|
logger.Debug("Sending registration message to server with public key: %s and relay: %v", publicKey, !doHolepunch)
|
||||||
|
|
||||||
stopRegister = olm.SendMessageInterval("olm/wg/register", map[string]interface{}{
|
stopRegister = olm.SendMessageInterval("olm/wg/register", map[string]interface{}{
|
||||||
"publicKey": publicKey.String(),
|
"publicKey": publicKey.String(),
|
||||||
"relay": !doHolepunch,
|
"relay": !doHolepunch,
|
||||||
|
"olmVersion": olmVersion,
|
||||||
}, 1*time.Second)
|
}, 1*time.Second)
|
||||||
|
|
||||||
go keepSendingPing(olm)
|
go keepSendingPing(olm)
|
||||||
|
|||||||
88
olm.iss
Normal file
88
olm.iss
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
; Script generated by the Inno Setup Script Wizard.
|
||||||
|
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||||
|
|
||||||
|
#define MyAppName "olm"
|
||||||
|
#define MyAppVersion "1.0.0"
|
||||||
|
#define MyAppPublisher "Fossorial Inc."
|
||||||
|
#define MyAppURL "https://fossorial.io"
|
||||||
|
#define MyAppExeName "olm.exe"
|
||||||
|
|
||||||
|
[Setup]
|
||||||
|
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
|
||||||
|
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
|
||||||
|
AppId={{44A24E4C-B616-476F-ADE7-8D56B930959E}
|
||||||
|
AppName={#MyAppName}
|
||||||
|
AppVersion={#MyAppVersion}
|
||||||
|
;AppVerName={#MyAppName} {#MyAppVersion}
|
||||||
|
AppPublisher={#MyAppPublisher}
|
||||||
|
AppPublisherURL={#MyAppURL}
|
||||||
|
AppSupportURL={#MyAppURL}
|
||||||
|
AppUpdatesURL={#MyAppURL}
|
||||||
|
DefaultDirName={autopf}\{#MyAppName}
|
||||||
|
UninstallDisplayIcon={app}\{#MyAppExeName}
|
||||||
|
; "ArchitecturesAllowed=x64compatible" specifies that Setup cannot run
|
||||||
|
; on anything but x64 and Windows 11 on Arm.
|
||||||
|
ArchitecturesAllowed=x64compatible
|
||||||
|
; "ArchitecturesInstallIn64BitMode=x64compatible" requests that the
|
||||||
|
; install be done in "64-bit mode" on x64 or Windows 11 on Arm,
|
||||||
|
; meaning it should use the native 64-bit Program Files directory and
|
||||||
|
; the 64-bit view of the registry.
|
||||||
|
ArchitecturesInstallIn64BitMode=x64compatible
|
||||||
|
DefaultGroupName={#MyAppName}
|
||||||
|
DisableProgramGroupPage=yes
|
||||||
|
; Uncomment the following line to run in non administrative install mode (install for current user only).
|
||||||
|
;PrivilegesRequired=lowest
|
||||||
|
OutputBaseFilename=mysetup
|
||||||
|
SolidCompression=yes
|
||||||
|
WizardStyle=modern
|
||||||
|
; Add this to ensure PATH changes are applied and the system is prompted for a restart if needed
|
||||||
|
RestartIfNeededByRun=no
|
||||||
|
ChangesEnvironment=true
|
||||||
|
|
||||||
|
[Languages]
|
||||||
|
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
|
||||||
|
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||||
|
|
||||||
|
[Icons]
|
||||||
|
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
||||||
|
|
||||||
|
[Registry]
|
||||||
|
; Add the application's installation directory to the system PATH environment variable.
|
||||||
|
; HKLM (HKEY_LOCAL_MACHINE) is used for system-wide changes.
|
||||||
|
; The 'Path' variable is located under 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'.
|
||||||
|
; ValueType: expandsz allows for environment variables (like %ProgramFiles%) in the path.
|
||||||
|
; ValueData: "{olddata};{app}" appends the current application directory to the existing PATH.
|
||||||
|
; Flags: uninsdeletevalue ensures the entry is removed upon uninstallation.
|
||||||
|
; Check: IsWin64 ensures this is applied on 64-bit systems, which matches ArchitecturesAllowed.
|
||||||
|
[Registry]
|
||||||
|
; Add the application's installation directory to the system PATH.
|
||||||
|
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; \
|
||||||
|
ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}"; \
|
||||||
|
Flags: uninsdeletevalue; Check: NeedsAddPath(ExpandConstant('{app}'))
|
||||||
|
|
||||||
|
[Code]
|
||||||
|
function NeedsAddPath(Path: string): boolean;
|
||||||
|
var
|
||||||
|
OrigPath: string;
|
||||||
|
begin
|
||||||
|
if not RegQueryStringValue(HKEY_LOCAL_MACHINE,
|
||||||
|
'SYSTEM\CurrentControlSet\Control\Session Manager\Environment',
|
||||||
|
'Path', OrigPath)
|
||||||
|
then begin
|
||||||
|
// Path variable doesn't exist at all, so we definitely need to add it.
|
||||||
|
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
|
||||||
|
Result := False
|
||||||
|
else
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
@@ -3,6 +3,7 @@ package peermonitor
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -204,12 +205,18 @@ func (pm *PeerMonitor) HandleFailover(siteID int, relayEndpoint string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for IPv6 and format the endpoint correctly
|
||||||
|
formattedEndpoint := relayEndpoint
|
||||||
|
if strings.Contains(relayEndpoint, ":") {
|
||||||
|
formattedEndpoint = fmt.Sprintf("[%s]", relayEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
// Configure WireGuard to use the relay
|
// Configure WireGuard to use the relay
|
||||||
wgConfig := fmt.Sprintf(`private_key=%s
|
wgConfig := fmt.Sprintf(`private_key=%s
|
||||||
public_key=%s
|
public_key=%s
|
||||||
allowed_ip=%s/32
|
allowed_ip=%s/32
|
||||||
endpoint=%s:21820
|
endpoint=%s:21820
|
||||||
persistent_keepalive_interval=1`, pm.privateKey, config.PublicKey, config.ServerIP, relayEndpoint)
|
persistent_keepalive_interval=1`, pm.privateKey, config.PublicKey, config.ServerIP, formattedEndpoint)
|
||||||
|
|
||||||
err := pm.device.IpcSet(wgConfig)
|
err := pm.device.IpcSet(wgConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -48,3 +48,7 @@ func setupWindowsEventLog() {
|
|||||||
func watchLogFile(end bool) error {
|
func watchLogFile(end bool) error {
|
||||||
return fmt.Errorf("watching log file is only available on Windows")
|
return fmt.Errorf("watching log file is only available on Windows")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func showServiceConfig() {
|
||||||
|
fmt.Println("Service configuration is only available on Windows")
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -69,12 +70,6 @@ func loadServiceArgs() ([]string, error) {
|
|||||||
return nil, fmt.Errorf("failed to read service args: %v", err)
|
return nil, fmt.Errorf("failed to read service args: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete the file after reading
|
|
||||||
err = os.Remove(argsPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to delete service args file: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var args []string
|
var args []string
|
||||||
err = json.Unmarshal(data, &args)
|
err = json.Unmarshal(data, &args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -95,7 +90,7 @@ func (s *olmService) Execute(args []string, r <-chan svc.ChangeRequest, changes
|
|||||||
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
|
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
|
||||||
changes <- svc.Status{State: svc.StartPending}
|
changes <- svc.Status{State: svc.StartPending}
|
||||||
|
|
||||||
s.elog.Info(1, "Service Execute called, starting main logic")
|
s.elog.Info(1, fmt.Sprintf("Service Execute called with args: %v", args))
|
||||||
|
|
||||||
// Load saved service arguments
|
// Load saved service arguments
|
||||||
savedArgs, err := loadServiceArgs()
|
savedArgs, err := loadServiceArgs()
|
||||||
@@ -104,7 +99,24 @@ func (s *olmService) Execute(args []string, r <-chan svc.ChangeRequest, changes
|
|||||||
// Continue with empty args if loading fails
|
// Continue with empty args if loading fails
|
||||||
savedArgs = []string{}
|
savedArgs = []string{}
|
||||||
}
|
}
|
||||||
s.args = savedArgs
|
|
||||||
|
// Combine service start args with saved args, giving priority to service start args
|
||||||
|
finalArgs := []string{}
|
||||||
|
if len(args) > 0 {
|
||||||
|
// Skip the first arg which is typically the service name
|
||||||
|
if len(args) > 1 {
|
||||||
|
finalArgs = append(finalArgs, args[1:]...)
|
||||||
|
}
|
||||||
|
s.elog.Info(1, fmt.Sprintf("Using service start parameters: %v", finalArgs))
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no service start parameters, use saved args
|
||||||
|
if len(finalArgs) == 0 && len(savedArgs) > 0 {
|
||||||
|
finalArgs = savedArgs
|
||||||
|
s.elog.Info(1, fmt.Sprintf("Using saved service args: %v", finalArgs))
|
||||||
|
}
|
||||||
|
|
||||||
|
s.args = finalArgs
|
||||||
|
|
||||||
// Start the main olm functionality
|
// Start the main olm functionality
|
||||||
olmDone := make(chan struct{})
|
olmDone := make(chan struct{})
|
||||||
@@ -309,7 +321,7 @@ func removeService() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func startService(args []string) error {
|
func startService(args []string) error {
|
||||||
// Save the service arguments before starting
|
// Save the service arguments as backup
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
err := saveServiceArgs(args)
|
err := saveServiceArgs(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -329,7 +341,8 @@ func startService(args []string) error {
|
|||||||
}
|
}
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
err = s.Start()
|
// Pass arguments directly to the service start call
|
||||||
|
err = s.Start(args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to start service: %v", err)
|
return fmt.Errorf("failed to start service: %v", err)
|
||||||
}
|
}
|
||||||
@@ -379,17 +392,12 @@ func debugService(args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fmt.Printf("Starting service in debug mode...\n")
|
// Start the service with the provided arguments
|
||||||
|
err := startService(args)
|
||||||
// Start the service
|
|
||||||
err := startService([]string{}) // Pass empty args since we already saved them
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to start service: %v", err)
|
return fmt.Errorf("failed to start service: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// fmt.Printf("Service started. Watching logs (Press Ctrl+C to stop watching)...\n")
|
|
||||||
// fmt.Printf("================================================================================\n")
|
|
||||||
|
|
||||||
// Watch the log file
|
// Watch the log file
|
||||||
return watchLogFile(true)
|
return watchLogFile(true)
|
||||||
}
|
}
|
||||||
@@ -509,11 +517,89 @@ func getServiceStatus() (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// showServiceConfig displays current saved service configuration
|
||||||
|
func showServiceConfig() {
|
||||||
|
configPath := getServiceArgsPath()
|
||||||
|
fmt.Printf("Service configuration file: %s\n", configPath)
|
||||||
|
|
||||||
|
args, err := loadServiceArgs()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("No saved configuration found or error loading: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) == 0 {
|
||||||
|
fmt.Println("No saved service arguments found")
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Saved service arguments: %v\n", args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func isWindowsService() bool {
|
func isWindowsService() bool {
|
||||||
isWindowsService, err := svc.IsWindowsService()
|
isWindowsService, err := svc.IsWindowsService()
|
||||||
return err == nil && isWindowsService
|
return err == nil && isWindowsService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rotateLogFile handles daily log rotation
|
||||||
|
func rotateLogFile(logDir string, logFile string) error {
|
||||||
|
// Get current log file info
|
||||||
|
info, err := os.Stat(logFile)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil // No current log file to rotate
|
||||||
|
}
|
||||||
|
return fmt.Errorf("failed to stat log file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if log file is from today
|
||||||
|
now := time.Now()
|
||||||
|
fileTime := info.ModTime()
|
||||||
|
|
||||||
|
// If the log file is from today, no rotation needed
|
||||||
|
if now.Year() == fileTime.Year() && now.YearDay() == fileTime.YearDay() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create rotated filename with date
|
||||||
|
rotatedName := fmt.Sprintf("olm-%s.log", fileTime.Format("2006-01-02"))
|
||||||
|
rotatedPath := filepath.Join(logDir, rotatedName)
|
||||||
|
|
||||||
|
// Rename current log file to dated filename
|
||||||
|
err = os.Rename(logFile, rotatedPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to rotate log file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up old log files (keep last 30 days)
|
||||||
|
cleanupOldLogFiles(logDir, 30)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanupOldLogFiles removes log files older than specified days
|
||||||
|
func cleanupOldLogFiles(logDir string, daysToKeep int) {
|
||||||
|
cutoff := time.Now().AddDate(0, 0, -daysToKeep)
|
||||||
|
|
||||||
|
files, err := os.ReadDir(logDir)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
if !file.IsDir() && strings.HasPrefix(file.Name(), "olm-") && strings.HasSuffix(file.Name(), ".log") {
|
||||||
|
filePath := filepath.Join(logDir, file.Name())
|
||||||
|
info, err := file.Info()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.ModTime().Before(cutoff) {
|
||||||
|
os.Remove(filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func setupWindowsEventLog() {
|
func setupWindowsEventLog() {
|
||||||
// Create log directory if it doesn't exist
|
// Create log directory if it doesn't exist
|
||||||
logDir := filepath.Join(os.Getenv("PROGRAMDATA"), "olm", "logs")
|
logDir := filepath.Join(os.Getenv("PROGRAMDATA"), "olm", "logs")
|
||||||
@@ -524,6 +610,14 @@ func setupWindowsEventLog() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logFile := filepath.Join(logDir, "olm.log")
|
logFile := filepath.Join(logDir, "olm.log")
|
||||||
|
|
||||||
|
// Rotate log file if needed
|
||||||
|
err = rotateLogFile(logDir, logFile)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to rotate log file: %v\n", err)
|
||||||
|
// Continue anyway to create new log file
|
||||||
|
}
|
||||||
|
|
||||||
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
file, err := os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Failed to open log file: %v\n", err)
|
fmt.Printf("Failed to open log file: %v\n", err)
|
||||||
|
|||||||
Reference in New Issue
Block a user