Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SSL support in master and clients #2676

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASE_NOTES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ ARTIQ-9 (Unreleased)
* Support for coredevice reflashing through the new ``flash`` tool in ``artiq_coremgmt``.
* ``artiq_coremgmt`` now supports configuring satellites.
* ``artiq.coredevice.fmcdio_vhdci_eem`` has been removed.
* Optional SSL support with mutual certificate authentication was added between master and clients.

ARTIQ-8
-------
Expand Down
17 changes: 16 additions & 1 deletion artiq/frontend/artiq_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ def get_argparser():
parser.add_argument("--version", action="version",
version="ARTIQ v{}".format(artiq_version),
help="print the ARTIQ version number")
parser.add_argument(
"--client-cert", default=None,
help="client SSL certificate file (default: %(default)s)")
parser.add_argument(
"--client-key", default=None,
help="client SSL private key file (default: %(default)s)")
parser.add_argument(
"--server-cert", default=None,
help="trusted server certificate file (default: %(default)s)")

subparsers = parser.add_subparsers(dest="action")
subparsers.required = True
Expand Down Expand Up @@ -391,7 +400,13 @@ def main():
"ls": "experiment_db",
"terminate": "master_management",
}[action]
remote = Client(args.server, port, target_name)

ssl_config = dict()
if args.client_cert is not None:
ssl_config = {"local_cert": args.client_cert,
"local_key": args.client_key,
"peer_cert": args.server_cert}
remote = Client(args.server, port, target_name, **ssl_config)
try:
globals()["_action_" + action](remote, args)
finally:
Expand Down
23 changes: 19 additions & 4 deletions artiq/frontend/artiq_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ def get_argparser():
parser.add_argument(
"--port-broadcast", default=1067, type=int,
help="TCP port to connect to for broadcasts (default: %(default)s)")
parser.add_argument(
"--client-cert", default=None,
help="client SSL certificate file (default: %(default)s)")
parser.add_argument(
"--client-key", default=None,
help="client SSL private key file (default: %(default)s)")
parser.add_argument(
"--server-cert", default=None,
help="trusted server certificate file (default: %(default)s)")
parser.add_argument(
"--db-file", default=None,
help="database file for local GUI settings (default: %(default)s)")
Expand Down Expand Up @@ -133,6 +142,12 @@ def main():
server=args.server.replace(":", "."),
port=args.port_notify))

ssl_config = dict()
if args.client_cert is not None:
ssl_config = {"local_cert": args.client_cert,
"local_key": args.client_key,
"peer_cert": args.server_cert}

forced_platform = []
if (QtGui.QGuiApplication.platformName() == "wayland" and
not os.getenv("QT_QPA_PLATFORM")):
Expand All @@ -149,11 +164,11 @@ def main():
for target in "schedule", "experiment_db", "dataset_db", "device_db", "interactive_arg_db":
client = AsyncioClient()
loop.run_until_complete(client.connect_rpc(
args.server, args.port_control, target))
args.server, args.port_control, target, **ssl_config))
atexit.register(client.close_rpc)
rpc_clients[target] = client

master_management = Client(args.server, args.port_control, "master_management")
master_management = Client(args.server, args.port_control, "master_management", **ssl_config)
try:
server_name = master_management.get_name()
finally:
Expand All @@ -176,15 +191,15 @@ def report_disconnect():
("interactive_args", interactive_args.Model)):
subscriber = ModelSubscriber(notifier_name, modelf, report_disconnect)
loop.run_until_complete(subscriber.connect(
args.server, args.port_notify))
args.server, args.port_notify, **ssl_config))
atexit_register_coroutine(subscriber.close, loop=loop)
sub_clients[notifier_name] = subscriber

broadcast_clients = dict()
for target in "log", "ccb":
client = Receiver(target, [], report_disconnect)
loop.run_until_complete(client.connect(
args.server, args.port_broadcast))
args.server, args.port_broadcast, **ssl_config))
atexit_register_coroutine(client.close, loop=loop)
broadcast_clients[target] = client

Expand Down
25 changes: 21 additions & 4 deletions artiq/frontend/artiq_master.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,17 @@ def get_argparser():
"--experiment-subdir", default="",
help=("path to the experiment folder from the repository root "
"(default: %(default)s)"))

group = parser.add_argument_group("SSL")
group.add_argument(
"--server-cert", default=None,
help="server SSL certificate file (enables SSL) (default: %(default)s)")
group.add_argument(
"--server-key", default=None,
help="server SSL private key file (default: %(default)s)")
group.add_argument(
"--client-cert", default=None,
help="trusted client certificate file (default: %(default)s)")
log_args(parser)

parser.add_argument("--name",
Expand All @@ -77,9 +88,15 @@ def main():
atexit.register(signal_handler.teardown)
bind = common_args.bind_address_from_args(args)

ssl_config = dict()
if args.server_cert is not None:
ssl_config = {"local_cert": args.server_cert,
"local_key": args.server_key,
"peer_cert": args.client_cert}

server_broadcast = Broadcaster()
loop.run_until_complete(server_broadcast.start(
bind, args.port_broadcast))
bind, args.port_broadcast, **ssl_config))
atexit_register_coroutine(server_broadcast.stop, loop=loop)

log_forwarder.callback = lambda msg: server_broadcast.broadcast("log", msg)
Expand Down Expand Up @@ -148,7 +165,7 @@ def get_interactive_arguments(*args, **kwargs):
"experiment_db": experiment_db,
}, allow_parallel=True)
loop.run_until_complete(server_control.start(
bind, args.port_control))
bind, args.port_control, **ssl_config))
atexit_register_coroutine(server_control.stop, loop=loop)

server_notify = Publisher({
Expand All @@ -160,12 +177,12 @@ def get_interactive_arguments(*args, **kwargs):
"explist_status": experiment_db.status,
})
loop.run_until_complete(server_notify.start(
bind, args.port_notify))
bind, args.port_notify, **ssl_config))
atexit_register_coroutine(server_notify.stop, loop=loop)

server_logging = LoggingServer()
loop.run_until_complete(server_logging.start(
bind, args.port_logging))
bind, args.port_logging, **ssl_config))
atexit_register_coroutine(server_logging.stop, loop=loop)

print("ARTIQ master is now ready.")
Expand Down