replace predefined ssh keys with generated ones
This commit is contained in:
parent
8d3e913a8c
commit
28298d3ce6
3 changed files with 105 additions and 6 deletions
|
@ -33,14 +33,17 @@ else:
|
||||||
backup_target = repo.get_node(node.metadata.get('backup-client/target'))
|
backup_target = repo.get_node(node.metadata.get('backup-client/target'))
|
||||||
|
|
||||||
files['/etc/backup.priv'] = {
|
files['/etc/backup.priv'] = {
|
||||||
'content': repo.vault.decrypt_file(join('backup', 'keys', f'{node.name}.key.vault')),
|
'content': repo.libs.ssh.generate_ed25519_private_key(
|
||||||
|
node.metadata.get('backup-client/user-name'),
|
||||||
|
backup_target,
|
||||||
|
),
|
||||||
'mode': '0400',
|
'mode': '0400',
|
||||||
}
|
}
|
||||||
|
|
||||||
files['/usr/local/bin/generate-backup'] = {
|
files['/usr/local/bin/generate-backup'] = {
|
||||||
'content_type': 'mako',
|
'content_type': 'mako',
|
||||||
'context': {
|
'context': {
|
||||||
'username': node.metadata['backup-client']['user-name'],
|
'username': node.metadata.get('backup-client/user-name'),
|
||||||
'server': backup_target.metadata.get('backup-server/my_hostname'),
|
'server': backup_target.metadata.get('backup-server/my_hostname'),
|
||||||
'port': backup_target.metadata.get('backup-server/my_ssh_port'),
|
'port': backup_target.metadata.get('backup-server/my_ssh_port'),
|
||||||
'paths': backup_paths,
|
'paths': backup_paths,
|
||||||
|
|
|
@ -27,9 +27,6 @@ directories['/etc/backup-server/clients'] = {
|
||||||
sudoers = {}
|
sudoers = {}
|
||||||
|
|
||||||
for nodename, config in node.metadata.get('backup-server/clients', {}).items():
|
for nodename, config in node.metadata.get('backup-server/clients', {}).items():
|
||||||
with open(join(repo.path, 'data', 'backup', 'keys', f'{nodename}.pub'), 'r') as f:
|
|
||||||
pubkey = f.read().strip()
|
|
||||||
|
|
||||||
sudoers[config['user']] = nodename
|
sudoers[config['user']] = nodename
|
||||||
|
|
||||||
users[config['user']] = {
|
users[config['user']] = {
|
||||||
|
@ -41,7 +38,10 @@ for nodename, config in node.metadata.get('backup-server/clients', {}).items():
|
||||||
}
|
}
|
||||||
|
|
||||||
files[f'/srv/backups/{nodename}/.ssh/authorized_keys'] = {
|
files[f'/srv/backups/{nodename}/.ssh/authorized_keys'] = {
|
||||||
'content': pubkey,
|
'content': repo.libs.ssh.generate_ed25519_public_key(
|
||||||
|
config['user'],
|
||||||
|
node,
|
||||||
|
),
|
||||||
'owner': config['user'],
|
'owner': config['user'],
|
||||||
'mode': '0400',
|
'mode': '0400',
|
||||||
'needs': {
|
'needs': {
|
||||||
|
|
96
libs/ssh.py
Normal file
96
libs/ssh.py
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
Loading…
Reference in a new issue