refactor addKnownHost

This commit is contained in:
ansgarz 2023-08-18 23:25:21 +02:00
parent 2071128371
commit 4452cf5d01
5 changed files with 55 additions and 29 deletions

View file

@ -7,7 +7,7 @@ package org.domaindrivenarchitecture.provs.desktop.domain
*/ */
typealias HostKey = String typealias HostKey = String
open class KnownHost protected constructor( open class KnownHost(
val hostName: String, val hostName: String,
val hostKeys: List<HostKey> val hostKeys: List<HostKey>
) { ) {

View file

@ -6,6 +6,6 @@ import org.domaindrivenarchitecture.provs.framework.ubuntu.keys.base.addKnownHos
fun Prov.addKnownHosts(knownHosts: List<KnownHost> = KnownHost.values()) = task { fun Prov.addKnownHosts(knownHosts: List<KnownHost> = KnownHost.values()) = task {
for (knownHost in knownHosts) { for (knownHost in knownHosts) {
addKnownHost(knownHost.hostName, knownHost.hostKeys, verifyKeys = true) addKnownHost(knownHost, verifyKeys = true)
} }
} }

View file

@ -1,5 +1,6 @@
package org.domaindrivenarchitecture.provs.framework.ubuntu.keys.base package org.domaindrivenarchitecture.provs.framework.ubuntu.keys.base
import org.domaindrivenarchitecture.provs.desktop.domain.KnownHost
import org.domaindrivenarchitecture.provs.framework.core.Prov import org.domaindrivenarchitecture.provs.framework.core.Prov
import org.domaindrivenarchitecture.provs.framework.core.ProvResult import org.domaindrivenarchitecture.provs.framework.core.ProvResult
import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.* import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.*
@ -7,8 +8,6 @@ import org.domaindrivenarchitecture.provs.framework.ubuntu.keys.SshKeyPair
import java.io.File import java.io.File
const val KNOWN_HOSTS_FILE = "~/.ssh/known_hosts"
/** /**
* Installs ssh keys for active user; ssh filenames depend on the ssh key type, e.g. for public key file: "id_rsa.pub", "id_id_ed25519.pub", etc * Installs ssh keys for active user; ssh filenames depend on the ssh key type, e.g. for public key file: "id_rsa.pub", "id_id_ed25519.pub", etc
*/ */
@ -30,28 +29,31 @@ fun Prov.isHostKnown(hostOrIp: String) : Boolean {
/** /**
* Adds ssh keys for specified host (which also can be an ip-address) to ssh-file "known_hosts" * Adds ssh keys for specified host (which also can be an ip-address) to the ssh-file "known_hosts".
* Either add the specified keys or - if null - add keys automatically retrieved. * If parameter verifyKeys is true the keys are checked against the live keys of the host and only added if valid.
* Note: adding keys automatically is vulnerable to a man-in-the-middle attack, thus considered insecure and not recommended.
*/ */
fun Prov.addKnownHost(host: String, keysToBeAdded: List<String>?, verifyKeys: Boolean = false) = task { fun Prov.addKnownHost(knownHost: KnownHost, verifyKeys: Boolean = false) = task {
if (!checkFile(KNOWN_HOSTS_FILE)) { val knownHostsFile = "~/.ssh/known_hosts"
if (!checkFile(knownHostsFile)) {
createDir(".ssh") createDir(".ssh")
createFile(KNOWN_HOSTS_FILE, null) createFile(knownHostsFile, null)
} }
if (keysToBeAdded == null) { with(knownHost) {
// auto add keys for (key in hostKeys) {
cmd("ssh-keyscan $host >> $KNOWN_HOSTS_FILE")
} else {
for (key in keysToBeAdded) {
if (!verifyKeys) { if (!verifyKeys) {
addTextToFile("\n$host $key\n", File(KNOWN_HOSTS_FILE)) addTextToFile("\n$hostName $key\n", File(knownHostsFile))
} else { } else {
val validKeys = getSshKeys(host) val validKeys = getSshKeys(hostName)
if (validKeys?.contains(key) == true) { if (validKeys?.contains(key) == true) {
addTextToFile("\n$host $key\n", File(KNOWN_HOSTS_FILE)) addTextToFile("\n$hostName $key\n", File(knownHostsFile))
} else { } else {
addResultToEval(ProvResult(false, err = "The following key of host [$host] could not be verified successfully: " + key)) addResultToEval(
ProvResult(
false,
err = "The following key of host [$hostName] could not be verified successfully: " + key
)
)
} }
} }
} }

View file

@ -2,7 +2,6 @@ package org.domaindrivenarchitecture.provs.desktop.domain
import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.deleteFile import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.deleteFile
import org.domaindrivenarchitecture.provs.framework.ubuntu.install.base.aptInstall 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.defaultTestContainer
import org.domaindrivenarchitecture.provs.test.tags.ContainerTest import org.domaindrivenarchitecture.provs.test.tags.ContainerTest
import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertEquals
@ -18,7 +17,7 @@ class KnownHostTest {
val prov = defaultTestContainer() val prov = defaultTestContainer()
prov.task { prov.task {
aptInstall("ssh") aptInstall("ssh")
deleteFile(KNOWN_HOSTS_FILE) deleteFile("~/.ssh/known_hosts")
} }
// when // when

View file

@ -1,5 +1,6 @@
package org.domaindrivenarchitecture.provs.framework.ubuntu.keys.base package org.domaindrivenarchitecture.provs.framework.ubuntu.keys.base
import org.domaindrivenarchitecture.provs.desktop.domain.KnownHost
import org.domaindrivenarchitecture.provs.framework.core.Secret import org.domaindrivenarchitecture.provs.framework.core.Secret
import org.domaindrivenarchitecture.provs.framework.ubuntu.filesystem.base.deleteFile 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.fileContainsText
@ -10,6 +11,8 @@ import org.domaindrivenarchitecture.provs.test.defaultTestContainer
import org.domaindrivenarchitecture.provs.test.tags.ContainerTest import org.domaindrivenarchitecture.provs.test.tags.ContainerTest
import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Assertions.*
const val KNOWN_HOSTS_FILE = "~/.ssh/known_hosts"
internal class SshKtTest { internal class SshKtTest {
@ContainerTest @ContainerTest
fun configureSshKeys_for_ssh_type_rsa() { fun configureSshKeys_for_ssh_type_rsa() {
@ -48,7 +51,7 @@ internal class SshKtTest {
} }
@ContainerTest @ContainerTest
fun addKnownHost() { fun addKnownHost_without_verification() {
// given // given
val prov = defaultTestContainer() val prov = defaultTestContainer()
prov.task { prov.task {
@ -57,10 +60,8 @@ internal class SshKtTest {
} }
// when // when
val res = prov.addKnownHost("github.com", listOf("dummyProtocol dummyKey", "dummyProtocol2 dummyKey2", )) val res = prov.addKnownHost(KnownHost("github.com", listOf("dummyProtocol dummyKey", "dummyProtocol2 dummyKey2", )))
val res2 = prov.addKnownHost("github.com", listOf("dummyProtocol dummyKey", "dummyProtocol2 dummyKey2", )) val res2 = prov.addKnownHost(KnownHost("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 // then
assertTrue(res.success) assertTrue(res.success)
@ -70,8 +71,32 @@ internal class SshKtTest {
assertTrue(res2.success) assertTrue(res2.success)
val keyCount = prov.cmd("grep -o -i dummyKey2 $KNOWN_HOSTS_FILE | wc -l").out?.trim() val keyCount = prov.cmd("grep -o -i dummyKey2 $KNOWN_HOSTS_FILE | wc -l").out?.trim()
assertEquals("1", keyCount) assertEquals("1", keyCount)
}
assertTrue(res3.success) @ContainerTest
assertFalse(res4.success) fun addKnownHost_with_verifications() {
// given
val prov = defaultTestContainer()
prov.task {
aptInstall("ssh")
deleteFile(KNOWN_HOSTS_FILE)
}
// when
val res1 = prov.addKnownHost(KnownHost.GITHUB, verifyKeys = true)
val res2 = prov.addKnownHost(KnownHost.GITHUB, verifyKeys = true)
val invalidKey = "ssh-ed25519 AAAAC3Nzalwrongkey!!!"
val res3 = prov.addKnownHost(KnownHost("github.com", listOf(invalidKey )), verifyKeys = true)
// then
assertTrue(res1.success)
assertTrue(prov.fileContainsText(KNOWN_HOSTS_FILE, KnownHost.GITHUB.hostKeys[0]))
assertTrue(res2.success)
assertTrue(prov.fileContainsText(KNOWN_HOSTS_FILE, KnownHost.GITHUB.hostKeys[0]))
assertFalse(res3.success)
assertFalse(prov.fileContainsText(KNOWN_HOSTS_FILE, invalidKey))
} }
} }