diff --git a/mtik_cert_pusher/__main__.py b/mtik_cert_pusher/__main__.py index db27c4a..dd0a686 100644 --- a/mtik_cert_pusher/__main__.py +++ b/mtik_cert_pusher/__main__.py @@ -29,24 +29,24 @@ def make_arg_parser(): description="push TLS privkey & certificate to MikroTik RouterOS router", ) - subparsers = parser.add_subparsers(dest="subcommand") + subparsers = parser.add_subparsers(dest="subcommand", required=True) - install_parser = subparsers.add_parser( - "install", help="push TLS privkey & certificate to MikroTik RouterOS router" + deploy_parser = subparsers.add_parser( + "deploy", help="deploy TLS privkey & certificate to MikroTik RouterOS router" ) - install_parser.add_argument( - "-k", "--privkey", type=Path, required=True, help="private key file" + deploy_parser.add_argument("--ssh-config", type=Path, help="ssh config file") + deploy_parser.add_argument("--ssh-user", help="target ssh user") + deploy_parser.add_argument("--ssh-port", type=int, help="target ssh port") + deploy_parser.add_argument("--ssh-host", required=True, help="target ssh host") + deploy_parser.add_argument( + "-k", "--private-key", type=Path, required=True, help="PEM private key file" ) - install_parser.add_argument( - "--cert", type=Path, required=True, help="certificate file" + deploy_parser.add_argument( + "--cert", type=Path, required=True, help="PEM certificate file" ) - install_parser.add_argument( + deploy_parser.add_argument( "--chain", type=Path, help="separate certificate chain file (optional)" ) - install_parser.add_argument("--ssh-config", type=Path, help="ssh config file") - install_parser.add_argument("--ssh-host", required=True, help="target ssh host") - install_parser.add_argument("--ssh-user", help="target ssh user") - install_parser.add_argument("--ssh-port", type=int, help="target ssh port") fingerprint_parser = subparsers.add_parser( "fingerprint", aliases=["fpr"], help="calculate fingerprint of certificate(s)" @@ -54,22 +54,21 @@ def make_arg_parser(): fingerprint_parser.add_argument( dest="files", metavar="cert.pem", - nargs='+', + nargs="+", type=Path, help="PEM certificate file to read", ) skid_parser = subparsers.add_parser( - #"skid", help="calculate SubjectKeyIdentifier of certificate(s) or key(s)" "skid", help="show the SubjectKeyIdentifier of certificate(s)" ) skid_parser.add_argument( dest="files", - #metavar="file.pem", + # metavar="file.pem", metavar="cert.pem", - nargs='+', + nargs="+", type=Path, - #help="PEM file to read", + # help="PEM file to read", help="PEM certificate file to read", ) @@ -85,10 +84,10 @@ def parse_args(): def main(): args, parser = parse_args() - if args.subcommand == "install": + if args.subcommand == "deploy": assert ":" not in args.ssh_host - privkey_data = args.privkey.read_text() + privkey_data = args.private_key.read_text() cert_data = args.cert.read_text() chain_data = args.chain.read_text() if args.chain is not None else None @@ -106,7 +105,6 @@ def main(): ros_remote.use_certificate(fingerprint) - elif args.subcommand in ("fingerprint", "fpr"): for path in args.files: try: @@ -116,7 +114,7 @@ def main(): exc.add_note(f"path={path}") raise - elif args.subcommand in ("skid"): + elif args.subcommand == "skid": for path in args.files: try: for cert_obj in x509.load_pem_x509_certificates(path.read_bytes()): diff --git a/mtik_cert_pusher/connector.py b/mtik_cert_pusher/connector.py index baf3abe..1c98b60 100644 --- a/mtik_cert_pusher/connector.py +++ b/mtik_cert_pusher/connector.py @@ -98,7 +98,7 @@ class SSHConnector(Connector): self, cmdline: str, text: bool = False, capture: bool = False ) -> str: cmd = self._ssh_args([self.ssh_host, cmdline]) - #print("running: ", shlex.join(cmd)) + # print("running: ", shlex.join(cmd)) if capture: return subprocess.check_output(cmd, text=text) subprocess.run(cmd, check=True) @@ -106,7 +106,9 @@ class SSHConnector(Connector): def create_remote_files(self, content_by_name: dict, remote_directory: str): if not content_by_name: raise ValueError("require at least one file to copy") - with tempfile.TemporaryDirectory(dir=self.temporary_directory, prefix="mtik-connector-tmp") as td: + with tempfile.TemporaryDirectory( + dir=self.temporary_directory, prefix="mtik-connector-tmp" + ) as td: tempfile_paths = [] # Write the files to a temporary directory @@ -127,5 +129,5 @@ class SSHConnector(Connector): f"{self.ssh_host}:{remote_directory}", ] ) - #print("running: ", shlex.join(cmd)) + # print("running: ", shlex.join(cmd)) subprocess.run(cmd, check=True) diff --git a/mtik_cert_pusher/routeros.py b/mtik_cert_pusher/routeros.py index a631489..d0495d3 100644 --- a/mtik_cert_pusher/routeros.py +++ b/mtik_cert_pusher/routeros.py @@ -250,7 +250,7 @@ class RouterOS: raise ValueError(f"illegal fingerprint {fingerprint!r}") cmds = [ f'/ip/service set api-ssl,www-ssl certificate=[/certificate find where fingerprint="{fingerprint}"]', - f':put [:serialize to=json value={{[/certificate get [/ip/service get api-ssl certificate] fingerprint],[/certificate get [/ip/service get www-ssl certificate] fingerprint]}}]' + ":put [:serialize to=json value={[/certificate get [/ip/service get api-ssl certificate] fingerprint],[/certificate get [/ip/service get www-ssl certificate] fingerprint]}]", ] remote_cmdline = "\n".join(cmds) raw_result = self.connector.invoke_remote_command( diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8322c86 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,40 @@ +[project] +name = "mtik-cert-pusher" +version = "0.0.1" +description = "Script that pushes a TLS private key and certificate chain to a host running MikroTik RouterOS" +authors = [ + {name = "Darsey Litzenberger",email = "dlitz@dlitz.net"} +] +license = "MIT" +#readme = "README.md" +requires-python = ">=3.13" +dependencies = [ + "cryptography (>=40.0.0)" +] + +[dependency-groups] +dev = [ + "black (>=26.3.1,<27.0.0)", + "isort (>=8.0.1,<9.0.0)" +] + + +[build-system] +requires = ["poetry-core>=2.0.0,<3.0.0"] +build-backend = "poetry.core.masonry.api" + +[project.scripts] +mtik-cert-pusher = "mtik_cert_pusher.__main__:main" + +[tool.black] +# make black compatible with isort +line_length = 88 + +[tool.flake8] +max-line-length=88 + +[tool.isort] +# make isort compatible with black +multi_line_output = 3 +line_length = 88 +include_trailing_comma = true