Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
jamescun committed Feb 20, 2020
0 parents commit 07d8892
Show file tree
Hide file tree
Showing 11 changed files with 1,123 additions and 0 deletions.
11 changes: 11 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM golang:1.13 AS builder

WORKDIR /go/src/github.com/jamescun/wireguard-api
COPY . /go/src/github.com/jamescun/wireguard-api

RUN CGO_ENABLED=0 GOOS=linux go build -o wireguard-api cmd/wireguard-api.go


FROM scratch
COPY --from=builder /go/src/github.com/jamescun/wireguard-api/wireguard-api /bin/wireguard-api
CMD ["wireguard-api"]
19 changes: 19 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Copyright (c) 2020 James Cunningham

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
200 changes: 200 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
# WireGuard-API 🔐

WireGuard-API presents a JSON-RPC interface on top of a WireGuard network interface.

* 💖 **Add/Remove Peers**
Modify known peers without reloading

* 📈 **Statistics**
View data usage and allowed IPs of all peers

* 📞 **JSON-RPC 2.0 API**
No custom client integrations required, standard API accepted everywhere.

**NOTE:** WireGuard-API is currently only compatible with the WireGuard Linux kernel module and userland wireguard-go. It does not currently work with the MacOS NetworkExtension.


## Getting WireGuard-API

### Pre-Built Binary

