Skip to content
Open
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
2 changes: 1 addition & 1 deletion contrib/sync-places.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ async def do_dump(session, args):
strings and tags should be assigned to those places. The files are
structured like:

places: # A dictonary of places where each key is a place name
places: # A dictionary of places where each key is a place name
my-place1: # Replace with your place
matches: # A list of match patterns. Replace with your match patterns
- "*/my-place1/*"
Expand Down
38 changes: 38 additions & 0 deletions labgrid/remote/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import traceback
from enum import Enum
from functools import wraps
from ipaddress import ip_address
from os import environ
import time
from contextlib import contextmanager
import copy
Expand Down Expand Up @@ -173,6 +175,20 @@ class ResourceImport(ResourceEntry):
orphaned = attr.ib(init=False, default=False, validator=attr.validators.instance_of(bool))


def _is_from_loopback_ip(peer: str) -> bool:
family, address_and_port = peer.split(":", maxsplit=1)
if family not in ("ipv4", "ipv6"):
msg = "Invalid address family found"
logging.warning(msg)
return False
address, _port = address_and_port.rsplit(":", maxsplit=1)
address = address.removeprefix("%5B").removesuffix("%5D")
try:
return ip_address(address).is_loopback
except ValueError:
return False


def locked(func):
@wraps(func)
async def wrapper(self, *args, **kwargs):
Expand All @@ -182,6 +198,20 @@ async def wrapper(self, *args, **kwargs):
return wrapper


def auth_required(func):
@wraps(func)
async def decorated(self, request, context):
if environ.get("AUTH") == "LOCAL_ADMIN":
peer = context.peer()
if not _is_from_loopback_ip(peer):
context.set_code(grpc.StatusCode.UNAUTHENTICATED)
context.set_details("Access was not local")
raise grpc.RpcError(grpc.StatusCode.UNAUTHENTICATED, "Access was not local")
return await func(self, request, context)

return decorated


class ExporterCommand:
def __init__(self, request) -> None:
self.request = request
Expand Down Expand Up @@ -499,6 +529,7 @@ async def request_task():
except KeyError:
logging.info("Never received startup from peer %s that disconnected", peer)

@auth_required
@locked
async def AddPlace(self, request, context):
name = request.name
Expand All @@ -513,6 +544,7 @@ async def AddPlace(self, request, context):
self.save_later()
return labgrid_coordinator_pb2.AddPlaceResponse()

@auth_required
@locked
async def DeletePlace(self, request, context):
name = request.name
Expand All @@ -529,6 +561,7 @@ async def DeletePlace(self, request, context):
self.save_later()
return labgrid_coordinator_pb2.DeletePlaceResponse()

@auth_required
@locked
async def AddPlaceAlias(self, request, context):
placename = request.placename
Expand All @@ -543,6 +576,7 @@ async def AddPlaceAlias(self, request, context):
self.save_later()
return labgrid_coordinator_pb2.AddPlaceAliasResponse()

@auth_required
@locked
async def DeletePlaceAlias(self, request, context):
placename = request.placename
Expand All @@ -560,6 +594,7 @@ async def DeletePlaceAlias(self, request, context):
self.save_later()
return labgrid_coordinator_pb2.DeletePlaceAliasResponse()

@auth_required
@locked
async def SetPlaceTags(self, request, context):
placename = request.placename
Expand Down Expand Up @@ -589,6 +624,7 @@ async def SetPlaceTags(self, request, context):
self.save_later()
return labgrid_coordinator_pb2.SetPlaceTagsResponse()

@auth_required
@locked
async def SetPlaceComment(self, request, context):
placename = request.placename
Expand All @@ -603,6 +639,7 @@ async def SetPlaceComment(self, request, context):
self.save_later()
return labgrid_coordinator_pb2.SetPlaceCommentResponse()

@auth_required
@locked
async def AddPlaceMatch(self, request, context):
placename = request.placename
Expand All @@ -621,6 +658,7 @@ async def AddPlaceMatch(self, request, context):
self.save_later()
return labgrid_coordinator_pb2.AddPlaceMatchResponse()

@auth_required
@locked
async def DeletePlaceMatch(self, request, context):
placename = request.placename
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ dependencies = [
"exceptiongroup>=1.3.0", # TODO: drop if Python >= 3.11 guaranteed
"grpcio>=1.64.1, <2.0.0",
"grpcio-reflection>=1.64.1, <2.0.0",
"ipaddress>=1.0",
"protobuf>=5.27.0",
"jinja2>=3.0.2",
"pexpect>=4.8.0",
Expand Down
Loading