├── .github └── workflows │ └── build.yml ├── .gitignore ├── CONTRIBUTING.md ├── DESIGN.md ├── Dockerfile ├── LICENSE ├── README.md ├── VERSION ├── cmd ├── fleetspeak_admin │ └── fleetspeak_admin.go ├── fleetspeak_client │ └── fleetspeak_client.go ├── fleetspeak_config │ └── fleetspeak_config.go └── fleetspeak_server │ └── fleetspeak_server.go ├── docs ├── DB-PRUNING.md └── guide.md ├── fleetspeak ├── build-pkgs.sh ├── build.sh ├── client-mac │ ├── Distribution.xml │ ├── build.sh │ ├── com.google.code.fleetspeak.plist │ ├── communicator.txt │ └── postinstall ├── client-pkg-tmpl │ ├── debian │ │ ├── compat │ │ ├── control │ │ ├── docs │ │ ├── fleetspeak-client.service │ │ ├── install │ │ ├── postinst │ │ └── rules │ ├── etc │ │ └── fleetspeak-client │ │ │ └── communicator.txt │ └── usr │ │ └── share │ │ └── doc │ │ └── fleetspeak-client │ │ └── copyright ├── client-wheel │ └── setup.py ├── client-win │ ├── build.ps1 │ ├── fleetspeak.wxs │ └── fleetspeak_lib.wxs ├── generate_protos.sh ├── generate_protos_setup.sh ├── server-pkg-tmpl │ ├── debian │ │ ├── compat │ │ ├── conffiles │ │ ├── control │ │ ├── docs │ │ ├── fleetspeak-server.service │ │ ├── install │ │ ├── postinst │ │ └── rules │ ├── etc │ │ └── fleetspeak-server │ │ │ ├── configurator.config │ │ │ └── server.services.config │ └── usr │ │ └── share │ │ └── doc │ │ └── fleetspeak-server │ │ └── copyright ├── server-wheel │ └── setup.py ├── src │ ├── admin │ │ ├── cli │ │ │ └── cli.go │ │ └── history │ │ │ ├── history.go │ │ │ └── history_test.go │ ├── client │ │ ├── channel │ │ │ ├── channel.go │ │ │ ├── channel_test.go │ │ │ ├── proto │ │ │ │ └── fleetspeak_channel │ │ │ │ │ ├── channel.pb.go │ │ │ │ │ └── channel.proto │ │ │ ├── relentless.go │ │ │ └── relentless_test.go │ │ ├── client.go │ │ ├── client_test.go │ │ ├── clienttestutils │ │ │ └── clienttestutils.go │ │ ├── clitesting │ │ │ └── fake_service_context.go │ │ ├── comms.go │ │ ├── comms │ │ │ └── comms.go │ │ ├── comms_test.go │ │ ├── config │ │ │ ├── config.go │ │ │ ├── filesystem_persistence_handler.go │ │ │ ├── noop_persistence_handler.go │ │ │ └── windows_registry_persistence_handler.go │ │ ├── daemonservice │ │ │ ├── client │ │ │ │ └── direct.go │ │ │ ├── command │ │ │ │ ├── command.go │ │ │ │ ├── command_unix.go │ │ │ │ └── command_windows.go │ │ │ ├── daemonservice.go │ │ │ ├── daemonservice_oss_test.go │ │ │ ├── daemonservice_test.go │ │ │ ├── execution │ │ │ │ ├── execution.go │ │ │ │ ├── execution_oss_test.go │ │ │ │ └── execution_test.go │ │ │ ├── proto │ │ │ │ └── fleetspeak_daemonservice │ │ │ │ │ ├── config.pb.go │ │ │ │ │ ├── config.proto │ │ │ │ │ ├── messages.pb.go │ │ │ │ │ └── messages.proto │ │ │ └── testclient │ │ │ │ └── testclient.go │ │ ├── entry │ │ │ ├── entry.go │ │ │ ├── entry_unix.go │ │ │ └── entry_windows.go │ │ ├── flow │ │ │ ├── filter.go │ │ │ └── filter_test.go │ │ ├── generic │ │ │ ├── config.go │ │ │ ├── config_unix.go │ │ │ ├── config_windows.go │ │ │ └── proto │ │ │ │ └── fleetspeak_client_generic │ │ │ │ ├── config.pb.go │ │ │ │ └── config.proto │ │ ├── https │ │ │ ├── compression.go │ │ │ ├── https.go │ │ │ ├── https_test.go │ │ │ ├── polling.go │ │ │ ├── polling_test.go │ │ │ ├── streaming.go │ │ │ └── streaming_test.go │ │ ├── internal │ │ │ ├── config │ │ │ │ ├── manager.go │ │ │ │ └── manager_test.go │ │ │ ├── message │ │ │ │ ├── retry.go │ │ │ │ ├── retry_test.go │ │ │ │ ├── sort.go │ │ │ │ └── sort_test.go │ │ │ ├── monitoring │ │ │ │ ├── resource_usage_fetcher.go │ │ │ │ ├── resource_usage_fetcher_test.go │ │ │ │ ├── resource_usage_fetcher_unix.go │ │ │ │ ├── resource_usage_monitor.go │ │ │ │ └── resource_usage_monitor_test.go │ │ │ └── process │ │ │ │ └── process.go │ │ ├── proto │ │ │ └── fleetspeak_client │ │ │ │ ├── api.pb.go │ │ │ │ ├── api.proto │ │ │ │ ├── client.pb.go │ │ │ │ └── client.proto │ │ ├── service │ │ │ └── service.go │ │ ├── services.go │ │ ├── signer │ │ │ └── signer.go │ │ ├── socketservice │ │ │ ├── checks │ │ │ │ ├── sock_checks_unix.go │ │ │ │ └── sock_checks_windows.go │ │ │ ├── client │ │ │ │ ├── proxy.go │ │ │ │ ├── proxy_unix.go │ │ │ │ └── proxy_windows.go │ │ │ ├── proto │ │ │ │ └── fleetspeak_socketservice │ │ │ │ │ ├── config.pb.go │ │ │ │ │ └── config.proto │ │ │ ├── socketservice.go │ │ │ ├── socketservice_oss_test.go │ │ │ ├── socketservice_test.go │ │ │ ├── socketservice_unix.go │ │ │ ├── socketservice_windows.go │ │ │ └── testclient │ │ │ │ └── testclient.go │ │ ├── stats │ │ │ └── collector.go │ │ ├── stdinservice │ │ │ ├── proto │ │ │ │ └── fleetspeak_stdinservice │ │ │ │ │ ├── config.pb.go │ │ │ │ │ ├── config.proto │ │ │ │ │ ├── messages.pb.go │ │ │ │ │ └── messages.proto │ │ │ ├── stdinservice.go │ │ │ └── stdinservice_test.go │ │ ├── system_service.go │ │ ├── system_service_test.go │ │ └── watchdog │ │ │ ├── watchdog.go │ │ │ ├── watchdog_test.go │ │ │ ├── watchdog_unix.go │ │ │ └── watchdog_windows.go │ ├── common │ │ ├── anypbtest │ │ │ └── anypbtest.go │ │ ├── fscontext │ │ │ ├── fscontext.go │ │ │ └── fscontext_test.go │ │ ├── ids.go │ │ ├── ids_test.go │ │ ├── proto │ │ │ ├── fleetspeak │ │ │ │ ├── common.pb.go │ │ │ │ ├── common.proto │ │ │ │ ├── system.pb.go │ │ │ │ └── system.proto │ │ │ └── fleetspeak_monitoring │ │ │ │ ├── resource.pb.go │ │ │ │ └── resource.proto │ │ └── should │ │ │ └── never_oss.go │ ├── comtesting │ │ ├── server_cert.go │ │ ├── tempdir.go │ │ ├── tempdir_unix.go │ │ └── tempdir_windows.go │ ├── config │ │ ├── certs │ │ │ ├── server.go │ │ │ └── trusted.go │ │ ├── client │ │ │ └── config.go │ │ ├── proto │ │ │ └── fleetspeak_config │ │ │ │ ├── config.pb.go │ │ │ │ └── config.proto │ │ └── server │ │ │ └── config.go │ ├── e2etesting │ │ ├── README.md │ │ ├── balancer │ │ │ ├── balancer.go │ │ │ └── proxyproto │ │ │ │ └── proxyproto.go │ │ ├── frr_master_server_main │ │ │ └── frr_master_server_main.go │ │ ├── localtesting │ │ │ └── end_to_end_test.go │ │ ├── setup │ │ │ └── setup_components.go │ │ └── tests │ │ │ └── end_to_end_tests.go │ ├── inttesting │ │ ├── frr │ │ │ ├── frr.go │ │ │ ├── frr_test.go │ │ │ └── proto │ │ │ │ └── fleetspeak_frr │ │ │ │ ├── frr.pb.go │ │ │ │ ├── frr.proto │ │ │ │ └── frr_grpc.pb.go │ │ ├── integration_test.go │ │ └── integrationtest │ │ │ ├── clone.go │ │ │ ├── frr.go │ │ │ └── sigs.go │ ├── server │ │ ├── admin │ │ │ └── admin.go │ │ ├── authorizer │ │ │ └── authorizer.go │ │ ├── comms.go │ │ ├── comms │ │ │ └── comms.go │ │ ├── components │ │ │ ├── authorizer │ │ │ │ └── authorizer.go │ │ │ ├── components.go │ │ │ ├── components_test.go │ │ │ ├── https │ │ │ │ └── proxy.go │ │ │ ├── notifications │ │ │ │ ├── http.go │ │ │ │ └── http_test.go │ │ │ ├── prometheus │ │ │ │ └── prometheus.go │ │ │ └── proto │ │ │ │ └── fleetspeak_components │ │ │ │ ├── config.pb.go │ │ │ │ └── config.proto │ │ ├── cpsservice │ │ │ ├── proto │ │ │ │ └── fleetspeak_cpsservice │ │ │ │ │ ├── cpsservice.pb.go │ │ │ │ │ └── cpsservice.proto │ │ │ ├── service.go │ │ │ └── service_test.go │ │ ├── db │ │ │ ├── store.go │ │ │ └── time.go │ │ ├── dbtesting │ │ │ ├── broadcaststore_suite.go │ │ │ ├── clientstore_suite.go │ │ │ ├── dbtesting.go │ │ │ ├── filestore_suite.go │ │ │ ├── integration_suite.go │ │ │ └── messagestore_suite.go │ │ ├── grpcservice │ │ │ ├── client │ │ │ │ └── testing │ │ │ │ │ ├── client_test.sh │ │ │ │ │ └── tester.go │ │ │ ├── proto │ │ │ │ └── fleetspeak_grpcservice │ │ │ │ │ ├── grpcservice.pb.go │ │ │ │ │ ├── grpcservice.proto │ │ │ │ │ └── grpcservice_grpc.pb.go │ │ │ ├── service.go │ │ │ └── service_test.go │ │ ├── https │ │ │ ├── client_certificate.go │ │ │ ├── client_certificate_test.go │ │ │ ├── compression.go │ │ │ ├── file_server.go │ │ │ ├── https.go │ │ │ ├── https_test.go │ │ │ ├── message_server.go │ │ │ └── streaming_message_server.go │ │ ├── ids │ │ │ └── ids.go │ │ ├── internal │ │ │ ├── broadcasts │ │ │ │ ├── broadcasts.go │ │ │ │ └── broadcasts_test.go │ │ │ ├── cache │ │ │ │ ├── cache.go │ │ │ │ └── cache_test.go │ │ │ ├── ftime │ │ │ │ ├── ftime.go │ │ │ │ └── retry.go │ │ │ ├── notifications │ │ │ │ ├── notifications.go │ │ │ │ └── notifications_test.go │ │ │ ├── services │ │ │ │ ├── manager.go │ │ │ │ └── system_service.go │ │ │ └── signatures │ │ │ │ └── validation.go │ │ ├── mysql │ │ │ ├── broadcaststore.go │ │ │ ├── clientstore.go │ │ │ ├── filestore.go │ │ │ ├── messagestore.go │ │ │ ├── mysql.go │ │ │ └── mysql_test.go │ │ ├── notifications │ │ │ └── notifications.go │ │ ├── proto │ │ │ └── fleetspeak_server │ │ │ │ ├── admin.pb.go │ │ │ │ ├── admin.proto │ │ │ │ ├── admin_grpc.pb.go │ │ │ │ ├── broadcasts.pb.go │ │ │ │ ├── broadcasts.proto │ │ │ │ ├── resource.pb.go │ │ │ │ ├── resource.proto │ │ │ │ ├── server.pb.go │ │ │ │ ├── server.proto │ │ │ │ ├── services.pb.go │ │ │ │ └── services.proto │ │ ├── sertesting │ │ │ ├── cache.go │ │ │ ├── context.go │ │ │ ├── retry.go │ │ │ └── time.go │ │ ├── server.go │ │ ├── servertests │ │ │ ├── admin_test.go │ │ │ ├── comms_test.go │ │ │ └── system_service_test.go │ │ ├── service │ │ │ ├── service.go │ │ │ └── service_test.go │ │ ├── spanner │ │ │ ├── broadcaststore.go │ │ │ ├── clientstore.go │ │ │ ├── filestore.go │ │ │ ├── messagestore.go │ │ │ ├── spanner.go │ │ │ └── spanner_test.go │ │ ├── sqlite │ │ │ ├── broadcaststore.go │ │ │ ├── clientstore.go │ │ │ ├── filestore.go │ │ │ ├── messagestore.go │ │ │ ├── sqlite.go │ │ │ └── sqlite_test.go │ │ ├── stats.go │ │ ├── stats │ │ │ └── collector.go │ │ └── testserver │ │ │ └── testserver.go │ └── windows │ │ ├── hashpipe │ │ ├── hashpipe.go │ │ ├── hashpipe_test.go │ │ ├── hashpipe_windows.go │ │ └── hashpipe_windows_test.go │ │ ├── regutil │ │ ├── regutil.go │ │ ├── regutil_test.go │ │ ├── regutil_windows.go │ │ └── regutil_windows_test.go │ │ └── wnixsocket │ │ ├── wnixsocket.go │ │ ├── wnixsocket_test.go │ │ ├── wnixsocket_windows.go │ │ └── wnixsocket_windows_test.go ├── test-package.sh └── test.sh ├── fleetspeak_python ├── fleetspeak │ ├── client_connector │ │ ├── connector.py │ │ └── testing │ │ │ ├── testclient.py │ │ │ └── testclient_launcher.py │ └── server_connector │ │ ├── connector.py │ │ ├── connector_test.py │ │ └── testing │ │ └── loopback.py └── setup.py ├── frr_python ├── frr_client.py ├── frr_server.py └── setup.py ├── go.mod ├── go.sum ├── sandboxes ├── README.md ├── cleartext-header-mode │ ├── README.md │ ├── config │ │ ├── fleetspeak-client │ │ │ ├── communicator.txt │ │ │ ├── config.textproto │ │ │ └── textservices │ │ │ │ └── hello.service │ │ ├── fleetspeak-server │ │ │ ├── components.textproto │ │ │ └── services.textproto │ │ ├── fleetspeak.textproto │ │ ├── hello.py │ │ └── hello.sh │ ├── docker-compose.yaml │ └── envoy-https-http.yaml ├── cleartext-xfcc-mode │ ├── README.md │ ├── config │ │ ├── fleetspeak-client │ │ │ ├── communicator.txt │ │ │ ├── config.textproto │ │ │ └── textservices │ │ │ │ └── hello.service │ │ ├── fleetspeak-server │ │ │ ├── components.textproto │ │ │ └── services.textproto │ │ ├── fleetspeak.textproto │ │ ├── hello.py │ │ └── hello.sh │ ├── docker-compose.yaml │ └── envoy-https-http.yaml ├── createConfig.sh ├── diagrams │ ├── cleartextHeaderMode_355.png │ ├── cleartextXfccMode_355.png │ ├── directMode_355.png │ ├── httpsHeaderMode_355.png │ └── passthroughMode_355.png ├── direct-mtls-mode │ ├── README.md │ ├── config │ │ ├── fleetspeak-client │ │ │ ├── communicator.txt │ │ │ ├── config.textproto │ │ │ └── textservices │ │ │ │ └── hello.service │ │ ├── fleetspeak-server │ │ │ ├── components.textproto │ │ │ └── services.textproto │ │ ├── fleetspeak.textproto │ │ ├── hello.py │ │ └── hello.sh │ └── docker-compose.yaml ├── https-header-mode │ ├── README.md │ ├── config │ │ ├── fleetspeak-client │ │ │ ├── communicator.txt │ │ │ ├── config.textproto │ │ │ └── textservices │ │ │ │ └── hello.service │ │ ├── fleetspeak-server │ │ │ ├── components.textproto │ │ │ └── services.textproto │ │ ├── fleetspeak.textproto │ │ ├── hello.py │ │ └── hello.sh │ ├── docker-compose.yaml │ └── envoy-https-https.yaml ├── passthrough-mode │ ├── README.md │ ├── config │ │ ├── fleetspeak-client │ │ │ ├── communicator.txt │ │ │ ├── config.textproto │ │ │ └── textservices │ │ │ │ └── hello.service │ │ ├── fleetspeak-server │ │ │ ├── components.textproto │ │ │ └── services.textproto │ │ ├── fleetspeak.textproto │ │ ├── hello.py │ │ └── hello.sh │ ├── docker-compose.yaml │ └── envoy-https-passthrough.yaml └── shared │ ├── envoy │ └── Dockerfile │ ├── fleetspeak-client │ └── Dockerfile │ ├── fleetspeak-server │ └── Dockerfile │ └── greeter │ ├── Dockerfile │ └── greeter.py ├── spanner-setup ├── Dockerfile.dev ├── SPANNER.md ├── docker-compose.yaml ├── fleetspeak.pb └── setup.sh └── terraform ├── README.md ├── cloudtesting └── end_to_end_test.go ├── fleetspeak_configurator └── build_configs.go ├── fs_client_start.sh ├── fs_server_start.sh ├── main.tf ├── main_vm_start.sh └── master_server_start.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Common temporary and build artifact file types. 2 | *.pyc 3 | *.swp 4 | *.o 5 | *.so 6 | *.pb.cc 7 | *.pb.h 8 | *~ 9 | .idea 10 | *.deb 11 | 12 | # Autogenerated Python files (unlike go proto-files, 13 | # these do not need to be checked in). 14 | *_pb2.py 15 | *_pb2_grpc.py 16 | 17 | # Generated when installing the Python lib. 18 | fleetspeak.egg-info/ 19 | 20 | # Binaries created by fleetspeak/build.sh 21 | cmd/*/* 22 | !cmd/*/*.go 23 | 24 | fleetspeak/src/client/client/client 25 | fleetspeak/src/client/client/client.exe 26 | fleetspeak/src/client/daemonservice/testclient/testclient 27 | fleetspeak/src/client/daemonservice/testclient/testclient.exe 28 | fleetspeak/src/client/socketservice/testclient/testclient 29 | fleetspeak/src/client/socketservice/testclient/testclient.exe 30 | fleetspeak/src/server/grpcservice/client/testing/tester 31 | 32 | # Directory laying out debian package structures, fully populated by 33 | # fleetspeak/build-pkgs.sh 34 | fleetspeak/server-pkg/ 35 | fleetspeak/client-pkg/ 36 | 37 | fleetspeak/src/e2etesting/balancer/balancer 38 | fleetspeak/src/e2etesting/frr-master-server-main/frr_master_server_main 39 | 40 | # Ignore pki files 41 | *.pem 42 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution, 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.22 AS builder 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y \ 5 | python-is-python3 6 | 7 | COPY . /fleetspeak 8 | 9 | ENV GOBIN=/fleetspeak/bin 10 | RUN mkdir -p $GOBIN 11 | RUN cd /fleetspeak && go install ./... 12 | 13 | 14 | FROM golang:1.22 15 | 16 | RUN apt update 17 | 18 | WORKDIR / 19 | 20 | ENV FLEETSPEAK_BIN=/fleetspeak/bin 21 | RUN mkdir -p $FLEETSPEAK_BIN 22 | 23 | COPY --from=builder /fleetspeak/bin/fleetspeak_server $FLEETSPEAK_BIN/server 24 | COPY --from=builder /fleetspeak/bin/fleetspeak_client $FLEETSPEAK_BIN/client 25 | COPY --from=builder /fleetspeak/bin/fleetspeak_config $FLEETSPEAK_BIN 26 | COPY --from=builder /fleetspeak/bin/fleetspeak_admin $FLEETSPEAK_BIN 27 | 28 | ENV PATH="$FLEETSPEAK_BIN:$PATH" 29 | 30 | ENTRYPOINT [ "server" ] 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fleetspeak 2 | 3 | [![.github/workflows/build.yml](https://github.com/google/fleetspeak/actions/workflows/build.yml/badge.svg)](https://github.com/google/fleetspeak/actions/workflows/build.yml) 4 | [![Go Report Card](https://goreportcard.com/badge/github.com/google/fleetspeak)](https://goreportcard.com/report/github.com/google/fleetspeak) 5 | 6 | Fleetspeak is a framework for communicating with a fleet of machines, with a 7 | focus on security monitoring and basic administrative use cases. It is a 8 | subproject of [GRR](https://github.com/google/grr/blob/master/README.md), and 9 | can be seen as an effort to modularizing and modernizing its communication 10 | mechanism. 11 | 12 | ## Status 13 | 14 | We have this code working internally as part of our GRR installation. 15 | 16 | ## Getting Started 17 | 18 | On linux, assuming a recent version of the go development environment (see the 19 | `go.mod` file for the exact requirement) and virtualenv, the following sequence 20 | of commands will build and test this pre-release: 21 | 22 | ```bash 23 | go get -v -t github.com/google/fleetspeak/... 24 | 25 | # Assuming default $GOPATH: 26 | cd ~/go/src/github.com/google/fleetspeak 27 | 28 | # Setup virtualenv - fleetspeak provides some python integration libraries, 29 | # and this ensures they are set up in a known way. 30 | virtualenv $HOME/FSENV 31 | source $HOME/FSENV/bin/activate 32 | 33 | pip install -e fleetspeak_python/ 34 | 35 | # Set mysql parameters. The mysql datastore test will run if the following environment 36 | # variables are set. Otherwise it will be skipped. 37 | export MYSQL_TEST_USER= 38 | export MYSQL_TEST_PASS= # will assume null password if unset. 39 | export MYSQL_TEST_ADDR= 40 | 41 | # Build and test the release: 42 | fleetspeak/build.sh 43 | fleetspeak/test.sh 44 | 45 | # After modifying proto files, the resulting go and python files need to be regenerated: 46 | go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.34.1 47 | go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.4 48 | fleetspeak/generate_go_py_protos.sh 49 | ``` 50 | 51 | Once built, you can take a look at the files and instructions in our 52 | [demo directory](https://github.com/google/fleetspeak/tree/master/fleetspeak/src/demo). 53 | 54 | ## DISCLAIMER 55 | 56 | While the code presented here is in some sense feature complete, much of it is 57 | barely tested or documented, and breaking changes are still possible. Therefore, 58 | please consider this a preview release while the dust settles. Suggestions and 59 | pull requests are very much appreciated. 60 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.1.18 2 | -------------------------------------------------------------------------------- /cmd/fleetspeak_admin/fleetspeak_admin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package main implements a general command line interface which performs 16 | // administrative actions on a fleetspeak installation. 17 | // 18 | // Most functionality is implemented by the cli sub-package. Installations may 19 | // want to branch this file to adjust the default admin_addr flag, dial 20 | // parameters and similar. 21 | package main 22 | 23 | import ( 24 | "flag" 25 | 26 | log "github.com/golang/glog" 27 | "google.golang.org/grpc" 28 | "google.golang.org/grpc/credentials/insecure" 29 | 30 | "github.com/google/fleetspeak/fleetspeak/src/admin/cli" 31 | ) 32 | 33 | var adminAddr = flag.String("admin_addr", "localhost:6061", "Address for the admin server.") 34 | 35 | func main() { 36 | flag.Parse() 37 | 38 | conn, err := grpc.Dial(*adminAddr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()) 39 | if err != nil { 40 | log.Exitf("Unable to connect to fleetspeak admin interface [%v]: %v", *adminAddr, err) 41 | } 42 | 43 | cli.Execute(conn, flag.Args()...) 44 | } 45 | -------------------------------------------------------------------------------- /fleetspeak/build-pkgs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2019 Google LLC. 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 | # https://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 | set -e 17 | 18 | /bin/echo >&2 "" 19 | /bin/echo >&2 "Building binaries" 20 | 21 | export BINDIR="$(mktemp -d)" 22 | trap "rm -rf ${BINDIR}" EXIT 23 | 24 | cd .. 25 | CGO_ENABLED=0 GOBIN="${BINDIR}" go install ./cmd/... 26 | cd - 27 | 28 | /bin/echo >&2 "" 29 | /bin/echo >&2 "Building server.deb" 30 | 31 | export DEB_DEST="server-pkg/debian/fleetspeak-server" 32 | export DEB_VERSION=$(cat ../VERSION) 33 | 34 | fakeroot bash -c ' 35 | set -e 36 | rm -rf server-pkg 37 | cp -r server-pkg-tmpl server-pkg 38 | 39 | chmod 755 server-pkg/* 40 | 41 | cd server-pkg 42 | debchange --create \ 43 | --newversion "${DEB_VERSION}" \ 44 | --package fleetspeak-server \ 45 | --urgency low \ 46 | --controlmaint \ 47 | --distribution unstable \ 48 | "Built by GitHub Actions at ${GITHUB_SHA}" 49 | cd - 50 | 51 | mkdir -p server-pkg/usr/bin 52 | install -o root -g root "${BINDIR}/fleetspeak_server" server-pkg/usr/bin/fleetspeak-server 53 | install -o root -g root "${BINDIR}/fleetspeak_config" server-pkg/usr/bin/fleetspeak-config 54 | install -o root -g root "${BINDIR}/fleetspeak_admin" server-pkg/usr/bin/fleetspeak-admin 55 | 56 | cd server-pkg 57 | dpkg-buildpackage -us -uc 58 | cd - 59 | ' 60 | 61 | /bin/echo >&2 "" 62 | /bin/echo >&2 "Building client.deb" 63 | fakeroot bash -c ' 64 | set -e 65 | rm -rf client-pkg 66 | cp -r client-pkg-tmpl client-pkg 67 | 68 | chmod 755 client-pkg/* 69 | 70 | cd client-pkg 71 | debchange --create \ 72 | --newversion "${DEB_VERSION}" \ 73 | --package fleetspeak-client \ 74 | --urgency low \ 75 | --controlmaint \ 76 | --distribution unstable \ 77 | "Built by GitHub Actions at ${GITHUB_SHA}" 78 | cd - 79 | 80 | mkdir -p client-pkg/usr/bin 81 | install -o root -g root "${BINDIR}/fleetspeak_client" client-pkg/usr/bin/fleetspeak-client 82 | 83 | cd client-pkg 84 | dpkg-buildpackage -us -uc 85 | cd - 86 | ' 87 | -------------------------------------------------------------------------------- /fleetspeak/client-mac/Distribution.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Fleetspeak Client 4 | com.google.code 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | fleetspeak-client-component.xar 17 | 18 | 19 | -------------------------------------------------------------------------------- /fleetspeak/client-mac/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -ex 4 | 5 | realpath() { 6 | [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}" 7 | } 8 | 9 | CONFIGS_DIR=$(pwd) 10 | FS_BINARY=$(realpath $1) 11 | FS_VERSION=$(cat ../../VERSION) 12 | COMPONENT_PKG_NAME="fleetspeak-client-component.xar" 13 | PRODUCT_ARCHIVE_NAME="fleetspeak-client-${FS_VERSION}.pkg" 14 | 15 | rm -rf ./work 16 | mkdir work 17 | cd work 18 | 19 | mkdir -p pkg_root/Library/LaunchDaemons 20 | mkdir -p pkg_root/etc/fleetspeak-client/services 21 | mkdir -p pkg_root/etc/fleetspeak-client/textservices 22 | 23 | # We install the Fleetspeak binary in /usr/local/bin, and 24 | # not /usr/sbin (as we do for Linux), because MacOS won't let us: 25 | # https://en.wikipedia.org/wiki/System_Integrity_Protection. 26 | mkdir -p pkg_root/usr/local/bin 27 | 28 | # Copy postinstall script. 29 | mkdir install-scripts 30 | cp "${CONFIGS_DIR}/postinstall" install-scripts 31 | chmod +x install-scripts/* 32 | 33 | cp "${CONFIGS_DIR}/com.google.code.fleetspeak.plist" pkg_root/Library/LaunchDaemons 34 | cp "${CONFIGS_DIR}/communicator.txt" pkg_root/etc/fleetspeak-client/ 35 | cp "${FS_BINARY}" pkg_root/usr/local/bin/fleetspeak-client 36 | 37 | # Set permissions for files and directories in the payload. 38 | find pkg_root -type d -exec chmod 755 {} \; 39 | find pkg_root -type f -exec chmod 644 {} \; 40 | chmod +x pkg_root/usr/local/bin/fleetspeak-client 41 | 42 | # Create a component package containing files to be installed. 43 | pkgbuild \ 44 | --root pkg_root \ 45 | --identifier com.google.code.fleetspeak \ 46 | --version "${FS_VERSION}" \ 47 | --scripts install-scripts \ 48 | "${COMPONENT_PKG_NAME}" 49 | 50 | # Copy over distribution file. 51 | cp "${CONFIGS_DIR}/Distribution.xml" ./ 52 | # Interpolate the version in the distribution file. 53 | # The tilde after the 'i' flag is the suffix to use for the backup file 54 | # created by sed. 55 | sed -i~ "s:\$VERSION:${FS_VERSION}:g" Distribution.xml 56 | rm -f 'Distribution.xml~' 57 | 58 | # Create final product archive. 59 | productbuild --distribution Distribution.xml --package-path "${COMPONENT_PKG_NAME}" "${PRODUCT_ARCHIVE_NAME}" 60 | 61 | -------------------------------------------------------------------------------- /fleetspeak/client-mac/com.google.code.fleetspeak.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | com.google.code.fleetspeak 7 | UserName 8 | root 9 | GroupName 10 | daemon 11 | ProgramArguments 12 | 13 | /usr/local/bin/fleetspeak-client 14 | --config=/etc/fleetspeak-client/client.config 15 | --log_dir=/private/var/log 16 | 17 | KeepAlive 18 | 19 | RunAtLoad 20 | 21 | ThrottleInterval 22 | 120 23 | ServiceDescription 24 | Fleetspeak Client 25 | StandardOutPath 26 | /dev/null 27 | StandardErrorPath 28 | /dev/null 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /fleetspeak/client-mac/communicator.txt: -------------------------------------------------------------------------------- 1 | # This is a text format fleetspeak_client.CommunicatorConfig and can be used 2 | # to tweak how often fleetspeak tries to ping the server. 3 | # 4 | # Example values for debugging: 5 | # 6 | # max_poll_delay_seconds: 10 7 | # max_buffer_delay_seconds: 1 8 | # min_failure_delay_seconds: 10 9 | 10 | -------------------------------------------------------------------------------- /fleetspeak/client-mac/postinstall: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # Abort if package install is not happening on the running volume. 6 | [[ $3 != '/' ]] && exit 0 7 | 8 | readonly DAEMON_PLIST=/Library/LaunchDaemons/com.google.code.fleetspeak.plist 9 | 10 | # Restart the Fleetspeak process. 11 | if [[ -f "${DAEMON_PLIST}" ]]; then 12 | launchctl unload "${DAEMON_PLIST}" || true 13 | launchctl load -w "${DAEMON_PLIST}" 14 | fi 15 | -------------------------------------------------------------------------------- /fleetspeak/client-pkg-tmpl/debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /fleetspeak/client-pkg-tmpl/debian/control: -------------------------------------------------------------------------------- 1 | Source: fleetspeak-client 2 | Maintainer: GRR Dev 3 | Build-Depends: debhelper (>= 9), debhelper (>= 9.20160709), devscripts 4 | Standards-Version: 3.8.3 5 | Homepage: https://github.com/google/fleetspeak 6 | 7 | Package: fleetspeak-client 8 | Architecture: any 9 | Pre-Depends: 10 | Depends: systemd 11 | Description: client for the Fleetspeak communications framework 12 | Fleetspeak is a framework for communicating with a fleet of machines, with a 13 | focus on security and administrative tasks. 14 | -------------------------------------------------------------------------------- /fleetspeak/client-pkg-tmpl/debian/docs: -------------------------------------------------------------------------------- 1 | usr/share/doc/fleetspeak-client/* 2 | -------------------------------------------------------------------------------- /fleetspeak/client-pkg-tmpl/debian/fleetspeak-client.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Fleetspeak Client Service 3 | After=syslog.target network.target 4 | Documentation=https://github.com/google/fleetspeak 5 | 6 | [Service] 7 | User=root 8 | ExecStart=/usr/bin/fleetspeak-client --config /etc/fleetspeak-client/client.config 9 | ExecReload=kill -s HUP $MAINPID 10 | Restart=always 11 | KillMode=process 12 | 13 | [Install] 14 | WantedBy=multi-user.target 15 | -------------------------------------------------------------------------------- /fleetspeak/client-pkg-tmpl/debian/install: -------------------------------------------------------------------------------- 1 | etc/fleetspeak-client/communicator.txt etc/fleetspeak-client 2 | usr/bin/fleetspeak-client usr/bin 3 | -------------------------------------------------------------------------------- /fleetspeak/client-pkg-tmpl/debian/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Post-installation script for the Fleetspeak client deb. 4 | # 5 | 6 | set -e 7 | 8 | # The token below is replaced with shellscript snippets generated 9 | # by debhelper commands. See http://manpages.ubuntu.com/dh_installdeb 10 | 11 | #DEBHELPER# 12 | 13 | case "$1" in 14 | configure) 15 | ;; 16 | esac 17 | -------------------------------------------------------------------------------- /fleetspeak/client-pkg-tmpl/debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | 4 | # Uncomment this to turn on verbose mode. 5 | # export DH_VERBOSE=1 6 | 7 | %: 8 | dh $@ --with systemd 9 | -------------------------------------------------------------------------------- /fleetspeak/client-pkg-tmpl/etc/fleetspeak-client/communicator.txt: -------------------------------------------------------------------------------- 1 | # This is a text format fleetspeak.client.CommunicatorConfig and can be used 2 | # to tweak how often the fleetspeak client tries to ping the server. 3 | # 4 | # Example values for debugging: 5 | # 6 | # max_poll_delay_seconds: 10 7 | # max_buffer_delay_seconds: 1 8 | # min_failure_delay_seconds: 10 9 | 10 | -------------------------------------------------------------------------------- /fleetspeak/client-pkg-tmpl/usr/share/doc/fleetspeak-client/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: Fleetspeak 3 | Upstream-Contact: GRR Dev 4 | Source: https://github.com/google/fleetspeak 5 | 6 | Files: * 7 | Copyright: 2017-2018 Google 8 | License: Apache-2.0 9 | -------------------------------------------------------------------------------- /fleetspeak/client-wheel/setup.py: -------------------------------------------------------------------------------- 1 | from optparse import OptionParser 2 | import pathlib 3 | import sys 4 | 5 | from setuptools import setup 6 | from wheel.bdist_wheel import bdist_wheel 7 | 8 | 9 | class BdistWheel(bdist_wheel): 10 | user_options = [ 11 | ("platform-name=", None, "Platform name to force."), 12 | ] 13 | 14 | def initialize_options(self): 15 | self.platform_name = None 16 | bdist_wheel.initialize_options(self) 17 | 18 | def finalize_options(self): 19 | bdist_wheel.finalize_options(self) 20 | self.root_is_pure = False 21 | 22 | def get_tag(self): 23 | impl, abi_tag, plat_name = bdist_wheel.get_tag(self) 24 | if self.platform_name is not None: 25 | plat_name = self.platform_name 26 | return "py2.py3", "none", plat_name 27 | 28 | 29 | def GetOptions(): 30 | parser = OptionParser() 31 | parser.add_option("--package-root") 32 | parser.add_option("--version") 33 | options, sys.argv[1:] = parser.parse_args() 34 | for option in "package_root", "version": 35 | if not getattr(options, option): 36 | parser.error("--{} is required.".format(option)) 37 | return options 38 | 39 | 40 | def DataFiles(prefix_in_pkg, root_dir): 41 | result = [] 42 | for path in pathlib.Path(root_dir).glob("**/*"): 43 | if path.is_dir(): 44 | continue 45 | relative = path.relative_to(root_dir) 46 | result.append((str(prefix_in_pkg / relative.parent), [str(path)])) 47 | return result 48 | 49 | 50 | options = GetOptions() 51 | 52 | setup( 53 | name="fleetspeak-client-bin", 54 | version=options.version, 55 | packages=[], 56 | cmdclass={ 57 | "bdist_wheel": BdistWheel, 58 | }, 59 | data_files=DataFiles("fleetspeak-client-bin", options.package_root), 60 | ) 61 | -------------------------------------------------------------------------------- /fleetspeak/client-win/fleetspeak.wxs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /fleetspeak/generate_protos.sh: -------------------------------------------------------------------------------- 1 | # Exit on error. 2 | set -e 3 | 4 | echo 'Transpiling required protos to Go and Python.' 5 | 6 | # Go to this script's directory. 7 | cd "$(/usr/bin/dirname "$(/bin/readlink -e "${0}")")" 8 | 9 | # Check dependencies. 10 | ./generate_protos_setup.sh check 11 | 12 | find .. -name '*.proto' -print0 | xargs -0 -t -I % protoc \ 13 | --go_out=.. --go-grpc_out=.. \ 14 | --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative \ 15 | --proto_path=.. % 16 | -------------------------------------------------------------------------------- /fleetspeak/generate_protos_setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | PROTOC_VER="26.1" # https://github.com/protocolbuffers/protobuf/releases 5 | GEN_GO_VER="1.35.1" # https://pkg.go.dev/google.golang.org/protobuf/cmd/protoc-gen-go?tab=versions 6 | GEN_GO_GRPC_VER="1.5.1" # https://pkg.go.dev/google.golang.org/grpc/cmd/protoc-gen-go-grpc?tab=versions 7 | 8 | function err() { 9 | echo "$*" >&2 10 | } 11 | 12 | # Installs protoc from https://github.com/protocolbuffers/protobuf/releases to 13 | # ~/.local/bin. Well known proto types are placed in ~/.local/include. 14 | # Globals: 15 | # PROTOC_VER 16 | # Arguments: 17 | # None 18 | function install_protoc() { 19 | local temp_zip 20 | temp_zip="$(mktemp)" 21 | trap "rm -f ${temp_zip}" EXIT 22 | curl -L "https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VER}/protoc-${PROTOC_VER}-linux-x86_64.zip" -o "${temp_zip}" 23 | unzip -o "${temp_zip}" -x readme.txt -d "${HOME}/.local" 24 | echo "Installed protoc v${PROTOC_VER}. Ensure ${HOME}/.local/bin is in your PATH." 25 | } 26 | 27 | if [[ "$(protoc --version)" != "libprotoc ${PROTOC_VER}" ]]; then 28 | if [[ "$1" == "check" ]]; then 29 | err "Wrong protobuf version, please run fleetspeak/generate_protos_setup.sh" 30 | exit 1 31 | fi 32 | install_protoc 33 | fi 34 | 35 | if [[ "$(protoc-gen-go --version)" != "protoc-gen-go v${GEN_GO_VER}" ]]; then 36 | if [[ "$1" == "check" ]]; then 37 | err "Wrong protoc-gen-go version, please run fleetspeak/generate_protos_setup.sh" 38 | exit 1 39 | fi 40 | go install "google.golang.org/protobuf/cmd/protoc-gen-go@v${GEN_GO_VER}" 41 | fi 42 | 43 | if [[ "$(protoc-gen-go-grpc --version)" != "protoc-gen-go-grpc ${GEN_GO_GRPC_VER}" ]]; then 44 | if [[ "$1" == "check" ]]; then 45 | err "Wrong protoc-gen-go-grpc version, please run fleetspeak/generate_protos_setup.sh" 46 | exit 1 47 | fi 48 | go install "google.golang.org/grpc/cmd/protoc-gen-go-grpc@v${GEN_GO_GRPC_VER}" 49 | fi 50 | -------------------------------------------------------------------------------- /fleetspeak/server-pkg-tmpl/debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /fleetspeak/server-pkg-tmpl/debian/conffiles: -------------------------------------------------------------------------------- 1 | /etc/fleetspeak-server/server.services.config 2 | -------------------------------------------------------------------------------- /fleetspeak/server-pkg-tmpl/debian/control: -------------------------------------------------------------------------------- 1 | Source: fleetspeak-server 2 | Maintainer: GRR Dev 3 | Build-Depends: debhelper (>= 9), debhelper (>= 9.20160709), devscripts 4 | Standards-Version: 3.8.3 5 | Homepage: https://github.com/google/fleetspeak 6 | 7 | Package: fleetspeak-server 8 | Architecture: any 9 | Pre-Depends: 10 | Depends: adduser, systemd 11 | Description: server for the Fleetspeak communications framework 12 | Fleetspeak is a framework for communicating with a fleet of machines, with a 13 | focus on security and administrative tasks. 14 | -------------------------------------------------------------------------------- /fleetspeak/server-pkg-tmpl/debian/docs: -------------------------------------------------------------------------------- 1 | usr/share/doc/fleetspeak-server/* -------------------------------------------------------------------------------- /fleetspeak/server-pkg-tmpl/debian/fleetspeak-server.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Fleetspeak Server Service 3 | After=syslog.target network.target 4 | After=mysql.service 5 | Documentation=https://github.com/google/fleetspeak 6 | ConditionPathExists=!/etc/fleetspeak-server/disabled 7 | 8 | [Service] 9 | User=fleetspeak 10 | Group=fleetspeak 11 | ExecStart=/usr/bin/fleetspeak-server --services_config /etc/fleetspeak-server/server.services.config --components_config /etc/fleetspeak-server/server.components.config 12 | Restart=always 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /fleetspeak/server-pkg-tmpl/debian/install: -------------------------------------------------------------------------------- 1 | etc/fleetspeak-server/server.services.config etc/fleetspeak-server 2 | etc/fleetspeak-server/configurator.config etc/fleetspeak-server 3 | usr/bin/fleetspeak-server usr/bin 4 | usr/bin/fleetspeak-config usr/bin 5 | usr/bin/fleetspeak-admin usr/bin -------------------------------------------------------------------------------- /fleetspeak/server-pkg-tmpl/debian/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Post-installation script for the Fleetspeak server deb. 4 | # 5 | 6 | set -e 7 | 8 | # The token below is replaced with shellscript snippets generated 9 | # by debhelper commands. See http://manpages.ubuntu.com/dh_installdeb 10 | 11 | #DEBHELPER# 12 | 13 | case "$1" in 14 | configure) 15 | adduser --system fleetspeak 16 | groupadd --system -f fleetspeak 17 | chown -R fleetspeak:fleetspeak /etc/fleetspeak-server 18 | 19 | # Allow "fleetspeak" user to bind to a low port (443). 20 | setcap 'cap_net_bind_service=+ep' /usr/bin/fleetspeak-server 21 | ;; 22 | esac 23 | -------------------------------------------------------------------------------- /fleetspeak/server-pkg-tmpl/debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | 4 | # See this article for ideas re Go packaging in Debian: 5 | # https://vincent.bernat.ch/en/blog/2016-pragmatic-debian-packaging 6 | 7 | # Uncomment this to turn on verbose mode. 8 | # export DH_VERBOSE=1 9 | 10 | %: 11 | dh $@ --with systemd 12 | -------------------------------------------------------------------------------- /fleetspeak/server-pkg-tmpl/etc/fleetspeak-server/server.services.config: -------------------------------------------------------------------------------- 1 | # Fleetspeak server configuration file. This is a text format 2 | # fleetspeak.server.ServerConfig protocol buffer. 3 | 4 | # The primary purpose of this file is to determine where messages for each 5 | # service go. Each 'services' block routes message for a particular service. 6 | 7 | # This example sends GRR messages to a server over GRPC without authentication 8 | # or encryption - it should only be used when the GRR server is bound to 9 | # localhost or otherwise firewalled away from general access. 10 | #services { 11 | # name: "GRR" 12 | # factory: "GRPC" 13 | # config { 14 | # [type.googleapis.com/fleetspeak.grpcservce.Config] { 15 | # target: "localhost:" 16 | # insecure: true 17 | # } 18 | # } 19 | #} 20 | -------------------------------------------------------------------------------- /fleetspeak/server-pkg-tmpl/usr/share/doc/fleetspeak-server/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: Fleetspeak 3 | Upstream-Contact: GRR Dev 4 | Source: https://github.com/google/fleetspeak 5 | 6 | Files: * 7 | Copyright: 2017-2018 Google 8 | License: Apache-2.0 9 | -------------------------------------------------------------------------------- /fleetspeak/server-wheel/setup.py: -------------------------------------------------------------------------------- 1 | from optparse import OptionParser 2 | import pathlib 3 | import sys 4 | 5 | from setuptools import setup 6 | from wheel.bdist_wheel import bdist_wheel 7 | 8 | 9 | class BdistWheel(bdist_wheel): 10 | user_options = [ 11 | ("platform-name=", None, "Platform name to force."), 12 | ] 13 | 14 | def initialize_options(self): 15 | self.platform_name = None 16 | bdist_wheel.initialize_options(self) 17 | 18 | def finalize_options(self): 19 | bdist_wheel.finalize_options(self) 20 | self.root_is_pure = False 21 | 22 | def get_tag(self): 23 | impl, abi_tag, plat_name = bdist_wheel.get_tag(self) 24 | if self.platform_name is not None: 25 | plat_name = self.platform_name 26 | return "py2.py3", "none", plat_name 27 | 28 | 29 | def GetOptions(): 30 | parser = OptionParser() 31 | parser.add_option("--package-root") 32 | parser.add_option("--version") 33 | options, sys.argv[1:] = parser.parse_args() 34 | for option in "package_root", "version": 35 | if not getattr(options, option): 36 | parser.error("--{} is required.".format(option)) 37 | return options 38 | 39 | 40 | def DataFiles(prefix_in_pkg, root_dir): 41 | result = [] 42 | for path in pathlib.Path(root_dir).glob("**/*"): 43 | if path.is_dir(): 44 | continue 45 | relative = path.relative_to(root_dir) 46 | result.append((str(prefix_in_pkg / relative.parent), [str(path)])) 47 | return result 48 | 49 | 50 | options = GetOptions() 51 | 52 | setup( 53 | name="fleetspeak-server-bin", 54 | version=options.version, 55 | packages=[], 56 | cmdclass={ 57 | "bdist_wheel": BdistWheel, 58 | }, 59 | data_files=DataFiles("fleetspeak-server-bin", options.package_root), 60 | ) 61 | -------------------------------------------------------------------------------- /fleetspeak/src/client/channel/proto/fleetspeak_channel/channel.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package fleetspeak.channel; 4 | 5 | option go_package = "github.com/google/fleetspeak/fleetspeak/src/client/channel/proto/fleetspeak_channel"; 6 | 7 | // Optional first message sent through a channel when it is created. It is meant 8 | // to contain info about the process that the other end of the channel might 9 | // find useful. 10 | message StartupData { 11 | // Self-reported PID. 12 | int64 pid = 1; 13 | 14 | // Self-reported service version string. 15 | string version = 2; 16 | } 17 | -------------------------------------------------------------------------------- /fleetspeak/src/client/clienttestutils/clienttestutils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package clienttestutils contains utility functions for the client test, in part platform-specific. 16 | package clienttestutils 17 | 18 | import ( 19 | "fmt" 20 | "os" 21 | "path/filepath" 22 | 23 | "google.golang.org/protobuf/encoding/prototext" 24 | "google.golang.org/protobuf/proto" 25 | 26 | fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak" 27 | ) 28 | 29 | // WriteSignedServiceConfig saves the given signed config to a test location. 30 | func WriteSignedServiceConfig(dirpath, filename string, cfg *fspb.SignedClientServiceConfig) error { 31 | b, err := proto.Marshal(cfg) 32 | if err != nil { 33 | return fmt.Errorf("unable to serialize signed service config: %v", err) 34 | } 35 | 36 | configPath := filepath.Join(dirpath, filename) 37 | if err := os.WriteFile(configPath, b, 0644); err != nil { 38 | return fmt.Errorf("unable to write signed service config[%s]: %v", configPath, err) 39 | } 40 | 41 | return nil 42 | } 43 | 44 | // WriteServiceConfig saves the given config to a test location. 45 | func WriteServiceConfig(dirpath, filename string, cfg *fspb.ClientServiceConfig) error { 46 | b, err := prototext.Marshal(cfg) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | configPath := filepath.Join(dirpath, filename) 52 | if err := os.WriteFile(configPath, b, 0644); err != nil { 53 | return fmt.Errorf("unable to write service config[%s]: %v", configPath, err) 54 | } 55 | 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /fleetspeak/src/client/config/noop_persistence_handler.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package config 16 | 17 | import ( 18 | "errors" 19 | 20 | clpb "github.com/google/fleetspeak/fleetspeak/src/client/proto/fleetspeak_client" 21 | fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak" 22 | ) 23 | 24 | // NoopPersistenceHandler indicates that this client should not attempt 25 | // to maintain state across restarts. If used, every execution will identify 26 | // itself as a new client. 27 | // 28 | // Intended for testing and specialized applications. 29 | type NoopPersistenceHandler struct{} 30 | 31 | // NewNoopPersistenceHandler instantiates a NoopPersistenceHandler. 32 | func NewNoopPersistenceHandler() *NoopPersistenceHandler { 33 | return &NoopPersistenceHandler{} 34 | } 35 | 36 | // ReadState implements PersistenceHandler. 37 | func (*NoopPersistenceHandler) ReadState() (*clpb.ClientState, error) { 38 | return &clpb.ClientState{}, nil 39 | } 40 | 41 | // WriteState implements PersistenceHandler. 42 | func (*NoopPersistenceHandler) WriteState(s *clpb.ClientState) error { 43 | return nil 44 | } 45 | 46 | // ReadCommunicatorConfig implements PersistenceHandler. 47 | func (*NoopPersistenceHandler) ReadCommunicatorConfig() (*clpb.CommunicatorConfig, error) { 48 | return nil, nil 49 | } 50 | 51 | // ReadSignedServices implements PersistenceHandler. 52 | func (*NoopPersistenceHandler) ReadSignedServices() ([]*fspb.SignedClientServiceConfig, error) { 53 | return nil, nil 54 | } 55 | 56 | // ReadServices implements PersistenceHandler. 57 | func (*NoopPersistenceHandler) ReadServices() ([]*fspb.ClientServiceConfig, error) { 58 | return nil, nil 59 | } 60 | 61 | // SaveSignedService implements PersistenceHandler. 62 | func (*NoopPersistenceHandler) SaveSignedService(*fspb.SignedClientServiceConfig) error { 63 | return errors.New("not implemented") 64 | } 65 | -------------------------------------------------------------------------------- /fleetspeak/src/client/daemonservice/command/command_unix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build linux || darwin 16 | 17 | package command 18 | 19 | import ( 20 | "fmt" 21 | "os" 22 | 23 | "golang.org/x/sys/unix" 24 | ) 25 | 26 | func (cmd *Command) softKill() error { 27 | return unix.Kill(cmd.Process.Pid, unix.SIGINT) 28 | } 29 | 30 | func (cmd *Command) kill() error { 31 | return unix.Kill(cmd.Process.Pid, unix.SIGKILL) 32 | } 33 | 34 | func (cmd *Command) addInPipeFDImpl() (*os.File, int, error) { 35 | pr, pw, err := os.Pipe() 36 | if err != nil { 37 | return nil, 0, fmt.Errorf("error in os.Pipe: %v", err) 38 | } 39 | 40 | cmd.ExtraFiles = append(cmd.ExtraFiles, pr) 41 | cmd.filesToClose = append(cmd.filesToClose, pr) 42 | 43 | // Starts with 3. 44 | // See: https://golang.org/pkg/os/exec/#Cmd 45 | fd := len(cmd.ExtraFiles) + 2 46 | 47 | return pw, fd, nil 48 | } 49 | 50 | func (cmd *Command) addOutPipeFDImpl() (*os.File, int, error) { 51 | pr, pw, err := os.Pipe() 52 | if err != nil { 53 | return nil, 0, fmt.Errorf("error in os.Pipe: %v", err) 54 | } 55 | 56 | cmd.ExtraFiles = append(cmd.ExtraFiles, pw) 57 | cmd.filesToClose = append(cmd.filesToClose, pw) 58 | 59 | // Starts with 3. 60 | // See: https://golang.org/pkg/os/exec/#Cmd 61 | fd := len(cmd.ExtraFiles) + 2 62 | 63 | return pr, fd, nil 64 | } 65 | -------------------------------------------------------------------------------- /fleetspeak/src/client/daemonservice/command/command_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build windows 16 | 17 | package command 18 | 19 | import ( 20 | "fmt" 21 | "os" 22 | "syscall" 23 | ) 24 | 25 | func (cmd *Command) softKill() error { 26 | return cmd.Kill() 27 | } 28 | 29 | func (cmd *Command) kill() error { 30 | return cmd.Cmd.Process.Kill() 31 | } 32 | 33 | func (cmd *Command) addInPipeFDImpl() (*os.File, int, error) { 34 | pr, pw, err := os.Pipe() 35 | if err != nil { 36 | return nil, 0, fmt.Errorf("error in os.Pipe: %v", err) 37 | } 38 | 39 | fd := pr.Fd() 40 | syscall.SetHandleInformation(syscall.Handle(fd), syscall.HANDLE_FLAG_INHERIT, 1) 41 | cmd.filesToClose = append(cmd.filesToClose, pr) 42 | 43 | if cmd.SysProcAttr == nil { 44 | cmd.SysProcAttr = &syscall.SysProcAttr{} 45 | } 46 | cmd.SysProcAttr.AdditionalInheritedHandles = append(cmd.SysProcAttr.AdditionalInheritedHandles, syscall.Handle(fd)) 47 | 48 | return pw, int(fd), nil 49 | } 50 | 51 | func (cmd *Command) addOutPipeFDImpl() (*os.File, int, error) { 52 | pr, pw, err := os.Pipe() 53 | if err != nil { 54 | return nil, 0, fmt.Errorf("error in os.Pipe: %v", err) 55 | } 56 | 57 | fd := pw.Fd() 58 | syscall.SetHandleInformation(syscall.Handle(fd), syscall.HANDLE_FLAG_INHERIT, 1) 59 | cmd.filesToClose = append(cmd.filesToClose, pw) 60 | 61 | if cmd.SysProcAttr == nil { 62 | cmd.SysProcAttr = &syscall.SysProcAttr{} 63 | } 64 | cmd.SysProcAttr.AdditionalInheritedHandles = append(cmd.SysProcAttr.AdditionalInheritedHandles, syscall.Handle(fd)) 65 | 66 | return pr, int(fd), nil 67 | } 68 | -------------------------------------------------------------------------------- /fleetspeak/src/client/daemonservice/daemonservice_oss_test.go: -------------------------------------------------------------------------------- 1 | //go:build !google_internal 2 | 3 | package daemonservice 4 | 5 | import ( 6 | "runtime" 7 | "testing" 8 | ) 9 | 10 | func testClient(t *testing.T) []string { 11 | if runtime.GOOS == "windows" { 12 | return []string{`testclient\testclient.exe`} 13 | } 14 | 15 | return []string{"testclient/testclient"} 16 | } 17 | 18 | func testClientPY(t *testing.T) []string { 19 | return []string{"python", "-m", "fleetspeak.client_connector.testing.testclient"} 20 | } 21 | 22 | func testClientLauncherPY(t *testing.T) []string { 23 | return []string{"python", "-m", "fleetspeak.client_connector.testing.testclient_launcher"} 24 | } 25 | -------------------------------------------------------------------------------- /fleetspeak/src/client/daemonservice/execution/execution_oss_test.go: -------------------------------------------------------------------------------- 1 | //go:build !google_internal 2 | 3 | package execution 4 | 5 | import ( 6 | "runtime" 7 | "testing" 8 | ) 9 | 10 | func testClient(t *testing.T) string { 11 | if runtime.GOOS == "windows" { 12 | return `..\testclient\testclient.exe` 13 | } 14 | 15 | return "../testclient/testclient" 16 | } 17 | -------------------------------------------------------------------------------- /fleetspeak/src/client/daemonservice/proto/fleetspeak_daemonservice/messages.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package fleetspeak.daemonservice; 4 | 5 | option go_package = "github.com/google/fleetspeak/fleetspeak/src/client/daemonservice/proto/fleetspeak_daemonservice"; 6 | 7 | // A fleetspeak.Message with message type "StdOutput" and data type 8 | // StdOutputData is sent by a daemon service to the server when the daemon 9 | // subprocess produces output on stdout or stderr. 10 | message StdOutputData { 11 | // The pid of the daemon process. 12 | int64 pid = 1; 13 | 14 | // The index of this message within the set of messages returned for 15 | // this pid. 16 | int64 message_index = 2; 17 | 18 | bytes stdout = 3; 19 | bytes stderr = 4; 20 | } 21 | -------------------------------------------------------------------------------- /fleetspeak/src/client/entry/entry.go: -------------------------------------------------------------------------------- 1 | // Package entry provides platform-specific wrappers around the application's 2 | // entry points to manage its lifecycle. 3 | package entry 4 | 5 | import ( 6 | "context" 7 | "os" 8 | "time" 9 | ) 10 | 11 | // Timeout for shutting down gracefully. 12 | // 13 | // If the [InnerMain] function does not return within this time, the process 14 | // may be shut down ungracefully. 15 | const shutdownTimeout = 10 * time.Second 16 | 17 | // InnerMain is an inner entry function responsible for creating a 18 | // [client.Client] and managing its configuration and lifecycle. It is called by 19 | // [RunMain] which handles platform-specific mechanics to manage the passed 20 | // [Context]. 21 | // The [cfgReloadSignals] channel gets a [syscall.SIGHUP] when a config reload 22 | // is requested. We use UNIX conventions here, the Windows layer can send a 23 | // [syscall.SIGHUP] when appropriate. 24 | type InnerMain func(ctx context.Context, cfgReloadSignals <-chan os.Signal) error 25 | -------------------------------------------------------------------------------- /fleetspeak/src/client/flow/filter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package flow contains structures and utility method relating to client-server 16 | // flow control configuration. 17 | package flow 18 | 19 | import "sync/atomic" 20 | 21 | const ( 22 | lowFiltered int32 = 1 << iota 23 | medFiltered 24 | highFiltered 25 | ) 26 | 27 | // Filter is a thread safe data structure which remembers 3 booleans, used to 28 | // indicate which of three priority levels should be filtered. The underlying 29 | // type should be seen as an implementation detail. 30 | type Filter int32 31 | 32 | // NewFilter returns a Filter which is not set to filter anything. 33 | func NewFilter() *Filter { 34 | return new(Filter) 35 | } 36 | 37 | // Set sets the filter bits according to the provided booleans. 38 | func (f *Filter) Set(low, medium, high bool) { 39 | var i int32 40 | if low { 41 | i |= lowFiltered 42 | } 43 | if medium { 44 | i |= medFiltered 45 | } 46 | if high { 47 | i |= highFiltered 48 | } 49 | atomic.StoreInt32((*int32)(f), i) 50 | } 51 | 52 | // Get returns the current state of the filter. 53 | func (f *Filter) Get() (low, medium, high bool) { 54 | i := atomic.LoadInt32((*int32)(f)) 55 | return (i&lowFiltered != 0), (i&medFiltered != 0), (i&highFiltered != 0) 56 | } 57 | -------------------------------------------------------------------------------- /fleetspeak/src/client/flow/filter_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package flow 16 | 17 | import ( 18 | "testing" 19 | ) 20 | 21 | func TestFilter(t *testing.T) { 22 | f := NewFilter() 23 | 24 | l, m, h := f.Get() 25 | if l != false || m != false || h != false { 26 | t.Errorf("Filter should start (false,false,false), got (%v, %v, %v)", l, m, h) 27 | } 28 | 29 | f.Set(true, false, false) 30 | l, m, h = f.Get() 31 | if l != true || m != false || h != false { 32 | t.Errorf("Filter should be (true,false,false), got (%v, %v, %v)", l, m, h) 33 | } 34 | 35 | f.Set(true, true, false) 36 | l, m, h = f.Get() 37 | if l != true || m != true || h != false { 38 | t.Errorf("Filter should be (true,true,false), got (%v, %v, %v)", l, m, h) 39 | } 40 | 41 | f.Set(true, true, true) 42 | l, m, h = f.Get() 43 | if l != true || m != true || h != true { 44 | t.Errorf("Filter should be (true,true,true), got (%v, %v, %v)", l, m, h) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /fleetspeak/src/client/generic/config_unix.go: -------------------------------------------------------------------------------- 1 | //go:build linux || darwin 2 | 3 | package generic 4 | 5 | import ( 6 | "errors" 7 | "fmt" 8 | 9 | "github.com/google/fleetspeak/fleetspeak/src/client/config" 10 | 11 | gpb "github.com/google/fleetspeak/fleetspeak/src/client/generic/proto/fleetspeak_client_generic" 12 | ) 13 | 14 | func makePersistenceHandler(cfg *gpb.Config) (config.PersistenceHandler, error) { 15 | if cfg.PersistenceHandler == nil { 16 | return nil, errors.New("persistence_handler is required") 17 | } 18 | switch h := cfg.PersistenceHandler.(type) { 19 | case *gpb.Config_FilesystemHandler: 20 | return config.NewFilesystemPersistenceHandler(h.FilesystemHandler.ConfigurationDirectory, h.FilesystemHandler.StateFile) 21 | default: 22 | return nil, fmt.Errorf("persistence_handler has unsupported type: %T", cfg.PersistenceHandler) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /fleetspeak/src/client/generic/config_windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package generic 4 | 5 | import ( 6 | "errors" 7 | "fmt" 8 | 9 | "github.com/google/fleetspeak/fleetspeak/src/client/config" 10 | 11 | gpb "github.com/google/fleetspeak/fleetspeak/src/client/generic/proto/fleetspeak_client_generic" 12 | ) 13 | 14 | func makePersistenceHandler(cfg *gpb.Config) (config.PersistenceHandler, error) { 15 | if cfg.PersistenceHandler == nil { 16 | return nil, errors.New("persistence_handler is required") 17 | } 18 | switch h := cfg.PersistenceHandler.(type) { 19 | case *gpb.Config_FilesystemHandler: 20 | return config.NewFilesystemPersistenceHandler(h.FilesystemHandler.ConfigurationDirectory, h.FilesystemHandler.StateFile) 21 | case *gpb.Config_RegistryHandler: 22 | return config.NewWindowsRegistryPersistenceHandler(h.RegistryHandler.ConfigurationKey, false) 23 | default: 24 | return nil, fmt.Errorf("persistence_handler has unsupported type: %T", cfg.PersistenceHandler) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /fleetspeak/src/client/generic/proto/fleetspeak_client_generic/config.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package fleetspeak.client.generic; 4 | 5 | option go_package = "github.com/google/fleetspeak/fleetspeak/src/client/generic/proto/fleetspeak_client_generic"; 6 | 7 | message Config { 8 | // One or more PEM encoded certificates that the client should trust, 9 | // typically a CA certificate specific to the installation. 10 | string trusted_certs = 1; 11 | 12 | // The servers that the client should attempt to connect to in : 13 | // format. E.g. "lazy.com:443", "10.0.0.5:1234" 14 | repeated string server = 2; 15 | 16 | // The client labels that this client should present to the server. Labels 17 | // indicating the client architecture and OS are automatically included. 18 | repeated string client_label = 3; 19 | 20 | oneof persistence_handler { 21 | FilesystemHandler filesystem_handler = 4; 22 | RegistryHandler registry_handler = 5; 23 | } 24 | 25 | // If set, the client will use long running persistent connections, otherwise 26 | // it will make regular short lived polls to the server. Recommended. 27 | bool streaming = 6; 28 | 29 | // If provided, proxy used for connecting to the server. 30 | // The format is a URL. 31 | // See https://golang.org/pkg/net/http/#Transport.Proxy for details. 32 | string proxy = 7; 33 | 34 | // The name of the HTTP header that the client uses to pass its certificate to 35 | // the server frontend for identification. Required only if the server 36 | // frontend is configured to use https_header_checksum_config. 37 | string client_certificate_header = 8; 38 | 39 | // If set, used for SNI and certificate validation. 40 | string server_name = 9; 41 | } 42 | 43 | message FilesystemHandler { 44 | // Where to persist client state, see NewFilesystemPersistenceHandler for 45 | // details: 46 | // 47 | // https://godoc.org/github.com/google/fleetspeak/fleetspeak/src/client/config#FilesystemPersistenceHandler 48 | string configuration_directory = 1; 49 | string state_file = 2; 50 | } 51 | 52 | message RegistryHandler { 53 | // Where to persist client state, see NewWindowsRegistryPersistenceHandler 54 | // for details: 55 | // 56 | // https://github.com/google/fleetspeak/blob/master/fleetspeak/src/client/config/windows_registry_persistence_handler.go 57 | string configuration_key = 1; 58 | } 59 | -------------------------------------------------------------------------------- /fleetspeak/src/client/https/compression.go: -------------------------------------------------------------------------------- 1 | package https 2 | 3 | import ( 4 | "compress/zlib" 5 | "io" 6 | "net/http" 7 | 8 | fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak" 9 | ) 10 | 11 | // BodyWriter is an interface that groups Write, Flush, and Close functions. 12 | type BodyWriter interface { 13 | io.WriteCloser 14 | Flush() error 15 | } 16 | 17 | // CompressingWriter returns a BodyWriter that will compress the data written to 18 | // it with the given compression algorithm and write it to w. 19 | func CompressingWriter(w io.Writer, compression fspb.CompressionAlgorithm) BodyWriter { 20 | switch compression { 21 | case fspb.CompressionAlgorithm_COMPRESSION_DEFLATE: 22 | return zlib.NewWriter(w) 23 | default: 24 | return nopWriteCloseFlusherTo{w} 25 | } 26 | } 27 | 28 | type nopWriteCloseFlusherTo struct { 29 | io.Writer 30 | } 31 | 32 | func (nopWriteCloseFlusherTo) Flush() error { 33 | return nil 34 | } 35 | 36 | func (nopWriteCloseFlusherTo) Close() error { 37 | return nil 38 | } 39 | 40 | // SetContentEncoding sets the Content-Encoding header on req to the appropriate 41 | // value for the given compression algorithm. 42 | func SetContentEncoding(h http.Header, compression fspb.CompressionAlgorithm) { 43 | switch compression { 44 | case fspb.CompressionAlgorithm_COMPRESSION_DEFLATE: 45 | h.Set("Content-Encoding", "deflate") 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /fleetspeak/src/client/internal/process/process.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package process encapsulates OS-independent process management helpers. 16 | package process 17 | 18 | import ( 19 | "os" 20 | ) 21 | 22 | // KillByPid causes the Process to exit immediately. It does not wait 23 | // until the Process has actually exited. This only kills the Process 24 | // itself, not any other processes it may have started. If the process 25 | // can't be found at the moment of the call, KillByPid returns nil. 26 | func KillByPid(pid int) error { 27 | p, err := os.FindProcess(pid) 28 | if err != nil { 29 | return nil 30 | } 31 | 32 | return p.Kill() 33 | } 34 | -------------------------------------------------------------------------------- /fleetspeak/src/client/proto/fleetspeak_client/api.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package fleetspeak.client; 4 | 5 | import "google/protobuf/any.proto"; 6 | 7 | option go_package = "github.com/google/fleetspeak/fleetspeak/src/client/proto/fleetspeak_client"; 8 | 9 | message ByteBlob { 10 | bytes data = 1; 11 | } 12 | 13 | message APIMessage { 14 | string type = 1; 15 | google.protobuf.Any data = 2; 16 | } 17 | -------------------------------------------------------------------------------- /fleetspeak/src/client/proto/fleetspeak_client/client.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package fleetspeak.client; 4 | 5 | import "fleetspeak/src/common/proto/fleetspeak/common.proto"; 6 | 7 | option go_package = "github.com/google/fleetspeak/fleetspeak/src/client/proto/fleetspeak_client"; 8 | 9 | // Parameters used to configure communicator plugins. 10 | message CommunicatorConfig { 11 | // The longest possible wait before attempting to contact the server. The 12 | // communicator may poll sooner if there are messages for the server, of it 13 | // there has been recent communication with the server. 14 | // 15 | // A default of 5 minutes is used if unset. 16 | int32 max_poll_delay_seconds = 2; 17 | 18 | // The longest possible wait before attempting to contact the server when 19 | // there is some data to transmit. The communicator may poll sooner if the 20 | // buffer is full. 21 | // 22 | // A default of 5 seconds is used if unset. 23 | int32 max_buffer_delay_seconds = 3; 24 | 25 | // The minimum time to wait after a failure to reach any server. 26 | // 27 | // A default of 5 minutes is used if unset. 28 | int32 min_failure_delay_seconds = 4; 29 | 30 | // If the communicator is unable to communicate with the server for this long, 31 | // it should kill fleetspeak, in the hope that a restart will fix things. 32 | // 33 | // A default of 7 days is used if unset. 34 | int32 failure_suicide_time_seconds = 5; 35 | 36 | // The compression algorithm to apply on data sent to the server. 37 | // 38 | // No compression is applied if unset. 39 | fleetspeak.CompressionAlgorithm compression = 6; 40 | 41 | // If set, the client will prefer comms with HTTP2 Transport 42 | bool prefer_http2 = 7; 43 | } 44 | 45 | // ClientState contains the state of the client which should be persisted across 46 | // restarts. 47 | message ClientState { 48 | // The client key, normally fixed after first execution. Also defines the 49 | // client's id. 50 | bytes client_key = 1; 51 | 52 | // The most recent sequencing nonce received from the server. 53 | uint64 sequencing_nonce = 7; 54 | 55 | // A set of revoked/blacklisted certificate serial numbers in big endian 56 | // format. Not restricted, but normally at most 20 bytes. (RFC 3280) 57 | repeated bytes revoked_cert_serials = 8; 58 | } 59 | -------------------------------------------------------------------------------- /fleetspeak/src/client/signer/signer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package signer defines an interface to add additional signatures to 16 | // communications with the Fleetspeak server. 17 | package signer 18 | 19 | import fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak" 20 | 21 | // A Signer is given a chance to sign all data sent to the server. 22 | type Signer interface { 23 | // SignContact attempts to produce a signature of data which 24 | // will be included when sending it to the server. 25 | // 26 | // SignContact may return nil if the intended certificate is currently 27 | // unavailable. Otherwise it should return a proto with all fields set. 28 | SignContact(data []byte) *fspb.Signature 29 | } 30 | -------------------------------------------------------------------------------- /fleetspeak/src/client/socketservice/client/proxy_unix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build linux || darwin 16 | 17 | package client 18 | 19 | import ( 20 | "net" 21 | "time" 22 | 23 | log "github.com/golang/glog" 24 | "github.com/google/fleetspeak/fleetspeak/src/client/channel" 25 | "github.com/google/fleetspeak/fleetspeak/src/client/socketservice/checks" 26 | ) 27 | 28 | // buildChannel is almost a channel.Builder, meant to be wrapped to become one 29 | // once socketPath is known. 30 | func buildChannel(socketPath string) (*channel.Channel, func()) { 31 | // Try once per second until the fleetspeak server is available. 32 | var err error 33 | var conn *net.UnixConn 34 | 35 | retryDelay := time.Second 36 | for { 37 | if err = checks.CheckSocketFile(socketPath); err != nil { 38 | log.Warningf("failure checking perms of [%s], will retry: %v", socketPath, err) 39 | } else if conn, err = net.DialUnix("unix", nil, &net.UnixAddr{Name: socketPath, Net: "unix"}); err != nil { 40 | log.Warningf("failed to connect to [%s], will retry: %v", socketPath, err) 41 | } else { 42 | log.Infof("connected to [%s]", socketPath) 43 | return channel.New(conn, conn), func() { 44 | // Because it is a socket, this is sufficient to terminate any pending 45 | // I/O operations. 46 | conn.Close() 47 | } 48 | } 49 | time.Sleep(retryDelay) 50 | retryDelay = backOffChannelRetryDelay(retryDelay) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /fleetspeak/src/client/socketservice/client/proxy_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build windows 16 | 17 | package client 18 | 19 | import ( 20 | "net" 21 | "time" 22 | 23 | log "github.com/golang/glog" 24 | "github.com/google/fleetspeak/fleetspeak/src/client/channel" 25 | "github.com/google/fleetspeak/fleetspeak/src/client/socketservice/checks" 26 | "github.com/google/fleetspeak/fleetspeak/src/windows/wnixsocket" 27 | ) 28 | 29 | // buildChannel is almost a channel.Builder, meant to be wrapped to become one 30 | // once socketPath is known. 31 | func buildChannel(socketPath string) (*channel.Channel, func()) { 32 | // Try once per second until the fleetspeak server is available. 33 | var err error 34 | var conn net.Conn 35 | 36 | retryDelay := time.Second 37 | for { 38 | if err = checks.CheckSocketFile(socketPath); err != nil { 39 | log.Warningf("Failure checking perms of [%s], will retry: %v", socketPath, err) 40 | } else if conn, err = wnixsocket.Dial(socketPath, time.Second); err != nil { 41 | log.Warningf("Failed to connect to [%s], will retry: %v", socketPath, err) 42 | } else { 43 | log.Infof("Connected to [%s]", socketPath) 44 | 45 | return channel.New(conn, conn), func() { 46 | // Because it is a socket, this is sufficient to terminate any pending 47 | // I/O operations. 48 | conn.Close() 49 | } 50 | } 51 | time.Sleep(retryDelay) 52 | retryDelay = backOffChannelRetryDelay(retryDelay) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /fleetspeak/src/client/socketservice/proto/fleetspeak_socketservice/config.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package fleetspeak.socketservice; 4 | 5 | option go_package = "github.com/google/fleetspeak/fleetspeak/src/client/socketservice/proto/fleetspeak_socketservice"; 6 | 7 | // The configuration information expected by socketservice.Factory in 8 | // ClientServiceConfig.config. 9 | message Config { 10 | // The given api_proxy_path may be an arbitrary filesystem path and will be 11 | // used to pair the daemon service with its non-child client process. 12 | // 13 | // On Unix in particular, a Unix socket will be created at this path and used 14 | // for communication between FS and the client. 15 | // 16 | // Side note: FS requires the proxy's parent directory's perms to be 0700. 17 | // If the parent directory doesn't exist, FS will mkdir -p it with perms set 18 | // to 0700. 19 | string api_proxy_path = 1; 20 | 21 | // By default, socket services report resource usage every 10 minutes. This 22 | // flag disables this if set. 23 | bool disable_resource_monitoring = 2; 24 | 25 | // How many samples to aggregate into a report when monitoring resource usage. 26 | // If unset, defaults to 20. 27 | int32 resource_monitoring_sample_size = 3; 28 | 29 | // How long to wait between resource monitoring samples. If unset, defaults to 30 | // 30 seconds. 31 | int32 resource_monitoring_sample_period_seconds = 4; 32 | } 33 | -------------------------------------------------------------------------------- /fleetspeak/src/client/socketservice/socketservice_oss_test.go: -------------------------------------------------------------------------------- 1 | //go:build !google_internal 2 | 3 | package socketservice 4 | 5 | import "testing" 6 | 7 | func testClient(t *testing.T) string { 8 | _ = t // intentionally unused 9 | return "testclient/testclient" 10 | } 11 | -------------------------------------------------------------------------------- /fleetspeak/src/client/socketservice/socketservice_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build windows 16 | 17 | package socketservice 18 | 19 | import ( 20 | "fmt" 21 | "net" 22 | "os" 23 | "path/filepath" 24 | 25 | log "github.com/golang/glog" 26 | 27 | "github.com/google/fleetspeak/fleetspeak/src/client/socketservice/checks" 28 | "github.com/google/fleetspeak/fleetspeak/src/windows/wnixsocket" 29 | "github.com/hectane/go-acl" 30 | ) 31 | 32 | func listen(socketPath string) (net.Listener, error) { 33 | parent := filepath.Dir(socketPath) 34 | 35 | // Ensure that the parent directory exists. wnixsocket.Listen ensures the 36 | // socket file exists and is truncated. 37 | stat, err := os.Lstat(parent) 38 | if err != nil && !os.IsNotExist(err) { 39 | return nil, fmt.Errorf("os.Lstat failed: %v", err) 40 | } 41 | if err == nil { 42 | log.Infof("wnix socket parent %v already exists (IsDir: %v)", parent, stat.Mode().IsDir()) 43 | } 44 | 45 | if err := os.MkdirAll(parent, 0); err != nil { 46 | return nil, fmt.Errorf("os.MkdirAll failed: %v", err) 47 | } 48 | 49 | // MkdirAll doesn't set mode as expected on Windows, so we make 50 | // sure with Chmod. Note that os.Chmod also doesn't work as expected, so 51 | // we use go-acl. 52 | if err := acl.Chmod(parent, 0700); err != nil { 53 | return nil, fmt.Errorf("failed to chmod a Wnix domain listener's parent directory: %v", err) 54 | } 55 | 56 | l, err := wnixsocket.Listen(socketPath) 57 | if err != nil { 58 | return nil, fmt.Errorf("failed to create a Wnix domain listener: %v", err) 59 | } 60 | 61 | if err := checks.CheckSocketFile(socketPath); err != nil { 62 | return nil, fmt.Errorf("CheckSocketFile(...): %v", err) 63 | } 64 | 65 | return l, nil 66 | } 67 | -------------------------------------------------------------------------------- /fleetspeak/src/client/stdinservice/proto/fleetspeak_stdinservice/config.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package fleetspeak.stdinservice; 4 | 5 | option go_package = "github.com/google/fleetspeak/fleetspeak/src/client/stdinservice/proto/fleetspeak_stdinservice"; 6 | 7 | // The configuration information expected by stdinservice.Factory 8 | // in ClientServiceConfig.config. 9 | message Config { 10 | string cmd = 1; 11 | } 12 | -------------------------------------------------------------------------------- /fleetspeak/src/client/stdinservice/proto/fleetspeak_stdinservice/messages.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package fleetspeak.stdinservice; 4 | 5 | import "google/protobuf/timestamp.proto"; 6 | 7 | option go_package = "github.com/google/fleetspeak/fleetspeak/src/client/stdinservice/proto/fleetspeak_stdinservice"; 8 | 9 | message InputMessage { 10 | // The data to be forwarded to the service. 11 | bytes input = 1; 12 | 13 | // Command line arguments. 14 | repeated string args = 2; 15 | } 16 | 17 | message OutputMessage { 18 | bytes stdout = 1; 19 | bytes stderr = 2; 20 | 21 | // When the message was generated. 22 | google.protobuf.Timestamp timestamp = 4; 23 | 24 | reserved 3; 25 | } 26 | -------------------------------------------------------------------------------- /fleetspeak/src/client/watchdog/watchdog_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package watchdog 16 | 17 | import ( 18 | "os" 19 | "testing" 20 | "time" 21 | 22 | "github.com/google/fleetspeak/fleetspeak/src/comtesting" 23 | ) 24 | 25 | func TestCreate(t *testing.T) { 26 | w := MakeWatchdog("asdf", "asdf", time.Second, true) 27 | w.Stop() 28 | } 29 | 30 | func TestReset(t *testing.T) { 31 | w := MakeWatchdog("asdf", "asdf", time.Second, true) 32 | defer w.Stop() 33 | for range 5 { 34 | time.Sleep(2 * time.Second) 35 | w.Reset() 36 | } 37 | } 38 | 39 | func TestDump(t *testing.T) { 40 | dir, fin := comtesting.GetTempDir("TestDump") 41 | defer fin() 42 | 43 | w := MakeWatchdog(dir, "TestTimer", time.Second, false) 44 | defer w.Stop() 45 | 46 | time.Sleep(6500 * time.Millisecond) 47 | 48 | files, err := os.ReadDir(dir) 49 | if err != nil { 50 | t.Fatal(err) 51 | } 52 | if len(files) != 1 { 53 | t.Fatalf("Expected 1 file, but found %d", len(files)) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /fleetspeak/src/client/watchdog/watchdog_unix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build linux || darwin 16 | 17 | package watchdog 18 | 19 | const DefaultDir = "/tmp" 20 | -------------------------------------------------------------------------------- /fleetspeak/src/client/watchdog/watchdog_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build windows 16 | 17 | package watchdog 18 | 19 | const DefaultDir = `C:\Windows\Temp` 20 | -------------------------------------------------------------------------------- /fleetspeak/src/common/anypbtest/anypbtest.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package anypbtest offers test helpers for working with AnyPb protos. 16 | package anypbtest 17 | 18 | import ( 19 | "testing" 20 | 21 | "google.golang.org/protobuf/proto" 22 | anypb "google.golang.org/protobuf/types/known/anypb" 23 | ) 24 | 25 | // New creates a new anypb.Any, failing the test on error. 26 | func New(t *testing.T, msg proto.Message) *anypb.Any { 27 | t.Helper() 28 | 29 | a, err := anypb.New(msg) 30 | if err != nil { 31 | t.Fatalf("anypb.New(%+v): %v", msg, err) 32 | } 33 | return a 34 | } 35 | -------------------------------------------------------------------------------- /fleetspeak/src/common/proto/fleetspeak_monitoring/resource.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package fleetspeak.monitoring; 4 | 5 | import "google/protobuf/timestamp.proto"; 6 | 7 | option go_package = "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak_monitoring"; 8 | 9 | // Contains resource-usage metrics for Fleetspeak clients. The stats are 10 | // arrived at by aggregating raw data retrieved from the OS. 11 | // CPU-usage is in milliseconds per second, and memory usage is in bytes. 12 | message AggregatedResourceUsage { 13 | double mean_user_cpu_rate = 1; 14 | double max_user_cpu_rate = 2; 15 | double mean_system_cpu_rate = 3; 16 | double max_system_cpu_rate = 4; 17 | double mean_resident_memory = 5; 18 | int64 max_resident_memory = 6; 19 | int32 max_num_fds = 7; 20 | double mean_num_fds = 8; 21 | } 22 | 23 | // A fleetspeak.Message with message type "ResourceUsage" is sent regularly by 24 | // the system and daemon services to the server, to report the performance of 25 | // processes. 26 | // 27 | // Next tag: 9 28 | message ResourceUsageData { 29 | // Name of the client service that resource usage is charged/attributed to 30 | // e.g 'system' for the system Fleetspeak service, or the name of a daemon 31 | // service as specified in its config. 32 | string scope = 1; 33 | 34 | int64 pid = 2; 35 | 36 | // The self reported service/service binary version. 37 | string version = 8; 38 | 39 | // Time when the process was started by Fleetspeak. 40 | google.protobuf.Timestamp process_start_time = 3; 41 | 42 | // Corresponds to when computation of the resource-usage data was finalized. 43 | google.protobuf.Timestamp data_timestamp = 4; 44 | 45 | AggregatedResourceUsage resource_usage = 5; 46 | 47 | // Optional debug info for the process. 48 | string debug_status = 6; 49 | 50 | // If true, indicates that the process has terminated, and that this is 51 | // the final resource-usage report for that process. 52 | bool process_terminated = 7; 53 | } 54 | 55 | // Sent by clients when a service gets killed by Fleetspeak, e.g. for using 56 | // too much memory. 57 | message KillNotification { 58 | string service = 1; 59 | 60 | int64 pid = 2; 61 | 62 | // The self-reported version of the service. 63 | string version = 3; 64 | 65 | // Time when the process was started by Fleetspeak. 66 | google.protobuf.Timestamp process_start_time = 4; 67 | 68 | // Time when the process was killed by Fleetspeak. 69 | google.protobuf.Timestamp killed_when = 5; 70 | 71 | enum Reason { 72 | UNSPECIFIED = 0; 73 | HEARTBEAT_FAILURE = 1; 74 | MEMORY_EXCEEDED = 2; 75 | } 76 | Reason reason = 6; 77 | } 78 | -------------------------------------------------------------------------------- /fleetspeak/src/common/should/never_oss.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build !google_internal 16 | 17 | // Package should lets callers indicate impossible conditions from the code. 18 | // 19 | // It is a lightweight way to make these impossible conditions observable, 20 | // when it is hooked up with an appropriate monitoring framework. 21 | package should 22 | 23 | // Never indicates that the given condition should never (or seldomly) happen. 24 | // 25 | // Callers should only pass a fixed set of strings to Never(), 26 | // to avoid a state explosion in monitoring counters. 27 | func Never(condition string) { 28 | // Unfortunately empty in the open source version. 29 | } 30 | -------------------------------------------------------------------------------- /fleetspeak/src/comtesting/tempdir.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package comtesting 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | 21 | log "github.com/golang/glog" 22 | ) 23 | 24 | var tempDir string 25 | 26 | // GetTempDir creates and returns the name of a temporary directory. Multiple 27 | // calls will return the same name. It uses the name of the test that is being 28 | // run as part of the name of the directory. 29 | // 30 | // The second returned value is a platform-specific cleanup function. 31 | // This is particularly useful on Windows, where the current user's homedir 32 | // can be returned as the temp dir, and gets cluttered quickly. 33 | func GetTempDir(testName string) (string, func()) { 34 | if tempDir != "" { 35 | return tempDir, cleanupTempDir 36 | } 37 | 38 | tempDir = os.Getenv("TEST_TMPDIR") 39 | 40 | if tempDir == "" { 41 | d, err := os.MkdirTemp("", testName+"_") 42 | if err != nil { 43 | panic(fmt.Sprintf("Unable to create temp directory: %v", err)) 44 | } 45 | tempDir = d 46 | } 47 | log.Infof("Created temp directory: %s", tempDir) 48 | return tempDir, cleanupTempDir 49 | } 50 | -------------------------------------------------------------------------------- /fleetspeak/src/comtesting/tempdir_unix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build linux || darwin 16 | 17 | package comtesting 18 | 19 | func cleanupTempDir() { 20 | tempDir = "" 21 | } 22 | -------------------------------------------------------------------------------- /fleetspeak/src/comtesting/tempdir_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build windows 16 | 17 | package comtesting 18 | 19 | import ( 20 | "os" 21 | 22 | log "github.com/golang/glog" 23 | ) 24 | 25 | func cleanupTempDir() { 26 | if tempDir == "" { 27 | return 28 | } 29 | 30 | if err := os.RemoveAll(tempDir); err != nil { 31 | log.Errorf("Failed to cleanup temp dir; os.RemoveAll(%q): %v", tempDir, err) 32 | return 33 | } 34 | 35 | tempDir = "" 36 | } 37 | -------------------------------------------------------------------------------- /fleetspeak/src/config/server/config.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package serer 16 | package server 17 | 18 | import ( 19 | "errors" 20 | "fmt" 21 | "os" 22 | 23 | "google.golang.org/protobuf/encoding/prototext" 24 | 25 | cpb "github.com/google/fleetspeak/fleetspeak/src/config/proto/fleetspeak_config" 26 | ) 27 | 28 | // WriteConfig validates and then writes a server component configuration. 29 | func WriteConfig(cfg *cpb.Config, certPEM, keyPEM []byte) error { 30 | if cfg.ServerComponentConfigurationFile == "" { 31 | return errors.New("server_component_configuration_file is required") 32 | } 33 | 34 | cc := cfg.ComponentsConfig 35 | if cc == nil { 36 | return errors.New("components_config is required") 37 | } 38 | if cc.HttpsConfig == nil { 39 | return errors.New("components_config.https_config is required") 40 | } 41 | if cc.HttpsConfig.ListenAddress == "" { 42 | return errors.New("components_config.https_config.listen_address is required") 43 | } 44 | cc.HttpsConfig.Certificates = string(certPEM) 45 | cc.HttpsConfig.Key = string(keyPEM) 46 | 47 | b, err := prototext.Marshal(cc) 48 | if err != nil { 49 | return fmt.Errorf("failed to marshal server component configuration: %v", err) 50 | } 51 | err = os.WriteFile(cfg.ServerComponentConfigurationFile, b, 0600) 52 | if err != nil { 53 | return fmt.Errorf("failed to write server component configuration file [%s]: %v", cfg.ServerComponentConfigurationFile, err) 54 | } 55 | 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /fleetspeak/src/e2etesting/balancer/balancer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io" 7 | "log" 8 | "math/rand" 9 | "net" 10 | "os" 11 | "strings" 12 | "time" 13 | 14 | "github.com/google/fleetspeak/fleetspeak/src/e2etesting/balancer/proxyproto" 15 | ) 16 | 17 | var ( 18 | serversFile = flag.String("servers_file", "", "File with server hosts") 19 | serverFrontendAddr = flag.String("frontend_address", "", "Frontend address for clients to connect") 20 | useProxyProto = flag.Bool("use_proxy_proto", true, "Whether to forward client information using proxy proto") 21 | ) 22 | 23 | func copy(wc io.WriteCloser, r io.Reader) { 24 | defer wc.Close() 25 | io.Copy(wc, r) 26 | } 27 | 28 | func run() error { 29 | dat, err := os.ReadFile(*serversFile) 30 | if err != nil { 31 | return fmt.Errorf("Failed to read serversFile: %v", err) 32 | } 33 | serverHosts := strings.Fields(string(dat)) 34 | if len(serverHosts) == 0 { 35 | return fmt.Errorf("No server hosts were provided") 36 | } 37 | 38 | ln, err := net.Listen("tcp", *serverFrontendAddr) 39 | if err != nil { 40 | return fmt.Errorf("Failed to bind: %v", err) 41 | } 42 | log.Printf("Load balancer started on %v\n", *serverFrontendAddr) 43 | 44 | for { 45 | lbConn, err := ln.Accept() 46 | if err != nil { 47 | return fmt.Errorf("Failed to accept connection: %v", err) 48 | } 49 | var serverAddr string 50 | var serverConn net.Conn 51 | retriesLeft := 10 52 | for { 53 | serverAddr = serverHosts[rand.Int()%len(serverHosts)] 54 | serverConn, err = net.Dial("tcp", serverAddr) 55 | if err != nil { 56 | log.Printf("Failed to connect to server (%v): %v, retrying...\n", serverAddr, err) 57 | retriesLeft-- 58 | if retriesLeft < 0 { 59 | return fmt.Errorf("Maximum number of retries exceeded - no active servers were found") 60 | } 61 | time.Sleep(time.Second * 2) 62 | } else { 63 | break 64 | } 65 | } 66 | log.Printf("Connection accepted, server: %v\n", serverAddr) 67 | if *useProxyProto { 68 | err = proxyproto.WriteFirstProxyMessage(serverConn, lbConn.RemoteAddr().String(), serverAddr) 69 | if err != nil { 70 | return err 71 | } 72 | } 73 | go copy(serverConn, lbConn) 74 | go copy(lbConn, serverConn) 75 | } 76 | } 77 | 78 | func main() { 79 | flag.Parse() 80 | err := run() 81 | if err != nil { 82 | fmt.Printf("Load balancer failed: %v\n", err) 83 | os.Exit(1) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /fleetspeak/src/e2etesting/balancer/proxyproto/proxyproto.go: -------------------------------------------------------------------------------- 1 | package proxyproto 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net" 7 | "strconv" 8 | 9 | proxyproto "github.com/pires/go-proxyproto" 10 | ) 11 | 12 | func splitHostPort(addr string) (net.IP, uint16, error) { 13 | ta, err := net.ResolveTCPAddr("tcp", addr) 14 | if err == nil { 15 | return ta.IP, uint16(ta.Port), nil 16 | } 17 | hostStr, portStr, err := net.SplitHostPort(addr) 18 | if err != nil { 19 | return nil, 0, err 20 | } 21 | host := net.ParseIP(hostStr) 22 | if host == nil { 23 | return nil, 0, fmt.Errorf("Failed to parse IP") 24 | } 25 | port, err := strconv.Atoi(portStr) 26 | if err != nil { 27 | return nil, 0, fmt.Errorf("Failed to parse port: %v", err) 28 | } 29 | return host, uint16(port), nil 30 | } 31 | 32 | func WriteFirstProxyMessage(w io.Writer, srcAddr, dstAddr string) error { 33 | srcHost, srcPort, err := splitHostPort(srcAddr) 34 | if err != nil { 35 | return fmt.Errorf("Failed to parse source (client) address: %v", err) 36 | } 37 | dstHost, dstPort, err := splitHostPort(dstAddr) 38 | if err != nil { 39 | return fmt.Errorf("Failed to parse destination (server) address: %v", err) 40 | } 41 | header := proxyproto.Header{ 42 | Version: 1, 43 | TransportProtocol: proxyproto.TCPv4, 44 | SourceAddr: &net.TCPAddr{IP: srcHost, Port: int(srcPort)}, 45 | DestinationAddr: &net.TCPAddr{IP: dstHost, Port: int(dstPort)}, 46 | } 47 | _, err = header.WriteTo(w) 48 | if err != nil { 49 | return fmt.Errorf("Failed to write Proxy header: %v", err) 50 | } 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /fleetspeak/src/e2etesting/frr_master_server_main/frr_master_server_main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "net" 7 | "os" 8 | 9 | frr "github.com/google/fleetspeak/fleetspeak/src/inttesting/frr" 10 | fgrpc "github.com/google/fleetspeak/fleetspeak/src/inttesting/frr/proto/fleetspeak_frr" 11 | sgrpc "github.com/google/fleetspeak/fleetspeak/src/server/proto/fleetspeak_server" 12 | "google.golang.org/grpc" 13 | "google.golang.org/grpc/credentials/insecure" 14 | ) 15 | 16 | var ( 17 | listenAddr = flag.String("listen_address", "localhost:6059", "Address for clients to connect") 18 | adminAddr = flag.String("admin_address", "localhost:6061", "Fleetspeak server admin address") 19 | ) 20 | 21 | // StartMasterServer starts FRR Master Server listening to listenAddr 22 | func StartMasterServer(listenAddr, adminAddr string) error { 23 | conn, err := grpc.NewClient(adminAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) 24 | if err != nil { 25 | return fmt.Errorf("Unable to connect to FS server: %v", err) 26 | } 27 | defer conn.Close() 28 | 29 | ms := frr.NewMasterServer(sgrpc.NewAdminClient(conn)) 30 | gms := grpc.NewServer() 31 | fgrpc.RegisterMasterServer(gms, ms) 32 | ad, err := net.ResolveTCPAddr("tcp", listenAddr) 33 | if err != nil { 34 | return fmt.Errorf("Unable to resolve tcp address: %v", err) 35 | } 36 | tl, err := net.ListenTCP("tcp", ad) 37 | if err != nil { 38 | return fmt.Errorf("Unable to start listening TCP: %v", err) 39 | } 40 | defer gms.Stop() 41 | gms.Serve(tl) 42 | return nil 43 | } 44 | 45 | func main() { 46 | flag.Parse() 47 | err := StartMasterServer(*listenAddr, *adminAddr) 48 | if err != nil { 49 | fmt.Println(err) 50 | os.Exit(1) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /fleetspeak/src/e2etesting/localtesting/end_to_end_test.go: -------------------------------------------------------------------------------- 1 | package localtesting_test 2 | 3 | import ( 4 | "flag" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | 9 | "github.com/google/fleetspeak/fleetspeak/src/e2etesting/setup" 10 | "github.com/google/fleetspeak/fleetspeak/src/e2etesting/tests" 11 | ) 12 | 13 | var ( 14 | mysqlAddress = flag.String("mysql_address", "", "MySQL server address") 15 | mysqlDatabase = flag.String("mysql_database", "", "MySQL database name to use") 16 | mysqlUsername = flag.String("mysql_username", "", "MySQL username to use") 17 | mysqlPassword = flag.String("mysql_password", "", "MySQL password to use") 18 | numClients = flag.Int("num_clients", 3, "Number of clients to test") 19 | numServers = flag.Int("num_servers", 2, "Number of servers to test") 20 | ) 21 | 22 | func parseFlags() { 23 | flag.Parse() 24 | for flagVar, envVarName := range map[*string]string{ 25 | mysqlAddress: "MYSQL_TEST_ADDR", 26 | mysqlUsername: "MYSQL_TEST_USER", 27 | mysqlPassword: "MYSQL_TEST_PASS", 28 | mysqlDatabase: "MYSQL_TEST_E2E_DB", 29 | } { 30 | val := os.Getenv(envVarName) 31 | if len(val) > 0 { 32 | *flagVar = val 33 | } 34 | } 35 | } 36 | 37 | // Test end to end 38 | func TestLocalEndToEnd(t *testing.T) { 39 | parseFlags() 40 | if *mysqlAddress == "" { 41 | t.Skip("Mysql address not provided") 42 | } 43 | if *mysqlUsername == "" { 44 | t.Skip("Mysql user not provided") 45 | } 46 | if *mysqlDatabase == "" { 47 | t.Skip("Mysql database for end-to-end testing not provided") 48 | } 49 | 50 | wd, err := os.Getwd() 51 | if err != nil { 52 | t.Fatalf("Failed to get working directory: %v", err) 53 | } 54 | for range 4 { 55 | wd = filepath.Dir(wd) 56 | } 57 | err = os.Chdir(wd) 58 | if err != nil { 59 | t.Fatalf("Failed to change directory: %v", err) 60 | } 61 | 62 | frontendAddress := "localhost:6000" 63 | msAddress := "localhost:6059" 64 | 65 | clientIDs := setup.ConfigureAndStart(t, setup.MysqlCredentials{ 66 | Host: *mysqlAddress, 67 | Password: *mysqlPassword, 68 | Username: *mysqlUsername, 69 | Database: *mysqlDatabase, 70 | }, frontendAddress, msAddress, *numServers, *numClients) 71 | 72 | tests.RunTests(t, msAddress, clientIDs) 73 | } 74 | -------------------------------------------------------------------------------- /fleetspeak/src/inttesting/integration_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package integration_test runs integrationtest against the sqlite datastore. 16 | package integration_test 17 | 18 | import ( 19 | "path/filepath" 20 | "testing" 21 | 22 | "github.com/google/fleetspeak/fleetspeak/src/inttesting/integrationtest" 23 | "github.com/google/fleetspeak/fleetspeak/src/server/sqlite" 24 | ) 25 | 26 | func mustMakeDatastore(t *testing.T) *sqlite.Datastore { 27 | t.Helper() 28 | 29 | ds, err := sqlite.MakeDatastore(filepath.Join(t.TempDir(), "DataStore.sqlite")) 30 | if err != nil { 31 | t.Fatalf("sqlite.MakeDatastore: %v", err) 32 | } 33 | t.Cleanup(func() { 34 | err := ds.Close() 35 | if err != nil { 36 | t.Errorf("Closing SQLite datastore: %v", err) 37 | } 38 | }) 39 | return ds 40 | } 41 | 42 | func TestFRRIntegration(t *testing.T) { 43 | ds := mustMakeDatastore(t) 44 | integrationtest.FRRIntegrationTest(t, ds, false) 45 | } 46 | 47 | func TestCloneHandling(t *testing.T) { 48 | ds := mustMakeDatastore(t) 49 | integrationtest.CloneHandlingTest(t, ds) 50 | } 51 | 52 | func TestFRRIntegrationStreaming(t *testing.T) { 53 | ds := mustMakeDatastore(t) 54 | integrationtest.FRRIntegrationTest(t, ds, true) 55 | } 56 | -------------------------------------------------------------------------------- /fleetspeak/src/server/components/authorizer/authorizer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package authorizer provide generic implementations and utility methods 16 | // for Fleetspeak's authorizer component type. 17 | package authorizer 18 | 19 | import ( 20 | "net" 21 | 22 | "github.com/google/fleetspeak/fleetspeak/src/server/authorizer" 23 | 24 | fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak" 25 | ) 26 | 27 | // LabelFilter is an authorizer.Authorizer which refuses connections from 28 | // clients that do not have a specific label. 29 | type LabelFilter struct { 30 | Label string 31 | } 32 | 33 | // Allow1 implements Authorizer. 34 | func (f LabelFilter) Allow1(net.Addr) bool { return true } 35 | 36 | // Allow2 implements Authorizer. 37 | func (f LabelFilter) Allow2(_ net.Addr, i authorizer.ContactInfo) bool { 38 | if f.Label == "" { 39 | return true 40 | } 41 | for _, l := range i.ClientLabels { 42 | if l == f.Label { 43 | return true 44 | } 45 | } 46 | return false 47 | } 48 | 49 | // Allow3 implements Authorizer. 50 | func (f LabelFilter) Allow3(net.Addr, authorizer.ContactInfo, authorizer.ClientInfo) bool { 51 | return true 52 | } 53 | 54 | // Allow4 implements Authorizer. 55 | func (f LabelFilter) Allow4(net.Addr, authorizer.ContactInfo, authorizer.ClientInfo, []authorizer.SignatureInfo) (bool, *fspb.ValidationInfo) { 56 | return true, nil 57 | } 58 | -------------------------------------------------------------------------------- /fleetspeak/src/server/components/components_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package components 16 | 17 | import ( 18 | "testing" 19 | 20 | cpb "github.com/google/fleetspeak/fleetspeak/src/server/components/proto/fleetspeak_components" 21 | ) 22 | 23 | func TestValidation(t *testing.T) { 24 | for _, tc := range []struct { 25 | cfg *cpb.Config 26 | wantErr string 27 | }{ 28 | { 29 | cfg: &cpb.Config{}, 30 | wantErr: "mysql_data_source_name OR spanner_database_name is required", 31 | }, 32 | { 33 | cfg: &cpb.Config{ 34 | MysqlDataSourceName: "fs:seecret@tcp(localhost:1234)/fsdb", 35 | HttpsConfig: &cpb.HttpsConfig{ 36 | ListenAddress: "localhost:443", 37 | }, 38 | }, 39 | wantErr: "https_config requires listen_address, certificates and key", 40 | }, 41 | { 42 | cfg: &cpb.Config{ 43 | MysqlDataSourceName: "fs:seecret@tcp(localhost:1234)/fsdb", 44 | HttpsConfig: &cpb.HttpsConfig{ 45 | ListenAddress: "localhost:443", 46 | Certificates: "<>", 47 | Key: "<>", 48 | }, 49 | AdminConfig: &cpb.AdminConfig{}, 50 | }, 51 | wantErr: "admin_config.listen_address can't be empty", 52 | }, 53 | } { 54 | _, err := MakeComponents(tc.cfg) 55 | if err == nil || err.Error() != tc.wantErr { 56 | t.Errorf("Expected error message [%v], got [%v]", tc.wantErr, err) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /fleetspeak/src/server/components/https/proxy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package https provides generic implementations and utility methods for 16 | // Fleetspeak https communication component type. 17 | package https 18 | 19 | import ( 20 | "bufio" 21 | "fmt" 22 | "net" 23 | 24 | log "github.com/golang/glog" 25 | 26 | proxyproto "github.com/pires/go-proxyproto" 27 | ) 28 | 29 | // ProxyListener wraps a net.Listener and adapts the perceived source of the connection 30 | // to be that provided by the PROXY protocol header string. Should be used only when the 31 | // server is behind a load balancer which implements the PROXY protocol. 32 | // 33 | // https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt 34 | type ProxyListener struct { 35 | net.Listener 36 | } 37 | 38 | func (l *ProxyListener) Accept() (net.Conn, error) { 39 | c, err := l.Listener.Accept() 40 | if err != nil { 41 | return c, err 42 | } 43 | r := bufio.NewReader(c) 44 | h, err := proxyproto.Read(r) 45 | if err == proxyproto.ErrNoProxyProtocol { 46 | log.Warningf("Received connection from %v without proxy header.", c.RemoteAddr()) 47 | return &proxyConn{ 48 | Conn: c, 49 | reader: r, 50 | remote: c.RemoteAddr(), 51 | }, nil 52 | } 53 | if err != nil { 54 | return nil, fmt.Errorf("error parsing proxy header: %v", err) 55 | } 56 | return &proxyConn{ 57 | Conn: c, 58 | reader: r, 59 | remote: h.SourceAddr, 60 | }, nil 61 | } 62 | 63 | type proxyConn struct { 64 | net.Conn 65 | reader *bufio.Reader 66 | remote net.Addr 67 | } 68 | 69 | func (c *proxyConn) Read(b []byte) (int, error) { 70 | return c.reader.Read(b) 71 | } 72 | 73 | func (c *proxyConn) RemoteAddr() net.Addr { 74 | return c.remote 75 | } 76 | -------------------------------------------------------------------------------- /fleetspeak/src/server/components/notifications/http_test.go: -------------------------------------------------------------------------------- 1 | package notifications 2 | 3 | import ( 4 | "context" 5 | "reflect" 6 | "sync" 7 | "testing" 8 | "time" 9 | 10 | log "github.com/golang/glog" 11 | 12 | "github.com/google/fleetspeak/fleetspeak/src/common" 13 | "github.com/google/fleetspeak/fleetspeak/src/server/notifications" 14 | ) 15 | 16 | func TestListenNotify(t *testing.T) { 17 | var l notifications.Listener 18 | l = &HttpListener{ 19 | BindAddress: "localhost:", 20 | } 21 | c, err := l.Start() 22 | if err != nil { 23 | t.Fatalf("Failed to start listener: %v", err) 24 | } 25 | defer l.Stop() 26 | log.Infof("Started [locahost:] listener, reports address: %v", l.Address()) 27 | 28 | mtx := &sync.Mutex{} 29 | var gotIDs []common.ClientID 30 | go func() { 31 | for id := range c { 32 | mtx.Lock() 33 | gotIDs = append(gotIDs, id) 34 | mtx.Unlock() 35 | } 36 | }() 37 | 38 | n := HttpNotifier{} 39 | id1, _ := common.StringToClientID("0000000000000001") 40 | id2, _ := common.StringToClientID("0000000000000002") 41 | 42 | for _, id := range []common.ClientID{id1, id2} { 43 | if err := n.NewMessageForClient(context.Background(), l.Address(), id); err != nil { 44 | t.Errorf("Unable to send notification for client: %v", err) 45 | } 46 | } 47 | 48 | // TODO: Clean up concurrency in this test! We are not waiting for results correctly. 49 | time.Sleep(500 * time.Millisecond) 50 | 51 | mtx.Lock() 52 | defer mtx.Unlock() 53 | if !reflect.DeepEqual(gotIDs, []common.ClientID{id1, id2}) { 54 | t.Errorf("Unexpected ids received got: %v want: %v", gotIDs, []common.ClientID{id1, id2}) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /fleetspeak/src/server/cpsservice/proto/fleetspeak_cpsservice/cpsservice.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package fleetspeak.cpsservice; 4 | 5 | option go_package = "github.com/google/fleetspeak/fleetspeak/src/server/cpsservice/proto/fleetspeak_cpsservice"; 6 | 7 | message Config { 8 | string project = 1; 9 | string topic = 2; // The cloud pubsub topic to which messages will be posted 10 | // for the service. 11 | } 12 | -------------------------------------------------------------------------------- /fleetspeak/src/server/db/time.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package db 16 | 17 | import ( 18 | "time" 19 | 20 | log "github.com/golang/glog" 21 | 22 | "github.com/google/fleetspeak/fleetspeak/src/server/internal/ftime" 23 | tspb "google.golang.org/protobuf/types/known/timestamppb" 24 | ) 25 | 26 | // Now is the clock used by the server. Normally just time.Now, but can be replaced to support testing. It should be used by db.Store implementations to determine the time. 27 | func Now() time.Time { 28 | return ftime.Now() 29 | } 30 | 31 | // NowProto returns a proto representation of Now(). 32 | func NowProto() *tspb.Timestamp { 33 | n := tspb.New(Now()) 34 | if err := n.CheckValid(); err != nil { 35 | // Really shouldn't happen; the most likely situation is that we 36 | // are in a test using a badly broken Now. 37 | log.Fatalf("Unable to convert Now() to a protocol buffer: %s", err) 38 | } 39 | return n 40 | } 41 | 42 | // ClientRetryTime returns when a client message, being sent to the client 43 | // approximately Now(), will be considered timed out and eligible to be sent 44 | // again. It should be used by MessageStore implementations to determine when a 45 | // message can next be provided by ClientMessagesForProcessing. 46 | func ClientRetryTime() time.Time { 47 | return ftime.ClientRetryTime() 48 | } 49 | 50 | // ServerRetryTime returns when server message, whose processing is about to 51 | // start, will be considered timed out and eligible to be processed again. 52 | // It should be used by MessageStore implementations to determine when a message 53 | // can next be provided to a MessageProcessor. 54 | func ServerRetryTime(retryCount uint32) time.Time { 55 | return ftime.ServerRetryTime(retryCount) 56 | } 57 | -------------------------------------------------------------------------------- /fleetspeak/src/server/dbtesting/dbtesting.go: -------------------------------------------------------------------------------- 1 | package dbtesting 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/google/fleetspeak/fleetspeak/src/common" 7 | "github.com/google/fleetspeak/fleetspeak/src/server/db" 8 | ) 9 | 10 | // DbTestEnv has to be implemented for each datastore where data store tests are expected to run. 11 | type DbTestEnv interface { 12 | // Creates the database testing environment. Called once per database test suite. 13 | Create() error 14 | // Cleans the database testing environment before every test and returns a new Store instance to be used by the test. 15 | Clean() (db.Store, error) 16 | // Destroys the database testing environment after all the tests have run. Called once per database test suite. 17 | Destroy() error 18 | } 19 | 20 | // Predefined client ids to be used in tests. 21 | var clientID, _ = common.BytesToClientID([]byte{0, 0, 0, 0, 0, 0, 0, 1}) 22 | var clientID2, _ = common.BytesToClientID([]byte{0, 0, 0, 0, 0, 0, 0, 2}) 23 | var clientID3, _ = common.BytesToClientID([]byte{0, 0, 0, 0, 0, 0, 0, 3}) 24 | 25 | // RunTestSuite runs a suite of databases tests. 26 | func runTestSuite(t *testing.T, env DbTestEnv, tests map[string]func(*testing.T, db.Store)) { 27 | for n, fn := range tests { 28 | ms, err := env.Clean() 29 | if err != nil { 30 | t.Fatalf("Can't clean the datastore for test '%v': %v", n, err) 31 | } 32 | t.Run(n, func(t *testing.T) { 33 | fn(t, ms) 34 | }) 35 | } 36 | } 37 | 38 | // DataStoreTestSuite combines all test suites for datastore testing. 39 | func DataStoreTestSuite(t *testing.T, env DbTestEnv) { 40 | if err := env.Create(); err != nil { 41 | t.Fatalf("Can't create the datastore: %v", err) 42 | } 43 | 44 | messageStoreTestSuite(t, env) 45 | clientStoreTestSuite(t, env) 46 | broadcastStoreTestSuite(t, env) 47 | fileStoreTestSuite(t, env) 48 | integrationTestSuite(t, env) 49 | 50 | if err := env.Destroy(); err != nil { 51 | t.Fatalf("Can't destroy the datastore: %v", err) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /fleetspeak/src/server/dbtesting/filestore_suite.go: -------------------------------------------------------------------------------- 1 | package dbtesting 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "io" 7 | "testing" 8 | 9 | "github.com/google/fleetspeak/fleetspeak/src/server/db" 10 | "github.com/google/fleetspeak/fleetspeak/src/server/sertesting" 11 | ) 12 | 13 | // FileStoreTest tests a FileStore. 14 | func FileStoreTest(t *testing.T, fs db.Store) { 15 | ctx := context.Background() 16 | 17 | fakeTime := sertesting.FakeNow(84) 18 | defer fakeTime.Revert() 19 | 20 | data := []byte("The quick sly fox jumped over the lazy dogs.") 21 | 22 | if err := fs.StoreFile(ctx, "testService", "testFile", bytes.NewReader(data)); err != nil { 23 | t.Errorf("Error from StoreFile(testService, testFile): %v", err) 24 | } 25 | 26 | ts, err := fs.StatFile(ctx, "testService", "testFile") 27 | if err != nil { 28 | t.Errorf("Error from StatFile(testService, testFile): %v", err) 29 | } 30 | if ts != fakeTime.Get() { 31 | t.Errorf("Wrong result of StatfileFile(testService, testFile), want %v got %v:", fakeTime.Get(), ts) 32 | } 33 | 34 | res, ts, err := fs.ReadFile(ctx, "testService", "testFile") 35 | if err != nil { 36 | t.Fatalf("Error from ReadFile(testService, testFile): %v", err) 37 | } 38 | rb, err := io.ReadAll(res) 39 | if err != nil { 40 | t.Errorf("Error reading result of ReadFile(testService, testFile): %v", err) 41 | } 42 | if c, ok := res.(io.Closer); ok { 43 | c.Close() 44 | } 45 | if !bytes.Equal(rb, data) || ts != fakeTime.Get() { 46 | t.Errorf("Wrong result of ReadFile(testService, testFile), want (%v, %v) got (%v, %v):", 47 | fakeTime.Get(), data, ts, rb) 48 | } 49 | 50 | if _, err := fs.StatFile(ctx, "testService", "missingFile"); err == nil || !fs.IsNotFound(err) { 51 | t.Errorf("Wrong error for ReadFile(testService, missingFile), want IsNotFound(err)=true, got %v", err) 52 | } 53 | if _, _, err := fs.ReadFile(ctx, "testService", "missingFile"); err == nil || !fs.IsNotFound(err) { 54 | t.Errorf("Wrong error for ReadFile(testService, missingFile), want IsNotFound(err)=true, got %v", err) 55 | } 56 | } 57 | 58 | func fileStoreTestSuite(t *testing.T, env DbTestEnv) { 59 | t.Run("FileStoreTestSuite", func(t *testing.T) { 60 | runTestSuite(t, env, map[string]func(*testing.T, db.Store){ 61 | "FileStoreTest": FileStoreTest, 62 | }) 63 | }) 64 | } 65 | -------------------------------------------------------------------------------- /fleetspeak/src/server/dbtesting/integration_suite.go: -------------------------------------------------------------------------------- 1 | package dbtesting 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/google/fleetspeak/fleetspeak/src/inttesting/integrationtest" 7 | "github.com/google/fleetspeak/fleetspeak/src/server/db" 8 | ) 9 | 10 | func integrationTest(t *testing.T, ms db.Store) { 11 | integrationtest.FRRIntegrationTest(t, ms, false) 12 | } 13 | 14 | func cloneHandlingTest(t *testing.T, ms db.Store) { 15 | integrationtest.CloneHandlingTest(t, ms) 16 | } 17 | 18 | func integrationTestSuite(t *testing.T, env DbTestEnv) { 19 | t.Run("IntegrationTestSuite", func(t *testing.T) { 20 | runTestSuite(t, env, map[string]func(*testing.T, db.Store){ 21 | "IntegrationTest": integrationTest, 22 | "CloneHandlingTest": cloneHandlingTest, 23 | }) 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /fleetspeak/src/server/grpcservice/client/testing/client_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2017 Google Inc. 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 | # https://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 | # Unit test for client. 18 | # 19 | # This script reserves ports and then starts two processes which communicate 20 | # with each other using these ports: 21 | # 22 | # 1) A loopback python script based on the grpcservice client library. 23 | # 24 | # 2) A tester which is, primarily, a fleetspeak server with the grpcservice 25 | # installed. 26 | # 27 | # Then the tester sends a message through the loopback, as if it came from a 28 | # client, and waits for the looped message to come back to the same client. 29 | # 30 | # (Alternatively, this could be done by spawning loopback with tester, but using 31 | # independent processes is more realistic.) 32 | 33 | # Exit on error. 34 | set -e 35 | 36 | readonly LOOPBACK_MODULE='fleetspeak.server_connector.testing.loopback' 37 | readonly TESTER='src/server/grpcservice/client/testing/tester' 38 | 39 | function randomize_tcp_port { 40 | local readonly MINPORT=32760 41 | local readonly MAXPORT=59759 42 | /bin/echo $(( MINPORT + RANDOM%(MAXPORT-MINPORT+1) )) 43 | } 44 | 45 | readonly MESSAGE_PORT=$(randomize_tcp_port) 46 | readonly ADMIN_PORT=$(randomize_tcp_port) 47 | 48 | # Start loopback in the background, kill when finished. We do not care about its 49 | # exit code. 50 | python -m "${LOOPBACK_MODULE}" \ 51 | --fleetspeak_message_listen_address="localhost:${MESSAGE_PORT}" \ 52 | --fleetspeak_server="localhost:${ADMIN_PORT}" & 53 | readonly PID=${!} 54 | trap "/bin/kill -- $PID ; wait" EXIT SIGINT 55 | 56 | # If anything goes wrong the tester should return a non-zero exit code and as a 57 | # unit test we should preserve this. 58 | "${TESTER}" \ 59 | --admin_addr="localhost:${ADMIN_PORT}" \ 60 | --message_addr="localhost:${MESSAGE_PORT}" 61 | -------------------------------------------------------------------------------- /fleetspeak/src/server/grpcservice/proto/fleetspeak_grpcservice/grpcservice.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package fleetspeak.grpcservice; 4 | 5 | import "fleetspeak/src/common/proto/fleetspeak/common.proto"; 6 | 7 | option go_package = "github.com/google/fleetspeak/fleetspeak/src/server/grpcservice/proto/fleetspeak_grpcservice"; 8 | 9 | message Config { 10 | string target = 1; // The address to dial. 11 | bool insecure = 2; // If set, will not secure connection to the target. 12 | string cert_file = 13 | 3; // If set, a pool of trusted certificates will be read from this file 14 | // and used to authenticate the connection to target. 15 | } 16 | 17 | // Processor is the service that a target system must implement to 18 | // receive messages through a GRPCService based service. 19 | service Processor { 20 | // Process accepts message and processes it. 21 | rpc Process(fleetspeak.Message) returns (fleetspeak.EmptyMessage) {} 22 | } 23 | -------------------------------------------------------------------------------- /fleetspeak/src/server/https/compression.go: -------------------------------------------------------------------------------- 1 | package https 2 | 3 | import ( 4 | "compress/zlib" 5 | "fmt" 6 | "net/http" 7 | ) 8 | 9 | // CompressionHandler is a http.Handler that transparently decompresses the 10 | // request body if it is compressed and forwards the request to the wrapped 11 | // handler. 12 | type CompressionHandler struct { 13 | Wrapped http.Handler 14 | } 15 | 16 | // ServeHTTP implements http.Handler.ServeHTTP by transparently decompressing 17 | // the request body if it is compressed and forwarding the call to the wrapped 18 | // handler. 19 | func (h *CompressionHandler) ServeHTTP(res http.ResponseWriter, req *http.Request) { 20 | encoding := req.Header.Get("Content-Encoding") 21 | switch encoding { 22 | case "": 23 | // No compression. 24 | case "deflate": 25 | // "deflate" is the commonly used directive for deflate compressed data in 26 | // zlib format (https://www.rfc-editor.org/rfc/rfc9110#name-deflate-coding). 27 | zr, err := zlib.NewReader(req.Body) 28 | if err != nil { 29 | http.Error(res, fmt.Sprintf("failed to create zlib reader: %v", err), http.StatusBadRequest) 30 | return 31 | } 32 | defer zr.Close() 33 | req.Body = zr 34 | req.Header.Del("Content-Encoding") 35 | default: 36 | http.Error(res, fmt.Sprintf("unsupported content encoding: %s", encoding), http.StatusUnsupportedMediaType) 37 | return 38 | } 39 | h.Wrapped.ServeHTTP(res, req) 40 | } 41 | -------------------------------------------------------------------------------- /fleetspeak/src/server/https/file_server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package https 16 | 17 | import ( 18 | "fmt" 19 | "net/http" 20 | "net/url" 21 | "strings" 22 | ) 23 | 24 | // fileServer wraps a Communicator in order to serve files. 25 | type fileServer struct { 26 | *Communicator 27 | } 28 | 29 | // ServeHTTP implements http.Handler 30 | func (s fileServer) ServeHTTP(res http.ResponseWriter, req *http.Request) { 31 | path := strings.Split(strings.TrimPrefix(req.URL.EscapedPath(), "/"), "/") 32 | if len(path) != 3 || path[0] != "files" { 33 | http.Error(res, fmt.Sprintf("unable to parse files uri: %v", req.URL.EscapedPath()), http.StatusBadRequest) 34 | return 35 | } 36 | service, err := url.PathUnescape(path[1]) 37 | if err != nil { 38 | http.Error(res, fmt.Sprintf("unable to parse path component [%v]: %v", path[1], err), http.StatusBadRequest) 39 | return 40 | } 41 | name, err := url.PathUnescape(path[2]) 42 | if err != nil { 43 | http.Error(res, fmt.Sprintf("unable to parse path component [%v]: %v", path[2], err), http.StatusBadRequest) 44 | return 45 | } 46 | 47 | ctx := req.Context() 48 | data, modtime, err := s.fs.ReadFile(ctx, service, name) 49 | if err != nil { 50 | if s.fs.IsNotFound(err) { 51 | http.Error(res, "file not found", http.StatusNotFound) 52 | return 53 | } 54 | http.Error(res, fmt.Sprintf("internal error: %v", err), http.StatusInternalServerError) 55 | return 56 | } 57 | http.ServeContent(res, req, name, modtime, data) 58 | data.Close() 59 | } 60 | -------------------------------------------------------------------------------- /fleetspeak/src/server/internal/ftime/ftime.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package ftime defines "fleetspeak time" as a global variable. This is the 16 | // sense of time used by the fleetspeak server and it can be changed to support 17 | // unit testing. 18 | package ftime 19 | 20 | import "time" 21 | 22 | // Now is the time used by the Fleetspeak system. Variable to support unit testing. 23 | var Now = time.Now 24 | -------------------------------------------------------------------------------- /fleetspeak/src/server/internal/ftime/retry.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ftime 16 | 17 | import ( 18 | "math" 19 | "math/rand" 20 | "time" 21 | ) 22 | 23 | // ClientRetryTime determines how long to wait for an acknowledgement before 24 | // sending a message to a client again. The normal implementation waits 15 25 | // minutes. It is mutable primarily to support testing. 26 | var ClientRetryTime = func() time.Time { 27 | return Now().Add(15 * time.Minute) 28 | } 29 | 30 | // ServerRetryTime determines how long to wait before attempting to process a 31 | // message again on the FS server. The normal implementation provides 32 | // exponential backoff with jitter, with an initial wait of 1-2 min. It is 33 | // mutable primarily to support testing. 34 | var ServerRetryTime = func(retryCount uint32) time.Time { 35 | delay := float64(time.Minute) * math.Pow(1.1, float64(retryCount)) 36 | delay *= 1.0 + rand.Float64() 37 | 38 | return Now().Add(time.Duration(delay)) 39 | } 40 | -------------------------------------------------------------------------------- /fleetspeak/src/server/mysql/filestore.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package mysql 16 | 17 | import ( 18 | "bytes" 19 | "context" 20 | "database/sql" 21 | "io" 22 | "time" 23 | 24 | "github.com/google/fleetspeak/fleetspeak/src/server/db" 25 | ) 26 | 27 | func (d *Datastore) StoreFile(ctx context.Context, service, name string, data io.Reader) error { 28 | b, err := io.ReadAll(data) 29 | if err != nil { 30 | return err 31 | } 32 | return d.runInTx(ctx, false, func(tx *sql.Tx) error { 33 | _, err := tx.ExecContext(ctx, "REPLACE INTO files (service, name, modified_time_nanos, data) VALUES(?, ?, ?, ?)", 34 | service, name, db.Now().UnixNano(), b) 35 | return err 36 | }) 37 | } 38 | 39 | func (d *Datastore) StatFile(ctx context.Context, service, name string) (time.Time, error) { 40 | var ts int64 41 | 42 | err := d.runInTx(ctx, true, func(tx *sql.Tx) error { 43 | row := tx.QueryRowContext(ctx, "SELECT modified_time_nanos FROM files WHERE service = ? AND name = ?", service, name) 44 | return row.Scan(&ts) 45 | }) 46 | 47 | return time.Unix(0, ts).UTC(), err 48 | } 49 | 50 | func (d *Datastore) ReadFile(ctx context.Context, service, name string) (data db.ReadSeekerCloser, modtime time.Time, err error) { 51 | var b []byte 52 | var ts int64 53 | 54 | err = d.runInTx(ctx, true, func(tx *sql.Tx) error { 55 | row := tx.QueryRowContext(ctx, "SELECT modified_time_nanos, data FROM files WHERE service = ? AND name = ?", service, name) 56 | if err := row.Scan(&ts, &b); err != nil { 57 | b = nil 58 | return err 59 | } 60 | return nil 61 | }) 62 | 63 | if err != nil { 64 | return nil, time.Time{}, err 65 | } 66 | if b == nil { 67 | b = []byte{} 68 | } 69 | return db.NOOPCloser{ReadSeeker: bytes.NewReader(b)}, time.Unix(0, ts).UTC(), nil 70 | } 71 | -------------------------------------------------------------------------------- /fleetspeak/src/server/notifications/notifications.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package notifications defines the plugin interface for components which allow 16 | // the fleetspeak servers within an installation to notify each other about 17 | // interesting events. 18 | package notifications 19 | 20 | import ( 21 | "context" 22 | 23 | "github.com/google/fleetspeak/fleetspeak/src/common" 24 | ) 25 | 26 | type Listener interface { 27 | // Start causes the Listener to begin listening for notifications. Once 28 | // started, should write to ids every time it is notified that there may be 29 | // new messages for a client. 30 | Start() (<-chan common.ClientID, error) 31 | 32 | // Stop causes the Listener to stop listening for notifications and close the 33 | // ids channel. 34 | Stop() 35 | 36 | // Address returns the address of this listener. These addresses must be 37 | // suitable to pass as a target to any Notifier compatible with this Listener. 38 | // Will only be called after Start(). 39 | Address() string 40 | } 41 | 42 | type Notifier interface { 43 | // NewMessageForClient indicates that one or more messages have been written 44 | // and notifies the provided target of this. 45 | NewMessageForClient(ctx context.Context, target string, id common.ClientID) error 46 | } 47 | -------------------------------------------------------------------------------- /fleetspeak/src/server/proto/fleetspeak_server/broadcasts.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package fleetspeak.server; 4 | 5 | import "fleetspeak/src/common/proto/fleetspeak/common.proto"; 6 | import "google/protobuf/any.proto"; 7 | import "google/protobuf/timestamp.proto"; 8 | 9 | option go_package = "github.com/google/fleetspeak/fleetspeak/src/server/proto/fleetspeak_server"; 10 | 11 | // A Broadcast is a template to build messages to send to a number of machines. 12 | message Broadcast { 13 | bytes broadcast_id = 1; 14 | 15 | // The source of the broadcast, it should only be a server side service. The 16 | // destinations for the resulting broadcast messages will be different clients 17 | // with the same service name. 18 | fleetspeak.Address source = 2; 19 | 20 | string message_type = 3; 21 | 22 | // A client will only be sent this broadcast if it has been marked with all of 23 | // the required labels. 24 | repeated fleetspeak.Label required_labels = 4; 25 | 26 | // A broadcast will stop being sent at this time. 27 | google.protobuf.Timestamp expiration_time = 5; 28 | 29 | google.protobuf.Any data = 6; 30 | } 31 | -------------------------------------------------------------------------------- /fleetspeak/src/server/proto/fleetspeak_server/resource.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package fleetspeak.server; 4 | 5 | import "google/protobuf/timestamp.proto"; 6 | 7 | option go_package = "github.com/google/fleetspeak/fleetspeak/src/server/proto/fleetspeak_server"; 8 | 9 | // Represents client resource-usage data in the data-store. 10 | // Next id: 15 11 | message ClientResourceUsageRecord { 12 | // Name of the client service that resource usage is charged/attributed to 13 | // e.g 'system' for the system Fleetspeak service, or the name of a daemon 14 | // service as specified in its config. 15 | string scope = 1; 16 | 17 | int64 pid = 2; 18 | google.protobuf.Timestamp process_start_time = 3; 19 | 20 | // When the resource-usage metrics were measured on the client. 21 | google.protobuf.Timestamp client_timestamp = 4; 22 | 23 | // When the resource usage record was written to the data-store. 24 | google.protobuf.Timestamp server_timestamp = 5; 25 | 26 | // If true, indicates that the process has terminated, and that this is 27 | // the final resource-usage record for that process. 28 | bool process_terminated = 12; 29 | 30 | // CPU-usage is in millis per second. 31 | float mean_user_cpu_rate = 6; 32 | float max_user_cpu_rate = 7; 33 | float mean_system_cpu_rate = 8; 34 | float max_system_cpu_rate = 9; 35 | 36 | int32 mean_resident_memory_mib = 10; 37 | int32 max_resident_memory_mib = 11; 38 | 39 | int32 mean_num_fds = 13; 40 | int32 max_num_fds = 14; 41 | } 42 | -------------------------------------------------------------------------------- /fleetspeak/src/server/proto/fleetspeak_server/server.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package fleetspeak.server; 4 | 5 | import "fleetspeak/src/server/proto/fleetspeak_server/services.proto"; 6 | import "google/protobuf/duration.proto"; 7 | 8 | option go_package = "github.com/google/fleetspeak/fleetspeak/src/server/proto/fleetspeak_server"; 9 | 10 | // Describes a server's configuration. If unset, all values default to values 11 | // reasonable for a unit test or small installation. Larger installations may 12 | // need to tune these. 13 | message ServerConfig { 14 | // The collection of services that this server should include. 15 | repeated ServiceConfig services = 1; 16 | 17 | // The approximate time to wait between checking for new broadcasts. If unset, 18 | // a default of 1 minute is used. 19 | google.protobuf.Duration broadcast_poll_time = 2; 20 | } 21 | -------------------------------------------------------------------------------- /fleetspeak/src/server/proto/fleetspeak_server/services.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package fleetspeak.server; 4 | 5 | import "google/protobuf/any.proto"; 6 | 7 | option go_package = "github.com/google/fleetspeak/fleetspeak/src/server/proto/fleetspeak_server"; 8 | 9 | // A ServiceConfig describes how the server should configure a 'service', which 10 | // is a module that sends and processes messages. 11 | message ServiceConfig { 12 | // The name that the service will be known as. Primary use is to address 13 | // messages to the service. The service names 'server' and 'client' are 14 | // reserved. 15 | string name = 1; 16 | 17 | // The name of the factory which will be used to generate the service. 18 | string factory = 2; 19 | 20 | // The maximum number of simultaneous calls to the service's ProcessMessage 21 | // method. If unset, defaults to 100. 22 | uint32 max_parallelism = 3; 23 | 24 | // Additional configuration information for the factory to use when setting up 25 | // the service. The allowed type depends upon the factory. 26 | google.protobuf.Any config = 4; 27 | } 28 | -------------------------------------------------------------------------------- /fleetspeak/src/server/sertesting/cache.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sertesting 16 | 17 | import ( 18 | "time" 19 | 20 | "github.com/google/fleetspeak/fleetspeak/src/server/internal/cache" 21 | ) 22 | 23 | // SetClientCacheMaxAge adjusts the maximum age that cached client data is 24 | // considered valid for. This can be used to make tests run faster. 25 | func SetClientCacheMaxAge(d time.Duration) func() { 26 | o := cache.MaxAge 27 | cache.MaxAge = d 28 | return func() { 29 | cache.MaxAge = o 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /fleetspeak/src/server/sertesting/context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sertesting 16 | 17 | import ( 18 | "context" 19 | 20 | "github.com/google/fleetspeak/fleetspeak/src/common" 21 | "github.com/google/fleetspeak/fleetspeak/src/server/db" 22 | 23 | fspb "github.com/google/fleetspeak/fleetspeak/src/common/proto/fleetspeak" 24 | ) 25 | 26 | // A FakeContext is a fake service.Context, suitable for unit testing services. 27 | type FakeContext struct { 28 | SentMessages chan *fspb.Message 29 | ClientData map[common.ClientID]*db.ClientData 30 | } 31 | 32 | func (c *FakeContext) Send(ctx context.Context, m *fspb.Message) error { 33 | select { 34 | case c.SentMessages <- m: 35 | return nil 36 | case <-ctx.Done(): 37 | return ctx.Err() 38 | } 39 | } 40 | 41 | func (c *FakeContext) GetClientData(_ context.Context, id common.ClientID) (*db.ClientData, error) { 42 | return c.ClientData[id], nil 43 | } 44 | -------------------------------------------------------------------------------- /fleetspeak/src/server/sertesting/retry.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sertesting 16 | 17 | import ( 18 | "time" 19 | 20 | "github.com/google/fleetspeak/fleetspeak/src/server/internal/ftime" 21 | ) 22 | 23 | // SetClientRetryTime changes db.ClientRetryTime method used by datastores to 24 | // decide when to retry requests to clients. It returns a closure which returns 25 | // the system to the previous state. 26 | // 27 | // This function and the returned closure are not thread safe. In particular, they should 28 | // be called at the start and end of tests when no Fleetspeak component is started. 29 | func SetClientRetryTime(f func() time.Time) func() { 30 | op := ftime.ClientRetryTime 31 | ftime.ClientRetryTime = f 32 | return func() { 33 | ftime.ClientRetryTime = op 34 | } 35 | } 36 | 37 | // SetServerRetryTime changes the db.ServerRetryTime used by the Fleetspeak 38 | // server to decide when to retry requests to clients. It returns a closure 39 | // which returns the system to the previous state. 40 | // 41 | // This function and the returned closure are not thread safe. In particular, they should 42 | // be called at the start and end of tests when no Fleetspeak component is started. 43 | func SetServerRetryTime(f func(uint32) time.Time) func() { 44 | op := ftime.ServerRetryTime 45 | ftime.ServerRetryTime = f 46 | return func() { 47 | ftime.ServerRetryTime = op 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /fleetspeak/src/server/sertesting/time.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package sertesting contains utilities useful for testing the fleetspeak server 16 | // and server components. 17 | package sertesting 18 | 19 | import ( 20 | "sync/atomic" 21 | "time" 22 | 23 | "github.com/google/fleetspeak/fleetspeak/src/server/internal/ftime" 24 | ) 25 | 26 | // FakeTime represents a fake time which can control the time seen by the fleetspeak 27 | // system during unit tests. 28 | type FakeTime struct { 29 | t int64 30 | orig func() time.Time 31 | } 32 | 33 | // Get returns the current fake time. 34 | func (t *FakeTime) Get() time.Time { 35 | return time.Unix(atomic.LoadInt64(&t.t), 0).UTC() 36 | } 37 | 38 | // SetSeconds sets the current fake time to s seconds since epoch. 39 | func (t *FakeTime) SetSeconds(s int64) { 40 | atomic.StoreInt64(&t.t, s) 41 | } 42 | 43 | // AddSeconds adds s seconds to the fake time. 44 | func (t *FakeTime) AddSeconds(s int64) { 45 | atomic.AddInt64(&t.t, s) 46 | } 47 | 48 | // Revert returns the time seen by the fleetspeak system to what it was 49 | // previously. 50 | func (t *FakeTime) Revert() { 51 | ftime.Now = t.orig 52 | } 53 | 54 | // FakeNow changes the implementation of Now used by the fleetspeak system. It 55 | // returns a FakeTime which controls the time that the system sees until Revert 56 | // is called. 57 | // 58 | // Note that this function, and FakeTime.Revert are not thread safe. In 59 | // particular it they should be called at the start and end of tests when no Fleetspeak 60 | // component is started. 61 | func FakeNow(start int64) *FakeTime { 62 | r := FakeTime{ 63 | t: start, 64 | orig: ftime.Now, 65 | } 66 | ftime.Now = r.Get 67 | return &r 68 | } 69 | -------------------------------------------------------------------------------- /fleetspeak/src/server/service/service_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package service 16 | 17 | import ( 18 | "errors" 19 | "testing" 20 | ) 21 | 22 | type netErr struct { 23 | msg string 24 | timeout bool 25 | } 26 | 27 | func (e netErr) Error() string { 28 | return e.msg 29 | } 30 | 31 | func (e netErr) Temporary() bool { 32 | return e.timeout 33 | } 34 | 35 | func (e netErr) Timeout() bool { 36 | return e.timeout 37 | } 38 | 39 | func TestIsTemporary(t *testing.T) { 40 | for _, tc := range []struct { 41 | e error 42 | want bool 43 | }{ 44 | { 45 | e: errors.New("a permanent error"), 46 | want: false, 47 | }, 48 | { 49 | e: TemporaryError{errors.New("a temporary error")}, 50 | want: true, 51 | }, 52 | { 53 | e: &TemporaryError{errors.New("a referenced temporary error")}, 54 | want: true, 55 | }, 56 | { 57 | e: netErr{msg: "A generic net.Error"}, 58 | want: false, 59 | }, 60 | { 61 | e: netErr{msg: "A timeout net.Error", timeout: true}, 62 | want: true, 63 | }, 64 | } { 65 | got := IsTemporary(tc.e) 66 | if got != tc.want { 67 | t.Errorf("IsTemporary(%T)=%v, but want %v", tc.e, got, tc.want) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /fleetspeak/src/server/sqlite/filestore.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sqlite 16 | 17 | import ( 18 | "bytes" 19 | "context" 20 | "database/sql" 21 | "io" 22 | "time" 23 | 24 | "github.com/google/fleetspeak/fleetspeak/src/server/db" 25 | ) 26 | 27 | func (d *Datastore) StoreFile(ctx context.Context, service, name string, data io.Reader) error { 28 | b, err := io.ReadAll(data) 29 | if err != nil { 30 | return err 31 | } 32 | d.l.Lock() 33 | defer d.l.Unlock() 34 | return d.runInTx(func(tx *sql.Tx) error { 35 | _, err := tx.ExecContext(ctx, "INSERT OR REPLACE INTO files (service, name, modified_time_nanos, data) VALUES(?, ?, ?, ?)", 36 | service, name, db.Now().UnixNano(), b) 37 | return err 38 | }) 39 | } 40 | 41 | func (d *Datastore) StatFile(ctx context.Context, service, name string) (time.Time, error) { 42 | d.l.Lock() 43 | defer d.l.Unlock() 44 | 45 | var ts int64 46 | 47 | err := d.runInTx(func(tx *sql.Tx) error { 48 | row := tx.QueryRowContext(ctx, "SELECT modified_time_nanos FROM files WHERE service = ? AND name = ?", service, name) 49 | return row.Scan(&ts) 50 | }) 51 | 52 | return time.Unix(0, ts).UTC(), err 53 | } 54 | 55 | func (d *Datastore) ReadFile(ctx context.Context, service, name string) (data db.ReadSeekerCloser, modtime time.Time, err error) { 56 | d.l.Lock() 57 | defer d.l.Unlock() 58 | 59 | var b []byte 60 | var ts int64 61 | 62 | err = d.runInTx(func(tx *sql.Tx) error { 63 | row := tx.QueryRowContext(ctx, "SELECT modified_time_nanos, data FROM files WHERE service = ? AND name = ?", service, name) 64 | if err := row.Scan(&ts, &b); err != nil { 65 | b = nil 66 | return err 67 | } 68 | return nil 69 | }) 70 | 71 | if err != nil { 72 | return nil, time.Time{}, err 73 | } 74 | if b == nil { 75 | b = []byte{} 76 | } 77 | return db.NOOPCloser{ReadSeeker: bytes.NewReader(b)}, time.Unix(0, ts).UTC(), nil 78 | } 79 | -------------------------------------------------------------------------------- /fleetspeak/src/server/sqlite/sqlite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sqlite 16 | 17 | import ( 18 | "fmt" 19 | "path" 20 | "testing" 21 | 22 | "github.com/google/fleetspeak/fleetspeak/src/comtesting" 23 | 24 | log "github.com/golang/glog" 25 | 26 | "github.com/google/fleetspeak/fleetspeak/src/server/db" 27 | "github.com/google/fleetspeak/fleetspeak/src/server/dbtesting" 28 | ) 29 | 30 | type sqliteTestEnv struct { 31 | tempDirCleanup func() 32 | counter int 33 | } 34 | 35 | func (e *sqliteTestEnv) Create() error { 36 | return nil 37 | } 38 | 39 | func (e *sqliteTestEnv) Clean() (db.Store, error) { 40 | dir, fn := comtesting.GetTempDir("sqlite_test") 41 | e.tempDirCleanup = fn 42 | 43 | p := path.Join(dir, fmt.Sprintf("%d.sqlite", e.counter)) 44 | e.counter++ 45 | 46 | log.Infof("Using database: %v", p) 47 | s, err := MakeDatastore(p) 48 | if err != nil { 49 | return nil, err 50 | } 51 | return s, nil 52 | } 53 | 54 | func (e *sqliteTestEnv) Destroy() error { 55 | e.tempDirCleanup() 56 | return nil 57 | } 58 | 59 | func TestSqliteStore(t *testing.T) { 60 | dbtesting.DataStoreTestSuite(t, &sqliteTestEnv{}) 61 | } 62 | -------------------------------------------------------------------------------- /fleetspeak/src/windows/hashpipe/hashpipe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package hashpipe 16 | -------------------------------------------------------------------------------- /fleetspeak/src/windows/hashpipe/hashpipe_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package hashpipe 16 | 17 | import ( 18 | "testing" 19 | ) 20 | 21 | func TestNoop(t *testing.T) {} 22 | -------------------------------------------------------------------------------- /fleetspeak/src/windows/hashpipe/hashpipe_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build windows 16 | 17 | // Package hashpipe implements a name randomization mechanism over Windows 18 | // named pipes. 19 | package hashpipe 20 | 21 | import ( 22 | "crypto/rand" 23 | "encoding/base64" 24 | "fmt" 25 | "net" 26 | 27 | "github.com/Microsoft/go-winio" 28 | ) 29 | 30 | // ListenPipe refines winio.ListenPipe by providing a crypto-secure random name 31 | // for the pipe and returning the name alongside the normal net.Listener. 32 | func ListenPipe(c *winio.PipeConfig) (l net.Listener, path string, err error) { 33 | if path, err = randomizePath(); err != nil { 34 | return 35 | } 36 | 37 | l, err = winio.ListenPipe(path, c) 38 | return 39 | } 40 | 41 | func randomizePath() (string, error) { 42 | // 96 bytes of information gives us 128 base-64 characters. Windows named 43 | // pipes support paths of up to 256 characters. See: 44 | // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365783(v=vs.85).aspx 45 | randBuf := make([]byte, 96) 46 | 47 | if n, err := rand.Read(randBuf); err != nil { 48 | return "", fmt.Errorf("error in rand.Read (%d bytes read): %v", n, err) 49 | } 50 | 51 | randB64String := base64.URLEncoding.EncodeToString(randBuf) 52 | 53 | pipePath := `\\.\pipe\` + randB64String 54 | return pipePath, nil 55 | } 56 | -------------------------------------------------------------------------------- /fleetspeak/src/windows/regutil/regutil.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package regutil 16 | -------------------------------------------------------------------------------- /fleetspeak/src/windows/regutil/regutil_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build !windows 16 | 17 | package regutil 18 | 19 | import ( 20 | "testing" 21 | ) 22 | 23 | func TestNoop(t *testing.T) {} 24 | -------------------------------------------------------------------------------- /fleetspeak/src/windows/wnixsocket/wnixsocket.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package wnixsocket 16 | -------------------------------------------------------------------------------- /fleetspeak/src/windows/wnixsocket/wnixsocket_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package wnixsocket 16 | 17 | import ( 18 | "testing" 19 | ) 20 | 21 | func TestNoop(t *testing.T) {} 22 | -------------------------------------------------------------------------------- /fleetspeak/test-package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2019 Google Inc. 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 | # https://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 | set -ex 17 | 18 | /bin/echo 'Installing newly built server package.' 19 | apt install -y $1 20 | 21 | if [[ ! -z "$MYSQL_TEST_USER" ]] 22 | then 23 | sed -i "s/mysql_data_source_name: .*/mysql_data_source_name: \"$MYSQL_TEST_USER:$MYSQL_TEST_PASS@tcp($MYSQL_TEST_ADDR)\/$MYSQL_TEST_E2E_DB\"/g" /etc/fleetspeak-server/configurator.config 24 | fi 25 | 26 | sudo -u fleetspeak /usr/bin/fleetspeak-config --config=/etc/fleetspeak-server/configurator.config 27 | 28 | /bin/echo 'Checking that the installation was successful' 29 | ls -l /etc/fleetspeak-server 30 | find /etc/systemd/ -name 'fleetspeak*' 31 | 32 | # At this point the service is down, since right after the installation it was 33 | # started without a configuration. 34 | # Reset a list of failed services to ensure the restart below works fine. 35 | systemctl reset-failed 36 | 37 | # Restart the service. 38 | systemctl restart fleetspeak-server 39 | 40 | # Check that it's now up and running. 41 | systemctl is-active fleetspeak-server 42 | 43 | # Now copy the linux client configuration to the expected location. 44 | mkdir -p /etc/fleetspeak-client 45 | cp /etc/fleetspeak-server/linux.client.configuration /etc/fleetspeak-client/client.config 46 | 47 | # Install the client package. 48 | apt install -y $2 49 | 50 | # Check that the client is up and running. 51 | systemctl is-active fleetspeak-client 52 | 53 | journalctl -u fleetspeak-server 54 | journalctl -u fleetspeak-client 55 | systemctl -l status fleetspeak-server 56 | systemctl -l status fleetspeak-client 57 | 58 | # Check that fleetspeak_admin functions and returns info about a single client we have. 59 | fleetspeak-admin -admin_addr localhost:9000 listclients | grep "[a-z0-9]\{16\} .*client:linux" 60 | -------------------------------------------------------------------------------- /fleetspeak/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2017 Google Inc. 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 | # https://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 | # Get absolute path to symlink's directory 18 | set +e # Do not exit if readlink errors out 19 | SCRIPT_DIR="$(/usr/bin/dirname "$(readlink -e "${ARGV0}" 2>/dev/null)")" 20 | # Exit on error. 21 | set -e 22 | if [[ -z "${SCRIPT_DIR}" || "${SCRIPT_DIR}" == '.' ]]; then 23 | SCRIPT_DIR="$(/usr/bin/dirname "${BASH_SOURCE[0]}")" 24 | if [[ -z "${SCRIPT_DIR}" ]]; then 25 | /bin/echo 'Failed to resolve script directory.' 26 | exit 1 27 | fi 28 | fi 29 | # Go to this script's directory. 30 | cd "${SCRIPT_DIR}" 31 | 32 | readonly TEST_SHS='src/server/grpcservice/client/testing/client_test.sh' 33 | 34 | function test_single_sh { 35 | local readonly SH=${1} 36 | 37 | OUT=$("${SH}" 2>&1) 38 | 39 | if [ "$?" -ne 0 ]; then 40 | /bin/echo "FAIL ${SH}" 41 | /bin/echo "${OUT}" 42 | return 1 43 | fi 44 | 45 | /bin/echo "PASS ${SH}" 46 | } 47 | 48 | function pretty_echo { 49 | /bin/echo '---' 50 | /bin/echo "${@}" 51 | /bin/echo '---' 52 | } 53 | 54 | RC=0 55 | 56 | pretty_echo 'Building prerequisites for Go tests.' 57 | go build -o ./src/e2etesting/frr_master_server_main/frr_master_server_main{,.go} 58 | go build -o ./src/e2etesting/balancer/balancer{,.go} 59 | go build -o ./src/client/daemonservice/testclient/testclient{,.go} 60 | go build -o ./src/client/socketservice/testclient/testclient{,.go} 61 | go build -o ./src/server/grpcservice/client/testing/tester{,.go} 62 | cd .. 63 | # Required for src/e2etesting/localtesting. 64 | go build -o ./cmd/fleetspeak_config/fleetspeak_config{,.go} 65 | go build -o ./cmd/fleetspeak_client/fleetspeak_client{,.go} 66 | go build -o ./cmd/fleetspeak_server/fleetspeak_server{,.go} 67 | cd - 68 | 69 | pretty_echo 'Executing Go tests.' 70 | go test -race --timeout 2.5m ./... || RC=1 71 | 72 | pretty_echo 'Executing Python tests.' 73 | pytest -v ../fleetspeak_python || RC=2 74 | 75 | pretty_echo 'Executing Bash tests.' 76 | for s in ${TEST_SHS}; do 77 | test_single_sh "${s}" || RC=3 78 | done 79 | 80 | exit "${RC}" 81 | -------------------------------------------------------------------------------- /fleetspeak_python/fleetspeak/client_connector/testing/testclient_launcher.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2018 Google Inc. 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 | # https://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 | """Script that launches testclient.Loopback in another process.""" 17 | 18 | import subprocess 19 | 20 | from absl import app 21 | 22 | 23 | def main(argv=None): 24 | del argv 25 | p = subprocess.Popen( 26 | [ 27 | "python", 28 | "-m", 29 | "fleetspeak.client_connector.testing.testclient", 30 | "--", 31 | "--mode=loopback", 32 | ], 33 | # Make sure file descriptors passed from the parent Fleetspeak process are 34 | # not closed. This is critical for inter-process communication between 35 | # the Fleetspeak client and the test client. 36 | close_fds=False, 37 | ) 38 | p.communicate() 39 | p.wait() 40 | 41 | 42 | if __name__ == "__main__": 43 | app.run(main) 44 | -------------------------------------------------------------------------------- /fleetspeak_python/fleetspeak/server_connector/testing/loopback.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """A simple GRPCService based loopback. 16 | 17 | This script uses the GRPCService client library to receive messages from 18 | a fleetspeak server. They are returned to the sending address, again 19 | using the GRPCService client library. 20 | """ 21 | 22 | import logging 23 | import threading 24 | import time 25 | 26 | from absl import app 27 | from absl import flags 28 | from fleetspeak.server_connector import connector 29 | 30 | FLAGS = flags.FLAGS 31 | 32 | 33 | def main(argv=None): 34 | del argv # Unused. 35 | 36 | service_client = connector.InsecureGRPCServiceClient("TestService") 37 | seen = set() 38 | seen_lock = threading.Lock() 39 | 40 | def loop(message, context): 41 | """loop a message back to fleetspeak.""" 42 | del context # Unused 43 | 44 | logging.info("Received message.") 45 | with seen_lock: 46 | if message.message_id in seen: 47 | logging.warning("Ignoring duplicate.") 48 | return 49 | seen.add(message.message_id) 50 | 51 | message.ClearField("source_message_id") 52 | message.ClearField("message_id") 53 | message.destination.service_name = message.source.service_name 54 | message.destination.client_id = message.source.client_id 55 | 56 | service_client.outgoing.InsertMessage(message) 57 | logging.info("Sent message.") 58 | 59 | service_client.Listen(loop) 60 | while True: 61 | time.sleep(600) 62 | 63 | 64 | if __name__ == "__main__": 65 | app.run(main) 66 | -------------------------------------------------------------------------------- /frr_python/frr_client.py: -------------------------------------------------------------------------------- 1 | """FRR Fleetspeak client 2 | 3 | Receives TrafficRequestData messages from server service and responses with 4 | TrafficResponseData 5 | """ 6 | 7 | from absl import app 8 | from fleetspeak.client_connector.connector import FleetspeakConnection 9 | from fleetspeak.src.common.proto.fleetspeak.common_pb2 import Message 10 | from fleetspeak.src.inttesting.frr.proto.fleetspeak_frr.frr_pb2 import TrafficRequestData 11 | from fleetspeak.src.inttesting.frr.proto.fleetspeak_frr.frr_pb2 import TrafficResponseData 12 | 13 | 14 | def main(argv): 15 | del argv # Unused. 16 | 17 | connection = FleetspeakConnection(version="0.0.1") 18 | while True: 19 | request, _ = connection.Recv() 20 | if request.message_type != "TrafficRequest": 21 | continue 22 | 23 | request_data = TrafficRequestData() 24 | request.data.Unpack(request_data) 25 | 26 | response_data = TrafficResponseData( 27 | master_id=request_data.master_id, 28 | request_id=request_data.request_id, 29 | response_index=0, 30 | data=b"client response", 31 | fin=True, 32 | ) 33 | 34 | response = Message() 35 | response.destination.service_name = request.source.service_name 36 | response.data.Pack(response_data) 37 | response.message_type = "TrafficResponse" 38 | 39 | connection.Send(response) 40 | 41 | 42 | if __name__ == "__main__": 43 | app.run(main) 44 | -------------------------------------------------------------------------------- /frr_python/frr_server.py: -------------------------------------------------------------------------------- 1 | """FRR Fleetspeak server 2 | 3 | Receives messages from a client, prints them and forwards them to master server 4 | """ 5 | 6 | import logging 7 | import time 8 | from absl import app 9 | from absl import flags 10 | from fleetspeak.server_connector.connector import InsecureGRPCServiceClient 11 | from fleetspeak.src.inttesting.frr.proto.fleetspeak_frr.frr_pb2 import MessageInfo 12 | from fleetspeak.src.inttesting.frr.proto.fleetspeak_frr.frr_pb2 import TrafficResponseData 13 | from fleetspeak.src.inttesting.frr.proto.fleetspeak_frr.frr_pb2_grpc import MasterStub 14 | import grpc 15 | 16 | 17 | FLAGS = flags.FLAGS 18 | 19 | flags.DEFINE_string( 20 | name="master_server_address", 21 | default="localhost:6059", 22 | help="Address of master server to forward clients' messages", 23 | ) 24 | 25 | 26 | class Listener: 27 | """Connects to master server and processes messages from clients""" 28 | 29 | def __init__(self): 30 | channel = grpc.insecure_channel(FLAGS.master_server_address) 31 | self.stub = MasterStub(channel) 32 | 33 | def __call__(self, message, context): 34 | del context # Unused 35 | 36 | if message.message_type != "TrafficResponse": 37 | logging.info("Unknown message type: %s", message.message_type) 38 | return 39 | 40 | response_data = TrafficResponseData() 41 | message.data.Unpack(response_data) 42 | logging.info( 43 | "RESPONSE - master_id: %d, " 44 | "request_id: %d, " 45 | "response_index: %d, " 46 | "text: %s", 47 | response_data.master_id, 48 | response_data.request_id, 49 | response_data.response_index, 50 | response_data.data, 51 | ) 52 | 53 | self.stub.RecordTrafficResponse( 54 | MessageInfo(client_id=message.source.client_id, data=response_data) 55 | ) 56 | 57 | 58 | def main(argv=None): 59 | del argv # Unused. 60 | 61 | service_client = InsecureGRPCServiceClient("FRR") 62 | service_client.Listen(Listener()) 63 | 64 | while True: 65 | time.sleep(1) 66 | 67 | 68 | if __name__ == "__main__": 69 | app.run(main) 70 | -------------------------------------------------------------------------------- /frr_python/setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import sys 4 | 5 | from setuptools import setup 6 | from setuptools.command.develop import develop 7 | 8 | GRPCIO_TOOLS = "grpcio-tools==1.69.0" 9 | THIS_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) 10 | os.chdir(THIS_DIRECTORY) 11 | 12 | 13 | def compile_protos(): 14 | """Compiles all proto files.""" 15 | p = subprocess.Popen( 16 | [sys.executable, "-m", "grpc_tools.protoc", "--help"], 17 | stdout=subprocess.PIPE, 18 | stderr=subprocess.PIPE, 19 | ) 20 | p.communicate() 21 | if p.returncode != 0: 22 | subprocess.check_call( 23 | [sys.executable, "-m", "pip", "install", GRPCIO_TOOLS] 24 | ) 25 | 26 | proto_files = [] 27 | for dir_path, _, filenames in os.walk(os.path.join(THIS_DIRECTORY, "..")): 28 | for filename in filenames: 29 | if filename.endswith(".proto"): 30 | proto_files.append(os.path.join(dir_path, filename)) 31 | if not proto_files: 32 | return 33 | 34 | root_dir = os.path.join(THIS_DIRECTORY, "..") 35 | protoc_command = [ 36 | "python", 37 | "-m", 38 | "grpc_tools.protoc", 39 | "--python_out", 40 | THIS_DIRECTORY, 41 | "--grpc_python_out", 42 | THIS_DIRECTORY, 43 | "--proto_path", 44 | root_dir, 45 | ] 46 | protoc_command.extend(proto_files) 47 | subprocess.check_call(protoc_command, cwd=root_dir) 48 | 49 | 50 | class Develop(develop): 51 | 52 | def run(self): 53 | compile_protos() 54 | develop.run(self) 55 | 56 | 57 | setup( 58 | name="frr_python", 59 | description="Frr server and client services", 60 | url="https://github.com/google/fleetspeak/tree/master/frr_python", 61 | maintainer="GRR Development Team", 62 | maintainer_email="grr-dev@googlegroups.com", 63 | license="Apache License, Version 2.0", 64 | py_modules=["frr_client", "frr_server"], 65 | install_requires=["absl-py>=0.8.0", "fleetspeak>=0.1.7"], 66 | cmdclass={ 67 | "develop": Develop, 68 | }, 69 | ) 70 | -------------------------------------------------------------------------------- /sandboxes/cleartext-header-mode/config/fleetspeak-client/communicator.txt: -------------------------------------------------------------------------------- 1 | prefer_http2: true 2 | -------------------------------------------------------------------------------- /sandboxes/cleartext-header-mode/config/fleetspeak-client/config.textproto: -------------------------------------------------------------------------------- 1 | server: "fleetspeak-frontend:10000" 2 | client_certificate_header: "client-certificate" 3 | trusted_certs:"FRONTEND_CERTIFICATE" 4 | client_label: "" 5 | filesystem_handler: { 6 | configuration_directory:"/config/fleetspeak-client" 7 | state_file:"/fleetspeak-client.state" 8 | } 9 | streaming:true 10 | -------------------------------------------------------------------------------- /sandboxes/cleartext-header-mode/config/fleetspeak-client/textservices/hello.service: -------------------------------------------------------------------------------- 1 | name: "hello" 2 | factory: "Daemon" 3 | config: { 4 | [type.googleapis.com/fleetspeak.daemonservice.Config]: { 5 | argv: "/venv/FSENV/bin/python" 6 | argv: "/config/hello.py" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /sandboxes/cleartext-header-mode/config/fleetspeak-server/components.textproto: -------------------------------------------------------------------------------- 1 | mysql_data_source_name:"fleetspeak-user:fleetspeak-password@tcp(mysql-server:3306)/fleetspeak" 2 | https_config: { 3 | listen_address: "0.0.0.0:9090" 4 | certificates:"FRONTEND_CERTIFICATE" 5 | key:"FRONTEND_KEY" 6 | frontend_config: { 7 | cleartext_header_checksum_config: { 8 | client_certificate_header: "client-certificate" 9 | client_certificate_checksum_header: "x-client-cert-hash" 10 | } 11 | } 12 | } 13 | admin_config: { 14 | listen_address: "0.0.0.0:9091" 15 | } 16 | health_check_config: { 17 | listen_address: "0.0.0.0:8080" 18 | } 19 | notification_use_http_notifier:false 20 | -------------------------------------------------------------------------------- /sandboxes/cleartext-header-mode/config/fleetspeak-server/services.textproto: -------------------------------------------------------------------------------- 1 | services { 2 | name: "greeter" 3 | factory: "GRPC" 4 | config: { 5 | [type.googleapis.com/fleetspeak.grpcservice.Config] { 6 | target: "greeter:1337" 7 | insecure: true 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /sandboxes/cleartext-header-mode/config/fleetspeak.textproto: -------------------------------------------------------------------------------- 1 | configuration_name: "Example" 2 | 3 | components_config { 4 | 5 | mysql_data_source_name: "fleetspeak-user:fleetspeak-password@tcp(mysql-server:3306)/fleetspeak" 6 | 7 | https_config { 8 | listen_address: "fleetspeak-server:9090" 9 | } 10 | 11 | admin_config { 12 | listen_address: "fleetspeak-server:9091" 13 | } 14 | } 15 | 16 | public_host_port: "fleetspeak-server:9090" 17 | 18 | trusted_cert_file: "/config/fleetspeak-server/ca.pem" 19 | trusted_cert_key_file: "/config/fleetspeak-server/ca-key.pem" 20 | 21 | server_cert_file: "/config/fleetspeak-server/server.pem" 22 | server_cert_key_file: "/config/fleetspeak-server/server-key.pem" 23 | 24 | server_component_configuration_file: "/config/fleetspeak-server/components.textproto" 25 | linux_client_configuration_file: "/config/fleetspeak-client/config.textproto" 26 | -------------------------------------------------------------------------------- /sandboxes/cleartext-header-mode/config/hello.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from absl import app 16 | from fleetspeak.client_connector.connector import FleetspeakConnection 17 | from fleetspeak.src.common.proto.fleetspeak.common_pb2 import Message 18 | from google.protobuf.wrappers_pb2 import StringValue 19 | 20 | 21 | def main(argv): 22 | del argv # Unused. 23 | 24 | conn = FleetspeakConnection(version="0.0.1") 25 | while True: 26 | request, _ = conn.Recv() 27 | 28 | data = StringValue() 29 | request.data.Unpack(data) 30 | 31 | data.value = f"Hello {data.value}!" 32 | 33 | response = Message() 34 | response.destination.service_name = request.source.service_name 35 | response.data.Pack(data) 36 | 37 | conn.Send(response) 38 | 39 | 40 | if __name__ == "__main__": 41 | app.run(main) 42 | -------------------------------------------------------------------------------- /sandboxes/cleartext-header-mode/config/hello.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | /venv/FSENV/bin/python /config/hello.py 3 | -------------------------------------------------------------------------------- /sandboxes/cleartext-header-mode/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | 3 | mysql-server: 4 | image: mysql:8.2 5 | restart: always 6 | hostname: mysql-server 7 | environment: 8 | MYSQL_DATABASE: 'fleetspeak' 9 | MYSQL_USER: 'fleetspeak-user' 10 | MYSQL_PASSWORD: 'fleetspeak-password' 11 | MYSQL_ROOT_PASSWORD: 'password' 12 | ports: 13 | - '3306:3306' 14 | expose: 15 | - '3306' 16 | healthcheck: 17 | test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] 18 | timeout: 5s 19 | retries: 10 20 | 21 | front-envoy: 22 | build: 23 | context: . 24 | dockerfile: ../shared/envoy/Dockerfile 25 | args: 26 | ENVOY_CONFIG: ./envoy-https-http.yaml 27 | hostname: fleetspeak-frontend 28 | ports: 29 | - "10000:10000" 30 | 31 | fleetspeak-server: 32 | build: 33 | context: . 34 | dockerfile: ../shared/fleetspeak-server/Dockerfile 35 | hostname: fleetspeak-server 36 | depends_on: 37 | mysql-server: 38 | condition: service_healthy 39 | entrypoint: ["/app/bin/server", "-components_config", "/config/fleetspeak-server/components.textproto", "-services_config", "/config/fleetspeak-server/services.textproto", "-alsologtostderr"] 40 | volumes: 41 | - "./config:/config" 42 | ports: 43 | - '9090:9090' 44 | - '9091:9091' 45 | - '8080:8080' 46 | expose: 47 | - '9090' 48 | - '9091' 49 | - '8080' 50 | healthcheck: 51 | test: ["CMD", "curl", "http://localhost:8080"] 52 | timeout: 5s 53 | retries: 10 54 | 55 | fleetspeak-client: 56 | build: 57 | context: . 58 | dockerfile: ../shared/fleetspeak-client/Dockerfile 59 | hostname: fleetspeak-client 60 | depends_on: 61 | fleetspeak-server: 62 | condition: service_healthy 63 | entrypoint: ["/app/bin/client", "-config", "/config/fleetspeak-client/config.textproto", "-alsologtostderr"] 64 | volumes: 65 | - "./config:/config" 66 | -------------------------------------------------------------------------------- /sandboxes/cleartext-xfcc-mode/config/fleetspeak-client/communicator.txt: -------------------------------------------------------------------------------- 1 | prefer_http2: true 2 | -------------------------------------------------------------------------------- /sandboxes/cleartext-xfcc-mode/config/fleetspeak-client/config.textproto: -------------------------------------------------------------------------------- 1 | server: "fleetspeak-frontend:10000" 2 | trusted_certs:"FRONTEND_CERTIFICATE" 3 | client_label: "" 4 | filesystem_handler: { 5 | configuration_directory:"/config/fleetspeak-client" 6 | state_file:"/fleetspeak-client.state" 7 | } 8 | streaming:true 9 | -------------------------------------------------------------------------------- /sandboxes/cleartext-xfcc-mode/config/fleetspeak-client/textservices/hello.service: -------------------------------------------------------------------------------- 1 | name: "hello" 2 | factory: "Daemon" 3 | config: { 4 | [type.googleapis.com/fleetspeak.daemonservice.Config]: { 5 | argv: "/venv/FSENV/bin/python" 6 | argv: "/config/hello.py" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /sandboxes/cleartext-xfcc-mode/config/fleetspeak-server/components.textproto: -------------------------------------------------------------------------------- 1 | mysql_data_source_name:"fleetspeak-user:fleetspeak-password@tcp(mysql-server:3306)/fleetspeak" 2 | https_config: { 3 | listen_address: "0.0.0.0:9090" 4 | certificates:"FRONTEND_CERTIFICATE" 5 | key:"FRONTEND_KEY" 6 | frontend_config: { 7 | cleartext_xfcc_config: { 8 | client_certificate_header: "x-forwarded-client-cert" 9 | } 10 | } 11 | } 12 | admin_config: { 13 | listen_address: "0.0.0.0:9091" 14 | } 15 | health_check_config: { 16 | listen_address: "0.0.0.0:8080" 17 | } 18 | notification_use_http_notifier:false 19 | -------------------------------------------------------------------------------- /sandboxes/cleartext-xfcc-mode/config/fleetspeak-server/services.textproto: -------------------------------------------------------------------------------- 1 | services { 2 | name: "greeter" 3 | factory: "GRPC" 4 | config: { 5 | [type.googleapis.com/fleetspeak.grpcservice.Config] { 6 | target: "greeter:1337" 7 | insecure: true 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /sandboxes/cleartext-xfcc-mode/config/fleetspeak.textproto: -------------------------------------------------------------------------------- 1 | configuration_name: "Example" 2 | 3 | components_config { 4 | 5 | mysql_data_source_name: "fleetspeak-user:fleetspeak-password@tcp(mysql-server:3306)/fleetspeak" 6 | 7 | https_config { 8 | listen_address: "fleetspeak-server:9090" 9 | } 10 | 11 | admin_config { 12 | listen_address: "fleetspeak-server:9091" 13 | } 14 | } 15 | 16 | public_host_port: "fleetspeak-server:9090" 17 | 18 | trusted_cert_file: "/config/fleetspeak-server/ca.pem" 19 | trusted_cert_key_file: "/config/fleetspeak-server/ca-key.pem" 20 | 21 | server_cert_file: "/config/fleetspeak-server/server.pem" 22 | server_cert_key_file: "/config/fleetspeak-server/server-key.pem" 23 | 24 | server_component_configuration_file: "/config/fleetspeak-server/components.textproto" 25 | linux_client_configuration_file: "/config/fleetspeak-client/config.textproto" 26 | -------------------------------------------------------------------------------- /sandboxes/cleartext-xfcc-mode/config/hello.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from absl import app 16 | from fleetspeak.client_connector.connector import FleetspeakConnection 17 | from fleetspeak.src.common.proto.fleetspeak.common_pb2 import Message 18 | from google.protobuf.wrappers_pb2 import StringValue 19 | 20 | 21 | def main(argv): 22 | del argv # Unused. 23 | 24 | conn = FleetspeakConnection(version="0.0.1") 25 | while True: 26 | request, _ = conn.Recv() 27 | 28 | data = StringValue() 29 | request.data.Unpack(data) 30 | 31 | data.value = f"Hello {data.value}!" 32 | 33 | response = Message() 34 | response.destination.service_name = request.source.service_name 35 | response.data.Pack(data) 36 | 37 | conn.Send(response) 38 | 39 | 40 | if __name__ == "__main__": 41 | app.run(main) 42 | -------------------------------------------------------------------------------- /sandboxes/cleartext-xfcc-mode/config/hello.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | /venv/FSENV/bin/python /config/hello.py 3 | -------------------------------------------------------------------------------- /sandboxes/cleartext-xfcc-mode/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | 3 | mysql-server: 4 | image: mysql:8.2 5 | restart: always 6 | hostname: mysql-server 7 | environment: 8 | MYSQL_DATABASE: 'fleetspeak' 9 | MYSQL_USER: 'fleetspeak-user' 10 | MYSQL_PASSWORD: 'fleetspeak-password' 11 | MYSQL_ROOT_PASSWORD: 'password' 12 | ports: 13 | - '3306:3306' 14 | expose: 15 | - '3306' 16 | healthcheck: 17 | test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] 18 | timeout: 5s 19 | retries: 10 20 | 21 | front-envoy: 22 | build: 23 | context: . 24 | dockerfile: ../shared/envoy/Dockerfile 25 | args: 26 | ENVOY_CONFIG: ./envoy-https-http.yaml 27 | hostname: fleetspeak-frontend 28 | ports: 29 | - "10000:10000" 30 | 31 | fleetspeak-server: 32 | build: 33 | context: . 34 | dockerfile: ../shared/fleetspeak-server/Dockerfile 35 | hostname: fleetspeak-server 36 | depends_on: 37 | mysql-server: 38 | condition: service_healthy 39 | entrypoint: ["/app/bin/server", "-components_config", "/config/fleetspeak-server/components.textproto", "-services_config", "/config/fleetspeak-server/services.textproto", "-alsologtostderr"] 40 | volumes: 41 | - "./config:/config" 42 | ports: 43 | - '9090:9090' 44 | - '9091:9091' 45 | - '8080:8080' 46 | expose: 47 | - '9090' 48 | - '9091' 49 | - '8080' 50 | healthcheck: 51 | test: ["CMD", "curl", "http://localhost:8080"] 52 | timeout: 5s 53 | retries: 10 54 | 55 | fleetspeak-client: 56 | build: 57 | context: . 58 | dockerfile: ../shared/fleetspeak-client/Dockerfile 59 | hostname: fleetspeak-client 60 | depends_on: 61 | fleetspeak-server: 62 | condition: service_healthy 63 | entrypoint: ["/app/bin/client", "-config", "/config/fleetspeak-client/config.textproto", "-alsologtostderr"] 64 | volumes: 65 | - "./config:/config" 66 | -------------------------------------------------------------------------------- /sandboxes/createConfig.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | openssl ecparam -list_curves 3 | 4 | # generate a private key for a curve 5 | openssl ecparam -name prime256v1 -genkey -noout -out key.pem 6 | 7 | # optional: generate corresponding public key 8 | openssl ec -in key.pem -pubout -out public-key.pem 9 | 10 | # create a self-signed certificate 11 | openssl req -new -x509 -key key.pem -out cert.pem -days 365 -subj "/C=AU/CN=fleetspeak-frontend" -addext "subjectAltName = DNS:fleetspeak-frontend" 12 | 13 | FRONTEND_CERTIFICATE=$(sed ':a;N;$!ba;s/\n/\\\\n/g' cert.pem) 14 | FRONTEND_KEY=$(sed ':a;N;$!ba;s/\n/\\\\n/g' key.pem) 15 | 16 | sed -i 's@FRONTEND_CERTIFICATE@'"$FRONTEND_CERTIFICATE"'@' ./cleartext-header-mode/config/fleetspeak-client/config.textproto 17 | sed -i 's@FRONTEND_CERTIFICATE@'"$FRONTEND_CERTIFICATE"'@' ./cleartext-xfcc-mode/config/fleetspeak-client/config.textproto 18 | sed -i 's@FRONTEND_CERTIFICATE@'"$FRONTEND_CERTIFICATE"'@' ./direct-mtls-mode/config/fleetspeak-client/config.textproto 19 | sed -i 's@FRONTEND_CERTIFICATE@'"$FRONTEND_CERTIFICATE"'@' ./https-header-mode/config/fleetspeak-client/config.textproto 20 | sed -i 's@FRONTEND_CERTIFICATE@'"$FRONTEND_CERTIFICATE"'@' ./passthrough-mode/config/fleetspeak-client/config.textproto 21 | 22 | sed -i 's@FRONTEND_CERTIFICATE@'"$FRONTEND_CERTIFICATE"'@' ./cleartext-header-mode/config/fleetspeak-server/components.textproto 23 | sed -i 's@FRONTEND_CERTIFICATE@'"$FRONTEND_CERTIFICATE"'@' ./cleartext-xfcc-mode/config/fleetspeak-server/components.textproto 24 | sed -i 's@FRONTEND_CERTIFICATE@'"$FRONTEND_CERTIFICATE"'@' ./direct-mtls-mode/config/fleetspeak-server/components.textproto 25 | sed -i 's@FRONTEND_CERTIFICATE@'"$FRONTEND_CERTIFICATE"'@' ./https-header-mode/config/fleetspeak-server/components.textproto 26 | sed -i 's@FRONTEND_CERTIFICATE@'"$FRONTEND_CERTIFICATE"'@' ./passthrough-mode/config/fleetspeak-server/components.textproto 27 | 28 | sed -i 's@FRONTEND_KEY@'"$FRONTEND_KEY"'@' ./cleartext-header-mode/config/fleetspeak-server/components.textproto 29 | sed -i 's@FRONTEND_KEY@'"$FRONTEND_KEY"'@' ./cleartext-xfcc-mode/config/fleetspeak-server/components.textproto 30 | sed -i 's@FRONTEND_KEY@'"$FRONTEND_KEY"'@' ./direct-mtls-mode/config/fleetspeak-server/components.textproto 31 | sed -i 's@FRONTEND_KEY@'"$FRONTEND_KEY"'@' ./https-header-mode/config/fleetspeak-server/components.textproto 32 | sed -i 's@FRONTEND_KEY@'"$FRONTEND_KEY"'@' ./passthrough-mode/config/fleetspeak-server/components.textproto 33 | 34 | cp cert.pem key.pem ./cleartext-header-mode/ 35 | cp cert.pem key.pem ./cleartext-xfcc-mode/ 36 | cp cert.pem key.pem ./direct-mtls-mode/ 37 | cp cert.pem key.pem ./https-header-mode/ 38 | cp cert.pem key.pem ./passthrough-mode/ 39 | -------------------------------------------------------------------------------- /sandboxes/diagrams/cleartextHeaderMode_355.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/fleetspeak/dde186c1e913f5160df82afbe2f84bcd72900c1e/sandboxes/diagrams/cleartextHeaderMode_355.png -------------------------------------------------------------------------------- /sandboxes/diagrams/cleartextXfccMode_355.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/fleetspeak/dde186c1e913f5160df82afbe2f84bcd72900c1e/sandboxes/diagrams/cleartextXfccMode_355.png -------------------------------------------------------------------------------- /sandboxes/diagrams/directMode_355.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/fleetspeak/dde186c1e913f5160df82afbe2f84bcd72900c1e/sandboxes/diagrams/directMode_355.png -------------------------------------------------------------------------------- /sandboxes/diagrams/httpsHeaderMode_355.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/fleetspeak/dde186c1e913f5160df82afbe2f84bcd72900c1e/sandboxes/diagrams/httpsHeaderMode_355.png -------------------------------------------------------------------------------- /sandboxes/diagrams/passthroughMode_355.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/fleetspeak/dde186c1e913f5160df82afbe2f84bcd72900c1e/sandboxes/diagrams/passthroughMode_355.png -------------------------------------------------------------------------------- /sandboxes/direct-mtls-mode/config/fleetspeak-client/communicator.txt: -------------------------------------------------------------------------------- 1 | prefer_http2: true 2 | -------------------------------------------------------------------------------- /sandboxes/direct-mtls-mode/config/fleetspeak-client/config.textproto: -------------------------------------------------------------------------------- 1 | server:"fleetspeak-frontend:9090" 2 | trusted_certs:"FRONTEND_CERTIFICATE" 3 | client_label:"" 4 | filesystem_handler: { 5 | configuration_directory:"/config/fleetspeak-client" 6 | state_file:"/fleetspeak-client.state" 7 | } 8 | streaming:true 9 | -------------------------------------------------------------------------------- /sandboxes/direct-mtls-mode/config/fleetspeak-client/textservices/hello.service: -------------------------------------------------------------------------------- 1 | name: "hello" 2 | factory: "Daemon" 3 | config: { 4 | [type.googleapis.com/fleetspeak.daemonservice.Config]: { 5 | argv: "/venv/FSENV/bin/python" 6 | argv: "/config/hello.py" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /sandboxes/direct-mtls-mode/config/fleetspeak-server/components.textproto: -------------------------------------------------------------------------------- 1 | mysql_data_source_name:"fleetspeak-user:fleetspeak-password@tcp(mysql-server:3306)/fleetspeak" 2 | https_config: { 3 | listen_address: "0.0.0.0:9090" 4 | certificates:"FRONTEND_CERTIFICATE" 5 | key:"FRONTEND_KEY" 6 | } 7 | admin_config: { 8 | listen_address: "0.0.0.0:9091" 9 | } 10 | health_check_config: { 11 | listen_address: "0.0.0.0:8080" 12 | } 13 | notification_use_http_notifier:false 14 | -------------------------------------------------------------------------------- /sandboxes/direct-mtls-mode/config/fleetspeak-server/services.textproto: -------------------------------------------------------------------------------- 1 | services { 2 | name: "greeter" 3 | factory: "GRPC" 4 | config: { 5 | [type.googleapis.com/fleetspeak.grpcservice.Config] { 6 | target: "greeter:1337" 7 | insecure: true 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /sandboxes/direct-mtls-mode/config/fleetspeak.textproto: -------------------------------------------------------------------------------- 1 | configuration_name: "Example" 2 | 3 | components_config { 4 | 5 | mysql_data_source_name: "fleetspeak-user:fleetspeak-password@tcp(mysql-server:3306)/fleetspeak" 6 | 7 | https_config { 8 | listen_address: "fleetspeak-server:9090" 9 | } 10 | 11 | admin_config { 12 | listen_address: "fleetspeak-server:9091" 13 | } 14 | } 15 | 16 | public_host_port: "fleetspeak-server:9090" 17 | 18 | trusted_cert_file: "/config/fleetspeak-server/ca.pem" 19 | trusted_cert_key_file: "/config/fleetspeak-server/ca-key.pem" 20 | 21 | server_cert_file: "/config/fleetspeak-server/server.pem" 22 | server_cert_key_file: "/config/fleetspeak-server/server-key.pem" 23 | 24 | server_component_configuration_file: "/config/fleetspeak-server/components.textproto" 25 | linux_client_configuration_file: "/config/fleetspeak-client/config.textproto" 26 | -------------------------------------------------------------------------------- /sandboxes/direct-mtls-mode/config/hello.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from absl import app 16 | from fleetspeak.client_connector.connector import FleetspeakConnection 17 | from fleetspeak.src.common.proto.fleetspeak.common_pb2 import Message 18 | from google.protobuf.wrappers_pb2 import StringValue 19 | 20 | 21 | def main(argv): 22 | del argv # Unused. 23 | 24 | conn = FleetspeakConnection(version="0.0.1") 25 | while True: 26 | request, _ = conn.Recv() 27 | 28 | data = StringValue() 29 | request.data.Unpack(data) 30 | 31 | data.value = f"Hello {data.value}!" 32 | 33 | response = Message() 34 | response.destination.service_name = request.source.service_name 35 | response.data.Pack(data) 36 | 37 | conn.Send(response) 38 | 39 | 40 | if __name__ == "__main__": 41 | app.run(main) 42 | -------------------------------------------------------------------------------- /sandboxes/direct-mtls-mode/config/hello.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | /venv/FSENV/bin/python /config/hello.py 3 | -------------------------------------------------------------------------------- /sandboxes/direct-mtls-mode/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | 3 | mysql-server: 4 | image: mysql:8.2 5 | restart: always 6 | hostname: mysql-server 7 | environment: 8 | MYSQL_DATABASE: 'fleetspeak' 9 | MYSQL_USER: 'fleetspeak-user' 10 | MYSQL_PASSWORD: 'fleetspeak-password' 11 | MYSQL_ROOT_PASSWORD: 'password' 12 | ports: 13 | - '3306:3306' 14 | expose: 15 | - '3306' 16 | healthcheck: 17 | test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] 18 | timeout: 5s 19 | retries: 10 20 | 21 | fleetspeak-server: 22 | build: 23 | context: . 24 | dockerfile: ../shared/fleetspeak-server/Dockerfile 25 | hostname: fleetspeak-frontend 26 | depends_on: 27 | mysql-server: 28 | condition: service_healthy 29 | entrypoint: ["/app/bin/server", "-components_config", "/config/fleetspeak-server/components.textproto", "-services_config", "/config/fleetspeak-server/services.textproto", "-alsologtostderr"] 30 | volumes: 31 | - "./config:/config" 32 | ports: 33 | - '9090:9090' 34 | - '9091:9091' 35 | - '8080:8080' 36 | expose: 37 | - '9090' 38 | - '9091' 39 | - '8080' 40 | healthcheck: 41 | test: ["CMD", "curl", "http://localhost:8080"] 42 | timeout: 5s 43 | retries: 10 44 | 45 | fleetspeak-client: 46 | build: 47 | context: . 48 | dockerfile: ../shared/fleetspeak-client/Dockerfile 49 | hostname: fleetspeak-client 50 | depends_on: 51 | fleetspeak-server: 52 | condition: service_healthy 53 | entrypoint: ["/app/bin/client", "-config", "/config/fleetspeak-client/config.textproto", "-alsologtostderr"] 54 | volumes: 55 | - "./config:/config" 56 | -------------------------------------------------------------------------------- /sandboxes/https-header-mode/config/fleetspeak-client/communicator.txt: -------------------------------------------------------------------------------- 1 | prefer_http2: true 2 | -------------------------------------------------------------------------------- /sandboxes/https-header-mode/config/fleetspeak-client/config.textproto: -------------------------------------------------------------------------------- 1 | server: "fleetspeak-frontend:10000" 2 | client_certificate_header: "client-certificate" 3 | trusted_certs:"FRONTEND_CERTIFICATE" 4 | client_label: "" 5 | filesystem_handler: { 6 | configuration_directory:"/config/fleetspeak-client" 7 | state_file:"/fleetspeak-client.state" 8 | } 9 | streaming:true 10 | -------------------------------------------------------------------------------- /sandboxes/https-header-mode/config/fleetspeak-client/textservices/hello.service: -------------------------------------------------------------------------------- 1 | name: "hello" 2 | factory: "Daemon" 3 | config: { 4 | [type.googleapis.com/fleetspeak.daemonservice.Config]: { 5 | argv: "/venv/FSENV/bin/python" 6 | argv: "/config/hello.py" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /sandboxes/https-header-mode/config/fleetspeak-server/components.textproto: -------------------------------------------------------------------------------- 1 | mysql_data_source_name:"fleetspeak-user:fleetspeak-password@tcp(mysql-server:3306)/fleetspeak" 2 | https_config: { 3 | listen_address: "0.0.0.0:9090" 4 | certificates:"FRONTEND_CERTIFICATE" 5 | key:"FRONTEND_KEY" 6 | frontend_config: { 7 | https_header_checksum_config: { 8 | client_certificate_header: "client-certificate" 9 | client_certificate_checksum_header: "x-client-cert-hash" 10 | } 11 | } 12 | } 13 | admin_config: { 14 | listen_address: "0.0.0.0:9091" 15 | } 16 | health_check_config: { 17 | listen_address: "0.0.0.0:8080" 18 | } 19 | notification_use_http_notifier:false 20 | -------------------------------------------------------------------------------- /sandboxes/https-header-mode/config/fleetspeak-server/services.textproto: -------------------------------------------------------------------------------- 1 | services { 2 | name: "greeter" 3 | factory: "GRPC" 4 | config: { 5 | [type.googleapis.com/fleetspeak.grpcservice.Config] { 6 | target: "greeter:1337" 7 | insecure: true 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /sandboxes/https-header-mode/config/fleetspeak.textproto: -------------------------------------------------------------------------------- 1 | configuration_name: "Example" 2 | 3 | components_config { 4 | 5 | mysql_data_source_name: "fleetspeak-user:fleetspeak-password@tcp(mysql-server:3306)/fleetspeak" 6 | 7 | https_config { 8 | listen_address: "fleetspeak-server:9090" 9 | } 10 | 11 | admin_config { 12 | listen_address: "fleetspeak-server:9091" 13 | } 14 | } 15 | 16 | public_host_port: "fleetspeak-server:9090" 17 | 18 | trusted_cert_file: "/config/fleetspeak-server/ca.pem" 19 | trusted_cert_key_file: "/config/fleetspeak-server/ca-key.pem" 20 | 21 | server_cert_file: "/config/fleetspeak-server/server.pem" 22 | server_cert_key_file: "/config/fleetspeak-server/server-key.pem" 23 | 24 | server_component_configuration_file: "/config/fleetspeak-server/components.textproto" 25 | linux_client_configuration_file: "/config/fleetspeak-client/config.textproto" 26 | -------------------------------------------------------------------------------- /sandboxes/https-header-mode/config/hello.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from absl import app 16 | from fleetspeak.client_connector.connector import FleetspeakConnection 17 | from fleetspeak.src.common.proto.fleetspeak.common_pb2 import Message 18 | from google.protobuf.wrappers_pb2 import StringValue 19 | 20 | 21 | def main(argv): 22 | del argv # Unused. 23 | 24 | conn = FleetspeakConnection(version="0.0.1") 25 | while True: 26 | request, _ = conn.Recv() 27 | 28 | data = StringValue() 29 | request.data.Unpack(data) 30 | 31 | data.value = f"Hello {data.value}!" 32 | 33 | response = Message() 34 | response.destination.service_name = request.source.service_name 35 | response.data.Pack(data) 36 | 37 | conn.Send(response) 38 | 39 | 40 | if __name__ == "__main__": 41 | app.run(main) 42 | -------------------------------------------------------------------------------- /sandboxes/https-header-mode/config/hello.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | /venv/FSENV/bin/python /config/hello.py 3 | -------------------------------------------------------------------------------- /sandboxes/https-header-mode/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | 3 | mysql-server: 4 | image: mysql:8.2 5 | restart: always 6 | hostname: mysql-server 7 | environment: 8 | MYSQL_DATABASE: 'fleetspeak' 9 | MYSQL_USER: 'fleetspeak-user' 10 | MYSQL_PASSWORD: 'fleetspeak-password' 11 | MYSQL_ROOT_PASSWORD: 'password' 12 | ports: 13 | - '3306:3306' 14 | expose: 15 | - '3306' 16 | healthcheck: 17 | test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] 18 | timeout: 5s 19 | retries: 10 20 | 21 | front-envoy: 22 | build: 23 | context: . 24 | dockerfile: ../shared/envoy/Dockerfile 25 | args: 26 | ENVOY_CONFIG: ./envoy-https-https.yaml 27 | hostname: fleetspeak-frontend 28 | ports: 29 | - "10000:10000" 30 | 31 | fleetspeak-server: 32 | build: 33 | context: . 34 | dockerfile: ../shared/fleetspeak-server/Dockerfile 35 | hostname: fleetspeak-server 36 | depends_on: 37 | mysql-server: 38 | condition: service_healthy 39 | entrypoint: ["/app/bin/server", "-components_config", "/config/fleetspeak-server/components.textproto", "-services_config", "/config/fleetspeak-server/services.textproto", "-alsologtostderr"] 40 | volumes: 41 | - "./config:/config" 42 | ports: 43 | - '9090:9090' 44 | - '9091:9091' 45 | - '8080:8080' 46 | expose: 47 | - '9090' 48 | - '9091' 49 | - '8080' 50 | healthcheck: 51 | test: ["CMD", "curl", "http://localhost:8080"] 52 | timeout: 5s 53 | retries: 10 54 | 55 | fleetspeak-client: 56 | build: 57 | context: . 58 | dockerfile: ../shared/fleetspeak-client/Dockerfile 59 | hostname: fleetspeak-client 60 | depends_on: 61 | fleetspeak-server: 62 | condition: service_healthy 63 | entrypoint: ["/app/bin/client", "-config", "/config/fleetspeak-client/config.textproto", "-alsologtostderr"] 64 | volumes: 65 | - "./config:/config" 66 | -------------------------------------------------------------------------------- /sandboxes/passthrough-mode/config/fleetspeak-client/communicator.txt: -------------------------------------------------------------------------------- 1 | prefer_http2: true 2 | -------------------------------------------------------------------------------- /sandboxes/passthrough-mode/config/fleetspeak-client/config.textproto: -------------------------------------------------------------------------------- 1 | server:"fleetspeak-frontend:10003" 2 | trusted_certs:"FRONTEND_CERTIFICATE" 3 | client_label:"" 4 | filesystem_handler: { 5 | configuration_directory:"/config/fleetspeak-client" 6 | state_file:"/fleetspeak-client.state" 7 | } 8 | streaming:true 9 | -------------------------------------------------------------------------------- /sandboxes/passthrough-mode/config/fleetspeak-client/textservices/hello.service: -------------------------------------------------------------------------------- 1 | name: "hello" 2 | factory: "Daemon" 3 | config: { 4 | [type.googleapis.com/fleetspeak.daemonservice.Config]: { 5 | argv: "/venv/FSENV/bin/python" 6 | argv: "/config/hello.py" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /sandboxes/passthrough-mode/config/fleetspeak-server/components.textproto: -------------------------------------------------------------------------------- 1 | mysql_data_source_name:"fleetspeak-user:fleetspeak-password@tcp(mysql-server:3306)/fleetspeak" 2 | https_config: { 3 | listen_address: "0.0.0.0:9090" 4 | certificates:"FRONTEND_CERTIFICATE" 5 | key:"FRONTEND_KEY" 6 | } 7 | admin_config: { 8 | listen_address: "0.0.0.0:9091" 9 | } 10 | health_check_config: { 11 | listen_address: "0.0.0.0:8080" 12 | } 13 | notification_use_http_notifier:false 14 | -------------------------------------------------------------------------------- /sandboxes/passthrough-mode/config/fleetspeak-server/services.textproto: -------------------------------------------------------------------------------- 1 | services { 2 | name: "greeter" 3 | factory: "GRPC" 4 | config: { 5 | [type.googleapis.com/fleetspeak.grpcservice.Config] { 6 | target: "greeter:1337" 7 | insecure: true 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /sandboxes/passthrough-mode/config/fleetspeak.textproto: -------------------------------------------------------------------------------- 1 | configuration_name: "Example" 2 | 3 | components_config { 4 | 5 | mysql_data_source_name: "fleetspeak-user:fleetspeak-password@tcp(mysql-server:3306)/fleetspeak" 6 | 7 | https_config { 8 | listen_address: "fleetspeak-server:9090" 9 | } 10 | 11 | admin_config { 12 | listen_address: "fleetspeak-server:9091" 13 | } 14 | } 15 | 16 | public_host_port: "fleetspeak-server:9090" 17 | 18 | trusted_cert_file: "/config/fleetspeak-server/ca.pem" 19 | trusted_cert_key_file: "/config/fleetspeak-server/ca-key.pem" 20 | 21 | server_cert_file: "/config/fleetspeak-server/server.pem" 22 | server_cert_key_file: "/config/fleetspeak-server/server-key.pem" 23 | 24 | server_component_configuration_file: "/config/fleetspeak-server/components.textproto" 25 | linux_client_configuration_file: "/config/fleetspeak-client/config.textproto" 26 | -------------------------------------------------------------------------------- /sandboxes/passthrough-mode/config/hello.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from absl import app 16 | from fleetspeak.client_connector.connector import FleetspeakConnection 17 | from fleetspeak.src.common.proto.fleetspeak.common_pb2 import Message 18 | from google.protobuf.wrappers_pb2 import StringValue 19 | 20 | 21 | def main(argv): 22 | del argv # Unused. 23 | 24 | conn = FleetspeakConnection(version="0.0.1") 25 | while True: 26 | request, _ = conn.Recv() 27 | 28 | data = StringValue() 29 | request.data.Unpack(data) 30 | 31 | data.value = f"Hello {data.value}!" 32 | 33 | response = Message() 34 | response.destination.service_name = request.source.service_name 35 | response.data.Pack(data) 36 | 37 | conn.Send(response) 38 | 39 | 40 | if __name__ == "__main__": 41 | app.run(main) 42 | -------------------------------------------------------------------------------- /sandboxes/passthrough-mode/config/hello.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | /venv/FSENV/bin/python /config/hello.py 3 | -------------------------------------------------------------------------------- /sandboxes/passthrough-mode/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | 3 | mysql-server: 4 | image: mysql:8.2 5 | restart: always 6 | hostname: mysql-server 7 | environment: 8 | MYSQL_DATABASE: 'fleetspeak' 9 | MYSQL_USER: 'fleetspeak-user' 10 | MYSQL_PASSWORD: 'fleetspeak-password' 11 | MYSQL_ROOT_PASSWORD: 'password' 12 | ports: 13 | - '3306:3306' 14 | expose: 15 | - '3306' 16 | healthcheck: 17 | test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] 18 | timeout: 5s 19 | retries: 10 20 | 21 | front-envoy: 22 | build: 23 | context: . 24 | dockerfile: ../shared/envoy/Dockerfile 25 | args: 26 | ENVOY_CONFIG: ./envoy-https-passthrough.yaml 27 | hostname: fleetspeak-frontend 28 | ports: 29 | - "10003:10003" 30 | expose: 31 | - '10003' 32 | 33 | fleetspeak-server: 34 | build: 35 | context: . 36 | dockerfile: ../shared/fleetspeak-server/Dockerfile 37 | hostname: fleetspeak-server 38 | depends_on: 39 | mysql-server: 40 | condition: service_healthy 41 | entrypoint: ["/app/bin/server", "-components_config", "/config/fleetspeak-server/components.textproto", "-services_config", "/config/fleetspeak-server/services.textproto", "-alsologtostderr"] 42 | volumes: 43 | - "./config:/config" 44 | ports: 45 | - '9090:9090' 46 | - '9091:9091' 47 | - '8080:8080' 48 | expose: 49 | - '9090' 50 | - '9091' 51 | - '8080' 52 | healthcheck: 53 | test: ["CMD", "curl", "http://localhost:8080"] 54 | timeout: 5s 55 | retries: 10 56 | 57 | fleetspeak-client: 58 | build: 59 | context: . 60 | dockerfile: ../shared/fleetspeak-client/Dockerfile 61 | hostname: fleetspeak-client 62 | depends_on: 63 | fleetspeak-server: 64 | condition: service_healthy 65 | entrypoint: ["/app/bin/client", "-config", "/config/fleetspeak-client/config.textproto", "-alsologtostderr"] 66 | volumes: 67 | - "./config:/config" 68 | -------------------------------------------------------------------------------- /sandboxes/passthrough-mode/envoy-https-passthrough.yaml: -------------------------------------------------------------------------------- 1 | static_resources: 2 | listeners: 3 | - address: 4 | socket_address: 5 | address: 0.0.0.0 6 | port_value: 10003 7 | filter_chains: 8 | - filters: 9 | - name: envoy.filters.network.tcp_proxy 10 | typed_config: 11 | "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy 12 | cluster: fleetspeak-server-cluster 13 | stat_prefix: https_passthrough 14 | 15 | clusters: 16 | - name: fleetspeak-server-cluster 17 | type: STRICT_DNS 18 | lb_policy: ROUND_ROBIN 19 | load_assignment: 20 | cluster_name: fleetspeak-server-cluster 21 | endpoints: 22 | - lb_endpoints: 23 | - endpoint: 24 | address: 25 | socket_address: 26 | address: fleetspeak-server 27 | port_value: 9090 28 | -------------------------------------------------------------------------------- /sandboxes/shared/fleetspeak-client/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | FROM golang:1.22 as builder 15 | 16 | RUN apt update && \ 17 | apt install -y python3-venv && \ 18 | apt install -y pip && \ 19 | apt install -y git 20 | 21 | WORKDIR / 22 | 23 | SHELL ["/bin/bash", "-c"] 24 | 25 | RUN git clone https://github.com/google/fleetspeak.git && \ 26 | cd fleetspeak && \ 27 | go get -u golang.org/x/lint/golint && \ 28 | ./fleetspeak/generate_protos_setup.sh 29 | 30 | ENV PATH="$HOME/.local/bin:$PATH" 31 | 32 | RUN mkdir -p /app/bin 33 | 34 | RUN cd /fleetspeak && \ 35 | python3 -m venv /venv/FSENV && \ 36 | source /venv/FSENV/bin/activate && \ 37 | pip install wheel pytest && \ 38 | pip install -e ./fleetspeak_python[test] && \ 39 | pip install -e ./frr_python && \ 40 | ./fleetspeak/generate_protos.sh && \ 41 | go build -o /app/bin/server ./cmd/fleetspeak_server && \ 42 | go build -o /app/bin/client ./cmd/fleetspeak_client && \ 43 | go build -o /app/bin/fleetspeak_config ./cmd/fleetspeak_config 44 | 45 | -------------------------------------------------------------------------------- /sandboxes/shared/fleetspeak-server/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | FROM golang:1.22 as builder 15 | 16 | RUN apt update && \ 17 | apt install -y python3-venv && \ 18 | apt install -y pip && \ 19 | apt install -y git 20 | 21 | WORKDIR / 22 | 23 | SHELL ["/bin/bash", "-c"] 24 | 25 | RUN git clone https://github.com/google/fleetspeak.git && \ 26 | cd fleetspeak && \ 27 | go get -u golang.org/x/lint/golint && \ 28 | ./fleetspeak/generate_protos_setup.sh 29 | 30 | ENV PATH="$HOME/.local/bin:$PATH" 31 | 32 | ENV GOBIN=/fleetspeak-bin 33 | 34 | RUN mkdir -p ${GOBIN} 35 | 36 | RUN cd /fleetspeak && \ 37 | python3 -m venv $HOME/.venv/FSENV && \ 38 | source $HOME/.venv/FSENV/bin/activate && \ 39 | pip install wheel pytest && \ 40 | pip install -e ./fleetspeak_python[test] && \ 41 | pip install -e ./frr_python && \ 42 | ./fleetspeak/generate_protos.sh && \ 43 | go install ./cmd/fleetspeak_server ./cmd/fleetspeak_client \ 44 | ./cmd/fleetspeak_config 45 | 46 | FROM golang:1.22 47 | 48 | RUN mkdir -p /app/bin 49 | 50 | COPY --from=builder /fleetspeak-bin/fleetspeak_server /app/bin/server 51 | COPY --from=builder /fleetspeak-bin/fleetspeak_client /app/bin/client 52 | COPY --from=builder /fleetspeak-bin/fleetspeak_config /app/bin/ 53 | -------------------------------------------------------------------------------- /sandboxes/shared/greeter/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | FROM golang:1.22 as builder 15 | 16 | RUN apt update && \ 17 | apt install -y python3-venv && \ 18 | apt install -y pip && \ 19 | apt install -y git 20 | 21 | WORKDIR / 22 | 23 | SHELL ["/bin/bash", "-c"] 24 | 25 | RUN git clone https://github.com/google/fleetspeak.git 26 | 27 | RUN cd /fleetspeak && \ 28 | python3 -m venv /venv/FSENV && \ 29 | source /venv/FSENV/bin/activate && \ 30 | pip install wheel pytest && \ 31 | pip install -e ./fleetspeak_python[test] && \ 32 | pip install -e ./frr_python 33 | 34 | COPY greeter.py . 35 | -------------------------------------------------------------------------------- /sandboxes/shared/greeter/greeter.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import binascii 15 | import logging 16 | 17 | from absl import app 18 | from absl import flags 19 | from fleetspeak.server_connector.connector import InsecureGRPCServiceClient 20 | from fleetspeak.src.common.proto.fleetspeak.common_pb2 import Message 21 | from google.protobuf.wrappers_pb2 import StringValue 22 | 23 | 24 | FLAGS = flags.FLAGS 25 | 26 | flags.DEFINE_string( 27 | name="client_id", 28 | default="", 29 | help="An id of the client to send the messages to.", 30 | ) 31 | 32 | 33 | def listener(message, context): 34 | del context # Unused 35 | 36 | data = StringValue() 37 | message.data.Unpack(data) 38 | logging.info(f"RESPONSE: {data.value}") 39 | 40 | 41 | def main(argv=None): 42 | del argv # Unused. 43 | 44 | service_client = InsecureGRPCServiceClient("greeter") 45 | service_client.Listen(listener) 46 | 47 | while True: 48 | data = StringValue() 49 | data.value = input("Enter your name: ") 50 | 51 | request = Message() 52 | request.destination.client_id = binascii.unhexlify(FLAGS.client_id) 53 | request.destination.service_name = "hello" 54 | request.data.Pack(data) 55 | 56 | service_client.Send(request) 57 | 58 | 59 | if __name__ == "__main__": 60 | app.run(main) 61 | -------------------------------------------------------------------------------- /spanner-setup/Dockerfile.dev: -------------------------------------------------------------------------------- 1 | FROM golang:1.22 AS builder 2 | 3 | RUN apt-get update && \ 4 | apt install -y python3-venv && \ 5 | apt install -y pip && \ 6 | apt install -y vim && \ 7 | apt-get install -y python-is-python3 8 | 9 | SHELL ["/bin/bash", "-c"] 10 | 11 | COPY . /fleetspeak 12 | 13 | ENV GOBIN=/fleetspeak/bin 14 | RUN mkdir -p $GOBIN 15 | RUN cd /fleetspeak && go install ./... 16 | 17 | RUN cd /fleetspeak && \ 18 | python3 -m venv /venv/FSENV && \ 19 | source /venv/FSENV/bin/activate && \ 20 | pip install wheel pytest && \ 21 | pip install -e ./fleetspeak_python[test] && \ 22 | pip install -e ./frr_python 23 | 24 | WORKDIR / 25 | 26 | RUN cd /fleetspeak && \ 27 | git clone https://github.com/googleapis/python-pubsub.git && \ 28 | cd python-pubsub/samples/snippets && \ 29 | source /venv/FSENV/bin/activate && \ 30 | pip install -r requirements.txt 31 | 32 | ENV FLEETSPEAK_BIN=/fleetspeak/bin 33 | 34 | RUN ln -s /fleetspeak/bin/fleetspeak_server $FLEETSPEAK_BIN/server 35 | RUN ln -s /fleetspeak/bin/fleetspeak_client $FLEETSPEAK_BIN/client 36 | 37 | ENV PATH="$FLEETSPEAK_BIN:$PATH" 38 | 39 | ENTRYPOINT [ "server" ] -------------------------------------------------------------------------------- /spanner-setup/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | mysql-db: 3 | image: mysql:8.2 4 | restart: always 5 | hostname: mysql-db 6 | environment: 7 | MYSQL_DATABASE: 'fleetspeak' 8 | MYSQL_USER: 'fleetspeak-user' 9 | MYSQL_PASSWORD: 'fleetspeak-password' 10 | MYSQL_ROOT_PASSWORD: 'password' 11 | ports: 12 | - '3306:3306' 13 | expose: 14 | - '3306' 15 | healthcheck: 16 | test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] 17 | timeout: 5s 18 | retries: 10 19 | 20 | fleetspeak: 21 | build: 22 | context: ../ 23 | dockerfile: ./spanner-setup/Dockerfile.dev 24 | hostname: fleetspeak-test 25 | depends_on: 26 | mysql-db: 27 | condition: service_healthy 28 | entrypoint: ["tail", "-F", "/fleetspeak/spanner-setup/setup.sh"] 29 | volumes: 30 | - "/home/user/.config/gcloud/:/root/.config/gcloud" 31 | 32 | pubsub-emulator: 33 | image: google/cloud-sdk:emulators 34 | restart: always 35 | hostname: pubsub-emulator 36 | entrypoint: ["/google-cloud-sdk/bin/gcloud", "beta", "emulators", "pubsub", "start", "--project=spanner-emulator-project", "--host-port=0.0.0.0:8085"] 37 | ports: 38 | - '8085:8085' 39 | expose: 40 | - '8085' 41 | 42 | spanner-emulator: 43 | image: gcr.io/cloud-spanner-emulator/emulator 44 | restart: always 45 | hostname: spanner-emulator 46 | ports: 47 | - '9010:9010' 48 | - '9020:9020' 49 | expose: 50 | - '9010' 51 | - '9020' -------------------------------------------------------------------------------- /spanner-setup/fleetspeak.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/fleetspeak/dde186c1e913f5160df82afbe2f84bcd72900c1e/spanner-setup/fleetspeak.pb -------------------------------------------------------------------------------- /terraform/README.md: -------------------------------------------------------------------------------- 1 | # Using Terraform to run Fleetspeak End-to-End testing in Google Cloud 2 | 3 | ## Installing Terraform 4 | 5 | Please follow 6 | [these instructions](https://www.terraform.io/intro/getting-started/install.html) 7 | to install Terraform binary on your machine. 8 | 9 | ## Setting up a Google Cloud Project 10 | 11 | 1. Create a new project in Google Cloud Platform console 12 | ([link](https://console.cloud.google.com/project)). 13 | 1. Enable billing for the project 14 | ([link](https://support.google.com/cloud/answer/6293499#enable-billing)). 15 | 1. Enable Compute Engine and Cloud SQL APIs 16 | ([link](https://console.cloud.google.com/flows/enableapi?apiid=compute_component,sqladmin)). 17 | 18 | ## Instrumenting Terraform with credentials 19 | 20 | 1. Install Google Cloud SDK ([link](https://cloud.google.com/sdk/install)). 21 | 2. Obtain Google Cloud credentials by running the following: `bash gcloud auth 22 | application-default login` Your credentials will be saved to a file 23 | `~/.config/gcloud/application_default_credentials.json`. It will be used by 24 | terraform. 25 | 26 | ## Running Terraform 27 | 28 | 1. `cd` to terraform directory 29 | 2. Run the following to initialize Terraform: `bash terraform init` 30 | 3. Then run the following to start installation: `bash terraform apply -var 31 | "project_name=*your_project_name*"` 32 | 33 | By default 1 fleetspeak server and 1 client will be started. You can specify 34 | these numbers with terraform variables: `bash terraform apply -var 35 | "project_name=*your_project_name*" -var "num_servers=2" -var "num_clients=3"` 36 | 37 | If you run the installation in the internal Google Cloud, the default image of 38 | virtual machines may be not allowed. In this case run `terraform apply` with the 39 | additional variable as following: `bash terraform apply -var 40 | "project_name=*your_project_name*" -var 41 | "vm_image=projects/eip-images/global/images/ubuntu-1804-lts-drawfork-v20200208"` 42 | 43 | ## Viewing tests results 44 | 45 | After all the tests are completed, the `results.txt` file will be uploaded into 46 | the bucket created by Terraform 47 | ([link](https://console.cloud.google.com/storage)). 48 | 49 | ## Destroying resources 50 | 51 | When tests are finished, destroy all the resources by running `bash terraform 52 | destroy -var "project_name=*your_project_name*"` 53 | -------------------------------------------------------------------------------- /terraform/cloudtesting/end_to_end_test.go: -------------------------------------------------------------------------------- 1 | package cloudtesting_test 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | "testing" 11 | "time" 12 | 13 | "github.com/google/fleetspeak/fleetspeak/src/e2etesting/setup" 14 | "github.com/google/fleetspeak/fleetspeak/src/e2etesting/tests" 15 | ) 16 | 17 | var ( 18 | numClients = flag.Int("num_clients", 0, "Number of clients") 19 | masterServerAddress = flag.String("ms_address", "", "Address of master server") 20 | serversFile = flag.String("servers_file", "", "File with server hosts") 21 | ) 22 | 23 | func TestCloudEndToEnd(t *testing.T) { 24 | flag.Parse() 25 | 26 | if *numClients == 0 { 27 | t.Skip("num_clients flag is required to run this test.") 28 | } 29 | if *masterServerAddress == "" { 30 | t.Skip("ms_address flag is required to run this test.") 31 | } 32 | if *serversFile == "" { 33 | t.Skip("servers_file flag is required to run this test.") 34 | } 35 | 36 | wd, err := os.Getwd() 37 | if err != nil { 38 | t.Fatalf("Failed to get working directory: %v", err) 39 | } 40 | err = os.Chdir(filepath.Dir(filepath.Dir(wd))) 41 | if err != nil { 42 | t.Fatal("Failed to cd to fleetspeak directory") 43 | } 44 | 45 | dat, err := ioutil.ReadFile(*serversFile) 46 | if err != nil { 47 | t.Fatalf("Failed to read serversFile: %v", err) 48 | } 49 | serverHosts := strings.Fields(string(dat)) 50 | 51 | startTime := time.Now() 52 | var clientIDs []string 53 | 54 | for range 20 { 55 | // All clients that connected more than 30 minutes ago are considered inactive (not newly connected) 56 | clientIDs, err = setup.WaitForNewClientIDs(fmt.Sprintf("%v:6061", serverHosts[0]), startTime.Add(-time.Minute*30), *numClients) 57 | if err == nil { 58 | break 59 | } 60 | if time.Now().After(startTime.Add(time.Minute * 10)) { 61 | t.Fatalf("Not all clients connected (connected: %v, expected: %v, connected clients: %v): %v", len(clientIDs), *numClients, clientIDs, err) 62 | } 63 | } 64 | 65 | tests.RunTests(t, *masterServerAddress, clientIDs) 66 | } 67 | -------------------------------------------------------------------------------- /terraform/fleetspeak_configurator/build_configs.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "strings" 9 | 10 | "github.com/google/fleetspeak/fleetspeak/src/e2etesting/setup" 11 | ) 12 | 13 | var ( 14 | configDir = flag.String("config_dir", "", "Directory to put config files") 15 | numClients = flag.Int("num_clients", 1, "Number of clients") 16 | serversFile = flag.String("servers_file", "", "File with server hosts") 17 | serverFrontendAddress = flag.String("frontend_address", "", "Frontend address for clients to connect") 18 | mysqlAddress = flag.String("mysql_address", "", "MySQL server address") 19 | mysqlDatabase = flag.String("mysql_database", "", "MySQL database name to use") 20 | mysqlUsername = flag.String("mysql_username", "", "MySQL username to use") 21 | mysqlPassword = flag.String("mysql_password", "", "MySQL password to use") 22 | ) 23 | 24 | func run() error { 25 | dat, err := ioutil.ReadFile(*serversFile) 26 | if err != nil { 27 | return fmt.Errorf("Failed to read serversFile: %v", err) 28 | } 29 | serverHosts := strings.Fields(string(dat)) 30 | 31 | err = setup.BuildConfigurations(*configDir, serverHosts, *serverFrontendAddress, *numClients, 32 | setup.MysqlCredentials{ 33 | Host: *mysqlAddress, 34 | Password: *mysqlPassword, 35 | Username: *mysqlUsername, 36 | Database: *mysqlDatabase, 37 | }) 38 | if err != nil { 39 | return fmt.Errorf("Failed to build configs: %v", err) 40 | } 41 | 42 | return nil 43 | } 44 | 45 | func main() { 46 | flag.Parse() 47 | err := run() 48 | if err != nil { 49 | fmt.Println(err) 50 | os.Exit(1) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /terraform/fs_client_start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | touch communicator.txt 6 | touch client.state 7 | mkdir services 8 | 9 | export PATH=/snap/bin:$PATH 10 | ln -fs /usr/bin/python3 /usr/bin/python 11 | 12 | # apt_install command_to_check package_name 13 | function apt_install { 14 | while [[ ! `command -v $1` ]]; 15 | do 16 | apt-get -y update ||: 17 | apt-get -y install $2 ||: 18 | sleep 3 19 | done 20 | } 21 | 22 | #cp_from_bucket source_url destination 23 | function cp_from_bucket { 24 | mkdir -p $(dirname $2) 25 | while [[ (! -f $2) && (! -e $2) ]] 26 | do 27 | gsutil cp -r $1 $(dirname $2) ||: 28 | sleep 5 29 | done 30 | } 31 | 32 | apt_install pip3 python3-pip 33 | cp_from_bucket ${storage_bucket_url}/frr_python/wheel frr_python/wheel 34 | pip3 install --target=frr_python frr_python/wheel/* 35 | cp_from_bucket ${storage_bucket_url}/bin/client ./client 36 | cp_from_bucket ${storage_bucket_url}/protos/frr.textproto textservices/frr.textproto 37 | cp_from_bucket ${storage_bucket_url}/client_configs/linux_client.config linux_client.config 38 | touch client${self_index}.ready 39 | gsutil cp client${self_index}.ready ${storage_bucket_url}/started_components/ 40 | 41 | chmod +x client 42 | 43 | ./client -logtostderr -config "linux_client.config" 44 | -------------------------------------------------------------------------------- /terraform/fs_server_start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # apt_install command_to_check package_name 6 | function apt_install { 7 | while [[ ! `command -v $1` ]]; 8 | do 9 | apt-get -y update ||: 10 | apt-get -y install $2 ||: 11 | sleep 3 12 | done 13 | } 14 | 15 | apt_install pip3 python3-pip 16 | apt_install mysql mysql-client 17 | 18 | export PATH=/snap/bin:$PATH 19 | 20 | wget https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64 -O ./cloud_sql_proxy 21 | chmod +x ./cloud_sql_proxy 22 | 23 | ./cloud_sql_proxy -instances=${mysql_instance_connection_name}=tcp:3306 & 24 | 25 | ln -fs /usr/bin/python3 /usr/bin/python 26 | 27 | #cp_from_bucket source_url destination 28 | function cp_from_bucket { 29 | mkdir -p $(dirname $2) 30 | while [[ (! -f $2) && (! -e $2) ]] 31 | do 32 | gsutil cp -r $1 $(dirname $2) ||: 33 | sleep 5 34 | done 35 | } 36 | 37 | cp_from_bucket ${storage_bucket_url}/frr_python/wheel frr_python/wheel 38 | pip3 install --target=frr_python frr_python/wheel/* 39 | cp_from_bucket ${storage_bucket_url}/bin/server ./server 40 | cp_from_bucket ${storage_bucket_url}/server_configs/server${self_index}.config ./server${self_index}.config 41 | cp_from_bucket ${storage_bucket_url}/server_configs/server${self_index}.services.config ./server${self_index}.services.config 42 | touch server${self_index}.ready 43 | gsutil cp server${self_index}.ready ${storage_bucket_url}/started_components/ 44 | 45 | chmod +x server 46 | 47 | ./server -logtostderr -components_config "server${self_index}.config" -services_config "server${self_index}.services.config" & 48 | python3 frr_python/frr_server.py --master_server_address=${master_server_host}:6059 --fleetspeak_message_listen_address=${self_host}:6062 --fleetspeak_server=${self_host}:6061 49 | -------------------------------------------------------------------------------- /terraform/master_server_start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | while ! gsutil cp ${storage_bucket_url}/bin/frr_master_server_main ./; do 6 | sleep 10 7 | done 8 | 9 | chmod +x frr_master_server_main 10 | 11 | ./frr_master_server_main --listen_address=${master_server_host}:6059 --admin_address=${admin_host}:6061 12 | --------------------------------------------------------------------------------