Binaries for Linux are available [here](https://github.com/jamescun/wireguard-api/releases).

### Build Yourself

WireGuard-API requires at least Go 1.13.

```sh
go install github.com/jamescun/wireguard-api/cmd
```

This should install the server binary `wireguard-api` in your $GOPATH/bin.

### Docker

WireGuard-API can also be run inside a Docker container, however the container will need to existing within the same network namespace as the host and have network administrator capability (CAP_NET_ADMIN) to be able to control the WireGuard interface.

```sh
docker run --name=wireguard-api -d -p 8080:8080 --network host --cap-add NET_ADMIN james/wireguard-api:latest wireguard-api --device=<my device>
```


## Configuring WireGuard-API

WireGuard is configured using command line arguments:

```sh
$ wireguard-api --help
WireGuard-API presents a JSON-RPC API to a WireGuard device
Usage: wireguard-api [options]

Helpers:
--list-devices list wireguard devices on this system and their name to be
given to --device
--version display the version number of WireGuard-API

Options:
--device=<name> (required) name of WireGuard device to manager
--listen=<[host:]port> address where API server will bind
(default localhost:8080)
--tls enable Transport Layer Security (SSL) on server
--tls-key TLS private key
--tks-cert TLS certificate file
--tls-client-ca enable mutual TLS authentication (mTLS) of the client

Warnings:
WireGuard-API can perform sensitive network operations, as such it should not
be publically exposed. It should be bound to the local interface only, or
failing that, be behind an authenticating proxy or have mTLS enabled.
```

The only required argument is `--device`, which tells WireGuard-API which WireGuard device to control. To control multiple WireGuard devices, launch multiple instances of WireGuard-API.

By default, this launches WireGuard-API on `localhost:8080` which may conflict with the typical development environment. To bind it elsewhere, use `--listen`:

```sh
$ wireguard-api --device=<my device> --listen=localhost:1234
```

**NOTE:** `--listen` will not prevent you from binding the server to a public interface. Care should be taken to prevent public access to the WireGuard-API server; such as binding it only to a local interface, placing an authenticating reverse proxy in-front of it or using mTLS (detailed below).

WireGuard-API can optional listen using TLS and HTTP/2. To enable TLS, you will also need a TLS Certificate and matching private key.

```sh
$ wireguard-api --device=<my device> --tls --tls-key=key.pem --tls-cert=cert.pem
```

And optionally WireGuard-API can request and validate client certificates to implement TLS Mutual Authentication (mTLS):

```sh
$ wireguard-api --device=<my device> --tls --tls-key=key.pem --tls-cert=cert.pem --tls-client-ca=clientca.pem
```


## Using WireGuard-API

WireGuard-API exposes a JSON-RPC 2.0 API with five methods.

All calls are made using the POST method, and require the `Content-Type` header to be set to `application/json`. The server ignores the URL path it is given, allowing the server to be mounted under another hierarchy in a reverse proxy.

The structures expected by the server can be found in [client/client.go](client/client.go).


### GetDeviceInfo

GetDeviceInfo returns information such as the public key and type of interface for the currently configured device.

```sh
curl http://localhost:8080 -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "GetDeviceInfo", "params": {}}'
```

#### Example Response

```json
{
"device": {
"name": "wg0",
"type": "Linux kernel",
"public_key": "xoY2MZZ1UmbEakFBPyqryHwTaMi6ae4myP+vuILmJUY=",
"listen_port": 51820,
"num_peers": 13
}
}
```


### ListPeers

ListPeers retrieves information about all Peers known to the current WireGuard interface, including allowed IP addresses and usage stats, optionally with pagination.

```sh
curl http://localhost:8080 -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "ListPeers", "params": {}}'
```

#### Example Response

```json
{
"peers": [
{
"public_key": "xoY2MZZ1UmbEakFBPyqryHwTaMi6ae4myP+vuILmJUY=",
"has_preshared_key": false,
"endpoint": "67.234.65.104:57436",
"last_handshake": "2020-02-20T16:35:12Z",
"receive_bytes": 834854756,
"transmit_bytes": 3883746,
"allowed_ips": [
"10.1.1.0/24"
],
"protocol_version": 1
},
...
]
}
```


### GetPeer

GetPeer retrieves a specific Peer by their public key.

```sh
curl http://localhost:8080 -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "GetPeer", "params": {"public_key": "xoY2MZZ1UmbEakFBPyqryHwTaMi6ae4myP+vuILmJUY="}}'
```

#### Example Response

```json
{
"peer": {
"public_key": "xoY2MZZ1UmbEakFBPyqryHwTaMi6ae4myP+vuILmJUY=",
"has_preshared_key": false,
"endpoint": "67.234.65.104:57436",
"last_handshake": "2020-02-20T16:35:12Z",
"receive_bytes": 834854756,
"transmit_bytes": 3883746,
"allowed_ips": [
"10.1.1.0/24"
],
"protocol_version": 1
}
}
```


### AddPeer

AddPeer inserts a new Peer into the WireGuard interfaces table, multiple calls to AddPeer can be used to update details of the Peer.

```sh
curl http://localhost:8080 -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "AddPeer", "params": {"public_key": "xoY2MZZ1UmbEakFBPyqryHwTaMi6ae4myP+vuILmJUY=","allowed_ips": [ "10.1.1.0/24" ]}}'
```


### RemovePeer

RemovePeer deletes a Peer from the WireGuard interfaces table by their public key,

```sh
curl http://localhost:8080 -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "RemovePeer", "params": {"public_key": "xoY2MZZ1UmbEakFBPyqryHwTaMi6ae4myP+vuILmJUY="}}'
```
101 changes: 101 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package client

import (
"context"
"time"
)

// Client is the interface expected to be presented to consumers of the API.
type Client interface {
// GetDeviceInfo returns information such as the public key and type of
// interface for the currently configured device.
GetDeviceInfo(context.Context, *GetDeviceInfoRequest) (*GetDeviceInfoResponse, error)

// ListPeers retrieves information about all Peers known to the current
// WireGuard interface, including allowed IP addresses and usage stats,
// optionally with pagination.
ListPeers(context.Context, *ListPeersRequest) (*ListPeersResponse, error)

// GetPeer retrieves a specific Peer by their public key.
GetPeer(context.Context, *GetPeerRequest) (*GetPeerResponse, error)

// AddPeer inserts a new Peer into the WireGuard interfaces table, multiple
// calls to AddPeer can be used to update details of the Peer.
AddPeer(context.Context, *AddPeerRequest) (*AddPeerResponse, error)

// RemovePeer deletes a Peer from the WireGuard interfaces table by their
// public key,
RemovePeer(context.Context, *RemovePeerRequest) (*RemovePeerResponse, error)
}

type Device struct {
Name string `json:"name"`
Type string `json:"type"`
PublicKey string `json:"public_key"`
ListenPort int `json:"listen_port"`
FirewallMark int `json:"firewall_mark,omitempty"`
NumPeers int `json:"num_peers"`
}

type GetDeviceInfoRequest struct{}

type GetDeviceInfoResponse struct {
Device *Device `json:"device"`
}

type Peer struct {
PublicKey string `json:"public_key"`
HasPresharedKey bool `json:"has_preshared_key"`
Endpoint string `json:"endpoint"`
PersistentKeepAlive string `json:"persistent_keep_alive,omitempty"`
LastHandshake time.Time `json:"last_handshake"`
ReceiveBytes int64 `json:"receive_bytes"`
TransmitBytes int64 `json:"transmit_bytes"`
AllowedIPs []string `json:"allowed_ips"`
ProtocolVersion int `json:"protocol_version"`
}

type ListPeersRequest struct {
Limit int `json:"limit,omitempty"`
Offset int `json:"offset,omitempty"`
}

type ListPeersResponse struct {
Peers []*Peer `json:"peers"`
}

type GetPeerRequest struct {
PublicKey string `json:"public_key"`
}

type GetPeerResponse struct {
Peer *Peer `json:"peer"`
}

type AddPeerRequest struct {
PublicKey string `json:"public_key"`
PresharedKey string `json:"preshared_key,omitempty"`
Endpoint string `json:"endpoint,omitempty"`
PersistentKeepAlive string `json:"persistent_keep_alive,omitempty"`
AllowedIPs []string `json:"allowed_ips,omitempty"`

// ValidateOnly ensures only validation is completed, no side effects
ValidateOnly bool `json:"validate_only"`
}

type AddPeerResponse struct {
// OK will only ever be false if ValidateOnly has been requested.
OK bool `json:"ok"`
}

type RemovePeerRequest struct {
PublicKey string `json:"public_key"`

// ValidateOnly ensures only validation is completed, no side effects
ValidateOnly bool `json:"validate_only"`
}

type RemovePeerResponse struct {
// OK will only ever be false if ValidateOnly has been requested.
OK bool `json:"ok"`
}
Loading

0 comments on commit 07d8892

Please sign in to comment.