diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..67783afb --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,49 @@ +version: '3' + +services: + container1: + image: archlinux + privileged: true + command: sleep infinity + volumes: + - ./wag:/usr/local/bin/wag + networks: + custom_network: + ipv4_address: 172.20.0.2 + cap_add: + - NET_ADMIN + + container2: + image: archlinux + privileged: true + command: sleep infinity + ports: + - "4433:4433/tcp" + volumes: + - ./wag:/usr/local/bin/wag:ro + - ./docker-test-config.json:/opt/config.json + - ./devices.db:/opt/devices.db + networks: + custom_network: + ipv4_address: 172.20.0.3 + cap_add: + - NET_ADMIN + + container3: + image: archlinux + privileged: true + volumes: + - ./wag:/usr/local/bin/wag + command: sleep infinity + networks: + custom_network: + ipv4_address: 172.20.0.4 + cap_add: + - NET_ADMIN + +networks: + custom_network: + ipam: + config: + - subnet: 172.20.0.0/24 + gateway: 172.20.0.1 \ No newline at end of file diff --git a/docker-test-config.json b/docker-test-config.json new file mode 100644 index 00000000..c7163329 --- /dev/null +++ b/docker-test-config.json @@ -0,0 +1,117 @@ +{ + "Socket": "/tmp/wag.sock", + "NumberProxies": 0, + "Proxied": false, + "ExposePorts": [ + "10/tcp", + "100-500/tcp" + ], + "NAT": true, + "HelpMail": "help@example.com", + "Lockout": 5, + "ExternalAddress": "192.168.121.61", + "MaxSessionLifetimeMinutes": 2, + "SessionInactivityTimeoutMinutes": 1, + "DownloadConfigFileName": "wg0.conf", + "ManagementUI": { + "ListenAddress": "container2:4433", + "Enabled": true, + "Debug": false + }, + "Webserver": { + "Public": { + "ListenAddress": ":8081" + }, + "Tunnel": { + "Port": "8080" + } + }, + "Authenticators": { + "DefaultMethod": "totp", + "Issuer": "vpn.test", + "Methods": [ + "totp" + ], + "DomainURL": "https://vpn.test:8080", + "OIDC": { + "IssuerURL": "", + "ClientSecret": "", + "ClientID": "" + }, + "PAM": { + "ServiceName": "" + } + }, + "Wireguard": { + "DevName": "wg1", + "ListenPort": 53230, + "PrivateKey": "uP2iyvfBFkz7Ks6yZmXbTN2PDSOaLb0zKTziMhBYs0E=", + "Address": "192.168.122.1/24", + "ServerPersistentKeepAlive": 0 + }, + "Clustering": { + "ClusterState": "new", + "ListenAddresses": [ + "https://172.20.0.3:2380" + ], + "ETCDLogLevel": "info", + "TLSManagerListenURL": "https://container2:3434" + }, + "DatabaseLocation": "devices.db", + "Acls": { + "Groups": { + "group:administrators": [ + "toaster", + "tester" + ], + "group:nerds": [ + "toaster", + "tester", + "abc" + ] + }, + "Policies": { + "*": { + "Mfa": [ + "1.1.1.1", + "12.2.3.2", + "22.22.22.2", + "33.33.33.33", + "4.4.5.5", + "5.5.5.5" + ], + "Allow": [ + "7.7.7.7", + "google.com" + ] + }, + "group:administrators": { + "Mfa": [ + "8.8.8.8" + ] + }, + "group:nerds": { + "Mfa": [ + "192.168.3.4/32" + ], + "Allow": [ + "192.168.3.5/32" + ] + }, + "tester": { + "Mfa": [ + "192.168.3.0/24", + "192.168.5.0/24" + ], + "Allow": [ + "4.3.3.3/32" + ] + }, + "toaster": { + "Allow": [ + "1.1.1.1/32" + ] + } + } + } +} \ No newline at end of file diff --git a/go.mod b/go.mod index c56bb03b..9334f97e 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( ) require ( - github.com/NHAS/autoetcdtls v0.0.0-20240225081804-acd04dff69ee // indirect + github.com/NHAS/autoetcdtls v0.0.0-20240225231227-9d5906c5b4f2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect diff --git a/go.sum b/go.sum index 4f33580f..82ece8e0 100644 --- a/go.sum +++ b/go.sum @@ -21,6 +21,8 @@ github.com/NHAS/autoetcdtls v0.0.0-20240225075350-f723fdcc4f13 h1:UZaHXSm58Lhzhk github.com/NHAS/autoetcdtls v0.0.0-20240225075350-f723fdcc4f13/go.mod h1:fuY7v/jzCIxBZALo5dW81OYO9pY9tyiinI+LOtWt0a0= github.com/NHAS/autoetcdtls v0.0.0-20240225081804-acd04dff69ee h1:HFVRgY8Tg6b0r7+n/C48r5dDpcXiofUPtDb2PzKkuXw= github.com/NHAS/autoetcdtls v0.0.0-20240225081804-acd04dff69ee/go.mod h1:fuY7v/jzCIxBZALo5dW81OYO9pY9tyiinI+LOtWt0a0= +github.com/NHAS/autoetcdtls v0.0.0-20240225231227-9d5906c5b4f2 h1:Vdm10gX7bQ46IB19wgqIxUV0btxBj1NjEpp7XPjMidE= +github.com/NHAS/autoetcdtls v0.0.0-20240225231227-9d5906c5b4f2/go.mod h1:fuY7v/jzCIxBZALo5dW81OYO9pY9tyiinI+LOtWt0a0= github.com/NHAS/session v0.0.0-20231102064618-2b73ec5c2462 h1:i11v8Eu/H9myD2moKmbpTybGBvdxc4urIezqH5sZHig= github.com/NHAS/session v0.0.0-20231102064618-2b73ec5c2462/go.mod h1:RrYUQgrmfMmXblxB8uWEWhmTKk24PT/VoMsyQ5PD580= github.com/NHAS/webauthn v0.0.0-20231125055213-62271cf1ca6b h1:8u7ipJi28WYhEZwqd4cEyy62MrUVlgP/l69LeKjwBNI= diff --git a/internal/data/clustering.go b/internal/data/clustering.go index 7875f68b..beae81b0 100644 --- a/internal/data/clustering.go +++ b/internal/data/clustering.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "net" "net/url" "strconv" "strings" @@ -53,7 +54,11 @@ func GetMembers() []*membership.Member { return etcdServer.Server.Cluster().Members() } -func AddMember(name, etcPeerUrlAddress, managerAddressURL string) (joinToken string, err error) { +// AddMember adds a new node to the etcd cluster, and subsequently wag. +// This is done by creating a join token which allows an existing member to issue the CA private key, and download the wag config +// etcPeerUrlAddress is where the new node etcd instance is contactable +// newManagerAddressURL is where the tls manager will listen (i.e the place that serves tls certs and config) +func AddMember(name, etcPeerUrlAddress, newManagerAddressURL string) (joinToken string, err error) { if !strings.HasPrefix(etcPeerUrlAddress, "https://") { return "", errors.New("url must be https://") @@ -68,7 +73,35 @@ func AddMember(name, etcPeerUrlAddress, managerAddressURL string) (joinToken str newUrl.Host = newUrl.Host + ":443" } - token, err := TLSManager.CreateToken(etcPeerUrlAddress) + // Determine the listen ip address + listenAddr := newUrl.Hostname() + + if net.ParseIP(newUrl.Hostname()) == nil { + + addresses, err := net.LookupIP(newUrl.Hostname()) + if err != nil { + return "", fmt.Errorf("unable to lookup new etcd listen address hostname: %s", err) + } + + if len(addresses) == 0 { + return "", fmt.Errorf("no addresses found for hostname: %s", newUrl.Hostname()) + } + + listenAddr = addresses[0].String() + } + + if newUrl.Port() != "" { + listenAddr += ":" + newUrl.Port() + } else { + listenAddr += ":443" + } + newUrl.Host = listenAddr + + if newManagerAddressURL == "" { + newManagerAddressURL = "https://" + newUrl.Hostname() + ":4545" + } + + token, err := TLSManager.CreateToken(newManagerAddressURL) if err != nil { return "", err } @@ -92,7 +125,7 @@ func AddMember(name, etcPeerUrlAddress, managerAddressURL string) (joinToken str copyValues.Clustering.ClusterState = "existing" copyValues.Clustering.Name = name copyValues.Clustering.ListenAddresses = []string{newUrl.String()} - copyValues.Clustering.TLSManagerListenURL = managerAddressURL + copyValues.Clustering.TLSManagerListenURL = newManagerAddressURL copyValues.Acls = config.Acls{} copyValues.Acls.Groups = map[string][]string{}