diff --git a/README.md b/README.md index 17aecda..ce413f8 100644 --- a/README.md +++ b/README.md @@ -58,13 +58,13 @@ If you'll use DNS, you can install the server in one step (replace 0.0.0.0 with kubectl create namespace wormhole # Replace 1.0.0 with latest version from the releases page -helm install -n wormhole wh oci://ghcr.io/glothriel/wormhole/wormhole --version 1.0.0 --set server.enabled=true --set server.service.type=LoadBalancer --set server.wg.publicHost="0.0.0.0" +helm install -n wormhole wh oci://ghcr.io/glothriel/wormhole/wormhole --version 1.1.0 --set server.enabled=true --set server.service.type=LoadBalancer --set server.wg.publicHost="0.0.0.0" # Wait for the LoadBalancer to get an IP kubectl get svc -n wormhole # Update the server with the IP -helm upgrade -n wormhole wh oci://ghcr.io/glothriel/wormhole/wormhole --version 1.0.0 --set server.enabled=true --set server.service.type=LoadBalancer --set server.wg.publicHost="" +helm upgrade -n wormhole wh oci://ghcr.io/glothriel/wormhole/wormhole --version 1.1.0 --reuse-values --set server.wg.publicHost="" ``` ### Install client @@ -134,6 +134,54 @@ Such policies allow communication from any pod in any namespace, providing, that Effectively this means, that the permission to communicate is granted per application, not per peer. Having permission to communicate with app having given name, allows the pod to communicate with all the apps with given name, no matter the peer the app is exposed from. This is especially important in the context of the server, as it may have multiple clients, all exposing the same app. +## HTTP API + +Wormhole exposes API, that allows querying apps exposed by remote peers. The API does not require authentication. The API by default listens on port 8082. + +### GET /api/apps/v1 + +This endpoint returns the list of apps exposed locally by the remote peers. + +#### Request + +No body or query parameters are required. + +#### Response + +| Property | Required | Type | Description | +|:---------|:---------|:-----|:------------| +| **name** | yes | String | Name of the exposed app | +| **address** | yes | String | `{hostname}:{port}` of the app exposed on the local cluster | +| **peer** | yes | String | Name of the remote peer, that exposed the app | + + +| Code | Description | +|:-----|:------------| +|200 Ok | Returned when request was successful | +|500 Internal server error | Returned when the apps could not be fetched for unknown reasons. | + +### GET /api/peers/v1 + +This endpoint is only available on the server. It returns the list of remote peers that are connected to the server. + +#### Request + +No body or query parameters are required. + +#### Response + +| Property | Required | Type | Description | +|:---------|:---------|:-----|:------------| +| **name** | yes | String | Name of the remote peer | +| **ip** | yes | String | IP of the peer in wireguard network | +| **public_key** | yes | String | Wireguard public key of the peer | + + +| Code | Description | +|:-----|:------------| +|200 Ok | Returned when request was successful | +|500 Internal server error | Returned when the peers could not be fetched for unknown reasons. | + ## Local development ### Development environment @@ -158,7 +206,7 @@ The development environment deploys a server, two clients and a mock service, th kubectl annotate --overwrite svc --namespace nginx nginx wormhole.glothriel.github.com/exposed=yes ``` -The additional services should be immediately created. Please note, that all three workloads are deployed on the same (and by extension are monitoring the same services for annotations), so the nginx will be exposed 4 times - client1 to server, client2 to server, server to client1 and server to client2. +The additional services should be immediately created. Please note, that all three workloads are deployed on the same cluster (and by extension are monitoring the same services for annotations), so the nginx will be exposed 4 times - client1 to server, client2 to server, server to client1 and server to client2. ### Integration tests diff --git a/kubernetes/helm/templates/client-deployment.yaml b/kubernetes/helm/templates/client-deployment.yaml index 86fc24a..22a1e58 100644 --- a/kubernetes/helm/templates/client-deployment.yaml +++ b/kubernetes/helm/templates/client-deployment.yaml @@ -95,6 +95,8 @@ spec: - image: {{ $.Values.docker.registry }}{{ if $.Values.docker.registry }}/{{ end }}{{ $.Values.docker.image }}:{{ $.Values.docker.version }} name: wormhole + ports: + - containerPort: 8082 envFrom: - secretRef: name: {{ template "name-client" . }}-env diff --git a/kubernetes/helm/templates/client-svc.yaml b/kubernetes/helm/templates/client-svc.yaml new file mode 100644 index 0000000..1de7e7b --- /dev/null +++ b/kubernetes/helm/templates/client-svc.yaml @@ -0,0 +1,19 @@ +{{- if .Values.client.enabled }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ template "name-client" . }}-api + namespace: {{ $.Release.Namespace }} + labels: + application: {{ template "name-client" . }} +spec: + ports: + - name: api + port: 8082 + targetPort: 8082 + selector: + application: {{ template "name-client" . }} + sessionAffinity: None + type: ClusterIP +{{ end }} \ No newline at end of file diff --git a/kubernetes/helm/templates/server-svc.yaml b/kubernetes/helm/templates/server-svc.yaml index 1a103e9..e296f24 100644 --- a/kubernetes/helm/templates/server-svc.yaml +++ b/kubernetes/helm/templates/server-svc.yaml @@ -20,4 +20,21 @@ spec: application: {{ template "name-server" . }} sessionAffinity: None type: {{ $.Values.server.service.type }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ template "name-server" . }}-api + namespace: {{ $.Release.Namespace }} + labels: + application: {{ template "name-server" . }} +spec: + ports: + - name: api + port: 8082 + targetPort: 8082 + selector: + application: {{ template "name-server" . }} + sessionAffinity: None + type: ClusterIP {{ end }} \ No newline at end of file diff --git a/pkg/api/apps.go b/pkg/api/apps.go index 339c9a4..529aeed 100644 --- a/pkg/api/apps.go +++ b/pkg/api/apps.go @@ -3,6 +3,7 @@ package api import ( "github.com/gin-gonic/gin" "github.com/glothriel/wormhole/pkg/hello" + "github.com/glothriel/wormhole/pkg/peers" ) type appsController struct { @@ -18,6 +19,9 @@ func (ac *appsController) registerRoutes(r *gin.Engine) { }) return } + if apps == nil { + apps = []peers.App{} + } c.JSON(200, apps) }) } diff --git a/pkg/api/peers.go b/pkg/api/peers.go index e779fbc..9e08ac0 100644 --- a/pkg/api/peers.go +++ b/pkg/api/peers.go @@ -7,9 +7,10 @@ import ( ) type peerController struct { - peers hello.PeerStorage - wgConfig *wg.Config - watcher *wg.Watcher + peers hello.PeerStorage + wgConfig *wg.Config + watcher *wg.Watcher + enablePeerDeletion bool } func (p *peerController) deletePeer(name string) error { @@ -46,6 +47,12 @@ func (p *peerController) registerRoutes(r *gin.Engine) { }) r.DELETE("/api/peers/v1/:name", func(c *gin.Context) { + if !p.enablePeerDeletion { + c.JSON(403, gin.H{ + "error": "Peer deletion is disabled", + }) + return + } name := c.Param("name") err := p.deletePeer(name) if err != nil { @@ -64,5 +71,7 @@ func NewPeersController(peers hello.PeerStorage, wgConfig *wg.Config, watcher *w peers: peers, wgConfig: wgConfig, watcher: watcher, + // We currently don't have authorization in place, disabling peer deletion + enablePeerDeletion: false, } } diff --git a/pkg/cmd/client.go b/pkg/cmd/client.go index 84a5ed0..2ad2d7f 100644 --- a/pkg/cmd/client.go +++ b/pkg/cmd/client.go @@ -4,6 +4,7 @@ package cmd import ( "time" + "github.com/glothriel/wormhole/pkg/api" "github.com/glothriel/wormhole/pkg/hello" "github.com/glothriel/wormhole/pkg/k8s" "github.com/glothriel/wormhole/pkg/listeners" @@ -139,6 +140,17 @@ var clientCommand *cli.Command = &cli.Command{ logrus.Fatalf("Failed to create syncing client: %v", scErr) } + go func() { + err := api.NewAdminAPI([]api.Controller{ + api.NewAppsController( + remoteListenerRegistry, + ), + }, c.Bool(debugFlag.Name)).Run(":8082") + if err != nil { + logrus.Fatalf("Failed to start admin API: %v", err) + } + }() + return sc.Start() }, } diff --git a/pkg/cmd/server.go b/pkg/cmd/server.go index 0eded5c..87c859f 100644 --- a/pkg/cmd/server.go +++ b/pkg/cmd/server.go @@ -189,7 +189,7 @@ var serverCommand *cli.Command = &cli.Command{ go ss.Start() go func() { err := api.NewAdminAPI([]api.Controller{ - api.NewAppsController(appSource), + api.NewAppsController(appsExposedFromRemote), api.NewPeersController(peerStorage, wgConfig, watcher), }, c.Bool(debugFlag.Name)).Run(":8082") if err != nil { diff --git a/pkg/hello/if.go b/pkg/hello/if.go index d4fba6d..b92e9ec 100644 --- a/pkg/hello/if.go +++ b/pkg/hello/if.go @@ -96,8 +96,7 @@ type WireguardConfigReloader interface { // PeerInfo is a struct that contains information about a peer type PeerInfo struct { - Name string `json:"name"` - IP string `json:"ip"` - PublicKey string `json:"public_key"` - LastContact int64 `json:"last_contact"` + Name string `json:"name"` + IP string `json:"ip"` + PublicKey string `json:"public_key"` }