Skip to content

Commit

Permalink
[tools/shoestring] fix: allow the user to retain their node key
Browse files Browse the repository at this point in the history
problem: migrating from symbol-bootstrap creates a new node key
solution: allow the node to retain its node key during bootstrap import and cert renewal.
  • Loading branch information
Wayonb committed Nov 27, 2024
1 parent f3f3e37 commit 4b43339
Show file tree
Hide file tree
Showing 19 changed files with 289 additions and 54 deletions.
4 changes: 3 additions & 1 deletion tools/shoestring/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,15 @@ min-cosignatures-count --config CONFIG --ca-key-path CA_KEY_PATH [--update]

### import-bootstrap

Imports settings from a symbol-bootstap installation.
Imports settings from a symbol-bootstrap installation.


```
import-bootstrap --config CONFIG --bootstrap BOOTSTRAP
--config CONFIG path to shoestring configuration file
--bootstrap BOOTSTRAP path to bootstrap target directory
--node-key include the node key
```

### import-harvesters
Expand Down Expand Up @@ -221,6 +222,7 @@ renew-certificates --config CONFIG [--directory DIRECTORY] --ca-key-path CA_KEY_
--directory DIRECTORY installation directory (default: $HOME)
--ca-key-path CA_KEY_PATH path to main private key PEM file
--renew-ca renews CA certificate too
--retain-key retain the node key
```

When `--renew-ca` is set, both CA and node certificates will be regenerated. Otherwise, only node certificate will be.
Expand Down
10 changes: 10 additions & 0 deletions tools/shoestring/shoestring/commands/import_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ async def run_main(args):
log.info(_('import-bootstrap-importing-voter').format(path=bootstrap_voting_keys_directory))
replacements.append(('imports', 'voter', str(bootstrap_voting_keys_directory)))

bootstrap_node_key = bootstrap_node_path / 'cert/node.key.pem'
if args.node_key:
if bootstrap_node_key.exists():
log.info(_('import-bootstrap-importing-node-key').format(path=bootstrap_node_key))
replacements.append(('imports', 'node_key', str(bootstrap_node_key.absolute())))
else:
log.error(_('import-bootstrap-importing-node-key-not-found').format(path=bootstrap_node_key))
sys.exit(1)

if replacements:
config_filepath = Path(args.config)
ConfigurationManager(config_filepath.parent).patch(config_filepath.name, replacements)
Expand All @@ -32,4 +41,5 @@ async def run_main(args):
def add_arguments(parser):
parser.add_argument('--config', help=_('argument-help-config'), required=True)
parser.add_argument('--bootstrap', help=_('argument-help-import-bootstrap-bootstrap'), required=True)
parser.add_argument('--node-key', help=_('argument-help-import-bootstrap-node-key'), action='store_true')
parser.set_defaults(func=run_main)
4 changes: 3 additions & 1 deletion tools/shoestring/shoestring/commands/renew_certificates.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from pathlib import Path

from shoestring.internal.CertificateFactory import CertificateFactory
from shoestring.internal.NodeKeyProvider import NodeKeyProvider
from shoestring.internal.OpensslExecutor import OpensslExecutor
from shoestring.internal.Preparer import Preparer
from shoestring.internal.ShoestringConfiguration import parse_shoestring_configuration
Expand All @@ -22,7 +23,7 @@ async def run_main(args):
else: # health node full certificate check needs current ca cert to pass
factory.reuse_ca_certificate(config.node.ca_common_name, directories.certificates)

factory.generate_random_node_private_key()
NodeKeyProvider(directories.certificates / 'node.key.pem' if args.retain_key else None).create_key_file(factory)
factory.generate_node_certificate(config.node.node_common_name)
factory.create_node_certificate_chain()

Expand All @@ -34,4 +35,5 @@ def add_arguments(parser):
parser.add_argument('--directory', help=_('argument-help-directory').format(default_path=Path.home()), default=str(Path.home()))
parser.add_argument('--ca-key-path', help=_('argument-help-ca-key-path'), required=True)
parser.add_argument('--renew-ca', help=_('argument-help-renew-certificates-renew-ca'), action='store_true')
parser.add_argument('--retain-key', help=_('argument-help-renew-certificates-retain-key'), action='store_true')
parser.set_defaults(func=run_main)
26 changes: 26 additions & 0 deletions tools/shoestring/shoestring/internal/NodeKeyProvider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import shutil
from pathlib import Path


class NodeKeyProvider:
"""Generators the Node private key."""

def __init__(self, import_source=None):
"""Creates a Node key generator."""

self.is_imported = bool(import_source)
if self.is_imported:
self.import_source = Path(import_source).absolute()

def create_key_file(self, factory):
"""Create node private key file."""

if self.is_imported:
shutil.copy(self.import_source, '.')
return

if not factory:
raise RuntimeError('unable to generate node private key')

factory.generate_random_node_private_key()

4 changes: 3 additions & 1 deletion tools/shoestring/shoestring/internal/Preparer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from .HarvesterConfigurator import HarvesterConfigurator
from .LinkTransactionBuilder import LinkTransactionBuilder
from .NodeFeatures import NodeFeatures
from .NodeKeyProvider import NodeKeyProvider
from .OpensslExecutor import OpensslExecutor
from .VoterConfigurator import VoterConfigurator

Expand Down Expand Up @@ -138,6 +139,7 @@ def __init__(self, directory, config, logger=None):
self.config_manager = ConfigurationManager(self.directories.resources)
self.harvester_configurator = HarvesterConfigurator(self.config_manager, self.config.imports.harvester)
self.voter_configurator = VoterConfigurator(self.config_manager, self.config.imports.voter)
self.node_key_provider = NodeKeyProvider(self.config.imports.node_key)

def __enter__(self):
self.temp_directory = tempfile.TemporaryDirectory()
Expand Down Expand Up @@ -330,7 +332,7 @@ def generate_certificates(self, ca_key_path, require_ca=True):

factory.extract_ca_public_key()
factory.generate_ca_certificate(self.config.node.ca_common_name)
factory.generate_random_node_private_key()
self.node_key_provider.create_key_file(factory)
factory.generate_node_certificate(self.config.node.node_common_name)
factory.create_node_certificate_chain()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
TransactionConfiguration = namedtuple('TransactionConfiguration', [
'fee_multiplier', 'timeout_hours', 'min_cosignatures_count', 'hash_lock_duration', 'currency_mosaic_id', 'locked_funds_per_aggregate'
])
ImportsConfiguration = namedtuple('ImportsConfiguration', ['harvester', 'voter'])
ImportsConfiguration = namedtuple('ImportsConfiguration', ['harvester', 'voter', 'node_key'])
NodeConfiguration = namedtuple('NodeConfiguration', [
'features', 'user_id', 'group_id', 'ca_password', 'api_https', 'ca_common_name', 'node_common_name'
])
Expand Down Expand Up @@ -63,7 +63,7 @@ def parse_transaction_configuration(config):
def parse_imports_configuration(config):
"""Parses imports configuration."""

return ImportsConfiguration(config['harvester'], config['voter'])
return ImportsConfiguration(config['harvester'], config['voter'], config['node_key'])


def parse_node_configuration(config):
Expand Down
35 changes: 26 additions & 9 deletions tools/shoestring/shoestring/lang/en/LC_MESSAGES/messages.po
Original file line number Diff line number Diff line change
Expand Up @@ -20,33 +20,37 @@ msgid "argument-help-announce-transaction-transaction"
msgstr "file containing serialized transaction to send"

#: shoestring/commands/min_cosignatures_count.py:35
#: shoestring/commands/renew_certificates.py:31
#: shoestring/commands/renew_certificates.py:36
#: shoestring/commands/setup.py:144 shoestring/commands/signer.py:93
msgid "argument-help-ca-key-path"
msgstr "path to main private key PEM file"

#: shoestring/commands/announce_transaction.py:35
#: shoestring/commands/health.py:74 shoestring/commands/import_bootstrap.py:33
#: shoestring/commands/health.py:74 shoestring/commands/import_bootstrap.py:41
#: shoestring/commands/import_harvesters.py:97 shoestring/commands/init.py:29
#: shoestring/commands/min_cosignatures_count.py:34
#: shoestring/commands/renew_certificates.py:29
#: shoestring/commands/renew_certificates.py:34
#: shoestring/commands/renew_voting_keys.py:112
#: shoestring/commands/reset_data.py:87 shoestring/commands/setup.py:136
#: shoestring/commands/signer.py:92
msgid "argument-help-config"
msgstr "path to shoestring configuration file"

#: shoestring/commands/health.py:75
#: shoestring/commands/renew_certificates.py:30
#: shoestring/commands/renew_certificates.py:35
#: shoestring/commands/renew_voting_keys.py:113
#: shoestring/commands/reset_data.py:88 shoestring/commands/setup.py:138
msgid "argument-help-directory"
msgstr "installation directory (default: {default_path})"

#: shoestring/commands/import_bootstrap.py:34
#: shoestring/commands/import_bootstrap.py:42
msgid "argument-help-import-bootstrap-bootstrap"
msgstr "path to bootstrap target directory"

#: shoestring/commands/import_bootstrap.py:43
msgid "argument-help-import-bootstrap-node-key"
msgstr "import node key"

#: shoestring/commands/import_harvesters.py:98
msgid "argument-help-import-harvesters-in-harvesters"
msgstr "input harvesters.dat file that is encrypted with in-pem"
Expand Down Expand Up @@ -83,10 +87,14 @@ msgstr "input private key file (optional)"
msgid "argument-help-pemtool-output"
msgstr "output PEM key file"

#: shoestring/commands/renew_certificates.py:32
#: shoestring/commands/renew_certificates.py:37
msgid "argument-help-renew-certificates-renew-ca"
msgstr "renews CA certificate too"

#: shoestring/commands/renew_certificates.py:38
msgid "argument-help-renew-certificates-retain-key"
msgstr "retain the node key"

#: shoestring/commands/reset_data.py:89
msgid "argument-help-reset-data-purge-harvesters"
msgstr "purge harvesters.dat file"
Expand Down Expand Up @@ -136,17 +144,17 @@ msgid "general-connecting-to-node"
msgstr "connecting to {endpoint}"

#: shoestring/commands/init.py:17 shoestring/commands/reset_data.py:57
#: shoestring/internal/Preparer.py:189
#: shoestring/internal/Preparer.py:191
#: shoestring/internal/VoterConfigurator.py:53
msgid "general-copying-file"
msgstr "copying FILE {source_path} into {destination_path}"

#: shoestring/commands/reset_data.py:62 shoestring/internal/Preparer.py:195
#: shoestring/commands/reset_data.py:62 shoestring/internal/Preparer.py:197
msgid "general-copying-tree"
msgstr "copying TREE {source_path} to {destination_path}"

#: shoestring/commands/renew_voting_keys.py:46
#: shoestring/internal/Preparer.py:388
#: shoestring/internal/Preparer.py:390
msgid "general-created-aggregate-transaction"
msgstr "created aggregate transaction with hash {transaction_hash}"

Expand Down Expand Up @@ -250,6 +258,14 @@ msgstr ""
msgid "import-bootstrap-importing-harvester"
msgstr "importing harvesting keys from bootstrap ({path})"

#: shoestring/commands/import_bootstrap.py:30
msgid "import-bootstrap-importing-node-key"
msgstr "importing node key from bootstrap ({path})"

#: shoestring/commands/import_bootstrap.py:33
msgid "import-bootstrap-importing-node-key-not-found"
msgstr "importing node key from bootstrap failed. File not found ({path})"

#: shoestring/commands/import_bootstrap.py:24
msgid "import-bootstrap-importing-voter"
msgstr "importing voting keys from bootstrap ({path})"
Expand Down Expand Up @@ -826,3 +842,4 @@ msgstr "Welcome"
#: shoestring/wizard/screens/welcome.py:36
msgid "wizard-welcome-upgrade"
msgstr "upgrade"

35 changes: 26 additions & 9 deletions tools/shoestring/shoestring/lang/ja/LC_MESSAGES/messages.po
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,37 @@ msgid "argument-help-announce-transaction-transaction"
msgstr ""

#: shoestring/commands/min_cosignatures_count.py:35
#: shoestring/commands/renew_certificates.py:31
#: shoestring/commands/renew_certificates.py:36
#: shoestring/commands/setup.py:144 shoestring/commands/signer.py:93
msgid "argument-help-ca-key-path"
msgstr ""

#: shoestring/commands/announce_transaction.py:35
#: shoestring/commands/health.py:74 shoestring/commands/import_bootstrap.py:33
#: shoestring/commands/health.py:74 shoestring/commands/import_bootstrap.py:41
#: shoestring/commands/import_harvesters.py:97 shoestring/commands/init.py:29
#: shoestring/commands/min_cosignatures_count.py:34
#: shoestring/commands/renew_certificates.py:29
#: shoestring/commands/renew_certificates.py:34
#: shoestring/commands/renew_voting_keys.py:112
#: shoestring/commands/reset_data.py:87 shoestring/commands/setup.py:136
#: shoestring/commands/signer.py:92
msgid "argument-help-config"
msgstr ""

#: shoestring/commands/health.py:75
#: shoestring/commands/renew_certificates.py:30
#: shoestring/commands/renew_certificates.py:35
#: shoestring/commands/renew_voting_keys.py:113
#: shoestring/commands/reset_data.py:88 shoestring/commands/setup.py:138
msgid "argument-help-directory"
msgstr ""

#: shoestring/commands/import_bootstrap.py:34
#: shoestring/commands/import_bootstrap.py:42
msgid "argument-help-import-bootstrap-bootstrap"
msgstr ""

#: shoestring/commands/import_bootstrap.py:43
msgid "argument-help-import-bootstrap-node-key"
msgstr ""

#: shoestring/commands/import_harvesters.py:98
msgid "argument-help-import-harvesters-in-harvesters"
msgstr ""
Expand Down Expand Up @@ -81,10 +85,14 @@ msgstr ""
msgid "argument-help-pemtool-output"
msgstr ""

#: shoestring/commands/renew_certificates.py:32
#: shoestring/commands/renew_certificates.py:37
msgid "argument-help-renew-certificates-renew-ca"
msgstr ""

#: shoestring/commands/renew_certificates.py:38
msgid "argument-help-renew-certificates-retain-key"
msgstr ""

#: shoestring/commands/reset_data.py:89
msgid "argument-help-reset-data-purge-harvesters"
msgstr ""
Expand Down Expand Up @@ -132,17 +140,17 @@ msgid "general-connecting-to-node"
msgstr ""

#: shoestring/commands/init.py:17 shoestring/commands/reset_data.py:57
#: shoestring/internal/Preparer.py:189
#: shoestring/internal/Preparer.py:191
#: shoestring/internal/VoterConfigurator.py:53
msgid "general-copying-file"
msgstr ""

#: shoestring/commands/reset_data.py:62 shoestring/internal/Preparer.py:195
#: shoestring/commands/reset_data.py:62 shoestring/internal/Preparer.py:197
msgid "general-copying-tree"
msgstr ""

#: shoestring/commands/renew_voting_keys.py:46
#: shoestring/internal/Preparer.py:388
#: shoestring/internal/Preparer.py:390
msgid "general-created-aggregate-transaction"
msgstr ""

Expand Down Expand Up @@ -242,6 +250,14 @@ msgstr ""
msgid "import-bootstrap-importing-harvester"
msgstr ""

#: shoestring/commands/import_bootstrap.py:30
msgid "import-bootstrap-importing-node-key"
msgstr ""

#: shoestring/commands/import_bootstrap.py:33
msgid "import-bootstrap-importing-node-key-not-found"
msgstr ""

#: shoestring/commands/import_bootstrap.py:24
msgid "import-bootstrap-importing-voter"
msgstr ""
Expand Down Expand Up @@ -803,3 +819,4 @@ msgstr ""
#: shoestring/wizard/screens/welcome.py:36
msgid "wizard-welcome-upgrade"
msgstr ""

Loading

0 comments on commit 4b43339

Please sign in to comment.