diff --git a/examples/heart-uci/client.py b/examples/heart-uci/client.py index ff9ae126014afb869772a6d20b0f1290f1b55d30..8e30f6d693baacd9d9cb699fa15e3ec2e7b70216 100644 --- a/examples/heart-uci/client.py +++ b/examples/heart-uci/client.py @@ -36,6 +36,8 @@ from data import get_data # pylint: disable=wrong-import-order def run_client( name: str, ca_cert: str, + protocol: str = "websockets", + serv_uri: str = "wss://localhost:8765", ) -> None: """Instantiate and run a given client. @@ -46,6 +48,10 @@ def run_client( ca_cert: str Path to the certificate authority file that was used to sign the server's SSL certificate. + protocol: str, default="websockets" + Name of the communication protocol to use. + serv_uri: str, default="wss://localhost:8765" + URI of the server to which to connect. """ # (1-2) Interface training and optional validation data. @@ -73,8 +79,8 @@ def run_client( # Here, use websockets protocol on localhost:8765, with SSL encryption. network = NetworkClientConfig( - protocol="websockets", - server_uri="wss://localhost:8765", + protocol=protocol, + server_uri=serv_uri, name=name, certificate=ca_cert, ) @@ -104,12 +110,26 @@ if __name__ == "__main__": choices=["cleveland", "hungarian", "switzerland", "va"], ) parser.add_argument( - "--cert_path", + "--cert", dest="cert_path", type=str, help="path to the client-side ssl certification", default=os.path.join(FILEDIR, "ca-cert.pem"), ) + parser.add_argument( + "--protocol", + dest="protocol", + type=str, + help="name of the communication protocol to use", + default="websockets", + ) + parser.add_argument( + "--uri", + dest="uri", + type=str, + help="server URI to which to connect", + default="wss://localhost:8765", + ) args = parser.parse_args() # Run the client routine. - run_client(args.name, args.cert_path) + run_client(args.name, args.cert_path, args.protocol, args.uri) diff --git a/examples/heart-uci/readme.md b/examples/heart-uci/readme.md index a0869b066b99bc791b212603c9ccecb6fd47f5a3..90897adef12823c6ed44110b86c623efc9376ddc 100644 --- a/examples/heart-uci/readme.md +++ b/examples/heart-uci/readme.md @@ -60,28 +60,50 @@ communications. We give the code to simulate this on a single machine. We then sequentially run the server then the clients on separate terminals. -1. **Set up self-signed SSL certificates**:<br/> - Start by running executing the `gen_ssl.py` script. - This creates self-signed SSL certificates: +1. **Set up SSL certificates**:<br/> + Start by creating a signed SSL certificate for the server and sharing the + CA file with each and every clients. The CA may be self-signed. + + When testing locally, execute the `gen_ssl.py` script, to create a + self-signed root CA and an SSL certificate for "localhost": ```bash python gen_ssl.py ``` + Note that in real-life applications, one would most likely use certificates - signed by a trusted certificate authority instead. + certificates signed by a trusted certificate authority instead. + Alternatively, `declearn.test_utils.gen_ssl_certificates` may be used to + generate a self-signed CA and a signed certificate for a given domain name + or IP address. 2. **Run the server**:<br/> Open a terminal and launch the server script for 1 to 4 clients, - using the generated SSL certificates: + specifying the path to the SSL certificate and private key files, + and network parameters. By default, things will run on the local + host, looking for `gen_ssl.py`-created PEM files. + + E.g., to use 2 clients: ```bash - python server.py 2 # use --help for details on SSL files options + python server.py 2 # use --help for details on network and SSL options ``` 3. **Run each client**:<br/> - Open a new terminal and launch the client script, using one of the - dataset's location name and the generated SSL certificate, e.g.: + Open a new terminal and launch the client script, specifying one of the + dataset-provider names, and optionally the path the CA file and network + parameters. By default, things will run on the local host, looking for + a `gen_ssl.py`-created CA PEM file. + + E.g., to launch a client using the "cleveland" dataset: ```bash - python client.py cleveland # use --help for details on SSL files options + python client.py cleveland # use --help for details on other options ``` Note that the server should be launched before the clients, otherwise the -latter would fail to connect which might cause the script to terminate. +latter might fail to connect which would cause the script to terminate. A +few seconds' delay is tolerable as clients will make multiple connection +attempts prior to failing. + +**To run the example in a real-life setting**, follow the instructions from +this section, after having generated and shared the appropriate PEM files to +set up SSL-encryption, and using additional script parameters to specify the +network host and port to use. diff --git a/examples/heart-uci/server.py b/examples/heart-uci/server.py index f63607c6e37e0b0ac088a0e64dbd6524bd1df239..56de202ff0cd093c998527c419f0e848f52260dd 100644 --- a/examples/heart-uci/server.py +++ b/examples/heart-uci/server.py @@ -31,7 +31,10 @@ FILEDIR = os.path.dirname(os.path.abspath(__file__)) def run_server( nb_clients: int, sv_cert: str, - sv_priv: str, + sv_pkey: str, + protocol: str = "websockets", + host: str = "localhost", + port: int = 8765, ) -> None: """Instantiate and run the orchestrating server. @@ -41,8 +44,14 @@ def run_server( Exact number of clients used in this example. sv_cert: str Path to the (self-signed) SSL certificate to use. - sv_priv: str + sv_pkey: str Path to the associated private-key to use. + protocol: str, default="websockets" + Name of the communication protocol to use. + host: str, default="localhost" + Hostname or IP address on which to serve. + port: int, default=8765 + Communication port on which to serve. """ # (1) Define a model @@ -88,11 +97,11 @@ def run_server( # Here, use websockets protocol on localhost:8765, with SSL encryption. network = NetworkServerConfig( - protocol="websockets", - host="localhost", - port=8765, + protocol=protocol, + host=host, + port=port, certificate=sv_cert, - private_key=sv_priv, + private_key=sv_pkey, ) # (4) Instantiate and run a FederatedServer. @@ -131,19 +140,40 @@ if __name__ == "__main__": choices=[1, 2, 3, 4], ) parser.add_argument( - "--cert_path", - dest="cert_path", + "--cert", + dest="sv_cert", type=str, - help="path to the server-side ssl certification", + help="path to the server-side ssl certificate", default=os.path.join(FILEDIR, "server-cert.pem"), ) parser.add_argument( - "--key_path", - dest="key_path", + "--pkey", + dest="sv_pkey", type=str, help="path to the server-side ssl private key", default=os.path.join(FILEDIR, "server-pkey.pem"), ) + parser.add_argument( + "--protocol", + dest="protocol", + type=str, + help="name of the communication protocol to use", + default="websockets", + ) + parser.add_argument( + "--host", + dest="host", + type=str, + help="hostname or IP address on which to serve", + default="localhost", + ) + parser.add_argument( + "--port", + dest="port", + type=int, + help="communication port on which to serve", + default=8765, + ) args = parser.parse_args() # Run the server routine. - run_server(args.nb_clients, args.cert_path, args.key_path) + run_server(**args.__dict__)