From a4ff484ef8f05657491ec11e5e0a692e64893d1b Mon Sep 17 00:00:00 2001 From: bom Date: Fri, 18 Oct 2024 16:11:17 +0200 Subject: [PATCH 01/18] Add credential-rotation skeleton --- src/dda/backup/cred_rot.clj | 22 ++++++++++++++++++++++ src/dda/backup/cred_rot/domain.clj | 18 ++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 src/dda/backup/cred_rot.clj create mode 100644 src/dda/backup/cred_rot/domain.clj diff --git a/src/dda/backup/cred_rot.clj b/src/dda/backup/cred_rot.clj new file mode 100644 index 0000000..761def0 --- /dev/null +++ b/src/dda/backup/cred_rot.clj @@ -0,0 +1,22 @@ +(ns dda.backup.cred-rot + (:require + [orchestra.core :refer [defn-spec]] + [clojure.spec.alpha :as s] + [dda.backup.cred-rot.domain :as domain])) + +(s/def ::new-password-file string?) + +(s/def ::cred-rot + (s/keys :req-un [] + :opt-un [::new-password-file])) + +; Refer to "docs/CredentialRotation.md" for specifics + +(defn-spec maybe-add-new! nil? + [config ::cred-rot] + (when-let [{:keys [new-password-file]} config] + (domain/add-new-password! new-password-file))) + +(defn-spec replace-old-password! nil? + [] + ) diff --git a/src/dda/backup/cred_rot/domain.clj b/src/dda/backup/cred_rot/domain.clj new file mode 100644 index 0000000..f05ce35 --- /dev/null +++ b/src/dda/backup/cred_rot/domain.clj @@ -0,0 +1,18 @@ +(ns dda.backup.cred-rot.domain + (:require + [orchestra.core :refer [defn-spec]] + [clojure.spec.alpha :as s])) + +(s/def ::new-password-file string?) + +; Refer to "docs/CredentialRotation.md" for specifics + +(defn-spec add-new-password! nil? + [new-password-file ::new-password-file] + ; TODO +) + +(defn-spec replace-old-password! nil? + [] + ; TODO +) \ No newline at end of file From b96f51220d0cbd6b10b4790c34f54e0be836fb7d Mon Sep 17 00:00:00 2001 From: bom Date: Fri, 25 Oct 2024 12:08:51 +0200 Subject: [PATCH 02/18] Add specs for reading passwords --- deps.edn | 4 ++- src/dda/backup/cred_rot/domain.clj | 30 +++++++++++++++++++-- test/dda/backup/cred_rot/domain_test.clj | 33 ++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 test/dda/backup/cred_rot/domain_test.clj diff --git a/deps.edn b/deps.edn index 5833359..415ad59 100644 --- a/deps.edn +++ b/deps.edn @@ -11,7 +11,9 @@ {;; Application org.clojure/clojure {:mvn/version "1.11.4"} org.clojure/spec.alpha {:mvn/version "0.5.238"} - orchestra/orchestra {:mvn/version "2021.01.01-1"}} + orchestra/orchestra {:mvn/version "2021.01.01-1"} + cheshire/cheshire {:mvn/version "5.13.0"} + com.widdindustries/cljc.java-time {:mvn/version "0.1.21"}} ;; --------------------------------------------------------- ;; --------------------------------------------------------- diff --git a/src/dda/backup/cred_rot/domain.clj b/src/dda/backup/cred_rot/domain.clj index f05ce35..abe2e1b 100644 --- a/src/dda/backup/cred_rot/domain.clj +++ b/src/dda/backup/cred_rot/domain.clj @@ -1,11 +1,37 @@ (ns dda.backup.cred-rot.domain (:require [orchestra.core :refer [defn-spec]] - [clojure.spec.alpha :as s])) + [clojure.spec.alpha :as s] + [clojure.string :as str] + [cljc.java-time.local-date :as ld] + [cljc.java-time.format.date-time-formatter :as df])) (s/def ::new-password-file string?) -; Refer to "docs/CredentialRotation.md" for specifics +(def lowercase-numeric #"[a-z0-9]+") +(def alphanumeric #"[a-zA-Z0-9]+") + +; true | false +(s/def ::current boolean?) +; 521e0760 +(s/def ::id (s/and string? #(re-matches lowercase-numeric %))) +; root +(s/def ::userName #(re-matches alphanumeric %)) +; backup-restore-65bd9b6ff5-z69sn +(s/def ::hostName (fn [in] (every? #(re-matches lowercase-numeric %) (str/split in #"-")))) +; "2024-10-18 13:08:16" +(def timestamp-formatter (df/of-pattern "yyyy-MM-dd HH:mm:ss")) +(s/def ::created #(try + (ld/parse % timestamp-formatter) + true + (catch Exception _ false))) + +(s/def ::entry (s/keys :opt-un [] + :req-un [::current ::id ::userName ::hostName ::created])) +(s/def ::response (s/coll-of ::entry)) + + +; Refer to "docs/CredentialRotation.md" for specifics (defn-spec add-new-password! nil? [new-password-file ::new-password-file] diff --git a/test/dda/backup/cred_rot/domain_test.clj b/test/dda/backup/cred_rot/domain_test.clj new file mode 100644 index 0000000..95042bc --- /dev/null +++ b/test/dda/backup/cred_rot/domain_test.clj @@ -0,0 +1,33 @@ +(ns dda.backup.cred-rot.domain-test + (:require + [clojure.test :refer [deftest is]] + [clojure.spec.alpha :as s] + [clojure.spec.test.alpha :as st] + [dda.backup.cred-rot.domain :as cut])) + +(deftest test-spec-id + (is (s/valid? ::cut/id "521e0760")) + (is (s/valid? ::cut/id "test")) + (is (s/valid? ::cut/id "123456")) + (is (not (s/valid? ::cut/id "ROOT"))) + (is (not (s/valid? ::cut/id "Test!")))) + +(deftest test-spec-username + (is (s/valid? ::cut/userName "521e0760")) + (is (s/valid? ::cut/userName "Testuser")) + (is (s/valid? ::cut/userName "root")) + (is (s/valid? ::cut/userName "ROOT")) + (is (not (s/valid? ::cut/userName "test-user")))) + +(deftest test-spec-hostName + (let [valid #(s/valid? ::cut/hostName %)] + (is (valid "test-some-combination-2")) + (is (valid "backup-restore-65bd9b6ff5-z69sn")))) + +(deftest test-spec-created + (let [valid #(s/valid? ::cut/created %)] + (is (valid "2024-10-18 13:08:16")) + (is (valid "2032-09-01 12:56:59")) + (is (not (valid "2024-13-5 13:08:16"))) + (is (not (valid "2024-6-42 13:08:16"))) + (is (not (valid "test"))))) \ No newline at end of file From 21dee3cfc0f46ed2a394839a6ac67b1c2c7e532d Mon Sep 17 00:00:00 2001 From: bom Date: Fri, 25 Oct 2024 13:13:28 +0200 Subject: [PATCH 03/18] Add config spec for cred_rot --- src/dda/backup/cred_rot/domain.clj | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/dda/backup/cred_rot/domain.clj b/src/dda/backup/cred_rot/domain.clj index abe2e1b..1a1cec1 100644 --- a/src/dda/backup/cred_rot/domain.clj +++ b/src/dda/backup/cred_rot/domain.clj @@ -3,14 +3,19 @@ [orchestra.core :refer [defn-spec]] [clojure.spec.alpha :as s] [clojure.string :as str] + [dda.backup.core.domain :as cd] + [dda.backup.restic.domain :as rd] [cljc.java-time.local-date :as ld] [cljc.java-time.format.date-time-formatter :as df])) (s/def ::new-password-file string?) +(s/def ::config (s/keys :req-un [::rd/restic-repository + ::rd/password-file] + :opt-un [::rd/certificate-file + ::new-password-file])) (def lowercase-numeric #"[a-z0-9]+") (def alphanumeric #"[a-zA-Z0-9]+") - ; true | false (s/def ::current boolean?) ; 521e0760 From 5c583fd72f0ebbc48aa53ef2e529888a43933235 Mon Sep 17 00:00:00 2001 From: bom Date: Fri, 25 Oct 2024 13:19:59 +0200 Subject: [PATCH 04/18] Add list passwords command --- src/dda/backup/cred_rot/domain.clj | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/dda/backup/cred_rot/domain.clj b/src/dda/backup/cred_rot/domain.clj index 1a1cec1..b8e846e 100644 --- a/src/dda/backup/cred_rot/domain.clj +++ b/src/dda/backup/cred_rot/domain.clj @@ -38,6 +38,21 @@ ; Refer to "docs/CredentialRotation.md" for specifics +(defn-spec list-passwords-command ::cd/command + [config ::config] + (let [{:keys [restic-repository password-file + certificate-file new-password-file]} config] + (into + [] + (concat ["restic" "-r" restic-repository "key" "list" "--json"] + (cond + (some? certificate-file) + ["--cacert" certificate-file] + (some? password-file) + ["--password-file" password-file] + :else + []))))) + (defn-spec add-new-password! nil? [new-password-file ::new-password-file] ; TODO From 433017308409f1b8c0189c029185b52e23f23c78 Mon Sep 17 00:00:00 2001 From: bom Date: Fri, 15 Nov 2024 13:10:38 +0100 Subject: [PATCH 05/18] Implement cred_rot commands --- src/dda/backup/cred_rot/domain.clj | 33 ++++++++++++++++++------------ 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/dda/backup/cred_rot/domain.clj b/src/dda/backup/cred_rot/domain.clj index b8e846e..631d176 100644 --- a/src/dda/backup/cred_rot/domain.clj +++ b/src/dda/backup/cred_rot/domain.clj @@ -38,27 +38,34 @@ ; Refer to "docs/CredentialRotation.md" for specifics -(defn-spec list-passwords-command ::cd/command - [config ::config] +(defn-spec base-command ::cd/command + [config ::config + command ::cd/command] (let [{:keys [restic-repository password-file certificate-file new-password-file]} config] - (into + (into [] - (concat ["restic" "-r" restic-repository "key" "list" "--json"] + (concat ["restic" "-r" restic-repository] (cond (some? certificate-file) ["--cacert" certificate-file] (some? password-file) ["--password-file" password-file] :else - []))))) + []) + command)))) -(defn-spec add-new-password! nil? - [new-password-file ::new-password-file] - ; TODO -) +(defn-spec list-passwords-command ::cd/command + [config ::config] + (base-command config ["key" "list" "--json"])) -(defn-spec replace-old-password! nil? - [] - ; TODO -) \ No newline at end of file +(defn-spec add-password-command ::cd/command + [config ::config] + (let [{:keys [new-password-file]} config] + (base-command config ["key" "add" "--new-password-file" new-password-file]))) + +(defn-spec remove-password-command ::cd/command + [config ::config + new-id ::id + old-id ::id] + (base-command config ["key" "remove" "--key-hint" new-id old-id])) From f4a4a30f2439edc700a35d9cd6db0bbf29b97c8c Mon Sep 17 00:00:00 2001 From: bom Date: Fri, 22 Nov 2024 12:44:27 +0100 Subject: [PATCH 06/18] Add ability to add and list passwords --- .../backup/image/resources/init-bb.bb | 41 ++++++++++++++++++- .../backup/image/resources/install.sh | 2 +- src/dda/backup/cred_rot.clj | 12 ++++-- src/dda/backup/cred_rot/domain.clj | 6 +-- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/infrastructure/backup/image/resources/init-bb.bb b/infrastructure/backup/image/resources/init-bb.bb index af0856c..aa09429 100755 --- a/infrastructure/backup/image/resources/init-bb.bb +++ b/infrastructure/backup/image/resources/init-bb.bb @@ -1,3 +1,42 @@ #!/usr/bin/env bb -(println "initialized") +(require '[babashka.tasks :as tasks] + '[dda.backup.cred-rot :as cr] + '[dda.backup.restic :as rc] + '[dda.backup.postgresql :as pg]) + +(def restic-repo {:password-file "restic-pwd" + :restic-repository "restic-repo"}) + +(def file-config (merge restic-repo {:backup-path "files" + :files ["test-backup"] + :restore-target-directory "test-restore"})) + +(def db-config (merge restic-repo {:backup-path "db" + :pg-db "mydb" + :pg-user "user" + :pg-password "password"})) + +(def cred-config (merge restic-repo {:new-password-file "new-pw-file"})) + +(def dry-run {:dry-run true :debug true}) + +(defn prepare! + [] + (spit "/tmp/file_password" "file-password") + (spit "restic-pwd" "ThePassword") + (tasks/shell "mkdir" "-p" "test-backup") + (spit "test-backup/file" "I was here") + (spit "new-pw-file" "newpassword") + (tasks/shell "mkdir" "-p" "test-restore") + (pg/create-pg-pass! db-config)) + +(defn restic-repo-init! + [] + (rc/init! restic-repo)) + +(prepare!) +(restic-repo-init!) +(cr/list-passwords! cred-config) +(cr/maybe-add-new! cred-config) +(cr/list-passwords! cred-config) \ No newline at end of file diff --git a/infrastructure/backup/image/resources/install.sh b/infrastructure/backup/image/resources/install.sh index 559ad1a..d31a042 100755 --- a/infrastructure/backup/image/resources/install.sh +++ b/infrastructure/backup/image/resources/install.sh @@ -18,12 +18,12 @@ function main() { apt-get install -qqy ca-certificates curl gnupg postgresql-client-16 restic openjdk-21-jre-headless nano curl -Ss --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/trusted.gpg.d/postgresql-common_pgdg_archive_keyring.gpg sh -c 'echo "deb [signed-by=/etc/apt/trusted.gpg.d/postgresql-common_pgdg_archive_keyring.gpg] https://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list' - upgradeSystem babashka_install } > /dev/null update-ca-certificates install -m 0700 -o root -g root /tmp/init-bb.bb /usr/local/bin/ + install -m 0600 -o root -g root /tmp/bb.edn /usr/local/bin/ cleanupDocker } diff --git a/src/dda/backup/cred_rot.clj b/src/dda/backup/cred_rot.clj index 761def0..8f6e6f5 100644 --- a/src/dda/backup/cred_rot.clj +++ b/src/dda/backup/cred_rot.clj @@ -2,7 +2,8 @@ (:require [orchestra.core :refer [defn-spec]] [clojure.spec.alpha :as s] - [dda.backup.cred-rot.domain :as domain])) + [dda.backup.cred-rot.domain :as domain] + [dda.backup.infrastructure :as i])) (s/def ::new-password-file string?) @@ -14,8 +15,13 @@ (defn-spec maybe-add-new! nil? [config ::cred-rot] - (when-let [{:keys [new-password-file]} config] - (domain/add-new-password! new-password-file))) + (let [{:keys [new-password-file]} config] + (if (not (nil? new-password-file)) + (i/execute! (domain/add-password-command config) config)))) + +(defn-spec list-passwords! nil? + [config ::cred-rot] + (i/execute! (domain/list-passwords-command config) config)) (defn-spec replace-old-password! nil? [] diff --git a/src/dda/backup/cred_rot/domain.clj b/src/dda/backup/cred_rot/domain.clj index 631d176..1d92599 100644 --- a/src/dda/backup/cred_rot/domain.clj +++ b/src/dda/backup/cred_rot/domain.clj @@ -57,15 +57,15 @@ (defn-spec list-passwords-command ::cd/command [config ::config] - (base-command config ["key" "list" "--json"])) + [(base-command config ["key" "list" "--json"])]) (defn-spec add-password-command ::cd/command [config ::config] (let [{:keys [new-password-file]} config] - (base-command config ["key" "add" "--new-password-file" new-password-file]))) + [(base-command config ["key" "add" "--new-password-file" new-password-file])])) (defn-spec remove-password-command ::cd/command [config ::config new-id ::id old-id ::id] - (base-command config ["key" "remove" "--key-hint" new-id old-id])) + [(base-command config ["key" "remove" "--key-hint" new-id old-id])]) From 9441cfa773ff8162d107757f5993c1769809eec2 Mon Sep 17 00:00:00 2001 From: bom Date: Fri, 22 Nov 2024 13:47:47 +0100 Subject: [PATCH 07/18] Add function to capture output in infra --- src/dda/backup/infrastructure.clj | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/dda/backup/infrastructure.clj b/src/dda/backup/infrastructure.clj index 9a92d9c..a0b8b89 100644 --- a/src/dda/backup/infrastructure.clj +++ b/src/dda/backup/infrastructure.clj @@ -12,3 +12,12 @@ (println c)) (when-not dry-run (apply t/shell c))))) + +(defn-spec execute-out! string? + [command ::core/command + config ::core/execution] + (let [{:keys [dry-run debug]} config] + (when debug + (println command)) + (when-not dry-run + (:out (t/shell {:out :string} (clojure.string/join " " command)))))) \ No newline at end of file From bd7ea0ccba89965ce97b14cbd3003ec5f8ced302 Mon Sep 17 00:00:00 2001 From: bom Date: Fri, 22 Nov 2024 13:48:58 +0100 Subject: [PATCH 08/18] Parse list pw json response --- src/dda/backup/cred_rot.clj | 11 ++++++++--- src/dda/backup/cred_rot/domain.clj | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/dda/backup/cred_rot.clj b/src/dda/backup/cred_rot.clj index 8f6e6f5..8213bd6 100644 --- a/src/dda/backup/cred_rot.clj +++ b/src/dda/backup/cred_rot.clj @@ -3,7 +3,8 @@ [orchestra.core :refer [defn-spec]] [clojure.spec.alpha :as s] [dda.backup.cred-rot.domain :as domain] - [dda.backup.infrastructure :as i])) + [dda.backup.infrastructure :as i] + [cheshire.core :as cc])) (s/def ::new-password-file string?) @@ -19,9 +20,13 @@ (if (not (nil? new-password-file)) (i/execute! (domain/add-password-command config) config)))) -(defn-spec list-passwords! nil? +(defn-spec list-passwords! map? [config ::cred-rot] - (i/execute! (domain/list-passwords-command config) config)) + (i/execute-out! (domain/list-passwords-command config) config)) + +(defn-spec parse-passwords! nil? + [config ::cred-rot] + (println (cc/parse-string (list-passwords! config)))) (defn-spec replace-old-password! nil? [] diff --git a/src/dda/backup/cred_rot/domain.clj b/src/dda/backup/cred_rot/domain.clj index 1d92599..462b127 100644 --- a/src/dda/backup/cred_rot/domain.clj +++ b/src/dda/backup/cred_rot/domain.clj @@ -57,7 +57,7 @@ (defn-spec list-passwords-command ::cd/command [config ::config] - [(base-command config ["key" "list" "--json"])]) + (base-command config ["key" "list" "--json"])) (defn-spec add-password-command ::cd/command [config ::config] From b60bf5bb8730c639a2a8839ae6d758005744d852 Mon Sep 17 00:00:00 2001 From: bom Date: Fri, 29 Nov 2024 11:17:32 +0100 Subject: [PATCH 09/18] mob: update parsing --- src/dda/backup/cred_rot.clj | 23 ++++++++++------------- src/dda/backup/cred_rot/domain.clj | 15 +++++++++++---- test/dda/backup/cred_rot/domain_test.clj | 22 ++++++++++++++++++++-- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/dda/backup/cred_rot.clj b/src/dda/backup/cred_rot.clj index 8213bd6..738b938 100644 --- a/src/dda/backup/cred_rot.clj +++ b/src/dda/backup/cred_rot.clj @@ -3,30 +3,27 @@ [orchestra.core :refer [defn-spec]] [clojure.spec.alpha :as s] [dda.backup.cred-rot.domain :as domain] - [dda.backup.infrastructure :as i] - [cheshire.core :as cc])) + [dda.backup.infrastructure :as i])) +(s/def ::valid-from domain/timestamp?) (s/def ::new-password-file string?) +(s/def ::new-password-config (s/keys :req-un [::new-password-file ::valid-from])) (s/def ::cred-rot (s/keys :req-un [] - :opt-un [::new-password-file])) + :opt-un [::new-password-config])) ; Refer to "docs/CredentialRotation.md" for specifics - -(defn-spec maybe-add-new! nil? - [config ::cred-rot] - (let [{:keys [new-password-file]} config] - (if (not (nil? new-password-file)) - (i/execute! (domain/add-password-command config) config)))) - -(defn-spec list-passwords! map? +(defn-spec list-passwords! string? [config ::cred-rot] (i/execute-out! (domain/list-passwords-command config) config)) -(defn-spec parse-passwords! nil? +(defn-spec maybe-add-new! nil? [config ::cred-rot] - (println (cc/parse-string (list-passwords! config)))) + (let [{:keys [new-password-file valid-from]} (:new-password-config config)] + (if (not (nil? new-password-file)) + (let [parsed-passwords (parsed-passwords! config)] + (i/execute! (domain/add-password-command config) config))))) (defn-spec replace-old-password! nil? [] diff --git a/src/dda/backup/cred_rot/domain.clj b/src/dda/backup/cred_rot/domain.clj index 462b127..5bbe2e0 100644 --- a/src/dda/backup/cred_rot/domain.clj +++ b/src/dda/backup/cred_rot/domain.clj @@ -5,6 +5,7 @@ [clojure.string :as str] [dda.backup.core.domain :as cd] [dda.backup.restic.domain :as rd] + [cheshire.core :as cc] [cljc.java-time.local-date :as ld] [cljc.java-time.format.date-time-formatter :as df])) @@ -26,10 +27,12 @@ (s/def ::hostName (fn [in] (every? #(re-matches lowercase-numeric %) (str/split in #"-")))) ; "2024-10-18 13:08:16" (def timestamp-formatter (df/of-pattern "yyyy-MM-dd HH:mm:ss")) -(s/def ::created #(try - (ld/parse % timestamp-formatter) - true - (catch Exception _ false))) +(defn timestamp? [in] + (try + (ld/parse in timestamp-formatter) + true + (catch Exception _ false))) +(s/def ::created timestamp?) (s/def ::entry (s/keys :opt-un [] :req-un [::current ::id ::userName ::hostName ::created])) @@ -69,3 +72,7 @@ new-id ::id old-id ::id] [(base-command config ["key" "remove" "--key-hint" new-id old-id])]) + +(defn-spec parse-response ::response + [response string?] + (s/conform )(cc/parse-string response #(keyword %))) diff --git a/test/dda/backup/cred_rot/domain_test.clj b/test/dda/backup/cred_rot/domain_test.clj index 95042bc..fc58321 100644 --- a/test/dda/backup/cred_rot/domain_test.clj +++ b/test/dda/backup/cred_rot/domain_test.clj @@ -5,6 +5,24 @@ [clojure.spec.test.alpha :as st] [dda.backup.cred-rot.domain :as cut])) +(deftest test-parse-response + (is (= [] (cut/parse-response "[ + { + \"current\": false, + \"id\": \"521e0760\", + \"userName\": \"root\", + \"hostName\": \"backup-restore-65bd9b6ff5-z69sn\", + \"created\": \"2024-10-18 13:08:16\" + }, + { + \"current\": true, + \"id\": \"b67161fb\", + \"userName\": \"root\", + \"hostName\": \"backup-restore-65bd9b6ff5-z69sn\", + \"created\": \"2024-10-18 13:16:54\" + } +]")))) + (deftest test-spec-id (is (s/valid? ::cut/id "521e0760")) (is (s/valid? ::cut/id "test")) @@ -24,8 +42,8 @@ (is (valid "test-some-combination-2")) (is (valid "backup-restore-65bd9b6ff5-z69sn")))) -(deftest test-spec-created - (let [valid #(s/valid? ::cut/created %)] +(deftest test-spec-timestamp + (let [valid #(s/valid? cut/timestamp? %)] (is (valid "2024-10-18 13:08:16")) (is (valid "2032-09-01 12:56:59")) (is (not (valid "2024-13-5 13:08:16"))) From e07d0021b29730e8cd9c22e4b057eeb886599d82 Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Fri, 29 Nov 2024 15:14:01 +0100 Subject: [PATCH 10/18] speed up the roundtrip time --- infrastructure/backup/image/Dockerfile | 2 + .../backup/image/resources/init-bb.bb | 41 +---------------- .../backup/image/resources2/test-me.bb | 44 +++++++++++++++++++ src/dda/backup/cred_rot.clj | 7 ++- src/dda/backup/cred_rot/domain.clj | 2 +- test/dda/backup/cred_rot/domain_test.clj | 15 ++++++- 6 files changed, 66 insertions(+), 45 deletions(-) create mode 100755 infrastructure/backup/image/resources2/test-me.bb diff --git a/infrastructure/backup/image/Dockerfile b/infrastructure/backup/image/Dockerfile index 24e9b47..37aa6df 100644 --- a/infrastructure/backup/image/Dockerfile +++ b/infrastructure/backup/image/Dockerfile @@ -5,3 +5,5 @@ ADD resources /tmp/ RUN /tmp/install.sh ADD local/ /usr/local/lib/dda-backup RUN init-bb.bb +ADD resources2 /tmp/ +RUN install -m 0700 -o root -g root /tmp/test-me.bb /usr/local/bin/ diff --git a/infrastructure/backup/image/resources/init-bb.bb b/infrastructure/backup/image/resources/init-bb.bb index aa09429..af0856c 100755 --- a/infrastructure/backup/image/resources/init-bb.bb +++ b/infrastructure/backup/image/resources/init-bb.bb @@ -1,42 +1,3 @@ #!/usr/bin/env bb -(require '[babashka.tasks :as tasks] - '[dda.backup.cred-rot :as cr] - '[dda.backup.restic :as rc] - '[dda.backup.postgresql :as pg]) - -(def restic-repo {:password-file "restic-pwd" - :restic-repository "restic-repo"}) - -(def file-config (merge restic-repo {:backup-path "files" - :files ["test-backup"] - :restore-target-directory "test-restore"})) - -(def db-config (merge restic-repo {:backup-path "db" - :pg-db "mydb" - :pg-user "user" - :pg-password "password"})) - -(def cred-config (merge restic-repo {:new-password-file "new-pw-file"})) - -(def dry-run {:dry-run true :debug true}) - -(defn prepare! - [] - (spit "/tmp/file_password" "file-password") - (spit "restic-pwd" "ThePassword") - (tasks/shell "mkdir" "-p" "test-backup") - (spit "test-backup/file" "I was here") - (spit "new-pw-file" "newpassword") - (tasks/shell "mkdir" "-p" "test-restore") - (pg/create-pg-pass! db-config)) - -(defn restic-repo-init! - [] - (rc/init! restic-repo)) - -(prepare!) -(restic-repo-init!) -(cr/list-passwords! cred-config) -(cr/maybe-add-new! cred-config) -(cr/list-passwords! cred-config) \ No newline at end of file +(println "initialized") diff --git a/infrastructure/backup/image/resources2/test-me.bb b/infrastructure/backup/image/resources2/test-me.bb new file mode 100755 index 0000000..c00a43a --- /dev/null +++ b/infrastructure/backup/image/resources2/test-me.bb @@ -0,0 +1,44 @@ +#!/usr/bin/env bb + +(println "initialized") + +(require '[babashka.tasks :as tasks] + '[dda.backup.cred-rot :as cr] + '[dda.backup.restic :as rc] + '[dda.backup.postgresql :as pg]) + +(def restic-repo {:password-file "restic-pwd" + :restic-repository "restic-repo"}) + +(def file-config (merge restic-repo {:backup-path "files" + :files ["test-backup"] + :restore-target-directory "test-restore"})) + +(def db-config (merge restic-repo {:backup-path "db" + :pg-db "mydb" + :pg-user "user" + :pg-password "password"})) + +(def cred-config (merge restic-repo {:new-password-file "new-pw-file"})) + +(def dry-run {:dry-run true :debug true}) + +(defn prepare! + [] + (spit "/tmp/file_password" "file-password") + (spit "restic-pwd" "ThePassword") + (tasks/shell "mkdir" "-p" "test-backup") + (spit "test-backup/file" "I was here") + (spit "new-pw-file" "newpassword") + (tasks/shell "mkdir" "-p" "test-restore") + (pg/create-pg-pass! db-config)) + +(defn restic-repo-init! + [] + (rc/init! restic-repo)) + +(prepare!) +(restic-repo-init!) +(cr/list-passwords! cred-config) +(cr/maybe-add-new! cred-config) +(cr/list-passwords! cred-config) diff --git a/src/dda/backup/cred_rot.clj b/src/dda/backup/cred_rot.clj index 738b938..1b64370 100644 --- a/src/dda/backup/cred_rot.clj +++ b/src/dda/backup/cred_rot.clj @@ -22,8 +22,11 @@ [config ::cred-rot] (let [{:keys [new-password-file valid-from]} (:new-password-config config)] (if (not (nil? new-password-file)) - (let [parsed-passwords (parsed-passwords! config)] - (i/execute! (domain/add-password-command config) config))))) + (let [parsed-passwords (domain/parse-response (list-passwords! config))] + (when (> 0 (compare + (:created (last parsed-passwords)) + valid-from)) + (i/execute! (domain/add-password-command config) config)))))) (defn-spec replace-old-password! nil? [] diff --git a/src/dda/backup/cred_rot/domain.clj b/src/dda/backup/cred_rot/domain.clj index 5bbe2e0..c1b6b64 100644 --- a/src/dda/backup/cred_rot/domain.clj +++ b/src/dda/backup/cred_rot/domain.clj @@ -75,4 +75,4 @@ (defn-spec parse-response ::response [response string?] - (s/conform )(cc/parse-string response #(keyword %))) + (sort-by :created (cc/parse-string response #(keyword %)))) diff --git a/test/dda/backup/cred_rot/domain_test.clj b/test/dda/backup/cred_rot/domain_test.clj index fc58321..c1b1aeb 100644 --- a/test/dda/backup/cred_rot/domain_test.clj +++ b/test/dda/backup/cred_rot/domain_test.clj @@ -6,13 +6,24 @@ [dda.backup.cred-rot.domain :as cut])) (deftest test-parse-response - (is (= [] (cut/parse-response "[ + (is (= + [{:current true, + :id "b67161fb", + :userName "root", + :hostName "backup-restore-65bd9b6ff5-z69sn", + :created "2024-10-18 13:16:54"} + {:current false, + :id "521e0760", + :userName "root", + :hostName "backup-restore-65bd9b6ff5-z69sn", + :created "2024-11-18 13:08:16"}] + (cut/parse-response "[ { \"current\": false, \"id\": \"521e0760\", \"userName\": \"root\", \"hostName\": \"backup-restore-65bd9b6ff5-z69sn\", - \"created\": \"2024-10-18 13:08:16\" + \"created\": \"2024-11-18 13:08:16\" }, { \"current\": true, From 747755eb2c279a4ae540259c79cec021047842d6 Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Wed, 11 Dec 2024 10:43:44 +0100 Subject: [PATCH 11/18] change pw now works --- infrastructure/backup/image/Dockerfile | 3 +- .../image/resources2/{test-me.bb => test.bb} | 23 +--- src/dda/backup/cred_rot.clj | 72 +++++++++-- src/dda/backup/cred_rot/domain.clj | 51 +++++++- test/dda/backup/cred_rot/domain_test.clj | 122 +++++++++++++----- 5 files changed, 207 insertions(+), 64 deletions(-) rename infrastructure/backup/image/resources2/{test-me.bb => test.bb} (53%) diff --git a/infrastructure/backup/image/Dockerfile b/infrastructure/backup/image/Dockerfile index 37aa6df..bb0759a 100644 --- a/infrastructure/backup/image/Dockerfile +++ b/infrastructure/backup/image/Dockerfile @@ -6,4 +6,5 @@ RUN /tmp/install.sh ADD local/ /usr/local/lib/dda-backup RUN init-bb.bb ADD resources2 /tmp/ -RUN install -m 0700 -o root -g root /tmp/test-me.bb /usr/local/bin/ +RUN install -m 0700 -o root -g root /tmp/test.bb /usr/local/bin/ +RUN test.bb diff --git a/infrastructure/backup/image/resources2/test-me.bb b/infrastructure/backup/image/resources2/test.bb similarity index 53% rename from infrastructure/backup/image/resources2/test-me.bb rename to infrastructure/backup/image/resources2/test.bb index c00a43a..7f2eecc 100755 --- a/infrastructure/backup/image/resources2/test-me.bb +++ b/infrastructure/backup/image/resources2/test.bb @@ -1,7 +1,5 @@ #!/usr/bin/env bb -(println "initialized") - (require '[babashka.tasks :as tasks] '[dda.backup.cred-rot :as cr] '[dda.backup.restic :as rc] @@ -14,24 +12,19 @@ :files ["test-backup"] :restore-target-directory "test-restore"})) -(def db-config (merge restic-repo {:backup-path "db" - :pg-db "mydb" - :pg-user "user" - :pg-password "password"})) - -(def cred-config (merge restic-repo {:new-password-file "new-pw-file"})) +(def cred-config (merge restic-repo + {:new-password-config {:new-password-file "new-pw" + :valid-from "2024-12-12 00:00:00"}})) (def dry-run {:dry-run true :debug true}) (defn prepare! [] - (spit "/tmp/file_password" "file-password") - (spit "restic-pwd" "ThePassword") + (spit "restic-pwd" "thePassword") + (spit "new-pw" "newPassword") (tasks/shell "mkdir" "-p" "test-backup") (spit "test-backup/file" "I was here") - (spit "new-pw-file" "newpassword") - (tasks/shell "mkdir" "-p" "test-restore") - (pg/create-pg-pass! db-config)) + (tasks/shell "mkdir" "-p" "test-restore")) (defn restic-repo-init! [] @@ -39,6 +32,4 @@ (prepare!) (restic-repo-init!) -(cr/list-passwords! cred-config) -(cr/maybe-add-new! cred-config) -(cr/list-passwords! cred-config) +(cr/change-password! cred-config) diff --git a/src/dda/backup/cred_rot.clj b/src/dda/backup/cred_rot.clj index 1b64370..4d8cce0 100644 --- a/src/dda/backup/cred_rot.clj +++ b/src/dda/backup/cred_rot.clj @@ -3,11 +3,13 @@ [orchestra.core :refer [defn-spec]] [clojure.spec.alpha :as s] [dda.backup.cred-rot.domain :as domain] - [dda.backup.infrastructure :as i])) + [dda.backup.infrastructure :as i] + [cljc.java-time.instant :as inst] + [cljc.java-time.local-date :as ld])) -(s/def ::valid-from domain/timestamp?) +(s/def ::replace-until domain/timestamp?) (s/def ::new-password-file string?) -(s/def ::new-password-config (s/keys :req-un [::new-password-file ::valid-from])) +(s/def ::new-password-config (s/keys :req-un [::new-password-file ::replace-until])) (s/def ::cred-rot (s/keys :req-un [] @@ -18,16 +20,60 @@ [config ::cred-rot] (i/execute-out! (domain/list-passwords-command config) config)) -(defn-spec maybe-add-new! nil? +(defn-spec change-password-step! nil? [config ::cred-rot] - (let [{:keys [new-password-file valid-from]} (:new-password-config config)] + (when-some [new-password-config (:new-password-config config)] + (let [{:keys [new-password-file replace-until]} new-password-config + initial-passwords-list (domain/parse-response (list-passwords! config)) + action (domain/set-new-password-action + (ld/now) + initial-passwords-list + config)] + (cond + (= action :wait-for-new-pwd) + (println "wait till new password is valid") + (= action :set-new-pwd) + (i/execute! (domain/add-password-command config) config) + (= action :remove-old-pwd) + (i/execute! (domain/remove-password-command + config + (:id (first initial-passwords-list)) + (:id (last initial-passwords-list))) + config) + (= action :new-pwd-change-finished) + (println "pw-change sucessfull") + (= action :no-pwd-change-needed) + (println "nothing to do.") + :else + (throw (Exception. (str action))) + ) + (println initial-passwords-list) + (println action) + (println (list-passwords! config)) + action + ))) + +(defn-spec change-password! nil? + [config ::cred-rot] + (while (let [action (change-password-step! config) ] + (and (not= :no-pwd-change-needed action) + (not= :wait-for-new-pwd action))) + (println "call step") + )) + + +(defn-spec set-new-password-old! nil? + [config ::cred-rot] + (let [{:keys [new-password-file replace-until]} (:new-password-config config) + replace-until-date (ld/parse "2024-10-19 13:16:54" domain/timestamp-formatter)] (if (not (nil? new-password-file)) (let [parsed-passwords (domain/parse-response (list-passwords! config))] - (when (> 0 (compare - (:created (last parsed-passwords)) - valid-from)) - (i/execute! (domain/add-password-command config) config)))))) - -(defn-spec replace-old-password! nil? - [] - ) + (println (str "add-new-password: relace until " replace-until)) + (cond + (> 0 (compare + (:created (last parsed-passwords)) + replace-until-date)) + (do (println "add-new-password: set new pw") + (i/execute! (domain/add-password-command config) config)) + :else (println (str "else " (inst/now))))) + (println "add-new-password: there was no new pw configured")))) diff --git a/src/dda/backup/cred_rot/domain.clj b/src/dda/backup/cred_rot/domain.clj index c1b6b64..893222d 100644 --- a/src/dda/backup/cred_rot/domain.clj +++ b/src/dda/backup/cred_rot/domain.clj @@ -27,20 +27,32 @@ (s/def ::hostName (fn [in] (every? #(re-matches lowercase-numeric %) (str/split in #"-")))) ; "2024-10-18 13:08:16" (def timestamp-formatter (df/of-pattern "yyyy-MM-dd HH:mm:ss")) -(defn timestamp? [in] +(defn timestamp-string? [in] (try (ld/parse in timestamp-formatter) true (catch Exception _ false))) +(def timestamp? any?) + (s/def ::created timestamp?) (s/def ::entry (s/keys :opt-un [] :req-un [::current ::id ::userName ::hostName ::created])) (s/def ::response (s/coll-of ::entry)) +(s/def ::set-password-action #{:error-parse-password :error-undefined + :wait-for-new-pwd :set-new-pwd :remove-old-pwd + :new-pwd-change-finished :no-pwd-change-needed}) + +(s/def ::valid-from timestamp-string?) +(s/def ::new-password-file string?) +(s/def ::new-password-config (s/keys :req-un [::new-password-file ::valid-from])) + +(s/def ::cred-rot + (s/keys :req-un [] + :opt-un [::new-password-config])) ; Refer to "docs/CredentialRotation.md" for specifics - (defn-spec base-command ::cd/command [config ::config command ::cd/command] @@ -75,4 +87,37 @@ (defn-spec parse-response ::response [response string?] - (sort-by :created (cc/parse-string response #(keyword %)))) + (->> (cc/parse-string response true) + (map #(merge % {:created (ld/parse (:created %) timestamp-formatter)})) + (sort-by :created) + )) + +(defn-spec set-new-password-action ::set-password-action + [current-date timestamp? + parsed-response ::response + config ::cred-rot] + (if-let [new-password-config (:new-password-config config)] + (let [valid-from (:valid-from new-password-config) + valid-from-date (ld/parse valid-from timestamp-formatter)] + (cond + (> 1 (count parsed-response)) + :error-parse-password + (> 0 (compare current-date valid-from-date)) + :wait-for-new-pwd + (and (<= 0 (compare current-date valid-from-date)) + (= 1 (count parsed-response)) + (> 0 (compare (:created (last parsed-response)) valid-from-date))) + :set-new-pwd + (and (<= 0 (compare current-date valid-from-date)) + (= 2 (count parsed-response)) + (<= 0 (compare (:created (last parsed-response)) valid-from-date)) + (not (:current (last parsed-response)))) + :remove-old-pwd + (and (<= 0 (compare current-date valid-from-date)) + (= 1 (count parsed-response)) + (<= 0 (compare (:created (last parsed-response)) valid-from-date)) + (:current (last parsed-response))) + :new-pwd-change-finished + :else + :error-undefined)) + :no-pwd-change-needed)) diff --git a/test/dda/backup/cred_rot/domain_test.clj b/test/dda/backup/cred_rot/domain_test.clj index c1b1aeb..e713f67 100644 --- a/test/dda/backup/cred_rot/domain_test.clj +++ b/test/dda/backup/cred_rot/domain_test.clj @@ -3,37 +3,9 @@ [clojure.test :refer [deftest is]] [clojure.spec.alpha :as s] [clojure.spec.test.alpha :as st] + [cljc.java-time.local-date :as ld] [dda.backup.cred-rot.domain :as cut])) -(deftest test-parse-response - (is (= - [{:current true, - :id "b67161fb", - :userName "root", - :hostName "backup-restore-65bd9b6ff5-z69sn", - :created "2024-10-18 13:16:54"} - {:current false, - :id "521e0760", - :userName "root", - :hostName "backup-restore-65bd9b6ff5-z69sn", - :created "2024-11-18 13:08:16"}] - (cut/parse-response "[ - { - \"current\": false, - \"id\": \"521e0760\", - \"userName\": \"root\", - \"hostName\": \"backup-restore-65bd9b6ff5-z69sn\", - \"created\": \"2024-11-18 13:08:16\" - }, - { - \"current\": true, - \"id\": \"b67161fb\", - \"userName\": \"root\", - \"hostName\": \"backup-restore-65bd9b6ff5-z69sn\", - \"created\": \"2024-10-18 13:16:54\" - } -]")))) - (deftest test-spec-id (is (s/valid? ::cut/id "521e0760")) (is (s/valid? ::cut/id "test")) @@ -54,9 +26,97 @@ (is (valid "backup-restore-65bd9b6ff5-z69sn")))) (deftest test-spec-timestamp - (let [valid #(s/valid? cut/timestamp? %)] + (let [valid #(s/valid? cut/timestamp-string? %)] (is (valid "2024-10-18 13:08:16")) (is (valid "2032-09-01 12:56:59")) (is (not (valid "2024-13-5 13:08:16"))) (is (not (valid "2024-6-42 13:08:16"))) - (is (not (valid "test"))))) \ No newline at end of file + (is (not (valid "test"))))) + +(deftest test-parse-response + (is (= + (ld/parse "2024-10-19 13:16:54" cut/timestamp-formatter) + (:created + (first + (cut/parse-response "[ + { + \"current\": false, + \"id\": \"521e0760\", + \"userName\": \"root\", + \"hostName\": \"backup-restore-65bd9b6ff5-z69sn\", + \"created\": \"2024-11-18 13:08:16\" + }, + { + \"current\": true, + \"id\": \"b67161fb\", + \"userName\": \"root\", + \"hostName\": \"backup-restore-65bd9b6ff5-z69sn\", + \"created\": \"2024-10-19 13:16:54\" + } +]")))))) + +(deftest test-set-new-password-action + (is (= :error-parse-password + (cut/set-new-password-action + (ld/parse "2024-10-19 13:16:54" cut/timestamp-formatter) + [] + {:new-password-config {:new-password-file "new-pw-file" + :valid-from "2024-11-29 12:00:16"}}))) + (is (= :wait-for-new-pwd + (cut/set-new-password-action + (ld/parse "2024-10-19 13:16:54" cut/timestamp-formatter) + [{:current true + :id "a1" + :userName "root" + :hostName "host" + :created (ld/parse "2023-01-01 00:00:00" cut/timestamp-formatter)}] + {:new-password-config {:new-password-file "new-pw-file" + :valid-from "2024-11-29 12:00:16"}}))) + (is (= :set-new-pwd + (cut/set-new-password-action + (ld/parse "2024-11-29 13:16:54" cut/timestamp-formatter) + [{:current true + :id "a1" + :userName "root" + :hostName "host" + :created (ld/parse "2023-01-01 00:00:00" cut/timestamp-formatter)}] + {:new-password-config {:new-password-file "new-pw-file" + :valid-from "2024-11-29 12:00:16"}}))) + + (is (= :remove-old-pwd + (cut/set-new-password-action + (ld/parse "2024-11-29 13:16:55" cut/timestamp-formatter) + [{:current true + :id "a1" + :userName "root" + :hostName "host" + :created (ld/parse "2023-01-01 00:00:00" cut/timestamp-formatter)} + {:current false + :id "a2" + :userName "root" + :hostName "host" + :created (ld/parse "2024-11-29 13:16:54" cut/timestamp-formatter)}] + {:new-password-config {:new-password-file "new-pw-file" + :valid-from "2024-11-29 12:00:16"}}))) + + (is (= :new-pwd-change-finished + (cut/set-new-password-action + (ld/parse "2024-11-29 13:16:55" cut/timestamp-formatter) + [{:current true + :id "a2" + :userName "root" + :hostName "host" + :created (ld/parse "2024-11-29 13:16:54" cut/timestamp-formatter)}] + {:new-password-config {:new-password-file "new-pw-file" + :valid-from "2024-11-29 12:00:16"}}))) + + (is (= :no-pwd-change-needed + (cut/set-new-password-action + (ld/parse "2024-11-29 13:16:55" cut/timestamp-formatter) + [{:current true + :id "a2" + :userName "root" + :hostName "host" + :created (ld/parse "2024-11-29 13:16:54" cut/timestamp-formatter)}] + {}))) + ) \ No newline at end of file From 10902da9987d866320f843304aa1ccfc27f426ef Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Wed, 11 Dec 2024 11:16:06 +0100 Subject: [PATCH 12/18] add doc --- docs/CredentialRotation.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/CredentialRotation.md b/docs/CredentialRotation.md index fb66135..e584238 100644 --- a/docs/CredentialRotation.md +++ b/docs/CredentialRotation.md @@ -1,5 +1,32 @@ # Credential Rotation +## change password step + +```mermaid +stateDiagram-v2 + noAction: no-pwd-change-needed + wait: wait-for-new-pwd + new: set-new-pwd + removeOld: remove-old-pwd + finished: new-pwd-change-finished + state configExist? <> + state valid? <> + state finished? <> + + [*] --> configExist? + configExist? --> valid?: new-password-config-exist? + configExist? --> noAction + valid? --> finished?: valid-from > now? + valid? --> wait + finished? --> finished: current > valid-from? + finished? --> new + new --> removeOld + removeOld --> [*] + finished --> [*] + noAction --> [*] + wait --> [*] +``` + ## Example Data Default From b4d9a690f9378f794a0ebc910a2d2c80ff4d8f1b Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Wed, 11 Dec 2024 19:48:23 +0100 Subject: [PATCH 13/18] credential-rotation now works --- infrastructure/backup/image/Dockerfile | 6 +- .../backup/image/resources2/test.bb | 78 +++++++++++++++---- infrastructure/backup/test/resources/test.bb | 50 +++++++++--- src/dda/backup/cred_rot.clj | 4 +- 4 files changed, 108 insertions(+), 30 deletions(-) diff --git a/infrastructure/backup/image/Dockerfile b/infrastructure/backup/image/Dockerfile index bb0759a..87344ab 100644 --- a/infrastructure/backup/image/Dockerfile +++ b/infrastructure/backup/image/Dockerfile @@ -5,6 +5,6 @@ ADD resources /tmp/ RUN /tmp/install.sh ADD local/ /usr/local/lib/dda-backup RUN init-bb.bb -ADD resources2 /tmp/ -RUN install -m 0700 -o root -g root /tmp/test.bb /usr/local/bin/ -RUN test.bb +#ADD resources2 /tmp/ +#RUN install -m 0700 -o root -g root /tmp/test.bb /usr/local/bin/ +#RUN test.bb diff --git a/infrastructure/backup/image/resources2/test.bb b/infrastructure/backup/image/resources2/test.bb index 7f2eecc..fd4c043 100755 --- a/infrastructure/backup/image/resources2/test.bb +++ b/infrastructure/backup/image/resources2/test.bb @@ -1,35 +1,87 @@ #!/usr/bin/env bb (require '[babashka.tasks :as tasks] + '[dda.backup.core :as bc] '[dda.backup.cred-rot :as cr] '[dda.backup.restic :as rc] - '[dda.backup.postgresql :as pg]) + '[dda.backup.postgresql :as pg] + '[dda.backup.backup :as bak] + '[dda.backup.restore :as rs]) (def restic-repo {:password-file "restic-pwd" - :restic-repository "restic-repo"}) + :restic-repository "/restic-repo"}) (def file-config (merge restic-repo {:backup-path "files" - :files ["test-backup"] - :restore-target-directory "test-restore"})) + :files ["/test-backup"] + :restore-target-directory "/test-restore"})) -(def cred-config (merge restic-repo - {:new-password-config {:new-password-file "new-pw" + +(def db-config (merge restic-repo {:backup-path "db" + :pg-db "mydb" + :pg-user "user" + :pg-password "password"})) + +(def cred-config (merge file-config + {:restic-repository "/restic-repo/files" + :new-password-config {:new-password-file "new-pw" :valid-from "2024-12-12 00:00:00"}})) + (def dry-run {:dry-run true :debug true}) (defn prepare! [] - (spit "restic-pwd" "thePassword") - (spit "new-pw" "newPassword") - (tasks/shell "mkdir" "-p" "test-backup") - (spit "test-backup/file" "I was here") - (tasks/shell "mkdir" "-p" "test-restore")) + (spit "/tmp/file_password" "file-password") + + (spit "/restic-pwd" "ThePassword") + (spit "/new-pw" "newPassword") + + (tasks/shell "mkdir" "-p" "/test-backup") + (spit "/test-backup/file" "I was here") + (tasks/shell "mkdir" "-p" "/test-restore") + (pg/create-pg-pass! db-config)) + +(defn check-env-or-file + [] + (println "check-env-or-file") + (println (bc/env-or-file "FILE_PASSWORD")) + (println (bc/env-or-file "ENV_PASSWORD"))) (defn restic-repo-init! [] - (rc/init! restic-repo)) + (println "restic-repo-init!") + (rc/init! file-config) + (rc/init! (merge db-config dry-run))) + +(defn restic-backup! + [] + (println "restic-backup!") + (bak/backup-file! file-config) + (bak/backup-db! (merge db-config dry-run))) + +(defn list-snapshots! + [] + (println "list-snapshots!") + (rc/list-snapshots! file-config) + (rc/list-snapshots! (merge db-config dry-run))) + + +(defn restic-restore! + [] + (println "restic-restore!") + (rs/restore-file! file-config) + (pg/drop-create-db! (merge db-config dry-run)) + (rs/restore-db! (merge db-config dry-run))) + +(defn change-password! + [] + (println "change-password!") + (cr/change-password! cred-config)) + (prepare!) (restic-repo-init!) -(cr/change-password! cred-config) +(restic-backup!) +(list-snapshots!) +(restic-restore!) +(change-password!) diff --git a/infrastructure/backup/test/resources/test.bb b/infrastructure/backup/test/resources/test.bb index 7883125..0e3c983 100755 --- a/infrastructure/backup/test/resources/test.bb +++ b/infrastructure/backup/test/resources/test.bb @@ -2,17 +2,18 @@ (require '[babashka.tasks :as tasks] '[dda.backup.core :as bc] + '[dda.backup.cred-rot :as cr] '[dda.backup.restic :as rc] '[dda.backup.postgresql :as pg] '[dda.backup.backup :as bak] '[dda.backup.restore :as rs]) (def restic-repo {:password-file "restic-pwd" - :restic-repository "restic-repo"}) + :restic-repository "/restic-repo"}) (def file-config (merge restic-repo {:backup-path "files" - :files ["test-backup"] - :restore-target-directory "test-restore"})) + :files ["/test-backup"] + :restore-target-directory "/test-restore"})) (def db-config (merge restic-repo {:backup-path "db" @@ -20,43 +21,68 @@ :pg-user "user" :pg-password "password"})) +(def cred-config (merge file-config + {:restic-repository "/restic-repo/files" + :new-password-config {:new-password-file "new-pw" + :valid-from "2024-12-12 00:00:00"}})) + + (def dry-run {:dry-run true :debug true}) (defn prepare! [] (spit "/tmp/file_password" "file-password") - (println (bc/env-or-file "FILE_PASSWORD")) - (println (bc/env-or-file "ENV_PASSWORD")) - (spit "restic-pwd" "ThePassword") - (tasks/shell "mkdir" "-p" "test-backup") - (spit "test-backup/file" "I was here") - (tasks/shell "mkdir" "-p" "test-restore") + + (spit "/restic-pwd" "ThePassword") + (spit "/new-pw" "newPassword") + + (tasks/shell "mkdir" "-p" "/test-backup") + (spit "/test-backup/file" "I was here") + (tasks/shell "mkdir" "-p" "/test-restore") (pg/create-pg-pass! db-config)) +(defn check-env-or-file + [] + (println "check-env-or-file") + (println (bc/env-or-file "FILE_PASSWORD")) + (println (bc/env-or-file "ENV_PASSWORD"))) + (defn restic-repo-init! - [] + [] + (println "restic-repo-init!") (rc/init! file-config) (rc/init! (merge db-config dry-run))) (defn restic-backup! - [] + [] + (println "restic-backup!") (bak/backup-file! file-config) (bak/backup-db! (merge db-config dry-run))) (defn list-snapshots! [] + (println "list-snapshots!") (rc/list-snapshots! file-config) (rc/list-snapshots! (merge db-config dry-run))) (defn restic-restore! - [] + [] + (println "restic-restore!") (rs/restore-file! file-config) (pg/drop-create-db! (merge db-config dry-run)) (rs/restore-db! (merge db-config dry-run))) +(defn change-password! + [] + (println "change-password!") + (cr/change-password! cred-config)) + + (prepare!) +(check-env-or-file) (restic-repo-init!) (restic-backup!) (list-snapshots!) (restic-restore!) +(change-password!) diff --git a/src/dda/backup/cred_rot.clj b/src/dda/backup/cred_rot.clj index 4d8cce0..a8476ab 100644 --- a/src/dda/backup/cred_rot.clj +++ b/src/dda/backup/cred_rot.clj @@ -20,7 +20,7 @@ [config ::cred-rot] (i/execute-out! (domain/list-passwords-command config) config)) -(defn-spec change-password-step! nil? +(defn-spec change-password-step! ::domain/set-password-action [config ::cred-rot] (when-some [new-password-config (:new-password-config config)] (let [{:keys [new-password-file replace-until]} new-password-config @@ -31,7 +31,7 @@ config)] (cond (= action :wait-for-new-pwd) - (println "wait till new password is valid") + (println "nothing to do.") (= action :set-new-pwd) (i/execute! (domain/add-password-command config) config) (= action :remove-old-pwd) From a4deb8143b9b94725628ec3b08a458bfb0774982 Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Sat, 14 Dec 2024 18:26:09 +0100 Subject: [PATCH 14/18] change-pwd instead of adding keys --- docs/CredentialRotation.md | 10 +++--- .../image/resources2/change-password.bb | 33 +++++++++++++++++++ src/dda/backup/cred_rot.clj | 13 ++------ src/dda/backup/cred_rot/domain.clj | 24 ++++---------- test/dda/backup/cred_rot/domain_test.clj | 20 ++--------- 5 files changed, 48 insertions(+), 52 deletions(-) create mode 100755 infrastructure/backup/image/resources2/change-password.bb diff --git a/docs/CredentialRotation.md b/docs/CredentialRotation.md index e584238..833e5df 100644 --- a/docs/CredentialRotation.md +++ b/docs/CredentialRotation.md @@ -6,9 +6,8 @@ stateDiagram-v2 noAction: no-pwd-change-needed wait: wait-for-new-pwd - new: set-new-pwd - removeOld: remove-old-pwd - finished: new-pwd-change-finished + new: change-pwd + finished: pwd-change-finished state configExist? <> state valid? <> state finished? <> @@ -20,8 +19,7 @@ stateDiagram-v2 valid? --> wait finished? --> finished: current > valid-from? finished? --> new - new --> removeOld - removeOld --> [*] + new --> [*] finished --> [*] noAction --> [*] wait --> [*] @@ -113,7 +111,7 @@ Validation: Steps to perform: - Add new password -- `restic -r key add --new-password-file ` +- `restic -r --new-password-file key passwd` #### New password has been added diff --git a/infrastructure/backup/image/resources2/change-password.bb b/infrastructure/backup/image/resources2/change-password.bb new file mode 100755 index 0000000..c2c89d7 --- /dev/null +++ b/infrastructure/backup/image/resources2/change-password.bb @@ -0,0 +1,33 @@ +#!/usr/bin/env bb + +(require '[dda.backup.cred-rot :as cr]) + +(def restic-repo {:password-file "/restic-pwd" + :restic-repository "/restic-repo"}) + +(def file-config (merge restic-repo {:backup-path "files" + :files ["/test-backup"] + :restore-target-directory "/test-restore"})) + +(def cred-config (merge file-config + {:restic-repository "/restic-repo/files" + :new-password-config {:new-password-file "/new-pw" + :valid-from "2024-12-12 00:00:00"}})) + + +(def dry-run {:dry-run true :debug true}) + +(defn prepare! + [] + (spit "/restic-pwd" "ThePassword") + (spit "/new-pw" "newPassword")) + + +(defn change-password! + [] + (println "change-password!") + (cr/change-password! cred-config)) + + +(prepare!) +(change-password!) diff --git a/src/dda/backup/cred_rot.clj b/src/dda/backup/cred_rot.clj index a8476ab..a5729b6 100644 --- a/src/dda/backup/cred_rot.clj +++ b/src/dda/backup/cred_rot.clj @@ -23,8 +23,7 @@ (defn-spec change-password-step! ::domain/set-password-action [config ::cred-rot] (when-some [new-password-config (:new-password-config config)] - (let [{:keys [new-password-file replace-until]} new-password-config - initial-passwords-list (domain/parse-response (list-passwords! config)) + (let [initial-passwords-list (domain/parse-response (list-passwords! config)) action (domain/set-new-password-action (ld/now) initial-passwords-list @@ -32,14 +31,8 @@ (cond (= action :wait-for-new-pwd) (println "nothing to do.") - (= action :set-new-pwd) - (i/execute! (domain/add-password-command config) config) - (= action :remove-old-pwd) - (i/execute! (domain/remove-password-command - config - (:id (first initial-passwords-list)) - (:id (last initial-passwords-list))) - config) + (= action :change-pwd) + (i/execute! (domain/change-password-command config) config) (= action :new-pwd-change-finished) (println "pw-change sucessfull") (= action :no-pwd-change-needed) diff --git a/src/dda/backup/cred_rot/domain.clj b/src/dda/backup/cred_rot/domain.clj index 893222d..5ac8224 100644 --- a/src/dda/backup/cred_rot/domain.clj +++ b/src/dda/backup/cred_rot/domain.clj @@ -41,8 +41,7 @@ (s/def ::response (s/coll-of ::entry)) (s/def ::set-password-action #{:error-parse-password :error-undefined - :wait-for-new-pwd :set-new-pwd :remove-old-pwd - :new-pwd-change-finished :no-pwd-change-needed}) + :wait-for-new-pwd :change-pwd :pwd-change-finished :no-pwd-change-needed}) (s/def ::valid-from timestamp-string?) (s/def ::new-password-file string?) @@ -74,16 +73,10 @@ [config ::config] (base-command config ["key" "list" "--json"])) -(defn-spec add-password-command ::cd/command +(defn-spec change-password-command ::cd/command [config ::config] - (let [{:keys [new-password-file]} config] - [(base-command config ["key" "add" "--new-password-file" new-password-file])])) - -(defn-spec remove-password-command ::cd/command - [config ::config - new-id ::id - old-id ::id] - [(base-command config ["key" "remove" "--key-hint" new-id old-id])]) + (let [{:keys [new-password-file]} (:new-password-config config)] + [(base-command config ["--new-password-file" new-password-file "key" "passwd"])])) (defn-spec parse-response ::response [response string?] @@ -107,17 +100,12 @@ (and (<= 0 (compare current-date valid-from-date)) (= 1 (count parsed-response)) (> 0 (compare (:created (last parsed-response)) valid-from-date))) - :set-new-pwd - (and (<= 0 (compare current-date valid-from-date)) - (= 2 (count parsed-response)) - (<= 0 (compare (:created (last parsed-response)) valid-from-date)) - (not (:current (last parsed-response)))) - :remove-old-pwd + :change-pwd (and (<= 0 (compare current-date valid-from-date)) (= 1 (count parsed-response)) (<= 0 (compare (:created (last parsed-response)) valid-from-date)) (:current (last parsed-response))) - :new-pwd-change-finished + :pwd-change-finished :else :error-undefined)) :no-pwd-change-needed)) diff --git a/test/dda/backup/cred_rot/domain_test.clj b/test/dda/backup/cred_rot/domain_test.clj index e713f67..48222f9 100644 --- a/test/dda/backup/cred_rot/domain_test.clj +++ b/test/dda/backup/cred_rot/domain_test.clj @@ -72,7 +72,7 @@ :created (ld/parse "2023-01-01 00:00:00" cut/timestamp-formatter)}] {:new-password-config {:new-password-file "new-pw-file" :valid-from "2024-11-29 12:00:16"}}))) - (is (= :set-new-pwd + (is (= :change-pwd (cut/set-new-password-action (ld/parse "2024-11-29 13:16:54" cut/timestamp-formatter) [{:current true @@ -83,23 +83,7 @@ {:new-password-config {:new-password-file "new-pw-file" :valid-from "2024-11-29 12:00:16"}}))) - (is (= :remove-old-pwd - (cut/set-new-password-action - (ld/parse "2024-11-29 13:16:55" cut/timestamp-formatter) - [{:current true - :id "a1" - :userName "root" - :hostName "host" - :created (ld/parse "2023-01-01 00:00:00" cut/timestamp-formatter)} - {:current false - :id "a2" - :userName "root" - :hostName "host" - :created (ld/parse "2024-11-29 13:16:54" cut/timestamp-formatter)}] - {:new-password-config {:new-password-file "new-pw-file" - :valid-from "2024-11-29 12:00:16"}}))) - - (is (= :new-pwd-change-finished + (is (= :pwd-change-finished (cut/set-new-password-action (ld/parse "2024-11-29 13:16:55" cut/timestamp-formatter) [{:current true From b415956a359e2379c4baee8c361e3237a96079bb Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Tue, 24 Dec 2024 15:50:17 +0100 Subject: [PATCH 15/18] finished change-password --- infrastructure/backup/image/Dockerfile | 7 +- .../image/resources2/change-password.bb | 5 +- .../backup/image/resources2/check.bb | 23 ++++ .../backup/image/resources2/test.bb | 49 ++------ infrastructure/backup/test/resources/test.bb | 36 +++--- src/dda/backup/backup.clj | 32 +++-- src/dda/backup/cred_rot.clj | 72 ------------ src/dda/backup/cred_rot/domain.clj | 111 ------------------ src/dda/backup/infrastructure.clj | 30 +++-- src/dda/backup/restic.clj | 64 ++++++++-- src/dda/backup/restic/domain.clj | 36 ++++-- src/dda/backup/restore.clj | 21 ++-- test/dda/backup/cred_rot/domain_test.clj | 106 ----------------- test/dda/backup/restic/domain_test.clj | 29 ++++- 14 files changed, 216 insertions(+), 405 deletions(-) create mode 100755 infrastructure/backup/image/resources2/check.bb delete mode 100644 src/dda/backup/cred_rot.clj delete mode 100644 src/dda/backup/cred_rot/domain.clj delete mode 100644 test/dda/backup/cred_rot/domain_test.clj diff --git a/infrastructure/backup/image/Dockerfile b/infrastructure/backup/image/Dockerfile index 87344ab..a6436eb 100644 --- a/infrastructure/backup/image/Dockerfile +++ b/infrastructure/backup/image/Dockerfile @@ -5,6 +5,7 @@ ADD resources /tmp/ RUN /tmp/install.sh ADD local/ /usr/local/lib/dda-backup RUN init-bb.bb -#ADD resources2 /tmp/ -#RUN install -m 0700 -o root -g root /tmp/test.bb /usr/local/bin/ -#RUN test.bb +# ADD resources2 /tmp/ +# RUN install -m 0700 -o root -g root /tmp/test.bb /usr/local/bin/ +# RUN install -m 0700 -o root -g root /tmp/check.bb /usr/local/bin/ +# RUN test.bb diff --git a/infrastructure/backup/image/resources2/change-password.bb b/infrastructure/backup/image/resources2/change-password.bb index c2c89d7..ab0d700 100755 --- a/infrastructure/backup/image/resources2/change-password.bb +++ b/infrastructure/backup/image/resources2/change-password.bb @@ -3,7 +3,8 @@ (require '[dda.backup.cred-rot :as cr]) (def restic-repo {:password-file "/restic-pwd" - :restic-repository "/restic-repo"}) + :restic-repository "/restic-repo" + :debug true}) (def file-config (merge restic-repo {:backup-path "files" :files ["/test-backup"] @@ -12,7 +13,7 @@ (def cred-config (merge file-config {:restic-repository "/restic-repo/files" :new-password-config {:new-password-file "/new-pw" - :valid-from "2024-12-12 00:00:00"}})) + :valid-from "2024-12-17 00:00:00"}})) (def dry-run {:dry-run true :debug true}) diff --git a/infrastructure/backup/image/resources2/check.bb b/infrastructure/backup/image/resources2/check.bb new file mode 100755 index 0000000..183feec --- /dev/null +++ b/infrastructure/backup/image/resources2/check.bb @@ -0,0 +1,23 @@ +#!/usr/bin/env bb + +(require '[dda.backup.restic :as rc]) + +(def restic-repo {:password-file "/restic-pwd" + :restic-repository "/restic-repo" + :debug true}) + +(def file-config (merge restic-repo {:backup-path "files" + :files ["/test-backup"] + :restore-target-directory "/test-restore"})) + +(def cred-config (merge file-config {:new-password-file "new-pw"})) + + +(def dry-run {:dry-run true :debug true}) + +(defn restic-repo-check + [] + (println "restic-repo-check") + (println (rc/check file-config))) + +(restic-repo-check) \ No newline at end of file diff --git a/infrastructure/backup/image/resources2/test.bb b/infrastructure/backup/image/resources2/test.bb index fd4c043..fb0a550 100755 --- a/infrastructure/backup/image/resources2/test.bb +++ b/infrastructure/backup/image/resources2/test.bb @@ -1,82 +1,57 @@ #!/usr/bin/env bb (require '[babashka.tasks :as tasks] - '[dda.backup.core :as bc] - '[dda.backup.cred-rot :as cr] '[dda.backup.restic :as rc] - '[dda.backup.postgresql :as pg] '[dda.backup.backup :as bak] '[dda.backup.restore :as rs]) -(def restic-repo {:password-file "restic-pwd" - :restic-repository "/restic-repo"}) +(def restic-repo {:password-file "/restic-pwd" + :new-password-file "/new-restic-pwd" + :restic-repository "/restic-repo" + :debug true}) (def file-config (merge restic-repo {:backup-path "files" :files ["/test-backup"] :restore-target-directory "/test-restore"})) - -(def db-config (merge restic-repo {:backup-path "db" - :pg-db "mydb" - :pg-user "user" - :pg-password "password"})) - -(def cred-config (merge file-config - {:restic-repository "/restic-repo/files" - :new-password-config {:new-password-file "new-pw" - :valid-from "2024-12-12 00:00:00"}})) - - (def dry-run {:dry-run true :debug true}) (defn prepare! [] (spit "/tmp/file_password" "file-password") - (spit "/restic-pwd" "ThePassword") - (spit "/new-pw" "newPassword") + (spit "/restic-pwd" "oldPassword") + (spit "/new-restic-pwd" "newPassword") (tasks/shell "mkdir" "-p" "/test-backup") (spit "/test-backup/file" "I was here") - (tasks/shell "mkdir" "-p" "/test-restore") - (pg/create-pg-pass! db-config)) - -(defn check-env-or-file - [] - (println "check-env-or-file") - (println (bc/env-or-file "FILE_PASSWORD")) - (println (bc/env-or-file "ENV_PASSWORD"))) + (tasks/shell "mkdir" "-p" "/test-restore")) (defn restic-repo-init! [] (println "restic-repo-init!") - (rc/init! file-config) - (rc/init! (merge db-config dry-run))) + (rc/init! file-config)) (defn restic-backup! [] (println "restic-backup!") - (bak/backup-file! file-config) - (bak/backup-db! (merge db-config dry-run))) + (bak/backup-file! file-config)) (defn list-snapshots! [] (println "list-snapshots!") - (rc/list-snapshots! file-config) - (rc/list-snapshots! (merge db-config dry-run))) + (rc/list-snapshots! file-config)) (defn restic-restore! [] (println "restic-restore!") - (rs/restore-file! file-config) - (pg/drop-create-db! (merge db-config dry-run)) - (rs/restore-db! (merge db-config dry-run))) + (rs/restore-file! file-config)) (defn change-password! [] (println "change-password!") - (cr/change-password! cred-config)) + (rc/change-password! file-config)) (prepare!) diff --git a/infrastructure/backup/test/resources/test.bb b/infrastructure/backup/test/resources/test.bb index 0e3c983..a129154 100755 --- a/infrastructure/backup/test/resources/test.bb +++ b/infrastructure/backup/test/resources/test.bb @@ -2,39 +2,33 @@ (require '[babashka.tasks :as tasks] '[dda.backup.core :as bc] - '[dda.backup.cred-rot :as cr] '[dda.backup.restic :as rc] '[dda.backup.postgresql :as pg] '[dda.backup.backup :as bak] '[dda.backup.restore :as rs]) -(def restic-repo {:password-file "restic-pwd" - :restic-repository "/restic-repo"}) +(def restic-repo {:password-file "/restic-pwd" + :new-password-file "/new-restic-pwd" + :restic-repository "/restic-repo" + :debug true}) (def file-config (merge restic-repo {:backup-path "files" :files ["/test-backup"] :restore-target-directory "/test-restore"})) - (def db-config (merge restic-repo {:backup-path "db" :pg-db "mydb" :pg-user "user" :pg-password "password"})) -(def cred-config (merge file-config - {:restic-repository "/restic-repo/files" - :new-password-config {:new-password-file "new-pw" - :valid-from "2024-12-12 00:00:00"}})) - - (def dry-run {:dry-run true :debug true}) (defn prepare! [] (spit "/tmp/file_password" "file-password") - (spit "/restic-pwd" "ThePassword") - (spit "/new-pw" "newPassword") + (spit "/restic-pwd" "oldPassword") + (spit "/new-restic-pwd" "newPassword") (tasks/shell "mkdir" "-p" "/test-backup") (spit "/test-backup/file" "I was here") @@ -43,41 +37,40 @@ (defn check-env-or-file [] - (println "check-env-or-file") + (println "\ncheck-env-or-file") (println (bc/env-or-file "FILE_PASSWORD")) (println (bc/env-or-file "ENV_PASSWORD"))) (defn restic-repo-init! [] - (println "restic-repo-init!") + (println "\nrestic-repo-init!") (rc/init! file-config) (rc/init! (merge db-config dry-run))) (defn restic-backup! [] - (println "restic-backup!") + (println "\nrestic-backup!") (bak/backup-file! file-config) (bak/backup-db! (merge db-config dry-run))) (defn list-snapshots! [] - (println "list-snapshots!") + (println "\nlist-snapshots!") (rc/list-snapshots! file-config) (rc/list-snapshots! (merge db-config dry-run))) (defn restic-restore! [] - (println "restic-restore!") + (println "\nrestic-restore!") (rs/restore-file! file-config) (pg/drop-create-db! (merge db-config dry-run)) (rs/restore-db! (merge db-config dry-run))) (defn change-password! [] - (println "change-password!") - (cr/change-password! cred-config)) - + (println "\nchange-password!") + (rc/change-password! file-config)) (prepare!) (check-env-or-file) @@ -86,3 +79,6 @@ (list-snapshots!) (restic-restore!) (change-password!) +(restic-backup!) +(list-snapshots!) +(restic-restore!) diff --git a/src/dda/backup/backup.clj b/src/dda/backup/backup.clj index 13d5e2d..5facd07 100644 --- a/src/dda/backup/backup.clj +++ b/src/dda/backup/backup.clj @@ -26,25 +26,31 @@ (s/merge ::pg/pg-config ::restic/restic-config)) +(defn- config-w-defaults + [config] + (if (restic/use-new-password? config) + (merge default config {:password-file (:new-password-file config)}) + (merge default config))) + (defn-spec backup-file! nil? [config ::backup-file-config] - (let [config-w-defaults (merge default config)] - (restic/unlock! config-w-defaults) + (let [config-2-use (config-w-defaults config)] + (restic/unlock! config-2-use) (i/execute! - (domain/backup-files-command config-w-defaults) - config-w-defaults) - (restic/forget! config-w-defaults))) + (domain/backup-files-command config-2-use) + config-2-use) + (restic/forget! config-2-use))) (defn-spec backup-db-roles! nil? [config ::pg-role-dump-config] - (let [config-w-defaults (merge default config)] - (restic/unlock! config-w-defaults) - (i/execute! (domain/backup-role-command config-w-defaults) config-w-defaults) - (restic/forget! config-w-defaults))) + (let [config-2-use (config-w-defaults config)] + (restic/unlock! config-2-use) + (i/execute! (domain/backup-role-command config-2-use) config-2-use) + (restic/forget! config-2-use))) (defn-spec backup-db! nil? [config ::pg-db-dump-config] - (let [config-w-defaults (merge default config)] - (restic/unlock! config-w-defaults) - (i/execute! (domain/backup-db-command config-w-defaults) config-w-defaults) - (restic/forget! config-w-defaults))) \ No newline at end of file + (let [config-2-use (config-w-defaults config)] + (restic/unlock! config-2-use) + (i/execute! (domain/backup-db-command config-2-use) config-2-use) + (restic/forget! config-2-use))) \ No newline at end of file diff --git a/src/dda/backup/cred_rot.clj b/src/dda/backup/cred_rot.clj deleted file mode 100644 index a5729b6..0000000 --- a/src/dda/backup/cred_rot.clj +++ /dev/null @@ -1,72 +0,0 @@ -(ns dda.backup.cred-rot - (:require - [orchestra.core :refer [defn-spec]] - [clojure.spec.alpha :as s] - [dda.backup.cred-rot.domain :as domain] - [dda.backup.infrastructure :as i] - [cljc.java-time.instant :as inst] - [cljc.java-time.local-date :as ld])) - -(s/def ::replace-until domain/timestamp?) -(s/def ::new-password-file string?) -(s/def ::new-password-config (s/keys :req-un [::new-password-file ::replace-until])) - -(s/def ::cred-rot - (s/keys :req-un [] - :opt-un [::new-password-config])) - -; Refer to "docs/CredentialRotation.md" for specifics -(defn-spec list-passwords! string? - [config ::cred-rot] - (i/execute-out! (domain/list-passwords-command config) config)) - -(defn-spec change-password-step! ::domain/set-password-action - [config ::cred-rot] - (when-some [new-password-config (:new-password-config config)] - (let [initial-passwords-list (domain/parse-response (list-passwords! config)) - action (domain/set-new-password-action - (ld/now) - initial-passwords-list - config)] - (cond - (= action :wait-for-new-pwd) - (println "nothing to do.") - (= action :change-pwd) - (i/execute! (domain/change-password-command config) config) - (= action :new-pwd-change-finished) - (println "pw-change sucessfull") - (= action :no-pwd-change-needed) - (println "nothing to do.") - :else - (throw (Exception. (str action))) - ) - (println initial-passwords-list) - (println action) - (println (list-passwords! config)) - action - ))) - -(defn-spec change-password! nil? - [config ::cred-rot] - (while (let [action (change-password-step! config) ] - (and (not= :no-pwd-change-needed action) - (not= :wait-for-new-pwd action))) - (println "call step") - )) - - -(defn-spec set-new-password-old! nil? - [config ::cred-rot] - (let [{:keys [new-password-file replace-until]} (:new-password-config config) - replace-until-date (ld/parse "2024-10-19 13:16:54" domain/timestamp-formatter)] - (if (not (nil? new-password-file)) - (let [parsed-passwords (domain/parse-response (list-passwords! config))] - (println (str "add-new-password: relace until " replace-until)) - (cond - (> 0 (compare - (:created (last parsed-passwords)) - replace-until-date)) - (do (println "add-new-password: set new pw") - (i/execute! (domain/add-password-command config) config)) - :else (println (str "else " (inst/now))))) - (println "add-new-password: there was no new pw configured")))) diff --git a/src/dda/backup/cred_rot/domain.clj b/src/dda/backup/cred_rot/domain.clj deleted file mode 100644 index 5ac8224..0000000 --- a/src/dda/backup/cred_rot/domain.clj +++ /dev/null @@ -1,111 +0,0 @@ -(ns dda.backup.cred-rot.domain - (:require - [orchestra.core :refer [defn-spec]] - [clojure.spec.alpha :as s] - [clojure.string :as str] - [dda.backup.core.domain :as cd] - [dda.backup.restic.domain :as rd] - [cheshire.core :as cc] - [cljc.java-time.local-date :as ld] - [cljc.java-time.format.date-time-formatter :as df])) - -(s/def ::new-password-file string?) -(s/def ::config (s/keys :req-un [::rd/restic-repository - ::rd/password-file] - :opt-un [::rd/certificate-file - ::new-password-file])) - -(def lowercase-numeric #"[a-z0-9]+") -(def alphanumeric #"[a-zA-Z0-9]+") -; true | false -(s/def ::current boolean?) -; 521e0760 -(s/def ::id (s/and string? #(re-matches lowercase-numeric %))) -; root -(s/def ::userName #(re-matches alphanumeric %)) -; backup-restore-65bd9b6ff5-z69sn -(s/def ::hostName (fn [in] (every? #(re-matches lowercase-numeric %) (str/split in #"-")))) -; "2024-10-18 13:08:16" -(def timestamp-formatter (df/of-pattern "yyyy-MM-dd HH:mm:ss")) -(defn timestamp-string? [in] - (try - (ld/parse in timestamp-formatter) - true - (catch Exception _ false))) -(def timestamp? any?) - -(s/def ::created timestamp?) - -(s/def ::entry (s/keys :opt-un [] - :req-un [::current ::id ::userName ::hostName ::created])) -(s/def ::response (s/coll-of ::entry)) - -(s/def ::set-password-action #{:error-parse-password :error-undefined - :wait-for-new-pwd :change-pwd :pwd-change-finished :no-pwd-change-needed}) - -(s/def ::valid-from timestamp-string?) -(s/def ::new-password-file string?) -(s/def ::new-password-config (s/keys :req-un [::new-password-file ::valid-from])) - -(s/def ::cred-rot - (s/keys :req-un [] - :opt-un [::new-password-config])) - -; Refer to "docs/CredentialRotation.md" for specifics -(defn-spec base-command ::cd/command - [config ::config - command ::cd/command] - (let [{:keys [restic-repository password-file - certificate-file new-password-file]} config] - (into - [] - (concat ["restic" "-r" restic-repository] - (cond - (some? certificate-file) - ["--cacert" certificate-file] - (some? password-file) - ["--password-file" password-file] - :else - []) - command)))) - -(defn-spec list-passwords-command ::cd/command - [config ::config] - (base-command config ["key" "list" "--json"])) - -(defn-spec change-password-command ::cd/command - [config ::config] - (let [{:keys [new-password-file]} (:new-password-config config)] - [(base-command config ["--new-password-file" new-password-file "key" "passwd"])])) - -(defn-spec parse-response ::response - [response string?] - (->> (cc/parse-string response true) - (map #(merge % {:created (ld/parse (:created %) timestamp-formatter)})) - (sort-by :created) - )) - -(defn-spec set-new-password-action ::set-password-action - [current-date timestamp? - parsed-response ::response - config ::cred-rot] - (if-let [new-password-config (:new-password-config config)] - (let [valid-from (:valid-from new-password-config) - valid-from-date (ld/parse valid-from timestamp-formatter)] - (cond - (> 1 (count parsed-response)) - :error-parse-password - (> 0 (compare current-date valid-from-date)) - :wait-for-new-pwd - (and (<= 0 (compare current-date valid-from-date)) - (= 1 (count parsed-response)) - (> 0 (compare (:created (last parsed-response)) valid-from-date))) - :change-pwd - (and (<= 0 (compare current-date valid-from-date)) - (= 1 (count parsed-response)) - (<= 0 (compare (:created (last parsed-response)) valid-from-date)) - (:current (last parsed-response))) - :pwd-change-finished - :else - :error-undefined)) - :no-pwd-change-needed)) diff --git a/src/dda/backup/infrastructure.clj b/src/dda/backup/infrastructure.clj index a0b8b89..15a3b45 100644 --- a/src/dda/backup/infrastructure.clj +++ b/src/dda/backup/infrastructure.clj @@ -3,16 +3,6 @@ [babashka.tasks :as t] [dda.backup.core.domain :as core])) -(defn-spec execute! nil? - [commands ::core/commands - config ::core/execution] - (let [{:keys [dry-run debug]} config] - (doseq [c commands] - (when debug - (println c)) - (when-not dry-run - (apply t/shell c))))) - (defn-spec execute-out! string? [command ::core/command config ::core/execution] @@ -20,4 +10,22 @@ (when debug (println command)) (when-not dry-run - (:out (t/shell {:out :string} (clojure.string/join " " command)))))) \ No newline at end of file + (:out (t/shell {:out :string :err :string} (clojure.string/join " " command)))))) + +(defn-spec execute-single! string? + [command ::core/command + config ::core/execution] + (let [{:keys [dry-run debug]} config] + (when debug + (println command)) + (when-not dry-run + (:out (t/shell {:err :string} (clojure.string/join " " command)))))) + + +(defn-spec execute! nil? + [commands ::core/commands + config ::core/execution] + (let [{:keys [dry-run debug]} config] + (doseq [c commands] + (when-not dry-run + (execute-single! c config))))) \ No newline at end of file diff --git a/src/dda/backup/restic.clj b/src/dda/backup/restic.clj index 419a7ce..3da74cb 100644 --- a/src/dda/backup/restic.clj +++ b/src/dda/backup/restic.clj @@ -12,39 +12,77 @@ :months-to-keep 12})) (s/def ::restic-config - (s/merge ::core/execution + (s/merge ::core/execution (s/keys :req-un [::domain/restic-repository ::domain/backup-path] :opt-un [::domain/certificate-file ::domain/password-file + ::domain/new-password-file ::domain/days-to-keep ::domain/months-to-keep]))) -(defn-spec initalized? boolean? +(s/def ::check-result #{:initialized :wrong-password :not-initialized :error}) + +(defn-spec check ::check-result [restic-config ::restic-config] (let [config-w-defaults (merge core/default restic-config)] (try (i/execute! (domain/check-repo-command config-w-defaults) config-w-defaults) - true - (catch Exception e false)))) + :initialized + (catch Exception e + (let [data (ex-data e) + parsed-error (domain/parse-check-error (get-in data [:proc :err]))] + (cond + (= parsed-error :not-initialized) :not-initialized + (= parsed-error :wrong-password) :wrong-password + :default :error)))))) + +(defn-spec use-new-password? boolean? + "deprecated" + [restic-config ::restic-config] + (if (contains? restic-config :new-password-file) + (= :initialized (check (merge restic-config {:password-file (:new-password-file restic-config)}))) + false)) + +(defn- config-w-defaults + [restic-config] + (if (use-new-password? restic-config) + (merge default restic-config {:password-file (:new-password-file restic-config)}) + (merge default restic-config))) + +(defn-spec initalized? boolean? + "deprecated" + [restic-config ::restic-config] + (let [config-2-use (config-w-defaults restic-config)] + (= :initialized (check config-2-use)))) (defn-spec init! nil? [restic-config ::restic-config] - (let [config-w-defaults (merge core/default restic-config)] - (when (not (initalized? config-w-defaults)) - (i/execute! (domain/init-repo-command config-w-defaults) config-w-defaults)))) + (let [config-2-use (config-w-defaults restic-config)] + (when (= :not-initialized (check config-2-use)) + (i/execute! (domain/init-repo-command config-2-use) config-2-use)))) (defn-spec unlock! nil? [restic-config ::restic-config] - (let [config-w-defaults (merge core/default restic-config)] - (i/execute! (domain/unlock-repo-command config-w-defaults) config-w-defaults))) + (let [config-2-use (config-w-defaults restic-config)] + (i/execute! (domain/unlock-repo-command config-2-use) config-2-use))) (defn-spec forget! nil? [restic-config ::restic-config] - (let [config-w-defaults (merge core/default restic-config)] - (i/execute! (domain/forget-command config-w-defaults) config-w-defaults))) + (let [config-2-use (config-w-defaults restic-config)] + (i/execute! (domain/forget-command config-2-use) config-2-use))) (defn-spec list-snapshots! nil? [restic-config ::restic-config] - (let [config-w-defaults (merge core/default restic-config)] - (i/execute! (domain/list-snapshot-command config-w-defaults) config-w-defaults))) + (let [config-2-use (config-w-defaults restic-config)] + (i/execute! (domain/list-snapshot-command config-2-use) config-2-use))) + +(defn-spec change-password! nil? + [restic-config ::restic-config] + (when (contains? restic-config :new-password-file) + (let [config-2-use (merge core/default restic-config)] + (when (= :initialized (check config-2-use)) + (do + (i/execute! (domain/change-password-command config-2-use) config-2-use) + (when-not (= :wrong-password (check config-2-use)) + (throw (Exception. "password-change did not work!")))))))) diff --git a/src/dda/backup/restic/domain.clj b/src/dda/backup/restic/domain.clj index d06ae11..10594e5 100644 --- a/src/dda/backup/restic/domain.clj +++ b/src/dda/backup/restic/domain.clj @@ -6,24 +6,28 @@ (s/def ::certificate-file string?) (s/def ::password-file string?) +(s/def ::new-password-file string?) (s/def ::restic-repository string?) (s/def ::backup-path string?) (s/def ::days-to-keep pos?) (s/def ::months-to-keep pos?) (s/def ::restic-config - (s/keys :req-un [::restic-repository - ::backup-path - ::days-to-keep + (s/keys :req-un [::restic-repository + ::backup-path + ::days-to-keep ::months-to-keep] - :opt-un [::certificate-file - ::password-file + :opt-un [::certificate-file + ::password-file + ::new-password-file ::cd/execution-directory])) +(s/def ::check-error #{:not-initialized :wrong-password :no-password :unknown}) + (defn-spec repo-command ::cd/command [config ::restic-config command ::cd/command] - (let [{:keys [certificate-file password-file execution-directory + (let [{:keys [certificate-file password-file execution-directory restic-repository backup-path]} config] (into [] @@ -60,7 +64,23 @@ (defn-spec forget-command ::cd/commands [config ::restic-config] (let [{:keys [days-to-keep months-to-keep]} config] - [(repo-command config ["forget" "--group-by" "" + [(repo-command config ["forget" "--group-by" "''" "--keep-last" "1" "--keep-daily" (str days-to-keep) - "--keep-monthly" (str months-to-keep) "--prune"])])) \ No newline at end of file + "--keep-monthly" (str months-to-keep) "--prune"])])) + +(defn-spec change-password-command ::cd/command + [config ::restic-config] + (if (contains? config :new-password-file) + (let [{:keys [new-password-file]} config] + [(repo-command config ["--new-password-file" new-password-file + "key" "passwd"])]) + (throw (Exception. "change-password: new password required")))) + +(defn-spec parse-check-error ::check-error + [error string?] + (cond + (clojure.string/includes? error "Fatal: unable to open config file") :not-initialized + (clojure.string/includes? error "Fatal: wrong password or no key found") :wrong-password + (clojure.string/includes? error "Resolving password failed") :no-password + :default :unknown)) diff --git a/src/dda/backup/restore.clj b/src/dda/backup/restore.clj index 63054e3..57a5988 100644 --- a/src/dda/backup/restore.clj +++ b/src/dda/backup/restore.clj @@ -20,19 +20,26 @@ (s/merge ::pg/pg-config (s/keys :req-un [::domain/snapshot-id]))) +(defn- config-w-defaults + [config] + (if (restic/use-new-password? config) + (merge default config {:password-file (:new-password-file config)}) + (merge default config))) + + (defn-spec restore-file! nil? [config ::restore-file-config] - (let [config-w-defaults (merge default config)] - (restic/unlock! config-w-defaults) + (let [config-2-use (config-w-defaults config)] + (restic/unlock! config-2-use) (i/execute! - (domain/restore-dir-command config-w-defaults) - config-w-defaults))) + (domain/restore-dir-command config-2-use) + config-2-use))) (defn-spec restore-db! nil? [config ::restore-db-config] - (let [config-w-defaults (merge default config)] - (restic/unlock! config-w-defaults) - (i/execute! (domain/restore-db-command config-w-defaults) config-w-defaults))) + (let [config-2-use (config-w-defaults config)] + (restic/unlock! config-2-use) + (i/execute! (domain/restore-db-command config-2-use) config-2-use))) ;; function restore-roles() { diff --git a/test/dda/backup/cred_rot/domain_test.clj b/test/dda/backup/cred_rot/domain_test.clj deleted file mode 100644 index 48222f9..0000000 --- a/test/dda/backup/cred_rot/domain_test.clj +++ /dev/null @@ -1,106 +0,0 @@ -(ns dda.backup.cred-rot.domain-test - (:require - [clojure.test :refer [deftest is]] - [clojure.spec.alpha :as s] - [clojure.spec.test.alpha :as st] - [cljc.java-time.local-date :as ld] - [dda.backup.cred-rot.domain :as cut])) - -(deftest test-spec-id - (is (s/valid? ::cut/id "521e0760")) - (is (s/valid? ::cut/id "test")) - (is (s/valid? ::cut/id "123456")) - (is (not (s/valid? ::cut/id "ROOT"))) - (is (not (s/valid? ::cut/id "Test!")))) - -(deftest test-spec-username - (is (s/valid? ::cut/userName "521e0760")) - (is (s/valid? ::cut/userName "Testuser")) - (is (s/valid? ::cut/userName "root")) - (is (s/valid? ::cut/userName "ROOT")) - (is (not (s/valid? ::cut/userName "test-user")))) - -(deftest test-spec-hostName - (let [valid #(s/valid? ::cut/hostName %)] - (is (valid "test-some-combination-2")) - (is (valid "backup-restore-65bd9b6ff5-z69sn")))) - -(deftest test-spec-timestamp - (let [valid #(s/valid? cut/timestamp-string? %)] - (is (valid "2024-10-18 13:08:16")) - (is (valid "2032-09-01 12:56:59")) - (is (not (valid "2024-13-5 13:08:16"))) - (is (not (valid "2024-6-42 13:08:16"))) - (is (not (valid "test"))))) - -(deftest test-parse-response - (is (= - (ld/parse "2024-10-19 13:16:54" cut/timestamp-formatter) - (:created - (first - (cut/parse-response "[ - { - \"current\": false, - \"id\": \"521e0760\", - \"userName\": \"root\", - \"hostName\": \"backup-restore-65bd9b6ff5-z69sn\", - \"created\": \"2024-11-18 13:08:16\" - }, - { - \"current\": true, - \"id\": \"b67161fb\", - \"userName\": \"root\", - \"hostName\": \"backup-restore-65bd9b6ff5-z69sn\", - \"created\": \"2024-10-19 13:16:54\" - } -]")))))) - -(deftest test-set-new-password-action - (is (= :error-parse-password - (cut/set-new-password-action - (ld/parse "2024-10-19 13:16:54" cut/timestamp-formatter) - [] - {:new-password-config {:new-password-file "new-pw-file" - :valid-from "2024-11-29 12:00:16"}}))) - (is (= :wait-for-new-pwd - (cut/set-new-password-action - (ld/parse "2024-10-19 13:16:54" cut/timestamp-formatter) - [{:current true - :id "a1" - :userName "root" - :hostName "host" - :created (ld/parse "2023-01-01 00:00:00" cut/timestamp-formatter)}] - {:new-password-config {:new-password-file "new-pw-file" - :valid-from "2024-11-29 12:00:16"}}))) - (is (= :change-pwd - (cut/set-new-password-action - (ld/parse "2024-11-29 13:16:54" cut/timestamp-formatter) - [{:current true - :id "a1" - :userName "root" - :hostName "host" - :created (ld/parse "2023-01-01 00:00:00" cut/timestamp-formatter)}] - {:new-password-config {:new-password-file "new-pw-file" - :valid-from "2024-11-29 12:00:16"}}))) - - (is (= :pwd-change-finished - (cut/set-new-password-action - (ld/parse "2024-11-29 13:16:55" cut/timestamp-formatter) - [{:current true - :id "a2" - :userName "root" - :hostName "host" - :created (ld/parse "2024-11-29 13:16:54" cut/timestamp-formatter)}] - {:new-password-config {:new-password-file "new-pw-file" - :valid-from "2024-11-29 12:00:16"}}))) - - (is (= :no-pwd-change-needed - (cut/set-new-password-action - (ld/parse "2024-11-29 13:16:55" cut/timestamp-formatter) - [{:current true - :id "a2" - :userName "root" - :hostName "host" - :created (ld/parse "2024-11-29 13:16:54" cut/timestamp-formatter)}] - {}))) - ) \ No newline at end of file diff --git a/test/dda/backup/restic/domain_test.clj b/test/dda/backup/restic/domain_test.clj index d4e7a4f..44453c6 100644 --- a/test/dda/backup/restic/domain_test.clj +++ b/test/dda/backup/restic/domain_test.clj @@ -64,9 +64,34 @@ (deftest should-calculate-forget-command (is (= [["restic" "-r" "repo/dir" "-v" "forget" - "--group-by" "" "--keep-last" "1" + "--group-by" "''" "--keep-last" "1" "--keep-daily" "39" "--keep-monthly" "3" "--prune"]] (cut/forget-command {:restic-repository "repo" :backup-path "dir" :days-to-keep 39 - :months-to-keep 3})))) \ No newline at end of file + :months-to-keep 3})))) + +(deftest should-calculate-change-password-command + (is (= [["restic" + "-r" + "repo/dir" + "-v" + "--new-password-file" + "/new-pwd" + "key" + "passwd"]] + (cut/change-password-command {:restic-repository "repo" + :new-password-file "/new-pwd" + :backup-path "dir" + :days-to-keep 39 + :months-to-keep 3})))) + +(deftest should-parse-check-error + (is (= :not-initialized + (cut/parse-check-error "Fatal: unable to open config file: stat /restic-repo/files/config: no such file or directory\nIs there a repository at the following location?\n/restic-repo/files" ) + )) + (is (= :wrong-password + (cut/parse-check-error "Fatal: wrong password or no key found\n"))) + (is (= :no-password + (cut/parse-check-error "Resolving password failed: Fatal: /restic-pwd does not exist\n"))) + ) From 33e869a44c541f7fe78ded5e26be4793d2abaa6a Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Fri, 27 Dec 2024 10:05:09 +0100 Subject: [PATCH 16/18] fix ci: install clojure --- .gitlab-ci.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d4dc574..d7d11c0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,7 +5,7 @@ stages: - image .img: &img - image: "domaindrivenarchitecture/ddadevops-dind:4.13.0" + image: "domaindrivenarchitecture/ddadevops-dind:4.13.1" services: - docker:dind before_script: @@ -15,13 +15,17 @@ stages: - export IMAGE_TAG=$CI_COMMIT_TAG .clj-job: &clj - image: "domaindrivenarchitecture/ddadevops-clj:4.13.0" + image: "domaindrivenarchitecture/ddadevops-clj:4.13.1" cache: key: ${CI_COMMIT_REF_SLUG} paths: - .m2 before_script: - export RELEASE_ARTIFACT_TOKEN=$MEISSA_REPO_BUERO_RW + - cd /tmp + - curl -L -O https://github.com/clojure/brew-install/releases/latest/download/linux-install.sh + - chmod +x linux-install.sh + - /tmp/linux-install.sh - mkdir -p /root/.lein - echo "{:auth {:repository-auth {#\"clojars\" {:username \"${CLOJARS_USER}\" :password \"${CLOJARS_TOKEN_DOMAINDRIVENARCHITECTURE}\" }}}}" > ~/.lein/profiles.clj From 5b61521874a8882658f03e5e8b148b12b51bed87 Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Fri, 27 Dec 2024 10:13:30 +0100 Subject: [PATCH 17/18] fix ci: install clojure --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d7d11c0..d991294 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,7 +22,6 @@ stages: - .m2 before_script: - export RELEASE_ARTIFACT_TOKEN=$MEISSA_REPO_BUERO_RW - - cd /tmp - curl -L -O https://github.com/clojure/brew-install/releases/latest/download/linux-install.sh - chmod +x linux-install.sh - /tmp/linux-install.sh From e0ea4f623d3c5586a1407ed17d197802bfb60095 Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Fri, 27 Dec 2024 10:14:56 +0100 Subject: [PATCH 18/18] fix ci: install clojure --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d991294..f58418f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -24,7 +24,7 @@ stages: - export RELEASE_ARTIFACT_TOKEN=$MEISSA_REPO_BUERO_RW - curl -L -O https://github.com/clojure/brew-install/releases/latest/download/linux-install.sh - chmod +x linux-install.sh - - /tmp/linux-install.sh + - ./linux-install.sh - mkdir -p /root/.lein - echo "{:auth {:repository-auth {#\"clojars\" {:username \"${CLOJARS_USER}\" :password \"${CLOJARS_TOKEN_DOMAINDRIVENARCHITECTURE}\" }}}}" > ~/.lein/profiles.clj