Skip to content

Commit

Permalink
Limit interfaces listened on by NGINX, fixes #54
Browse files Browse the repository at this point in the history
  • Loading branch information
glothriel committed May 28, 2024
1 parent b71b15a commit 955b037
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 7 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@ tilt up

First start of wormhole will be really slow - it compiles the go code inside the container. Subsequent starts will be faster, as the go build cache is preserved in PVC.

The development environment deploys a server, two clients and a mock service, that you can use to test the tunnels.

```
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.

### Integration tests

```
Expand Down
2 changes: 2 additions & 0 deletions pkg/cmd/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,15 @@ var joinCommand *cli.Command = &cli.Command{
"local",
nginx.NewDefaultReloader(),
nginx.NewRangePortAllocator(20000, 25000),
nginx.NewOnlyWireguardListener(),
))

remoteNginxExposer := nginx.NewNginxExposer(
c.String(nginxExposerConfdPathFlag.Name),
"remote",
nginx.NewDefaultReloader(),
nginx.NewRangePortAllocator(25001, 30000),
nginx.NewAllAcceptWireguardListener(),
)
var effectiveExposer listeners.Exposer = remoteNginxExposer

Expand Down
2 changes: 2 additions & 0 deletions pkg/cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,15 @@ var listenCommand *cli.Command = &cli.Command{
"local",
nginx.NewDefaultReloader(),
nginx.NewRangePortAllocator(20000, 25000),
nginx.NewOnlyWireguardListener(),
))

remoteNginxExposer := nginx.NewNginxExposer(
c.String(nginxExposerConfdPathFlag.Name),
"remote",
nginx.NewDefaultReloader(),
nginx.NewRangePortAllocator(25001, 30000),
nginx.NewAllAcceptWireguardListener(),
)
var effectiveExposer listeners.Exposer = remoteNginxExposer

Expand Down
25 changes: 18 additions & 7 deletions pkg/nginx/exposer.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import (

// Exposer is an Exposer implementation that uses NGINX as a proxy server
type Exposer struct {
prefix string
path string
fs afero.Fs
prefix string
path string
fs afero.Fs
listener Listener

reloader Reloader
ports PortAllocator
Expand All @@ -35,17 +36,24 @@ func (n *Exposer) Add(app peers.App) (peers.App, error) {
File: nginxConfigPath(n.prefix, app),
App: app,
}

listen := ""
listenAddrs, listenerErr := n.listener.Addrs(port)
if listenerErr != nil || len(listenAddrs) == 0 {
logrus.Errorf("Could not get listener addresses: %v", listenerErr)
}
for _, addr := range listenAddrs {
listen += fmt.Sprintf(" listen %s;\n", addr)
}
if writeErr := afero.WriteFile(n.fs, path.Join(n.path, server.File), []byte(fmt.Sprintf(`
# [%s] %s
server {
listen %d;
%s
proxy_pass %s;
}
`,
server.App.Peer,
server.App.Name,
server.ListenPort,
listen,
server.ProxyPass,
)), 0644); writeErr != nil {
logrus.Errorf("Could not write NGINX config file: %v", writeErr)
Expand Down Expand Up @@ -117,7 +125,9 @@ func (n *Exposer) WithdrawAll() error {
}

// NewNginxExposer creates a new NGINX exposer
func NewNginxExposer(path, confPrefix string, reloader Reloader, allocator PortAllocator) listeners.Exposer {
func NewNginxExposer(
path, confPrefix string, reloader Reloader, allocator PortAllocator, listener Listener,
) listeners.Exposer {
fs := afero.NewOsFs()
cg := &Exposer{
path: path,
Expand All @@ -126,6 +136,7 @@ func NewNginxExposer(path, confPrefix string, reloader Reloader, allocator PortA

reloader: reloader,
ports: allocator,
listener: listener,
}
createErr := fs.MkdirAll(path, 0755)
if createErr != nil && createErr != afero.ErrDestinationExists {
Expand Down
93 changes: 93 additions & 0 deletions pkg/nginx/listener.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package nginx

import (
"fmt"
"net"
"strings"
)

// Listener is an interface for NGINX listeners
type Listener interface {
// Addrs returns a list of addresses that the listener is listening on
Addrs(portNumber int) ([]string, error)
}

type portOnlyListener struct {
}

// Addrs implements Listener
func (p *portOnlyListener) Addrs(portNumber int) ([]string, error) {
return []string{fmt.Sprintf("%d", portNumber)}, nil
}

// NewPortOnlyListener creates a new Listener that listens on a single port
func NewPortOnlyListener() Listener {
return &portOnlyListener{}
}

type wg0FilteringListener struct {
includeWg0 bool
}

// Addrs implements Listener
func (a *wg0FilteringListener) Addrs(portNumber int) ([]string, error) {
interfaces, interfacesErr := net.Interfaces()
if interfacesErr != nil {
return []string{}, interfacesErr
}

var allAddrs []string

for _, iface := range interfaces {
if iface.Name == "wg0" && !a.includeWg0 {
continue
}
if iface.Name != "wg0" && a.includeWg0 {
continue
}

addrs, err := iface.Addrs()
if err != nil {
fmt.Println("Error retrieving addresses for interface:", iface.Name, err)
continue
}

for _, addr := range addrs {
switch v := addr.(type) {
// Ignore ipv6
case *net.IPNet:
if strings.Contains(v.IP.String(), ":") {
continue
} else {
allAddrs = append(allAddrs, formatIP(v.IP, portNumber))
}

case *net.IPAddr:
if strings.Contains(v.IP.String(), ":") {
continue
} else {
allAddrs = append(allAddrs, formatIP(v.IP, portNumber))
}
}
}
}
return allAddrs, nil
}

func formatIP(ip fmt.Stringer, portNumber int) string {
return fmt.Sprintf("%s:%d", ip.String(), portNumber)
}

// NewAllAcceptWireguardListener creates a new Listener that listens on all interfaces accept wg0
func NewAllAcceptWireguardListener() Listener {
return &wg0FilteringListener{
includeWg0: false,
}
}

// NewOnlyWireguardListener creates a new Listener that listens on all interfaces
func NewOnlyWireguardListener() Listener {
return &wg0FilteringListener{
includeWg0: true,
}
}

0 comments on commit 955b037

Please sign in to comment.