Skip to content

Commit

Permalink
feat(update-server): add from_local endpoint to add ssh rsa keys from…
Browse files Browse the repository at this point in the history
… usb drives connected to the Flex (#13509)
  • Loading branch information
vegano1 authored Sep 12, 2023
1 parent 4da8e69 commit 443e88c
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 13 deletions.
68 changes: 55 additions & 13 deletions update-server/otupdate/common/ssh_key_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,16 @@
import ipaddress
import logging
import os
from typing import (
Any,
Generator,
IO,
List,
Tuple,
)

from aiohttp import web
from pathlib import Path
from typing import Any, Generator, IO, List, Tuple

from .handler_type import Handler


LOG = logging.getLogger(__name__)
SSH_DIR = Path(os.path.expanduser("~/.ssh"))
AUTHORIZED_KEYS = SSH_DIR / "authorized_keys"


def require_linklocal(handler: Handler) -> Handler:
Expand Down Expand Up @@ -109,6 +105,12 @@ def key_present(hashval: str) -> bool:
return hashval in [keyhash for keyhash, _ in get_keys()]


def key_error(error: str, message: str, status: int = 400) -> web.Response:
return web.json_response( # type: ignore[no-untyped-call,no-any-return]
data={"error": error, "message": message}, status=status
)


@require_linklocal
async def list_keys(request: web.Request) -> web.Response:
"""List keys in the authorized_keys file.
Expand Down Expand Up @@ -138,11 +140,6 @@ async def add(request: web.Request) -> web.Response:
If the key string doesn't look like an openssh public key, rejects with 400
"""

def key_error(error: str, message: str) -> web.Response:
return web.json_response( # type: ignore[no-untyped-call,no-any-return]
data={"error": error, "message": message}, status=400
)

body = await request.json()

if "key" not in body or not isinstance(body["key"], str):
Expand Down Expand Up @@ -238,3 +235,48 @@ async def remove(request: web.Request) -> web.Response:
},
status=200,
)


async def add_from_local(request: web.Request) -> web.Response:
"""Add a public keys from usb device to the authorized_keys file.
POST /server/ssh_keys/from_local
-> 201 Created
-> 404 Not Found otherwise
"""

LOG.info("Searching for public keys in /media")
pub_keys = [
Path(root, file)
for root, _, files in os.walk("/media")
for file in files
if file.endswith(".pub")
]
if not pub_keys:
LOG.warning("No keys found")
return key_error("no-key", "No valid keys found", 404)

# Create the .ssh folder if it does not exist
if not os.path.exists(SSH_DIR):
os.mkdir(SSH_DIR, mode=0o700)

# Update the existing keys if the ssh public key is valid
new_keys = list()
with open(AUTHORIZED_KEYS, "a") as fh:
for key in pub_keys:
with open(key, "r") as gh:
ssh_key = gh.read()
if "ssh-rsa" not in ssh_key:
LOG.warning(f"Invalid ssh public key: {key}")
continue
key_hash = hashlib.new("md5", ssh_key.encode()).hexdigest()
if not key_present(key_hash):
fh.write(f"{ssh_key}\n")
LOG.info(f"Added new rsa key: {key}")
new_keys.append(key_hash)

return web.json_response( # type: ignore[no-untyped-call,no-any-return]
data={"message": f"Added {len(new_keys)} new keys", "key_md5": new_keys},
status=201,
)
1 change: 1 addition & 0 deletions update-server/otupdate/openembedded/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ async def get_app(
web.post("/server/restart", control.restart),
web.get("/server/ssh_keys", ssh_key_management.list_keys),
web.post("/server/ssh_keys", ssh_key_management.add),
web.post("/server/ssh_keys/from_local", ssh_key_management.add_from_local),
web.delete("/server/ssh_keys", ssh_key_management.clear),
web.delete("/server/ssh_keys/{key_md5}", ssh_key_management.remove),
web.post("/server/name", name_management.set_name_endpoint),
Expand Down

0 comments on commit 443e88c

Please sign in to comment.