Skip to content

Commit

Permalink
bw/bundles/crs-worker: manage upload ssh keys
Browse files Browse the repository at this point in the history
  • Loading branch information
Kunsi committed May 12, 2024
1 parent faa0754 commit fef10d7
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 2 deletions.
3 changes: 1 addition & 2 deletions bundlewrap/bundles/crs-worker/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,9 @@

if not node.has_bundle('cifs-client'):
files['/video/upload-key'] = {
'content_type': 'any', # do not touch file contents
'content': repo.libs.ssh.generate_ed25519_private_key('upload', node),
'owner': 'voc',
'mode': '0600',
'unless': '! test -f /video/upload-key',
}

files['/etc/fuse.conf'] = {}
Expand Down
8 changes: 8 additions & 0 deletions bundlewrap/bundles/releasing_specials/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,11 @@
},
},
}

files['/home/upload/.ssh/authorized_keys'] = {
'content': repo.libs.faults.join_faults(
sorted(node.metadata.get('users/upload/ssh_pubkeys')),
'\n',
) + '\n',
'owner': 'upload',
}
18 changes: 18 additions & 0 deletions bundlewrap/bundles/releasing_specials/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,21 @@
},
},
}


@metadata_reactor.provides(
'users/upload/ssh_pubkeys',
)
def upload_keys(metadata):
pubkeys = set()
for rnode in repo.nodes:
if not rnode.has_bundle('crs-worker'):
continue
pubkeys.add(repo.libs.ssh.generate_ed25519_public_key('upload', rnode))
return {
'users': {
'upload': {
'ssh_pubkeys': pubkeys,
},
},
}
94 changes: 94 additions & 0 deletions bundlewrap/libs/ssh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from base64 import b64decode, b64encode
from functools import lru_cache
from hashlib import sha3_224

from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives.serialization import (Encoding,
NoEncryption,
PrivateFormat,
PublicFormat)

from bundlewrap.utils import Fault


@lru_cache(maxsize=None)
def generate_ed25519_private_key(username, node):
return Fault(
f'private key {username}@{node.name}',
lambda username, node: _generate_ed25519_private_key(username, node),
username=username,
node=node,
)


@lru_cache(maxsize=None)
def generate_ed25519_public_key(username, node):
return Fault(
f'public key {username}@{node.name}',
lambda username, node: _generate_ed25519_public_key(username, node),
username=username,
node=node,
)


def _generate_ed25519_private_key(username, node):
privkey_bytes = Ed25519PrivateKey.from_private_bytes(_secret(username, node))

nondeterministic_privatekey = privkey_bytes.private_bytes(
encoding=Encoding.PEM,
format=PrivateFormat.OpenSSH,
encryption_algorithm=NoEncryption(),
).decode()

# get relevant lines from string
nondeterministic_bytes = b64decode(
''.join(nondeterministic_privatekey.split('\n')[1:-2])
)

# sanity check
if nondeterministic_bytes[98:102] != nondeterministic_bytes[102:106]:
raise Exception("checksums should be the same: whats going on here?")

# replace random bytes with deterministic values
random_bytes = sha3_224(_secret(username, node)).digest()[0:4]
deterministic_bytes = (
nondeterministic_bytes[:98]
+ random_bytes
+ random_bytes
+ nondeterministic_bytes[106:]
)

# reassemble file
deterministic_privatekey = '\n'.join(
[
'-----BEGIN OPENSSH PRIVATE KEY-----',
b64encode(deterministic_bytes).decode(),
'-----END OPENSSH PRIVATE KEY-----',
]
) + '\n'

return deterministic_privatekey


def _generate_ed25519_public_key(username, node):
return (
Ed25519PrivateKey.from_private_bytes(_secret(username, node))
.public_key()
.public_bytes(
encoding=Encoding.OpenSSH,
format=PublicFormat.OpenSSH,
)
.decode()
+ f' {username}@{node.name}'
)


@lru_cache(maxsize=None)
def _secret(username, node):
return b64decode(
str(
node.repo.vault.random_bytes_as_base64_for(
f"{username}@{node.name}", length=32
)
)
)

0 comments on commit fef10d7

Please sign in to comment.