From 3f4d5bb4d6889fef13a1c127f41f9f43e32aca72 Mon Sep 17 00:00:00 2001 From: zwa Date: Tue, 15 Aug 2023 19:40:53 +0000 Subject: [PATCH] refactor-git-trust (#4) Co-authored-by: Michael Jerger Co-authored-by: ansgarz Reviewed-on: https://repo.prod.meissa.de/meissa/provs/pulls/4 --- doc/dev/ADRServicesImplementationStatic.md | 38 +++++++++ doc/dev/architecture.md | 77 +++++++++++++++++++ ...liTargetCommand.kt => TargetCliCommand.kt} | 0 .../provs/desktop/domain/DesktopService.kt | 1 + .../provs/desktop/domain/KnownHost.kt | 35 +++++++++ .../provs/desktop/domain/KnownHostService.kt | 13 ++++ .../provs/desktop/infrastructure/DevOps.kt | 2 + .../provs/framework/ubuntu/git/base/Git.kt | 25 ------ .../provs/framework/ubuntu/keys/base/Ssh.kt | 63 +++++---------- .../domain/CliTargetCommandTest.kt | 2 +- .../provs/desktop/domain/KnownHostTest.kt | 54 +++++++++++++ .../framework/ubuntu/git/base/GitKtTest.kt | 7 +- .../framework/ubuntu/keys/base/SshKtTest.kt | 35 ++++++++- 13 files changed, 275 insertions(+), 77 deletions(-) create mode 100644 doc/dev/ADRServicesImplementationStatic.md create mode 100644 doc/dev/architecture.md rename src/main/kotlin/org/domaindrivenarchitecture/provs/configuration/domain/{CliTargetCommand.kt => TargetCliCommand.kt} (100%) create mode 100644 src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/domain/KnownHost.kt create mode 100644 src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/domain/KnownHostService.kt create mode 100644 src/test/kotlin/org/domaindrivenarchitecture/provs/desktop/domain/KnownHostTest.kt diff --git a/doc/dev/ADRServicesImplementationStatic.md b/doc/dev/ADRServicesImplementationStatic.md new file mode 100644 index 0000000..4b22424 --- /dev/null +++ b/doc/dev/ADRServicesImplementationStatic.md @@ -0,0 +1,38 @@ +# ADR: We implement domain services static + +Domain services can be implemented either as object (and composed like done in spring / example1 ) or with extension +function and composed static (see example2). + +## example1 +```kotlin +class DesktopServie(val aptApi: AptApi, val prov: Prov) { + fun provisionIdeDesktop(onlyModules: List? = null) { + prov.task { + if (onlyModules == null) { + aptApi.aptInstall(OPEN_VPM) + } + } + } +} +``` + +## example2 +```kotlin +fun Prov.provisionIdeDesktop(onlyModules: List? = null) { + if (onlyModules == null) { + aptInstall(OPEN_VPM) + } +} +``` + +## Decission + +We use extension function and composed static. + +## Reason + +1. Similar to composed objects we can easily mock `aptInstall` in tests. Both solutions are equivalent. +2. Inheritance in case of composed objects we can solve by static composition. +3. Object composition we can solve by static composition. + +There is no reason left to change the current implementd pattern. diff --git a/doc/dev/architecture.md b/doc/dev/architecture.md new file mode 100644 index 0000000..8f091a2 --- /dev/null +++ b/doc/dev/architecture.md @@ -0,0 +1,77 @@ + +## Initialization + +```mermaid +sequenceDiagram + actor user + participant app as Application + participant ds as DesktopService + participant gtr as KnownHost + participant pa as CliArgumentsParser + participant cr as DesktopConfigRepository + participant ut as CliUtils + participant su as ProvsWithSudo + + user ->> app: main + activate app + app ->> pa: parseCommands + app ->> cr: getConfig(configFileName) + app ->> ut: createProvInstance(cmd.target) + app ->> su: ensureSudoWithoutPassword(cmd.target.remoteTarget()?.password) + app ->> ds: provisionDesktopCommand(cmd, config) + activate ds + ds ->> gtr: values() + gtr -->> ds: List(KnownHost) + deactivate ds + deactivate app +``` + +## Domain + +```mermaid +classDiagram + + namespace configuration { + + class TargetCliCommand { + val target: String, + val passwordInteractive: Boolean = false + } + + class ConfigFileName { + fileName: String + } + } + + namespace desktop { + + class DesktopCliCommand { + } + + class DesktopConfig { + val ssh: SshKeyPairSource? = null, + val gpg: KeyPairSource? = null, + val gitUserName: String? = null, + val gitEmail: String? = null, + } + + class DesktopType { + val name: String + } + class DesktopOnlyModule { + <> + FIREFOX, VERIFY + } + + class KnownHost { + hostName: String, + hostKeys: List + } + } + + DesktopCliCommand "1" *-- "1" DesktopType: type + DesktopCliCommand "1" *-- "1" TargetCliCommand: target + DesktopCliCommand "1" *-- "1" ConfigFileName: configFile + DesktopCliCommand "1" *-- "..n" DesktopOnlyModule: onlyModules + +``` \ No newline at end of file diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/configuration/domain/CliTargetCommand.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/configuration/domain/TargetCliCommand.kt similarity index 100% rename from src/main/kotlin/org/domaindrivenarchitecture/provs/configuration/domain/CliTargetCommand.kt rename to src/main/kotlin/org/domaindrivenarchitecture/provs/configuration/domain/TargetCliCommand.kt diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/domain/DesktopService.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/domain/DesktopService.kt index 778e341..0e351bb 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/domain/DesktopService.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/domain/DesktopService.kt @@ -12,6 +12,7 @@ import org.domaindrivenarchitecture.provs.framework.ubuntu.keys.provisionKeys import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.currentUserCanSudoWithoutPassword import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.whoami + internal fun Prov.provisionDesktopCommand(cmd: DesktopCliCommand, conf: DesktopConfig) = task { provisionDesktop( cmd.type, diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/domain/KnownHost.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/domain/KnownHost.kt new file mode 100644 index 0000000..fa51e38 --- /dev/null +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/domain/KnownHost.kt @@ -0,0 +1,35 @@ +package org.domaindrivenarchitecture.provs.desktop.domain + +/** + * A HostKey should contain space-separated: keytype, key and (optionally) a comment + * + * See: https://man7.org/linux/man-pages/man8/sshd.8.html#SSH_KNOWN_HOSTS_FILE_FORMAT + */ +typealias HostKey = String + +open class KnownHost protected constructor(val hostName: String, val hostKeys: List) { + companion object { + val GITHUB = KnownHost( + "github.com", listOf( + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl", + "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=", + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=", + ) + ) + val GITLAB = KnownHost( + "gitlab.com", listOf( + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf", + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9", + "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY=", + ) + ) + + @JvmStatic + protected val values = listOf(GITHUB, GITLAB) + + fun values(): List { + return values + } + } +} + diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/domain/KnownHostService.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/domain/KnownHostService.kt new file mode 100644 index 0000000..77e6e0f --- /dev/null +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/domain/KnownHostService.kt @@ -0,0 +1,13 @@ +package org.domaindrivenarchitecture.provs.desktop.domain + +import org.domaindrivenarchitecture.provs.framework.core.Prov +import org.domaindrivenarchitecture.provs.framework.ubuntu.keys.base.addKnownHost + + +fun Prov.addKnownHosts(knownHosts: List = KnownHost.values()) = task { + for (knownHost in knownHosts) { + with(knownHost) { + addKnownHost(hostName, hostKeys, verifyKeys = true) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/DevOps.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/DevOps.kt index a75a6d9..178f201 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/DevOps.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/infrastructure/DevOps.kt @@ -15,7 +15,9 @@ fun Prov.installDevOps() = task { installTerraform() installKubectlAndTools() installYq() + // TODO: the can be removed installAwsCredentials() + // TODO: the can be removed installDevOpsFolder() } diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/git/base/Git.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/git/base/Git.kt index ddc04f7..d1bbe23 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/git/base/Git.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/git/base/Git.kt @@ -3,9 +3,6 @@ package org.domaindrivenarchitecture.provs.framework.ubuntu.git.base import org.domaindrivenarchitecture.provs.framework.core.Prov import org.domaindrivenarchitecture.provs.framework.core.ProvResult import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.* -import org.domaindrivenarchitecture.provs.framework.ubuntu.keys.base.KNOWN_HOSTS_FILE -import org.domaindrivenarchitecture.provs.framework.ubuntu.keys.base.trustHost -import java.io.File /** @@ -41,25 +38,3 @@ fun Prov.gitClone( } } - -fun Prov.trustGithub() = task { - // current fingerprints from https://docs.github.com/en/github/authenticating-to-github/githubs-ssh-key-fingerprints - val fingerprints = setOf( - "SHA256:uNiVztksCsDhcc0u9e8BujQXVUpKZIDTMczCvj3tD2s github.com", // (RSA) - "SHA256:p2QAMXNIC1TJYWeIOttrVc98/R1BUFWu3/LiyKgUfQM github.com", // (ECDSA) - "SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU github.com" // (Ed25519) - ) - trustHost("github.com", fingerprints) -} - - -fun Prov.trustGitlab() = task { - // entries for known_hosts from https://docs.gitlab.com/ee/user/gitlab_com/ - val gitlabFingerprints = """ - gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf - gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9 - gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY= - """.trimIndent() - addTextToFile("\n" + gitlabFingerprints + "\n", File(KNOWN_HOSTS_FILE)) -} - diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/keys/base/Ssh.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/keys/base/Ssh.kt index 4eb39ea..cee5133 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/keys/base/Ssh.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/keys/base/Ssh.kt @@ -2,12 +2,9 @@ package org.domaindrivenarchitecture.provs.framework.ubuntu.keys.base import org.domaindrivenarchitecture.provs.framework.core.Prov import org.domaindrivenarchitecture.provs.framework.core.ProvResult -import org.domaindrivenarchitecture.provs.framework.core.echoCommandForText -import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.checkFile -import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createDir -import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createFile -import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.createSecretFile +import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.* import org.domaindrivenarchitecture.provs.framework.ubuntu.keys.SshKeyPair +import java.io.File const val KNOWN_HOSTS_FILE = "~/.ssh/known_hosts" @@ -34,61 +31,39 @@ fun Prov.isHostKnown(hostOrIp: String) : Boolean { /** * Adds ssh keys for specified host (which also can be an ip-address) to ssh-file "known_hosts" - * Either add the specified rsaFingerprints or - if null - add automatically retrieved keys. + * Either add the specified keys or - if null - add keys automatically retrieved. * Note: adding keys automatically is vulnerable to a man-in-the-middle attack, thus considered insecure and not recommended. */ -fun Prov.trustHost(host: String, fingerprintsOfKeysToBeAdded: Set?) = taskWithResult { - if (isHostKnown(host)) { - return@taskWithResult ProvResult(true, out = "Host already known") - } +fun Prov.addKnownHost(host: String, keysToBeAdded: List?, verifyKeys: Boolean = false) = task { if (!checkFile(KNOWN_HOSTS_FILE)) { createDir(".ssh") createFile(KNOWN_HOSTS_FILE, null) } - if (fingerprintsOfKeysToBeAdded == null) { + if (keysToBeAdded == null) { // auto add keys cmd("ssh-keyscan $host >> $KNOWN_HOSTS_FILE") } else { - // logic based on https://serverfault.com/questions/447028/non-interactive-git-clone-ssh-fingerprint-prompt - val actualKeys = findSshKeys(host) - if (actualKeys == null || actualKeys.size == 0) { - return@taskWithResult ProvResult(false, out = "No valid keys found for host: $host") - } - val actualFingerprints = getFingerprintsForKeys(actualKeys) - for (fingerprintToBeAdded in fingerprintsOfKeysToBeAdded) { - var indexOfKeyFound = -1 - - // search for fingerprint in actual fingerprints - for ((i, actualFingerprint) in actualFingerprints.withIndex()) { - if (actualFingerprint.contains(fingerprintToBeAdded)) { - indexOfKeyFound = i - break + for (key in keysToBeAdded) { + if (!verifyKeys) { + addTextToFile("\n$host $key\n", File(KNOWN_HOSTS_FILE)) + } else { + val validKeys = getSshKeys(host) + if (validKeys?.contains(key) == true) { + addTextToFile("\n$host $key\n", File(KNOWN_HOSTS_FILE)) + } else { + addResultToEval(ProvResult(false, err = "The following key of host [$host] could not be verified successfully: " + key)) } } - if (indexOfKeyFound == -1) { - return@taskWithResult ProvResult( - false, - err = "Fingerprint ($fingerprintToBeAdded) could not be found in actual fingerprints: $actualFingerprints" - ) - } - cmd(echoCommandForText(actualKeys.get(indexOfKeyFound) + "\n") + " >> $KNOWN_HOSTS_FILE") } - ProvResult(true) } } /** - * Returns a list of valid ssh keys for the given host (host can also be an ip address) + * Returns a list of valid ssh keys for the given host (host can also be an ip address), keys are returned as keytype and key BUT WITHOUT the host name */ -private fun Prov.findSshKeys(host: String): List? { - return cmd("ssh-keyscan $host 2>/dev/null").out?.split("\n")?.filter { x -> "" != x } -} - - -/** - * Returns a list of fingerprints of the given sshKeys; the returning list has same size and order as the specified list of sshKeys - */ -private fun Prov.getFingerprintsForKeys(sshKeys: List): List { - return sshKeys.map { x -> cmd("echo \"$x\" | ssh-keygen -lf -").out ?: "" } +private fun Prov.getSshKeys(host: String, keytype: String? = null): List? { + val keytypeOption = keytype?.let { " -t $keytype " } ?: "" + val output = cmd("ssh-keyscan $keytypeOption $host 2>/dev/null").out?.trim() + return output?.split("\n")?.filter { x -> "" != x }?.map { x -> x.substringAfter(" ") } } diff --git a/src/test/kotlin/org/domaindrivenarchitecture/provs/configuration/domain/CliTargetCommandTest.kt b/src/test/kotlin/org/domaindrivenarchitecture/provs/configuration/domain/CliTargetCommandTest.kt index 3b137c7..ba2f91a 100644 --- a/src/test/kotlin/org/domaindrivenarchitecture/provs/configuration/domain/CliTargetCommandTest.kt +++ b/src/test/kotlin/org/domaindrivenarchitecture/provs/configuration/domain/CliTargetCommandTest.kt @@ -13,7 +13,7 @@ import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test -internal class CliTargetCommandKtTest { +internal class TargetCliCommandKtTest { companion object { @BeforeAll diff --git a/src/test/kotlin/org/domaindrivenarchitecture/provs/desktop/domain/KnownHostTest.kt b/src/test/kotlin/org/domaindrivenarchitecture/provs/desktop/domain/KnownHostTest.kt new file mode 100644 index 0000000..ddae547 --- /dev/null +++ b/src/test/kotlin/org/domaindrivenarchitecture/provs/desktop/domain/KnownHostTest.kt @@ -0,0 +1,54 @@ +package org.domaindrivenarchitecture.provs.desktop.domain + +import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.deleteFile +import org.domaindrivenarchitecture.provs.framework.ubuntu.install.base.aptInstall +import org.domaindrivenarchitecture.provs.framework.ubuntu.keys.base.KNOWN_HOSTS_FILE +import org.domaindrivenarchitecture.provs.test.defaultTestContainer +import org.domaindrivenarchitecture.provs.test.tags.ContainerTest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + + +class KnownHostTest { + + @ContainerTest + fun defaultKnownHosts() { + // given + val prov = defaultTestContainer() + prov.task { + aptInstall("ssh") + deleteFile(KNOWN_HOSTS_FILE) + } + + // when + val res = prov.addKnownHosts() + + // then + assertTrue(res.success) + } + + + // Subclass of KnownHost for test knownHostSubclass_includes_additional_host + class KnownHostsSubclass(hostName: String, hostKeys: List): KnownHost(hostName, hostKeys) { + + companion object { + val ANOTHER_HOST = KnownHostsSubclass("anotherhost.com", listOf("key1")) + + fun values(): List { + return values + ANOTHER_HOST + } + } + } + + @Test + fun knownHostSubclass_includes_additional_host() { + // when + val hosts = KnownHostsSubclass.values() + + // then + assertTrue(hosts.size > 1) + assertEquals("key1", hosts.last().hostKeys[0]) + } +} + diff --git a/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/git/base/GitKtTest.kt b/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/git/base/GitKtTest.kt index 873d603..51f6a14 100644 --- a/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/git/base/GitKtTest.kt +++ b/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/git/base/GitKtTest.kt @@ -1,5 +1,6 @@ package org.domaindrivenarchitecture.provs.framework.ubuntu.git.base +import org.domaindrivenarchitecture.provs.desktop.domain.addKnownHosts import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.checkDir import org.domaindrivenarchitecture.provs.framework.ubuntu.install.base.aptInstall import org.domaindrivenarchitecture.provs.framework.ubuntu.keys.base.isHostKnown @@ -18,12 +19,10 @@ internal class GitKtTest { a.aptInstall("openssh-client") // when - val res = a.trustGithub() - val res2 = a.trustGitlab() + val res = a.addKnownHosts() // then assertTrue(res.success) - assertTrue(res2.success) assertTrue(a.isHostKnown("github.com"), "github.com does not seem to be a known host") assertTrue(a.isHostKnown("gitlab.com"), "gitlab.com does not seem to be a known host") @@ -37,7 +36,7 @@ internal class GitKtTest { prov.aptInstall("git") // when - prov.trustGithub() + prov.addKnownHosts() val res1 = prov.gitClone("https://gitlab.com/domaindrivenarchitecture/not a valid basename.git", "~/") val res2 = prov.gitClone(repo) val res3 = prov.gitClone(repo, pullIfExisting = false) diff --git a/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/keys/base/SshKtTest.kt b/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/keys/base/SshKtTest.kt index 8d5e999..5c317b8 100644 --- a/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/keys/base/SshKtTest.kt +++ b/src/test/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/keys/base/SshKtTest.kt @@ -1,15 +1,16 @@ package org.domaindrivenarchitecture.provs.framework.ubuntu.keys.base import org.domaindrivenarchitecture.provs.framework.core.Secret +import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.deleteFile +import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.fileContainsText import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.fileContent +import org.domaindrivenarchitecture.provs.framework.ubuntu.install.base.aptInstall import org.domaindrivenarchitecture.provs.framework.ubuntu.keys.* import org.domaindrivenarchitecture.provs.test.defaultTestContainer import org.domaindrivenarchitecture.provs.test.tags.ContainerTest -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Assertions.* internal class SshKtTest { - @ContainerTest fun configureSshKeys_for_ssh_type_rsa() { // given @@ -45,4 +46,32 @@ internal class SshKtTest { val privateSshKeyFileContent = prov.fileContent("~/.ssh/id_ed25519") assertEquals(privateED25519SnakeOilKey() + "\n", privateSshKeyFileContent) } + + @ContainerTest + fun addKnownHost() { + // given + val prov = defaultTestContainer() + prov.task { + aptInstall("ssh") + deleteFile(KNOWN_HOSTS_FILE) + } + + // when + val res = prov.addKnownHost("github.com", listOf("dummyProtocol dummyKey", "dummyProtocol2 dummyKey2", )) + val res2 = prov.addKnownHost("github.com", listOf("dummyProtocol dummyKey", "dummyProtocol2 dummyKey2", )) + val res3 = prov.addKnownHost("github.com", listOf("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl", ), verifyKeys = true) + val res4 = prov.addKnownHost("github.com", listOf("ssh-ed25519 AAAAC3Nzalwrongkey!!!", ), verifyKeys = true) + + // then + assertTrue(res.success) + assertTrue(prov.fileContainsText(KNOWN_HOSTS_FILE, "github.com dummyProtocol dummyKey")) + assertTrue(prov.fileContainsText(KNOWN_HOSTS_FILE, "github.com dummyProtocol2 dummyKey2")) + + assertTrue(res2.success) + val keyCount = prov.cmd("grep -o -i dummyKey2 $KNOWN_HOSTS_FILE | wc -l").out?.trim() + assertEquals("1", keyCount) + + assertTrue(res3.success) + assertFalse(res4.success) + } } \ No newline at end of file