Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F3766274
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
11 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/_tests/Makefile b/_tests/Makefile
index 84b6ad0..6be2616 100644
--- a/_tests/Makefile
+++ b/_tests/Makefile
@@ -1,4 +1,9 @@
-test:
+test: test-python test-bats
+
+test-python:
python -m unittest discover modules
python -m unittest discover pillar
python -m unittest discover scripts/python
+
+test-bats:
+ bats scripts/bats/test_edit_acme_dns_accounts.sh
diff --git a/_tests/data/acmedns-merged.json b/_tests/data/acmedns-merged.json
new file mode 100644
index 0000000..1604612
--- /dev/null
+++ b/_tests/data/acmedns-merged.json
@@ -0,0 +1,29 @@
+{
+ "nasqueron.org": {
+ "username": "082e20b6-83e2-4844-9dc8-95d625c6c8bb",
+ "password": "password1",
+ "fulldomain": "f65af502-9d34-402d-9a7e-c11529509b84.acme.nasqueron.org",
+ "subdomain": "f65af502-9d34-402d-9a7e-c11529509b84",
+ "allowFrom": [
+ "127.0.0.1"
+ ]
+ },
+ "foo.tld": {
+ "username": "9ec73be3-5556-11ea-9c51-0007cb03f249",
+ "password": "password2",
+ "fulldomain": "9f44bf1f-5556-11ea-9c51-0007cb03f249.acme.nasqueron.org",
+ "subdomain": "9f44bf1f-5556-11ea-9c51-0007cb03f249",
+ "allowFrom": [
+ "127.0.0.1"
+ ]
+ },
+ "bar.tld": {
+ "username": "c60f2111-5556-11ea-9c51-0007cb03f249",
+ "password": "password3",
+ "fulldomain": "c66a1b4c-5556-11ea-9c51-0007cb03f249.acme.nasqueron.org",
+ "subdomain": "c66a1b4c-5556-11ea-9c51-0007cb03f249",
+ "allowFrom": [
+ "127.0.0.1"
+ ]
+ }
+}
diff --git a/_tests/data/acmedns-toimport.json b/_tests/data/acmedns-toimport.json
new file mode 100644
index 0000000..b9cc8d5
--- /dev/null
+++ b/_tests/data/acmedns-toimport.json
@@ -0,0 +1,11 @@
+{
+ "bar.tld": {
+ "username": "c60f2111-5556-11ea-9c51-0007cb03f249",
+ "password": "password3",
+ "fulldomain": "c66a1b4c-5556-11ea-9c51-0007cb03f249.acme.nasqueron.org",
+ "subdomain": "c66a1b4c-5556-11ea-9c51-0007cb03f249",
+ "allowFrom": [
+ "127.0.0.1"
+ ]
+ }
+}
diff --git a/_tests/data/acmedns.json b/_tests/data/acmedns.json
new file mode 100644
index 0000000..4e5e15a
--- /dev/null
+++ b/_tests/data/acmedns.json
@@ -0,0 +1,20 @@
+{
+ "nasqueron.org": {
+ "username": "082e20b6-83e2-4844-9dc8-95d625c6c8bb",
+ "password": "password1",
+ "fulldomain": "f65af502-9d34-402d-9a7e-c11529509b84.acme.nasqueron.org",
+ "subdomain": "f65af502-9d34-402d-9a7e-c11529509b84",
+ "allowFrom": [
+ "127.0.0.1"
+ ]
+ },
+ "foo.tld": {
+ "username": "9ec73be3-5556-11ea-9c51-0007cb03f249",
+ "password": "password2",
+ "fulldomain": "9f44bf1f-5556-11ea-9c51-0007cb03f249.acme.nasqueron.org",
+ "subdomain": "9f44bf1f-5556-11ea-9c51-0007cb03f249",
+ "allowFrom": [
+ "127.0.0.1"
+ ]
+ }
+}
diff --git a/_tests/scripts/bats/test_edit_acme_dns_accounts.sh b/_tests/scripts/bats/test_edit_acme_dns_accounts.sh
new file mode 100755
index 0000000..ebd63c4
--- /dev/null
+++ b/_tests/scripts/bats/test_edit_acme_dns_accounts.sh
@@ -0,0 +1,55 @@
+#!/usr/bin/env bats
+
+SCRIPT="../roles/paas-docker/letsencrypt/files/edit-acme-dns-accounts.py"
+
+# -------------------------------------------------------------
+# Arguments parsing
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+@test "exit with error code if no arg provided" {
+ run $SCRIPT
+ [ "$status" -ne 0 ]
+}
+
+@test "exit with error code if command doesn't exist" {
+ run $SCRIPT somenonexistingcommand
+ [ "$status" -ne 0 ]
+}
+
+@test "exit with error code if no enough arg" {
+ run $SCRIPT import
+ [ "$status" -ne 0 ]
+}
+
+# -------------------------------------------------------------
+# Import
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+@test "can't import a file into itself" {
+ export ACME_ACCOUNTS=/dev/null
+ run $SCRIPT import /dev/null
+ [ "$output" = "You're trying to import /dev/null to itself" ]
+ [ "$status" -eq 2 ]
+}
+
+@test "can merge correctly two credentials files" {
+ ACME_ACCOUNTS=$(mktemp)
+ export ACME_ACCOUNTS
+ cp data/acmedns.json "$ACME_ACCOUNTS"
+
+ run $SCRIPT import data/acmedns-toimport.json
+ [ "$status" -eq 0 ]
+
+ isValid=0
+ run jsondiff "$ACME_ACCOUNTS" data/acmedns-merged.json
+ rm "$ACME_ACCOUNTS"
+ [ "$status" -eq 0 ]
+ [ "$output" = "{}" ] || isValid=1
+
+ if [ $isValid -ne 0 ]; then
+ echo "Non matching part according jsondiff:"
+ echo "$output"
+ fi
+
+ return $isValid
+}
diff --git a/_tests/scripts/python/test_edit_acme_dns_accounts.py b/_tests/scripts/python/test_edit_acme_dns_accounts.py
new file mode 100644
index 0000000..06e86c9
--- /dev/null
+++ b/_tests/scripts/python/test_edit_acme_dns_accounts.py
@@ -0,0 +1,48 @@
+from importlib.machinery import SourceFileLoader
+import os
+import unittest
+
+
+os.environ["ACME_ACCOUNTS"] = "/path/to/acmedns.json"
+
+path = "roles/paas-docker/letsencrypt/files/edit-acme-dns-accounts.py"
+script = SourceFileLoader('script', "../" + path).load_module()
+
+
+class TestInstance(unittest.TestCase):
+ def setUp(self):
+ self.testAccounts = script.AcmeAccounts("/dev/null")
+ pass
+
+ def test_read_path_from_environment(self):
+ self.assertEqual("/path/to/acmedns.json", script.get_acme_accounts_path())
+
+ def test_accounts_are_empty_on_init(self):
+ self.assertEqual({}, self.testAccounts.accounts)
+
+ def test_add(self):
+ self.testAccounts.add("foo.tld", {})
+ self.assertEqual(1, len(self.testAccounts.accounts))
+ self.assertIn("foo.tld", self.testAccounts.accounts)
+
+ def test_remove_existing(self):
+ self.testAccounts.add("foo.tld", {})
+ self.assertTrue(self.testAccounts.remove("foo.tld"))
+ self.assertEqual(0, len(self.testAccounts.accounts))
+
+ def test_remove_non_existing(self):
+ self.assertFalse(self.testAccounts.remove("not-existing.tld"))
+
+ def test_merge(self):
+ accounts_to_merge = script.AcmeAccounts("/dev/null") \
+ .add("bar.tld", {})
+
+ self.testAccounts \
+ .add("foo.tld", {}) \
+ .merge_with(accounts_to_merge)
+
+ self.assertEqual(2, len(self.testAccounts.accounts))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/requirements.txt b/requirements.txt
index db4ec67..82cf954 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,11 +1,12 @@
# Linters
flake8>=3.7.9,<4.0
autopep8>=1.4.4,<2.0
pycodestyle>=2.5.0,<3.0
# Tests and utilities
PyYAML>=4.2b1,<5.1
# Tests
mock>=2.0.0,<3.0
salt==2019.2.2
+jsondiff==1.2.0
diff --git a/roles/paas-docker/letsencrypt/files/edit-acme-dns-accounts.py b/roles/paas-docker/letsencrypt/files/edit-acme-dns-accounts.py
new file mode 100755
index 0000000..a456757
--- /dev/null
+++ b/roles/paas-docker/letsencrypt/files/edit-acme-dns-accounts.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# -------------------------------------------------------------
+# Let's encrypt — ACME DNS server accounts editor
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Project: Nasqueron
+# Created: 2020-02-22
+# Description: Edit /srv/letsencrypt/etc/acmedns.json to import
+# credentials for a specific subdomain to verify.
+# License: BSD-2-Clause
+# -------------------------------------------------------------
+
+
+import json
+import os
+import sys
+
+
+def get_acme_accounts_path():
+ try:
+ return os.environ["ACME_ACCOUNTS"]
+ except KeyError:
+ return "/srv/letsencrypt/etc/acmedns.json"
+
+
+ACME_ACCOUNTS_PATH = get_acme_accounts_path()
+
+
+class AcmeAccounts:
+ def __init__(self, path):
+ self.path = path
+ self.accounts = {}
+
+ def read_from_file(self):
+ with open(self.path) as fd:
+ self.accounts = json.load(fd)
+
+ return self
+
+ def write_to_file(self):
+ with open(self.path, "w") as fd:
+ json.dump(self.accounts, fd)
+
+ return self
+
+ def add(self, domain, account_parameters):
+ self.accounts[domain] = account_parameters
+
+ return self
+
+ def remove(self, domain):
+ try:
+ del self.accounts[domain]
+ return True
+ except KeyError:
+ return False
+
+ def merge_with(self, other_accounts: 'AcmeAccounts'):
+ self.accounts.update(other_accounts.accounts)
+
+ return self
+
+
+def usage():
+ print(f"Usage: {sys.argv[0]} <command> [parameters]", file=sys.stderr)
+ exit(1)
+
+
+def import_other_file(file_to_import):
+ if file_to_import == ACME_ACCOUNTS_PATH:
+ print(f"You're trying to import {ACME_ACCOUNTS_PATH} to itself")
+ exit(2)
+
+ accounts_to_import = AcmeAccounts(file_to_import).read_from_file()
+
+ AcmeAccounts(ACME_ACCOUNTS_PATH)\
+ .read_from_file()\
+ .merge_with(accounts_to_import)\
+ .write_to_file()
+
+
+commands = {
+ "import": {
+ "required_argc": 3,
+ "command_usage": "import <file>",
+ "callable": import_other_file
+ },
+},
+
+
+if __name__ == "__main__":
+ argc = len(sys.argv)
+
+ if argc < 2 or sys.argv[1] in ["-h", "--help", "/?", "/help"]:
+ usage()
+
+ command = sys.argv[1]
+
+ if command not in commands:
+ print(f"Unknown command: {command}", file=sys.stderr)
+ usage()
+
+ command = commands[command]
+
+ if argc < command["required_argc"]:
+ print(f"Usage: {sys.argv[0]} {command['command_usage']}", file=sys.stderr)
+ exit(1)
+
+ # We're good, time to invoke our command
+ command["callable"](*sys.argv[2:])
diff --git a/roles/paas-docker/letsencrypt/init.sls b/roles/paas-docker/letsencrypt/init.sls
index 85c0593..ac36ebd 100644
--- a/roles/paas-docker/letsencrypt/init.sls
+++ b/roles/paas-docker/letsencrypt/init.sls
@@ -1,50 +1,55 @@
# -------------------------------------------------------------
# Salt — Provision Docker engine
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Project: Nasqueron
# Created: 2018-03-16
# License: Trivial work, not eligible to copyright
# -------------------------------------------------------------
{% set has_selinux = salt['grains.get']('selinux:enabled', False) %}
# -------------------------------------------------------------
# See also
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Wrapper script
# - wrappers/init.sls
# - wrappers/files/certbot.sh
#
# Image
# - /pillar/paas/docker.sls
#
# Nginx configuration
# - nginx/files/includes/letsencrypt
# -------------------------------------------------------------
# Data directory
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/srv/letsencrypt:
file.directory
{% if has_selinux %}
selinux_context_letsencrypt_home:
selinux.fcontext_policy_present:
- name: /srv/letsencrypt
- sel_type: container_file_t
selinux_context_letsencrypt_home_applied:
selinux.fcontext_policy_applied:
- name: /srv/letsencrypt
{% endif %}
# -------------------------------------------------------------
# Plug-ins
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/srv/letsencrypt/etc/acme-dns-auth:
file.managed:
- source: salt://roles/paas-docker/letsencrypt/files/acme-dns-auth.py
- mode: 755
+
+/usr/local/bin/edit-acme-dns-accounts:
+ file.managed:
+ - source: salt://roles/paas-docker/letsencrypt/files/edit-acme-dns-accounts.py
+ - mode: 755
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Nov 24, 17:27 (18 m, 56 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2258531
Default Alt Text
(11 KB)
Attached To
Mode
rOPS Nasqueron Operations
Attached
Detach File
Event Timeline
Log In to Comment