Skip to content

Commit db766de

Browse files
Add some more fields to the http api
1 parent b462b2c commit db766de

File tree

3 files changed

+200
-34
lines changed

3 files changed

+200
-34
lines changed

README.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,109 @@ You can view the Windows Event Log using Event Viewer or PowerShell:
121121
Get-EventLog -LogName Application -Source "OlmWireguardService" -Newest 10
122122
```
123123

124+
## HTTP Endpoints
125+
126+
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:
127+
128+
### POST /connect
129+
Initiates a new connection request.
130+
131+
**Request Body:**
132+
```json
133+
{
134+
"id": "string",
135+
"secret": "string",
136+
"endpoint": "string"
137+
}
138+
```
139+
140+
**Required Fields:**
141+
- `id`: Connection identifier
142+
- `secret`: Authentication secret
143+
- `endpoint`: Target endpoint URL
144+
145+
**Response:**
146+
- **Status Code:** `202 Accepted`
147+
- **Content-Type:** `application/json`
148+
149+
```json
150+
{
151+
"status": "connection request accepted"
152+
}
153+
```
154+
155+
**Error Responses:**
156+
- `405 Method Not Allowed` - Non-POST requests
157+
- `400 Bad Request` - Invalid JSON or missing required fields
158+
159+
### GET /status
160+
Returns the current connection status and peer information.
161+
162+
**Response:**
163+
- **Status Code:** `200 OK`
164+
- **Content-Type:** `application/json`
165+
166+
```json
167+
{
168+
"status": "connected",
169+
"connected": true,
170+
"tunnelIP": "100.89.128.3/20",
171+
"version": "version_replaceme",
172+
"peers": {
173+
"10": {
174+
"siteId": 10,
175+
"connected": true,
176+
"rtt": 145338339,
177+
"lastSeen": "2025-08-13T14:39:17.208334428-07:00",
178+
"endpoint": "p.fosrl.io:21820",
179+
"isRelay": true
180+
},
181+
"8": {
182+
"siteId": 8,
183+
"connected": false,
184+
"rtt": 0,
185+
"lastSeen": "2025-08-13T14:39:19.663823645-07:00",
186+
"endpoint": "p.fosrl.io:21820",
187+
"isRelay": true
188+
}
189+
}
190+
}
191+
```
192+
193+
**Fields:**
194+
- `status`: Overall connection status ("connected" or "disconnected")
195+
- `connected`: Boolean connection state
196+
- `tunnelIP`: IP address and subnet of the tunnel (when connected)
197+
- `version`: Olm version string
198+
- `peers`: Map of peer statuses by site ID
199+
- `siteId`: Peer site identifier
200+
- `connected`: Boolean peer connection state
201+
- `rtt`: Peer round-trip time (integer, nanoseconds)
202+
- `lastSeen`: Last time peer was seen (RFC3339 timestamp)
203+
- `endpoint`: Peer endpoint address
204+
- `isRelay`: Whether the peer is relayed (true) or direct (false)
205+
206+
**Error Responses:**
207+
- `405 Method Not Allowed` - Non-GET requests
208+
209+
## Usage Examples
210+
211+
### Connect to a peer
212+
```bash
213+
curl -X POST http://localhost:8080/connect \
214+
-H "Content-Type: application/json" \
215+
-d '{
216+
"id": "31frd0uzbjvp721",
217+
"secret": "h51mmlknrvrwv8s4r1i210azhumt6isgbpyavxodibx1k2d6",
218+
"endpoint": "https://example.com"
219+
}'
220+
```
221+
222+
### Check connection status
223+
```bash
224+
curl http://localhost:8080/status
225+
```
226+
124227
## Build
125228

126229
### Container

httpserver/httpserver.go

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,16 @@ type PeerStatus struct {
2323
Connected bool `json:"connected"`
2424
RTT time.Duration `json:"rtt"`
2525
LastSeen time.Time `json:"lastSeen"`
26+
Endpoint string `json:"endpoint,omitempty"`
27+
IsRelay bool `json:"isRelay"`
2628
}
2729

2830
// StatusResponse is returned by the status endpoint
2931
type StatusResponse struct {
3032
Status string `json:"status"`
3133
Connected bool `json:"connected"`
3234
TunnelIP string `json:"tunnelIP,omitempty"`
35+
Version string `json:"version,omitempty"`
3336
PeerStatuses map[int]*PeerStatus `json:"peers,omitempty"`
3437
}
3538

@@ -42,6 +45,8 @@ type HTTPServer struct {
4245
peerStatuses map[int]*PeerStatus
4346
connectedAt time.Time
4447
isConnected bool
48+
tunnelIP string
49+
version string
4550
}
4651

4752
// NewHTTPServer creates a new HTTP server
@@ -87,8 +92,8 @@ func (s *HTTPServer) GetConnectionChannel() <-chan ConnectionRequest {
8792
return s.connectionChan
8893
}
8994

90-
// UpdatePeerStatus updates the status of a peer
91-
func (s *HTTPServer) UpdatePeerStatus(siteID int, connected bool, rtt time.Duration) {
95+
// UpdatePeerStatus updates the status of a peer including endpoint and relay info
96+
func (s *HTTPServer) UpdatePeerStatus(siteID int, connected bool, rtt time.Duration, endpoint string, isRelay bool) {
9297
s.statusMu.Lock()
9398
defer s.statusMu.Unlock()
9499

@@ -103,6 +108,8 @@ func (s *HTTPServer) UpdatePeerStatus(siteID int, connected bool, rtt time.Durat
103108
status.Connected = connected
104109
status.RTT = rtt
105110
status.LastSeen = time.Now()
111+
status.Endpoint = endpoint
112+
status.IsRelay = isRelay
106113
}
107114

108115
// SetConnectionStatus sets the overall connection status
@@ -120,6 +127,37 @@ func (s *HTTPServer) SetConnectionStatus(isConnected bool) {
120127
}
121128
}
122129

130+
// SetTunnelIP sets the tunnel IP address
131+
func (s *HTTPServer) SetTunnelIP(tunnelIP string) {
132+
s.statusMu.Lock()
133+
defer s.statusMu.Unlock()
134+
s.tunnelIP = tunnelIP
135+
}
136+
137+
// SetVersion sets the olm version
138+
func (s *HTTPServer) SetVersion(version string) {
139+
s.statusMu.Lock()
140+
defer s.statusMu.Unlock()
141+
s.version = version
142+
}
143+
144+
// UpdatePeerRelayStatus updates only the relay status of a peer
145+
func (s *HTTPServer) UpdatePeerRelayStatus(siteID int, endpoint string, isRelay bool) {
146+
s.statusMu.Lock()
147+
defer s.statusMu.Unlock()
148+
149+
status, exists := s.peerStatuses[siteID]
150+
if !exists {
151+
status = &PeerStatus{
152+
SiteID: siteID,
153+
}
154+
s.peerStatuses[siteID] = status
155+
}
156+
157+
status.Endpoint = endpoint
158+
status.IsRelay = isRelay
159+
}
160+
123161
// handleConnect handles the /connect endpoint
124162
func (s *HTTPServer) handleConnect(w http.ResponseWriter, r *http.Request) {
125163
if r.Method != http.MethodPost {
@@ -163,6 +201,8 @@ func (s *HTTPServer) handleStatus(w http.ResponseWriter, r *http.Request) {
163201

164202
resp := StatusResponse{
165203
Connected: s.isConnected,
204+
TunnelIP: s.tunnelIP,
205+
Version: s.version,
166206
PeerStatuses: s.peerStatuses,
167207
}
168208

main.go

Lines changed: 55 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
331331
var httpServer *httpserver.HTTPServer
332332
if enableHTTP {
333333
httpServer = httpserver.NewHTTPServer(httpAddr)
334+
httpServer.SetVersion(olmVersion)
334335
if err := httpServer.Start(); err != nil {
335336
logger.Fatal("Failed to start HTTP server: %v", err)
336337
}
@@ -372,31 +373,31 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
372373
// }
373374
// }
374375

375-
// // wait until we have a client id and secret and endpoint
376-
// waitCount := 0
377-
// for id == "" || secret == "" || endpoint == "" {
378-
// select {
379-
// case <-ctx.Done():
380-
// logger.Info("Context cancelled while waiting for credentials")
381-
// return
382-
// default:
383-
// missing := []string{}
384-
// if id == "" {
385-
// missing = append(missing, "id")
386-
// }
387-
// if secret == "" {
388-
// missing = append(missing, "secret")
389-
// }
390-
// if endpoint == "" {
391-
// missing = append(missing, "endpoint")
392-
// }
393-
// waitCount++
394-
// if waitCount%10 == 1 { // Log every 10 seconds instead of every second
395-
// logger.Debug("Waiting for missing parameters: %v (waiting %d seconds)", missing, waitCount)
396-
// }
397-
// time.Sleep(1 * time.Second)
398-
// }
399-
// }
376+
// wait until we have a client id and secret and endpoint
377+
waitCount := 0
378+
for id == "" || secret == "" || endpoint == "" {
379+
select {
380+
case <-ctx.Done():
381+
logger.Info("Context cancelled while waiting for credentials")
382+
return
383+
default:
384+
missing := []string{}
385+
if id == "" {
386+
missing = append(missing, "id")
387+
}
388+
if secret == "" {
389+
missing = append(missing, "secret")
390+
}
391+
if endpoint == "" {
392+
missing = append(missing, "endpoint")
393+
}
394+
waitCount++
395+
if waitCount%10 == 1 { // Log every 10 seconds instead of every second
396+
logger.Debug("Waiting for missing parameters: %v (waiting %d seconds)", missing, waitCount)
397+
}
398+
time.Sleep(1 * time.Second)
399+
}
400+
}
400401

401402
// parse the mtu string into an int
402403
mtuInt, err = strconv.Atoi(mtu)
@@ -644,10 +645,27 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
644645
logger.Error("Failed to configure interface: %v", err)
645646
}
646647

648+
// Set tunnel IP in HTTP server
649+
if httpServer != nil {
650+
httpServer.SetTunnelIP(wgData.TunnelIP)
651+
}
652+
647653
peerMonitor = peermonitor.NewPeerMonitor(
648654
func(siteID int, connected bool, rtt time.Duration) {
649655
if httpServer != nil {
650-
httpServer.UpdatePeerStatus(siteID, connected, rtt)
656+
// Find the site config to get endpoint information
657+
var endpoint string
658+
var isRelay bool
659+
for _, site := range wgData.Sites {
660+
if site.SiteId == siteID {
661+
endpoint = site.Endpoint
662+
// TODO: We'll need to track relay status separately
663+
// For now, assume not using relay unless we get relay data
664+
isRelay = !doHolepunch
665+
break
666+
}
667+
}
668+
httpServer.UpdatePeerStatus(siteID, connected, rtt, endpoint, isRelay)
651669
}
652670
if connected {
653671
logger.Info("Peer %d is now connected (RTT: %v)", siteID, rtt)
@@ -664,7 +682,7 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
664682
// loop over the sites and call ConfigurePeer for each one
665683
for _, site := range wgData.Sites {
666684
if httpServer != nil {
667-
httpServer.UpdatePeerStatus(site.SiteId, false, 0)
685+
httpServer.UpdatePeerStatus(site.SiteId, false, 0, site.Endpoint, false)
668686
}
669687
err = ConfigurePeer(dev, site, privateKey, endpoint)
670688
if err != nil {
@@ -893,18 +911,23 @@ func runOlmMainWithArgs(ctx context.Context, args []string) {
893911
return
894912
}
895913

896-
var removeData RelayPeerData
897-
if err := json.Unmarshal(jsonData, &removeData); err != nil {
898-
logger.Error("Error unmarshaling remove data: %v", err)
914+
var relayData RelayPeerData
915+
if err := json.Unmarshal(jsonData, &relayData); err != nil {
916+
logger.Error("Error unmarshaling relay data: %v", err)
899917
return
900918
}
901919

902-
primaryRelay, err := resolveDomain(removeData.Endpoint)
920+
primaryRelay, err := resolveDomain(relayData.Endpoint)
903921
if err != nil {
904922
logger.Warn("Failed to resolve primary relay endpoint: %v", err)
905923
}
906924

907-
peerMonitor.HandleFailover(removeData.SiteId, primaryRelay)
925+
// Update HTTP server to mark this peer as using relay
926+
if httpServer != nil {
927+
httpServer.UpdatePeerRelayStatus(relayData.SiteId, relayData.Endpoint, true)
928+
}
929+
930+
peerMonitor.HandleFailover(relayData.SiteId, primaryRelay)
908931
})
909932

910933
olm.RegisterHandler("olm/register/no-sites", func(msg websocket.WSMessage) {

0 commit comments

Comments
 (0)