├── .github ├── FUNDING.yaml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── alpine-base.yaml │ └── ci.yaml ├── .gitignore ├── .goreleaser.yaml ├── CONTRIBUTING.md ├── Dockerfile ├── Dockerfile.base ├── Dockerfile.distroless ├── LICENSE ├── Makefile ├── README.md ├── cmd ├── gendocs │ └── main.go ├── webmesh-node │ └── main.go ├── webmeshd │ ├── main.go │ ├── main_any.go │ └── main_windows.go └── wmctl │ └── main.go ├── contrib └── systemd │ └── webmeshd.service ├── examples ├── app-daemon │ ├── README.md │ └── docker-compose.yaml ├── basic-auth-plugin │ ├── README.md │ ├── docker-compose.yaml │ └── htpasswd ├── dht-discovery │ ├── README.md │ ├── docker-compose.yaml │ └── graph.png ├── embedded │ ├── .gitignore │ ├── README.md │ ├── chat-libp2p.gif │ ├── chat.gif │ ├── mesh-chat-libp2p │ │ ├── go.work │ │ ├── go.work.sum │ │ └── main.go │ └── mesh-chat │ │ ├── go.work │ │ ├── go.work.sum │ │ └── main.go ├── ice-peerings │ ├── README.md │ └── docker-compose.yaml ├── idauth-plugin │ ├── README.md │ ├── allowed-ids-http.txt │ ├── allowed-ids.txt │ ├── docker-compose.yaml │ ├── node-1.key │ └── node-2.key ├── libp2p-peerings │ ├── README.md │ └── docker-compose.yaml ├── mesh-to-mesh │ ├── README.md │ └── docker-compose.yaml ├── multi-bootstrap │ ├── README.md │ └── docker-compose.yaml ├── remote-server-plugin │ ├── README.md │ ├── docker-compose.yaml │ ├── main.go │ └── nodejs │ │ ├── .gitignore │ │ ├── .npmrc │ │ ├── index.ts │ │ ├── package-lock.json │ │ ├── package.json │ │ └── tsconfig.json ├── simple-persistence │ ├── README.md │ └── docker-compose.yaml ├── simple │ ├── README.md │ └── docker-compose.yaml └── site-to-site │ ├── README.md │ ├── docker-compose.yaml │ └── graph.png ├── go.mod ├── go.sum ├── img └── webmesh.png └── pkg ├── cmd ├── bridgecmd │ ├── meshbridge.go │ └── usage.go ├── cmdutil │ └── usage.go ├── ctlcmd │ ├── common.go │ ├── config │ │ ├── config.go │ │ └── interceptors.go │ ├── connect.go │ ├── debug.go │ ├── delete.go │ ├── genkey.go │ ├── get.go │ ├── pki.go │ ├── pki │ │ └── pki.go │ ├── port_forward.go │ ├── portforward │ │ └── parse.go │ ├── put.go │ ├── root.go │ ├── status.go │ └── version.go ├── daemoncmd │ ├── config.go │ ├── connmgr.go │ ├── daemon.go │ ├── profile_store.go │ ├── profiles.go │ └── server.go └── nodecmd │ ├── node.go │ └── usage.go ├── common ├── exec.go └── util.go ├── config ├── auth.go ├── auth_test.go ├── bootstrap.go ├── bootstrap_test.go ├── bridge.go ├── bridge_test.go ├── config.go ├── config_test.go ├── discovery.go ├── discovery_test.go ├── global.go ├── global_test.go ├── mesh.go ├── mesh_test.go ├── parser.go ├── parser_test.go ├── plugins.go ├── raft.go ├── raft_test.go ├── services.go ├── services_test.go ├── storage.go ├── storage_test.go ├── tls.go └── wireguard.go ├── context └── context.go ├── crypto ├── keys.go ├── keys_test.go ├── psk.go ├── psk_test.go ├── tlscerts.go ├── tlscerts_test.go ├── tlsverify.go └── tlsverify_test.go ├── embed ├── node.go └── transport.go ├── logging ├── badger_adapter.go ├── hclog_adapter.go ├── interceptors.go ├── logging.go ├── service_logger_windows.go └── stun_logger.go ├── meshnet ├── dns.go ├── endpoints │ ├── detection.go │ ├── detection_wasm.go │ └── endpoints.go ├── graph_filter.go ├── graph_filter_test.go ├── manager.go ├── nat64 │ └── nat64.go ├── netutil │ ├── dns.go │ ├── doc.go │ ├── ipv6.go │ ├── ipv6_test.go │ ├── ping.go │ └── ports.go ├── peer_map.go ├── peer_map_acl_test.go ├── peer_map_routes_test.go ├── peer_map_topology_test.go ├── peers.go ├── relay │ └── relay.go ├── resolvers.go ├── system │ ├── buffers │ │ ├── buffer_darwin.go │ │ ├── buffer_freebsd.go │ │ ├── buffer_linux.go │ │ ├── buffer_windows.go │ │ ├── buffers.go │ │ └── buffers_wasm.go │ ├── dns │ │ ├── dns.go │ │ ├── dnsconfig_darwin.go │ │ ├── dnsconfig_unix.go │ │ ├── dnsconfig_wasm.go │ │ └── dnsconfig_windows.go │ ├── firewall │ │ ├── firewall.go │ │ ├── firewall_darwin.go │ │ ├── firewall_freebsd.go │ │ ├── firewall_iptables_linux.go │ │ ├── firewall_nftables_init_linux.go │ │ ├── firewall_nftables_linux.go │ │ ├── firewall_wasm.go │ │ └── firewall_windows.go │ ├── interface.go │ ├── link │ │ ├── address_darwin.go │ │ ├── address_freebsd.go │ │ ├── address_linux.go │ │ ├── address_wasm.go │ │ ├── address_windows.go │ │ ├── link.go │ │ ├── link_darwin.go │ │ ├── link_freebsd.go │ │ ├── link_linux.go │ │ ├── link_wasm.go │ │ ├── link_windows.go │ │ ├── new_darwin.go │ │ ├── new_freebsd.go │ │ ├── new_linux.go │ │ ├── new_wasm.go │ │ └── new_windows.go │ ├── netns_any.go │ ├── netns_linux.go │ └── routes │ │ ├── forwarding_darwin.go │ │ ├── forwarding_freebsd.go │ │ ├── forwarding_linux.go │ │ ├── forwarding_wasm.go │ │ ├── forwarding_windows.go │ │ ├── routes.go │ │ ├── routes_darwin.go │ │ ├── routes_freebsd.go │ │ ├── routes_linux.go │ │ ├── routes_wasm.go │ │ └── routes_windows.go ├── testutil │ ├── dns.go │ ├── firewall.go │ ├── interface_system.go │ ├── interface_wireguard.go │ ├── manager.go │ ├── peers.go │ └── testutil.go ├── transport │ ├── datachannels │ │ ├── datachannels.go │ │ ├── peer_conn_client.go │ │ ├── peer_conn_server.go │ │ ├── wireguard_proxy_client.go │ │ └── wireguard_proxy_server.go │ ├── dialers.go │ ├── libp2p │ │ ├── announce.go │ │ ├── conn.go │ │ ├── dht.go │ │ ├── discovery.go │ │ ├── embedded │ │ │ ├── protocol │ │ │ │ └── protocol.go │ │ │ ├── security │ │ │ │ ├── secure_conn.go │ │ │ │ └── security.go │ │ │ ├── transport │ │ │ │ └── transport.go │ │ │ ├── util │ │ │ │ └── util.go │ │ │ └── wgtransport │ │ │ │ ├── conn.go │ │ │ │ ├── listener.go │ │ │ │ ├── secure_conn.go │ │ │ │ ├── security.go │ │ │ │ └── transport.go │ │ ├── host.go │ │ ├── libp2p.go │ │ ├── peerstore.go │ │ ├── peerstore_test.go │ │ ├── relay.go │ │ ├── round_trip.go │ │ ├── transport.go │ │ └── transport_test.go │ ├── resolvers.go │ ├── round_trippers.go │ ├── tcp │ │ ├── bootstrap.go │ │ ├── bootstrap_test.go │ │ ├── raft.go │ │ ├── round_trip.go │ │ └── transport.go │ ├── transports.go │ └── webrtc │ │ └── signaling.go └── wireguard │ ├── interface.go │ ├── peers.go │ ├── recorder.go │ └── recorder_wasm.go ├── meshnode ├── bootstrap.go ├── close.go ├── connect.go ├── join.go ├── node.go ├── observer.go ├── peer_updates.go └── test_node.go ├── plugins ├── builtins │ ├── basicauth │ │ ├── basicauth.go │ │ └── creds.go │ ├── builtins.go │ ├── debug │ │ └── debug.go │ ├── idauth │ │ ├── creds.go │ │ ├── idauth.go │ │ └── idauth_test.go │ ├── ldap │ │ ├── creds.go │ │ └── ldap.go │ └── mtls │ │ └── mtls.go ├── clients │ ├── client.go │ ├── external_process.go │ ├── external_server.go │ ├── in_process.go │ └── streams.go ├── ipam.go ├── manager.go └── serve.go ├── services ├── admin │ ├── delete_edge.go │ ├── delete_edge_test.go │ ├── delete_group.go │ ├── delete_group_test.go │ ├── delete_network_acl.go │ ├── delete_network_acl_test.go │ ├── delete_role.go │ ├── delete_role_binding.go │ ├── delete_role_binding_test.go │ ├── delete_role_test.go │ ├── delete_route.go │ ├── delete_route_test.go │ ├── get_edge.go │ ├── get_edge_test.go │ ├── get_group.go │ ├── get_group_test.go │ ├── get_network_acl.go │ ├── get_network_acl_test.go │ ├── get_role.go │ ├── get_role_binding.go │ ├── get_role_binding_test.go │ ├── get_role_test.go │ ├── get_route.go │ ├── get_route_test.go │ ├── list_edges.go │ ├── list_edges_test.go │ ├── list_groups.go │ ├── list_groups_test.go │ ├── list_network_acls.go │ ├── list_network_acls_test.go │ ├── list_role_bindings.go │ ├── list_role_bindings_test.go │ ├── list_roles.go │ ├── list_roles_test.go │ ├── list_routes.go │ ├── list_routes_test.go │ ├── put_edge.go │ ├── put_edge_test.go │ ├── put_group.go │ ├── put_group_test.go │ ├── put_network_acl.go │ ├── put_network_acl_test.go │ ├── put_role.go │ ├── put_role_binding.go │ ├── put_role_binding_test.go │ ├── put_role_test.go │ ├── put_route.go │ ├── put_route_test.go │ ├── server.go │ └── server_test.go ├── leaderproxy │ ├── leaderproxy.go │ ├── meta.go │ └── methods.go ├── membership │ ├── apply.go │ ├── join.go │ ├── leave.go │ ├── server.go │ ├── storage_consensus.go │ ├── subscribe_peers.go │ └── update.go ├── meshapi │ └── server.go ├── meshdns │ ├── default_handler.go │ ├── mesh_handlers.go │ ├── middlewares.go │ ├── peer_util.go │ ├── server.go │ └── server_util.go ├── metrics │ └── metrics.go ├── node │ ├── get_status.go │ ├── negotiate_data_channel.go │ └── server.go ├── rbac │ └── rbac.go ├── registrar │ └── server.go ├── server.go ├── server_test.go ├── storage │ ├── publish.go │ ├── query.go │ ├── server.go │ └── subscribe.go ├── turn │ ├── server.go │ └── stun_logger.go └── webrtc │ ├── server.go │ └── start_data_channel.go ├── storage ├── bootstrap.go ├── errors │ └── errors.go ├── graphstore.go ├── meshdb │ ├── database.go │ ├── graphstore │ │ └── graph_store.go │ ├── networking │ │ └── networking.go │ ├── rbac │ │ └── rbac.go │ ├── state │ │ └── state.go │ └── test_database.go ├── meshstate.go ├── networking.go ├── peers.go ├── provider.go ├── providers │ ├── backends │ │ └── badgerdb │ │ │ └── storage.go │ ├── builtin_test.go │ ├── external │ │ └── external.go │ ├── passthrough │ │ ├── meshdb.go │ │ └── passthrough.go │ ├── providers.go │ └── raftstorage │ │ ├── consensus.go │ │ ├── fsm │ │ └── fsm.go │ │ ├── meshstorage.go │ │ ├── options.go │ │ ├── provider.go │ │ ├── provider_test.go │ │ ├── raftlogs │ │ └── apply.go │ │ └── snapshots │ │ ├── snapshotter.go │ │ └── snapshotter_test.go ├── rbac.go ├── rpcdb │ ├── datastore.go │ └── rpcdb.go ├── rpcsrv │ ├── queries.go │ └── serve.go ├── testutil │ ├── database_conformance.go │ ├── graphstore_conformance.go │ ├── meshstate_conformance.go │ ├── networking_conformance.go │ ├── peers_conformance.go │ ├── provider_conformance.go │ ├── rbac_conformance.go │ ├── storage_conformance.go │ └── testutil.go └── types │ ├── graph.go │ ├── groups.go │ ├── ids.go │ ├── mesh_nodes.go │ ├── mesh_nodes_test.go │ ├── network_access.go │ ├── network_acls.go │ ├── network_state.go │ ├── peers.go │ ├── prefix.go │ ├── prefixes.go │ ├── rolebindings.go │ ├── roles.go │ ├── roles_test.go │ ├── routes.go │ ├── storage_peer.go │ └── storage_queries.go └── version └── version.go /.github/FUNDING.yaml: -------------------------------------------------------------------------------- 1 | github: webmeshproj 2 | liberapay: tinyzimmer 3 | custom: ["https://www.paypal.me/tinyzimmer"] -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | [//]: # (webmesh) 2 | [//]: # (Use the Format Below for your Pull Request Title) 3 | ## (Task Title) 4 | 5 | ## Changes (What does this PR do?) 6 | 7 | - _Add a description of changes here_ 8 | - _Any background context or QA test instructions you want to add?_ 9 | 10 | ## Types of changes 11 | 12 | What types of changes does your code introduce? 13 | 14 | _Put an `x` in the boxes that apply_ 15 | 16 | - [ ] Bugfix (non-breaking change which fixes an issue) 17 | - [ ] New feature (non-breaking change which adds functionality) 18 | - [ ] Breaking change (fix or feature that would potentially cause existing functionality not to work as expected) 19 | - [ ] Documentation Update (if none of the other choices applies) 20 | 21 | ## Checklist 22 | 23 | - [ ] All required dependency updates or new dependencies added? 24 | - [ ] I have included comments to describe my changes 25 | - [ ] I have performed and ensured all unit tests are passing 26 | 27 | ## Screenshots (If Applicable) 28 | 29 | ```` 30 | -------------------------------------------------------------------------------- /.github/workflows/alpine-base.yaml: -------------------------------------------------------------------------------- 1 | name: Build Alpine Base Images 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | alpine-version: 6 | description: "Alpine version to build" 7 | required: true 8 | default: "3.18" 9 | 10 | env: 11 | REGISTRY: ghcr.io 12 | IMAGE_NAME: ghcr.io/webmeshproj/alpine 13 | COSIGN_EXPERIMENTAL: 1 14 | DOCKER_PLATFORMS: linux/amd64,linux/arm64,linux/arm,linux/386,linux/ppc64le,linux/s390x 15 | 16 | jobs: 17 | build: 18 | name: Build Alpine Base Images 19 | runs-on: ubuntu-latest 20 | permissions: 21 | contents: "write" 22 | id-token: "write" 23 | packages: "write" 24 | steps: 25 | - name: Checkout Code 26 | uses: actions/checkout@v4 27 | 28 | - name: Setup Cosign 29 | uses: sigstore/cosign-installer@main 30 | 31 | - name: Set up QEMU 32 | uses: docker/setup-qemu-action@v3 33 | 34 | - name: Setup Buildx 35 | uses: docker/setup-buildx-action@v3 36 | 37 | - name: Login to GHCR 38 | uses: docker/login-action@v3 39 | with: 40 | registry: ${{ env.REGISTRY }} 41 | username: ${{ github.actor }} 42 | password: ${{ github.token }} 43 | 44 | - name: Build Images 45 | uses: docker/build-push-action@v5 46 | id: build 47 | with: 48 | context: . 49 | file: Dockerfile.base 50 | push: true 51 | tags: ${{ env.IMAGE_NAME }}:${{ inputs.alpine-version }} 52 | platforms: ${{ env.DOCKER_PLATFORMS }} 53 | build-args: ALPINE_VERSION=${{ inputs.alpine-version }} 54 | 55 | - name: Sign Images 56 | run: cosign sign --yes --recursive ${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }} 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | bin/ 3 | *.sqlite 4 | .vscode 5 | coverage.out 6 | */testdata/* 7 | junit-report.xml 8 | /test -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are welcome and encouraged. 4 | There are several areas where help is needed/welcomed, including: 5 | 6 | - Documentation 7 | - Testing 8 | - Code (Specifically around the networking and Raft consensus code) 9 | - Frontend (Preferably in Vue by someone who actually knows what they're doing :stuck_out_tongue:) 10 | - Design/Architecture 11 | 12 | A better list will come in the future, but a simple `git grep TODO` will return a list of things that need to be done ranging from small to large. 13 | 14 | Protocol Buffers are used for all APIs and inter-node communcation. 15 | They can be found in the [api](https://github.com/webmeshproj/api) repository. 16 | 17 | If you'd like to play with the project on Kubernetes, there is a work-in-progress Operator in the [operator](https://github.com/webmeshproj/operator/) repository. 18 | It already works fine on most clusters, and can be tested locally. However, it is lacking some features and is not yet ready for production use. 19 | One core area where it still needs work is in the area of support for multiple cloud providers. 20 | Currently, it can run in-cluster nodes on any provider, but out-of-cluster nodes can only be managed on GCP. 21 | Support for other providers is planned, but not yet implemented. 22 | 23 | Feel free to open an issue or PR if you have any questions or would like to contribute. 24 | 25 | The [Plugin](https://github.com/webmeshproj/api/blob/main/proto/v1/plugin.proto) interface is the preferred destination for most functionality (not related to API calls) as it comes in. 26 | I also hope to move a good chunk of the existing functionality into plugins as well. 27 | Currently all the authentication mechanisms are implemented as plugins, but the rest of the functionality is still in the main codebase. 28 | The current plugins and how they are used can all be found in the [plugins](./pkg/plugins/) directory. 29 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/webmeshproj/alpine:3.18 2 | 3 | ARG TARGETOS TARGETARCH PREFIX=node 4 | ADD dist/${PREFIX}_${TARGETOS}_${TARGETARCH}*/webmesh-node /webmesh-node 5 | ENTRYPOINT ["/webmesh-node"] 6 | -------------------------------------------------------------------------------- /Dockerfile.base: -------------------------------------------------------------------------------- 1 | # This Dockerfile produces an alpine image with a small set of network 2 | # utilities included that are useful for debugging. 3 | ARG ALPINE_VERSION=3.18 4 | FROM alpine:${ALPINE_VERSION} 5 | 6 | RUN apk add --update --no-cache wireguard-tools net-tools nftables iproute2 7 | -------------------------------------------------------------------------------- /Dockerfile.distroless: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | 3 | ARG TARGETOS TARGETARCH PREFIX=node 4 | ADD dist/${PREFIX}_${TARGETOS}_${TARGETARCH}*/webmesh-node /webmesh-node 5 | ENTRYPOINT ["/webmesh-node"] 6 | -------------------------------------------------------------------------------- /cmd/gendocs/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "errors" 21 | "flag" 22 | "fmt" 23 | "os" 24 | 25 | "github.com/spf13/cobra/doc" 26 | 27 | "github.com/webmeshproj/webmesh/pkg/cmd/bridgecmd" 28 | "github.com/webmeshproj/webmesh/pkg/cmd/ctlcmd" 29 | "github.com/webmeshproj/webmesh/pkg/cmd/nodecmd" 30 | ) 31 | 32 | func main() { 33 | fs := flag.NewFlagSet("gendocs", flag.ExitOnError) 34 | out := fs.String("out", "", "Output for generated file") 35 | ctldocs := fs.Bool("ctl", false, "Generate docs for wmctl") 36 | nodedocs := fs.Bool("node", false, "Generate docs for webmesh-node") 37 | bridgedocs := fs.Bool("bridge", false, "Generate docs for webmesh-node bridge mode") 38 | if len(os.Args) < 2 { 39 | fs.Usage() 40 | os.Exit(1) 41 | } 42 | err := fs.Parse(os.Args[1:]) 43 | if err != nil { 44 | fatal(err) 45 | } 46 | if !*ctldocs && !*nodedocs && !*bridgedocs { 47 | fs.Usage() 48 | fatal(errors.New("must specify -ctl or -node or -bridge")) 49 | } 50 | if *out == "" { 51 | fs.Usage() 52 | fatal(errors.New("must specify -out")) 53 | } 54 | if *ctldocs { 55 | cmd := ctlcmd.Root() 56 | err := doc.GenMarkdownTree(cmd, *out) 57 | if err != nil { 58 | fatal(err) 59 | } 60 | return 61 | } 62 | if *nodedocs { 63 | err = nodecmd.GenMarkdownDoc("Configuration", -10, *out) 64 | if err != nil { 65 | fatal(err) 66 | } 67 | } 68 | if *bridgedocs { 69 | err = bridgecmd.GenMarkdownDoc("Bridge Configuration", -9, *out) 70 | if err != nil { 71 | fatal(err) 72 | } 73 | } 74 | } 75 | 76 | func fatal(err error) { 77 | fmt.Fprintf(os.Stderr, "error: %s\n", err.Error()) 78 | os.Exit(1) 79 | } 80 | -------------------------------------------------------------------------------- /cmd/webmesh-node/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Entrypoint for webmesh nodes. 18 | package main 19 | 20 | import ( 21 | "fmt" 22 | "os" 23 | 24 | "github.com/webmeshproj/webmesh/pkg/cmd/nodecmd" 25 | ) 26 | 27 | func main() { 28 | if err := nodecmd.Execute(); err != nil { 29 | fmt.Fprintf(os.Stderr, "Error: %v\n", err) 30 | os.Exit(1) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cmd/webmeshd/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Entrypoint for the webmesh daemon process. This is the same as running 18 | // the webmesh-node with the --daemon flags, but is provided as a separate 19 | // entrypoint for platform specific daemon management. 20 | package main 21 | 22 | func main() { 23 | run() 24 | } 25 | -------------------------------------------------------------------------------- /cmd/webmeshd/main_any.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | /* 4 | Copyright 2023 Avi Zimmerman 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | package main 20 | 21 | import ( 22 | "context" 23 | "errors" 24 | "fmt" 25 | "os" 26 | 27 | "github.com/spf13/pflag" 28 | 29 | "github.com/webmeshproj/webmesh/pkg/cmd/daemoncmd" 30 | "github.com/webmeshproj/webmesh/pkg/version" 31 | ) 32 | 33 | func run() { 34 | flagset := pflag.NewFlagSet("webmeshd", pflag.ContinueOnError) 35 | versionFlag := flagset.Bool("version", false, "Print version information and exit") 36 | versionJSONFlag := flagset.Bool("json", false, "Print version information in JSON format") 37 | config := daemoncmd.NewDefaultConfig().BindFlags("daemon.", flagset) 38 | err := flagset.Parse(os.Args[1:]) 39 | if err != nil { 40 | if errors.Is(err, pflag.ErrHelp) { 41 | return 42 | } 43 | fmt.Fprintln(os.Stderr, "Error parsing flags:", err) 44 | os.Exit(1) 45 | } 46 | version := version.GetBuildInfo() 47 | if *versionFlag || len(os.Args) > 1 && os.Args[1] == "version" { 48 | if *versionJSONFlag { 49 | fmt.Println(version.PrettyJSON("webmesh-node")) 50 | return 51 | } 52 | fmt.Println("Webmesh Daemon") 53 | fmt.Println(" Version: ", version.Version) 54 | fmt.Println(" Git Commit: ", version.GitCommit) 55 | fmt.Println(" Build Date: ", version.BuildDate) 56 | return 57 | } 58 | if err := daemoncmd.Run(context.Background(), *config); err != nil { 59 | fmt.Fprintf(os.Stderr, "Error running daemon: %v\n", err) 60 | os.Exit(1) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /cmd/wmctl/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Entrypoint for wmctl command. 18 | package main 19 | 20 | import ( 21 | "fmt" 22 | "os" 23 | 24 | "github.com/webmeshproj/webmesh/pkg/cmd/ctlcmd" 25 | ) 26 | 27 | func main() { 28 | if err := ctlcmd.Execute(); err != nil { 29 | fmt.Fprintf(os.Stderr, "Error: %v\n", err) 30 | os.Exit(1) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /contrib/systemd/webmeshd.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Webmesh Daemon 3 | After=network.target 4 | 5 | [Service] 6 | Type=simple 7 | ExecStart=/usr/bin/webmesh-node \ 8 | --daemon.enabled=true \ 9 | --daemon.grpc-web=true \ 10 | --daemon.cors.enabled=true \ 11 | --daemon.bind=127.0.0.1:58080 \ 12 | --daemon.persistence.path=/var/lib/webmeshd \ 13 | --daemon.key-file=/var/lib/webmeshd/key 14 | Restart=always 15 | RestartSec=5 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /examples/app-daemon/README.md: -------------------------------------------------------------------------------- 1 | # App Daemon Example 2 | 3 | This example shows running the `webmesh-node` as a daemon process for applications to interact with. 4 | The API is served over gRPC and can be accessed by any language that supports gRPC. 5 | 6 | The bindings for the API are defined in the [Webmesh API](https://github.com/webmeshproj/api) repository. 7 | The schemas are published to the [Buf Schema Registry](https://buf.build/webmeshproj/api). 8 | 9 | A pre-generated golang client interface can be found within the same repository [here](https://pkg.go.dev/github.com/webmeshproj/api/go/v1#AppDaemonClient). 10 | A pre-generated Typescript interface is also available [here](https://webmeshproj.github.io/api/variables/app_connect.AppDaemon.html). 11 | 12 | To start the daemon process, run the following command: 13 | 14 | ```bash 15 | docker-compose up 16 | ``` 17 | 18 | This will start the daemon process with the following ports exposed. 19 | 20 | | Port | Description | 21 | | ---- | ----------- | 22 | | 8080 | gRPC UI | 23 | | 8081 | gRPC API | 24 | -------------------------------------------------------------------------------- /examples/app-daemon/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | networks: 4 | webmesh: 5 | enable_ipv6: true 6 | driver: bridge 7 | ipam: 8 | driver: default 9 | config: 10 | - subnet: 10.1.0.0/24 11 | gateway: 10.1.0.1 12 | - subnet: 2001:db8:3200::/64 13 | gateway: 2001:db8:3200::1 14 | 15 | volumes: 16 | webmesh-data: 17 | 18 | services: 19 | daemon: 20 | image: ${IMAGE:-ghcr.io/webmeshproj/node:latest} 21 | networks: 22 | webmesh: 23 | ipv4_address: 10.1.0.2 24 | ipv6_address: 2001:db8:3200::2 25 | entrypoint: 26 | - /webmesh-node 27 | - --daemon.enabled=true 28 | - --daemon.grpc-web=true 29 | - --daemon.bind=[::]:8081 30 | - --daemon.ui.enabled=true 31 | - --daemon.ui.listen-address=[::]:8080 32 | - --daemon.persistence.path=/data 33 | volumes: 34 | - webmesh-data:/data 35 | - /dev/net/tun:/dev/net/tun 36 | - /lib/modules:/lib/modules 37 | cap_add: ["NET_ADMIN", "NET_RAW", "SYS_MODULE"] 38 | ports: 39 | - "8080:8080" 40 | - "8081:8081" 41 | sysctls: 42 | - net.ipv6.conf.all.disable_ipv6=0 43 | -------------------------------------------------------------------------------- /examples/basic-auth-plugin/README.md: -------------------------------------------------------------------------------- 1 | # Basic Auth Example 2 | 3 | This example shows using the basic-auth plugin to protect the mesh. 4 | This is not a recommended architecture (at least without also using TLS), but it is a good example of how to use the plugin. 5 | For mTLS examples, the [operator](https://github.com/webmeshproj/operator) provides an easier setup and playground via cert-manager. 6 | 7 | Start and stop the example the same as the others. 8 | Use the `--basic-auth-username` and `--basic-auth-password` flags to set the username and password when using the CLI. 9 | -------------------------------------------------------------------------------- /examples/basic-auth-plugin/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | networks: 4 | bootstrap: 5 | ipam: 6 | driver: default 7 | config: 8 | - subnet: 10.1.0.0/24 9 | 10 | services: 11 | bootstrap-node: 12 | image: ${IMAGE:-ghcr.io/webmeshproj/node:latest} 13 | networks: 14 | bootstrap: 15 | ipv4_address: 10.1.0.5 16 | hostname: bootstrap-node 17 | entrypoint: 18 | - /webmesh-node 19 | - --global.insecure 20 | - --global.disable-ipv6 21 | - --global.detect-endpoints 22 | - --global.detect-private-endpoints 23 | - --bootstrap.enabled 24 | - --bootstrap.ipv4-network=10.10.10.0/24 25 | - --plugins.basic-auth.htpasswd-file=/etc/htpasswd 26 | ports: 27 | - 8443:8443 28 | volumes: 29 | - ./htpasswd:/etc/htpasswd 30 | cap_add: ["NET_ADMIN", "NET_RAW", "SYS_MODULE"] 31 | 32 | join-node: 33 | image: ${IMAGE:-ghcr.io/webmeshproj/node:latest} 34 | networks: 35 | bootstrap: 36 | hostname: join-node 37 | entrypoint: 38 | - /webmesh-node 39 | - --global.insecure 40 | - --global.disable-ipv6 41 | - --mesh.join-addresses=10.1.0.5:8443 42 | - --auth.basic.username=join-node 43 | - --auth.basic.password=dobad 44 | restart: on-failure 45 | cap_add: ["NET_ADMIN", "NET_RAW", "SYS_MODULE"] 46 | -------------------------------------------------------------------------------- /examples/basic-auth-plugin/htpasswd: -------------------------------------------------------------------------------- 1 | # dogood 2 | 3 | bootstrap-node:{SHA}OcO20M/Zqj/dOtQiEOtObE/CBus= 4 | 5 | # dobad 6 | 7 | join-node:{SHA}japQM7ANH2BcjMJgAGkyn+QlTHU= 8 | -------------------------------------------------------------------------------- /examples/dht-discovery/README.md: -------------------------------------------------------------------------------- 1 | # Kad DHT Discovery 2 | 3 | This example shows a fully peer-to-peer network negotiated using the Kademlia DHT. 4 | A peer in the mesh will "announce" themselves to the DHT with a pre-shared key, and other peers can discover them by querying the DHT for someone advertising the same key. 5 | 6 | ## Usage 7 | 8 | As with the other examples, you can start the compose file with: 9 | 10 | ```bash 11 | docker-compose up 12 | ``` 13 | 14 | After a few seconds, and a lot of random WARNINGS, you should see all the nodes connect to each other. 15 | The amount of time this takes will be dependent on the timing of the DHT discovery, and the time it takes for the nodes to connect to each other. 16 | It shouldn't take more than 10-20 seconds. 17 | The connections can be confirmed by executing into any of the nodes and running wg: 18 | 19 | ```bash 20 | $ docker-compose exec site-1-peer wg 21 | interface: webmesh0 22 | public key: jbDagjqI6EOslcdGlGro9dmULEKasszvQWsJwpg2wRE= 23 | private key: (hidden) 24 | listening port: 51820 25 | 26 | peer: 0gEnrk5TTUc3yx80pk+MLhLcYYeJjsxbB5tbsKwxLAc= 27 | endpoint: 127.0.0.1:48754 28 | allowed ips: 10.100.10.4/32 29 | latest handshake: Now 30 | transfer: 852 B received, 700 B sent 31 | persistent keepalive: every 30 seconds 32 | 33 | peer: dXyOh39BCX8yetKN4RT7GNM8m04BA7pY+ahSZdCrGyQ= 34 | endpoint: 127.0.0.1:51431 35 | allowed ips: 10.100.10.2/32 36 | latest handshake: 8 seconds ago 37 | transfer: 8.39 KiB received, 12.65 KiB sent 38 | persistent keepalive: every 30 seconds 39 | 40 | peer: kXjKEJmAWQbAMGnILA4tl83DQzS5GdkmB0CtMHQL42g= 41 | endpoint: 10.40.0.1:51820 42 | allowed ips: 10.100.10.1/32 43 | latest handshake: 10 seconds ago 44 | transfer: 47.23 KiB received, 43.91 KiB sent 45 | persistent keepalive: every 30 seconds 46 | ``` 47 | 48 | All nodes request direct peerings to each other, we can query the DOT graph to see the full mesh with the wmctl utility: 49 | 50 | ```bash 51 | # Generate a PNG of the graph 52 | wmctl --insecure --server localhost:8443 get graph | dot -Tpng > graph.png 53 | ``` 54 | 55 | The resulting graph should look something like this: 56 | 57 | ![Graph](graph.png) 58 | 59 | To shutdown the example, press `Ctrl+C` and then run: 60 | 61 | ```bash 62 | docker-compose down -v 63 | ``` 64 | -------------------------------------------------------------------------------- /examples/dht-discovery/graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webmeshproj/webmesh/dd98b85f82a763c09486a06cd8456c378e5ee2ff/examples/dht-discovery/graph.png -------------------------------------------------------------------------------- /examples/embedded/.gitignore: -------------------------------------------------------------------------------- 1 | chat -------------------------------------------------------------------------------- /examples/embedded/README.md: -------------------------------------------------------------------------------- 1 | # Embedded Applications 2 | 3 | This directory contains examples of embedded applications that use webmesh as a library. 4 | For now, only Go examples are provided. 5 | However, bindings for other languages are planned. 6 | 7 | ## Examples 8 | 9 | The current example is a simple chat application between a client and server. 10 | In reality, they are both servers, however for simplicity we let the server bootstrap the network. 11 | The client then connects to the server and starts a bidirectional chat. 12 | 13 | The example exists in two forms. 14 | One using native discovery with gRPC and the other using the libp2p Kademlia DHT. 15 | 16 | ### Native Discovery 17 | 18 | First build the binary: 19 | 20 | ```bash 21 | go build -o chat ./mesh-chat/main.go 22 | ``` 23 | 24 | To run the example, you'll either need to be root, or you can use the `setcap` command to give the binary the following permissions: 25 | 26 | ```bash 27 | sudo setcap cap_net_raw,cap_net_admin=eip ./chat 28 | ``` 29 | 30 | Then, you can run the server with just: 31 | 32 | ```bash 33 | ./chat 34 | ``` 35 | 36 | And the client with the join address of the server (assumed to be localhost): 37 | 38 | ```bash 39 | ./chat -join localhost:8443 40 | ``` 41 | 42 | ![](./chat.gif) 43 | 44 | ### Kademlia DHT 45 | 46 | Identical to the native discovery example, except you'll need to build the binary with the `mesh-chat-libp2p` binary: 47 | 48 | ```bash 49 | go build -o chat ./mesh-chat-libp2p/main.go 50 | ``` 51 | 52 | To run the example, you'll either need to be root, or you can use the `setcap` command to give the binary the following permissions: 53 | 54 | ```bash 55 | sudo setcap cap_net_raw,cap_net_admin=eip ./chat 56 | ``` 57 | 58 | Then, you can run the server with just: 59 | 60 | ```bash 61 | ./chat 62 | ``` 63 | 64 | The server will print out a PSK that you can use to connect to it. 65 | 66 | ```bash 67 | ./chat -psk $GENERATED_PSK 68 | ``` 69 | 70 | ![](./chat-libp2p.gif) 71 | -------------------------------------------------------------------------------- /examples/embedded/chat-libp2p.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webmeshproj/webmesh/dd98b85f82a763c09486a06cd8456c378e5ee2ff/examples/embedded/chat-libp2p.gif -------------------------------------------------------------------------------- /examples/embedded/chat.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webmeshproj/webmesh/dd98b85f82a763c09486a06cd8456c378e5ee2ff/examples/embedded/chat.gif -------------------------------------------------------------------------------- /examples/embedded/mesh-chat-libp2p/go.work: -------------------------------------------------------------------------------- 1 | go 1.21.0 2 | 3 | use ../../../ 4 | -------------------------------------------------------------------------------- /examples/embedded/mesh-chat-libp2p/go.work.sum: -------------------------------------------------------------------------------- 1 | github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= 2 | github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= 3 | github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d h1:t5Wuyh53qYyg9eqn4BbnlIT+vmhyww0TatL+zT3uWgI= 4 | github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= 5 | github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0= 6 | google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e h1:xIXmWJ303kJCuogpj0bHq+dcjcZHU+XFyc1I0Yl9cRg= 7 | google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb h1:XFBgcDwm7irdHTbz4Zk2h7Mh+eis4nfJEFQFYzJzuIA= 8 | google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0= 9 | -------------------------------------------------------------------------------- /examples/embedded/mesh-chat/go.work: -------------------------------------------------------------------------------- 1 | go 1.21.0 2 | 3 | use ../../../ 4 | -------------------------------------------------------------------------------- /examples/embedded/mesh-chat/go.work.sum: -------------------------------------------------------------------------------- 1 | github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= 2 | github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= 3 | github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d h1:t5Wuyh53qYyg9eqn4BbnlIT+vmhyww0TatL+zT3uWgI= 4 | github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= 5 | github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0= 6 | google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e h1:xIXmWJ303kJCuogpj0bHq+dcjcZHU+XFyc1I0Yl9cRg= 7 | google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb h1:XFBgcDwm7irdHTbz4Zk2h7Mh+eis4nfJEFQFYzJzuIA= 8 | google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a h1:fwgW9j3vHirt4ObdHoYNwuO24BEZjSzbh+zPaNWoiY8= 9 | -------------------------------------------------------------------------------- /examples/ice-peerings/README.md: -------------------------------------------------------------------------------- 1 | # Direct Peerings over ICE 2 | 3 | This example displays using a node as a TURN server to facilitate direct peerings between nodes behind NATs or otherwise not directly accessible. 4 | Under normal conditions, peerings only happen between nodes that can directly reach each other. 5 | Nodes can be configured to connect directly via the admin interface through manual edge creation or via the `--mesh.ice-peers` flag (assuming the node has permission to create data channels). 6 | 7 | Each node can optionally expose one or both of the WebRTC API or a TURN server. 8 | The WebRTC API is used to help facilitate ICE negotiation between nodes. 9 | The TURN server can be used to relay traffic between nodes that cannot directly reach each other. 10 | You can also use external TURN servers. 11 | 12 | Once the ICE tunnel has been established, it is used to establish a WireGuard tunnel between the two nodes. 13 | 14 | For more information, see the [configuration reference](https://webmeshproj.github.io/documentation/configuration/). 15 | 16 | ## Running the example 17 | 18 | This example uses the `--mesh.ice-peers` flag to configure the nodes to connect directly via ICE. 19 | The `--mesh.ice-peers` flag takes a comma-separated list of peer IDs and ICE URLs. 20 | 21 | You can run the example with: 22 | 23 | ```bash 24 | docker-compose up 25 | ``` 26 | 27 | To shutdown the example, press `Ctrl+C` and then run: 28 | 29 | ```bash 30 | docker-compose down -v 31 | ``` 32 | -------------------------------------------------------------------------------- /examples/ice-peerings/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | networks: 4 | public-net: 5 | ipam: 6 | driver: default 7 | config: 8 | - subnet: 10.30.0.0/24 9 | gateway: 10.30.0.100 10 | site-1: 11 | ipam: 12 | driver: default 13 | config: 14 | - subnet: 10.10.0.0/24 15 | site-2: 16 | ipam: 17 | driver: default 18 | config: 19 | - subnet: 10.20.0.0/24 20 | 21 | services: 22 | # Bootstrap/TURN Node 23 | 24 | bootstrap-node: 25 | image: ${IMAGE:-ghcr.io/webmeshproj/node:latest} 26 | networks: 27 | public-net: 28 | ipv4_address: 10.30.0.1 29 | hostname: bootstrap-node 30 | entrypoint: 31 | - /webmesh-node 32 | - --global.insecure 33 | - --global.disable-ipv6 34 | - --global.primary-endpoint=10.30.0.1 35 | - --global.detect-private-endpoints 36 | - --bootstrap.enabled 37 | - --bootstrap.ipv4-network=10.100.10.0/24 38 | - --storage.in-memory 39 | # Enable the WebRTC API to help nodes with ICE connectivity. 40 | - --services.webrtc.enabled 41 | - --services.webrtc.stun-servers=stun:10.30.0.1:3478 42 | # Enable an embedded TURN server. You can optionally use a public one. 43 | - --services.turn.enabled 44 | - --services.turn.public-ip=10.30.0.1 45 | restart: on-failure 46 | cap_add: ["NET_ADMIN", "NET_RAW"] 47 | 48 | site-1-peer: 49 | image: ${IMAGE:-ghcr.io/webmeshproj/node:latest} 50 | networks: 51 | public-net: 52 | ipv4_address: 10.30.0.2 53 | site-1: 54 | hostname: site-1-peer 55 | entrypoint: 56 | - /webmesh-node 57 | - --global.insecure 58 | - --global.disable-ipv6 59 | - --mesh.join-addresses=10.30.0.1:8443 60 | - --mesh.ice-peers=site-2-peer 61 | restart: on-failure 62 | cap_add: ["NET_ADMIN", "NET_RAW"] 63 | 64 | site-2-peer: 65 | image: ${IMAGE:-ghcr.io/webmeshproj/node:latest} 66 | networks: 67 | public-net: 68 | ipv4_address: 10.30.0.3 69 | site-2: 70 | hostname: site-2-peer 71 | entrypoint: 72 | - /webmesh-node 73 | - --global.insecure 74 | - --global.disable-ipv6 75 | - --mesh.join-addresses=10.30.0.1:8443 76 | - --mesh.ice-peers=site-1-peer 77 | restart: on-failure 78 | cap_add: ["NET_ADMIN", "NET_RAW"] 79 | -------------------------------------------------------------------------------- /examples/idauth-plugin/allowed-ids-http.txt: -------------------------------------------------------------------------------- 1 | 12D3LCx9Exr38J9dsHha8hKdkQLjD4jdsJ6MdpFv9o8yPhwt9MWi 2 | -------------------------------------------------------------------------------- /examples/idauth-plugin/allowed-ids.txt: -------------------------------------------------------------------------------- 1 | 12D3LCx9Q7nhKiCHpSY9eq89d3QQMLgvJTGHCFfJtFY1ejkLxUUJ 2 | -------------------------------------------------------------------------------- /examples/idauth-plugin/node-1.key: -------------------------------------------------------------------------------- 1 | CAUSQHOYsdP5XGJizWNfLuS+9it61Ioft6P0YN4/jnuMbWNTtzskcwh0PF8gAvcGPwC5ZlYzsM2bVfF8DmG14cjMcqM= 2 | -------------------------------------------------------------------------------- /examples/idauth-plugin/node-2.key: -------------------------------------------------------------------------------- 1 | CAUSQEeXOl6275jYMKNS5wxxGCKHbIfDAEcN2dJdWkYR8n5vLzeCzIeIUFDmL+OJyT4watmANVyZ7EzRyZnE27pHX7s= 2 | -------------------------------------------------------------------------------- /examples/libp2p-peerings/README.md: -------------------------------------------------------------------------------- 1 | # Direct Peerings over Libp2p 2 | 3 | This example shows a single public bootstrap node and two other nodes that form direct peerings with each other over libp2p relays. 4 | This is similar to the [ICE Peerings](../ice-peerings/) example, but uses libp2p relays instead of a TURN server. 5 | 6 | Nodes that wish to communicate directly over libp2p create a deterministic hash from their public keys and use it as a rendezvous string. 7 | Once the circuit relay is created, it is used to establish a WireGuard tunnel between the two nodes after they have verified the other end's public key. 8 | 9 | For more information, see the [configuration reference](https://webmeshproj.github.io/documentation/configuration/). 10 | 11 | ## Running the example 12 | 13 | This example uses the `--mesh.libp2p-peers` flag to configure the nodes to connect directly via libp2p. 14 | The `--libp2p.ice-peers` flag takes a map of peer IDs to rendezvous strings. 15 | 16 | You can run the example with: 17 | 18 | ```bash 19 | docker-compose up 20 | ``` 21 | 22 | To shutdown the example, press `Ctrl+C` and then run: 23 | 24 | ```bash 25 | docker-compose down -v 26 | ``` 27 | -------------------------------------------------------------------------------- /examples/libp2p-peerings/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | networks: 4 | public-net: 5 | ipam: 6 | driver: default 7 | config: 8 | - subnet: 10.30.0.0/24 9 | gateway: 10.30.0.100 10 | site-1: 11 | ipam: 12 | driver: default 13 | config: 14 | - subnet: 10.10.0.0/24 15 | site-2: 16 | ipam: 17 | driver: default 18 | config: 19 | - subnet: 10.20.0.0/24 20 | 21 | services: 22 | bootstrap-node: 23 | image: ${IMAGE:-ghcr.io/webmeshproj/node:latest} 24 | networks: 25 | public-net: 26 | ipv4_address: 10.30.0.1 27 | hostname: bootstrap-node 28 | entrypoint: 29 | - /webmesh-node 30 | - --global.insecure 31 | - --global.disable-ipv6 32 | - --global.primary-endpoint=10.30.0.1 33 | - --global.detect-private-endpoints 34 | - --bootstrap.enabled 35 | - --bootstrap.ipv4-network=10.100.10.0/24 36 | - --storage.in-memory 37 | restart: on-failure 38 | cap_add: ["NET_ADMIN", "NET_RAW"] 39 | 40 | site-1-peer: 41 | image: ${IMAGE:-ghcr.io/webmeshproj/node:latest} 42 | networks: 43 | public-net: 44 | ipv4_address: 10.30.0.2 45 | site-1: 46 | hostname: site-1-peer 47 | entrypoint: 48 | - /webmesh-node 49 | - --global.insecure 50 | - --global.disable-ipv6 51 | - --mesh.join-addresses=10.30.0.1:8443 52 | - --mesh.libp2p-peers=site-2-peer 53 | restart: on-failure 54 | cap_add: ["NET_ADMIN", "NET_RAW"] 55 | 56 | site-2-peer: 57 | image: ${IMAGE:-ghcr.io/webmeshproj/node:latest} 58 | networks: 59 | public-net: 60 | ipv4_address: 10.30.0.3 61 | site-2: 62 | hostname: site-2-peer 63 | entrypoint: 64 | - /webmesh-node 65 | - --global.insecure 66 | - --global.disable-ipv6 67 | - --mesh.join-addresses=10.30.0.1:8443 68 | - --mesh.libp2p-peers=site-1-peer 69 | restart: on-failure 70 | cap_add: ["NET_ADMIN", "NET_RAW"] 71 | -------------------------------------------------------------------------------- /examples/multi-bootstrap/README.md: -------------------------------------------------------------------------------- 1 | # Multiple Bootstrap Node Cluster 2 | 3 | This example has three nodes that all start a new cluster as voters. 4 | 5 | ## Running 6 | 7 | You can run the example with the following command: 8 | 9 | ```bash 10 | docker-compose up 11 | ``` 12 | 13 | To shutdown the example, press `Ctrl+C` and then run: 14 | 15 | ```bash 16 | docker-compose down -v 17 | ``` 18 | -------------------------------------------------------------------------------- /examples/remote-server-plugin/README.md: -------------------------------------------------------------------------------- 1 | # Remote Server Plugin 2 | 3 | This example is just like the [simple](../simple/) example, but it registers an external server as a plugin. 4 | The example plugin implements the Watch RPC, which allows the plugin to be notified of changes to the mesh state. 5 | The plugin also injects a querier that can be used to query the mesh state. 6 | 7 | ## Running 8 | 9 | You can run the example with the following command: 10 | 11 | ```bash 12 | # Start the plugin server 13 | go run ./main.go -listen-address :8081 14 | 15 | # In another terminal, start the nodes 16 | docker-compose up 17 | ``` 18 | 19 | The plugin will be notified when the `join-node` is added to the mesh state. 20 | -------------------------------------------------------------------------------- /examples/remote-server-plugin/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | networks: 4 | simple: 5 | ipam: 6 | driver: default 7 | config: 8 | - subnet: 10.1.0.0/24 9 | # Gateway is the same address as the host 10 | gateway: 10.1.0.1 11 | 12 | services: 13 | bootstrap-node: 14 | image: ${IMAGE:-ghcr.io/webmeshproj/node:latest} 15 | networks: 16 | simple: 17 | hostname: bootstrap-node 18 | entrypoint: 19 | - /webmesh-node 20 | - --global.insecure 21 | - --global.log-level=debug 22 | - --global.disable-ipv6 23 | - --global.detect-endpoints 24 | - --global.detect-private-endpoints 25 | - --bootstrap.enabled 26 | - --storage.in-memory 27 | - --plugins.test-plugin.remote.server=10.1.0.1:8081 28 | - --plugins.test-plugin.remote.insecure 29 | cap_add: ["NET_ADMIN", "NET_RAW", "SYS_MODULE"] 30 | 31 | join-node: 32 | image: ${IMAGE:-ghcr.io/webmeshproj/node:latest} 33 | networks: 34 | simple: 35 | hostname: join-node 36 | entrypoint: 37 | - /webmesh-node 38 | - --global.insecure 39 | - --global.disable-ipv6 40 | - --mesh.join-addresses=bootstrap-node:8443 41 | restart: on-failure 42 | cap_add: ["NET_ADMIN", "NET_RAW", "SYS_MODULE"] 43 | -------------------------------------------------------------------------------- /examples/remote-server-plugin/nodejs/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /examples/remote-server-plugin/nodejs/.npmrc: -------------------------------------------------------------------------------- 1 | @buf:registry=https://buf.build/gen/npm/v1/ 2 | -------------------------------------------------------------------------------- /examples/remote-server-plugin/nodejs/index.ts: -------------------------------------------------------------------------------- 1 | import { fastify } from "fastify"; 2 | import { fastifyConnectPlugin } from "@connectrpc/connect-fastify"; 3 | import { ConnectRouter } from "@connectrpc/connect"; 4 | import { Plugin, WatchPlugin } from "@buf/webmeshproj_api.connectrpc_es/v1/plugin_connect"; 5 | import { 6 | PluginInfo, 7 | PluginInfo_PluginCapability as caps, 8 | PluginConfiguration, 9 | Event, 10 | } from "@buf/webmeshproj_api.bufbuild_es/v1/plugin_pb.js"; 11 | 12 | 13 | function routes(router: ConnectRouter) { 14 | router.service(Plugin, { 15 | async getInfo(): Promise { 16 | return new PluginInfo({ 17 | name: "ts-watch-plugin", 18 | version: "0.0.1", 19 | description: "Example Typescript plugin for Webmesh", 20 | capabilities: [caps.WATCH], 21 | }) 22 | }, 23 | 24 | async configure(config: PluginConfiguration): Promise { 25 | console.log("configure", config) 26 | }, 27 | 28 | async close(): Promise { 29 | console.log("node closed") 30 | } 31 | }) 32 | 33 | router.service(WatchPlugin, { 34 | async emit(ev: Event): Promise { 35 | console.log("emit", ev) 36 | } 37 | }) 38 | } 39 | 40 | async function main() { 41 | const server = fastify(); 42 | await server.register(fastifyConnectPlugin, { 43 | routes, 44 | }); 45 | await server.listen({ host: "localhost", port: 8081 }); 46 | console.log("server is listening at", server.addresses()); 47 | } 48 | 49 | void main(); -------------------------------------------------------------------------------- /examples/remote-server-plugin/nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webmesh-plugin", 3 | "version": "1.0.0", 4 | "description": "An example webmesh plugin in Typescript", 5 | "main": "index.js", 6 | "repository": "https://github.com/webmeshproj/webmesh", 7 | "author": "Avi Zimmerman", 8 | "license": "Apache-2.0", 9 | "dependencies": { 10 | "@buf/webmeshproj_api.connectrpc_es": "^1.1.2-20231024171646-083590aca074.1", 11 | "@connectrpc/connect-fastify": "^1.1.2", 12 | "@connectrpc/connect-node": "^1.1.2", 13 | "fastify": "^4.24.3", 14 | "tsx": "^3.14.0", 15 | "typescript": "^5.2.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/simple-persistence/README.md: -------------------------------------------------------------------------------- 1 | # Simple Example with Persistence 2 | 3 | This example just has a single bootstrap server and a single join node. 4 | None of the optional services are enabled so very limited functionality is available. 5 | This is just like the [simple example](../simple/README.md) except that it uses data persistence to the local disk. 6 | 7 | ## Running 8 | 9 | You can run the example with the following command: 10 | 11 | ```bash 12 | docker-compose up 13 | ``` 14 | 15 | To shutdown the example, press `Ctrl+C` and then run: 16 | 17 | ```bash 18 | docker-compose down -v 19 | ``` 20 | 21 | There isn't much else to do with this example since none of the APIs are enabled. 22 | But you can still exec into them to test connectivity and such. 23 | -------------------------------------------------------------------------------- /examples/simple-persistence/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | networks: 4 | simple: 5 | enable_ipv6: true 6 | driver: bridge 7 | ipam: 8 | driver: default 9 | config: 10 | - subnet: 10.1.0.0/24 11 | gateway: 10.1.0.1 12 | - subnet: 2001:db8:3200::/64 13 | gateway: 2001:db8:3200::1 14 | 15 | volumes: 16 | data: 17 | 18 | services: 19 | bootstrap-node: 20 | image: ${IMAGE:-ghcr.io/webmeshproj/node:latest} 21 | networks: 22 | simple: 23 | ipv4_address: 10.1.0.2 24 | ipv6_address: 2001:db8:3200::2 25 | hostname: bootstrap-node 26 | entrypoint: 27 | - /webmesh-node 28 | - --global.insecure 29 | - --global.detect-endpoints 30 | - --global.detect-private-endpoints 31 | - --bootstrap.enabled 32 | - --storage.path=/data 33 | volumes: 34 | - data:/data 35 | cap_add: ["NET_ADMIN", "NET_RAW", "SYS_MODULE"] 36 | sysctls: 37 | - net.ipv6.conf.all.disable_ipv6=0 38 | 39 | join-node: 40 | image: ${IMAGE:-ghcr.io/webmeshproj/node:latest} 41 | networks: 42 | simple: 43 | ipv4_address: 10.1.0.3 44 | ipv6_address: 2001:db8:3200::3 45 | hostname: join-node 46 | entrypoint: 47 | - /webmesh-node 48 | - --global.insecure 49 | - --mesh.join-addresses=bootstrap-node:8443 50 | restart: on-failure 51 | cap_add: ["NET_ADMIN", "NET_RAW", "SYS_MODULE"] 52 | sysctls: 53 | - net.ipv6.conf.all.disable_ipv6=0 54 | -------------------------------------------------------------------------------- /examples/simple/README.md: -------------------------------------------------------------------------------- 1 | # Simple Example 2 | 3 | This example just has a single bootstrap server and a single join node. 4 | None of the optional services are enabled so very limited functionality is available. 5 | 6 | ## Running 7 | 8 | You can run the example with the following command: 9 | 10 | ```bash 11 | docker-compose up 12 | ``` 13 | 14 | To shutdown the example, press `Ctrl+C` and then run: 15 | 16 | ```bash 17 | docker-compose down -v 18 | ``` 19 | 20 | There isn't much else to do with this example since none of the APIs are enabled. 21 | But you can still exec into them to test connectivity and such. 22 | -------------------------------------------------------------------------------- /examples/simple/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | networks: 4 | simple: 5 | enable_ipv6: true 6 | driver: bridge 7 | ipam: 8 | driver: default 9 | config: 10 | - subnet: 10.1.0.0/24 11 | gateway: 10.1.0.1 12 | - subnet: 2001:db8:3200::/64 13 | gateway: 2001:db8:3200::1 14 | 15 | services: 16 | bootstrap-node: 17 | image: ${IMAGE:-ghcr.io/webmeshproj/node:latest} 18 | networks: 19 | simple: 20 | ipv4_address: 10.1.0.2 21 | ipv6_address: 2001:db8:3200::2 22 | hostname: bootstrap-node 23 | entrypoint: 24 | - /webmesh-node 25 | # To disable TLS over the gRPC server, set --global.insecure 26 | - --global.detect-endpoints 27 | - --global.detect-private-endpoints 28 | - --bootstrap.enabled 29 | - --storage.in-memory 30 | volumes: 31 | - /dev/net/tun:/dev/net/tun 32 | cap_add: ["NET_ADMIN", "NET_RAW", "SYS_MODULE"] 33 | ports: 34 | - "8443:8443" 35 | sysctls: 36 | - net.ipv6.conf.all.disable_ipv6=0 37 | 38 | join-node: 39 | image: ${IMAGE:-ghcr.io/webmeshproj/node:latest} 40 | networks: 41 | simple: 42 | ipv4_address: 10.1.0.3 43 | ipv6_address: 2001:db8:3200::3 44 | hostname: join-node 45 | entrypoint: 46 | - /webmesh-node 47 | # The bootstrap-node will run with a self-signed certificate by default. 48 | - --global.insecure-skip-verify 49 | - --mesh.join-addresses=bootstrap-node:8443 50 | restart: on-failure 51 | volumes: 52 | - /dev/net/tun:/dev/net/tun 53 | cap_add: ["NET_ADMIN", "NET_RAW", "SYS_MODULE"] 54 | sysctls: 55 | - net.ipv6.conf.all.disable_ipv6=0 56 | -------------------------------------------------------------------------------- /examples/site-to-site/README.md: -------------------------------------------------------------------------------- 1 | # Site-to-Site Example 2 | 3 | This example shows three sites each with a "leader" and three "followers". 4 | The leaders all bootstrap the mesh together, and then the followers join as spokes of their respective site leaders. 5 | 6 | The architecture looks like the below diagram (generated with `wmctl get graph | dot -Tpng > graph.png`): 7 | 8 | ![Site-to-Site Architecture](./graph.png) 9 | 10 | All nodes are able to communicate over the private WireGuard network. 11 | 12 | See the [multi-bootstrap example](../multi-bootstrap/) for how to start, stop, and interact with the mesh. 13 | -------------------------------------------------------------------------------- /examples/site-to-site/graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webmeshproj/webmesh/dd98b85f82a763c09486a06cd8456c378e5ee2ff/examples/site-to-site/graph.png -------------------------------------------------------------------------------- /img/webmesh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webmeshproj/webmesh/dd98b85f82a763c09486a06cd8456c378e5ee2ff/img/webmesh.png -------------------------------------------------------------------------------- /pkg/cmd/ctlcmd/root.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package ctlcmd contains the wmctl CLI tool. 18 | package ctlcmd 19 | 20 | import ( 21 | "fmt" 22 | "os" 23 | 24 | "github.com/spf13/cobra" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/cmd/ctlcmd/config" 27 | ) 28 | 29 | var ( 30 | configFileFlag string 31 | cliConfig *config.Config 32 | ) 33 | 34 | func init() { 35 | cliConfig = config.New() 36 | cliConfigPath := config.DefaultConfigPath 37 | if configPath := os.Getenv("WMCTL_CONFIG"); configPath != "" { 38 | cliConfigPath = configPath 39 | } 40 | if err := cliConfig.LoadFile(cliConfigPath); err != nil { 41 | if !os.IsNotExist(err) && cliConfigPath != config.DefaultConfigPath { 42 | fmt.Fprintf(os.Stderr, "Error loading CLI config: %v\n", err) 43 | os.Exit(1) 44 | } 45 | } 46 | cliConfig.BindFlags(rootCmd.PersistentFlags()) 47 | rootCmd.PersistentFlags().StringVarP(&configFileFlag, "config", "c", "", "Path to the CLI configuration file") 48 | } 49 | 50 | // Root returns the root command. 51 | func Root() *cobra.Command { 52 | return rootCmd 53 | } 54 | 55 | // Execute runs the root command. 56 | func Execute() error { 57 | return Root().Execute() 58 | } 59 | 60 | var rootCmd = &cobra.Command{ 61 | Use: "wmctl", 62 | Short: "wmctl is a CLI tool for managing a webmesh", 63 | SilenceErrors: true, 64 | SilenceUsage: true, 65 | PersistentPreRunE: func(_ *cobra.Command, _ []string) error { 66 | if configFileFlag != "" { 67 | if err := cliConfig.LoadFile(configFileFlag); err != nil { 68 | return fmt.Errorf("failed to load CLI config: %w", err) 69 | } 70 | } 71 | return nil 72 | }, 73 | } 74 | -------------------------------------------------------------------------------- /pkg/cmd/ctlcmd/status.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package ctlcmd 18 | 19 | import ( 20 | "github.com/spf13/cobra" 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | ) 23 | 24 | func init() { 25 | rootCmd.AddCommand(statusCmd) 26 | } 27 | 28 | var statusCmd = &cobra.Command{ 29 | Use: "status [NODE_ID]", 30 | Short: "Retrieves the status of a node in the cluster", 31 | Args: cobra.MaximumNArgs(1), 32 | ValidArgsFunction: completeNodes(1), 33 | RunE: func(cmd *cobra.Command, args []string) error { 34 | client, closer, err := cliConfig.NewNodeClient() 35 | if err != nil { 36 | return err 37 | } 38 | defer closer.Close() 39 | var req v1.GetStatusRequest 40 | if len(args) > 0 { 41 | req.Id = args[0] 42 | } 43 | status, err := client.GetStatus(cmd.Context(), &req) 44 | if err != nil { 45 | return err 46 | } 47 | return encodeToStdout(cmd, status) 48 | }, 49 | } 50 | -------------------------------------------------------------------------------- /pkg/cmd/ctlcmd/version.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package ctlcmd 18 | 19 | import ( 20 | "github.com/spf13/cobra" 21 | 22 | "github.com/webmeshproj/webmesh/pkg/version" 23 | ) 24 | 25 | var ( 26 | versionJSON bool 27 | ) 28 | 29 | func init() { 30 | versionCmd.Flags().BoolVar(&versionJSON, "json", false, "Print version information in JSON format") 31 | rootCmd.AddCommand(versionCmd) 32 | } 33 | 34 | var versionCmd = &cobra.Command{ 35 | Use: "version", 36 | Short: "Print the version number of the CLI", 37 | Args: cobra.NoArgs, 38 | RunE: func(cmd *cobra.Command, _ []string) error { 39 | version := version.GetBuildInfo() 40 | if versionJSON { 41 | cmd.Println(version.PrettyJSON("webmesh-cli")) 42 | return nil 43 | } 44 | cmd.Println("Webmesh CLI") 45 | cmd.Println(" Version: ", version.Version) 46 | cmd.Println(" Git Commit: ", version.GitCommit) 47 | cmd.Println(" Build Date: ", version.BuildDate) 48 | return nil 49 | }, 50 | } 51 | -------------------------------------------------------------------------------- /pkg/common/exec.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package common 18 | 19 | import ( 20 | "fmt" 21 | "log/slog" 22 | "os/exec" 23 | "strings" 24 | 25 | "github.com/webmeshproj/webmesh/pkg/context" 26 | ) 27 | 28 | // Exec executes a command with context. 29 | func Exec(ctx context.Context, command string, args ...string) error { 30 | log := context.LoggerFrom(ctx) 31 | cmd := exec.CommandContext(ctx, command, args...) 32 | log.Debug(command, slog.String("args", strings.Join(args, " "))) 33 | if out, err := cmd.CombinedOutput(); err != nil { 34 | return procError(command, args, err, out) 35 | } 36 | return nil 37 | } 38 | 39 | // ExecOutput executes a command with context and returns the output. 40 | func ExecOutput(ctx context.Context, command string, args ...string) ([]byte, error) { 41 | log := context.LoggerFrom(ctx) 42 | cmd := exec.CommandContext(ctx, command, args...) 43 | log.Debug(command, slog.String("args", strings.Join(args, " "))) 44 | out, err := cmd.CombinedOutput() 45 | if err != nil { 46 | return nil, procError(command, args, err, out) 47 | } 48 | return out, nil 49 | } 50 | 51 | func procError(command string, args []string, err error, out []byte) error { 52 | return fmt.Errorf("%s %s: %w: %s", command, strings.Join(args, " "), err, out) 53 | } 54 | -------------------------------------------------------------------------------- /pkg/common/util.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package common contains common utility functions. 18 | package common 19 | 20 | import ( 21 | "fmt" 22 | "math" 23 | ) 24 | 25 | // Pointer returns a pointer to the given value. 26 | func Pointer[T any](t T) *T { 27 | return &t 28 | } 29 | 30 | // UpsertSlice with insert the given item into a slice if it does not exist, 31 | // otherwise it will update the existing item and return the updated slice. 32 | func UpsertSlice[T comparable](sl []T, item T) []T { 33 | for i, v := range sl { 34 | if v == item { 35 | sl[i] = item 36 | return sl 37 | } 38 | } 39 | return append(sl, item) 40 | } 41 | 42 | // PrettyByteSize returns a human-readable string of the given byte size. 43 | func PrettyByteSize(bf float64) string { 44 | for _, unit := range []string{"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"} { 45 | if math.Abs(bf) < 1024.0 { 46 | return fmt.Sprintf("%3.1f%sB", bf, unit) 47 | } 48 | bf /= 1024.0 49 | } 50 | return fmt.Sprintf("%.1fYiB", bf) 51 | } 52 | 53 | // AllUnique returns true if all elements in the given slice are unique. 54 | func AllUnique[T comparable](sl []T) bool { 55 | seen := make(map[T]bool) 56 | for _, v := range sl { 57 | if seen[v] { 58 | return false 59 | } 60 | seen[v] = true 61 | } 62 | return true 63 | } 64 | -------------------------------------------------------------------------------- /pkg/config/bridge_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package config 18 | -------------------------------------------------------------------------------- /pkg/config/parser_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package config 18 | -------------------------------------------------------------------------------- /pkg/config/raft_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package config 18 | -------------------------------------------------------------------------------- /pkg/config/storage_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package config 18 | -------------------------------------------------------------------------------- /pkg/logging/badger_adapter.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package logging 18 | 19 | import ( 20 | "fmt" 21 | "log/slog" 22 | 23 | "github.com/dgraph-io/badger/v4" 24 | ) 25 | 26 | type badgerLogAdapter struct { 27 | *slog.Logger 28 | } 29 | 30 | // NewBadgerLogger returns a badger log adapter backed by slog with the given 31 | // log level. 32 | func NewBadgerLogger(logLevel string, format string) badger.Logger { 33 | return NewBadgerAdapter(NewLogger(logLevel, format)) 34 | } 35 | 36 | // NewBadgerAdapter returns a badger log adapter for the given logger. 37 | func NewBadgerAdapter(logger *slog.Logger) badger.Logger { 38 | return &badgerLogAdapter{Logger: logger} 39 | } 40 | 41 | func (l *badgerLogAdapter) Errorf(format string, args ...any) { 42 | l.Error(fmt.Sprintf(format, args...)) 43 | } 44 | 45 | func (l *badgerLogAdapter) Warningf(format string, args ...any) { 46 | l.Error(fmt.Sprintf(format, args...)) 47 | } 48 | 49 | func (l *badgerLogAdapter) Infof(format string, args ...any) { 50 | l.Info(fmt.Sprintf(format, args...)) 51 | } 52 | 53 | func (l *badgerLogAdapter) Debugf(format string, args ...any) { 54 | l.Debug(fmt.Sprintf(format, args...)) 55 | } 56 | -------------------------------------------------------------------------------- /pkg/logging/interceptors.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package logging 18 | 19 | import ( 20 | "log/slog" 21 | 22 | "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" 23 | "google.golang.org/grpc" 24 | 25 | "github.com/webmeshproj/webmesh/pkg/context" 26 | ) 27 | 28 | // ContextUnaryServerInterceptor returns a grpc.UnaryServerInterceptor that logs 29 | // to the given logger. 30 | func ContextUnaryServerInterceptor() grpc.UnaryServerInterceptor { 31 | return logging.UnaryServerInterceptor( 32 | ContextInterceptor(), 33 | logging.WithLogOnEvents(logging.StartCall, logging.FinishCall), 34 | ) 35 | } 36 | 37 | // ContextLogCallsStreamServerInterceptor returns a grpc.UnaryServerInterceptor that logs 38 | // to the given logger. 39 | func ContextStreamServerInterceptor() grpc.StreamServerInterceptor { 40 | return logging.StreamServerInterceptor( 41 | ContextInterceptor(), 42 | logging.WithLogOnEvents(logging.StartCall, logging.FinishCall), 43 | ) 44 | } 45 | 46 | // ContextInterceptor returns a logging.Logger that logs to the logger provided 47 | // in the context. 48 | func ContextInterceptor() logging.Logger { 49 | return logging.LoggerFunc(func(ctx context.Context, lvl logging.Level, msg string, fields ...any) { 50 | log := context.LoggerFrom(ctx) 51 | if msg == "started call" { 52 | msg = "Started gRPC call" 53 | } 54 | if msg == "finished call" { 55 | msg = "Finished gRPC call" 56 | } 57 | log.Log(ctx, slog.Level(lvl), msg, fields...) 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /pkg/logging/logging.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package logging contains logging utilities and adapters for various logging 18 | // libraries. 19 | package logging 20 | 21 | import ( 22 | "io" 23 | "log/slog" 24 | "os" 25 | "strings" 26 | ) 27 | 28 | // SetupLogging sets up logging for the application. 29 | func SetupLogging(logLevel string, format string) *slog.Logger { 30 | log := NewLogger(logLevel, format) 31 | slog.SetDefault(log) 32 | return log 33 | } 34 | 35 | // NewLogger returns a new logger with the given log level. Format can be one of "text" or "json". 36 | // If log level is empty or "silent" then the logger will be silent. 37 | func NewLogger(logLevel string, format string) *slog.Logger { 38 | if logLevel == "" || strings.ToLower(logLevel) == "silent" { 39 | return slog.New(slog.NewTextHandler(io.Discard, nil)) 40 | } 41 | level := func() slog.Level { 42 | switch strings.ToLower(logLevel) { 43 | case "debug": 44 | return slog.LevelDebug 45 | case "info": 46 | return slog.LevelInfo 47 | case "warn": 48 | return slog.LevelWarn 49 | case "error": 50 | return slog.LevelError 51 | default: 52 | slog.Default().Warn("Invalid log level specified, defaulting to info", "log-level", logLevel) 53 | } 54 | return slog.LevelInfo 55 | }() 56 | var handler slog.Handler 57 | switch format { 58 | case "text": 59 | handler = slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ 60 | Level: level, 61 | }) 62 | case "json": 63 | fallthrough 64 | default: 65 | handler = slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{ 66 | Level: level, 67 | }) 68 | } 69 | log := slog.New(handler) 70 | return log 71 | } 72 | -------------------------------------------------------------------------------- /pkg/logging/stun_logger.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package logging 18 | 19 | import ( 20 | "fmt" 21 | "log/slog" 22 | 23 | "github.com/pion/logging" 24 | ) 25 | 26 | type STUNLoggerFactory struct { 27 | *slog.Logger 28 | } 29 | 30 | func NewSTUNLoggerFactory(logger *slog.Logger) *STUNLoggerFactory { 31 | return &STUNLoggerFactory{logger} 32 | } 33 | 34 | func (f *STUNLoggerFactory) NewLogger(scope string) logging.LeveledLogger { 35 | return &slogSTUNLogger{f.Logger.With(slog.String("scope", scope))} 36 | } 37 | 38 | type slogSTUNLogger struct{ *slog.Logger } 39 | 40 | func (l *slogSTUNLogger) Trace(msg string) { 41 | l.Logger.Debug(msg) 42 | } 43 | 44 | func (l *slogSTUNLogger) Tracef(format string, args ...interface{}) { 45 | l.Logger.Debug(fmt.Sprintf(format, args...)) 46 | } 47 | 48 | func (l *slogSTUNLogger) Debug(msg string) { 49 | l.Logger.Debug(msg) 50 | } 51 | 52 | func (l *slogSTUNLogger) Debugf(format string, args ...interface{}) { 53 | l.Logger.Debug(fmt.Sprintf(format, args...)) 54 | } 55 | 56 | func (l *slogSTUNLogger) Info(msg string) { 57 | l.Logger.Info(msg) 58 | } 59 | 60 | func (l *slogSTUNLogger) Infof(format string, args ...interface{}) { 61 | l.Logger.Info(fmt.Sprintf(format, args...)) 62 | } 63 | 64 | func (l *slogSTUNLogger) Warn(msg string) { 65 | l.Logger.Warn(msg) 66 | } 67 | 68 | func (l *slogSTUNLogger) Warnf(format string, args ...interface{}) { 69 | l.Logger.Warn(fmt.Sprintf(format, args...)) 70 | } 71 | 72 | func (l *slogSTUNLogger) Error(msg string) { 73 | l.Logger.Error(msg) 74 | } 75 | 76 | func (l *slogSTUNLogger) Errorf(format string, args ...interface{}) { 77 | l.Logger.Error(fmt.Sprintf(format, args...)) 78 | } 79 | -------------------------------------------------------------------------------- /pkg/meshnet/nat64/nat64.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package nat64 provides a stateless bi-directional NAT64 implementation. It is 18 | // based on eBPF and XDP and is intended to be used in conjunction with a mesh network. 19 | // 20 | // TODO: The intention of this package is to be a potential option for bridging 21 | // meshes to either the public internet or other meshes. A node could use part of 22 | // its private IPv6 allocation to provide IPv6/IPv4 translation to another mesh. It 23 | // could also use a public IPv6 allocation to provide translation into its current 24 | // mesh. There are other projects capable of providing this functionality, such as 25 | // Jool, but it may be nice to have an option that is more tightly integrated with 26 | // the mesh. 27 | package nat64 28 | 29 | import "net/netip" 30 | 31 | // Options contains the configuration options for a NAT64 instance. 32 | type Options struct { 33 | // The interfaces to swap IPv4 and IPv6 addresses on. 34 | LIface, RIface string 35 | // The IPv6 prefixes to use for translation. 36 | LPrefixV6, RPrefixV6 netip.Prefix 37 | // The IPv4 prefixes to use for translation. 38 | LPrefixV4, RPrefixV4 netip.Prefix 39 | } 40 | -------------------------------------------------------------------------------- /pkg/meshnet/netutil/dns.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package netutil 18 | 19 | import ( 20 | "fmt" 21 | "log/slog" 22 | "net" 23 | "time" 24 | 25 | "github.com/webmeshproj/webmesh/pkg/context" 26 | ) 27 | 28 | // ResolveTCPAddr resolves a TCP address with retries and context. 29 | func ResolveTCPAddr(ctx context.Context, lookup string, maxRetries int) (net.Addr, error) { 30 | var addr net.Addr 31 | var err error 32 | var tries int 33 | for tries < maxRetries { 34 | addr, err = net.ResolveTCPAddr("tcp", lookup) 35 | if err != nil { 36 | tries++ 37 | err = fmt.Errorf("resolve tcp address: %w", err) 38 | context.LoggerFrom(ctx).Error("failed to resolve advertise address", slog.String("error", err.Error())) 39 | if tries < maxRetries { 40 | select { 41 | case <-ctx.Done(): 42 | return nil, fmt.Errorf("%w: %w", err, ctx.Err()) 43 | case <-time.After(time.Second * 1): 44 | continue 45 | } 46 | } 47 | } 48 | break 49 | } 50 | return addr, err 51 | } 52 | -------------------------------------------------------------------------------- /pkg/meshnet/netutil/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // package netutil provides common utility functions for networking. 18 | package netutil 19 | -------------------------------------------------------------------------------- /pkg/meshnet/netutil/ping.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package netutil 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "net/netip" 23 | "os" 24 | "time" 25 | 26 | "github.com/go-ping/ping" 27 | ) 28 | 29 | // Ping sends ICMP echo requests to the given address. The context must 30 | // have a timeout set and is used for the duration of the ping. The 31 | // function returns an error if no replies were received. 32 | func Ping(ctx context.Context, addr netip.Addr) error { 33 | deadline, ok := ctx.Deadline() 34 | if !ok { 35 | return fmt.Errorf("no deadline set") 36 | } 37 | pinger, err := ping.NewPinger(addr.String()) 38 | if err != nil { 39 | return fmt.Errorf("create pinger: %w", err) 40 | } 41 | pinger.Timeout = time.Until(deadline) 42 | pinger.Interval = 500 * time.Millisecond 43 | if os.Geteuid() == 0 { 44 | pinger.SetPrivileged(true) 45 | } 46 | pinger.SetLogger(ping.NoopLogger{}) 47 | err = pinger.Run() 48 | if err != nil { 49 | return fmt.Errorf("run pinger: %w", err) 50 | } 51 | stats := pinger.Statistics() 52 | if stats.PacketsRecv == 0 { 53 | return fmt.Errorf("no replies received") 54 | } 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /pkg/meshnet/netutil/ports.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package netutil 18 | 19 | import ( 20 | "fmt" 21 | "strconv" 22 | "strings" 23 | ) 24 | 25 | // ParsePortRange parses a port range string. 26 | func ParsePortRange(s string) (start int, end int, err error) { 27 | spl := strings.Split(s, "-") 28 | if len(spl) > 2 { 29 | return 0, 0, fmt.Errorf("invalid port range: %s", s) 30 | } 31 | start, err = strconv.Atoi(spl[0]) 32 | if err != nil { 33 | return 0, 0, fmt.Errorf("invalid port range: %s", s) 34 | } 35 | end = start 36 | if len(spl) == 2 { 37 | end, err = strconv.Atoi(spl[1]) 38 | if err != nil { 39 | return 0, 0, fmt.Errorf("invalid port range: %s", s) 40 | } 41 | } 42 | return start, end, nil 43 | } 44 | -------------------------------------------------------------------------------- /pkg/meshnet/system/buffers/buffer_darwin.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package buffers 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | "github.com/webmeshproj/webmesh/pkg/common" 24 | ) 25 | 26 | func setMaxReadBuffer(val int) error { 27 | // On darwin and BSD we need to add 15% to the value to account for 28 | // overhead. 29 | val = int(float64(val) * 1.15) 30 | return common.Exec(context.Background(), "sysctl", "-w", fmt.Sprintf("kern.ipc.maxsockbuf=%d", val)) 31 | } 32 | 33 | func setMaxWriteBuffer(val int) error { 34 | // On darwin and BSD we need to add 15% to the value to account for 35 | // overhead. 36 | val = int(float64(val) * 1.15) 37 | return common.Exec(context.Background(), "sysctl", "-w", fmt.Sprintf("kern.ipc.maxsockbuf=%d", val)) 38 | } 39 | -------------------------------------------------------------------------------- /pkg/meshnet/system/buffers/buffer_freebsd.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package buffers 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | "github.com/webmeshproj/webmesh/pkg/common" 24 | ) 25 | 26 | func setMaxReadBuffer(val int) error { 27 | // On darwin and BSD we need to add 15% to the value to account for 28 | // overhead. 29 | val = int(float64(val) * 1.15) 30 | return common.Exec(context.Background(), "sysctl", "-w", fmt.Sprintf("kern.ipc.maxsockbuf=%d", val)) 31 | } 32 | 33 | func setMaxWriteBuffer(val int) error { 34 | // On darwin and BSD we need to add 15% to the value to account for 35 | // overhead. 36 | val = int(float64(val) * 1.15) 37 | return common.Exec(context.Background(), "sysctl", "-w", fmt.Sprintf("kern.ipc.maxsockbuf=%d", val)) 38 | } 39 | -------------------------------------------------------------------------------- /pkg/meshnet/system/buffers/buffer_linux.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package buffers 18 | 19 | import ( 20 | "strconv" 21 | 22 | "github.com/containernetworking/plugins/pkg/utils/sysctl" 23 | ) 24 | 25 | func setMaxReadBuffer(val int) error { 26 | _, err := sysctl.Sysctl("net/core/rmem_max", strconv.Itoa(val)) 27 | return err 28 | } 29 | 30 | func setMaxWriteBuffer(val int) error { 31 | _, err := sysctl.Sysctl("net/core/wmem_max", strconv.Itoa(val)) 32 | return err 33 | } 34 | -------------------------------------------------------------------------------- /pkg/meshnet/system/buffers/buffer_windows.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package buffers 18 | 19 | import "errors" 20 | 21 | func setMaxReadBuffer(val int) error { 22 | return errors.New("not implemented") 23 | } 24 | 25 | func setMaxWriteBuffer(val int) error { 26 | return errors.New("not implemented") 27 | } 28 | -------------------------------------------------------------------------------- /pkg/meshnet/system/buffers/buffers.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package buffers contains facilities for changing system buffer sizes. 18 | package buffers 19 | 20 | // SetMaximumReadBuffer sets the maximum read buffer size. 21 | func SetMaximumReadBuffer(val int) error { 22 | return setMaxReadBuffer(val) 23 | } 24 | 25 | // SetMaximumWriteBuffer sets the maximum write buffer size. 26 | func SetMaximumWriteBuffer(val int) error { 27 | return setMaxWriteBuffer(val) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/meshnet/system/buffers/buffers_wasm.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package buffers 18 | 19 | import "errors" 20 | 21 | func setMaxReadBuffer(val int) error { 22 | return errors.New("not implemented") 23 | } 24 | 25 | func setMaxWriteBuffer(val int) error { 26 | return errors.New("not implemented") 27 | } 28 | -------------------------------------------------------------------------------- /pkg/meshnet/system/dns/dnsconfig_wasm.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package dns 18 | 19 | import ( 20 | "errors" 21 | "net/netip" 22 | ) 23 | 24 | func loadSystemConfig() (*DNSConfig, error) { 25 | return nil, errors.New("not implemented") 26 | } 27 | 28 | func addServers(iface string, servers []netip.AddrPort) error { 29 | return errors.New("not implemented") 30 | } 31 | 32 | func removeServers(iface string, servers []netip.AddrPort) error { 33 | return errors.New("not implemented") 34 | } 35 | -------------------------------------------------------------------------------- /pkg/meshnet/system/firewall/firewall_wasm.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package firewall contains an interface for interacting with the system firewall. 18 | package firewall 19 | 20 | import ( 21 | "context" 22 | "errors" 23 | ) 24 | 25 | func newFirewall(ctx context.Context, opts *Options) (Firewall, error) { 26 | return nil, errors.New("not implemented") 27 | } 28 | -------------------------------------------------------------------------------- /pkg/meshnet/system/link/address_darwin.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package link 18 | 19 | import ( 20 | "context" 21 | "errors" 22 | "fmt" 23 | "net/netip" 24 | "strings" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/common" 27 | ) 28 | 29 | // SetInterfaceAddress sets the address of the interface with the given name. 30 | func SetInterfaceAddress(ctx context.Context, name string, addr netip.Prefix) error { 31 | if addr.Addr().Is4() { 32 | out, err := common.ExecOutput(ctx, "ifconfig", name, "inet", addr.String(), addr.Addr().String()) 33 | if err != nil { 34 | if strings.Contains(string(out), "not exist") { 35 | return ErrLinkNotExists 36 | } 37 | return err 38 | } 39 | return nil 40 | } 41 | out, err := common.ExecOutput(ctx, "ifconfig", name, "inet6", addr.String(), "prefixlen", fmt.Sprintf("%d", addr.Bits()), "alias") 42 | if err != nil { 43 | if strings.Contains(string(out), "not exist") { 44 | return ErrLinkNotExists 45 | } 46 | return err 47 | } 48 | return nil 49 | } 50 | 51 | // RemoveInterfaceAddress removes the address of the interface with the given name. 52 | func RemoveInterfaceAddress(_ context.Context, name string, addr netip.Prefix) error { 53 | return errors.New("not implemented") 54 | } 55 | -------------------------------------------------------------------------------- /pkg/meshnet/system/link/address_freebsd.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package link 18 | 19 | import ( 20 | "context" 21 | "errors" 22 | "fmt" 23 | "net/netip" 24 | "strings" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/common" 27 | ) 28 | 29 | // SetInterfaceAddress sets the address of the interface with the given name. 30 | func SetInterfaceAddress(ctx context.Context, name string, addr netip.Prefix) error { 31 | if addr.Addr().Is4() { 32 | out, err := common.ExecOutput(ctx, "ifconfig", name, "inet", addr.String(), addr.Addr().String()) 33 | if err != nil { 34 | if strings.Contains(string(out), "not exist") { 35 | return ErrLinkNotExists 36 | } 37 | return err 38 | } 39 | return nil 40 | } 41 | out, err := common.ExecOutput(ctx, "ifconfig", name, "inet6", addr.String(), "prefixlen", fmt.Sprintf("%d", addr.Bits()), "alias") 42 | if err != nil { 43 | if strings.Contains(string(out), "not exist") { 44 | return ErrLinkNotExists 45 | } 46 | return err 47 | } 48 | return nil 49 | } 50 | 51 | // RemoveInterfaceAddress removes the address of the interface with the given name. 52 | func RemoveInterfaceAddress(_ context.Context, name string, addr netip.Prefix) error { 53 | return errors.New("not implemented") 54 | } 55 | -------------------------------------------------------------------------------- /pkg/meshnet/system/link/address_linux.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package link 18 | 19 | import ( 20 | "errors" 21 | "fmt" 22 | "net/netip" 23 | 24 | "github.com/vishvananda/netlink" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/context" 27 | ) 28 | 29 | // SetInterfaceAddress sets the address of the interface with the given name. 30 | func SetInterfaceAddress(_ context.Context, name string, addr netip.Prefix) error { 31 | link, err := netlink.LinkByName(name) 32 | if err != nil { 33 | var notExistsErr *netlink.LinkNotFoundError 34 | if errors.As(err, ¬ExistsErr) { 35 | return ErrLinkNotExists 36 | } 37 | return err 38 | } 39 | nladdr, err := netlink.ParseAddr(addr.String()) 40 | if err != nil { 41 | return fmt.Errorf("netlink parse addr: %w", err) 42 | } 43 | return netlink.AddrAdd(link, nladdr) 44 | } 45 | 46 | // RemoveInterfaceAddress removes the address of the interface with the given name. 47 | func RemoveInterfaceAddress(_ context.Context, name string, addr netip.Prefix) error { 48 | link, err := netlink.LinkByName(name) 49 | if err != nil { 50 | var notExistsErr *netlink.LinkNotFoundError 51 | if errors.As(err, ¬ExistsErr) { 52 | return ErrLinkNotExists 53 | } 54 | return err 55 | } 56 | nladdr, err := netlink.ParseAddr(addr.String()) 57 | if err != nil { 58 | return fmt.Errorf("netlink parse addr: %w", err) 59 | } 60 | return netlink.AddrDel(link, nladdr) 61 | } 62 | -------------------------------------------------------------------------------- /pkg/meshnet/system/link/address_wasm.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package link 18 | 19 | import ( 20 | "context" 21 | "errors" 22 | "net/netip" 23 | ) 24 | 25 | // SetInterfaceAddress sets the address of the interface with the given name. 26 | func SetInterfaceAddress(ctx context.Context, name string, addr netip.Prefix) error { 27 | return errors.New("not implemented") 28 | } 29 | 30 | // RemoveInterfaceAddress removes the address of the interface with the given name. 31 | func RemoveInterfaceAddress(_ context.Context, name string, addr netip.Prefix) error { 32 | return errors.New("not implemented") 33 | } 34 | -------------------------------------------------------------------------------- /pkg/meshnet/system/link/address_windows.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package link 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "net" 23 | "net/netip" 24 | 25 | "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" 26 | ) 27 | 28 | // SetInterfaceAddress sets the address of the interface with the given name. 29 | func SetInterfaceAddress(ctx context.Context, name string, addr netip.Prefix) error { 30 | link, err := net.InterfaceByName(name) 31 | if err != nil { 32 | return fmt.Errorf("net link by name: %w", err) 33 | } 34 | luid, err := winipcfg.LUIDFromIndex(uint32(link.Index)) 35 | if err != nil { 36 | return fmt.Errorf("winipcfg luid from index: %w", err) 37 | } 38 | err = luid.AddIPAddress(addr) 39 | if err != nil { 40 | return fmt.Errorf("winipcfg add ip address: %w", err) 41 | } 42 | return nil 43 | } 44 | 45 | // RemoveInterfaceAddress removes the address of the interface with the given name. 46 | func RemoveInterfaceAddress(_ context.Context, name string, addr netip.Prefix) error { 47 | link, err := net.InterfaceByName(name) 48 | if err != nil { 49 | return fmt.Errorf("net link by name: %w", err) 50 | } 51 | luid, err := winipcfg.LUIDFromIndex(uint32(link.Index)) 52 | if err != nil { 53 | return fmt.Errorf("winipcfg luid from index: %w", err) 54 | } 55 | err = luid.DeleteIPAddress(addr) 56 | if err != nil { 57 | return fmt.Errorf("winipcfg delete ip address: %w", err) 58 | } 59 | return nil 60 | } 61 | -------------------------------------------------------------------------------- /pkg/meshnet/system/link/link.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package link 18 | 19 | import "errors" 20 | 21 | var ( 22 | // ErrLinkNotExists is returned when a link does not exist. 23 | ErrLinkNotExists = errors.New("link does not exist") 24 | ) 25 | -------------------------------------------------------------------------------- /pkg/meshnet/system/link/link_wasm.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package link 18 | 19 | import ( 20 | "context" 21 | "errors" 22 | "net/netip" 23 | ) 24 | 25 | // ActivateInterface activates the interface with the given name. 26 | func ActivateInterface(ctx context.Context, name string) error { 27 | return errors.New("not implemented") 28 | } 29 | 30 | // DeactivateInterface deactivates the interface with the given name. 31 | func DeactivateInterface(ctx context.Context, name string) error { 32 | return errors.New("not implemented") 33 | } 34 | 35 | // RemoveInterface removes the given interface. 36 | func RemoveInterface(ctx context.Context, ifaceName string) error { 37 | return errors.New("not implemented") 38 | } 39 | 40 | // InterfaceNetwork returns the network for the given interface and address. 41 | func InterfaceNetwork(ifaceName string, forAddr netip.Addr, ipv6 bool) (netip.Prefix, error) { 42 | return netip.Prefix{}, errors.New("not implemented") 43 | } 44 | -------------------------------------------------------------------------------- /pkg/meshnet/system/link/new_wasm.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package link 18 | 19 | import ( 20 | "errors" 21 | 22 | "github.com/webmeshproj/webmesh/pkg/context" 23 | ) 24 | 25 | // NewKernel creates a new kernel WireGuard interface on the host system with the given name. 26 | func NewKernel(ctx context.Context, name string, mtu uint32) error { 27 | return errors.New("kernel interfaces not supported on wasm") 28 | } 29 | 30 | // NewTUN creates a new WireGuard interface using the userspace tun driver. 31 | func NewTUN(ctx context.Context, name string, mtu uint32) (realName string, closer func(), err error) { 32 | return "", nil, errors.New("tun interfaces not supported on wasm") 33 | } 34 | -------------------------------------------------------------------------------- /pkg/meshnet/system/link/new_windows.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package link 18 | 19 | import ( 20 | "errors" 21 | "fmt" 22 | "log/slog" 23 | 24 | "golang.zx2c4.com/wireguard/conn" 25 | "golang.zx2c4.com/wireguard/device" 26 | "golang.zx2c4.com/wireguard/ipc" 27 | "golang.zx2c4.com/wireguard/tun" 28 | 29 | "github.com/webmeshproj/webmesh/pkg/context" 30 | ) 31 | 32 | // NewKernel creates a new kernel WireGuard interface on the host system with the given name. 33 | func NewKernel(ctx context.Context, name string, mtu uint32) error { 34 | return errors.New("kernel interfaces not supported on windows") 35 | } 36 | 37 | // NewTUN creates a new WireGuard interface using the userspace tun driver. 38 | func NewTUN(ctx context.Context, name string, mtu uint32) (realName string, closer func(), err error) { 39 | tun, err := tun.CreateTUN(name, int(mtu)) 40 | if err != nil { 41 | err = fmt.Errorf("create tun: %w", err) 42 | return 43 | } 44 | realName, err = tun.Name() 45 | if err != nil { 46 | err = fmt.Errorf("get tun name: %w", err) 47 | return 48 | } 49 | device := device.NewDevice(tun, conn.NewDefaultBind(), device.NewLogger( 50 | func() int { 51 | if context.LoggerFrom(ctx).Handler().Enabled(context.Background(), slog.LevelDebug) { 52 | return device.LogLevelVerbose 53 | } 54 | return device.LogLevelError 55 | }(), 56 | fmt.Sprintf("(%s) ", realName), 57 | )) 58 | uapi, err := ipc.UAPIListen(realName) 59 | if err != nil { 60 | device.Close() 61 | err = fmt.Errorf("uapi listen: %w", err) 62 | return 63 | } 64 | go func() { 65 | for { 66 | conn, err := uapi.Accept() 67 | if err != nil { 68 | return 69 | } 70 | go device.IpcHandle(conn) 71 | } 72 | }() 73 | closer = func() { 74 | uapi.Close() 75 | device.Close() 76 | } 77 | return 78 | } 79 | -------------------------------------------------------------------------------- /pkg/meshnet/system/netns_any.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | 3 | /* 4 | Copyright 2023 Avi Zimmerman 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | // Package system contains utilities for managing network interfaces on the system. 20 | package system 21 | 22 | import ( 23 | "errors" 24 | ) 25 | 26 | func moveLinkIn(contNS string, ifName string) error { 27 | return errors.New("namespace support not implemented on this platform") 28 | } 29 | 30 | func moveLinkOut(contNS string, ifName string) error { 31 | return errors.New("namespace support not implemented on this platform") 32 | } 33 | 34 | func DoInNetNS(netNS string, fn func() error) error { 35 | return errors.New("namespace support not implemented on this platform") 36 | } 37 | -------------------------------------------------------------------------------- /pkg/meshnet/system/routes/forwarding_darwin.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package routes 18 | 19 | import ( 20 | "context" 21 | 22 | "github.com/webmeshproj/webmesh/pkg/common" 23 | ) 24 | 25 | // EnableIPForwarding enables IP forwarding. 26 | func EnableIPForwarding() error { 27 | return common.Exec(context.Background(), "sysctl", "-w", "net.inet.ip.forwarding=1") 28 | } 29 | -------------------------------------------------------------------------------- /pkg/meshnet/system/routes/forwarding_freebsd.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package routes 18 | 19 | import ( 20 | "context" 21 | 22 | "github.com/webmeshproj/webmesh/pkg/common" 23 | ) 24 | 25 | // EnableIPForwarding enables IP forwarding. 26 | func EnableIPForwarding() error { 27 | return common.Exec(context.Background(), "sysctl", "-w", "net.inet.ip.forwarding=1") 28 | } 29 | -------------------------------------------------------------------------------- /pkg/meshnet/system/routes/forwarding_linux.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package routes 18 | 19 | import ( 20 | "errors" 21 | "fmt" 22 | 23 | "github.com/containernetworking/plugins/pkg/utils/sysctl" 24 | ) 25 | 26 | // EnableIPForwarding enables IP forwarding. 27 | func EnableIPForwarding() error { 28 | errs := make([]error, 0, 3) 29 | _, err := sysctl.Sysctl("net/ipv4/conf/all/forwarding", "1") 30 | if err != nil { 31 | errs = append(errs, fmt.Errorf("write net.ipv4.conf.all.forwarding: %w", err)) 32 | } 33 | // Write to the legacy configuration file 34 | _, err = sysctl.Sysctl("net/ipv4/ip_forward", "1") 35 | if err != nil { 36 | errs = append(errs, fmt.Errorf("write net.ipv4.ip_forward: %w", err)) 37 | } 38 | _, err = sysctl.Sysctl("net/ipv6/conf/all/forwarding", "1") 39 | if err != nil { 40 | errs = append(errs, fmt.Errorf("write net.ipv6.conf.all.forwarding: %w", err)) 41 | } 42 | if len(errs) > 0 { 43 | return errors.Join(errs...) 44 | } 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /pkg/meshnet/system/routes/forwarding_wasm.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package routes 18 | 19 | import "log/slog" 20 | 21 | // EnableIPForwarding enables IP forwarding. 22 | func EnableIPForwarding() error { 23 | slog.Default().Debug("not enabling IP forwarding on wasm") 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /pkg/meshnet/system/routes/forwarding_windows.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package routes 18 | 19 | import "log/slog" 20 | 21 | // EnableIPForwarding enables IP forwarding. 22 | func EnableIPForwarding() error { 23 | slog.Default().Debug("not enabling IP forwarding on Windows") 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /pkg/meshnet/system/routes/routes.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package routes 18 | 19 | import ( 20 | "errors" 21 | "net/netip" 22 | ) 23 | 24 | // ErrRouteExists is returned when a route already exists. 25 | var ErrRouteExists = errors.New("route already exists") 26 | 27 | // Gateway represents a gateway route. It contains the name and IP address 28 | // of a gateway interface. 29 | type Gateway struct { 30 | // Name is the name of the gateway interface. 31 | Name string 32 | // Addr is the IP address of the gateway interface. 33 | Addr netip.Addr 34 | } 35 | -------------------------------------------------------------------------------- /pkg/meshnet/system/routes/routes_wasm.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package routes 18 | 19 | import ( 20 | "context" 21 | "errors" 22 | "net/netip" 23 | ) 24 | 25 | // GetDefaultGateway returns the default gateway of the current system. 26 | func GetDefaultGateway(ctx context.Context) (Gateway, error) { 27 | return netip.Addr{}, errors.New("not implemented") 28 | } 29 | 30 | // SetDefaultIPv4Gateway sets the default IPv4 gateway for the current system. 31 | func SetDefaultIPv4Gateway(ctx context.Context, gateway Gateway) error { 32 | return errors.New("not implemented") 33 | } 34 | 35 | // Add adds a route to the interface with the given name. 36 | func Add(ctx context.Context, ifaceName string, addr netip.Prefix) error { 37 | return errors.New("not implemented") 38 | } 39 | 40 | // Remove removes a route from the interface with the given name. 41 | func Remove(ctx context.Context, ifaceName string, addr netip.Prefix) error { 42 | return errors.New("not implemented") 43 | } 44 | -------------------------------------------------------------------------------- /pkg/meshnet/testutil/dns.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package testutil 18 | 19 | import ( 20 | "context" 21 | "net" 22 | "net/netip" 23 | ) 24 | 25 | // DNSManager is a mock dns manager. 26 | type DNSManager struct { 27 | servers []netip.AddrPort 28 | searchDomains []string 29 | } 30 | 31 | // Resolver returns a net.Resolver that can be used to resolve DNS names. 32 | func (d *DNSManager) Resolver() *net.Resolver { 33 | return net.DefaultResolver 34 | } 35 | 36 | // AddServers adds the given dns servers to the system configuration. 37 | func (d *DNSManager) AddServers(ctx context.Context, servers []netip.AddrPort) error { 38 | d.servers = append(d.servers, servers...) 39 | return nil 40 | } 41 | 42 | // AddServers adds the given dns servers to the system configuration. 43 | func (d *DNSManager) AddSearchDomains(ctx context.Context, domains []string) error { 44 | d.searchDomains = append(d.searchDomains, domains...) 45 | return nil 46 | } 47 | 48 | // RefreshServers checks which peers in the database are offering DNS 49 | // and updates the system configuration accordingly. 50 | func (d *DNSManager) RefreshServers(ctx context.Context) error { 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /pkg/meshnet/testutil/firewall.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package testutil 18 | 19 | import "context" 20 | 21 | // Firewall is a mock firewall. 22 | type Firewall struct{} 23 | 24 | // AddWireguardForwarding should configure the firewall to allow forwarding traffic on the wireguard interface. 25 | func (fw *Firewall) AddWireguardForwarding(ctx context.Context, ifaceName string) error { 26 | return nil 27 | } 28 | 29 | // AddMasquerade should configure the firewall to masquerade outbound traffic on the wireguard interface. 30 | func (fw *Firewall) AddMasquerade(ctx context.Context, ifaceName string) error { 31 | return nil 32 | } 33 | 34 | // Clear should clear any changes made to the firewall. 35 | func (fw *Firewall) Clear(ctx context.Context) error { 36 | return nil 37 | } 38 | 39 | // Close should close any resources used by the firewall. It should also perform a Clear. 40 | func (fw *Firewall) Close(ctx context.Context) error { 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /pkg/meshnet/testutil/testutil.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package testutil contains testing utilities for networking and meshnet. 18 | package testutil 19 | -------------------------------------------------------------------------------- /pkg/meshnet/transport/libp2p/conn.go: -------------------------------------------------------------------------------- 1 | //go:build !wasm 2 | 3 | /* 4 | Copyright 2023 Avi Zimmerman 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | // Package libp2p provides webmesh integration with libp2p. 20 | package libp2p 21 | 22 | import ( 23 | "net" 24 | 25 | "github.com/libp2p/go-libp2p/core/network" 26 | mnet "github.com/multiformats/go-multiaddr/net" 27 | ) 28 | 29 | // NewConnFromStream creates a new net.Conn from a libp2p stream. 30 | func NewConnFromStream(stream network.Stream) net.Conn { 31 | return &streamConn{stream} 32 | } 33 | 34 | type streamConn struct { 35 | network.Stream 36 | } 37 | 38 | func (s *streamConn) LocalAddr() net.Addr { 39 | addr, _ := mnet.ToNetAddr(s.Stream.Conn().LocalMultiaddr()) 40 | return addr 41 | } 42 | 43 | func (s *streamConn) RemoteAddr() net.Addr { 44 | addr, _ := mnet.ToNetAddr(s.Stream.Conn().RemoteMultiaddr()) 45 | return addr 46 | } 47 | -------------------------------------------------------------------------------- /pkg/meshnet/transport/libp2p/embedded/wgtransport/conn.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package wgtransport 18 | 19 | import ( 20 | "log/slog" 21 | 22 | "github.com/libp2p/go-libp2p/core/peer" 23 | ma "github.com/multiformats/go-multiaddr" 24 | mnet "github.com/multiformats/go-multiaddr/net" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/context" 27 | wmcrypto "github.com/webmeshproj/webmesh/pkg/crypto" 28 | wmproto "github.com/webmeshproj/webmesh/pkg/meshnet/transport/libp2p/embedded/protocol" 29 | "github.com/webmeshproj/webmesh/pkg/meshnet/wireguard" 30 | ) 31 | 32 | // Conn wraps the basic net.Conn with a reference back to the underlying transport. 33 | type Conn struct { 34 | mnet.Conn 35 | rt *Transport 36 | lkey wmcrypto.PrivateKey 37 | lpeer peer.ID 38 | iface wireguard.Interface 39 | rmaddr ma.Multiaddr 40 | eps []string 41 | log *slog.Logger 42 | } 43 | 44 | func (w *Conn) LocalMultiaddr() ma.Multiaddr { 45 | return wmproto.Encapsulate(w.Conn.LocalMultiaddr(), w.lpeer) 46 | } 47 | 48 | func (w *Conn) RemoteMultiaddr() ma.Multiaddr { 49 | return w.rmaddr 50 | } 51 | 52 | // Context returns a context that contains the logger tied 53 | // to this connection 54 | func (w *Conn) Context() context.Context { 55 | return context.WithLogger(context.Background(), w.log) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/meshnet/transport/libp2p/embedded/wgtransport/listener.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package wgtransport 18 | 19 | import ( 20 | "errors" 21 | "net" 22 | 23 | "github.com/libp2p/go-libp2p/core/transport" 24 | ma "github.com/multiformats/go-multiaddr" 25 | mnet "github.com/multiformats/go-multiaddr/net" 26 | 27 | wmproto "github.com/webmeshproj/webmesh/pkg/meshnet/transport/libp2p/embedded/protocol" 28 | ) 29 | 30 | // Listener wraps a basic listener to be upgraded and injects the transport 31 | // into incoming connections. 32 | type Listener struct { 33 | mnet.Listener 34 | rt *Transport 35 | conns chan *Conn 36 | donec chan struct{} 37 | } 38 | 39 | // Accept waits for and returns the next connection to the listener. 40 | func (ln *Listener) Accept() (mnet.Conn, error) { 41 | select { 42 | case c := <-ln.conns: 43 | return c, nil 44 | case <-ln.donec: 45 | return nil, transport.ErrListenerClosed 46 | } 47 | } 48 | 49 | func (ln *Listener) Multiaddr() ma.Multiaddr { 50 | return wmproto.Encapsulate(ln.Listener.Multiaddr(), ln.rt.peerID) 51 | } 52 | 53 | func (ln *Listener) handleIncoming() { 54 | defer close(ln.donec) 55 | for { 56 | c, err := ln.Listener.Accept() 57 | if err != nil { 58 | if errors.Is(err, net.ErrClosed) { 59 | return 60 | } 61 | ln.rt.log.Error("Failed to accept connection", "error", err.Error()) 62 | return 63 | } 64 | ln.conns <- ln.rt.WrapConn(c) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /pkg/meshnet/transport/libp2p/peerstore.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package libp2p 18 | 19 | import ( 20 | "time" 21 | 22 | "github.com/libp2p/go-libp2p/core/peer" 23 | "github.com/libp2p/go-libp2p/core/peerstore" 24 | "github.com/libp2p/go-libp2p/core/record" 25 | "github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem" 26 | ) 27 | 28 | var _ peerstore.Peerstore = (*UncertifiedPeerstore)(nil) 29 | 30 | // UncertifiedPeerstore is a peerstore that does not verify peer addresses with 31 | // signatures. 32 | type UncertifiedPeerstore struct { 33 | peerstore.Peerstore 34 | } 35 | 36 | // NewUncertifiedPeerstore creates a new uncertified peerstore. 37 | func NewUncertifiedPeerstore() (peerstore.Peerstore, error) { 38 | memstore, err := pstoremem.NewPeerstore() 39 | if err != nil { 40 | return nil, err 41 | } 42 | return &UncertifiedPeerstore{ 43 | Peerstore: memstore, 44 | }, nil 45 | } 46 | 47 | func (ps *UncertifiedPeerstore) ConsumePeerRecord(s *record.Envelope, ttl time.Duration) (accepted bool, err error) { 48 | return true, nil 49 | } 50 | 51 | func (ps *UncertifiedPeerstore) GetPeerRecord(p peer.ID) *record.Envelope { 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /pkg/meshnet/transport/libp2p/peerstore_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package libp2p 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/libp2p/go-libp2p/core/peer" 23 | "github.com/libp2p/go-libp2p/core/peerstore" 24 | "github.com/multiformats/go-multiaddr" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/crypto" 27 | ) 28 | 29 | func TestUncertifiedPeerstore(t *testing.T) { 30 | ps, err := NewUncertifiedPeerstore() 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | // Make sure we can coerce it to a certified peerstore. 35 | // The certification methods should be no-ops. 36 | _, ok := peerstore.GetCertifiedAddrBook(ps) 37 | if !ok { 38 | t.Fatal("expected certified addr book") 39 | } 40 | // Make sure we can add addresses and have them immediately be 41 | // available. 42 | key := crypto.MustGenerateKey() 43 | id := key.ID() 44 | ps.AddAddrs(peer.ID(id), []multiaddr.Multiaddr{multiaddr.StringCast("/ip4/127.0.0.1/tcp/8080")}, peerstore.PermanentAddrTTL) 45 | addrs := ps.Addrs(peer.ID(id)) 46 | if len(addrs) != 1 { 47 | t.Fatal("expected 1 address") 48 | } 49 | if addrs[0].String() != "/ip4/127.0.0.1/tcp/8080" { 50 | t.Errorf("unexpected address: %s", addrs[0]) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pkg/meshnet/transport/tcp/transport.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package tcp 18 | 19 | import ( 20 | "google.golang.org/grpc" 21 | 22 | "github.com/webmeshproj/webmesh/pkg/context" 23 | "github.com/webmeshproj/webmesh/pkg/meshnet/transport" 24 | ) 25 | 26 | // TransportOptions are options for a TCP RPC transport. 27 | type TransportOptions struct { 28 | // MaxRetries is the maximum number of retries for a request. 29 | MaxRetries int 30 | // Credentials are the gRPC DialOptions to use for the gRPC connection. 31 | Credentials []grpc.DialOption 32 | } 33 | 34 | // NewGRPCTransport creates a new gRPC transport using the native gRPC dialer. 35 | func NewGRPCTransport(opts TransportOptions) transport.RPCTransport { 36 | return &grpcTransport{opts} 37 | } 38 | 39 | type grpcTransport struct { 40 | TransportOptions 41 | } 42 | 43 | func (g *grpcTransport) Dial(ctx context.Context, _, address string) (conn transport.RPCClientConn, err error) { 44 | if g.MaxRetries == 0 { 45 | g.MaxRetries = 1 46 | } 47 | for i := 0; i < g.MaxRetries; i++ { 48 | conn, err = grpc.DialContext(ctx, address, g.Credentials...) 49 | if err == nil { 50 | return 51 | } 52 | } 53 | return 54 | } 55 | -------------------------------------------------------------------------------- /pkg/meshnet/wireguard/recorder_wasm.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package wireguard 18 | 19 | import ( 20 | "context" 21 | "time" 22 | ) 23 | 24 | // MetricsRecorder records metrics for a wireguard interface. It is a no-op interface on WASM. 25 | type MetricsRecorder struct { 26 | } 27 | 28 | // NewMetricsRecorder returns a new MetricsRecorder. 29 | func NewMetricsRecorder(ctx context.Context, wg Interface) *MetricsRecorder { 30 | return &MetricsRecorder{} 31 | } 32 | 33 | // Run starts the metrics recorder. 34 | func (m *MetricsRecorder) Run(ctx context.Context, interval time.Duration) {} 35 | -------------------------------------------------------------------------------- /pkg/plugins/builtins/basicauth/creds.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package basicauth 18 | 19 | import ( 20 | "context" 21 | 22 | "google.golang.org/grpc" 23 | ) 24 | 25 | // NewCreds returns a DialOption that sets the basic auth credentials. 26 | func NewCreds(username, password string) grpc.DialOption { 27 | return grpc.WithPerRPCCredentials(&basicAuthCreds{ 28 | username: username, 29 | password: password, 30 | }) 31 | } 32 | 33 | type basicAuthCreds struct { 34 | username, password string 35 | } 36 | 37 | func (c *basicAuthCreds) GetRequestMetadata(context.Context, ...string) (map[string]string, error) { 38 | return map[string]string{ 39 | usernameHeader: c.username, 40 | passwordHeader: c.password, 41 | }, nil 42 | } 43 | 44 | func (c *basicAuthCreds) RequireTransportSecurity() bool { 45 | return false 46 | } 47 | -------------------------------------------------------------------------------- /pkg/plugins/builtins/ldap/creds.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package ldap 18 | 19 | import ( 20 | "context" 21 | 22 | "google.golang.org/grpc" 23 | ) 24 | 25 | // NewCreds returns a DialOption that sets the LDAP credentials. 26 | func NewCreds(username, password string) grpc.DialOption { 27 | return grpc.WithPerRPCCredentials(&ldapCreds{ 28 | username: username, 29 | password: password, 30 | }) 31 | } 32 | 33 | type ldapCreds struct { 34 | username, password string 35 | } 36 | 37 | func (c *ldapCreds) GetRequestMetadata(context.Context, ...string) (map[string]string, error) { 38 | return map[string]string{ 39 | usernameHeader: c.username, 40 | passwordHeader: c.password, 41 | }, nil 42 | } 43 | 44 | func (c *ldapCreds) RequireTransportSecurity() bool { 45 | return false 46 | } 47 | -------------------------------------------------------------------------------- /pkg/plugins/clients/client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package clients contains the interface for using plugin clients. 18 | package clients 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | ) 23 | 24 | // PluginClient is an extension of the interface for a plugin client. 25 | // It provides methods for converting the client into other plugin clients. 26 | type PluginClient interface { 27 | v1.PluginClient 28 | 29 | // Storage returns a storage client. 30 | Storage() v1.StorageQuerierPluginClient 31 | // Auth returns an auth client. 32 | Auth() v1.AuthPluginClient 33 | // Events returns an events client. 34 | Events() v1.WatchPluginClient 35 | // IPAM returns an IPAM client. 36 | IPAM() v1.IPAMPluginClient 37 | } 38 | -------------------------------------------------------------------------------- /pkg/services/admin/delete_edge_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package admin 18 | 19 | import ( 20 | "testing" 21 | 22 | v1 "github.com/webmeshproj/api/go/v1" 23 | "google.golang.org/grpc/codes" 24 | ) 25 | 26 | func TestDeleteEdge(t *testing.T) { 27 | t.Parallel() 28 | 29 | server := newTestServer(t) 30 | 31 | tc := []testCase[v1.MeshEdge]{ 32 | { 33 | name: "no source", 34 | code: codes.InvalidArgument, 35 | req: &v1.MeshEdge{Source: "", Target: "bar"}, 36 | }, 37 | { 38 | name: "no target", 39 | code: codes.InvalidArgument, 40 | req: &v1.MeshEdge{Source: "foo", Target: ""}, 41 | }, 42 | { 43 | name: "valid request", 44 | code: codes.OK, 45 | req: &v1.MeshEdge{Source: "foo", Target: "bar"}, 46 | }, 47 | } 48 | 49 | runTestCases(t, tc, server.DeleteEdge) 50 | } 51 | -------------------------------------------------------------------------------- /pkg/services/admin/delete_group.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package admin provides the admin gRPC server. 18 | package admin 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | "google.golang.org/grpc/codes" 23 | "google.golang.org/grpc/status" 24 | "google.golang.org/protobuf/types/known/emptypb" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/context" 27 | "github.com/webmeshproj/webmesh/pkg/services/rbac" 28 | "github.com/webmeshproj/webmesh/pkg/storage" 29 | ) 30 | 31 | var deleteGroupAction = rbac.Actions{ 32 | { 33 | Resource: v1.RuleResource_RESOURCE_GROUPS, 34 | Verb: v1.RuleVerb_VERB_DELETE, 35 | }, 36 | } 37 | 38 | func (s *Server) DeleteGroup(ctx context.Context, group *v1.Group) (*emptypb.Empty, error) { 39 | if !s.storage.Consensus().IsLeader() { 40 | return nil, status.Error(codes.FailedPrecondition, "not the leader") 41 | } 42 | if group.GetName() == "" { 43 | return nil, status.Error(codes.InvalidArgument, "group name is required") 44 | } 45 | if ok, err := s.rbacEval.Evaluate(ctx, deleteGroupAction.For(group.GetName())); !ok { 46 | if err != nil { 47 | context.LoggerFrom(ctx).Error("failed to evaluate delete group action", "error", err) 48 | } 49 | return nil, status.Error(codes.PermissionDenied, "caller does not have permission to delete groups") 50 | } 51 | if storage.IsSystemGroup(group.GetName()) { 52 | return nil, status.Error(codes.InvalidArgument, "cannot delete system groups") 53 | } 54 | err := s.db.RBAC().DeleteGroup(ctx, group.GetName()) 55 | if err != nil { 56 | return nil, status.Error(codes.Internal, err.Error()) 57 | } 58 | return &emptypb.Empty{}, nil 59 | } 60 | -------------------------------------------------------------------------------- /pkg/services/admin/delete_group_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package admin 18 | 19 | import ( 20 | "testing" 21 | 22 | v1 "github.com/webmeshproj/api/go/v1" 23 | "google.golang.org/grpc/codes" 24 | 25 | "github.com/webmeshproj/webmesh/pkg/storage" 26 | ) 27 | 28 | func TestDeleteGroup(t *testing.T) { 29 | t.Parallel() 30 | 31 | server := newTestServer(t) 32 | 33 | tc := []testCase[v1.Group]{ 34 | { 35 | name: "no group name", 36 | code: codes.InvalidArgument, 37 | req: &v1.Group{}, 38 | }, 39 | { 40 | name: "system group", 41 | code: codes.InvalidArgument, 42 | req: &v1.Group{Name: string(storage.VotersGroup)}, 43 | }, 44 | { 45 | name: "any other group", 46 | code: codes.OK, 47 | req: &v1.Group{Name: "foo"}, 48 | }, 49 | } 50 | 51 | runTestCases(t, tc, server.DeleteGroup) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/services/admin/delete_network_acl.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package admin provides the admin gRPC server. 18 | package admin 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | "google.golang.org/grpc/codes" 23 | "google.golang.org/grpc/status" 24 | "google.golang.org/protobuf/types/known/emptypb" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/context" 27 | "github.com/webmeshproj/webmesh/pkg/services/rbac" 28 | ) 29 | 30 | var deleteNetworkACLAction = rbac.Actions{ 31 | { 32 | Resource: v1.RuleResource_RESOURCE_NETWORK_ACLS, 33 | Verb: v1.RuleVerb_VERB_DELETE, 34 | }, 35 | } 36 | 37 | func (s *Server) DeleteNetworkACL(ctx context.Context, acl *v1.NetworkACL) (*emptypb.Empty, error) { 38 | if !s.storage.Consensus().IsLeader() { 39 | return nil, status.Error(codes.FailedPrecondition, "not the leader") 40 | } 41 | if acl.GetName() == "" { 42 | return nil, status.Error(codes.InvalidArgument, "acl name is required") 43 | } 44 | if ok, err := s.rbacEval.Evaluate(ctx, deleteNetworkACLAction.For(acl.GetName())); !ok { 45 | if err != nil { 46 | context.LoggerFrom(ctx).Error("failed to evaluate delete network acl action", "error", err) 47 | } 48 | return nil, status.Error(codes.PermissionDenied, "caller does not have permission to delete network acls") 49 | } 50 | err := s.db.Networking().DeleteNetworkACL(ctx, acl.GetName()) 51 | if err != nil { 52 | return nil, status.Error(codes.Internal, err.Error()) 53 | } 54 | return &emptypb.Empty{}, nil 55 | } 56 | -------------------------------------------------------------------------------- /pkg/services/admin/delete_network_acl_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package admin 18 | 19 | import ( 20 | "testing" 21 | 22 | v1 "github.com/webmeshproj/api/go/v1" 23 | "google.golang.org/grpc/codes" 24 | ) 25 | 26 | func TestDeleteNetworkACL(t *testing.T) { 27 | t.Parallel() 28 | 29 | server := newTestServer(t) 30 | 31 | tc := []testCase[v1.NetworkACL]{ 32 | { 33 | name: "no acl name", 34 | code: codes.InvalidArgument, 35 | req: &v1.NetworkACL{}, 36 | }, 37 | { 38 | name: "any other acl", 39 | code: codes.OK, 40 | req: &v1.NetworkACL{Name: "foo"}, 41 | }, 42 | } 43 | 44 | runTestCases(t, tc, server.DeleteNetworkACL) 45 | } 46 | -------------------------------------------------------------------------------- /pkg/services/admin/delete_role.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package admin provides the admin gRPC server. 18 | package admin 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | "google.golang.org/grpc/codes" 23 | "google.golang.org/grpc/status" 24 | "google.golang.org/protobuf/types/known/emptypb" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/context" 27 | "github.com/webmeshproj/webmesh/pkg/services/rbac" 28 | "github.com/webmeshproj/webmesh/pkg/storage" 29 | ) 30 | 31 | var deleteRoleAction = rbac.Actions{ 32 | { 33 | Resource: v1.RuleResource_RESOURCE_ROLES, 34 | Verb: v1.RuleVerb_VERB_DELETE, 35 | }, 36 | } 37 | 38 | func (s *Server) DeleteRole(ctx context.Context, role *v1.Role) (*emptypb.Empty, error) { 39 | if !s.storage.Consensus().IsLeader() { 40 | return nil, status.Error(codes.FailedPrecondition, "not the leader") 41 | } 42 | if role.GetName() == "" { 43 | return nil, status.Error(codes.InvalidArgument, "name is required") 44 | } 45 | if ok, err := s.rbacEval.Evaluate(ctx, deleteRoleAction.For(role.GetName())); !ok { 46 | if err != nil { 47 | context.LoggerFrom(ctx).Error("failed to evaluate delete role action", "error", err) 48 | } 49 | return nil, status.Error(codes.PermissionDenied, "caller does not have permission to delete roles") 50 | } 51 | if storage.IsSystemRole(role.GetName()) { 52 | return nil, status.Error(codes.InvalidArgument, "cannot delete system roles") 53 | } 54 | err := s.db.RBAC().DeleteRole(ctx, role.GetName()) 55 | if err != nil { 56 | return nil, status.Error(codes.Internal, err.Error()) 57 | } 58 | return &emptypb.Empty{}, nil 59 | } 60 | -------------------------------------------------------------------------------- /pkg/services/admin/delete_role_binding.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package admin provides the admin gRPC server. 18 | package admin 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | "google.golang.org/grpc/codes" 23 | "google.golang.org/grpc/status" 24 | "google.golang.org/protobuf/types/known/emptypb" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/context" 27 | "github.com/webmeshproj/webmesh/pkg/services/rbac" 28 | "github.com/webmeshproj/webmesh/pkg/storage" 29 | ) 30 | 31 | var deleteRoleBindingAction = rbac.Actions{ 32 | { 33 | Resource: v1.RuleResource_RESOURCE_ROLE_BINDINGS, 34 | Verb: v1.RuleVerb_VERB_DELETE, 35 | }, 36 | } 37 | 38 | func (s *Server) DeleteRoleBinding(ctx context.Context, rb *v1.RoleBinding) (*emptypb.Empty, error) { 39 | if !s.storage.Consensus().IsLeader() { 40 | return nil, status.Error(codes.FailedPrecondition, "not the leader") 41 | } 42 | if rb.GetName() == "" { 43 | return nil, status.Error(codes.InvalidArgument, "name is required") 44 | } 45 | if ok, err := s.rbacEval.Evaluate(ctx, deleteRoleBindingAction.For(rb.GetName())); !ok { 46 | if err != nil { 47 | context.LoggerFrom(ctx).Error("failed to evaluate delete rolebinding action", "error", err) 48 | } 49 | return nil, status.Error(codes.PermissionDenied, "caller does not have permission to delete rolebindings") 50 | } 51 | if storage.IsSystemRoleBinding(rb.GetName()) { 52 | return nil, status.Error(codes.InvalidArgument, "cannot delete system rolebindings") 53 | } 54 | err := s.db.RBAC().DeleteRoleBinding(ctx, rb.GetName()) 55 | if err != nil { 56 | return nil, status.Error(codes.Internal, err.Error()) 57 | } 58 | return &emptypb.Empty{}, nil 59 | } 60 | -------------------------------------------------------------------------------- /pkg/services/admin/delete_role_binding_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package admin 18 | 19 | import ( 20 | "testing" 21 | 22 | v1 "github.com/webmeshproj/api/go/v1" 23 | "google.golang.org/grpc/codes" 24 | 25 | "github.com/webmeshproj/webmesh/pkg/storage" 26 | ) 27 | 28 | func TestDeleteRoleBinding(t *testing.T) { 29 | t.Parallel() 30 | 31 | server := newTestServer(t) 32 | 33 | tc := []testCase[v1.RoleBinding]{ 34 | { 35 | name: "no rolebinding name", 36 | code: codes.InvalidArgument, 37 | req: &v1.RoleBinding{}, 38 | }, 39 | { 40 | name: "system rolebinding", 41 | code: codes.InvalidArgument, 42 | req: &v1.RoleBinding{Name: string(storage.MeshAdminRoleBinding)}, 43 | }, 44 | { 45 | name: "any other rolebinding", 46 | code: codes.OK, 47 | req: &v1.RoleBinding{Name: "foo"}, 48 | }, 49 | } 50 | 51 | runTestCases(t, tc, server.DeleteRoleBinding) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/services/admin/delete_role_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package admin 18 | 19 | import ( 20 | "testing" 21 | 22 | v1 "github.com/webmeshproj/api/go/v1" 23 | "google.golang.org/grpc/codes" 24 | 25 | "github.com/webmeshproj/webmesh/pkg/storage" 26 | ) 27 | 28 | func TestDeleteRole(t *testing.T) { 29 | t.Parallel() 30 | 31 | server := newTestServer(t) 32 | 33 | tc := []testCase[v1.Role]{ 34 | { 35 | name: "no role name", 36 | code: codes.InvalidArgument, 37 | req: &v1.Role{}, 38 | }, 39 | { 40 | name: "system role", 41 | code: codes.InvalidArgument, 42 | req: &v1.Role{Name: string(storage.MeshAdminRole)}, 43 | }, 44 | { 45 | name: "any other role", 46 | code: codes.OK, 47 | req: &v1.Role{Name: "foo"}, 48 | }, 49 | } 50 | 51 | runTestCases(t, tc, server.DeleteRole) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/services/admin/delete_route.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package admin provides the admin gRPC server. 18 | package admin 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | "google.golang.org/grpc/codes" 23 | "google.golang.org/grpc/status" 24 | "google.golang.org/protobuf/types/known/emptypb" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/context" 27 | "github.com/webmeshproj/webmesh/pkg/services/rbac" 28 | ) 29 | 30 | var deleteRouteAction = rbac.Actions{ 31 | { 32 | Resource: v1.RuleResource_RESOURCE_ROUTES, 33 | Verb: v1.RuleVerb_VERB_DELETE, 34 | }, 35 | } 36 | 37 | func (s *Server) DeleteRoute(ctx context.Context, route *v1.Route) (*emptypb.Empty, error) { 38 | if !s.storage.Consensus().IsLeader() { 39 | return nil, status.Error(codes.FailedPrecondition, "not the leader") 40 | } 41 | if route.GetName() == "" { 42 | return nil, status.Error(codes.InvalidArgument, "route name is required") 43 | } 44 | if ok, err := s.rbacEval.Evaluate(ctx, deleteRouteAction.For(route.GetName())); !ok { 45 | if err != nil { 46 | context.LoggerFrom(ctx).Error("failed to evaluate delete route action", "error", err) 47 | } 48 | return nil, status.Error(codes.PermissionDenied, "caller does not have permission to delete network routes") 49 | } 50 | err := s.db.Networking().DeleteRoute(ctx, route.GetName()) 51 | if err != nil { 52 | return nil, status.Error(codes.Internal, err.Error()) 53 | } 54 | return &emptypb.Empty{}, nil 55 | } 56 | -------------------------------------------------------------------------------- /pkg/services/admin/delete_route_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package admin 18 | 19 | import ( 20 | "testing" 21 | 22 | v1 "github.com/webmeshproj/api/go/v1" 23 | "google.golang.org/grpc/codes" 24 | ) 25 | 26 | func TestDeleteRoute(t *testing.T) { 27 | t.Parallel() 28 | 29 | server := newTestServer(t) 30 | 31 | tc := []testCase[v1.Route]{ 32 | { 33 | name: "no route name", 34 | code: codes.InvalidArgument, 35 | req: &v1.Route{}, 36 | }, 37 | { 38 | name: "any route name", 39 | code: codes.OK, 40 | req: &v1.Route{Name: "foo"}, 41 | }, 42 | } 43 | 44 | runTestCases(t, tc, server.DeleteRoute) 45 | } 46 | -------------------------------------------------------------------------------- /pkg/services/admin/get_edge.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package admin provides the admin gRPC server. 18 | package admin 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | "google.golang.org/grpc/codes" 23 | "google.golang.org/grpc/status" 24 | 25 | "github.com/webmeshproj/webmesh/pkg/context" 26 | "github.com/webmeshproj/webmesh/pkg/storage/errors" 27 | "github.com/webmeshproj/webmesh/pkg/storage/types" 28 | ) 29 | 30 | func (s *Server) GetEdge(ctx context.Context, edge *v1.MeshEdge) (*v1.MeshEdge, error) { 31 | if edge.GetSource() == "" { 32 | return nil, status.Error(codes.InvalidArgument, "edge source is required") 33 | } 34 | if edge.GetTarget() == "" { 35 | return nil, status.Error(codes.InvalidArgument, "edge target is required") 36 | } 37 | graphEdge, err := s.db.Peers().Graph().Edge(types.NodeID(edge.GetSource()), types.NodeID(edge.GetTarget())) 38 | if err != nil { 39 | if errors.IsEdgeNotFound(err) { 40 | return nil, status.Errorf(codes.NotFound, "edge %q to %q not found", edge.GetSource(), edge.GetTarget()) 41 | } 42 | return nil, status.Error(codes.Internal, err.Error()) 43 | } 44 | return &v1.MeshEdge{ 45 | Source: graphEdge.Source.GetId(), 46 | Target: graphEdge.Target.GetId(), 47 | Weight: int32(graphEdge.Properties.Weight), 48 | Attributes: graphEdge.Properties.Attributes, 49 | }, nil 50 | } 51 | -------------------------------------------------------------------------------- /pkg/services/admin/get_group.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package admin provides the admin gRPC server. 18 | package admin 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | "google.golang.org/grpc/codes" 23 | "google.golang.org/grpc/status" 24 | 25 | "github.com/webmeshproj/webmesh/pkg/context" 26 | "github.com/webmeshproj/webmesh/pkg/storage/errors" 27 | ) 28 | 29 | func (s *Server) GetGroup(ctx context.Context, group *v1.Group) (*v1.Group, error) { 30 | if group.GetName() == "" { 31 | return nil, status.Error(codes.InvalidArgument, "group name is required") 32 | } 33 | out, err := s.db.RBAC().GetGroup(ctx, group.GetName()) 34 | if err != nil { 35 | if errors.IsGroupNotFound(err) { 36 | return nil, status.Errorf(codes.NotFound, "group %q not found", group.GetName()) 37 | } 38 | return nil, status.Error(codes.Internal, err.Error()) 39 | } 40 | return out.Proto(), nil 41 | } 42 | -------------------------------------------------------------------------------- /pkg/services/admin/get_group_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package admin 18 | 19 | import ( 20 | "testing" 21 | 22 | v1 "github.com/webmeshproj/api/go/v1" 23 | "google.golang.org/grpc/codes" 24 | 25 | "github.com/webmeshproj/webmesh/pkg/storage" 26 | ) 27 | 28 | func TestGetGroup(t *testing.T) { 29 | t.Parallel() 30 | 31 | server := newTestServer(t) 32 | 33 | tc := []testCase[v1.Group]{ 34 | { 35 | name: "no group name", 36 | code: codes.InvalidArgument, 37 | req: &v1.Group{}, 38 | }, 39 | { 40 | name: "non-existent group", 41 | req: &v1.Group{Name: "non-existent-group"}, 42 | code: codes.NotFound, 43 | }, 44 | { 45 | name: "existing system group", 46 | req: &v1.Group{Name: string(storage.VotersGroup)}, 47 | code: codes.OK, 48 | }, 49 | } 50 | 51 | runTestCases(t, tc, server.GetGroup) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/services/admin/get_network_acl.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package admin provides the admin gRPC server. 18 | package admin 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | "google.golang.org/grpc/codes" 23 | "google.golang.org/grpc/status" 24 | 25 | "github.com/webmeshproj/webmesh/pkg/context" 26 | "github.com/webmeshproj/webmesh/pkg/storage/errors" 27 | ) 28 | 29 | func (s *Server) GetNetworkACL(ctx context.Context, acl *v1.NetworkACL) (*v1.NetworkACL, error) { 30 | if acl.GetName() == "" { 31 | return nil, status.Error(codes.InvalidArgument, "acl name is required") 32 | } 33 | out, err := s.db.Networking().GetNetworkACL(ctx, acl.GetName()) 34 | if err != nil { 35 | if errors.IsACLNotFound(err) { 36 | return nil, status.Errorf(codes.NotFound, "network acl %q not found", acl.GetName()) 37 | } 38 | return nil, status.Error(codes.Internal, err.Error()) 39 | } 40 | return out.Proto(), nil 41 | } 42 | -------------------------------------------------------------------------------- /pkg/services/admin/get_network_acl_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package admin 18 | 19 | import ( 20 | "testing" 21 | 22 | v1 "github.com/webmeshproj/api/go/v1" 23 | "google.golang.org/grpc/codes" 24 | 25 | "github.com/webmeshproj/webmesh/pkg/storage" 26 | ) 27 | 28 | func TestGetNetworkACL(t *testing.T) { 29 | t.Parallel() 30 | 31 | server := newTestServer(t) 32 | 33 | tc := []testCase[v1.NetworkACL]{ 34 | { 35 | name: "no acl name", 36 | code: codes.InvalidArgument, 37 | req: &v1.NetworkACL{}, 38 | }, 39 | { 40 | name: "non-existent networkacl", 41 | req: &v1.NetworkACL{Name: "non-existent-networkacl"}, 42 | code: codes.NotFound, 43 | }, 44 | { 45 | name: "existing system networkacl", 46 | req: &v1.NetworkACL{Name: string(storage.BootstrapNodesNetworkACLName)}, 47 | code: codes.OK, 48 | }, 49 | } 50 | 51 | runTestCases(t, tc, server.GetNetworkACL) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/services/admin/get_role.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package admin provides the admin gRPC server. 18 | package admin 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | "google.golang.org/grpc/codes" 23 | "google.golang.org/grpc/status" 24 | 25 | "github.com/webmeshproj/webmesh/pkg/context" 26 | "github.com/webmeshproj/webmesh/pkg/storage/errors" 27 | ) 28 | 29 | func (s *Server) GetRole(ctx context.Context, role *v1.Role) (*v1.Role, error) { 30 | if role.GetName() == "" { 31 | return nil, status.Error(codes.InvalidArgument, "name is required") 32 | } 33 | out, err := s.db.RBAC().GetRole(ctx, role.GetName()) 34 | if err != nil { 35 | if errors.IsRoleNotFound(err) { 36 | return nil, status.Errorf(codes.NotFound, "role %q not found", role.GetName()) 37 | } 38 | return nil, status.Error(codes.Internal, err.Error()) 39 | } 40 | return out.Proto(), nil 41 | } 42 | -------------------------------------------------------------------------------- /pkg/services/admin/get_role_binding.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package admin provides the admin gRPC server. 18 | package admin 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | "google.golang.org/grpc/codes" 23 | "google.golang.org/grpc/status" 24 | 25 | "github.com/webmeshproj/webmesh/pkg/context" 26 | "github.com/webmeshproj/webmesh/pkg/storage/errors" 27 | ) 28 | 29 | func (s *Server) GetRoleBinding(ctx context.Context, rb *v1.RoleBinding) (*v1.RoleBinding, error) { 30 | if rb.GetName() == "" { 31 | return nil, status.Error(codes.InvalidArgument, "name is required") 32 | } 33 | out, err := s.db.RBAC().GetRoleBinding(ctx, rb.GetName()) 34 | if err != nil { 35 | if errors.IsRoleBindingNotFound(err) { 36 | return nil, status.Errorf(codes.NotFound, "rolebinding %q not found", rb.GetName()) 37 | } 38 | return nil, status.Error(codes.Internal, err.Error()) 39 | } 40 | return out.Proto(), nil 41 | } 42 | -------------------------------------------------------------------------------- /pkg/services/admin/get_role_binding_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package admin 18 | 19 | import ( 20 | "testing" 21 | 22 | v1 "github.com/webmeshproj/api/go/v1" 23 | "google.golang.org/grpc/codes" 24 | 25 | "github.com/webmeshproj/webmesh/pkg/storage" 26 | ) 27 | 28 | func TestGetRoleBinding(t *testing.T) { 29 | t.Parallel() 30 | 31 | server := newTestServer(t) 32 | 33 | tc := []testCase[v1.RoleBinding]{ 34 | { 35 | name: "no rolebinding name", 36 | code: codes.InvalidArgument, 37 | req: &v1.RoleBinding{}, 38 | }, 39 | { 40 | name: "non-existent rolebinding", 41 | req: &v1.RoleBinding{Name: "non-existent-rolebinding"}, 42 | code: codes.NotFound, 43 | }, 44 | { 45 | name: "existing system rolebinding", 46 | req: &v1.RoleBinding{Name: string(storage.MeshAdminRoleBinding)}, 47 | code: codes.OK, 48 | }, 49 | } 50 | 51 | runTestCases(t, tc, server.GetRoleBinding) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/services/admin/get_role_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package admin 18 | 19 | import ( 20 | "testing" 21 | 22 | v1 "github.com/webmeshproj/api/go/v1" 23 | "google.golang.org/grpc/codes" 24 | 25 | "github.com/webmeshproj/webmesh/pkg/storage" 26 | ) 27 | 28 | func TestGetRole(t *testing.T) { 29 | t.Parallel() 30 | 31 | server := newTestServer(t) 32 | 33 | tc := []testCase[v1.Role]{ 34 | { 35 | name: "no role name", 36 | code: codes.InvalidArgument, 37 | req: &v1.Role{}, 38 | }, 39 | { 40 | name: "non-existent role", 41 | req: &v1.Role{Name: "non-existent-role"}, 42 | code: codes.NotFound, 43 | }, 44 | { 45 | name: "existing system role", 46 | req: &v1.Role{Name: string(storage.MeshAdminRole)}, 47 | code: codes.OK, 48 | }, 49 | } 50 | 51 | runTestCases(t, tc, server.GetRole) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/services/admin/get_route.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package admin provides the admin gRPC server. 18 | package admin 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | "google.golang.org/grpc/codes" 23 | "google.golang.org/grpc/status" 24 | 25 | "github.com/webmeshproj/webmesh/pkg/context" 26 | "github.com/webmeshproj/webmesh/pkg/storage/errors" 27 | ) 28 | 29 | func (s *Server) GetRoute(ctx context.Context, route *v1.Route) (*v1.Route, error) { 30 | if route.GetName() == "" { 31 | return nil, status.Error(codes.InvalidArgument, "route name is required") 32 | } 33 | rt, err := s.db.Networking().GetRoute(ctx, route.GetName()) 34 | if err != nil { 35 | if errors.IsRouteNotFound(err) { 36 | return nil, status.Errorf(codes.NotFound, "network route %q not found", route.GetName()) 37 | } 38 | return nil, status.Error(codes.Internal, err.Error()) 39 | } 40 | return rt.Proto(), nil 41 | } 42 | -------------------------------------------------------------------------------- /pkg/services/admin/get_route_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package admin 18 | 19 | import ( 20 | "testing" 21 | 22 | v1 "github.com/webmeshproj/api/go/v1" 23 | "google.golang.org/grpc/codes" 24 | 25 | "github.com/webmeshproj/webmesh/pkg/context" 26 | ) 27 | 28 | func TestGetRoute(t *testing.T) { 29 | t.Parallel() 30 | 31 | ctx := context.Background() 32 | server := newTestServer(t) 33 | 34 | // Pre populate the store with a route 35 | _, err := server.PutRoute(ctx, &v1.Route{ 36 | Name: "foo", 37 | Node: "foo", 38 | DestinationCIDRs: []string{"0.0.0.0/0"}, 39 | }) 40 | if err != nil { 41 | t.Fatalf("failed to put route: %v", err) 42 | } 43 | 44 | tc := []testCase[v1.Route]{ 45 | { 46 | name: "no route name", 47 | code: codes.InvalidArgument, 48 | req: &v1.Route{}, 49 | }, 50 | { 51 | name: "non-existent route", 52 | req: &v1.Route{Name: "non-existent"}, 53 | code: codes.NotFound, 54 | }, 55 | { 56 | name: "existing route", 57 | req: &v1.Route{Name: "foo"}, 58 | }, 59 | } 60 | 61 | runTestCases(t, tc, server.GetRoute) 62 | } 63 | -------------------------------------------------------------------------------- /pkg/services/admin/list_edges.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package admin provides the admin gRPC server. 18 | package admin 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | "google.golang.org/grpc/codes" 23 | "google.golang.org/grpc/status" 24 | "google.golang.org/protobuf/types/known/emptypb" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/context" 27 | ) 28 | 29 | func (s *Server) ListEdges(ctx context.Context, _ *emptypb.Empty) (*v1.MeshEdges, error) { 30 | edges, err := s.db.Peers().Graph().Edges() 31 | if err != nil { 32 | return nil, status.Error(codes.Internal, err.Error()) 33 | } 34 | out := make([]*v1.MeshEdge, len(edges)) 35 | for i, edge := range edges { 36 | out[i] = &v1.MeshEdge{ 37 | Source: edge.Source.String(), 38 | Target: edge.Target.String(), 39 | Weight: int32(edge.Properties.Weight), 40 | Attributes: edge.Properties.Attributes, 41 | } 42 | } 43 | return &v1.MeshEdges{Items: out}, nil 44 | } 45 | -------------------------------------------------------------------------------- /pkg/services/admin/list_edges_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package admin 18 | 19 | import ( 20 | "testing" 21 | 22 | v1 "github.com/webmeshproj/api/go/v1" 23 | 24 | "github.com/webmeshproj/webmesh/pkg/context" 25 | "github.com/webmeshproj/webmesh/pkg/storage/types" 26 | ) 27 | 28 | func TestListEdges(t *testing.T) { 29 | t.Parallel() 30 | 31 | ctx := context.Background() 32 | server := newTestServer(t) 33 | 34 | // No empty condition 35 | 36 | // Place a dummy peer 37 | p := server.storage.MeshDB().Peers() 38 | err := p.Put(ctx, types.MeshNode{MeshNode: &v1.MeshNode{ 39 | Id: "foo", 40 | PublicKey: newEncodedPubKey(t), 41 | }}) 42 | if err != nil { 43 | t.Errorf("Put() error = %v", err) 44 | return 45 | } 46 | err = p.Put(ctx, types.MeshNode{MeshNode: &v1.MeshNode{ 47 | Id: "bar", 48 | PublicKey: newEncodedPubKey(t), 49 | }}) 50 | if err != nil { 51 | t.Errorf("Put() error = %v", err) 52 | return 53 | } 54 | // Place an edge from us to the dummy peer 55 | _, err = server.PutEdge(ctx, &v1.MeshEdge{ 56 | Source: "bar", 57 | Target: "foo", 58 | }) 59 | if err != nil { 60 | t.Errorf("PutEdge() error = %v", err) 61 | return 62 | } 63 | 64 | // Verify edge is present 65 | edge, err := server.GetEdge(ctx, &v1.MeshEdge{ 66 | Source: "bar", 67 | Target: "foo", 68 | }) 69 | if err != nil { 70 | t.Errorf("GetEdge() error = %v", err) 71 | return 72 | } 73 | if edge.Source != "bar" { 74 | t.Errorf("edge.Source = %v, want %v", edge.Source, "bar") 75 | } 76 | if edge.Target != "foo" { 77 | t.Errorf("edge.Target = %v, want %v", edge.Target, "foo") 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /pkg/services/admin/list_groups.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package admin provides the admin gRPC server. 18 | package admin 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | "google.golang.org/grpc/codes" 23 | "google.golang.org/grpc/status" 24 | "google.golang.org/protobuf/types/known/emptypb" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/context" 27 | ) 28 | 29 | func (s *Server) ListGroups(ctx context.Context, _ *emptypb.Empty) (*v1.Groups, error) { 30 | groups, err := s.db.RBAC().ListGroups(ctx) 31 | if err != nil { 32 | return nil, status.Error(codes.Internal, err.Error()) 33 | } 34 | out := make([]*v1.Group, len(groups)) 35 | for i, g := range groups { 36 | out[i] = g.Proto() 37 | } 38 | return &v1.Groups{Items: out}, nil 39 | } 40 | -------------------------------------------------------------------------------- /pkg/services/admin/list_groups_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package admin 18 | 19 | import ( 20 | "testing" 21 | 22 | v1 "github.com/webmeshproj/api/go/v1" 23 | 24 | "github.com/webmeshproj/webmesh/pkg/context" 25 | ) 26 | 27 | func TestListGroups(t *testing.T) { 28 | t.Parallel() 29 | 30 | ctx := context.Background() 31 | server := newTestServer(t) 32 | 33 | // No empty condition 34 | 35 | // Place a group 36 | _, err := server.PutGroup(ctx, &v1.Group{ 37 | Name: "foo", 38 | Subjects: []*v1.Subject{ 39 | { 40 | Name: "bar", 41 | Type: v1.SubjectType_SUBJECT_USER, 42 | }, 43 | }, 44 | }) 45 | if err != nil { 46 | t.Errorf("PutGroup() error = %v", err) 47 | return 48 | } 49 | 50 | // Verify group is present 51 | groups, err := server.ListGroups(ctx, nil) 52 | if err != nil { 53 | t.Errorf("ListGroups() error = %v", err) 54 | return 55 | } 56 | 57 | var group *v1.Group 58 | for _, g := range groups.GetItems() { 59 | if g.Name == "foo" { 60 | group = g 61 | break 62 | } 63 | } 64 | if group == nil { 65 | t.Errorf("ListGroups() did not return expected group") 66 | } 67 | if len(group.GetSubjects()) != 1 { 68 | t.Errorf("ListGroups() did not return expected group subjects") 69 | return 70 | } 71 | sub := group.GetSubjects()[0] 72 | if sub.Name != "bar" { 73 | t.Errorf("ListGroups() did not return expected group subject name") 74 | } 75 | if sub.Type != v1.SubjectType_SUBJECT_USER { 76 | t.Errorf("ListGroups() did not return expected group subject type") 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /pkg/services/admin/list_network_acls.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package admin provides the admin gRPC server. 18 | package admin 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | "google.golang.org/grpc/codes" 23 | "google.golang.org/grpc/status" 24 | "google.golang.org/protobuf/types/known/emptypb" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/context" 27 | ) 28 | 29 | func (s *Server) ListNetworkACLs(ctx context.Context, _ *emptypb.Empty) (*v1.NetworkACLs, error) { 30 | acls, err := s.db.Networking().ListNetworkACLs(ctx) 31 | if err != nil { 32 | return nil, status.Error(codes.Internal, err.Error()) 33 | } 34 | return &v1.NetworkACLs{Items: acls.Proto()}, nil 35 | } 36 | -------------------------------------------------------------------------------- /pkg/services/admin/list_network_acls_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package admin 18 | 19 | import ( 20 | "testing" 21 | 22 | v1 "github.com/webmeshproj/api/go/v1" 23 | 24 | "github.com/webmeshproj/webmesh/pkg/context" 25 | ) 26 | 27 | func TestListNetworkACLs(t *testing.T) { 28 | t.Parallel() 29 | 30 | ctx := context.Background() 31 | server := newTestServer(t) 32 | 33 | // No empty condition due to system acls created during bootstrap 34 | 35 | // Place a network acl 36 | _, err := server.PutNetworkACL(ctx, &v1.NetworkACL{ 37 | Name: "test-acl", 38 | SourceNodes: []string{"foo"}, 39 | DestinationCIDRs: []string{"0.0.0.0/0"}, 40 | }) 41 | if err != nil { 42 | t.Errorf("PutNetworkACL() error = %v", err) 43 | return 44 | } 45 | var acl *v1.NetworkACL 46 | acls, err := server.ListNetworkACLs(ctx, nil) 47 | if err != nil { 48 | t.Errorf("ListNetworkACLs() error = %v", err) 49 | return 50 | } 51 | for _, a := range acls.GetItems() { 52 | if a.GetName() == "test-acl" { 53 | acl = a 54 | break 55 | } 56 | } 57 | if acl == nil { 58 | t.Errorf("ListNetworkACLs() did not return the expected ACL") 59 | } 60 | if len(acl.GetSourceNodes()) != 1 { 61 | t.Errorf("ListNetworkACLs() returned an ACL with unexpected source nodes") 62 | } else if acl.GetSourceNodes()[0] != "foo" { 63 | t.Errorf("ListNetworkACLs() returned an ACL with unexpected source nodes") 64 | } 65 | if len(acl.GetDestinationCIDRs()) != 1 { 66 | t.Errorf("ListNetworkACLs() returned an ACL with unexpected destination cidrs") 67 | } else if acl.GetDestinationCIDRs()[0] != "0.0.0.0/0" { 68 | t.Errorf("ListNetworkACLs() returned an ACL with unexpected destination cidrs") 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /pkg/services/admin/list_role_bindings.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package admin provides the admin gRPC server. 18 | package admin 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | "google.golang.org/grpc/codes" 23 | "google.golang.org/grpc/status" 24 | "google.golang.org/protobuf/types/known/emptypb" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/context" 27 | ) 28 | 29 | func (s *Server) ListRoleBindings(ctx context.Context, _ *emptypb.Empty) (*v1.RoleBindings, error) { 30 | rbs, err := s.db.RBAC().ListRoleBindings(ctx) 31 | if err != nil { 32 | return nil, status.Error(codes.Internal, err.Error()) 33 | } 34 | out := make([]*v1.RoleBinding, len(rbs)) 35 | for i, rb := range rbs { 36 | out[i] = rb.Proto() 37 | } 38 | return &v1.RoleBindings{Items: out}, nil 39 | } 40 | -------------------------------------------------------------------------------- /pkg/services/admin/list_roles.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package admin provides the admin gRPC server. 18 | package admin 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | "google.golang.org/grpc/codes" 23 | "google.golang.org/grpc/status" 24 | "google.golang.org/protobuf/types/known/emptypb" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/context" 27 | ) 28 | 29 | func (s *Server) ListRoles(ctx context.Context, _ *emptypb.Empty) (*v1.Roles, error) { 30 | roles, err := s.db.RBAC().ListRoles(ctx) 31 | if err != nil { 32 | return nil, status.Error(codes.Internal, err.Error()) 33 | } 34 | out := make([]*v1.Role, len(roles)) 35 | for i, r := range roles { 36 | out[i] = r.Proto() 37 | } 38 | return &v1.Roles{Items: out}, nil 39 | } 40 | -------------------------------------------------------------------------------- /pkg/services/admin/list_routes.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package admin provides the admin gRPC server. 18 | package admin 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | "google.golang.org/grpc/codes" 23 | "google.golang.org/grpc/status" 24 | "google.golang.org/protobuf/types/known/emptypb" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/context" 27 | ) 28 | 29 | func (s *Server) ListRoutes(ctx context.Context, _ *emptypb.Empty) (*v1.Routes, error) { 30 | routes, err := s.db.Networking().ListRoutes(ctx) 31 | if err != nil { 32 | return nil, status.Error(codes.Internal, err.Error()) 33 | } 34 | return &v1.Routes{Items: routes.Proto()}, nil 35 | } 36 | -------------------------------------------------------------------------------- /pkg/services/admin/put_route.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package admin provides the admin gRPC server. 18 | package admin 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | "google.golang.org/grpc/codes" 23 | "google.golang.org/grpc/status" 24 | "google.golang.org/protobuf/types/known/emptypb" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/context" 27 | "github.com/webmeshproj/webmesh/pkg/services/rbac" 28 | "github.com/webmeshproj/webmesh/pkg/storage/types" 29 | ) 30 | 31 | var putRouteAction = rbac.Actions{ 32 | { 33 | Resource: v1.RuleResource_RESOURCE_ROUTES, 34 | Verb: v1.RuleVerb_VERB_PUT, 35 | }, 36 | } 37 | 38 | func (s *Server) PutRoute(ctx context.Context, route *v1.Route) (*emptypb.Empty, error) { 39 | if !s.storage.Consensus().IsLeader() { 40 | return nil, status.Error(codes.FailedPrecondition, "not the leader") 41 | } 42 | rt := types.Route{Route: route} 43 | err := types.ValidateRoute(rt) 44 | if err != nil { 45 | return nil, status.Error(codes.InvalidArgument, err.Error()) 46 | } 47 | if ok, err := s.rbacEval.Evaluate(ctx, putRouteAction.For(route.GetName())); !ok { 48 | if err != nil { 49 | context.LoggerFrom(ctx).Error("failed to evaluate put route action", "error", err) 50 | } 51 | return nil, status.Error(codes.PermissionDenied, "caller does not have permission to put network routes") 52 | } 53 | err = s.db.Networking().PutRoute(ctx, rt) 54 | if err != nil { 55 | return nil, status.Error(codes.Internal, err.Error()) 56 | } 57 | return &emptypb.Empty{}, nil 58 | } 59 | -------------------------------------------------------------------------------- /pkg/services/admin/server.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package admin provides the admin gRPC server. 18 | package admin 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | 23 | "github.com/webmeshproj/webmesh/pkg/services/rbac" 24 | "github.com/webmeshproj/webmesh/pkg/storage" 25 | ) 26 | 27 | // Server is the webmesh Admin service. 28 | type Server struct { 29 | v1.UnimplementedAdminServer 30 | 31 | storage storage.Provider 32 | db storage.MeshDB 33 | rbacEval rbac.Evaluator 34 | } 35 | 36 | // New creates a new admin server. 37 | func NewServer(storage storage.Provider, rbac rbac.Evaluator) *Server { 38 | return &Server{ 39 | storage: storage, 40 | db: storage.MeshDB(), 41 | rbacEval: rbac, 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /pkg/services/membership/storage_consensus.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package membership 18 | 19 | import ( 20 | "log/slog" 21 | 22 | v1 "github.com/webmeshproj/api/go/v1" 23 | "google.golang.org/grpc/codes" 24 | "google.golang.org/grpc/status" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/context" 27 | ) 28 | 29 | func (s *Server) GetCurrentConsensus(ctx context.Context, _ *v1.StorageConsensusRequest) (*v1.StorageConsensusResponse, error) { 30 | if !context.IsInNetwork(ctx, s.meshnet) { 31 | addr, _ := context.PeerAddrFrom(ctx) 32 | s.log.Warn("Received GetStorageConfiguration request from out of network", slog.String("peer", addr.String())) 33 | return nil, status.Errorf(codes.PermissionDenied, "request is not in-network") 34 | } 35 | storageStatus := s.storage.Status() 36 | s.log.Debug("Handling GetStorageConfiguration request", slog.Any("current-status", storageStatus)) 37 | resp := &v1.StorageConsensusResponse{ 38 | Servers: make([]*v1.StorageServer, 0), 39 | } 40 | for _, srv := range storageStatus.GetPeers() { 41 | resp.Servers = append(resp.Servers, &v1.StorageServer{ 42 | Id: srv.GetId(), 43 | PublicKey: srv.GetPublicKey(), 44 | Address: srv.GetAddress(), 45 | Suffrage: srv.GetClusterStatus(), 46 | }) 47 | } 48 | return resp, nil 49 | } 50 | -------------------------------------------------------------------------------- /pkg/services/meshdns/middlewares.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package meshdns 18 | 19 | import ( 20 | "log/slog" 21 | 22 | "github.com/miekg/dns" 23 | ) 24 | 25 | func (s *Server) validateRequest(next dns.HandlerFunc) dns.HandlerFunc { 26 | return func(w dns.ResponseWriter, r *dns.Msg) { 27 | if r == nil || len(r.Question) == 0 { 28 | s.log.Warn("Received empty DNS request") 29 | m := new(dns.Msg) 30 | m.SetReply(r) 31 | s.writeMsg(w, r, m, dns.RcodeFormatError) 32 | return 33 | } 34 | q := r.Question[0] 35 | s.log.Debug("handling DNS question", slog.String("name", q.Name), slog.String("question", q.String())) 36 | next(w, r) 37 | } 38 | } 39 | 40 | func (s *Server) denyZoneTransfers(next dns.HandlerFunc) dns.HandlerFunc { 41 | return func(w dns.ResponseWriter, r *dns.Msg) { 42 | for _, q := range r.Question { 43 | if q.Qtype == dns.TypeAXFR || q.Qtype == dns.TypeIXFR { 44 | s.log.Warn("Denying zone transfer request") 45 | m := new(dns.Msg) 46 | m.SetReply(r) 47 | s.writeMsg(w, r, m, dns.RcodeRefused) 48 | return 49 | } 50 | } 51 | next(w, r) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pkg/services/node/server.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package node contains the webmesh node service. 18 | package node 19 | 20 | import ( 21 | "fmt" 22 | "log/slog" 23 | "runtime" 24 | "time" 25 | 26 | v1 "github.com/webmeshproj/api/go/v1" 27 | 28 | "github.com/webmeshproj/webmesh/pkg/context" 29 | "github.com/webmeshproj/webmesh/pkg/meshnet" 30 | "github.com/webmeshproj/webmesh/pkg/meshnet/transport" 31 | "github.com/webmeshproj/webmesh/pkg/plugins" 32 | "github.com/webmeshproj/webmesh/pkg/storage" 33 | "github.com/webmeshproj/webmesh/pkg/storage/types" 34 | "github.com/webmeshproj/webmesh/pkg/version" 35 | ) 36 | 37 | // Server is the webmesh node service. 38 | type Server struct { 39 | v1.UnimplementedNodeServer 40 | Options 41 | startedAt time.Time 42 | log *slog.Logger 43 | } 44 | 45 | // Options are options for the Node service. 46 | type Options struct { 47 | NodeID types.NodeID 48 | Description string 49 | Version version.BuildInfo 50 | Storage storage.Provider 51 | Meshnet meshnet.Manager 52 | NodeDialer transport.NodeDialer 53 | Plugins plugins.Manager 54 | Features []*v1.FeaturePort 55 | } 56 | 57 | // NewServer returns a new Server. Features are used for returning what features are enabled. 58 | // It is the callers responsibility to ensure those servers are registered on the node. 59 | // Insecure is used to disable authorization. 60 | func NewServer(ctx context.Context, opts Options) *Server { 61 | opts.Description += fmt.Sprintf(" (%s)", runtime.Version()) 62 | return &Server{ 63 | Options: opts, 64 | startedAt: time.Now(), 65 | log: context.LoggerFrom(ctx).With("component", "node-server"), 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /pkg/services/server_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package services contains the gRPC server for inter-node communication. 18 | package services 19 | 20 | import ( 21 | "testing" 22 | 23 | "github.com/webmeshproj/webmesh/pkg/services/meshdns" 24 | ) 25 | 26 | func TestGetServerByType(t *testing.T) { 27 | servers := MeshServers{ 28 | &meshdns.Server{}, 29 | } 30 | srv, ok := servers.GetByType(&meshdns.Server{}) 31 | if !ok { 32 | t.Fatal("expected to find server") 33 | } 34 | if srv == nil { 35 | t.Fatal("expected server to not be nil") 36 | } 37 | if _, ok := srv.(*meshdns.Server); !ok { 38 | t.Fatal("expected server to be of type *meshdns.Server") 39 | } 40 | 41 | // Try the generic one too. 42 | dnsSrv, ok := GetByType(servers, &meshdns.Server{}) 43 | if !ok { 44 | t.Fatal("expected to find server") 45 | } 46 | if dnsSrv == nil { 47 | t.Fatal("expected server to not be nil") 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /pkg/services/storage/query.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package storage 18 | 19 | import ( 20 | "log/slog" 21 | 22 | v1 "github.com/webmeshproj/api/go/v1" 23 | "google.golang.org/grpc/codes" 24 | "google.golang.org/grpc/status" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/context" 27 | "github.com/webmeshproj/webmesh/pkg/storage/rpcsrv" 28 | ) 29 | 30 | func (s *Server) Query(ctx context.Context, req *v1.QueryRequest) (*v1.QueryResponse, error) { 31 | if !context.IsInNetwork(ctx, s.mnet) { 32 | addr, _ := context.PeerAddrFrom(ctx) 33 | s.log.Warn("Received Query request from out of network", slog.String("peer", addr.String())) 34 | return nil, status.Errorf(codes.PermissionDenied, "request is not in-network") 35 | } 36 | if !s.storage.Consensus().IsMember() { 37 | // In theory - non-storage members shouldn't even expose the Node service. 38 | return nil, status.Error(codes.Unavailable, "node not available to query") 39 | } 40 | return rpcsrv.ServeQuery(ctx, s.storage, req), nil 41 | } 42 | -------------------------------------------------------------------------------- /pkg/services/storage/server.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package storage provides the storage server. 18 | package storage 19 | 20 | import ( 21 | "log/slog" 22 | 23 | v1 "github.com/webmeshproj/api/go/v1" 24 | 25 | "github.com/webmeshproj/webmesh/pkg/context" 26 | "github.com/webmeshproj/webmesh/pkg/meshnet" 27 | "github.com/webmeshproj/webmesh/pkg/services/rbac" 28 | "github.com/webmeshproj/webmesh/pkg/storage" 29 | ) 30 | 31 | // Ensure we implement the interface. 32 | var _ v1.StorageQueryServiceServer = (*Server)(nil) 33 | 34 | // Server is the webmesh storage server. 35 | type Server struct { 36 | v1.UnimplementedStorageQueryServiceServer 37 | 38 | storage storage.Provider 39 | rbac rbac.Evaluator 40 | mnet meshnet.Manager 41 | log *slog.Logger 42 | } 43 | 44 | // NewServer returns a new storage Server. 45 | func NewServer(ctx context.Context, storage storage.Provider, rbac rbac.Evaluator, mnet meshnet.Manager) *Server { 46 | return &Server{ 47 | storage: storage, 48 | rbac: rbac, 49 | mnet: mnet, 50 | log: context.LoggerFrom(ctx).With("component", "storage-server"), 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /pkg/services/turn/stun_logger.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package turn 18 | 19 | import ( 20 | "log/slog" 21 | "net" 22 | 23 | "github.com/pion/stun" 24 | ) 25 | 26 | type stunLogger struct { 27 | net.PacketConn 28 | log *slog.Logger 29 | } 30 | 31 | func (s *stunLogger) WriteTo(p []byte, addr net.Addr) (n int, err error) { 32 | if n, err = s.PacketConn.WriteTo(p, addr); err == nil && stun.IsMessage(p) { 33 | msg := &stun.Message{Raw: p} 34 | if err = msg.Decode(); err != nil { 35 | return 36 | } 37 | s.log.Debug("Handling outbund STUN packet", slog.Any("msg", msg)) 38 | } 39 | return 40 | } 41 | 42 | func (s *stunLogger) ReadFrom(p []byte) (n int, addr net.Addr, err error) { 43 | if n, addr, err = s.PacketConn.ReadFrom(p); err == nil && stun.IsMessage(p) { 44 | msg := &stun.Message{Raw: p} 45 | if err = msg.Decode(); err != nil { 46 | return 47 | } 48 | s.log.Debug("Handling inbound STUN packet", slog.Any("msg", msg)) 49 | } 50 | return 51 | } 52 | -------------------------------------------------------------------------------- /pkg/services/webrtc/server.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package webrtc contains the webmesh WebRTC service. 18 | package webrtc 19 | 20 | import ( 21 | v1 "github.com/webmeshproj/api/go/v1" 22 | 23 | "github.com/webmeshproj/webmesh/pkg/meshnet/transport" 24 | "github.com/webmeshproj/webmesh/pkg/meshnet/wireguard" 25 | "github.com/webmeshproj/webmesh/pkg/services/rbac" 26 | "github.com/webmeshproj/webmesh/pkg/storage/types" 27 | ) 28 | 29 | // DefaultSTUNServers are the default STUN servers to use. 30 | var DefaultSTUNServers = []string{ 31 | "stun:stun.l.google.com:19302", 32 | } 33 | 34 | // Server is the webmesh WebRTC service. 35 | type Server struct { 36 | v1.UnimplementedWebRTCServer 37 | 38 | wg wireguard.Interface 39 | rbacEval rbac.Evaluator 40 | opts Options 41 | } 42 | 43 | // Options are options for the WebRTC service. 44 | type Options struct { 45 | ID types.NodeID 46 | Wireguard wireguard.Interface 47 | NodeDialer transport.NodeDialer 48 | RBAC rbac.Evaluator 49 | STUNServers []string 50 | } 51 | 52 | // NewServer returns a new Server. 53 | func NewServer(opts Options) *Server { 54 | if len(opts.STUNServers) == 0 { 55 | opts.STUNServers = DefaultSTUNServers 56 | } 57 | return &Server{ 58 | wg: opts.Wireguard, 59 | rbacEval: opts.RBAC, 60 | opts: opts, 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /pkg/storage/graphstore.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package storage 18 | 19 | import ( 20 | "context" 21 | 22 | "github.com/dominikbraun/graph" 23 | 24 | "github.com/webmeshproj/webmesh/pkg/storage/types" 25 | ) 26 | 27 | // GraphStore is a storage interface for graph data. 28 | type GraphStore interface { 29 | // PeerGraphStore is a storage interface for peer graph data. 30 | types.PeerGraphStore 31 | 32 | // Subscribe subscribes to changes to nodes and edges. 33 | Subscribe(ctx context.Context, fn PeerSubscribeFunc) (context.CancelFunc, error) 34 | } 35 | 36 | // NewGraphWithStore creates a new Graph instance with the given graph storage implementation. 37 | func NewGraphWithStore(store GraphStore) types.PeerGraph { 38 | return graph.NewWithStore(graphHasher, store) 39 | } 40 | 41 | // graphHasher is the hash key function for the graph. 42 | func graphHasher(n types.MeshNode) types.NodeID { return types.NodeID(n.GetId()) } 43 | -------------------------------------------------------------------------------- /pkg/storage/meshdb/test_database.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package meshdb 18 | 19 | import ( 20 | "io" 21 | 22 | "github.com/webmeshproj/webmesh/pkg/storage" 23 | "github.com/webmeshproj/webmesh/pkg/storage/providers/backends/badgerdb" 24 | ) 25 | 26 | // MeshDBCloser is a storage.MeshDB that can be closed. 27 | type MeshDBCloser interface { 28 | storage.MeshDB 29 | io.Closer 30 | } 31 | 32 | // NewTestDB returns a new in-memory storage.Database instance for testing. 33 | func NewTestDB() *TestDB { 34 | memdb := badgerdb.NewTestStorage(false) 35 | return &TestDB{ 36 | MeshDB: NewFromStorage(memdb), 37 | Closer: memdb, 38 | } 39 | } 40 | 41 | type TestDB struct { 42 | storage.MeshDB 43 | io.Closer 44 | } 45 | -------------------------------------------------------------------------------- /pkg/storage/meshstate.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package storage 18 | 19 | import ( 20 | "context" 21 | 22 | "github.com/webmeshproj/webmesh/pkg/storage/types" 23 | ) 24 | 25 | // MeshState is the interface for querying mesh state. 26 | type MeshState interface { 27 | // SetMeshState sets the full mesh state. 28 | SetMeshState(ctx context.Context, state types.NetworkState) error 29 | // GetMeshState returns the full mesh state. 30 | GetMeshState(ctx context.Context) (types.NetworkState, error) 31 | } 32 | -------------------------------------------------------------------------------- /pkg/storage/providers/builtin_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package providers 18 | 19 | import ( 20 | "context" 21 | "os" 22 | "testing" 23 | 24 | "github.com/webmeshproj/webmesh/pkg/storage" 25 | "github.com/webmeshproj/webmesh/pkg/storage/meshdb" 26 | "github.com/webmeshproj/webmesh/pkg/storage/providers/backends/badgerdb" 27 | "github.com/webmeshproj/webmesh/pkg/storage/testutil" 28 | ) 29 | 30 | func TestBuiltinDataStoreConformance(t *testing.T) { 31 | testutil.TestMeshDataStoreConformance(t, func(t *testing.T) storage.MeshDataStore { 32 | db := meshdb.NewTestDB() 33 | t.Cleanup(func() { 34 | _ = db.Close() 35 | }) 36 | return db 37 | }) 38 | testutil.TestPeerStorageConformance(t, func(t *testing.T) storage.Peers { 39 | db := meshdb.NewTestDB() 40 | t.Cleanup(func() { 41 | _ = db.Close() 42 | }) 43 | return db.Peers() 44 | }) 45 | } 46 | 47 | func TestBadgerStoreConformance(t *testing.T) { 48 | st, err := badgerdb.NewInMemory(badgerdb.Options{}) 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | defer st.Close() 53 | testutil.TestDualStorageConformance(context.Background(), t, st) 54 | dir, err := os.MkdirTemp("", "") 55 | if err != nil { 56 | t.Fatal(err) 57 | } 58 | defer os.RemoveAll(dir) 59 | st, err = badgerdb.New(badgerdb.Options{ 60 | DiskPath: dir, 61 | }) 62 | if err != nil { 63 | t.Fatal(err) 64 | } 65 | testutil.TestDualStorageConformance(context.Background(), t, st) 66 | } 67 | -------------------------------------------------------------------------------- /pkg/storage/providers/providers.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package providers contains the built-in storage providers. 18 | package providers 19 | -------------------------------------------------------------------------------- /pkg/storage/providers/raftstorage/raftlogs/apply.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package raftlogs provides facilities for applying raft logs to a database. 18 | // It also contains helpers for executing queries and statements provided as 19 | // protobuf messages. 20 | package raftlogs 21 | 22 | import ( 23 | "fmt" 24 | "log/slog" 25 | "time" 26 | 27 | v1 "github.com/webmeshproj/api/go/v1" 28 | 29 | "github.com/webmeshproj/webmesh/pkg/context" 30 | "github.com/webmeshproj/webmesh/pkg/storage" 31 | ) 32 | 33 | // Apply applies a raft log to the given storage. 34 | func Apply(ctx context.Context, db storage.MeshStorage, logEntry *v1.RaftLogEntry) *v1.RaftApplyResponse { 35 | start := time.Now() 36 | log := context.LoggerFrom(ctx) 37 | switch logEntry.GetType() { 38 | case v1.RaftCommandType_PUT: 39 | log.Debug("Applying put", 40 | slog.String("key", string(logEntry.GetKey())), 41 | slog.String("value", string(logEntry.GetValue())), 42 | ) 43 | err := db.PutValue(ctx, logEntry.GetKey(), logEntry.GetValue(), logEntry.Ttl.AsDuration()) 44 | res := &v1.RaftApplyResponse{} 45 | if err != nil { 46 | res.Error = err.Error() 47 | } 48 | res.Time = time.Since(start).String() 49 | return res 50 | case v1.RaftCommandType_DELETE: 51 | log.Debug("Applying delete", 52 | slog.String("key", string(logEntry.GetKey())), 53 | ) 54 | err := db.Delete(ctx, logEntry.GetKey()) 55 | res := &v1.RaftApplyResponse{} 56 | if err != nil { 57 | res.Error = err.Error() 58 | } 59 | res.Time = time.Since(start).String() 60 | return res 61 | default: 62 | return &v1.RaftApplyResponse{ 63 | Error: fmt.Sprintf("unknown command type: %v", logEntry.GetType()), 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /pkg/storage/rpcsrv/serve.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package rpcsrv contains utilities for serving mesh databases over RPC. 18 | package rpcsrv 19 | 20 | import ( 21 | "io" 22 | 23 | v1 "github.com/webmeshproj/api/go/v1" 24 | "google.golang.org/grpc" 25 | 26 | "github.com/webmeshproj/webmesh/pkg/context" 27 | "github.com/webmeshproj/webmesh/pkg/storage" 28 | ) 29 | 30 | // QueryClient is the interface for a storage query client. 31 | type QueryClient interface { 32 | // The underlying gRPC stream. 33 | grpc.ClientStream 34 | // Send sends a query result to the plugin. 35 | Send(*v1.QueryResponse) error 36 | // Recv receives a query request from the plugin. 37 | Recv() (*v1.QueryRequest, error) 38 | } 39 | 40 | // Serve serves database operations over a plugin query stream. 41 | func Serve(ctx context.Context, db storage.Provider, cli QueryClient) error { 42 | log := context.LoggerFrom(ctx) 43 | defer func() { 44 | err := cli.CloseSend() 45 | if err != nil { 46 | log.Warn("Error closing query stream", "error", err) 47 | } 48 | }() 49 | for { 50 | query, err := cli.Recv() 51 | if err != nil { 52 | if err == io.EOF { 53 | log.Debug("Query stream closed cleanly") 54 | return nil 55 | } 56 | // TODO: restart the stream? 57 | log.Error("Error receiving query", "error", err) 58 | return err 59 | } 60 | log.Debug("Handling query request", 61 | "command", query.GetCommand().String(), 62 | "type", query.GetType().String(), 63 | "query", query.GetQuery(), 64 | ) 65 | err = cli.Send(ServeQuery(ctx, db, query)) 66 | if err != nil { 67 | log.Error("Error sending query response", "error", err) 68 | return err 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /pkg/storage/testutil/database_conformance.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package testutil 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/webmeshproj/webmesh/pkg/storage" 23 | "github.com/webmeshproj/webmesh/pkg/storage/meshdb" 24 | "github.com/webmeshproj/webmesh/pkg/storage/types" 25 | ) 26 | 27 | // NewMeshDataStoreFunc is a function that creates a new MeshDataStore implementation. 28 | type NewMeshDataStoreFunc func(t *testing.T) storage.MeshDataStore 29 | 30 | // TestMeshDataStoreConformance is a helper for running all database conformance 31 | // tests against a MeshDataStore implementation. 32 | func TestMeshDataStoreConformance(t *testing.T, builder NewMeshDataStoreFunc) { 33 | t.Run("MeshDBConformance", func(t *testing.T) { 34 | TestPeerGraphstoreConformance(t, func(t *testing.T) types.PeerGraphStore { 35 | db := meshdb.New(builder(t)) 36 | return db.GraphStore() 37 | }) 38 | TestMeshStateStorageConformance(t, func(t *testing.T) storage.MeshState { 39 | db := meshdb.New(builder(t)) 40 | return db.MeshState() 41 | }) 42 | TestRBACStorageConformance(t, func(t *testing.T) storage.RBAC { 43 | db := meshdb.New(builder(t)) 44 | return db.RBAC() 45 | }) 46 | TestNetworkingStorageConformance(t, func(t *testing.T) storage.Networking { 47 | db := meshdb.New(builder(t)) 48 | return db.Networking() 49 | }) 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /pkg/storage/types/network_access.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package types 18 | 19 | import ( 20 | "net/netip" 21 | 22 | v1 "github.com/webmeshproj/api/go/v1" 23 | ) 24 | 25 | // NetworkAction wraps a NetworkAction. 26 | type NetworkAction struct { 27 | *v1.NetworkAction `json:",inline"` 28 | } 29 | 30 | // Proto returns the protobuf representation of the action. 31 | func (a *NetworkAction) Proto() *v1.NetworkAction { 32 | return a.NetworkAction 33 | } 34 | 35 | // SourcePrefix returns the source prefix for the action if it is valid. 36 | func (a *NetworkAction) SourcePrefix() netip.Prefix { 37 | if a.GetSrcCIDR() == "" { 38 | return netip.Prefix{} 39 | } 40 | if a.GetSrcCIDR() == "*" { 41 | return netip.MustParsePrefix("0.0.0.0/0") 42 | } 43 | out, _ := netip.ParsePrefix(a.GetSrcCIDR()) 44 | return out 45 | } 46 | 47 | // DestinationPrefix returns the destination prefix for the action if it is valid. 48 | func (a *NetworkAction) DestinationPrefix() netip.Prefix { 49 | if a.GetDstCIDR() == "" { 50 | return netip.Prefix{} 51 | } 52 | if a.GetDstCIDR() == "*" { 53 | return netip.MustParsePrefix("0.0.0.0/0") 54 | } 55 | out, _ := netip.ParsePrefix(a.GetDstCIDR()) 56 | return out 57 | } 58 | -------------------------------------------------------------------------------- /pkg/storage/types/prefix.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package types 18 | 19 | import "net/netip" 20 | 21 | // Prefix is wraps a netip.Prefix with a custom JSON marshaller. 22 | type Prefix struct{ netip.Prefix } 23 | 24 | // ParsePrefix parses a string into a Prefix. 25 | func ParsePrefix(s string) (Prefix, error) { 26 | p, err := netip.ParsePrefix(s) 27 | return Prefix{p}, err 28 | } 29 | 30 | // MustParsePrefix parses a string into a Prefix and panics on error. 31 | func MustParsePrefix(s string) Prefix { 32 | p, err := ParsePrefix(s) 33 | if err != nil { 34 | panic(err) 35 | } 36 | return p 37 | } 38 | 39 | // MarshalJSON marshals a Prefix as a string. 40 | func (p Prefix) MarshalJSON() ([]byte, error) { 41 | if !p.IsValid() { 42 | return []byte(`""`), nil 43 | } 44 | return []byte("\"" + p.String() + "\""), nil 45 | } 46 | 47 | // UnmarshalJSON unmarshals a Prefix from a string. 48 | func (p *Prefix) UnmarshalJSON(b []byte) error { 49 | if string(b) == "null" || string(b) == `""` { 50 | return nil 51 | } 52 | var err error 53 | p.Prefix, err = netip.ParsePrefix(string(b)) 54 | if err != nil { 55 | return err 56 | } 57 | return nil 58 | } 59 | -------------------------------------------------------------------------------- /pkg/storage/types/storage_peer.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package types 18 | 19 | import ( 20 | v1 "github.com/webmeshproj/api/go/v1" 21 | "google.golang.org/protobuf/encoding/protojson" 22 | ) 23 | 24 | // StoragePeer wraps a storage peer. 25 | type StoragePeer struct { 26 | *v1.StoragePeer `json:",inline"` 27 | } 28 | 29 | // Proto returns the underlying protobuf. 30 | func (n StoragePeer) Proto() *v1.StoragePeer { 31 | return n.StoragePeer 32 | } 33 | 34 | // DeepCopy returns a deep copy of the peer. 35 | func (n StoragePeer) DeepCopy() StoragePeer { 36 | return StoragePeer{StoragePeer: n.StoragePeer.DeepCopy()} 37 | } 38 | 39 | // DeepCopyInto copies the node into the given peer. 40 | func (n StoragePeer) DeepCopyInto(group *StoragePeer) { 41 | *group = n.DeepCopy() 42 | } 43 | 44 | // MarshalProtoJSON marshals the peer to JSON. 45 | func (n StoragePeer) MarshalProtoJSON() ([]byte, error) { 46 | return protojson.Marshal(n.StoragePeer) 47 | } 48 | 49 | // UnmarshalProtoJSON unmarshals the peer from JSON. 50 | func (n *StoragePeer) UnmarshalProtoJSON(data []byte) error { 51 | var peer v1.StoragePeer 52 | if err := protojson.Unmarshal(data, &peer); err != nil { 53 | return err 54 | } 55 | n.StoragePeer = &peer 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Avi Zimmerman 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package version contains compile-time version information. 18 | package version 19 | 20 | import "encoding/json" 21 | 22 | var ( 23 | // Version is the version of the binary. 24 | Version = "unknown" 25 | // GitCommit is the git commit of the binary. 26 | GitCommit = "unknown" 27 | // BuildDate is the date the binary was built. 28 | BuildDate = "unknown" 29 | ) 30 | 31 | // BuildInfo is the current build information. 32 | type BuildInfo struct { 33 | Version string `json:"version"` 34 | GitCommit string `json:"gitCommit"` 35 | BuildDate string `json:"buildDate"` 36 | } 37 | 38 | // GetBuildInfo returns the current build information. 39 | func GetBuildInfo() BuildInfo { 40 | return BuildInfo{ 41 | Version: Version, 42 | GitCommit: GitCommit, 43 | BuildDate: BuildDate, 44 | } 45 | } 46 | 47 | // MarshalJSON implements json.Marshaler. 48 | func (b BuildInfo) MarshalJSON() ([]byte, error) { 49 | return json.Marshal(map[string]string{ 50 | "version": b.Version, 51 | "gitCommit": b.GitCommit, 52 | "buildDate": b.BuildDate, 53 | }) 54 | } 55 | 56 | // PrettyJSON returns the current build information as a pretty-printed JSON string. 57 | func (b BuildInfo) PrettyJSON(component string) string { 58 | out, _ := json.MarshalIndent(map[string]string{ 59 | "component": component, 60 | "version": b.Version, 61 | "gitCommit": b.GitCommit, 62 | "buildDate": b.BuildDate, 63 | }, "", " ") 64 | return string(out) 65 | } 66 | --------------------------------------------------------------------------------