flwr 1.21.0__py3-none-any.whl → 1.23.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flwr/cli/app.py +17 -1
- flwr/cli/auth_plugin/__init__.py +15 -6
- flwr/cli/auth_plugin/auth_plugin.py +95 -0
- flwr/cli/auth_plugin/noop_auth_plugin.py +58 -0
- flwr/cli/auth_plugin/oidc_cli_plugin.py +16 -25
- flwr/cli/build.py +118 -47
- flwr/cli/{cli_user_auth_interceptor.py → cli_account_auth_interceptor.py} +6 -5
- flwr/cli/log.py +2 -2
- flwr/cli/login/login.py +34 -23
- flwr/cli/ls.py +13 -9
- flwr/cli/new/new.py +196 -42
- flwr/cli/new/templates/app/README.flowertune.md.tpl +1 -1
- flwr/cli/new/templates/app/code/client.baseline.py.tpl +64 -47
- flwr/cli/new/templates/app/code/client.huggingface.py.tpl +68 -30
- flwr/cli/new/templates/app/code/client.jax.py.tpl +63 -42
- flwr/cli/new/templates/app/code/client.mlx.py.tpl +80 -51
- flwr/cli/new/templates/app/code/client.numpy.py.tpl +36 -13
- flwr/cli/new/templates/app/code/client.pytorch.py.tpl +71 -46
- flwr/cli/new/templates/app/code/client.pytorch_legacy_api.py.tpl +55 -0
- flwr/cli/new/templates/app/code/client.sklearn.py.tpl +75 -30
- flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +69 -44
- flwr/cli/new/templates/app/code/client.xgboost.py.tpl +110 -0
- flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +56 -90
- flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +1 -23
- flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +37 -58
- flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +39 -44
- flwr/cli/new/templates/app/code/model.baseline.py.tpl +0 -14
- flwr/cli/new/templates/app/code/server.baseline.py.tpl +27 -29
- flwr/cli/new/templates/app/code/server.huggingface.py.tpl +23 -19
- flwr/cli/new/templates/app/code/server.jax.py.tpl +27 -14
- flwr/cli/new/templates/app/code/server.mlx.py.tpl +29 -19
- flwr/cli/new/templates/app/code/server.numpy.py.tpl +30 -17
- flwr/cli/new/templates/app/code/server.pytorch.py.tpl +36 -26
- flwr/cli/new/templates/app/code/server.pytorch_legacy_api.py.tpl +31 -0
- flwr/cli/new/templates/app/code/server.sklearn.py.tpl +29 -21
- flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +28 -19
- flwr/cli/new/templates/app/code/server.xgboost.py.tpl +56 -0
- flwr/cli/new/templates/app/code/task.huggingface.py.tpl +16 -20
- flwr/cli/new/templates/app/code/task.jax.py.tpl +1 -1
- flwr/cli/new/templates/app/code/task.numpy.py.tpl +1 -1
- flwr/cli/new/templates/app/code/task.pytorch.py.tpl +14 -27
- flwr/cli/new/templates/app/code/{task.pytorch_msg_api.py.tpl → task.pytorch_legacy_api.py.tpl} +27 -14
- flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +1 -2
- flwr/cli/new/templates/app/code/task.xgboost.py.tpl +67 -0
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +2 -2
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +2 -2
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +3 -3
- flwr/cli/new/templates/app/{pyproject.pytorch_msg_api.toml.tpl → pyproject.pytorch_legacy_api.toml.tpl} +3 -3
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl +61 -0
- flwr/cli/pull.py +100 -0
- flwr/cli/run/run.py +11 -7
- flwr/cli/stop.py +2 -2
- flwr/cli/supernode/__init__.py +25 -0
- flwr/cli/supernode/ls.py +260 -0
- flwr/cli/supernode/register.py +185 -0
- flwr/cli/supernode/unregister.py +138 -0
- flwr/cli/utils.py +109 -69
- flwr/client/__init__.py +2 -1
- flwr/client/grpc_adapter_client/connection.py +6 -8
- flwr/client/grpc_rere_client/connection.py +59 -31
- flwr/client/grpc_rere_client/grpc_adapter.py +28 -12
- flwr/client/grpc_rere_client/{client_interceptor.py → node_auth_client_interceptor.py} +3 -6
- flwr/client/mod/secure_aggregation/secaggplus_mod.py +7 -5
- flwr/client/rest_client/connection.py +82 -37
- flwr/clientapp/__init__.py +1 -2
- flwr/clientapp/mod/__init__.py +4 -1
- flwr/clientapp/mod/centraldp_mods.py +156 -40
- flwr/clientapp/mod/localdp_mod.py +169 -0
- flwr/clientapp/typing.py +22 -0
- flwr/{client/clientapp → clientapp}/utils.py +1 -1
- flwr/common/constant.py +56 -13
- flwr/common/exit/exit_code.py +24 -10
- flwr/common/inflatable_utils.py +10 -10
- flwr/common/record/array.py +3 -3
- flwr/common/record/arrayrecord.py +10 -1
- flwr/common/record/typeddict.py +12 -0
- flwr/common/secure_aggregation/crypto/symmetric_encryption.py +1 -89
- flwr/common/serde.py +4 -2
- flwr/common/typing.py +7 -6
- flwr/compat/client/app.py +1 -1
- flwr/compat/client/grpc_client/connection.py +2 -2
- flwr/proto/control_pb2.py +48 -31
- flwr/proto/control_pb2.pyi +95 -5
- flwr/proto/control_pb2_grpc.py +136 -0
- flwr/proto/control_pb2_grpc.pyi +52 -0
- flwr/proto/fab_pb2.py +11 -7
- flwr/proto/fab_pb2.pyi +21 -1
- flwr/proto/fleet_pb2.py +31 -23
- flwr/proto/fleet_pb2.pyi +63 -23
- flwr/proto/fleet_pb2_grpc.py +98 -28
- flwr/proto/fleet_pb2_grpc.pyi +45 -13
- flwr/proto/node_pb2.py +3 -1
- flwr/proto/node_pb2.pyi +48 -0
- flwr/server/app.py +152 -114
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +17 -7
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +132 -38
- flwr/server/superlink/fleet/grpc_rere/{server_interceptor.py → node_auth_server_interceptor.py} +27 -51
- flwr/server/superlink/fleet/message_handler/message_handler.py +67 -22
- flwr/server/superlink/fleet/rest_rere/rest_api.py +52 -31
- flwr/server/superlink/fleet/vce/backend/backend.py +1 -1
- flwr/server/superlink/fleet/vce/backend/raybackend.py +1 -1
- flwr/server/superlink/fleet/vce/vce_api.py +18 -5
- flwr/server/superlink/linkstate/in_memory_linkstate.py +167 -73
- flwr/server/superlink/linkstate/linkstate.py +107 -24
- flwr/server/superlink/linkstate/linkstate_factory.py +2 -1
- flwr/server/superlink/linkstate/sqlite_linkstate.py +306 -255
- flwr/server/superlink/linkstate/utils.py +3 -54
- flwr/server/superlink/serverappio/serverappio_servicer.py +2 -2
- flwr/server/superlink/simulation/simulationio_servicer.py +1 -1
- flwr/server/utils/validator.py +2 -3
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +4 -2
- flwr/serverapp/strategy/__init__.py +26 -0
- flwr/serverapp/strategy/bulyan.py +238 -0
- flwr/serverapp/strategy/dp_adaptive_clipping.py +335 -0
- flwr/serverapp/strategy/dp_fixed_clipping.py +71 -49
- flwr/serverapp/strategy/fedadagrad.py +0 -3
- flwr/serverapp/strategy/fedadam.py +0 -3
- flwr/serverapp/strategy/fedavg.py +89 -64
- flwr/serverapp/strategy/fedavgm.py +198 -0
- flwr/serverapp/strategy/fedmedian.py +105 -0
- flwr/serverapp/strategy/fedprox.py +174 -0
- flwr/serverapp/strategy/fedtrimmedavg.py +176 -0
- flwr/serverapp/strategy/fedxgb_bagging.py +117 -0
- flwr/serverapp/strategy/fedxgb_cyclic.py +220 -0
- flwr/serverapp/strategy/fedyogi.py +0 -3
- flwr/serverapp/strategy/krum.py +112 -0
- flwr/serverapp/strategy/multikrum.py +247 -0
- flwr/serverapp/strategy/qfedavg.py +252 -0
- flwr/serverapp/strategy/strategy_utils.py +48 -0
- flwr/simulation/app.py +1 -1
- flwr/simulation/ray_transport/ray_actor.py +1 -1
- flwr/simulation/ray_transport/ray_client_proxy.py +1 -1
- flwr/simulation/run_simulation.py +28 -32
- flwr/supercore/cli/flower_superexec.py +26 -1
- flwr/supercore/constant.py +41 -0
- flwr/supercore/object_store/in_memory_object_store.py +0 -4
- flwr/supercore/object_store/object_store_factory.py +26 -6
- flwr/supercore/object_store/sqlite_object_store.py +252 -0
- flwr/{client/clientapp → supercore/primitives}/__init__.py +1 -1
- flwr/supercore/primitives/asymmetric.py +117 -0
- flwr/supercore/primitives/asymmetric_ed25519.py +165 -0
- flwr/supercore/sqlite_mixin.py +156 -0
- flwr/supercore/superexec/plugin/exec_plugin.py +11 -1
- flwr/supercore/superexec/run_superexec.py +16 -2
- flwr/supercore/utils.py +20 -0
- flwr/superlink/artifact_provider/__init__.py +22 -0
- flwr/superlink/artifact_provider/artifact_provider.py +37 -0
- flwr/{common → superlink}/auth_plugin/__init__.py +6 -6
- flwr/superlink/auth_plugin/auth_plugin.py +91 -0
- flwr/superlink/auth_plugin/noop_auth_plugin.py +87 -0
- flwr/superlink/servicer/control/{control_user_auth_interceptor.py → control_account_auth_interceptor.py} +19 -19
- flwr/superlink/servicer/control/control_event_log_interceptor.py +1 -1
- flwr/superlink/servicer/control/control_grpc.py +16 -11
- flwr/superlink/servicer/control/control_servicer.py +207 -58
- flwr/supernode/cli/flower_supernode.py +19 -26
- flwr/supernode/runtime/run_clientapp.py +2 -2
- flwr/supernode/servicer/clientappio/clientappio_servicer.py +1 -1
- flwr/supernode/start_client_internal.py +17 -9
- {flwr-1.21.0.dist-info → flwr-1.23.0.dist-info}/METADATA +6 -16
- {flwr-1.21.0.dist-info → flwr-1.23.0.dist-info}/RECORD +170 -140
- flwr/cli/new/templates/app/code/client.pytorch_msg_api.py.tpl +0 -80
- flwr/cli/new/templates/app/code/server.pytorch_msg_api.py.tpl +0 -41
- flwr/common/auth_plugin/auth_plugin.py +0 -149
- flwr/serverapp/dp_fixed_clipping.py +0 -352
- flwr/serverapp/strategy/strategy_utils_tests.py +0 -304
- /flwr/cli/new/templates/app/code/{__init__.pytorch_msg_api.py.tpl → __init__.pytorch_legacy_api.py.tpl} +0 -0
- /flwr/{client → clientapp}/client_app.py +0 -0
- {flwr-1.21.0.dist-info → flwr-1.23.0.dist-info}/WHEEL +0 -0
- {flwr-1.21.0.dist-info → flwr-1.23.0.dist-info}/entry_points.txt +0 -0
flwr/server/app.py
CHANGED
|
@@ -16,30 +16,26 @@
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
import argparse
|
|
19
|
-
import csv
|
|
20
19
|
import importlib.util
|
|
21
20
|
import os
|
|
22
21
|
import subprocess
|
|
23
22
|
import sys
|
|
24
23
|
import threading
|
|
25
24
|
from collections.abc import Sequence
|
|
26
|
-
from logging import
|
|
25
|
+
from logging import INFO, WARN
|
|
27
26
|
from pathlib import Path
|
|
28
27
|
from time import sleep
|
|
29
|
-
from typing import
|
|
28
|
+
from typing import Callable, Optional, TypeVar, cast
|
|
30
29
|
|
|
31
30
|
import grpc
|
|
32
31
|
import yaml
|
|
33
|
-
from cryptography.hazmat.primitives.asymmetric import ec
|
|
34
|
-
from cryptography.hazmat.primitives.serialization import load_ssh_public_key
|
|
35
32
|
|
|
36
33
|
from flwr.common import GRPC_MAX_MESSAGE_LENGTH, EventType, event
|
|
37
34
|
from flwr.common.address import parse_address
|
|
38
35
|
from flwr.common.args import try_obtain_server_certificates
|
|
39
|
-
from flwr.common.auth_plugin import ControlAuthPlugin, ControlAuthzPlugin
|
|
40
36
|
from flwr.common.config import get_flwr_dir
|
|
41
37
|
from flwr.common.constant import (
|
|
42
|
-
|
|
38
|
+
AUTHN_TYPE_YAML_KEY,
|
|
43
39
|
AUTHZ_TYPE_YAML_KEY,
|
|
44
40
|
CLIENT_OCTET,
|
|
45
41
|
CONTROL_API_DEFAULT_SERVER_ADDRESS,
|
|
@@ -53,6 +49,8 @@ from flwr.common.constant import (
|
|
|
53
49
|
TRANSPORT_TYPE_GRPC_ADAPTER,
|
|
54
50
|
TRANSPORT_TYPE_GRPC_RERE,
|
|
55
51
|
TRANSPORT_TYPE_REST,
|
|
52
|
+
AuthnType,
|
|
53
|
+
AuthzType,
|
|
56
54
|
EventLogWriterType,
|
|
57
55
|
ExecPluginType,
|
|
58
56
|
)
|
|
@@ -60,37 +58,45 @@ from flwr.common.event_log_plugin import EventLogWriterPlugin
|
|
|
60
58
|
from flwr.common.exit import ExitCode, flwr_exit, register_signal_handlers
|
|
61
59
|
from flwr.common.grpc import generic_create_grpc_server
|
|
62
60
|
from flwr.common.logger import log
|
|
63
|
-
from flwr.common.
|
|
64
|
-
public_key_to_bytes,
|
|
65
|
-
)
|
|
61
|
+
from flwr.common.version import package_version
|
|
66
62
|
from flwr.proto.fleet_pb2_grpc import ( # pylint: disable=E0611
|
|
67
63
|
add_FleetServicer_to_server,
|
|
68
64
|
)
|
|
69
65
|
from flwr.proto.grpcadapter_pb2_grpc import add_GrpcAdapterServicer_to_server
|
|
70
66
|
from flwr.server.fleet_event_log_interceptor import FleetEventLogInterceptor
|
|
67
|
+
from flwr.supercore.constant import FLWR_IN_MEMORY_DB_NAME
|
|
71
68
|
from flwr.supercore.ffs import FfsFactory
|
|
72
69
|
from flwr.supercore.grpc_health import add_args_health, run_health_server_grpc_no_tls
|
|
73
70
|
from flwr.supercore.object_store import ObjectStoreFactory
|
|
71
|
+
from flwr.superlink.artifact_provider import ArtifactProvider
|
|
72
|
+
from flwr.superlink.auth_plugin import (
|
|
73
|
+
ControlAuthnPlugin,
|
|
74
|
+
ControlAuthzPlugin,
|
|
75
|
+
NoOpControlAuthnPlugin,
|
|
76
|
+
NoOpControlAuthzPlugin,
|
|
77
|
+
)
|
|
74
78
|
from flwr.superlink.servicer.control import run_control_api_grpc
|
|
75
79
|
|
|
76
80
|
from .superlink.fleet.grpc_adapter.grpc_adapter_servicer import GrpcAdapterServicer
|
|
77
81
|
from .superlink.fleet.grpc_rere.fleet_servicer import FleetServicer
|
|
78
|
-
from .superlink.fleet.grpc_rere.
|
|
82
|
+
from .superlink.fleet.grpc_rere.node_auth_server_interceptor import (
|
|
83
|
+
NodeAuthServerInterceptor,
|
|
84
|
+
)
|
|
79
85
|
from .superlink.linkstate import LinkStateFactory
|
|
80
86
|
from .superlink.serverappio.serverappio_grpc import run_serverappio_api_grpc
|
|
81
87
|
from .superlink.simulation.simulationio_grpc import run_simulationio_api_grpc
|
|
82
88
|
|
|
83
|
-
DATABASE = ":flwr-in-memory-state:"
|
|
84
89
|
BASE_DIR = get_flwr_dir() / "superlink" / "ffs"
|
|
85
|
-
P = TypeVar("P",
|
|
90
|
+
P = TypeVar("P", ControlAuthnPlugin, ControlAuthzPlugin)
|
|
86
91
|
|
|
87
92
|
|
|
88
93
|
try:
|
|
89
94
|
from flwr.ee import (
|
|
90
95
|
add_ee_args_superlink,
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
get_control_authn_ee_plugins,
|
|
97
|
+
get_control_authz_ee_plugins,
|
|
93
98
|
get_control_event_log_writer_plugins,
|
|
99
|
+
get_ee_artifact_provider,
|
|
94
100
|
get_fleet_event_log_writer_plugins,
|
|
95
101
|
)
|
|
96
102
|
except ImportError:
|
|
@@ -99,26 +105,42 @@ except ImportError:
|
|
|
99
105
|
def add_ee_args_superlink(parser: argparse.ArgumentParser) -> None:
|
|
100
106
|
"""Add EE-specific arguments to the parser."""
|
|
101
107
|
|
|
102
|
-
def get_control_auth_plugins() -> dict[str, type[ControlAuthPlugin]]:
|
|
103
|
-
"""Return all Control API authentication plugins."""
|
|
104
|
-
raise NotImplementedError("No authentication plugins are currently supported.")
|
|
105
|
-
|
|
106
|
-
def get_control_authz_plugins() -> dict[str, type[ControlAuthzPlugin]]:
|
|
107
|
-
"""Return all Control API authorization plugins."""
|
|
108
|
-
raise NotImplementedError("No authorization plugins are currently supported.")
|
|
109
|
-
|
|
110
108
|
def get_control_event_log_writer_plugins() -> dict[str, type[EventLogWriterPlugin]]:
|
|
111
109
|
"""Return all Control API event log writer plugins."""
|
|
112
110
|
raise NotImplementedError(
|
|
113
111
|
"No event log writer plugins are currently supported."
|
|
114
112
|
)
|
|
115
113
|
|
|
114
|
+
def get_ee_artifact_provider(config_path: str) -> ArtifactProvider:
|
|
115
|
+
"""Return the EE artifact provider."""
|
|
116
|
+
raise NotImplementedError("No artifact provider is currently supported.")
|
|
117
|
+
|
|
116
118
|
def get_fleet_event_log_writer_plugins() -> dict[str, type[EventLogWriterPlugin]]:
|
|
117
119
|
"""Return all Fleet API event log writer plugins."""
|
|
118
120
|
raise NotImplementedError(
|
|
119
121
|
"No event log writer plugins are currently supported."
|
|
120
122
|
)
|
|
121
123
|
|
|
124
|
+
def get_control_authn_ee_plugins() -> dict[str, type[ControlAuthnPlugin]]:
|
|
125
|
+
"""Return all Control API authentication plugins for EE."""
|
|
126
|
+
return {}
|
|
127
|
+
|
|
128
|
+
def get_control_authz_ee_plugins() -> dict[str, type[ControlAuthzPlugin]]:
|
|
129
|
+
"""Return all Control API authorization plugins for EE."""
|
|
130
|
+
return {}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def get_control_authn_plugins() -> dict[str, type[ControlAuthnPlugin]]:
|
|
134
|
+
"""Return all Control API authentication plugins."""
|
|
135
|
+
ee_dict: dict[str, type[ControlAuthnPlugin]] = get_control_authn_ee_plugins()
|
|
136
|
+
return ee_dict | {AuthnType.NOOP: NoOpControlAuthnPlugin}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def get_control_authz_plugins() -> dict[str, type[ControlAuthzPlugin]]:
|
|
140
|
+
"""Return all Control API authorization plugins."""
|
|
141
|
+
ee_dict: dict[str, type[ControlAuthzPlugin]] = get_control_authz_ee_plugins()
|
|
142
|
+
return ee_dict | {AuthzType.NOOP: NoOpControlAuthzPlugin}
|
|
143
|
+
|
|
122
144
|
|
|
123
145
|
# pylint: disable=too-many-branches, too-many-locals, too-many-statements
|
|
124
146
|
def run_superlink() -> None:
|
|
@@ -183,22 +205,82 @@ def run_superlink() -> None:
|
|
|
183
205
|
# Obtain certificates
|
|
184
206
|
certificates = try_obtain_server_certificates(args)
|
|
185
207
|
|
|
186
|
-
# Disable the
|
|
208
|
+
# Disable the account auth TLS check if args.disable_oidc_tls_cert_verification is
|
|
187
209
|
# provided
|
|
188
210
|
verify_tls_cert = not getattr(args, "disable_oidc_tls_cert_verification", None)
|
|
189
211
|
|
|
190
|
-
|
|
212
|
+
authn_plugin: Optional[ControlAuthnPlugin] = None
|
|
191
213
|
authz_plugin: Optional[ControlAuthzPlugin] = None
|
|
192
214
|
event_log_plugin: Optional[EventLogWriterPlugin] = None
|
|
193
|
-
# Load the auth plugin if the args.
|
|
215
|
+
# Load the auth plugin if the args.account_auth_config is provided
|
|
194
216
|
if cfg_path := getattr(args, "user_auth_config", None):
|
|
195
|
-
|
|
196
|
-
|
|
217
|
+
log(
|
|
218
|
+
WARN,
|
|
219
|
+
"The `--user-auth-config` flag is deprecated and will be removed in a "
|
|
220
|
+
"future release. Please use `--account-auth-config` instead.",
|
|
197
221
|
)
|
|
222
|
+
args.account_auth_config = cfg_path
|
|
223
|
+
cfg_path = getattr(args, "account_auth_config", None)
|
|
224
|
+
authn_plugin, authz_plugin = _load_control_auth_plugins(cfg_path, verify_tls_cert)
|
|
225
|
+
if cfg_path is not None:
|
|
198
226
|
# Enable event logging if the args.enable_event_log is True
|
|
199
227
|
if args.enable_event_log:
|
|
200
228
|
event_log_plugin = _try_obtain_control_event_log_writer_plugin()
|
|
201
229
|
|
|
230
|
+
# Load artifact provider if the args.artifact_provider_config is provided
|
|
231
|
+
artifact_provider = None
|
|
232
|
+
if cfg_path := getattr(args, "artifact_provider_config", None):
|
|
233
|
+
log(WARN, "The `--artifact-provider-config` flag is highly experimental.")
|
|
234
|
+
artifact_provider = get_ee_artifact_provider(cfg_path)
|
|
235
|
+
|
|
236
|
+
# Check for incompatible args with SuperNode authentication
|
|
237
|
+
enable_supernode_auth: bool = args.enable_supernode_auth
|
|
238
|
+
if enable_supernode_auth:
|
|
239
|
+
if args.insecure:
|
|
240
|
+
url_v = f"https://flower.ai/docs/framework/v{package_version}/en/"
|
|
241
|
+
page = "how-to-authenticate-supernodes.html"
|
|
242
|
+
flwr_exit(
|
|
243
|
+
ExitCode.SUPERLINK_INVALID_ARGS,
|
|
244
|
+
"The `--enable-supernode-auth` flag requires encrypted TLS "
|
|
245
|
+
"communications. Please provide TLS certificates using the "
|
|
246
|
+
"`--ssl-certfile`, `--ssl-keyfile` and `--ssl-ca-certfile` "
|
|
247
|
+
"arguments to your SuperLink. Please refer to the Flower "
|
|
248
|
+
f"documentation for more information: {url_v}{page}",
|
|
249
|
+
)
|
|
250
|
+
if args.fleet_api_type != TRANSPORT_TYPE_GRPC_RERE:
|
|
251
|
+
flwr_exit(
|
|
252
|
+
ExitCode.SUPERLINK_INVALID_ARGS,
|
|
253
|
+
"The `--enable-supernode-auth` flag is only supported "
|
|
254
|
+
"with the gRPC-rere Fleet API transport. Please set "
|
|
255
|
+
f"`--fleet-api-type` to `{TRANSPORT_TYPE_GRPC_RERE}`.",
|
|
256
|
+
)
|
|
257
|
+
if args.simulation:
|
|
258
|
+
log(
|
|
259
|
+
WARN,
|
|
260
|
+
"SuperNode authentication is not applicable with the simulation, "
|
|
261
|
+
"runtime as no SuperNodes can connect to this SuperLink. "
|
|
262
|
+
"Proceeding...",
|
|
263
|
+
)
|
|
264
|
+
# If supernode authentication is disabled, warn users
|
|
265
|
+
else:
|
|
266
|
+
log(
|
|
267
|
+
WARN,
|
|
268
|
+
"SuperNode authentication is disabled. The SuperLink will accept "
|
|
269
|
+
"connections from any SuperNode.",
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
if args.auth_list_public_keys:
|
|
273
|
+
url_v = f"https://flower.ai/docs/framework/v{package_version}/en/"
|
|
274
|
+
page = "how-to-authenticate-supernodes.html"
|
|
275
|
+
flwr_exit(
|
|
276
|
+
ExitCode.SUPERLINK_INVALID_ARGS,
|
|
277
|
+
"The `--auth-list-public-keys` "
|
|
278
|
+
"argument is no longer supported. To enable SuperNode authentication, "
|
|
279
|
+
"use the `--enable-supernode-auth` flag and use the Flower CLI to register "
|
|
280
|
+
"SuperNodes by supplying their public keys. Please refer"
|
|
281
|
+
f" to the Flower documentation for more information: {url_v}{page}",
|
|
282
|
+
)
|
|
283
|
+
|
|
202
284
|
# Initialize StateFactory
|
|
203
285
|
state_factory = LinkStateFactory(args.database)
|
|
204
286
|
|
|
@@ -206,7 +288,7 @@ def run_superlink() -> None:
|
|
|
206
288
|
ffs_factory = FfsFactory(args.storage_dir)
|
|
207
289
|
|
|
208
290
|
# Initialize ObjectStoreFactory
|
|
209
|
-
objectstore_factory = ObjectStoreFactory()
|
|
291
|
+
objectstore_factory = ObjectStoreFactory(args.database)
|
|
210
292
|
|
|
211
293
|
# Start Control API
|
|
212
294
|
is_simulation = args.simulation
|
|
@@ -217,9 +299,10 @@ def run_superlink() -> None:
|
|
|
217
299
|
objectstore_factory=objectstore_factory,
|
|
218
300
|
certificates=certificates,
|
|
219
301
|
is_simulation=is_simulation,
|
|
220
|
-
|
|
302
|
+
authn_plugin=authn_plugin,
|
|
221
303
|
authz_plugin=authz_plugin,
|
|
222
304
|
event_log_plugin=event_log_plugin,
|
|
305
|
+
artifact_provider=artifact_provider,
|
|
223
306
|
)
|
|
224
307
|
grpc_servers = [control_server]
|
|
225
308
|
bckg_threads: list[threading.Thread] = []
|
|
@@ -293,22 +376,8 @@ def run_superlink() -> None:
|
|
|
293
376
|
fleet_thread.start()
|
|
294
377
|
bckg_threads.append(fleet_thread)
|
|
295
378
|
elif args.fleet_api_type == TRANSPORT_TYPE_GRPC_RERE:
|
|
296
|
-
node_public_keys = _try_load_public_keys_node_authentication(args)
|
|
297
|
-
auto_auth = True
|
|
298
|
-
if node_public_keys is not None:
|
|
299
|
-
auto_auth = False
|
|
300
|
-
state = state_factory.state()
|
|
301
|
-
state.clear_supernode_auth_keys()
|
|
302
|
-
state.store_node_public_keys(node_public_keys)
|
|
303
|
-
log(
|
|
304
|
-
INFO,
|
|
305
|
-
"Node authentication enabled with %d known public keys",
|
|
306
|
-
len(node_public_keys),
|
|
307
|
-
)
|
|
308
|
-
else:
|
|
309
|
-
log(DEBUG, "Automatic node authentication enabled")
|
|
310
379
|
|
|
311
|
-
interceptors = [
|
|
380
|
+
interceptors = [NodeAuthServerInterceptor(state_factory)]
|
|
312
381
|
if getattr(args, "enable_event_log", None):
|
|
313
382
|
fleet_log_plugin = _try_obtain_fleet_event_log_writer_plugin()
|
|
314
383
|
if fleet_log_plugin is not None:
|
|
@@ -320,6 +389,7 @@ def run_superlink() -> None:
|
|
|
320
389
|
state_factory=state_factory,
|
|
321
390
|
ffs_factory=ffs_factory,
|
|
322
391
|
objectstore_factory=objectstore_factory,
|
|
392
|
+
enable_supernode_auth=enable_supernode_auth,
|
|
323
393
|
certificates=certificates,
|
|
324
394
|
interceptors=interceptors,
|
|
325
395
|
)
|
|
@@ -387,55 +457,21 @@ def _format_address(address: str) -> tuple[str, str, int]:
|
|
|
387
457
|
return (f"[{host}]:{port}" if is_v6 else f"{host}:{port}", host, port)
|
|
388
458
|
|
|
389
459
|
|
|
390
|
-
def
|
|
391
|
-
|
|
392
|
-
) ->
|
|
393
|
-
"""Return a set of node public keys."""
|
|
394
|
-
if args.auth_superlink_private_key or args.auth_superlink_public_key:
|
|
395
|
-
log(
|
|
396
|
-
WARN,
|
|
397
|
-
"The `--auth-superlink-private-key` and `--auth-superlink-public-key` "
|
|
398
|
-
"arguments are deprecated and will be removed in a future release. Node "
|
|
399
|
-
"authentication no longer requires these arguments.",
|
|
400
|
-
)
|
|
401
|
-
|
|
402
|
-
if not args.auth_list_public_keys:
|
|
403
|
-
return None
|
|
404
|
-
|
|
405
|
-
node_keys_file_path = Path(args.auth_list_public_keys)
|
|
406
|
-
if not node_keys_file_path.exists():
|
|
407
|
-
sys.exit(
|
|
408
|
-
"The provided path to the known public keys CSV file does not exist: "
|
|
409
|
-
f"{node_keys_file_path}. "
|
|
410
|
-
"Please provide the CSV file path containing known public keys "
|
|
411
|
-
"to '--auth-list-public-keys'."
|
|
412
|
-
)
|
|
413
|
-
|
|
414
|
-
node_public_keys: set[bytes] = set()
|
|
415
|
-
|
|
416
|
-
with open(node_keys_file_path, newline="", encoding="utf-8") as csvfile:
|
|
417
|
-
reader = csv.reader(csvfile)
|
|
418
|
-
for row in reader:
|
|
419
|
-
for element in row:
|
|
420
|
-
public_key = load_ssh_public_key(element.encode())
|
|
421
|
-
if isinstance(public_key, ec.EllipticCurvePublicKey):
|
|
422
|
-
node_public_keys.add(public_key_to_bytes(public_key))
|
|
423
|
-
else:
|
|
424
|
-
sys.exit(
|
|
425
|
-
"Error: Unable to parse the public keys in the CSV "
|
|
426
|
-
"file. Please ensure that the CSV file path points to a valid "
|
|
427
|
-
"known SSH public keys files and try again."
|
|
428
|
-
)
|
|
429
|
-
return node_public_keys
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
def _try_obtain_control_auth_plugins(
|
|
433
|
-
config_path: Path, verify_tls_cert: bool
|
|
434
|
-
) -> tuple[ControlAuthPlugin, ControlAuthzPlugin]:
|
|
460
|
+
def _load_control_auth_plugins(
|
|
461
|
+
config_path: Optional[str], verify_tls_cert: bool
|
|
462
|
+
) -> tuple[ControlAuthnPlugin, ControlAuthzPlugin]:
|
|
435
463
|
"""Obtain Control API authentication and authorization plugins."""
|
|
464
|
+
# Load NoOp plugins if no config path is provided
|
|
465
|
+
if config_path is None:
|
|
466
|
+
config_path = ""
|
|
467
|
+
config = {
|
|
468
|
+
"authentication": {AUTHN_TYPE_YAML_KEY: AuthnType.NOOP},
|
|
469
|
+
"authorization": {AUTHZ_TYPE_YAML_KEY: AuthzType.NOOP},
|
|
470
|
+
}
|
|
436
471
|
# Load YAML file
|
|
437
|
-
|
|
438
|
-
|
|
472
|
+
else:
|
|
473
|
+
with Path(config_path).open("r", encoding="utf-8") as file:
|
|
474
|
+
config = yaml.safe_load(file)
|
|
439
475
|
|
|
440
476
|
def _load_plugin(
|
|
441
477
|
section: str, yaml_key: str, loader: Callable[[], dict[str, type[P]]]
|
|
@@ -445,9 +481,7 @@ def _try_obtain_control_auth_plugins(
|
|
|
445
481
|
try:
|
|
446
482
|
plugins: dict[str, type[P]] = loader()
|
|
447
483
|
plugin_cls: type[P] = plugins[auth_plugin_name]
|
|
448
|
-
return plugin_cls(
|
|
449
|
-
user_auth_config_path=config_path, verify_tls_cert=verify_tls_cert
|
|
450
|
-
)
|
|
484
|
+
return plugin_cls(Path(cast(str, config_path)), verify_tls_cert)
|
|
451
485
|
except KeyError:
|
|
452
486
|
if auth_plugin_name:
|
|
453
487
|
sys.exit(
|
|
@@ -455,14 +489,22 @@ def _try_obtain_control_auth_plugins(
|
|
|
455
489
|
f"Please provide a valid {section} type in the configuration."
|
|
456
490
|
)
|
|
457
491
|
sys.exit(f"No {section} type is provided in the configuration.")
|
|
458
|
-
|
|
459
|
-
|
|
492
|
+
|
|
493
|
+
# Warn deprecated auth_type key
|
|
494
|
+
if authn_type := config["authentication"].pop("auth_type", None):
|
|
495
|
+
log(
|
|
496
|
+
WARN,
|
|
497
|
+
"The `auth_type` key in the authentication configuration is deprecated. "
|
|
498
|
+
"Use `%s` instead.",
|
|
499
|
+
AUTHN_TYPE_YAML_KEY,
|
|
500
|
+
)
|
|
501
|
+
config["authentication"][AUTHN_TYPE_YAML_KEY] = authn_type
|
|
460
502
|
|
|
461
503
|
# Load authentication plugin
|
|
462
|
-
|
|
504
|
+
authn_plugin = _load_plugin(
|
|
463
505
|
section="authentication",
|
|
464
|
-
yaml_key=
|
|
465
|
-
loader=
|
|
506
|
+
yaml_key=AUTHN_TYPE_YAML_KEY,
|
|
507
|
+
loader=get_control_authn_plugins,
|
|
466
508
|
)
|
|
467
509
|
|
|
468
510
|
# Load authorization plugin
|
|
@@ -472,7 +514,7 @@ def _try_obtain_control_auth_plugins(
|
|
|
472
514
|
loader=get_control_authz_plugins,
|
|
473
515
|
)
|
|
474
516
|
|
|
475
|
-
return
|
|
517
|
+
return authn_plugin, authz_plugin
|
|
476
518
|
|
|
477
519
|
|
|
478
520
|
def _try_obtain_control_event_log_writer_plugin() -> Optional[EventLogWriterPlugin]:
|
|
@@ -508,6 +550,7 @@ def _run_fleet_api_grpc_rere( # pylint: disable=R0913, R0917
|
|
|
508
550
|
state_factory: LinkStateFactory,
|
|
509
551
|
ffs_factory: FfsFactory,
|
|
510
552
|
objectstore_factory: ObjectStoreFactory,
|
|
553
|
+
enable_supernode_auth: bool,
|
|
511
554
|
certificates: Optional[tuple[bytes, bytes, bytes]],
|
|
512
555
|
interceptors: Optional[Sequence[grpc.ServerInterceptor]] = None,
|
|
513
556
|
) -> grpc.Server:
|
|
@@ -517,6 +560,7 @@ def _run_fleet_api_grpc_rere( # pylint: disable=R0913, R0917
|
|
|
517
560
|
state_factory=state_factory,
|
|
518
561
|
ffs_factory=ffs_factory,
|
|
519
562
|
objectstore_factory=objectstore_factory,
|
|
563
|
+
enable_supernode_auth=enable_supernode_auth,
|
|
520
564
|
)
|
|
521
565
|
fleet_add_servicer_to_server_fn = add_FleetServicer_to_server
|
|
522
566
|
fleet_grpc_server = generic_create_grpc_server(
|
|
@@ -535,6 +579,7 @@ def _run_fleet_api_grpc_rere( # pylint: disable=R0913, R0917
|
|
|
535
579
|
return fleet_grpc_server
|
|
536
580
|
|
|
537
581
|
|
|
582
|
+
# pylint: disable=R0913, R0917
|
|
538
583
|
def _run_fleet_api_grpc_adapter(
|
|
539
584
|
address: str,
|
|
540
585
|
state_factory: LinkStateFactory,
|
|
@@ -548,6 +593,7 @@ def _run_fleet_api_grpc_adapter(
|
|
|
548
593
|
state_factory=state_factory,
|
|
549
594
|
ffs_factory=ffs_factory,
|
|
550
595
|
objectstore_factory=objectstore_factory,
|
|
596
|
+
enable_supernode_auth=False,
|
|
551
597
|
)
|
|
552
598
|
fleet_add_servicer_to_server_fn = add_GrpcAdapterServicer_to_server
|
|
553
599
|
fleet_grpc_server = generic_create_grpc_server(
|
|
@@ -678,11 +724,9 @@ def _add_args_common(parser: argparse.ArgumentParser) -> None:
|
|
|
678
724
|
parser.add_argument(
|
|
679
725
|
"--database",
|
|
680
726
|
help="A string representing the path to the database "
|
|
681
|
-
"file that will be opened.
|
|
682
|
-
"will open a connection to a database that is in RAM, "
|
|
683
|
-
"instead of on disk. If nothing is provided, "
|
|
727
|
+
"file that will be opened. If nothing is provided, "
|
|
684
728
|
"Flower will just create a state in memory.",
|
|
685
|
-
default=
|
|
729
|
+
default=FLWR_IN_MEMORY_DB_NAME,
|
|
686
730
|
)
|
|
687
731
|
parser.add_argument(
|
|
688
732
|
"--storage-dir",
|
|
@@ -692,18 +736,12 @@ def _add_args_common(parser: argparse.ArgumentParser) -> None:
|
|
|
692
736
|
parser.add_argument(
|
|
693
737
|
"--auth-list-public-keys",
|
|
694
738
|
type=str,
|
|
695
|
-
help="A CSV file (as a path str) containing a list of known public "
|
|
696
|
-
"keys to enable authentication.",
|
|
697
|
-
)
|
|
698
|
-
parser.add_argument(
|
|
699
|
-
"--auth-superlink-private-key",
|
|
700
|
-
type=str,
|
|
701
739
|
help="This argument is deprecated and will be removed in a future release.",
|
|
702
740
|
)
|
|
703
741
|
parser.add_argument(
|
|
704
|
-
"--
|
|
705
|
-
|
|
706
|
-
help="
|
|
742
|
+
"--enable-supernode-auth",
|
|
743
|
+
action="store_true",
|
|
744
|
+
help="Enable supernode authentication.",
|
|
707
745
|
)
|
|
708
746
|
|
|
709
747
|
|
|
@@ -33,10 +33,12 @@ from flwr.common.version import package_name, package_version
|
|
|
33
33
|
from flwr.proto import grpcadapter_pb2_grpc # pylint: disable=E0611
|
|
34
34
|
from flwr.proto.fab_pb2 import GetFabRequest # pylint: disable=E0611
|
|
35
35
|
from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
ActivateNodeRequest,
|
|
37
|
+
DeactivateNodeRequest,
|
|
38
38
|
PullMessagesRequest,
|
|
39
39
|
PushMessagesRequest,
|
|
40
|
+
RegisterNodeFleetRequest,
|
|
41
|
+
UnregisterNodeFleetRequest,
|
|
40
42
|
)
|
|
41
43
|
from flwr.proto.grpcadapter_pb2 import MessageContainer # pylint: disable=E0611
|
|
42
44
|
from flwr.proto.heartbeat_pb2 import SendNodeHeartbeatRequest # pylint: disable=E0611
|
|
@@ -77,15 +79,23 @@ def _handle(
|
|
|
77
79
|
class GrpcAdapterServicer(grpcadapter_pb2_grpc.GrpcAdapterServicer, FleetServicer):
|
|
78
80
|
"""Fleet API via GrpcAdapter servicer."""
|
|
79
81
|
|
|
80
|
-
def SendReceive( # pylint: disable=too-many-return-statements
|
|
82
|
+
def SendReceive( # pylint: disable=too-many-return-statements, too-many-branches
|
|
81
83
|
self, request: MessageContainer, context: grpc.ServicerContext
|
|
82
84
|
) -> MessageContainer:
|
|
83
85
|
"""."""
|
|
84
86
|
log(DEBUG, "GrpcAdapterServicer.SendReceive")
|
|
85
|
-
if request.grpc_message_name ==
|
|
86
|
-
return _handle(
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
if request.grpc_message_name == RegisterNodeFleetRequest.__qualname__:
|
|
88
|
+
return _handle(
|
|
89
|
+
request, context, RegisterNodeFleetRequest, self.RegisterNode
|
|
90
|
+
)
|
|
91
|
+
if request.grpc_message_name == ActivateNodeRequest.__qualname__:
|
|
92
|
+
return _handle(request, context, ActivateNodeRequest, self.ActivateNode)
|
|
93
|
+
if request.grpc_message_name == DeactivateNodeRequest.__qualname__:
|
|
94
|
+
return _handle(request, context, DeactivateNodeRequest, self.DeactivateNode)
|
|
95
|
+
if request.grpc_message_name == UnregisterNodeFleetRequest.__qualname__:
|
|
96
|
+
return _handle(
|
|
97
|
+
request, context, UnregisterNodeFleetRequest, self.UnregisterNode
|
|
98
|
+
)
|
|
89
99
|
if request.grpc_message_name == SendNodeHeartbeatRequest.__qualname__:
|
|
90
100
|
return _handle(
|
|
91
101
|
request, context, SendNodeHeartbeatRequest, self.SendNodeHeartbeat
|